Skip to main content

ERC-1155 Tokens (Go)

This guide shows the core steps to use ULedger’s native ERC-1155-style token transactions with the ULedger Go SDK.

ERC-1155 tokens are multi-tokens: a single token collection can manage many token IDs, where each tokenId represents a different asset type. Each tokenId can be fungible (quantities like tickets, credits, items) or non-fungible (unique editions), all under the same token address.

ERC-1155 is commonly used for:

  • game items and inventories (multiple item types under one collection)
  • ticketing and event access (e.g., “General Admission”, “VIP”, “Backstage” as different IDs)
  • mixed catalogs where you want standardized IDs and efficient mint/transfer patterns

In ULedger, ERC-1155-style tokens are supported as native protocol transactions (no ULVM smart contract required). That means you can manage a multi-token collection using standard transaction types such as:

  • CREATE_TOKEN — define token collection metadata (name, symbol, baseURI)
  • MINT_MULTI_TOKEN — mint units for a given tokenId
  • TRANSFER_TOKEN — transfer a tokenId + amount between wallets
  • APPROVE_TOKEN — grant a spender an allowance
  • TRANSFER_TOKEN (with payload From) — transfer on behalf of an owner using an allowance/approval
  • BURN_TOKEN — destroy token units to reduce supply (if burnable)
  • CONVERT_TOKEN — convert units of one tokenId into another (optional utility operation)

Because these actions are normal ULedger transactions, token operations inherit ULedger’s core capabilities: wallet-based identity, cryptographic signatures, tamper-evident history, and an auditable trail of token events across the chain.

info

Your wallet must be registered on-chain (CREATE_WALLET) before it can submit token transactions. Make sure your wallet is registered.


1) Prerequisites​

You need:

  • A node endpoint (the examples use https://your-node-url as a placeholder, so replace it with the URL of your network)
  • A blockchainId (Testnet: 08c28f29a62819120958984b761ddf8ccb45951612731409873994958fd150a2)
  • A wallet you can sign with (recommended: load from .ukey)
  • A basic understanding that:
    • Token Address = the TransactionId returned by CREATE_TOKEN

2) Load a wallet​

Load from a .ukey file instead of hardcoding keys:

w, err := wallet.LoadFromFile("./wallets/my_wallet.ukey", "")
if err != nil {
return fmt.Errorf("load wallet: %w", err)
}

3) Create a transaction session​

Create a session bound to a node and wallet:

session, err := transaction.NewUL_TransactionSession(nodeEndpoint, w)
if err != nil {
return fmt.Errorf("create session: %w", err)
}

4) Create an ERC-1155 collection (CREATE_TOKEN)​

4.1 Build the payload​

func buildCreateERC1155Payload() ([]byte, error) {
return json.Marshal(transaction.CreateTokenPayload{
TokenType: transaction.ERC1155_TOKEN_TYPE,
Name: "Concert Tickets",
Symbol: "$CTIX",
BaseURI: "https://tickets.example.com/",
Mintable: true,
Burnable: true,
})
}

4.2 Submit the transaction​

payloadBytes, err := buildCreateERC1155Payload()
if err != nil {
return err
}

input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address,
PayloadType: transaction.CREATE_TOKEN.String(),
Payload: string(payloadBytes),
}

tx, err := session.GenerateTransaction(input)
if err != nil {
return fmt.Errorf("create collection tx: %w", err)
}

tokenAddress := tx.TransactionId // ✅ Save this
fmt.Println("Token address:", tokenAddress)
fmt.Println("Status:", tx.Status, "Output:", tx.Output)

✅ The returned tx.TransactionId is used as the collection’s “token address” in future calls.


5) Mint tokens (MINT_MULTI_TOKEN)​

:::info Confirm the recipient before you send

The recipient must be a wallet on this same blockchain with a matching key type. Transfers are final, and the network credits the address it is given: an address with no wallet on this chain holds a balance that cannot be spent. Run this check before every mint or transfer in this guide.

curl -o /dev/null -w '%{http_code}\n' \
"https://your-node-url/blockchains/{blockchainId}/wallets/{recipientAddress}"
# 200 = registered here 404 = not on this chain

:::

Mint creates token units under a specific tokenId.

5.1 Build the payload​

func buildMintERC1155Payload(tokenAddress, to string, tokenId uint64, amount uint64, tokenURI string) ([]byte, error) {
return json.Marshal(transaction.MintTokenPayload{
TokenAddress: tokenAddress,
To: to,
TokenId: tokenId,
Amount: amount,
TokenURI: tokenURI,
})
}

5.2 Submit the transaction​

payloadBytes, err := buildMintERC1155Payload(
tokenAddress,
toAddress,
1, // tokenId
1000, // amount
"Ticket URIs!",
)
if err != nil {
return err
}

input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address,
PayloadType: transaction.MINT_MULTI_TOKEN.String(),
Payload: string(payloadBytes),
}

tx, err := session.GenerateTransaction(input)
if err != nil {
return fmt.Errorf("mint tx: %w", err)
}

fmt.Println("Mint tx:", tx.TransactionId)
fmt.Println("Status:", tx.Status, "Output:", tx.Output)

6) Transfer tokens (TRANSFER_TOKEN)​

ERC-1155 transfers move a specific tokenId and amount.

6.1 Build the payload​

func buildTransferERC1155Payload(tokenAddress, to string, tokenId uint64, amount uint64) ([]byte, error) {
return json.Marshal(transaction.TransferTokenPayload{
TokenAddress: tokenAddress,
To: to,
TokenId: tokenId,
Amount: amount,
})
}

6.2 Submit the transaction​

payloadBytes, err := buildTransferERC1155Payload(tokenAddress, toAddress, 1, 5)
if err != nil {
return err
}

input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address,
PayloadType: transaction.TRANSFER_TOKEN.String(),
Payload: string(payloadBytes),
}

tx, err := session.GenerateTransaction(input)
if err != nil {
return fmt.Errorf("transfer tx: %w", err)
}

fmt.Println("Transfer tx:", tx.TransactionId)
fmt.Println("Status:", tx.Status, "Output:", tx.Output)

7) Approve a spender (APPROVE_TOKEN)​

Approvals allow a spender to transfer tokens on behalf of the token owner (allowance-style).

7.1 Build the payload​

func buildApproveERC1155Payload(tokenAddress, spender string, amount uint64) ([]byte, error) {
return json.Marshal(transaction.ApproveTokenPayload{
TokenAddress: tokenAddress,
Spender: spender,
Amount: amount,
})
}

7.2 Submit the transaction​

payloadBytes, err := buildApproveERC1155Payload(tokenAddress, spenderAddress, 5000)
if err != nil {
return err
}

input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address, // token owner
PayloadType: transaction.APPROVE_TOKEN.String(),
Payload: string(payloadBytes),
}

tx, err := session.GenerateTransaction(input)
if err != nil {
return fmt.Errorf("approve tx: %w", err)
}

fmt.Println("Approve tx:", tx.TransactionId)
fmt.Println("Status:", tx.Status, "Output:", tx.Output)

8) Transfer using approval (TRANSFER_TOKEN with payload From)​

:::warning The recipient must not be the spender

In an allowance transfer the signer is the spender, and the network rejects any transfer whose signer is also the recipient. Send to a third address instead.

The rule applies to ERC-20, NFTs and multi-tokens. A transfer that breaches it is accepted at submit and then not applied while the allowance is unchanged, so check the recipient first if you see that. :::

This pattern submits a transfer where:

  • the transaction signer (input.From) is the spender
  • the payload From is the token owner whose balance is being moved

8.1 Build the payload​

func buildTransferApprovalERC1155Payload(tokenAddress, to, owner string, amount uint64) ([]byte, error) {
return json.Marshal(transaction.TransferTokenPayload{
TokenAddress: tokenAddress,
To: to,
From: owner,
Amount: amount,
})
}

8.2 Submit the transaction​

payloadBytes, err := buildTransferApprovalERC1155Payload(tokenAddress, finalRecipient, ownerAddress, 3000)
if err != nil {
return err
}

input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: spenderWallet.Address,
PayloadType: transaction.TRANSFER_TOKEN.String(),
Payload: string(payloadBytes),
}

spenderSession, err := transaction.NewUL_TransactionSession(nodeEndpoint, spenderWallet)
if err != nil {
return err
}

tx, err := spenderSession.GenerateTransaction(input)
if err != nil {
return fmt.Errorf("transfer using approval tx: %w", err)
}

fmt.Println("Transfer (approval) tx:", tx.TransactionId)
fmt.Println("Status:", tx.Status, "Output:", tx.Output)

9) Burn tokens (BURN_TOKEN)​

9.1 Build the payload​

func buildBurnERC1155Payload(tokenAddress string, amount uint64) ([]byte, error) {
return json.Marshal(transaction.BurnTokenPayload{
TokenAddress: tokenAddress,
Amount: amount,
})
}

9.2 Submit the transaction​

payloadBytes, err := buildBurnERC1155Payload(tokenAddress, 5)
if err != nil {
return err
}

input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address,
PayloadType: transaction.BURN_TOKEN.String(),
Payload: string(payloadBytes),
}

tx, err := session.GenerateTransaction(input)
if err != nil {
return fmt.Errorf("burn tx: %w", err)
}

fmt.Println("Burn tx:", tx.TransactionId)
fmt.Println("Status:", tx.Status, "Output:", tx.Output)

10) Convert tokens (CONVERT_TOKEN)​

CONVERT_TOKEN converts units of an existing tokenId into a new representation (based on your chain rules). A common use is converting a token into a new state, such as "unused" → "used", by producing a new token URI and optionally burning the original units.

10.1 Build the payload​

func buildConvertERC1155Payload(tokenAddress string, fromTokenId uint64, amount uint64) ([]byte, error) {
return json.Marshal(transaction.ConvertTokenPayload{
TokenAddress: tokenAddress,
FromTokenId: fromTokenId,
Amount: amount,
NewTokenURI: "https://commemorative.example.com/used_ticket",
PreserveTokens: false,
})
}

10.2 Submit the transaction​

payloadBytes, err := buildConvertERC1155Payload(tokenAddress, 1, 5)
if err != nil {
return err
}

input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address,
PayloadType: transaction.CONVERT_TOKEN.String(),
Payload: string(payloadBytes),
}

tx, err := session.GenerateTransaction(input)
if err != nil {
return fmt.Errorf("convert tx: %w", err)
}

fmt.Println("Convert tx:", tx.TransactionId)
fmt.Println("Status:", tx.Status, "Output:", tx.Output)

11) Full example​

This single example shows:

  1. Create collection
  2. Mint tokenId=1 with amount=1000
  3. Transfer tokenId=1 with amount=5
  4. Approve spender
  5. Transfer using approval
  6. Burn tokens
  7. Convert tokens
package main

import (
"encoding/json"
"fmt"

"github.com/ULedgerInc/golang-sdk/pkg/transaction"
"github.com/ULedgerInc/golang-sdk/pkg/wallet"
)

func main() {
nodeEndpoint := "http://localhost:8080"
blockchainId := "MyBlockchain1"

ownerWallet, err := wallet.LoadFromFile("./wallets/owner.ukey", "")
if err != nil { panic(err) }

spenderWallet, err := wallet.LoadFromFile("./wallets/spender.ukey", "")
if err != nil { panic(err) }

ownerSession, err := transaction.NewUL_TransactionSession(nodeEndpoint, ownerWallet)
if err != nil { panic(err) }

// 1) CREATE_TOKEN (ERC1155)
createPayload, _ := json.Marshal(transaction.CreateTokenPayload{
TokenType: transaction.ERC1155_TOKEN_TYPE,
Name: "Concert Tickets",
Symbol: "$CTIX",
BaseURI: "https://tickets.example.com/",
Mintable: true,
Burnable: true,
})

createTx, err := ownerSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: ownerWallet.Address,
PayloadType: transaction.CREATE_TOKEN.String(),
Payload: string(createPayload),
})
if err != nil { panic(err) }

tokenAddress := createTx.TransactionId
fmt.Println("Token address:", tokenAddress)

// 2) MINT_MULTI_TOKEN (tokenId=1, amount=1000)
mintPayload, _ := json.Marshal(transaction.MintTokenPayload{
TokenAddress: tokenAddress,
To: ownerWallet.Address,
TokenId: 1,
Amount: 1000,
TokenURI: "Ticket URIs!",
})

mintTx, err := ownerSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: ownerWallet.Address,
PayloadType: transaction.MINT_MULTI_TOKEN.String(),
Payload: string(mintPayload),
})
if err != nil { panic(err) }
fmt.Println("Mint tx:", mintTx.TransactionId)

// 3) TRANSFER_TOKEN (tokenId=1, amount=5)
transferPayload, _ := json.Marshal(transaction.TransferTokenPayload{
TokenAddress: tokenAddress,
To: spenderWallet.Address,
TokenId: 1,
Amount: 5,
})

transferTx, err := ownerSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: ownerWallet.Address,
PayloadType: transaction.TRANSFER_TOKEN.String(),
Payload: string(transferPayload),
})
if err != nil { panic(err) }
fmt.Println("Transfer tx:", transferTx.TransactionId)

// 4) APPROVE_TOKEN (approve spender)
approvePayload, _ := json.Marshal(transaction.ApproveTokenPayload{
TokenType: "", // not required for approve
TokenAddress: tokenAddress,
Spender: spenderWallet.Address,
Amount: 50,
})

approveTx, err := ownerSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: ownerWallet.Address,
PayloadType: transaction.APPROVE_TOKEN.String(),
Payload: string(approvePayload),
})
if err != nil { panic(err) }
fmt.Println("Approve tx:", approveTx.TransactionId)

// 5) TRANSFER_TOKEN using approval (spender spends owner allowance)
spenderSession, err := transaction.NewUL_TransactionSession(nodeEndpoint, spenderWallet)
if err != nil { panic(err) }

transferApprovalPayload, _ := json.Marshal(transaction.TransferTokenPayload{
TokenAddress: tokenAddress,
From: ownerWallet.Address,
To: "DESTINATION_ADDRESS", // replace
Amount: 10,
})

transferApprovalTx, err := spenderSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: spenderWallet.Address,
PayloadType: transaction.TRANSFER_TOKEN.String(),
Payload: string(transferApprovalPayload),
})
if err != nil { panic(err) }
fmt.Println("Transfer (approval) tx:", transferApprovalTx.TransactionId)

// 6) BURN_TOKEN
burnPayload, _ := json.Marshal(transaction.BurnTokenPayload{
TokenAddress: tokenAddress,
Amount: 5,
})

burnTx, err := ownerSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: ownerWallet.Address,
PayloadType: transaction.BURN_TOKEN.String(),
Payload: string(burnPayload),
})
if err != nil { panic(err) }
fmt.Println("Burn tx:", burnTx.TransactionId)

// 7) CONVERT_TOKEN
convertPayload, _ := json.Marshal(transaction.ConvertTokenPayload{
TokenAddress: tokenAddress,
FromTokenId: 1,
Amount: 5,
NewTokenURI: "https://commemorative.example.com/used_ticket",
PreserveTokens: false,
})

convertTx, err := ownerSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: ownerWallet.Address,
PayloadType: transaction.CONVERT_TOKEN.String(),
Payload: string(convertPayload),
})
if err != nil { panic(err) }
fmt.Println("Convert tx:", convertTx.TransactionId)
}