Discussion: ICRC-99 and ckNFTs - Listing now on OpenSea!

I’m creating this post as a discussion point for the Grant that ICDevs.org is wrapping up around the concept of ckNFTs.

We are currently in the process of discussing the future of the tech we’ve written with a couple of other projects and will release the source code as soon as we have a clear path forward. In the meantime it is likely worth discussing a brief set of the functions and asking a few questions of the community/DFINITY as we move toward release.

Functionality:

As of now the the code lets you port an NFT off of an evm onto the IC. Each source contract on an EVM gets its own ckNFT canister that is a fully featured ICRC 7/37/3 NFT canister. Once the NFT is on the IC you can do all the normal NFT things with it.

Once on the IC the NFT can either be burned back to its native chain by an eventual owner, or cast to a remote EVM chain via the ICRC-99 cast standard that we’ve authored. This lets the canister hold the NFT in escrow while it is minted on a new chain and allowed to flow freely as an NFT on that chain.

For example, one can take a mainnet ethereum NFT, move it to the IC, sell it on an ICRC7-compatible marketplace. The buyer can move it to Base and list it on opensea with reduced fees.

The system makes extensive use of the EVM-RPC canister developed by DFINITY and allows configuration to use one or many RPC providers to provide enhanced security.

The first big question to answer would be what to call this thing. We’ll have the ICRC-99 standard and so we could just call it that. ckNFT would be nice, but to date we haven’t called anything “ck” that wasn’t developed by DFINITY. They’d need to weigh in on that I’d imagine and the community would need to generally get in line with what we want to call it.

Fun facts: ckNFT is a bit easier than your standard ckERC20 because you don’t need an input/output queue. Because things are ‘non-fungible’ you can kind of just keep them where they stand on their respective EVMs and keep track of who currently has access to potentially redeem those assets.

Annoying Fact: Gas is hard. I’m currently struggling with how to properly estimate gas and pass it through to the RPC canister. It seems I’m doing something horribly wrong as I can’t get contract creation transactions out of the mem pool despite setting(what seems to be) decent gas limits and prices. If anyone has done evm transactions and has figured out how to do generic calculations across evms, let me know as I’m about done with it all and could use some help.

I was able to take a bitfinity test net NFT: Testnet Bitfinity address details for 0x2dbCFA4f869DafaBc34b264064Cf0d79CA9973D2 | Blockscout
And cast it to my local replica here: Testnet Bitfinity transaction 0x7b32917aedba70c126303e6c62f8b540feb54e6b14499728bef4fdea59337b62 | Blockscout
I’m currently in no-mans-land trying to cast it to Base, but hopefully, I’ll get it soon enough and I’ll update this thread. Then onto mainnet.

I’m open to creative ideas and directions to take the code.

9 Likes

That sounds exciting—I hope it works out well for you.

icNFT maybe would be acceptable

1 Like

Ok! I finally got it to work(I was running into a nonce issue…I’ll have to work nonce sync into phase two of this)

I created an NFT Contract here on Bitfinity Testnet: Testnet Bitfinity address details for 0x2dbCFA4f869DafaBc34b264064Cf0d79CA9973D2 | Blockscout

Minted an nft…then I was able to use the IC to pull the item into an ICRC7 NFT on the local replica and cast it to Base here: Base Transaction Hash (Txhash) Details | BaseScan

Woo hoo! Now obviously I we have a ways to go to get it productized, but with the ICRC_99 functionality, you should be able to cast any ICRC7 NFT to other EVM chains. I haven’t written that demo, but will do so soon.

3 Likes

Main-net-ified!

An NFT Contract on Optimism: Contract Address 0x3c6d883e259d9eff09eb7c645bf72e0be140cd07 | OP Mainnet Etherscan

Minted an NFT: OP Mainnet Transaction Hash (Txhash) Details | OP Mainnet Etherscan

Moved it to an IC t-ecdsa Controlled approval address: OP Mainnet Transaction Hash (Txhash) Details | OP Mainnet Etherscan

Moved to the IC:
via a created ICRC-7 NFT Canister: https://dashboard.internetcomputer.org/canister/hsncy-tqaaa-aaaal-ar2eq-cai

And then moved to Base via a minted ERC-721 contract: Base Transaction Hash (Txhash) Details | BaseScan

And then minted: Base Transaction Hash (Txhash) Details | BaseScan

So now I have an optimism NFT on base and it is for sale on OpenSea at https://opensea.io/assets/base/0x8b7a44b98b270b9036513703dad1e28784b8fc55/0

4 Likes

Hey @skilesare,

thanks for posting all the information. I think this is in general a very interesting topic, but despite of having the technical possibility and showcase how NFTs could be transferred across chains, did you think about specific use cases where this is needed? I would love to hear your thoughts about this.

While this is a possibility, I highly doubt this will happen unless you have NFT marketplaces that have an aggregated overview about NFT collection details which includes:

  • where was the NFT originally minted and ultimately resides?
  • full-trace of sale history across all chains and marketplaces where the NFT was moved/sold

I am very curious on this topic as I had plans in the past to create such aggregated NFT marketplace (independent of ICP). But in the end we did not continue moving into this direction, mainly due to the lack of a reliable bridge though. And of course you need to build a lot of services to get this aggregated view incl. the full trace done correctly.

While you were building this, did you happen to learn how to get a canister to derive a base chain address from the evm rpc?

I’m using the edcsa with Key_1 for mainnet and dfx_local_key for keys, but I feel like I’m missing a critical step in calling the evm/bass address. Since you successfully sent/ signed a base tx you may know.

I have some motoko which produces

dfx canister call wallet_canister getEvmAddress

But it returns an error.

It’s part of a much larger file and I’m considering chunking it out into a cansiter which ONLY derives and evm address at first. I’m not to familiar with how evm works, and I think I’m chasing rabbits.

You get the evm address from the system canister and the tecdsa function calls. What you get back is a compressed public key(if I remember correctly). You need to do the decompress->keccak256->fiirst 20 character dance to get the addresses. I’ll send you some code.

1 Like

We’ve written the icrc99 standard draft that we will put out. This outlines how to derive and retrieve the various address that are needed to compile a full cross chain history.

1 Like