Skip to content

๐Ÿš€ Your First Ergo Transaction โ€‹

Quest Objective: Build and understand your first Ergo transaction Prerequisites: Node.js 18+, TypeScript basics Time Required: ~30 minutes Difficulty: โญโญ Easy

๐ŸŽฏ What You'll Build โ€‹

By the end of this tutorial, you'll have:

  • โœ… A working development environment
  • โœ… Understanding of the UTXO model
  • โœ… Your first transaction built with Fleet SDK
  • โœ… Knowledge of fees and change handling
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                                         โ”‚
โ”‚   ๐Ÿ“ฆ Input Box     โ†’    ๐Ÿ“ค Output Box   โ”‚
โ”‚   (Your Wallet)         (Recipient)     โ”‚
โ”‚                                         โ”‚
โ”‚                    โ†’    ๐Ÿ’ฐ Change Box   โ”‚
โ”‚                         (Back to You)   โ”‚
โ”‚                                         โ”‚
โ”‚                    โ†’    โ›ฝ Fee           โ”‚
โ”‚                         (To Miners)     โ”‚
โ”‚                                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ“‹ Prerequisites Checklist โ€‹

Before we begin, make sure you have:

  • [ ] Node.js 18+ installed (download)
  • [ ] A code editor (VS Code recommended)
  • [ ] Testnet ERG from the faucet
  • [ ] Basic TypeScript knowledge (variables, functions, async/await)

๐Ÿ—บ๏ธ Understanding the UTXO Model โ€‹

Before we write any code, let's understand how Ergo handles money.

What is UTXO? โ€‹

UTXO stands for Unspent Transaction Output. Think of it like physical cash:

mermaid
graph TD
    A[๐Ÿ’ต You have $20 bill] -->|Buy $5 coffee| B[โ˜• $5 to coffee shop]
    A -->|Change| C[๐Ÿ’ต $15 back to you]
    
    style A fill:#4CAF50
    style B fill:#FF9800
    style C fill:#4CAF50

In Ergo:

  • Boxes = Bills in your wallet (UTXOs)
  • Transactions = Exchanging bills
  • Inputs = Bills you're spending
  • Outputs = New bills created

Key Concept: Boxes โ€‹

On Ergo, value is stored in boxes. Each box contains:

PropertyDescriptionExample
valueAmount in nanoERG1000000000 (1 ERG)
ergoTreeLock script (who can spend)Address converted to script
tokensNative tokens insideNFTs, tokens
registersAdditional data (R4-R9)Metadata, state

1 ERG = 1,000,000,000 nanoERG

Always work in nanoERG when coding. The n suffix creates BigInt: 1_000_000_000n

โšก Step 1: Project Setup โ€‹

Create Project Directory โ€‹

bash
mkdir my-first-ergo-tx
cd my-first-ergo-tx
npm init -y

Install Dependencies โ€‹

bash
npm install @fleet-sdk/core @fleet-sdk/wallet @fleet-sdk/blockchain-providers
npm install -D typescript tsx @types/node

Create TypeScript Config โ€‹

bash
npx tsc --init

Project Structure โ€‹

my-first-ergo-tx/
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ first-transaction.ts  โ† We'll create this
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ tsconfig.json

โš”๏ธ Step 2: Understanding the Transaction Builder โ€‹

The TransactionBuilder is your main tool. Here's the pattern:

typescript
import { TransactionBuilder, OutputBuilder } from "@fleet-sdk/core";

const transaction = new TransactionBuilder(currentHeight)
  .from(inputBoxes)           // ๐Ÿ“ฆ What you're spending
  .to(outputBoxes)            // ๐Ÿ“ค Where it's going
  .sendChangeTo(yourAddress)  // ๐Ÿ’ฐ Leftover back to you
  .payMinFee()                // โ›ฝ Network fee
  .build();                   // ๐Ÿ”จ Construct it!

The Transaction Flow โ€‹

mermaid
sequenceDiagram
    participant U as ๐Ÿ‘ค User
    participant TB as ๐Ÿ”จ TransactionBuilder
    participant N as ๐ŸŒ Network
    
    U->>TB: 1. Add inputs (boxes to spend)
    U->>TB: 2. Define outputs (recipients)
    U->>TB: 3. Set change address
    U->>TB: 4. Calculate fee
    TB->>TB: 5. Build unsigned tx
    U->>TB: 6. Sign transaction
    TB->>N: 7. Broadcast
    N->>N: 8. Confirm in block

๐Ÿ’ป Step 3: Write Your First Transaction โ€‹

Create src/first-transaction.ts:

typescript
/**
 * โš”๏ธ QUEST: Your First Ergo Transaction
 * 
 * ๐ŸŽฏ Objective: Send ERG from one address to another
 * โฑ๏ธ Time: ~10 minutes
 * ๐Ÿ† Reward: Understanding of UTXO transactions!
 */

import { 
  TransactionBuilder, 
  OutputBuilder,
  RECOMMENDED_MIN_FEE_VALUE,
  SAFE_MIN_BOX_VALUE,
  type Box
} from "@fleet-sdk/core";

// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
// ๐Ÿ“ฆ CONFIGURATION
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

const CONFIG = {
  // ๐ŸŽฏ Recipient address (testnet)
  recipientAddress: "9fRAWhdxEsTcdb8PhGNrZfwqa65zfkuYHAMmkQLcic1gdLSV5vA",
  
  // ๐Ÿ’ฐ Amount to send (0.1 ERG = 100,000,000 nanoERG)
  amountToSend: 100_000_000n,
  
  // ๐Ÿ”„ Your address for change
  changeAddress: "9f4QF8AD1nQ3nJahQVkMj8hFSVVzVom77b52JU7EW71Zexg6N8v",
  
  // ๐Ÿ“Š Current blockchain height (fetch from explorer in production)
  networkHeight: 1_200_000,
};

// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
// ๐ŸŽฎ MOCK DATA (Replace with real wallet data)
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

/**
 * In production, you would fetch these from:
 * - Your wallet (Nautilus, etc.)
 * - Ergo Explorer API
 * - GraphQL endpoints
 */
const mockInputBoxes: Box<bigint>[] = [
  {
    boxId: "abc123...",
    value: 1_000_000_000n,  // 1 ERG
    ergoTree: "0008cd...",
    creationHeight: 1_100_000,
    assets: [],
    additionalRegisters: {},
    transactionId: "tx123...",
    index: 0
  }
];

// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
// ๐ŸŽฎ MAIN FUNCTION
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

async function buildFirstTransaction() {
  console.log("โš”๏ธ Starting your first transaction quest...\n");

  try {
    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    // ๐Ÿ“ฆ Step 1: Prepare Inputs
    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    console.log("๐Ÿ“ฆ Step 1: Gathering input boxes...");
    
    const inputs = mockInputBoxes;
    const totalInput = inputs.reduce((sum, box) => sum + box.value, 0n);
    
    console.log(`   Found ${inputs.length} input box(es)`);
    console.log(`   Total value: ${totalInput / 1_000_000_000n} ERG\n`);

    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    // ๐Ÿ“ค Step 2: Create Output
    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    console.log("๐Ÿ“ค Step 2: Creating output for recipient...");
    
    const recipientOutput = new OutputBuilder(
      CONFIG.amountToSend,
      CONFIG.recipientAddress
    );
    
    console.log(`   Recipient: ${CONFIG.recipientAddress.slice(0, 20)}...`);
    console.log(`   Amount: ${CONFIG.amountToSend / 1_000_000_000n} ERG\n`);

    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    // ๐Ÿ”จ Step 3: Build Transaction
    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    console.log("๐Ÿ”จ Step 3: Building transaction...");
    
    const unsignedTx = new TransactionBuilder(CONFIG.networkHeight)
      .from(inputs)
      .to(recipientOutput)
      .sendChangeTo(CONFIG.changeAddress)
      .payFee(RECOMMENDED_MIN_FEE_VALUE)
      .build();
    
    console.log("   โœ… Transaction built successfully!\n");

    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    // ๐Ÿ“Š Step 4: Review Transaction
    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    console.log("๐Ÿ“Š Step 4: Transaction Summary");
    console.log("โ”€".repeat(50));
    console.log(`   Inputs:  ${unsignedTx.inputs.length} box(es)`);
    console.log(`   Outputs: ${unsignedTx.outputs.length} box(es)`);
    console.log(`   Fee:     ${RECOMMENDED_MIN_FEE_VALUE / 1_000_000_000n} ERG`);
    
    // Calculate change
    const outputTotal = unsignedTx.outputs.reduce(
      (sum, out) => sum + out.value, 0n
    );
    const change = totalInput - CONFIG.amountToSend - RECOMMENDED_MIN_FEE_VALUE;
    console.log(`   Change:  ${change / 1_000_000_000n} ERG\n`);

    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    // ๐Ÿ† Quest Complete!
    // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    console.log("๐Ÿ† QUEST COMPLETE!");
    console.log("   Achievement Unlocked: First Transaction Builder\n");
    
    console.log("๐Ÿ“‹ Next Steps:");
    console.log("   1. Sign with wallet: wallet.sign(unsignedTx)");
    console.log("   2. Submit to network: await submitTx(signedTx)");
    console.log("   3. Wait for confirmation (~2 minutes)\n");

    return unsignedTx;

  } catch (error) {
    console.error("โŒ Quest Failed:", error);
    throw error;
  }
}

// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
// ๐ŸŽฌ EXECUTE
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

buildFirstTransaction()
  .then(() => {
    console.log("โœจ Tutorial completed successfully!");
  })
  .catch((err) => {
    console.error("๐Ÿ’€ Error:", err.message);
    process.exit(1);
  });

โ–ถ๏ธ Step 4: Run Your Code โ€‹

bash
npx tsx src/first-transaction.ts

Expected Output:

โš”๏ธ Starting your first transaction quest...

๐Ÿ“ฆ Step 1: Gathering input boxes...
   Found 1 input box(es)
   Total value: 1 ERG

๐Ÿ“ค Step 2: Creating output for recipient...
   Recipient: 9fRAWhdxEsTcdb8Ph...
   Amount: 0.1 ERG

๐Ÿ”จ Step 3: Building transaction...
   โœ… Transaction built successfully!

๐Ÿ“Š Step 4: Transaction Summary
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
   Inputs:  1 box(es)
   Outputs: 2 box(es)
   Fee:     0.001 ERG
   Change:  0.899 ERG

๐Ÿ† QUEST COMPLETE!
   Achievement Unlocked: First Transaction Builder

๐Ÿ“‹ Next Steps:
   1. Sign with wallet: wallet.sign(unsignedTx)
   2. Submit to network: await submitTx(signedTx)
   3. Wait for confirmation (~2 minutes)

โœจ Tutorial completed successfully!

๐Ÿ› Common Issues & Solutions โ€‹

Issue 1: "Insufficient Funds" โ€‹

Cause: Not enough ERG in input boxes to cover amount + fee

Solution:

typescript
// Check you have enough:
const required = CONFIG.amountToSend + RECOMMENDED_MIN_FEE_VALUE;
const available = inputs.reduce((sum, box) => sum + box.value, 0n);

if (available < required) {
  throw new Error(`Need ${required} nanoERG, have ${available}`);
}

Issue 2: "Box Value Too Small" โ€‹

Cause: Output value below minimum (SAFE_MIN_BOX_VALUE)

Solution:

typescript
import { SAFE_MIN_BOX_VALUE } from "@fleet-sdk/core";

// Ensure output meets minimum:
const amount = Math.max(yourAmount, SAFE_MIN_BOX_VALUE);

Issue 3: "Invalid Address" โ€‹

Cause: Wrong network or malformed address

Solution:

  • Testnet addresses start with 9 or 3
  • Mainnet addresses start with 9
  • Validate format before use

Issue 4: "Height Mismatch" โ€‹

Cause: Using outdated block height

Solution:

typescript
// Fetch current height from API:
const response = await fetch('https://api.ergoplatform.com/api/v1/blocks?limit=1');
const data = await response.json();
const currentHeight = data.items[0].height;

๐ŸŽ“ What You Learned โ€‹

  • โœ… UTXO Model - How Ergo stores and transfers value
  • โœ… Boxes - The fundamental unit of storage
  • โœ… TransactionBuilder - The main API for creating transactions
  • โœ… Outputs - Creating new boxes for recipients
  • โœ… Change Handling - Getting leftover value back
  • โœ… Fees - Paying miners for transaction processing

๐Ÿ” Deep Dive: Transaction Anatomy โ€‹

mermaid
graph TB
    subgraph "๐Ÿ“ฅ INPUTS"
        I1[Box 1: 1 ERG]
    end
    
    subgraph "๐Ÿ“ค OUTPUTS"
        O1[Recipient: 0.1 ERG]
        O2[Change: 0.899 ERG]
        O3[Fee: 0.001 ERG]
    end
    
    I1 --> O1
    I1 --> O2
    I1 --> O3
    
    style I1 fill:#f96
    style O1 fill:#9f6
    style O2 fill:#69f
    style O3 fill:#ff9

๐Ÿš€ Next Quest โ€‹

Ready for more? Continue to:

๐Ÿ“š Additional Resources โ€‹


๐Ÿ’ก Pro Tip

In production apps, never hardcode addresses or amounts. Use environment variables and user input instead!

Released under the MIT License.