Skip to main content

Add a resource and crafting system

Introduction

Honeycomb Protocol lets developers easily add a crafting system to their apps/games with just a few lines of code.

All the resources that your users mine or craft will be stored on the blockchain, and, besides being used in your app, can be traded or sold on marketplaces.

This guide will show you how to get started with Honeycomb Protocol in your apps/games.

Prerequisites

Before starting this guide, please make sure to setup your development environment.

Step by step guide

1. Create a project

A project in Honeycomb represents an app/game you want to use with Honeycomb Protocol. To create a project, use the following code:

import { client, sendTxns } from "@/utils"; // Import the client and sendTxns functions (covered in the dev environment setup)

const {
createCreateProjectTransaction: {
project: projectAddress, // Save this project address in your database, you'll need it to interact with the Web3 side of your app
tx: txResponse, // This is the transaction response you'll need to send to the blockchain after signing
},
} = await client.createCreateProjectTransaction({
name: "My First Project",
authority: authorityPublicKey, // The wallet public key of the authority, the authority has complete control over the project
payer: payerPublicKey, // The wallet public key of the payer, the payer pays for the transaction fees
profileDataConfig: {
customDataFields: [ // Specify an array of custom data fields that you can set on your users' profiles
"bugsReported"
]
}
});

await sendTxns(wallet, [txResponse]); // Send the transaction to the blockchain

2. Create a profiles tree

On Solana, when you use compression to store data, you need to use merkle trees. Honeycomb Protocol uses a merkle tree to store user profiles. To create a profiles tree, use the following code:

import { client, sendTxns } from "@/utils"; // Import the client and sendTxns functions (covered in the dev environment setup)

const {
createCreateProfilesTreeTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
} = await client.createCreateProfilesTreeTransaction({
payer: adminPublicKey.toString(),
project: projectAddress.toString(),
treeConfig: {
// 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 profiles this tree will be able to store
},
advanced: {
maxDepth: 20,
maxBufferSize: 64,
canopyDepth: 14,
}
}
});

await sendTxns(wallet, [txResponse]); // Send the transaction to the blockchain

3. Create Web3 profiles for your users

Whenenever a new user comes to your app/game, you can create a new Web3 profile for them using the following code:

import { client, sendTxns } from "@/utils"; // Import the client and sendTxns functions (covered in the dev environment setup)

const {
createNewUserWithProfileTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
} = await client.createNewUserWithProfileTransaction({
project: projectAddress.toString(),
wallet: userPublicKey.toString(),
payer: adminPublicKey.toString(),
profileIdentity: "main",
userInfo: { // Optional
username: "Honeycomb Developer",
name: "John Doe",
bio: "This user is created for testing purposes",
pfp: "https://lh3.googleusercontent.com/-Jsm7S8BHy4nOzrw2f5AryUgp9Fym2buUOkkxgNplGCddTkiKBXPLRytTMXBXwGcHuRr06EvJStmkHj-9JeTfmHsnT0prHg5Mhg",
},
profileInfo: { // Optional
bio: "John Doe's Bio",
name: "John Doe",
pfp: "link-to-pfp"
}
});

await sendTxns(wallet, [txResponse]); // Send the transaction to the blockchain to create the user

4. Create a resource

A resource in Honeycomb can be any on-chain item that can be mined or crafted.

To create a resource on Honeycomb Protocol, use the following code:

import { client, sendTxns } from "@/utils"; // Import the client and sendTxns functions (covered in the dev environment setup)

const {
createCreateNewResourceTransaction: {
resource: resourceAddress, // This is the resource address once it'll be created
group: resourceGroup, // This is the resource group, can be null if the resource is fungible, in case of non-fungible resources, this instruction will create a resource group and return its PDA address (save the group address in your database along with the actual resource address)
tx: txResponse, // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
},
} = await client.createCreateNewResourceTransaction({
project: projectAddress,
authority: adminPublicKey.toString(),
delegateAuthority: delegateAuthorityPublicKey.toString(), // Optional, resource delegate authority public key
payer: adminPublicKey.toString(), // Optional, specify when you want a different wallet to pay for the tx
params: {
metadata: {
name: "Iron Ore", // The name of the resource
symbol: "IOR", // The symbol of the resource
uri: "https://example.com", // URI for the resource's metadata
},
// Use either fungible, to create a fungible resource, or inf, to create a non-fungible resource
fungible: {
decimals: 9,
},
inf: {
characteristics: [],
supply: 1000000000,
}
}
});

await sendTxns(wallet, [txResponse]); // Send the transaction to the blockchain

5. Create a resource tree

By default, a lot of resources in Honeycomb Protocol are compressed in order to maximize cost savings.

Merkle trees are used to store resource ownership records, with each leaf representing an ownership record. Each time a resource that relies on merkle trees is minted, a new leaf is added to its available merkle tree.

import { client, sendTxns } from "@/utils"; // Import the client and sendTxns functions (covered in the dev environment setup)

const {
createCreateNewResourceTreeTransaction: {
tree: merkleTreeAddress, // This is the merkle tree address once it'll be created
tx, // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
},
} = await client.createCreateNewResourceTreeTransaction({
project: projectAddress.toString(),
authority: adminPublicKey.toString(),
delegateAuthority: delegateAuthorityPublicKey.toString(), // Optional
payer: adminPublicKey.toString(), // Optional, specify when you want a different wallet to pay for the tx
resource: resourceAddress.toString(),
treeConfig: {
// 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 resources this tree will be able to store
},
advanced: {
maxDepth: 20,
maxBufferSize: 64,
canopyDepth: 14,
}
}
});

6. Mint the resource

When your users perform certain actions in your game or app, like mining, you can reward them with resources. To mint a resource, use the following code:

import { client, sendTxns } from "@/utils"; // Import the client and sendTxns functions (covered in the dev environment setup)

const {
createMintResourceTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
} = await client.createMintResourceTransaction({
authority: adminPublicKey.toString(), // Project authority's public key
owner: userPublicKey.toString(), // The owner's public key, this wallet will receive the resource
delegateAuthority: delegateAuthorityPublicKey.toString(), // Optional, if specified, the delegate authority will be used to mint the resource
payer: adminPublicKey.toString(),
resource: resourceAddress.toString(),
params: {
// Use either fungible, if the resource is fungible, or inf, if the resource is non-fungible
fungible: {
amount: "500",
},
}
});

await sendTxns(wallet, [txResponse]); // Send the transaction to the blockchain

Let's say you have a fungible resource called iron ore, and you want to reward players with a random amount of iron ore when they mine. You can use the following code server-side:

import { client, sendTxns } from "@/utils"; // Import the client and sendTxns functions (covered in the dev environment setup)

const {
createMintResourceTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
} = await client.createMintResourceTransaction({
authority: adminPublicKey.toString(), // Project authority's public key
owner: userPublicKey.toString(), // The owner's public key, this wallet will receive the resource
delegateAuthority: delegateAuthorityPublicKey.toString(), // Optional, if specified, the delegate authority will be used to mint the resource
payer: adminPublicKey.toString(),
resource: resourceAddress.toString(),
params: {
// Use either fungible, if the resource is fungible, or inf, if the resource is non-fungible
fungible: {
amount: String(Math.floor((Math.random() * 5) + 1)) // Will mint a random amount of iron ore between 1 and 5
},
}
});

await sendTxns(wallet, [txResponse]); // Send the transaction to the blockchain

7. Create a crafting recipe

Let's say you want to create a crafting recipe that allows users to smelt iron ore into iron bars. Use the following code to do so:

First you'll need to define iron bars as a resource with its own merkle tree(s).

import { client, sendTxns } from "@/utils"; 

// First, create the resource
const {
createCreateNewResourceTransaction: {
resource: resourceAddress, // This is the resource address once it'll be created
group: resourceGroup, // This is the resource group, can be null if the resource is fungible, in case of non-fungible resources, this instruction will create a resource group and return its PDA address (save the group address in your database along with the actual resource address)
tx: txResponse, // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
},
} = await client.createCreateNewResourceTransaction({
project: projectAddress,
authority: adminPublicKey.toString(),
delegateAuthority: delegateAuthorityPublicKey.toString(), // Optional, resource delegate authority public key
payer: adminPublicKey.toString(), // Optional, specify when you want a different wallet to pay for the tx
params: {
metadata: {
name: "Iron Bar", // The name of the resource
symbol: "IBR", // The symbol of the resource
uri: "https://example.com", // URI for the resource's metadata
},
// Use either fungible, to create a fungible resource, or inf, to create a non-fungible resource
fungible: {
decimals: 9,
},
inf: {
characteristics: [],
supply: 1000000000,
}
}
});

await sendTxns(wallet, [txResponse]);

// Then create the resource tree
const {
createCreateNewResourceTreeTransaction: {
tree: merkleTreeAddress, // This is the merkle tree address once it'll be created
tx, // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
},
} = await client.createCreateNewResourceTreeTransaction({
project: projectAddress.toString(),
authority: adminPublicKey.toString(),
delegateAuthority: delegateAuthorityPublicKey.toString(), // Optional
payer: adminPublicKey.toString(), // Optional, specify when you want a different wallet to pay for the tx
resource: resourceAddress.toString(), // Pass the resource address from the previous step
treeConfig: {
// 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 resources this tree will be able to store
},
advanced: {
maxDepth: 20,
maxBufferSize: 64,
canopyDepth: 14,
}
}
});

await sendTxns(wallet, [tx]);

Now you can create the crafting recipe:

import { client, sendTxns } from "@/utils"; // Import the client and sendTxns functions (covered in the dev environment setup)

const {
createCreateRecipeTransaction: {
recipe: recipeAddress, // This is the recipe address once it'll be created
tx: txResponse, // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
},
} = await client.createInitializeRecipeTransaction({
project: projectAddress.toString(),
xp: "50000", // Crafting using this recipe will award this much XP to the user, can be set to 0
authority: adminPublicKey.toString(),
payer: adminPublicKey.toString(), // Optional, specify when you want a different wallet to pay for the tx
delegateAuthority: delegateAuthorityPublicKey.toString(), // Optional, resource delegate authority public key
ingredients: [
// Send an array of ingredients here, these resources must already be created in your project and should be of the same type
{
fungible: {
address: resourceAddress.toString(), // The resource address of the iron ore
amount: "5", // The amount of iron ore required to craft one iron bar
},
},
],
meal: {
fungible: {
isCompressed: false,
address: resultingResourceAddress.toString(), // The resource address of the iron bars
amount: "1", // The amount of iron bars that will be crafted each time the user uses this recipe
},
},
});

await sendTxns(wallet, [txResponse]); // Send the transaction to the blockchain

8. Craft the resource

Crafting a resource is a multi-step process. Please click here for the step-by-step guide.

9. (Optional) Mint the crafted resource directly

Sometimes, you might want to mint the crafted resource directly to the user's wallet. For example: if the user might open a chest, you might want them to receive a random crafted item.

You can achieve that by using the following code:

import { client, sendTxns } from "@/utils"; // Import the client and sendTxns functions (covered in the dev environment setup)

const {
createMintResourceTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction through client.sendBulkTransactions
} = await client.createMintResourceTransaction({
authority: adminPublicKey.toString(), // Project authority's public key
owner: userPublicKey.toString(), // The owner's public key, this wallet will receive the resource
delegateAuthority: delegateAuthorityPublicKey.toString(), // Optional, if specified, the delegate authority will be used to mint the resource
payer: adminPublicKey.toString(),
resource: resourceAddress.toString(), // The resource address of the iron bars (or any other resource that usually requires crafting)
params: {
fungible: {
amount: "3", // The amount of iron bars to mint and give the user (specified in the owner field above)
},
}
});

await sendTxns(wallet, [txResponse]); // Send the transaction to the blockchain