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)
}
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);
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 })
}
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)
})()
If you take a look into console.log you should see an array of objects, representing Flovatars:
Not so bad, right? Well… let’s try to unwrap that metadata
field:
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 })
}
Finally
Let’s update our IIFE and try again:
(async ()=> {
const user = "0x2a0eccae942667be"
await getFlovatarsImproved(user)
})()
This invocation shall result in much smaller footprint:
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
- Example code - https://codesandbox.io/s/dev-to-fcl-05-list-flovatars-at-address-0bibcd - full working solution
- Cadence Structs - https://docs.onflow.org/cadence/language/composite-types/#composite-type-declaration-and-creation
- Flovatar GitHub Repository - https://github.com/crash13override/flovatar
- Flovatar Contract on Flow View Source - https://flow-view-source.com/mainnet/account/0x921ea449dffec68a/contract/Flovatar
Other resources you might find useful:
- Flow Docs Site - https://docs.onflow.org/ - More detailed information about Flow blockchain and how to interact with it
- Flow Portal - https://flow.com/ - your entry point to Flow
- FCL JS - https://github.com/onflow/fcl-js - Source code and ability to contribute to the FCL JS library
- Cadence - https://docs.onflow.org/cadence/ - Introduction to Cadence
- Codesandbox - https://codesandbox.io - An amazing in-browser IDE enabling quick prototyping
Top comments (2)
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?
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 :)