Everyone sees the pfps, but few understand the code behind them.
In this series, we go line-by-line through NFT's contract code.
We’re going to take a look at the project with the most trading volume at the time of writing.
HAPE PRIME
Let's dive in 🧵👇
At the top, we have the variables nicely organized with some documentation.
We’ll touch on all of these where they’re used, but I wanted to kick it off by looking at the metadata because their approach here is unique.
Their metadata is accessed from a different contract through `IHapebeastMetadata`.
This is saying any contract that has `tokenURI(…)` implemented can conform to `IHapebeastMetadata`.
Which is any ERC721 contract.
In the `constructor` the `metadata` was initially set.
You can see `tokenURI(…)` calls `metadata.tokenURI(…)` which is the `IHapebeastMetadata` implementation.
It is essentially reaching into a different contract to get the metadata.
`updateMetadata(…)` is used to update `metadata` at any time as long as `isMetadataLocked` is false.
`lockMetadata()` is a one-way street to make `metadata` immutable.
The `metadata` is not locked.
The `metadata` is currently driven by this contract https://etherscan.io/address/0x609dfe72eef3ccb069bc2682b3114763e6f25b8f
The code hasn’t been verified for their metadata 0x609dfe72eef3ccb069bc2682b3114763e6f25b8f 🤔
The final pieces of metadata are driven by the `provenanceHash` and the `startingIndex`.
HAPE PRIME left a comment that says they implemented it this way so they could eventually put the metadata on-chain.
Great, but until that is done, what do you truly own?
There are three things they need to do to ultimately lock down the metadata.
They need to update `IHapebeastMetadata` with their finalized version of the metadata.
They need to call `lockMetadata()`
And finally, they need to verify the code for their metadata contract so people can validate that the metadata is immutable there as well. If the metadata is mutable in `IHapebeastMetadata`, it wouldn’t matter that they called `lockMetadata()`.
Now to minting.
Their approach here is unique as well. They maintain the concept of a `minter` address.
The `minter` can be updated as long as `isMinterLocked` is false.
The only way to mint is through this function.
And the only address that can mint is the `minter`. See the `onlyMinter` modifier.
This means there must’ve been some off-chain minting strategy they ran. I’m not sure why they did this…
They most likely used `_recipient` to send the NFT to the actual address that interacted with their website during mint.
Oh, and I forgot to mention the supply here is theoretically unlimited 🤦♂️
The `minter` can call this as many times as they’d like and create infinity HAPEs.
If you recall, there is a `lockMinter()` function, but it doesn’t lock the minter from minting.
It only locks the `minter` address from being changed.
Set the `minter` to the burn address so no one else can mint 🔥
The unlimited mint, mutable metadata, and mutable provenance all combine to make this contract vulnerable.
I’m shocked to see how often this is coming up in popular NFTs 🤯
Another unique thing this contract does is conform to IERC2981.
IERC2981 is used to manage royalties on-chain. You can set the address for where royalties should be sent and the royalty percentage.
Unfortunately, a marketplace like OpenSea has to respect IERC2981. It isn’t automatically used. i.e. you could still transfer a HAPE from one wallet to another without paying a royalty.
Here they added some functions for burning.
I’m not quite sure why they did this because `_burn(…)` simply sends the token to the burn address.
Anyone can do this without their `burn(…)` implementation.
Finally, we have `withdraw`.
For those new to the series, `withdraw` is how the owners of the contract send the ETH paid to mint to their own wallets.
Thanks for reading!
If you liked this write-up and want to read more of these, please give us a follow on Twitter @BurnableLabs!
If you want to get these write-ups in your inbox, subscribe to our Substack!