Skip to main content

Assembling characters

Pre-requisites

Before following this guide to assemble characters, Please make sure you have followed our getting started guide and set up your development environment.

You will also need to have an existing project on Honeycomb. If you don't have one, please see this guide.

Intro

Honeycomb has different ways to make characters. We've already covered how to create characters through wrapping. Now, we'll cover how to assemble characters.

The difference between the two is wrapping locks the NFT into a vault and creates a new Honeycomb character, while assembling gives you two other ways to create characters:

  1. Creating a character without an NFT. Using this method, your users will not need to own any NFTs to create characters. Characters created using this method are called native Honeycomb characters.
  2. Creating a character using an NFT. This method allows you to take the update authority of an NFT and create a new Honeycomb character from it.

Setting up

These are the mandatory steps before you can start assembling characters:

1. Create an assembler config

In order to assemble a character, you first need to create an assembler config. This config will mainly contain the information about the traits characters can have when they are made using this assembler config.

await client.createCreateAssemblerConfigTransaction({
project: projectAddress.toString(),
authority: adminPublicKey.toString(),
payer: adminPublicKey.toString(), // Optional payer
treeConfig: { // This tree is used to store character traits and their necessary information
// Provide either the basic or advanced configuration, we recommend using the basic configuration if you don't know the exact values of maxDepth, maxBufferSize, and canopyDepth (the basic configuration will automatically configure these values for you)
basic: {
numAssets: 100000, // The desired number of character information this tree will be able to store
},
// Uncomment the following config if you want to configure your own profile tree (also comment out the above config)
// advanced: {
// maxDepth: 20, // Max depth of the tree
// maxBufferSize: 64, // Max buffer size of the tree
// canopyDepth: 14, // Canopy depth of the tree
// },
},
ticker: "unique-string-id", // Provide a unique ticker for the config (the ticker ID only needs to be unique within the project)
order: ["Weapon", "Armor", "Clothes", "Shield"], // Provide the character traits here, example: ["Weapon", "Armor", "Clothes", "Shield"]
});

2. Add traits and their URIs

Each of the character traits you've added in the assembler config will need values and corresponding URIs. For example, if you have a trait called "Weapon", you can have values like "Sword", "Axe", "Bow", etc. and their corresponding image URLS.

await client.createAddCharacterTraitsTransactions({
traits: [ // Example traits given below, the labels have to match what you've declared in the assembler config
{
label: "Weapon",
name: "Bow",
uri: "https://example.com/bow.png",
},
{
label: "Weapon",
name: "Sword",
uri: "https://example.com/sword.png",
},
{
label: "Armor",
name: "Helmet",
uri: "https://example.com/helmet.png",
},
{
label: "Armor",
name: "Chestplate",
uri: "https://example.com/chestplate.png",
},
],
assemblerConfig: assemblerConfigAddress.toString(),
authority: adminPublicKey.toString(),
payer: adminPublicKey.toString(),
});

3. Create a character model

After creating an assembler config, you need to create a character model. It's worth noting that character models for assembled characters are different from wrapped ones.

await client.createCreateCharacterModelTransaction({
project: projectAddress.toString(),
authority: adminPublicKey.toString(),
payer: adminPublicKey.toString(), // Optional, use this if you want a different wallet to pay the transaction fee, by default the authority pays for this tx
config: {
kind: "Assembled",
assemblerConfigInput: {
assemblerConfig: assemblerConfigAddress.toString(),
collectionName: "Assembled NFT Collection",
name: "Assembled Character NFT 0",
symbol: "ACNFT",
description: "Creating this NFT with assembler",
creators: [
{
address: adminPublicKey.toString(),
share: 100,
},
],
sellerFeeBasisPoints: 0,
},
},
});

4. Create a characters tree

After creating a character model, you need to create a character tree. This tree is used to store characters and their necessary information, like who owns the character and if a character is on a mission.

await client.createCreateCharactersTreeTransaction({
authority: adminPublicKey.toString(),
project: projectAddress.toString(),
characterModel: characterModelAddress.toString(),
payer: adminPublicKey.toString(), // Optional, only use if you want to pay from a different wallet
treeConfig: { // Tree configuration, this affects how many characters this tree can store
basic: {
numAssets: 100000,
},
// Uncomment the following config if you want to configure your own profile tree (also comment out the above config)
// advanced: {
// maxDepth: 3,
// maxBufferSize: 8,
// canopyDepth: 3,
// },
},
});

To calculate the canopy depth, buffer size, and depth, use compressed.app. If you have any confusions about these values, please reach out to us on Discord or email.

Making a character

Make sure you follow all the previous steps before proceeding as they are required. Afterwards, you have two options to make a character:

  1. Without an NFT (Honeycomb native character)
  2. Using an NFT

Without an NFT (Honeycomb native character)

await client.createAssembleCharacterTransaction({
project: projectAddress.toString(),
assemblerConfig: assemblerConfigAddress.toString(),
characterModel: characterModelAddress.toString(),
charactersTree: charactersTreeAddress.toString(),
wallet: userPublicKey.toString(),
attributes: [
["Weapon", "Bow"],
["Armor", "Helmet"],
],
});

Using an NFT

The second method of creating a character using the assembler is by using an NFT. This method allows you to take the update authority of an NFT and create a new Honeycomb character from it.

You'll still need to follow steps 1 through 4 above. Aferwards, it's a two-step process:

  1. Populate assembleable character
  2. Wrap the character

1. Populate assembleable character

Populating an assembleable character will create a new character in an ejected state. This means that the character will be created, but it will not be usable (yet).

await client.createPopulateAssembleablCharacterTransaction({
project: projectAddress.toString(),
characterModel: characterModelAddress.toString(),
charactersTree: charactersTreeAddress.toString(),
mint: mintAddress.toString(),
owner: userPublicKey.toString(),
updateAuthority: adminPublicKey.toString(),
payer: adminPublicKey.toString(), // Optional, use this if you want to pay from a different wallet
attributes: [
["Weapon", "Bow"],
["Armor", "Helmet"],
],
});

2. Wrap the character

In order to use the character in Honeycomb, you need to wrap it. You can do so by sending a query like this:

await client.createWrapAssetsToCharacterTransactions({
project: projectAddress.toString(),
characterModel: characterModelAddress.toString(),
activeCharactersMerkleTree: charactersTreeAddress.toString(),
wallet: userPublicKey.toString(),
mintList: [ // Mint public keys as a string
mintAddress.toString(),
],
libreplexDeployment: libreplexDeployment.toString(), // Optional, only needed in case of LibrePlex NFTs
});

Other operations

Here are some other operations you can perform with the assembler.

Find characters

All of the variables in the find characters function are optional. You can use any combination of them as needed.

await client.findCharacters({
addresses: [], // String array of character addresses
includeProof: true,
filters: {}, // Available filters are usedBy, owner, and source
mints: [], // Array of NFT mint public keys as a string
trees: [], // Array of character model merkle tree public keys as a string
wallets: [], // Array of wallet public keys as a string (wallets that own the characters)
attributeHashes: [] // Array of attribute hashes as a string
});

Update assembler config

If you want to update the assembler config, you can do so by sending a query like this:


query CreateUpdateAssemblerConfigTransaction($updates: UpdateLayersInput!, $project: String!, $assemblerConfig: String!, $authority: String!, $payer: String) {
createUpdateAssemblerConfigTransaction(updates: $updates, project: $project, assemblerConfig: $assemblerConfig, authority: $authority, payer: $payer) {
blockhash
lastValidBlockHeight
transaction
}
}

Send the accompanying data like this.


{
"updates": {
"addLayers": [
{
"label": "string", // Label for the layer, example: "Weapon"
"traits": [
{
"name": "string", // Name of the trait, example: "Legendary Sword
"uri": "string" // URI of the trait, example: "https://example.com/legendary-sword.png
}
]
}
],
"removeLayers": "string" // Label for the layer to remove, example: "Weapon"
},
"project": "pubkey", // Project public key as a string
"assemblerConfig": "pubkey", // Assembler config public key as a string
"authority": "pubkey", // Project authority's public key as a string
"payer": "pubkey" // Payer public key as a string
}

Update a character's traits

If you want to update a particular character's traits, you can do so by sending a query like this:

await client.createUpdateCharacterTraitsTransaction({
project: projectAddress.toString(),
wallet: adminPublicKey.toString(),
assemblerConfig: assemblerConfigAddress.toString(),
attributes: [ // Send the updated attributes here in string tuple format
["Weapon", "Sword"],
["Armor", "Chestplate"],
],
characterAddress: characterAddress.toString(),
characterModel: characterModelAddress.toString(),
charactersTree: characterModelMerkleTree.toString(),
});

Unwrap a character

info

When a character made using the assembler is unwrapped, the NFT is sent to the user but the character is not deleted. The character will still exist on Honeycomb, albeit in an ejected state.

Unwrapping an asset means that the assets (NFTs/cNFTs) will be returned to the user's wallet. If it was a native Honeycomb assembled character (created without an NFT), a new NFT will be created and sent to the user's wallet.

await client.createUnwrapAssetsFromCharacterTransactions({
characterAddresses: [characterAddress.toString()], // String array of character addresses
project: projectAddress.toString(),
characterModel: characterModelAddress.toString(),
wallet: userPublicKey.toString(),
libreplexDeployment: libreplexDeploymentAddress.toString(), // Optional, only needed in case of LibrePlex NFTs
});

Rewrap an ejected character

info

When a character is rewrapped, the character's usedBy status on Honeycomb goes from "Ejected" to "None". The character will be usable again in Honeycomb.

await client.createWrapAssetsToCharacterTransactions({
project: projectAddress.toString(),
characterModel: characterModelAddress.toString(),
activeCharactersMerkleTree: charactersTreeAddress.toString(), // This is the tree that's currently active in the character model
wallet: userPublicKey.toString(), // The user must own all of the NFTs given in the mintList below
mintList: [ // NFT mint keys array as a string
mintAddress.toString(),
],
libreplexDeployment: libreplexDeployment.toString(), // Optional, only needed in case of LibrePlex NFTs
});