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.
Pre-requisites
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 { useWallet } from "@solana/wallet-adapter-react";
import { sendClientTransactions } from "@honeycomb-protocol/edge-client/client/walletHelpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
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: {
achievements: [ // Specify an array of achievements that you can give to your users
"Pioneer"
],
customDataFields: [ // Specify an array of custom data fields that you can set on your users' profiles
"bugsReported"
]
}
});
await sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it 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 { useWallet } from "@solana/wallet-adapter-react";
import { sendClientTransactions } from "@honeycomb-protocol/edge-client/client/walletHelpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
const {
createCreateProfilesTreeTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = 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
},
// Uncomment the following config if you want to configure your own profile tree (also comment out the above config)
// advanced: {
// maxDepth: 20,
// maxBufferSize: 64,
// canopyDepth: 14,
// }
}
});
await sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it 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 { useWallet } from "@solana/wallet-adapter-react";
import { sendClientTransactions } from "@honeycomb-protocol/edge-client/client/walletHelpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
const {
createNewUserWithProfileTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = await client.createNewUserWithProfileTransaction({
project: projectAddress.toString(),
wallet: userPublicKey.toString(),
payer: adminPublicKey.toString(),
profileIdentity: "main",
userInfo: {
name: "John Doe",
bio: "This user is created for testing purposes",
pfp: "https://lh3.googleusercontent.com/-Jsm7S8BHy4nOzrw2f5AryUgp9Fym2buUOkkxgNplGCddTkiKBXPLRytTMXBXwGcHuRr06EvJStmkHj-9JeTfmHsnT0prHg5Mhg",
}
});
await sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it to the blockchain
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 { useWallet } from "@solana/wallet-adapter-react";
import { sendClientTransactions } from "@honeycomb-protocol/edge-client/client/walletHelpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
const {
createCreateNewResourceTransaction: {
resource: resourceAddress, // This is the resource address once it'll be created
tx: txResponse, // This is the transaction response, you'll need to sign and send this transaction
},
} = 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: {
name: "Gold", // Name of the resource
decimals: 6, // Number of decimal places the resource can be divided into
symbol: "GOLD", // Symbol of the resource
uri: "https://example.com", // URI of the resource
storage: ResourceStorageEnum.AccountState, // Storage type of the resource, can be either AccountState or LedgerState
}
});
await sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it 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 { useWallet } from "@solana/wallet-adapter-react";
import { sendClientTransactions } from "@honeycomb-protocol/edge-client/client/walletHelpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
const {
createCreateNewResourceTreeTransaction: {
tree: merkleTreeAddress, // This is the merkle tree 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.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
},
// Uncomment the following config if you want to configure your own profile tree (also comment out the above config)
// advanced: {
// maxDepth: 20,
// maxBufferSize: 64,
// canopyDepth: 14,
// }
}
});
await sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it to the blockchain
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 { useWallet } from "@solana/wallet-adapter-react";
import { sendClientTransactions } from "@honeycomb-protocol/edge-client/client/walletHelpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
const {
createMintResourceTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = await client.createMintResourceTransaction({
resource: resourceAddress.toString(), // Resource public key as a string
amount: "50000", // Amount of the resource to mint
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(), // Optional, specify when you want a different wallet to pay for the tx
});
await sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it 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 { useWallet } from "@solana/wallet-adapter-react";
import { sendTransaction } from "@honeycomb-protocol/edge-client/client/helpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
const {
createMintResourceTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = 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(),
amount: "5",
});
await sendTransaction(client, wallet, txResponse); // Get the transaction signed by the user and send it 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 { useWallet } from "@solana/wallet-adapter-react";
import { sendClientTransactions } from "@honeycomb-protocol/edge-client/client/walletHelpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
// 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
},
} = 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: {
name: "Iron Bar", // The name of the resource
decimals: 6, // The number of decimal places the resource can be divided into
symbol: "IBR", // The symbol of the resource
uri: "https://example.com", // URI for the resource's metadata,
storage: ResourceStorageEnum.AccountState, // Storage type of the resource, can be either AccountState or LedgerState
}
});
await sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it to the blockchain
// Then create the resource tree
const {
createCreateNewResourceTreeTransaction: {
tree: merkleTreeAddress, // This is the merkle tree 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.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
},
// Uncomment the following config if you want to configure your own profile tree (also comment out the above config)
// advanced: {
// maxDepth: 20,
// maxBufferSize: 64,
// canopyDepth: 14,
// }
}
});
await sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it to the blockchain
Now you can create the crafting recipe:
import { useWallet } from "@solana/wallet-adapter-react";
import { sendClientTransactions } from "@honeycomb-protocol/edge-client/client/walletHelpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
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
},
} = 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 sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it 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 { useWallet } from "@solana/wallet-adapter-react";
import { sendClientTransactions } from "@honeycomb-protocol/edge-client/client/walletHelpers";
import { client } from "@/utils"; // Import the client (covered in the dev environment setup)
const wallet = useWallet();
const {
createMintResourceTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = 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 sendClientTransactions(client, wallet, txResponse); // Get the transaction signed by the user and send it to the blockchain