DEV Community

Max Daunarovich for Flow Blockchain

Posted on • Updated on

Build on FLOW | Learn FCL - 5. Get a List of NFTs Living at Known Address

Overview

In this guide, you will expand your knowledge on interacting with smart contracts via Cadence scripts. You will learn how to:

  • pull a list of NFTs stored on a specific account address (we’ll use Flovatar in this example!)
  • prepare and return them as an array of custom Structs

Previously on “Learn FCL”

In the previous post, we learned how we can interact with deployed contracts and how to return a custom Struct value from the script. We will build on that knowledge to execute a more complex procedure - fetching a list of NFTs stored on an account, mapping it into our preferred format, and then returning it back.

Let’s begin! 💪

What is Flovatar?

Flovatar is one of many amazing NFT projects built on Flow. Instead of letting you buy one of thousands unique NFTs, It provides you with tools to create your own identity matching your style and preferences! You would be able to add your personal touch to the final Flovatar persona, that will fit YOUR style 😁

Preparation

First things first, we should find the address where the contract is currently deployed. Flovatar’s GitHub has the addresses for deployed contracts on the front page:

  • Mainnet - 0x921ea449dffec68a
  • Testnet - 0x0cf264811b95d465

And in the get_flovatars.cdc file, the Flovatar contract has a handy method we can use called getFlovatars, that takes an address as an argument:



import Flovatar from **0x921ea449dffec68a**
pub fun main(address:Address) : [Flovatar.FlovatarData] {
    return Flovatar.getFlovatars(address: address)
}


Enter fullscreen mode Exit fullscreen mode

Step 1 - Installation

Add "@onflow/fcl": "1.0.0" as your dependency

Step 2 - Setup

Just like the last time, we will import the necessary methods and setup FCL:



// Import methods from FCL
import { query, config } from "@onflow/fcl";

// Specify the API endpoint - Mainnet
const api = "https://rest-mainnet.onflow.org";

// Configure FCL to use Mainnet as the access node
config().put("accessNode.api", api);


Enter fullscreen mode Exit fullscreen mode

Step 3 - Implement method getFlovatars



/// Get a List of Flovatars for Address
const getFlovatars = async (adddress) => {
    // inline code from above
    const cadence = `
        import Flovatar from **0x921ea449dffec68a**

        pub fun main(address:Address) : [Flovatar.FlovatarData] {
            return Flovatar.getFlovatars(address: address)
        }
    `

    // We need to pass single argument of type Address
    const args = (arg, t) => [arg(address, t.Address)];

    const flovatars = await query({ cadence, args });
    console.log({ flovatars })
}


Enter fullscreen mode Exit fullscreen mode

Step 4 - Give it a Try

Let’s make a call with our method. Our target will be Luca (creator of Flovatar) - his address is 0x2a0eccae942667be



(async()=>{
    const user = "0x2a0eccae942667be"
    await getFlovatars(user)
})()


Enter fullscreen mode Exit fullscreen mode

If you take a look into console.log you should see an array of objects, representing Flovatars:

Image description

Not so bad, right? Well… let’s try to unwrap that metadata field:

Image description

Now that’s SVG for you. While you can pull this data every time your user refreshes the page, ideally you would want to cache it somehow and query only the data you need.

Additionally if you query account with hundreds/thousands of NFTs you could hit the limit of the return value, which is around ~10Mbs.

If you go to Flovatar Showroom and use dev tools in your browser you can find out that all images are cached at https://flovatar.com/api/image/{id}. Let’s pull only NFT id and creatorAddress on metadata field. This way we can identify if the owner is an original creator of said NFT.

Step 5 - Rewrite Cadence script

As you remember from last post - we can create custom Struct, which will hold all the data we want to return. We will define Avatar struct with two fields: id and isCreator



/// Get a List of Flovatars for Address
const getFlovatarsImproved = async (adddress) => {
    // inline code from above
    const cadence = `
        import Flovatar from **0x921ea449dffec68a

        pub struct Avatar{
      pub let id: UInt64
      pub let isCreator: Bool

      // underscore in front of the argument allows us to pass unnamed arguments
            init(_ id: UInt64, _ isCreator: Bool){
        self.id = id
        self.isCreator = isCreator
      }
        }**

        pub fun main(address:Address) : [Avatar] {
            // Just like last time we will call "getFlovatars" method
            let flovatars = Flovatar.getFlovatars(address: address)

      // We will collect processed Flovatar data here
      let data: [Avatar] = []

      // Below is a simple loop over Flovatars collected by the contract code
      for flovatar in flovatars {
        let isCreator = flovatar.metadata.creatorAddress == address
        let avatar = Avatar(flovatar.id, isCreator)

                // append newly created instance of Avatar into resulting array
        data.append(avatar)
      }
      return data  
    }
    `

    // We need to pass single argument of type Address
    const args = (arg, t) => [arg(address, t.Address)];

    const flovatars = await query({ cadence, args });
    console.log({ flovatars })
}


Enter fullscreen mode Exit fullscreen mode

Finally

Let’s update our IIFE and try again:



(async ()=> {
    const user = "0x2a0eccae942667be"
    await getFlovatarsImproved(user)
})()


Enter fullscreen mode Exit fullscreen mode

This invocation shall result in much smaller footprint:

Image description

Contained information would be enough to construct a link (https://flovatar.com/flovatars/{id}) or image (https://flovatar.com/api/image/{id}) in your interface.

⭐Extra Challenge

Using your knowledge from .find identity resolver post modify your code to allow passing .find identity into getFlovatars method.

Resources

Other resources you might find useful:

Top comments (2)

Collapse
 
cmr_web3 profile image
CMR-Web3

Hi, in the Step 2, I saw that you identified mainnet as the api value. And then said in the next line // Configure FCL to use testnet as the access node
config().put("accessNode.api", api);

Do we always configure FCL to testnet?

Collapse
 
maxstalker profile image
Max Daunarovich

My bad, it was a typo 😅
For this specific example, we are configuring FCL to connect to mainnet Access Node. Simply because Flovatar contract is more widely spread on Mainnet and it's easier to find someone who have flovatars :)