Are NFT projects doing starting index randomization and provenance wrong or is it just me?

This is not true if you just don't publish metadata before end of sale.

Good info Patrick - I've actually didn't work with Chainlink yet but it's next on my learning list.

1 Like

@mhbln sounds like you understand the tradeoffs. We have a high-security mindset when it comes to these projects, and I just want to make sure you understand the risks if you go one of these non-random routes.

@jalil
Yeah of course. But the coolest thing would actually to have the Metadata beforehand without being able to Crack it, which would be possible if you do true randomness at mint (which is too expensive).
See that's what I don't like with the current state of affairs in the eth world. You either gotta go on a layer 2 and let your customers bridge their funds (which 99% of average Joe's don't understand) or you have to pay tons of money (e.g chain link for every call at mint) to run your code the best way. I mean what year is it? Its just a lot of nonsense you gotta do to fit an overloaded old system.
In what real world would a customer pay 5 dollars minimum transaction fee to buy a t-shirt or pay 40 dollars to get the true random lottery numbers. Ridiculous.

Anyway, sorry for my little rant. It is what it is.
Back to the point. With what is described here in the forum all the way scrolled up you could actually post meta data beforehand and it wouldn't matter because you set the offset on the end.

@jalil This is similar to the solution I proposed.
In my upcoming NFT, the contract constructor takes 8 args:

                string memory name,
                string memory symbol,
                uint256 maxNftSupply,
                uint256 revealTs,
                StorageType main,
                string memory provenance,
                string memory ipfsBase,
                string memory arweaveBase

Where StorageType is an enum and the main is the selected type - I'm doing both IPFS and Arweave and therefore need to determine which base URI to use in tokenUri by default.

The provenance hash is the sha-256 of sha-256's - I think it's preferable to stick to something that is platform-agnostic.

I am also keeping a setIPFSBase and setArweaveBase just in case.
To increase trust I also employ a locked state variable which once set cannot be unset and disables tampering with the baseUris (modifiers are handy here).

Whether we'll be launching with the baseUris (therefore making the collection visible) is still a question but mainly one of preference rather than exploitability.

Also do take note that the "seed" for determining startingIndex can also be derived from more factors other than block.number, such as block.difficulty etc...

1 Like

@mhbln Just popped into my head tell me if I'm right,

You could have all the metadata available beforehand and then use Chainlink VRF only inside setStartingIndex to get a true random number for your startingIndex.

You could even further restrict access to that function to onlyOwner.

@PatrickAlphaC forgive my ignorance but is there a local testing simulator for Chainlink that developers can use for unit tests?

2 Likes

@guyo13 yeah sure, thats a way to do it

You sure can!

This has unit tests written for the Chainlink VRF in hardhat. But you can get them for brownie and truffle as well.

@jalil What you mean by publishing it? I can just look up when you deployed the contract, find the ipfs-id. look up the metadata for all nfts, determine which is rare and which isn't and than mint there, with the semi-random thing I can just cancel out the transaction in the mempool if I do not like what randomIndex I got, its just gas intense.

Also making the baseUri non editable is a nice idea in theory, but what if your ipfs node doesnt get pinned at one point anymore and boom your data is gone or technology changes in the next years, you might want to change the baseUri.

@guyo13 the approach with the offset just has the one problem that you can not really give it a name such as "Cool Cat #123" because people will be confused since they will own tokenId #0 and metadata #123 > so on pages like rarible it will just show up as "untitled", like the bored apes do.

Yeah that solution is cool, but it messes up the beauty of matching tokenID to metadataID, no?

I am also keeping a setIPFSBase and setArweaveBase just in case.

Reasonable but i personally prefer no admin abilities at all...

What you mean by publishing it?

You can publish the smart contract including the CID without publishing the CID on IPFS. You create the hash locally before pinning it, so yes everyone will see the hash, but no one will know its' content until you publish it.

Also making the baseUri non editable is a nice idea in theory, but what if your ipfs node doesnt get pinned at one point anymore and boom your data is gone or technology changes in the next years, you might want to change the baseUri.

Here you are implying that you still have the metadata files and media assets lying around somewhere? In that case, you can just pin them again - the hash will be the same.
I think it's important/reasonable to regard the responsibility of pinning as the holders' responsibility, not the project creators responsibility. Same with the cryptopunks. The hash is in the contract, every owner has the responsibility to keep a copy of the punks.png to be able to verify the hash.

Yeah that solution is cool, but it messes up the beauty of matching tokenID to metadataID, no?

It would mess up the tokenID to metadataID matching. You could do it this way and then have setBaseUri option still in it, with an option to block it. That way the community could vote after if they want the metaData reuploaded so its matching correctly and then you block yourself from doing that again.

2 Likes

@jalil & @guyo13

A good solution I probably will go for is to post a provenanceHash in sha256 of all the combined sha256 hashes in the exact order into you smart contract beforehand and maybe even one of the metadata. Than you do semi-random minting without posting the metadata link than you add the baseUri, done. That way everybody knows you did not change the order and nobody can cheat either.

What you think?

@mhbln Regarding the mismatching ids in the item name - Yes thats one limitation but you can either opt for naming all the items the same or generating a name for each.

Personally, I opted for naming the items the same as the project, I think that giving them numbers isn't tidy - imagine 'My super cool NFT #XXX' - in my opinion kind of loses it's charm.

About your solution - This is exactly what I am going to do - you can see the.provenanceHash as an argument in one of the previous snippets.

I am still undecided whether people should be allowed to view the collection beforehand. @jalil proposed something interesting with pre-calculating the CID/deploying on a local offline node.

I might add a constructor argument mysteryNftUri to point to an out-of-collection IPFS identifier until the reveal period is over.

I wouldnt prepost it if i would you, then people maybe dont like this and that and takes away the surprise factor. Also prepost cid is good, but dont think its needed. You can just hash the metadata too and also post that. Every body then can just check if you changed it or not. On the end I think people care about the pictures and that you did not change the order

@guyo13 Sorry to necro this thread but I find it both fascinating and frustrating. I am trying to wrap my head around this (new to the space) and I have a couple questions if you could be so kind.

I have looked over some example contracts mentioned (BAYC per your example) and to be blunt I don't see how startingIndex, startingBlock or provenance even play a role in terms of the token generation or the metadata. When I look at sample ipfs metadata (ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/1598) it appears to be just be using the token index as the key for the metadata file and in turn the defined image.

You mention startingIndex as part of the mapping to the tokenId but where is that even utilized in the contract? The only means I could see would be to upload metadata after the sale and just manually change the order of items going under the baseURI based on the desired offset.

I am pretty sure I am asking a stupid question but you seem to understand the issue so I would deeply appreciate any insight you can share.

@guyo13 thank you for this post – it's been really interesting to read through all the comments.

Regarding one of your first comments:

Since the startingIndex is not known beforehand, it is not possible to generate the mapping between the tokenId and the initialSequenceId .
This leads the devs to upload the metadata only after the startingIndex has been set.

Why can the startingIndex not be known to devs beforehand? It could be assumed that the devs at least know their MAX_TOKENS, which is all that is necessary for defining a function that sets startingIndex. That startingIndex could be set in a random but transparent way, after the metadata has been prepared. If this startingIndex could be invoked only when baseURI is set, would it not make that original/initial sequence ID bijection meaningful? i.e. no man-in-the-middle by the devs possible since they only find out the startingIndex when everyone else does (at reveal)

Edit: fixed some wording on this because I asked my question incorrectly the first time

1 Like

I am still confused about the underlying premise here that points to a problem presented in high profile contracts like BAYC. If you look at the BAYC contract it appears that the entire startingIndex initialSequenceId argument is moot as it is not actually used in their contract (defined but the contract never actually uses the values in relation to the metadata). Even if you pull up the metadata for any of the minted tokens there is just a 1:1 mapping between tokenID and the metadata index.

I would love to be wrong here if someone can explain to me what i'm missing but the BAYC contract used in the original premise seems pretty straightforward. Following on that, it begs how much of an issue it is if BAYC made basically no attempt to hide the token to metadata relationship.

1 Like

@jalil interesting suggestion. how do you "dry run" the ipfs add to generate the CID without uploading in a deterministic way? it seems that even if you can mimc the way the CID is generated by IPFS, it is not guaranteed to be generated exactly the same way when the content is actually uploaded. thanks!

from ipfs add --help

Finally, a note on hash determinism. While not guaranteed, adding the same
file/directory with the same flags will almost always result in the same output
hash. However, almost all of the flags provided by this command (other than pin,
only-hash, and progress/status related flags) will change the final hash.

Looks like ipfs add --only-hash may do the trick – but it makes me uneasy that it's not 100% guaranteed to generate the same hash that will be used upon upload