Build a Transaction

In this recipe, we share how to use the spore-sdk to construct a transaction that can later be transformed into a spore on-chain.

There’re 3 steps in creating an on-chain spore: constructing the transaction, signing it with a wallet, and sending it on chain via RPC. This recipe focuses on the first step: constructing the transaction.

Ingredients for Constructing a Transaction

  • createSpore function from @spore-sdk/core

  • WALLET_ADDRESS

  • Content in MIME type

We start by using the createSpore function to construct TransactionSkeleton complete with inputs, outputs, and placeholders for witnesses.

Here's how you can implement this function:

import { createSpore, SporeConfig } from '@spore-sdk/core';

const createSporeResult = await createSpore({
  data: {
    contentType: 'image/jpeg',
    content: JPEG_AS_BYTES,
  },
  fromInfos: [WALLET_ADDRESS],
  toLock: WALLET_LOCK,
});

The createSpore function runs through a series of steps:

  1. Initialize an empty TransactionSkeleton

  2. Generate a spore and append it to TransactionSkeleton.outputs

  3. Extract CKB from WALLET_ADDRESS and add it to TransactionSkeleton.inputs

  4. If necessary, fill placeholders for each input's corresponding witness

  5. Add related cell dependencies to TransactionSkeleton.cellDeps

  6. Update the spore and generate a unique ID for it

As a result, the createSpore function returns a createSporeResult object, which contains:

  • txSkeleton: A TransactionSkeleton representing an ongoing transaction that you can continue to modify until it's ready to be dispatched to RPC.

  • outputIndex: This is the index of the newly created spore's output in the TransactionSkeleton.

  • cluster: This is an object that defines the input/output index of the new spore's dependent cluster in the TransactionSkeleton (if applicable).

After these steps, your TransactionSkeleton is now constructed and ready to be signed and sent to the chain!

Extra Steps:

Signing the transaction

Following the construction of the transaction, you can verify the ownership of all cells inputs and outputs by signing the transaction. Specifically, the createSpore function must verify the input cells associated with the WALLET_ADDRESS.

For hands-on references, check out this code examples of how a transaction is signed: examples/secp256k1/utils/wallet.ts

For a comprehensive guide on signing a transaction, refer to: How to sign a transaction

Sending the Signed Transaction

Once the transaction is signed, it's ready to go on-chain.

Here's how you can deploy using RPC:

import { predefinedSporeConfigs } from '@spore-sdk/core';
import { helpers, RPC } from '@ckb-lumos/lumos';

// Get testnet config
const config = predefinedSporeConfigs.Aggron4;

// Convert TransactionSkeleton to Transaction
const tx = helpers.createTransactionFromSkeleton(txSkeleton);

// Send transaction
const rpc = new RPC(config.ckbNodeUrl);
const hash = await rpc.sendTransaction(tx, 'passthrough');

The hash variable obtained at the end of the code represents the Transaction Hash of the transaction you just sent. This can be used to query and review transaction details.

To check your transaction, copy the hash and search for the transaction on the CKB Explorer (Testnet). In the transaction's details, you should find a cell in the outputs - the spore you just created on-chain!

What's Next?

Now that you've created an on-chain spore, you might want to decode the spore data and verify its accuracy. To do this, copy the cell's data and follow the provided recipe to decode it: Handle spore/cluster data

Keep exploring!

Your Feedback Matters!

Get in touch by:

Last updated