Fleet SDK Recipes
This page contains useful code snippets, patterns, and troubleshooting tips for common tasks when using the Fleet SDK in TypeScript/JavaScript.
Table of Contents
Validating Box Ownership (SigmaProp from Register)
Validate if an input box belongs to a specific owner whose public key (SigmaProp) is stored in another box's register. This is often needed for refund scenarios or proving ownership before allowing an action.
- Extract owner's SigmaProp bytes from a register (e.g., R4).
- Deserialize these bytes.
- Convert bytes into the owner's P2PK ErgoTree.
- Compare with the ErgoTree of the box being validated.
Important: Directly constructing ErgoAddress from SSigmaProp is not the correct approach in Fleet SDK. Extract public key bytes first.
Complete Example:
import { Box, ErgoAddress } from "@fleet-sdk/core";
import { deserialize } from "@fleet-sdk/serializer";
function validateBoxOwnership(boxToValidate: Box, boxWithOwnerInfo: Box): boolean {
try {
const registerValueHex = boxWithOwnerInfo.additionalRegisters.R4;
if (!registerValueHex) {
return false;
}
const deserializedValue = deserialize(registerValueHex);
let publicKeyBytes: Uint8Array;
if (deserializedValue.type === "SColl" && deserializedValue.elemType === "SByte") {
publicKeyBytes = new Uint8Array(deserializedValue.value);
} else if (deserializedValue.type === "SSigmaProp") {
if (deserializedValue.value?.type === "SGroupElement") {
publicKeyBytes = new Uint8Array(deserializedValue.value.value);
} else {
return false;
}
} else {
return false;
}
if (!publicKeyBytes || publicKeyBytes.length !== 33) {
return false;
}
const ownerAddress = ErgoAddress.fromPublicKey(publicKeyBytes);
const ownerErgoTree = ownerAddress.ergoTree;
return boxToValidate.ergoTree === ownerErgoTree;
} catch {
return false;
}
}Working with Numeric Registers
import { Box } from "@fleet-sdk/core";
import { deserialize } from "@fleet-sdk/serializer";
function getNumericFromRegister(box: Box, register: "R4" | "R5" | "R6" | "R7" | "R8" | "R9"): bigint | null {
try {
const registerValueHex = box.additionalRegisters[register];
if (!registerValueHex) return null;
const deserialized = deserialize(registerValueHex);
if (deserialized.type === "SLong") {
return BigInt(deserialized.value);
} else if (deserialized.type === "SInt") {
return BigInt(deserialized.value);
}
return null;
} catch {
return null;
}
}Extracting Token IDs from Registers
import { Box } from "@fleet-sdk/core";
import { deserialize } from "@fleet-sdk/serializer";
import { Buffer } from "buffer";
function getTokenIdFromRegister(box: Box, register: "R4" | "R5" | "R6" | "R7" | "R8" | "R9"): string | null {
try {
const registerValueHex = box.additionalRegisters[register];
if (!registerValueHex) return null;
const deserialized = deserialize(registerValueHex);
if (deserialized.type === "SColl" && deserialized.elemType === "SByte") {
return Buffer.from(deserialized.value).toString("hex");
}
return null;
} catch {
return null;
}
}Decoding Complex Types (Tuples, Options, Collections)
import { Box } from "@fleet-sdk/core";
import { deserialize } from "@fleet-sdk/serializer";
import { Buffer } from "buffer";
function decodeSigmaPropLongTuple(box: Box, register: "R4"|"R5"|"R6"|"R7"|"R8"|"R9") {
try {
const registerValueHex = box.additionalRegisters[register];
if (!registerValueHex) return null;
const deserialized = deserialize(registerValueHex);
if (deserialized.type !== "STuple" || !Array.isArray(deserialized.value) || deserialized.value.length !== 2) {
return null;
}
const [item1, item2] = deserialized.value;
let ownerPubKeyBytes: Uint8Array | null = null;
if (item1?.type === "SSigmaProp" && item1.value?.type === "SGroupElement" && item1.value.value) {
ownerPubKeyBytes = new Uint8Array(item1.value.value);
} else {
return null;
}
if (!ownerPubKeyBytes || ownerPubKeyBytes.length !== 33) return null;
const ownerPubKeyHex = Buffer.from(ownerPubKeyBytes).toString("hex");
let deadline: bigint | null = null;
if (item2?.type === "SLong" && typeof item2.value === "string") {
deadline = BigInt(item2.value);
} else {
return null;
}
return { ownerPubKeyHex, deadline };
} catch {
return null;
}
}Compiling ErgoScript to ErgoTree
import { compile } from "@fleet-sdk/compiler";
async function compileToErgoTree(script: string): Promise<string> {
try {
const result = await compile(script);
if (!result || !result.ergoTree) throw new Error("No ErgoTree.");
return result.ergoTree;
} catch (error) {
throw error;
}
}Troubleshooting Common Issues
ErgoTree Comparison Failures
function troubleshootErgoTreeComparison(ergoTree1: string, ergoTree2: string): string {
if (ergoTree1 === ergoTree2) return "Exact Match: Trees are identical.";
const p2pkPrefix = "0008cd";
const tree1IsP2PK = ergoTree1.startsWith(p2pkPrefix);
const tree2IsP2PK = ergoTree2.startsWith(p2pkPrefix);
if (tree1IsP2PK && tree2IsP2PK) {
if (ergoTree1.length !== ergoTree2.length) {
return "Mismatch: Both seem P2PK but have different lengths.";
}
return "Mismatch: Both seem P2PK with same length, but represent different public keys.";
} else if (tree1IsP2PK !== tree2IsP2PK) {
return `Mismatch: One tree appears P2PK (${p2pkPrefix} prefix), the other does not.`;
} else {
if (ergoTree1.length !== ergoTree2.length) {
return "Mismatch: Neither seems standard P2PK, and lengths differ.";
}
return "Mismatch: Neither seems standard P2PK, but lengths match.";
}
}Unexpected Register Format
function detectRegisterFormat(registerHex: string): string {
if (!registerHex || registerHex.length < 2) return "Invalid or empty register value";
const prefix = registerHex.substring(0, 2);
const lengthByte = parseInt(registerHex.substring(2, 4), 16);
if (prefix === "0e") {
if (registerHex.length === 68) return "Likely a Blake2b256 hash (0e + 20 + 32 bytes)";
return `Likely some hash or ID prefixed with 0e, length byte ${lengthByte}`;
} else if (prefix === "00") {
if (registerHex.startsWith("0008cd")) return "Likely a P2PK ErgoTree";
return "Starts with 00, possibly complex ErgoTree or other structure.";
} else if (prefix === "04") {
return "Likely an SInt (Integer)";
} else if (prefix === "05") {
return "Likely an SLong (Long Integer / BigInt)";
} else if (prefix === "07") {
return `Likely a CollByte (serialized byte array), length byte indicates ${lengthByte} bytes follow`;
} else if (prefix === "cd") {
return "Likely a raw SSigmaProp (without 00 ErgoTree wrapper)";
} else {
return `Unknown format starting with prefix ${prefix}`;
}
}Relationship Diagram
graph TD
A[Ergo Address String] -->|"ErgoAddress decode"| B[Public Key Bytes]
B -->|"ErgoAddress fromPublicKey"| C[ErgoAddress Object]
B -->|"SGroupElement"| D[SGroupElement Object]
D -->|"SSigmaProp"| E[SSigmaProp Object]
C -->|"address ergoTree"| F[ErgoTree Hex - P2PK Script]
E -->|"ErgoAddress sigmaProp ergoTree"| F