Skip to main content

Resources

Pre-requisites

Please make sure that you've followed our getting started guide and set up your development environment.

You'll also need to have a project set up in Honeycomb Protocol. If you haven't done that yet, you can create one using instructions here.

Overview

Resources and crafting form the backbone of any app or game that has an inventory system. It allows users to create new items by combining existing ones.

Honeycomb Protocol lets developers create crafting resources and recipes for their projects. These recipes can be as simple as combining two resources to create a new one, or as complex as having to craft multiple items in a specific order to create a new item.

Resources are fungible on Honeycomb Protocol. That means they can be divided into smaller units. For example, a fungible resource could be gold coins, which can be divided into smaller units like 1 gold coin, 0.5 gold coins, etc. The supply of these resources is unlimited.

There are two types of resources you can create in Honeycomb Protocol:

  1. Ledger State Resources: These are resources that are compressed and stored in a vault account. They require having an associated resource merkle tree to store ownership details.
  2. Account State Resources: These are resources that are uncompressed and stored in a user's account. They don't require a resource merkle tree.

Crafting resources

Creating a resource

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.LedgerState, // Type of the resource, can be either AccountState (uncompressed/unwrapped) or LedgerState (compressed/wrapped)
tags: ["Sword"], // Optional, tags for the resource; tags act as metadata to help you keep track of game stats
}
});

Creating a resource tree

With each new (compressed) resource, you need a resource tree to store ownership details. This merkle tree stores and verifies the ownership of resources.

const {
createCreateNewResourceTreeTransaction: {
treeAddress: 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
},
} = 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,
// }
}
});

Mint a resource

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
});

Burn a resource

const {
createBurnResourceTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = await client.createBurnResourceTransaction({
authority: userPublicKey.toString(), // The resource owner's public key
resource: resourceAddress.toString(),
amount: "50000", // Amount of the resource to burn
payer: adminPublicKey.toString(), // Optional, specify when you want a different wallet to pay for the tx
});

Transfer a resource

const {
createTransferResourceTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = await client.createTransferResourceTransaction({
resource: resourceAddress.toString(),
owner: userPublicKey.toString(), // Sender's public key as a string
recipient: recipientPublicKey.toString(), // Recipient's public key as a string
amount: "50000", // Amount of the resource to transfer
payer: adminPublicKey.toString(), // Optional, specify when you want a different wallet to pay for the tx
});

Importing and exporting resources

With the addition of Token 2022 into our resource program, you can now import Solana tokens into your project as fungible resources. They can then be minted, burned, and transferred like any other fungible resource on Honeycomb Protocol.

Import a resource

const {
createImportFungibleResourceTransaction: {
resource: resourceAddress, // This is the resource address once it's imported
tx: txResponse // This is the transaction response, you'll need to sign and send this transaction
},
} = await client.createImportFungibleResourceTransaction({
params: {
decimals: 0, // Number of decimal places the resource can be divided into
tags: ["Sword"], // Optional, tags for the resource; tags act as metadata to help you keep track of game stats
project: projectAddress, // Project public key as a string
mint: "mint", // Mint address of the resource
authority: adminPublicKey.toString(),
storage: ResourceStorageEnum.LedgerState, // Type of the resource, can be either AccountState (uncompressed/unwrapped) or LedgerState (compressed/wrapped)
custody: { // Optional, define a supply amount and optional burner destination
supply: "50000", // Amount of the resource to import
burnerDestination: burnerDestinationPublickey.toString(), // Optional, burner destination public key as a string
}
}
});

Export a resource

const {
createExportFungibleResourceTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = await client.createExportFungibleResourceTransaction({
resource: resourceAddress.toString(),
authority: adminPublicKey.toString(),
});

Wrapping and unwrapping resources

Wrapping and unwrapping are two important functions in Honeycomb Protocol. Wrapping a resource compresses it, while unwrapping a resource uncompresses it. These two methods let you convert a resource state (i.e., from AccountState to LedgerState or vice versa).

Wrap a resource

Wrapping a resource compresses it and stores it in a holding account. This holding account is managed by the project authority.

note

In case you're wrapping an AccountState resource, make sure you have an existing resource tree, otherwise the wrap operation will fail.

const { 
createCreateWrapHoldingTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = await client.createCreateWrapHoldingTransaction({
authority: userPublicKey.toString(),
resource: resourceAddress.toString(),
amount: "50000", // Amount of the resource to wrap
payer: adminPublicKey.toString(), // Optional, specify when you want a different wallet to pay for the tx
});

Unwrap a resource

When you unwrap a resource, it's transferred from the holding account to the user's account, where it can be used for transfering, trading, burning etc.

const {
createCreateUnwrapHoldingTransaction: {
tx: txResponse, // This is the transaction response, you'll need to sign and send this transaction
},
} = await client.createCreateUnwrapHoldingTransaction({
authority: userPublicKey.toString(),
resource: resourceAddress.toString(),
amount: "50000", // Amount of the resource to unwrap
payer: adminPublicKey.toString(), // Optional, specify when you want a different
});

Recipes

Recipes are a way to define how resources can be combined to create new items.

In Honeycomb Protocol, developers can define recipes that let their users craft new items. Let's take a look at how you can bake this functionality into your app or game.

Create a recipe

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",
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
{
resourceAddress: resourceAddress.toString(),
amount: "100", // Amount of the resource
},
],
meal: {
resourceAddress: resultingResourceAddress.toString(),
amount: "5",
},
});

Craft/cook an item using a recipe

We've simplified crafting/cooking so you only need to make one API call to our Edge Client. However, you'll receive an array of transactions in response. These transactions all need to be signed and sent back to the Edge Client using the sendClientTransactions function.

const {
createInitCookingProcessTransactions: {
transactions, // This is an array of transactions, you'll need to get these transactions signed by the user before sending them
blockhash,
lastValidBlockHeight,
},
} = await client.createInitCookingProcessTransactions({
recipe: recipeAddress.toString(), // Recipe PDA public key as a string
authority: userPublicKey.toString(), // User's public key as a string
});

Add an ingredient to a recipe

const {
createAddIngredientsTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = await client.createAddIngredientsTransaction({
recipe: recipeAddress.toString(),
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, also make sure they're of the same type as the ones already present in your recipe
{
resourceAddress: ingredientAddress.toString(), // Resource public key as a string
amount: "100", // Amount of the resource needed
},
],
});

Remove an ingredient from a recipe

const {
createRemoveIngredientsTransaction: txResponse // This is the transaction response, you'll need to sign and send this transaction
} = await client.createRemoveIngredientsTransaction({
recipe: recipeAddress.toString(),
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 ingredient public keys as a string here, these resources must already be present in your project and this recipe
ingredientAddress.toString(),
],
});