Tokens ERC-721 (Go)
Esta guía muestra los pasos principales para usar las transacciones de token ERC-721 nativas de ULedger con el ULedger Go SDK.
Los tokens ERC-721 son tokens no fungibles (NFTs): cada token es único y se identifica mediante un tokenId. Se usan comúnmente para
coleccionables, certificados, entradas, arte digital, membresías y cualquier escenario donde cada activo necesita una identidad única.
En ULedger, los tokens de estilo ERC-721 están soportados como transacciones nativas del protocolo. Eso significa que puedes gestionar una colección de NFTs y sus tokens usando tipos de transacción estándar como:
CREATE_TOKEN— define los metadatos de la colección de tokens (name,symbol,baseURI)MINT_NFT— crea un nuevo NFT (tokenId) para una dirección de walletTRANSFER_NFT— transfiere un NFT (tokenId) entre walletsAPPROVE_TOKEN— aprueba a otra wallet para transferir un token (patrón spender)TRANSFER_TOKEN(con payloadFrom) — transfiere en nombre de un propietario usando una aprobación (si es compatible con tu red)BURN_TOKEN— destruye un NFT (si es burnable)
Dado que estas acciones son transacciones normales de ULedger, las operaciones de token heredan las capacidades principales de ULedger: identidad basada en wallet, firmas criptográficas, historial a prueba de manipulaciones y un rastro auditable de eventos de token a lo largo de la chain.
En la siguiente guía, cubriremos el otro estándar de token nativo de ULedger:
- ERC-1155 (multi-tokens) — un único contrato/dirección puede gestionar tanto IDs de token fungibles como no fungibles, incluyendo mint/transfer por lotes.
Tu wallet debe estar registrada en la chain (CREATE_WALLET) antes de poder enviar transacciones de token.
Asegúrate de que tu wallet esté registrada.
1) Requisitos previos
Necesitas:
- Un endpoint de node (puedes usar un endpoint de testnet:
https://tn-w-1.uledger.net/) - Un
blockchainId(Testnet:08c28f29a62819120958984b761ddf8ccb45951612731409873994958fd150a2) - Una wallet con la que puedas firmar (recomendado: cargar desde
.ukey) - Una comprensión básica de que:
- Token Address = el
TransactionIddevuelto porCREATE_TOKEN
- Token Address = el
2) Cargar una wallet
Carga desde un archivo .ukey en lugar de escribir las claves directamente:
w, err := wallet.LoadFromFile("./wallets/my_wallet.ukey", "")
if err != nil {
return fmt.Errorf("load wallet: %w", err)
}
3) Crear una sesión de transacción
Crea una sesión vinculada a un node y una wallet:
session, err := transaction.NewUL_TransactionSession(nodeEndpoint, w)
if err != nil {
return fmt.Errorf("create session: %w", err)
}
4) Crear una colección ERC-721 (CREATE_TOKEN)
4.1 Construir el payload
func buildCreateERC721Payload() ([]byte, error) {
return json.Marshal(transaction.CreateTokenPayload{
TokenType: transaction.ERC721_TOKEN_TYPE,
Name: "Collectible Token",
Symbol: "CTK",
BaseURI: "https://api.collectibletoken.com/token/",
Mintable: true,
Burnable: true,
})
}
4.2 Enviar la transacción
payloadBytes, err := buildCreateERC721Payload()
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 // ✅ Guarda esto
fmt.Println("Token address:", tokenAddress)
fmt.Println("Status:", tx.Status, "Output:", tx.Output)
✅ El
tx.TransactionIddevuelto se usa como la "token address" de la colección en llamadas futuras.
5) Crear un NFT (MINT_NFT)
Mint crea un nuevo token en la colección y lo asigna a una dirección de wallet.
5.1 Construir el payload
func buildMintERC721Payload(tokenAddress, to string, tokenId uint64, tokenURI string) ([]byte, error) {
return json.Marshal(transaction.MintTokenPayload{
TokenAddress: tokenAddress,
To: to,
TokenId: tokenId,
TokenURI: tokenURI,
})
}
5.2 Enviar la transacción
payloadBytes, err := buildMintERC721Payload(
tokenAddress,
toAddress,
1, // tokenId
"https://api.collectibletoken.com/token/1",
)
if err != nil {
return err
}
input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address,
PayloadType: transaction.MINT_NFT.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) Transferir un NFT (TRANSFER_NFT)
Transfiere un tokenId específico a otra dirección de wallet.
6.1 Construir el payload
func buildTransferERC721Payload(tokenAddress, to string, tokenId uint64) ([]byte, error) {
return json.Marshal(transaction.TransferTokenPayload{
TokenAddress: tokenAddress,
To: to,
TokenId: tokenId,
})
}
6.2 Enviar la transacción
payloadBytes, err := buildTransferERC721Payload(tokenAddress, toAddress, 1)
if err != nil {
return err
}
input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address, // propietario/remitente actual
PayloadType: transaction.TRANSFER_NFT.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) Aprobar un spender (APPROVE_TOKEN)
Las aprobaciones permiten que un spender transfiera NFTs en nombre del propietario.
7.1 Construir el payload
La mayoría de las implementaciones ERC-721 aprueban un spender para un tokenId específico:
func buildApproveERC721Payload(tokenAddress, spender string, tokenId uint64) ([]byte, error) {
return json.Marshal(transaction.ApproveTokenPayload{
TokenAddress: tokenAddress,
Spender: spender,
TokenId: tokenId,
})
}
7.2 Enviar la transacción
payloadBytes, err := buildApproveERC721Payload(tokenAddress, spenderAddress, 1)
if err != nil {
return err
}
input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address, // propietario del token
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) Transferir usando aprobación (TRANSFER_TOKEN con payload From)
Este patrón envía una transferencia donde:
- el firmante de la transacción (
input.From) es el spender - el payload From es el propietario del token cuyo NFT está siendo movido
8.1 Construir el payload
func buildTransferApprovalERC721Payload(tokenAddress, to, owner string, tokenId uint64) ([]byte, error) {
return json.Marshal(transaction.TransferTokenPayload{
TokenAddress: tokenAddress,
To: to,
From: owner, // propietario cuyo NFT será movido
TokenId: tokenId, // token que será movido
})
}
8.2 Enviar la transacción
payloadBytes, err := buildTransferApprovalERC721Payload(tokenAddress, finalRecipient, ownerAddress, 1)
if err != nil {
return err
}
input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: spenderWallet.Address, // el firmante es el spender
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_approval tx: %w", err)
}
fmt.Println("Transfer (approval) tx:", tx.TransactionId)
fmt.Println("Status:", tx.Status, "Output:", tx.Output)
9) Quemar un NFT (BURN_TOKEN)
9.1 Construir el payload
func buildBurnERC721Payload(tokenAddress string, tokenId uint64) ([]byte, error) {
return json.Marshal(transaction.BurnTokenPayload{
TokenAddress: tokenAddress,
TokenId: tokenId,
})
}
9.2 Enviar la transacción
payloadBytes, err := buildBurnERC721Payload(tokenAddress, 1)
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) Ejemplo completo
Este ejemplo único muestra:
- Crear colección
- Mint tokenId=1
- Transferir tokenId=1
- Aprobar spender para tokenId=1
- Transferir usando aprobación (el spender mueve tokenId=1)
- Quemar tokenId=1
package main
import (
"encoding/json"
"fmt"
"github.com/ULedgerInc/golang-sdk/pkg/transaction"
"github.com/ULedgerInc/golang-sdk/pkg/wallet"
)
func main() {
nodeEndpoint := "https://tn-w-1.uledger.net/"
blockchainId := "08c28f29a62819120958984b761ddf8ccb45951612731409873994958fd150a2"
// Cargar dos wallets (propietario + spender) para demostración
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 (ERC721)
createPayload, err := json.Marshal(transaction.CreateTokenPayload{
TokenType: transaction.ERC721_TOKEN_TYPE,
Name: "Collectible Token",
Symbol: "CTK",
BaseURI: "https://api.collectibletoken.com/token/",
Mintable: true,
Burnable: true,
})
if err != nil { panic(err) }
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_NFT (mint tokenId=1 al propietario)
mintPayload, _ := json.Marshal(transaction.MintTokenPayload{
TokenAddress: tokenAddress,
To: ownerWallet.Address,
TokenId: 1,
TokenURI: "https://api.collectibletoken.com/token/1",
})
mintTx, err := ownerSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: ownerWallet.Address,
PayloadType: transaction.MINT_NFT.String(),
Payload: string(mintPayload),
})
if err != nil { panic(err) }
fmt.Println("Mint tx:", mintTx.TransactionId)
// 3) TRANSFER_NFT (propietario -> spender)
transferPayload, _ := json.Marshal(transaction.TransferTokenPayload{
TokenAddress: tokenAddress,
To: spenderWallet.Address,
TokenId: 1,
})
transferTx, err := ownerSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: ownerWallet.Address,
PayloadType: transaction.TRANSFER_NFT.String(),
Payload: string(transferPayload),
})
if err != nil { panic(err) }
fmt.Println("Transfer tx:", transferTx.TransactionId)
// 4) APPROVE_TOKEN (spender aprobado para tokenId=1)
approvePayload, _ := json.Marshal(transaction.ApproveTokenPayload{
TokenAddress: tokenAddress,
Spender: spenderWallet.Address,
TokenId: 1,
})
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 usando aprobación (spender mueve tokenId=1 del propietario al destinatario final)
spenderSession, err := transaction.NewUL_TransactionSession(nodeEndpoint, spenderWallet)
if err != nil { panic(err) }
transferApprovalPayload, _ := json.Marshal(transaction.TransferTokenPayload{
TokenAddress: tokenAddress,
From: ownerWallet.Address, // propietario
To: "DESTINATION_ADDRESS", // reemplazar
TokenId: 1,
})
transferApprovalTx, err := spenderSession.GenerateTransaction(transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: spenderWallet.Address, // firmante = spender
PayloadType: transaction.TRANSFER_TOKEN.String(),
Payload: string(transferApprovalPayload),
})
if err != nil { panic(err) }
fmt.Println("Transfer (approval) tx:", transferApprovalTx.TransactionId)
// 6) BURN_TOKEN (quemar tokenId=1)
burnPayload, _ := json.Marshal(transaction.BurnTokenPayload{
TokenAddress: tokenAddress,
TokenId: 1,
})
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)
}
Próximos pasos
- Sigue la guía de tokens ERC-1155.