- IPFS hashes need to be updated with new JSON pins (need to research "About" section) -> https://docs.opensea.io/docs/metadata-standards'
- Convert to SafeMath?
- Restructure contracts based on what's been learned from zombies
- New Alchemy API key (production version)
- Need to deploy using our shared wallet (not my personal MetaMask) - so update public/private keys
- Need to setup
hardhat.config.js
for Ethereum mainnet - Switch logic from cycling the asset from every 2 hours to every 2 months
- Potentially write a script that LOGIK can execute on his own to obtain the list of level owners so that I don't need to do it for him every time he wants an updated list
- Script that can auto batch transfer the airdrops based on a list of addresses
Working on the assumption that all of these NFT's are basically unrelated unless viewed on LOGIK's website
Passport
should have a mapping from owner addresses to stamp balances (bridged w/ the address for the collection), i.e.
// Passport.sol
// mapping from owner addresses to stamp balances (bridged w/ the address for the collection)
mapping (address => mapping(address => uint32[])
// e.g.
// 0xEab64..Gz9 => [0x8a..er3 => [0,1,1,0,0,0,1], ...]
// means owner 0xEa.. has collected stamps 1, 2, and 7 from the collection at address 0x8a..
!!!!!!(NOTE - 8.19.21) "The problem with this approach
This approach is tempting for its simplicity. But let's look at what happens if we later add a function to transfer a zombie from one owner to another (which we'll definitely want to add in a later lesson!).
That transfer function would need to:
Push the zombie to the new owner's ownerToZombies array,
Remove the zombie from the old owner's ownerToZombies array,
Shift every zombie in the older owner's array up one place to fill the hole, and then
Reduce the array length by 1.
" -> The point is transfering ownership of stamps becomes difficult WAIT MAYBE.... MY APPROACH MAY BE SLIGHTLY DIFFERENT
-
This means we need a function:
awardStamp(address passportOwner, address stamp, uint256 stampId)
-
Maybe also:
awardCitizen(address passportOwner, address citizen, uint256 citizenId)
-
Normal functions I can think of:
beforeTransfer
tokenURI
(the cover of the passport will be different if you own a character???)mintPassport
-
Does anything change if you also mint a character??? Or is this just the first page of the passport if so????
- This will communicate directly with
Passport.sol
in order to validate whether or not someone is allowed to mint a stamp - Each collection will be a new contract where NUM_PASSPORTS Stamps get minted
- The URI returned should never change, but should at least be unique on IPFS (API can handle this)
- Pretty sure these should be simple, while Passport is more complicated than I currently have documented...
-
The main thing with the Citizens is some sort of hash function that determines the URI (because it will be a random, unique citizen determined by something like the minter's address)
-
The API will be doing the heavy lifting as far as the logic is concerned -> I think it needs to use the minter's address to assemble a Character (using layers) and then in-turn pin it to IPFS using Pinata's API, then pin a JSON file that corresponds to it, and then deliver that final IPFS hash back to the contract for the URI.
-
Maybe this also communicates with
Passport.sol
in order to "award" a Citizen -
Create a
CitizenFactory
that has a function to randomly generate a 16 bit number to correspond to the citizen's features e.g.contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string memory _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } function _generateRandomDna(string memory _str) private view returns (uint) { uint rand = uint(keccak256(abi.encodePacked(_str))); return rand % dnaModulus; } function createRandomZombie(string memory _name) public { uint randDna = _generateRandomDna(_name); _createZombie(_name, randDna); } }
Then with javascript:
// Here's how we would access our contract: var abi = /* abi generated by the compiler */ var ZombieFactoryContract = web3.eth.contract(abi) var contractAddress = /* our contract address on Ethereum after deploying */ var ZombieFactory = ZombieFactoryContract.at(contractAddress) // `ZombieFactory` has access to our contract's public functions and events // some sort of event listener to take the text input: $("#ourButton").click(function(e) { var name = $("#nameInput").val() // Call our contract's `createRandomZombie` function: ZombieFactory.createRandomZombie(name) }) // Listen for the `NewZombie` event, and update the UI var event = ZombieFactory.NewZombie(function(error, result) { if (error) return generateZombie(result.zombieId, result.name, result.dna) }) // take the Zombie dna, and update our image function generateZombie(id, name, dna) { let dnaStr = String(dna) // pad DNA with leading zeroes if it's less than 16 characters while (dnaStr.length < 16) dnaStr = "0" + dnaStr let zombieDetails = { // first 2 digits make up the head. We have 7 possible heads, so % 7 // to get a number 0 - 6, then add 1 to make it 1 - 7. Then we have 7 // image files named "head1.png" through "head7.png" we load based on // this number: headChoice: dnaStr.substring(0, 2) % 7 + 1, // 2nd 2 digits make up the eyes, 11 variations: eyeChoice: dnaStr.substring(2, 4) % 11 + 1, // 6 variations of shirts: shirtChoice: dnaStr.substring(4, 6) % 6 + 1, // last 6 digits control color. Updated using CSS filter: hue-rotate // which has 360 degrees: skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360), eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360), clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360), zombieName: name, zombieDescription: "A Level 1 CryptoZombie", } return zombieDetails }
Doing something with a zombie:
function getZombieDetails(id) { return cryptoZombies.methods.zombies(id).call() } // Call the function and do something with the result: getZombieDetails(15) .then(function(result) { console.log("Zombie 15: " + JSON.stringify(result)); });
8.21.21. -
- NOTE: the `_squad` variable may be improperly used right now since I have that
Gnosis wallet (or whatever) ... it's useful for testing that's for sure
- Kind of want to try refactoring even more to make `KasbeerMade721` to handle a bunch
of the stuff I'd want done for every ERC721 i make...
8.23.21 -
- Refactored the code and am now testing it ... all of the ownership logic,
custom hash stuff, and erc721 overrides have been taken out of `Plug.sol`
in order to focus more on the code that matters for this particular NFT
-> so far so good
- Opensea testnet sucks
8.24.21 -
- My build environmnent was broken for a solid few hours,,,,,,,,,,,,:|
- Did some Zombie Truffle tutorials and that's why I got into intalling truffle and
eveything went to shit
- This is all a massive headache
- Okay i fixed all of the issues that somehow arose ... i don't rememeber exactly
what broke everything but `solc` was suddenly a problem and then upon re-installation
the versions for everything were fkd up
- Should have never added truffle....... hardhat already has testing abilities!!
- Removed truffle, added mocha (for hardhat)
- I think everything gets even cleaner with interfaces....
8.25.21 -
- Lots of issues with tests...:
```
Plug contract
Deployment
✓ Deployment should add my dev address to `_squad` (9436ms)
✓ Deployment should add logik's dev address to `_squad` (13428ms)
Squad functionality
1) Squad members should be addable
2) Squad members should be removable
Minting & burning
3) Minting should increment the token id by 1 each time
4) Minting should set the birthday of `tokenId` to the current time
5) Burning should remove a token permnently
Time-triggered asset cycling
6) Plug should update it's hash every 60 days for the first year
7) Plug should turn into an Alchemist after 4 years
2 passing (3m)
7 failing
1) Plug contract
Squad functionality
Squad members should be addable:
Error: Timeout of 20000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/kamikaze/Desktop/localsrc/crypto/logik-genesis-dapp/smart-contracts/test/Plug.js)
at listOnTimeout (internal/timers.js:549:17)
at processTimers (internal/timers.js:492:7)
2) Plug contract
Squad functionality
Squad members should be removable:
Error: Timeout of 20000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/kamikaze/Desktop/localsrc/crypto/logik-genesis-dapp/smart-contracts/test/Plug.js)
at listOnTimeout (internal/timers.js:549:17)
at processTimers (internal/timers.js:492:7)
3) Plug contract
Minting & burning
Minting should increment the token id by 1 each time:
Error: Timeout of 20000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/kamikaze/Desktop/localsrc/crypto/logik-genesis-dapp/smart-contracts/test/Plug.js)
at listOnTimeout (internal/timers.js:549:17)
at processTimers (internal/timers.js:492:7)
4) Plug contract
Minting & burning
Minting should set the birthday of `tokenId` to the current time:
Error: Timeout of 20000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/kamikaze/Desktop/localsrc/crypto/logik-genesis-dapp/smart-contracts/test/Plug.js)
at listOnTimeout (internal/timers.js:549:17)
at processTimers (internal/timers.js:492:7)
5) Plug contract
Minting & burning
Burning should remove a token permnently:
Error: invalid BigNumber value (argument="value", value={"hash":"0x7475821720995861e6a4361d312ad30d36e3eaeb89e027b61d05de3c4d6a2e89","type":2,"accessList":[],"blockHash":null,"blockNumber":null,"transactionIndex":null,"confirmations":0,"from":"0xEAb4Aea5cD7376C04923236c504e7e91362566D1","gasPrice":{"type":"BigNumber","hex":"0x3b9aca0b"},"maxPriorityFeePerGas":{"type":"BigNumber","hex":"0x3b9aca00"},"maxFeePerGas":{"type":"BigNumber","hex":"0x3b9aca0b"},"gasLimit":{"type":"BigNumber","hex":"0x01c78b"},"to":"0x41e80cBcd4705AFfD5bA39A438169A147DFd65cE","value":{"type":"BigNumber","hex":"0x00"},"nonce":235,"data":"0x815a12f6000000000000000000000000eab4aea5cd7376c04923236c504e7e91362566d1","r":"0x232a494fe1673bdb8796e96d0bb2730ed5f1165782a47b5eaba6750917dfb732","s":"0x1f7e1d7f34175eabbc901c7a17c85260c2cfdb4b14867dc8cd724f2cdcfb6df8","v":0,"creates":null,"chainId":4}, code=INVALID_ARGUMENT, version=bignumber/5.4.1)
at Logger.makeError (node_modules/@ethersproject/logger/src.ts/index.ts:213:28)
at Logger.throwError (node_modules/@ethersproject/logger/src.ts/index.ts:225:20)
at Logger.throwArgumentError (node_modules/@ethersproject/logger/src.ts/index.ts:229:21)
at Function.BigNumber.from (node_modules/@ethersproject/bignumber/src.ts/bignumber.ts:291:23)
at NumberCoder.encode (node_modules/@ethersproject/abi/src.ts/coders/number.ts:25:27)
at /Users/kamikaze/Desktop/localsrc/crypto/logik-genesis-dapp/smart-contracts/node_modules/@ethersproject/abi/src.ts/coders/array.ts:71:19
at Array.forEach (<anonymous>)
at Object.pack (node_modules/@ethersproject/abi/src.ts/coders/array.ts:54:12)
at TupleCoder.encode (node_modules/@ethersproject/abi/src.ts/coders/tuple.ts:54:16)
at AbiCoder.encode (node_modules/@ethersproject/abi/src.ts/abi-coder.ts:112:15)
at Interface._encodeParams (node_modules/@ethersproject/abi/src.ts/interface.ts:325:31)
at Interface.encodeFunctionData (node_modules/@ethersproject/abi/src.ts/interface.ts:380:18)
at /Users/kamikaze/Desktop/localsrc/crypto/logik-genesis-dapp/smart-contracts/node_modules/@ethersproject/contracts/src.ts/index.ts:212:37
at step (node_modules/@ethersproject/contracts/lib/index.js:48:23)
at Object.next (node_modules/@ethersproject/contracts/lib/index.js:29:53)
at fulfilled (node_modules/@ethersproject/contracts/lib/index.js:20:58)
6) Plug contract
Time-triggered asset cycling
Plug should update it's hash every 60 days for the first year:
ReferenceError: web3 is not defined
at Object.increase (test/helpers/time.js:4:5)
at Context.<anonymous> (test/Plug.js:154:16)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
7) Plug contract
Time-triggered asset cycling
Plug should turn into an Alchemist after 4 years:
ReferenceError: web3 is not defined
at Object.increase (test/helpers/time.js:4:5)
at Context.<anonymous> (test/Plug.js:174:15)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
```
- So... to summarize
1) Time-outs: Squad add, squad remove, mint increment, mint birthday
2) invalid BigNumber value: burning token
3) web3 is not defined: Asset cycling every 60 days, and 4 years
8.30.21 -
- Today I'd like to:
(1) finish writing tests for every function associated
with the Plug,
(2) deploy a few to opensea testnet to see if the testnet was the
problem before or if it's related to the code,
(3) research how i'm going to deploy
to the mainnet & how much it will cost,
(4) flatten the code for an audit & get a professional opinion on deployment
- Time traveling is turning out to be quite difficult in testing...
- I'm considering I just ignore this for the time being since I've seen
the time-dependent things happen on opensea first-hand
8.31.21 -
- Semi-finished (1), opensea (2) is still seemingly broken and can't rely on it,
didn't quite get to (3), not how necessary flattening is for an audit;
-> Need to just investigate `listLevelOwners` a little more thoroughly before
I can be confident in the contracts being ready for audit
- UPDATE: now we're making a website where the user mints the Plug for themself
(so opensea's problems aren't even a big deal really)
NOTE!!: THIS MEANS `onlyOwner` on the `mint721` function is problematic
ANYONE should be able to mint a plug
- Tonight:
(1) Investigate `listLevelOwners`
(2) Create a shell website that we can easily start to customize for visual
purposes (the other site I started for the citizens of the world should
essentially suffice, so just use the tutorial from Alchemy)