Accessing Collections in Cadence 1.0

Hey Guys,

I am trying to get the NFT data after the Cadence 1.0 upgrade.

In Cadence 0.42 I could use the getCapability.borrow method to get a collection, and use that collection’s .borrowMomentNFT(id) to get the NFT, and pass that to the MetaDataViews contract to get some data I was interested in.

I understand that the getCapability.borrow method has been changed to account.capabilities.get. But in NonFungibleToken contract the CollectionPublic interface had been removed, and I cannot find its successor. How should I go about getting an NFT if I have an nft_id?

Thanks,

Robert

You can do something like this.

import NonFungibleToken
import ExampleNFT 

access(all) fun main(addr:Address, id:UInt64): &ExampleNFT.NFT  {
    let account = getAccount(addr)
    let capRef = account.capabilities.borrow<&ExampleNFT.Collection>(ExampleNFT.CollectionPublicPath)!
    let nft = capRef.borrowNFT(id: id)!
    return nft
}

Or you can check here: flow-nft/transactions/scripts/borrow_nft.cdc at master · onflow/flow-nft · GitHub

Thanks! Great resources, I don’t know how I missed them.

I was trying to implement the borrow_nft.cdc, and specifically the following code in my script:

let collectionRef = account.capabilities.get<&{NonFungibleToken.Collection}>(
            collectionData.publicPath
            ) ?? panic("The account ".concat(address.toString()).concat(" does not have a NonFungibleToken Collection at ")
                        .concat(collectionData.publicPath.toString())
                        .concat("The account must initialize their account with this collection first!"))

but got an error:
cannot apply binary operation ?? to left-hand type

This should be occurring if on the left-hand side the variable was not an optional. But, to my knowledge, account.capabilities.get returns an optional.

Not sure what I am missing here. Also, if this works in the example script, why doesn’t it work for me? This should be pretty standard stuff.

Thanks for highlighting this @rgne and apologies for the oversight. Originally, .get did return an optional but was updated to return a non-optional.

I confirmed that documentation still reflects the original implementation and have created an issue in the Cadence repo to correct the error.

As for your script, if you do need to access a Capability, you can use .get without the optional guard, but I would advise checking the returned Capability to confirm that it’s valid. This would look like

let collectionCap = account.capabilities.get<&{NonFungibleToken.Collection}>(
            collectionData.publicPath
            )
assert(collectionCap.check(), message: "Invalid Capability retrieved")

If you only need a reference to the collection, you can use the script mentioned by @DappCoderr borrow_nft.cdc which uses .borrow. Just know that this method does return an optional so the guard would be necessary to unwrap the optional.

Thanks @gio_on_flow for the reply. With the account.capabilities.get I was able to borrow the capability, and I wanted to get a moment’s metadata like this:
let nft = collectionRef.borrowNFT(id: nft_id)
but no matter what I tried, I always get an error message:
“Error: value of type 'Capability<&{NonFungibleToken.Collection}>' has no member 'borrowNFT'”.
Of course, the NonFungibleToken contract’s Collection has a function borrowNFT() with public access.

With the account.capabilities.borrow I got the old error message, that said:
“error: Execution failed: error: value of type '...Collection' cannot be exported”

Still not sure what I am missing.

@rgne capabilities.get will get you a Capability, in your case a Capability<&{NonFungibleToken.Collection}>. In order to call methods on the collection, you need to borrow a reference to the underlying resource from the capability you retrieved.

If you already have a capability, you can call .borrow() which will return an optional reference to the resource the capability is linked to.

If you only need a reference to the collection, you can use the script mentioned by @DappCoderr borrow_nft.cdc which uses .borrow

Or as in the borrow_nft.cdc script, you can just borrow the reference from the capability directly. If you revisit that script, you’ll notice that capabilities.get() isn’t called. Rather, this expression gets a reference to the collection directly without first getting the capability.

let collectionRef = account.capabilities.borrow<&{NonFungibleToken.Collection}>(
  collectionData.publicPath
) ?? panic("The account ".concat(address.toString()).concat(" does not have a NonFungibleToken Collection at ")
    .concat(collectionData.publicPath.toString())
    .concat(". The account must initialize their account with this collection first!"))

The above lines are equivalent to

// first get the Capability
let collectionCap = accout.capabilities.get<&{NonFungibleToken.Collection}>(
  collectionData.publicPath
)
// then borrow a reference, reverting if the capability is invalid
let collectionRef = collectionCap.borrow()
  ?? panic("The account ".concat(address.toString()).concat(" does not have a NonFungibleToken Collection at ")
    .concat(collectionData.publicPath.toString())
    .concat(". The account must initialize their account with this collection first!"))

Thanks @gio_on_flow for the clarification. When reviewed the changes in Cadence 1.0, I thought that the capabilities.get takes care of the whole borrowing. Thanks for the clarification.

Unfortunately, when I try what you have suggested [capabilities.get + borrow(), or capabilities.borrow], I get the following error:
“error: Execution failed: error: value of type '...Collection' cannot be exported”

I won’t take up your time any more, I will dive into the documentation, and the into the lessons, there is clearly something in the changes that I don’t understand.