Skip to content

๐Ÿง™ Smart Contract Interaction โ€‹

Quest Objective: Compile and interact with ErgoScript contracts Prerequisites: Completed previous tutorials Time Required: ~60 minutes Difficulty: โญโญโญโญ Hard

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

  • โœ… ErgoScript basics
  • โœ… Compiling contracts with Fleet SDK
  • โœ… Creating contract boxes
  • โœ… Spending from contracts

๐Ÿ“œ ErgoScript Primer โ€‹

ErgoScript is Ergo's smart contract language. It's:

  • Declarative - Describes conditions, not procedures
  • Sigma Protocol based - Cryptographic proofs
  • UTXO-aware - Access box data directly
mermaid
graph LR
    A[ErgoScript] -->|Compile| B[ErgoTree]
    B -->|Hash| C[P2S Address]
    C -->|Lock Funds| D[Contract Box]
    D -->|Satisfy Conditions| E[Spend!]

๐Ÿ’ป Example: Simple Time-Lock Contract โ€‹

typescript
/**
 * ๐Ÿง™ QUEST: Time-Lock Contract
 * 
 * Lock funds until a specific block height
 */

import { compile } from "@fleet-sdk/compiler";
import { 
  TransactionBuilder, 
  OutputBuilder,
  SAFE_MIN_BOX_VALUE
} from "@fleet-sdk/core";

// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
// ๐Ÿ“œ ERGOSCRIPT CONTRACT
// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

const timeLockScript = `
{
  // Funds can only be spent after block 1,500,000
  val unlockHeight = 1500000L
  
  // Owner's public key (replace with actual)
  val ownerPk = PK("9f4QF8AD1nQ3nJahQVkMj8hFSVVzVom77b52JU7EW71Zexg6N8v")
  
  // Spending condition: height reached AND owner signs
  sigmaProp(HEIGHT >= unlockHeight && ownerPk)
}
`;

async function createTimeLock() {
  console.log("๐Ÿง™ Creating time-lock contract...\n");

  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  // Step 1: Compile the contract
  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  console.log("๐Ÿ“œ Step 1: Compiling ErgoScript...");
  
  const compiled = compile(timeLockScript);
  
  console.log(`   โœ… Compiled successfully!`);
  console.log(`   Contract Address: ${compiled.address}\n`);

  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  // Step 2: Lock funds in contract
  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  console.log("๐Ÿ”’ Step 2: Locking funds in contract...");

  const inputBox = {
    boxId: "input123...",
    value: 5_000_000_000n,  // 5 ERG to lock
    ergoTree: "0008cd...",
    creationHeight: 1_100_000,
    assets: [],
    additionalRegisters: {},
    transactionId: "tx123...",
    index: 0
  };

  // Create box at contract address
  const contractOutput = new OutputBuilder(
    1_000_000_000n,  // Lock 1 ERG
    compiled.address
  );

  const lockTx = new TransactionBuilder(1_200_000)
    .from([inputBox])
    .to(contractOutput)
    .sendChangeTo("9f4QF8AD1nQ3nJahQVkMj8hFSVVzVom77b52JU7EW71Zexg6N8v")
    .payMinFee()
    .build();

  console.log("   โœ… Funds locked in contract!\n");

  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  // Step 3: Spend from contract (after unlock)
  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  console.log("๐Ÿ”“ Step 3: Spending from contract...");
  console.log("   (Only works after block 1,500,000)\n");

  // The contract box that was created
  const contractBox = {
    boxId: "contract-box-id",
    value: 1_000_000_000n,
    ergoTree: compiled.ergoTree,  // Contract's ergoTree
    creationHeight: 1_200_001,
    assets: [],
    additionalRegisters: {},
    transactionId: "lock-tx-id",
    index: 0
  };

  // Spend at height >= 1,500,000
  const spendTx = new TransactionBuilder(1_500_001)  // After unlock!
    .from([contractBox])
    .to(
      new OutputBuilder(
        contractBox.value - 1_100_000n,  // Minus fee
        "9f4QF8AD1nQ3nJahQVkMj8hFSVVzVom77b52JU7EW71Zexg6N8v"
      )
    )
    .payMinFee()
    .build();

  console.log("   โœ… Spend transaction built!");
  console.log("   (Requires owner signature + height check)\n");

  return { lockTx, spendTx, contractAddress: compiled.address };
}

createTimeLock();

๐Ÿ“Š Contract Flow Diagram โ€‹

mermaid
sequenceDiagram
    participant U as ๐Ÿ‘ค User
    participant C as ๐Ÿ“œ Compiler
    participant B as โ›“๏ธ Blockchain
    
    U->>C: 1. Write ErgoScript
    C->>C: 2. Compile to ErgoTree
    C->>U: 3. Return P2S address
    U->>B: 4. Send ERG to contract
    B->>B: 5. Create contract box
    Note over B: Time passes...
    U->>B: 6. Spend (if conditions met)
    B->>U: 7. Funds released!

๐ŸŽ“ Common Contract Patterns โ€‹

Pattern 1: Signature Required โ€‹

typescript
const signatureRequired = `
{
  val ownerPk = PK("9f4QF8AD...")
  sigmaProp(ownerPk)
}
`;

Pattern 2: Multi-Signature (2 of 3) โ€‹

typescript
const multiSig = `
{
  val pk1 = PK("9fAAA...")
  val pk2 = PK("9fBBB...")
  val pk3 = PK("9fCCC...")
  
  sigmaProp(atLeast(2, Coll(pk1, pk2, pk3)))
}
`;

Pattern 3: Anyone Can Spend (After Height) โ€‹

typescript
const openAfterHeight = `
{
  sigmaProp(HEIGHT > 2000000L)
}
`;

๐Ÿ› Common Issues โ€‹

IssueCauseSolution
Compilation errorSyntax issueCheck ErgoScript syntax
Cannot spendConditions not metVerify HEIGHT, signatures
Invalid ErgoTreeWrong encodingUse compiler output directly

๐Ÿš€ Congratulations! โ€‹

You've completed the smart contracts tutorial! You now understand:

  • ErgoScript fundamentals
  • Contract compilation
  • Locking and unlocking funds
  • Multi-signature patterns

๐Ÿ“š Resources โ€‹

Released under the MIT License.