Grant proposal for SNIP721 work

I am planning on submitting a grant application to develop a SNIP721 reference implementation, so I am creating this topic to get some community feedback.

My thoughts on implementation are that upon instantiation, the token creator would configure whether:

  1. Ownership is public/private (default: private)
  2. Additional private metadata is enabled/disabled (default: enabled)
  3. That private metadata is/is not sealed (default: disabled)
  4. Burn is enabled/disabled (default: disabled)

It will have public metadata, and the enabling of private metadata will be in additional to the public, so that a token creator can have a mix of public/private data. Enabling sealed metadata means that the private metadata is not viewable by anyone, not even the owner, until the owner calls a Reveal function. When the sealed metadata is Revealed, the private metadata will irreversibly be moved to the public metadata. This will simulate being able to buy/sell wrapped cards that can have different rarities, although the actual card purchased is unknown until you unwrap the card. To be clear, I don’t believe that Seal/Reveal should be in the SNIP721 spec, but I think it is a nice additional feature to put in place with the reference implementation.

The owner of a token will be able to whitelist addresses to have permission to:

  1. View the owner of the token
  2. View the token’s private metadata
  3. Transfer the token

When whitelisting addresses, the owner can choose whether an address has the specified permission on just a specific token or on all of the owner’s tokens.

An optional, private memo can be included with any mint, transfer, or burn transaction, and only addresses involved in the transaction will be able to view that transaction (and its memo) in their transaction history

Along with creating the reference implementation, I will create a SNIP721 toolkit package similar to what I’ve already created for SNIP20 (secret-toolkit/packages/snip20 at master · enigmampc/secret-toolkit · GitHub) so that contract devs can easily interact with SNIP721 contracts.

I will also rewrite the SNIP721 spec. The current spec is just copy/pasted pieces of the cw721 spec and the SNIP20 spec, so it is inconsistent in how it specifies parameters for different functions. And the pieces that are copied from the cw721 spec do not take into account the possibility of private ownership or metadata. I suspect as the initial snip721 implementation gets feedback, the requirements/features of the SNIP721 spec will evolve as well.

I’m not sure whether the following future work should be included in the same grant application as milestones or submitted as separate applications, but once the SNIP721 reference implementation/toolkit/spec is finished, I will update the auction contracts so that you will be able to auction SNIP721 NFTs, and I am considering adding the capability of creating a special type of an NFT auction where the auction creator can specify a specific NFT they want in return. So the auction can facilitate a swap of two different NFTs. I am also considering adding the ability to specify a set of NFTs that are wanted in the trade, which would not be completed unless the “bidder” is able to provide all the components. This would act like being able to trade one rare game-related NFT for several other NFTs in the game that aren’t as rare/powerful.

Also, because I expect there could be more bidders on NFT auctions than for SNIP20 auctions/OTC trades, I will probably want to update the auction contracts to overcome the gas-limit issue. Because SNIP20s don’t have a batch send function, each losing bid has to be returned with a separate message. I will probably update the auction contracts so that when an auction is closed, it will determine and lock-in the winning bid, but return the losing bids in blocks of 50-60. So besides active and closed auctions, I’ll need to add a pending state for auctions that have closed, but haven’t returned all the bids yet.

Another future task I would like to do is create a UI where someone can easily instantiate a new SNIP721 contract. The UI would also allow them to perform admin commands on their contract (disable transfers, whitelist minting addresses, etc…), as well as provide an easy way to mint new NFTs. Because most NFT creators won’t want to code their own contracts, the UI should have the ability to not only specify the metadata uri, but also help create the uri so that it has the different fields the user needs. It would probably be best if the UI also hosted the uri page for the creator, instead of the creator needing to host it themself, but if providing hosting service is will be part of the UI, the funding will need to reflect those costs. If the metadata uri information is formatted according to the UI’s specifications, the UI will also be able to display all the relevant token data, like name, description, image, attributes, etc…as well as handle the ability to display any of the private information authenticated by viewing key

Again I don’t know whether all of those should be considered different milestones in one application or if each should be a separate application. So I guess I’m hoping to get some feedback on what people like/dislike about the proposed implementation, whether only the reference implementation/toolkit package/spec update should be included in the (first) application, and also what people thought would be a fair asking amount for the work.

And thank you to anyone who read through all of that to get to this point!


My 2 cents is that supporting baedrik is vital work for our ecosystem.


I am behind this proposal. I think secretNFTs are important for our ecosystem. I have a couple of questions to see whether the current proposal can address these situations:

  1. Say a game on Ethereum wants to offer wrapped cards or a lootbox to players, how would that work with the current proposal? This I believe is more around EIP-1155 where you have multiple (but limited) amount of the same NFT. Would Alice on ETH unwrap the card pack or lootbox (metamask TX), the secretNFT contract is triggered to determine what NFT Alice owns and Alice can choose to keep that NFT in SN (auction it etc.) or bring to ETH to use it (assuming we add SNIP721 / erc721 functionality to the bridge)?

  2. Say I want to build a game where only 50 (x) players are allowed. They each do a buy in (fixed amount) and get a card. These cards are numbered (SNIP721 or SNIP1155). Every hour a random number from 1-50 is drawn and the card that corresponds to that number loses the chance to win. This gets interesting, when a several cards are eliminated. Say I paid 10 SCRT to potentially win 500. 25 people already lost. Now my expected return from that card is 20SCRT, so I may decide to sell that card w/o waiting for the end of the game. This can be a cool mechanism to add to the auction contract

Wrt. different parts of the proposal, the ideas around future work seem relevant enough that I think this can be condensed into a single proposal. Maybe for the UI work, it may make sense to partner with someone who’s a front-end developer, similar to what we did with the Auction work :slight_smile:


Although there might be different implementations, I believe the normal way ERC721 loot boxes are done is that you buy the “wrapped” token, and when you unwrap it, it burns the original token and then mints your item token.

How I envision an ERC721<->SNIP721 bridge to work would be that when it receives an ERC721, it would mint a SNIP721 token, copying the ERC721 metadata (and likely give it an ID that is a combination of the eth contract and the ERC721 token ID). So on the SN side you could auction it, transfer it, etc… and if anyone wanted to get the original ERC721, they would send the SNIP721 to the bridge and the bridge would release the corresponding ERC721 to the given address.

So if Alice sent a wrapped lootbox token across the bridge to SN, the bridge is now the owner of the original ERC721, so she could no longer unwrap it. You wouldn’t want her to retain ownership of the original ERC721 once she sends it across the bridge or anyone that buys the SNIP721 would not be guaranteed to be able to redeem it for the original ERC721.

So if a lootbox token is ever sent across the bridge you don’t ever need to worry about the SN version needing to be notified that it was unwrapped. Because they represent the same thing, if anyone owns the SNIP721 version they know the original is locked in the bridge waiting for them to redeem for it.

The proposed implementation could easily be used for this. There would be several ways to do this, but I would probably implement this as a contract (lets call it the raffle contract to differentiate it from the token contract) that you instantiate telling it how many players you want, how much the buy-in is, and what time interval (or number of blocks) you want people to wait between losing tokens being picked. As people send in the buy-in, they will get added to a list of players. Once the number of players meet the wanted amount, the contract will instantiate a SNIP721 token contract. It will mint one token to each player, just giving them token IDs 1…n. Or if you think people would enjoy it, when they send the buy-in maybe they could specify the “lucky” name they want their token to have. After the time interval passes, anyone can call the raffle contract to reveal the next losing token ID, and then it would reset the time until that function can be called again. Anyone could query the raffle contract for the address of the token contract it is using, a list of known losing token IDs, the number of tokens still in the game, and the expected EV of each token (prize pool / number of tokens left).

And once the auction is SNIP721 ready, you would be able to sell the token there. The seller could use the auction description to say which raffle (contract address) it came from, and a potential bidder would be able to query the raffle contract to verify that the address of the token being sold matches the address that the raffle is using for tokens, and that the token ID being sold is not one of the losing IDs, so they can verify it is still a contender to win.

Once all the losing IDs have been drawn, whoever sends the token with the winning token ID will immediately be sent back the prize pool. And if people wanted to burn their losing token, they would have the option to send it to the raffle contract at any time after it was determined to lose (as a precaution against user error, before a winner is determined, the raffle contract will return any token it it is sent that isn’t in the losing list). Or if people want the souvenir they can just keep the losing token.
As a bonus, maybe the raffle creator wanted to include something in the token’s metadata uri so people who lose the raffle get some sort of keepsake

Totally agree. I just didn’t want to speak for anyone not knowing their interest level or schedule. But if no one is available or interested, I’d be willing to do the UI work too.


Thank you! I appreciate the kind words!

As a side note. The proposed implementation makes the lootbox example even cleaner if a SN-based game decides to to do it.

Instead of having to burn the “wrapped” token to trigger the minting of the item, the Seal/Reveal functionality would allow the game creator to mint the item immediately. The metadata would be sealed in the private metadata portion of the SNIP721 where no one, not even the owner can view it. At the same time, the public metadata could contain something that indicates that the hidden item properties are of at least a certain rarity, so prices can reflect that. (Maybe an image where the color of the treasure chest is colored differently for each level of rarity)

Then when someone calls Reveal and breaks the seal, it just makes public the properties that were always there

1 Like

Interested in doing the ui work for this.


Is the metadata stored on chain?

If the minter can pre-determine the contents of the NFTs then the minter could open the most valuable NFTs themselves right?

Maybe there needs to be some VRF so that the minter does not even know. Each NFT has pre-determined probabilities of each item but the items inside are not pre-determined.

For the auction side of things I am not sure if that is part of the ERC-721 spec right?

Auction would require an escrow smart contract that may be out of scope for this (not sure if that is already the case)


Overall I love this! I think there are some finer detail to outline but I think this is a great start.

In order to reduce the cognitive load I would recommend to keep this focused entirely on the ERC-721 part of the task and keep the Auction, UI stuff separate.

A single application linking to multiple deliverables might work well


The metadata will follow the same guidelines as the cw721 spec. The plan is to have SNIP721 also follow the cw721 spec in order to maintain cross-chain compatibility, but with privacy related additions that SN can enable. So both the public and the private metadata will have most information off-chain and will have the form of:

    /// Identifies the asset to which this NFT represents
    pub name: String,
    /// Describes the asset to which this NFT represents
    pub description: String,
    /// "A URI pointing to a resource with mime type image/* representing the asset to which this
    /// NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect
    /// ratio between 1.91:1 and 4:5 inclusive.
    pub image: Option<String>,

So the expectation is that the image field would contain a uri pointing the the JSON that would describe all the application specific properties of the NFT. Because the minting process for each application could have infinite variation, my intent for the reference implementation would be to only offer the function to mint the token with the supplied information. This would leave it up to the application to determine how the minting contract will randomize item generation when it calls the mint method. They could randomize the properties when it generates the uri that the metadata will point to, or they can have pre-generated uri pages, and randomize which uri gets sent as input when it calls the mint method. I plan to leave those implementation details to the app developers.

While I would expect a game contract to be written to fairly randomize the contents, if people are just wanting to generate their own randomly sealed NFTs outside of a contract-driven game, I think that might be a great thing to add to the UI. Give people the option to create a bunch of uri for all the items they want, and then specify an option that would randomly select one when they mint, and not even display to them what was chosen.

That is correct, the auction related action items would not be part of the spec. I would be modifying the OTC auction contracts I’ve created for selling SNIP20s to handle SNIP721s.

Thank you for the encouragement and the feedback!

I am starting to think I might first just make the proposal for the reference implementation/toolkit package/spec re-write, since the other things can’t really start until the reference implementation is done, and the things involving UI will likely incorporate other devs. Because there are a number of things I’ve been approached about working on that might have some possibility of temporarily jumping the priority queue, I probably wouldn’t want to make commitments impacting other people’s schedules until at least the reference implementation is done anyway.

Yes, this is my ultimate vision of tying the auction/swap with the display/mint UI. I see that basically being the Secret Network version of OpenSea with privacy-capable NFTs!

Thanks again for the feedback!


The metadata will follow the same guidelines as the cw721 spec. The plan is to have SNIP721 also follow the cw721 spec in order to maintain cross-chain compatibility

My only question is that, would be possible to move this NFT across the cosmos chain as an IBC data layer? i.e. in an encrypted format, but to settle anything about the NFT a callback has to be made or settles on the SN network?

Or maybe i am thinking of it wrong :thinking:

I honestly don’t know what the capabilities of IBC will be. I would guess that it includes the ability to call contracts on a different, compatible chain, but I doubt that it will provide input/output encryption, so odds are that if you are accessing private data from a secret contract and doing that from another, public chain, you will probably be making all your inputs/outputs public by using the public chain.

But that said. I think it would still be good to comply with cw721 specs so that there can be some standard to follow so an app could call any 721-style NFT contract on any of the IBC compatible chains.

IF IBC will allow you to specify addresses on different chains, then you wouldn’t be “moving” the NFT to another chain as much as just having the secret contract store that the owner is an address beginning with a prefix that signifies the other chain. Again, I don’t know if that is how IBC will work or not…

1 Like

This is cool! I’m new to Secret, but I have an NFT project on Cosmos that’s encountered some of these issues.

One thing to think about that I don’t see explicitly is when offering a trade for an NFT, specifying who the issuer of the NFT is, so if you want to trade across card sets you can do it safely.

The idea of unwrapping is a fun one! Would you bind any randomness to creation, or put it in the unwrap process?


@MikeSofaer welcome to the Secret Forum and thanks for your feedback!

Can you please elaborate on this?


So in Pylons you can specify a trade offer for an item of some type, with values in certain ranges, but of course you have to make sure that the creator is the correct one so you’re not just getting a copycat item from someone else. I know this is probably obvious to everyone but we missed it on our first pass.

Thank you for the info/heads-up! My initial thoughts were that the auction should be able to accommodate any contract that complies with snip721. So (assuming I move forward with the trade functionality as opposed to just selling an NFT for a snip20) when a person creates a “trade auction” for their snip721 NFT, they would explicitly be specifying the contract address and token ID of the NFT they were willing to trade for (or possibly a set of token IDs that are acceptable). The UI would probably have some of the most popular NFT contract addresses in a drop down, much like the current OTC auction has commonly used snip20s readily selectable. Specifying the contract address (either manually or through a drop down list) guarantees it is the NFT you want, since no one can “forge” the address if trying to make a counterfeit, and there will only be one token on that address with the ID you specify.

The idea of blindly trading for a rarity level is interesting though. While the snip721 spec shouldn’t include it as part of the base requirements, I think it would be interesting to have some sort of accepted supplemental standard where a query would return a rarity level of either common, uncommon, or rare depending on that specific item’s generation probability. There would need to be some sort of verification that the contract is honestly reporting rarities that fall in the standard’s probability cutoff ranges, though, to prevent someone from writing a contract that always reports every card is rare. But if that contract address (card set) was verified to honestly report rarity, that would be a cool option to say you will trade for any rare card from that address (card set)

1 Like

I love this especially the swapping of NFT for NFT the ethereum DEX Airswap has this facility if you paste the erc721 contract and token ID in. I have successfully swapped one NFT for another (but had problems with some bespoke contracts ie KnownOrigin/Superrare). An additional level of awesomeness would be if you could wrap several NFTS to swap a number of lower value NFTs for another high value NFT but I know that is outside the SNIP721 specs ERC988 supports parent and child NFTS so perhaps there will be a SNIP988 in the future.

1 Like

Personally I like the idea of a contract performing the item randomization when minting, instead of unwrapping. I think it more closely resembles the real life opening of a booster pack. The randomization happened when the pack was manufactured and sealed, and that specific pack was going to yield the exact same cards no matter when/where it gets opened (if anyone wants to draw schrodinger’s cat references here, I’m happy to engage, but please reserve it for a different thread/medium lol).

But I don’t plan on incorporating random item generation into the base reference implementation. I think each individual app/game would want to control how randomization is done, so the reference implementation would just be allowing them the opportunity to randomize an item as best as they see fit in their own contract, and just specify that random item’s metadata when they create the token, knowing that no one can see what that random item is until an owner unwraps it.

If you are getting privacy/secrecy for free then this makes sense. Since it’s mathematically the same to roll the dice at unwrapping time, it could potentially be easier not to try to carry secret but determined results around in the token. I don’t know the Secret protocol but I expect maybe it’s free for you.

All the voiced interest in swaps make me think the NFT auction should definitely include that functionality (I was just originally considering it not knowing the interest level). Even before a snip988 arrives, I could see implementing the “trade-auction” where you can either specify the specific token you want in exchange, a wishlist where any one in the specified set will trigger a swap, or a set where
all must be included to trigger a swap (indicating a willingness to swap for a combination of lower value NFTs). Maybe even make that reversible where you are offering up for sale a certain set of NFTs. I can’t make any promises regarding the use of sets of NFTs, since that adds complexity to the UI, which will be worked on by others, but I would be willing to work sets into the secret contracts as long as all tokens in the set belong to the same contract

1 Like

That’s a great point. It really is just about trying to make the functionality easier for the app that might need it.

If they are going to modify the base reference implementation anyway, then they will just be coding it to randomize at whichever point they want, so they’ll probably modify the Seal/Reveal code anyway.

But if a game contract dev found it easier to code a game contract that would just instantiate and manage a base implementation contract, without having to modify it, then they could just randomize when the game contract mints, and not have to worry about the token contract needing to call their game contract when someone unwraps.

I honestly don’t know if someone that wants to utilize seal/reveal functionality will be more likely to code their own variation, or use the existing implementation.

Perhaps it’s not something that even belongs in the base reference? It’s not going to be part of the snip721 spec. I just thought it could be useful if people didn’t want to code it themselves