Custom Templates

Complete examples for creating custom script templates for reusable locking and unlocking patterns.

Overview

The BSV SDK provides a template system that allows you to define custom locking and unlocking script patterns. Templates make it easy to create reusable script patterns without manually constructing Bitcoin Script each time. This is essential for applications that use custom scripts like multisig, time locks, or complex spending conditions.

Related SDK Components:

Basic Template Creation

import {
  Transaction,
  PrivateKey,
  PublicKey,
  Script,
  OP,
  LockingScript,
  UnlockingScript
} from '@bsv/sdk'

/**
 * Basic Custom Template
 *
 * Simple template for a password-locked output
 */
class PasswordLockTemplate {
  private password: string

  constructor(password: string) {
    this.password = password
  }

  /**
   * Create locking script
   *
   * Requires: <password> <signature> <pubkey>
   */
  lock(publicKeyHash: number[]): LockingScript {
    return {
      script: (): Script => {
        const script = new Script()

        // Password verification
        const passwordHash = Buffer.from(this.hashPassword(this.password), 'hex')
        script.writeOpCode(OP.OP_HASH256)
        script.writeBin(passwordHash)
        script.writeOpCode(OP.OP_EQUALVERIFY)

        // Standard P2PKH verification
        script.writeOpCode(OP.OP_DUP)
        script.writeOpCode(OP.OP_HASH160)
        script.writeBin(Buffer.from(publicKeyHash))
        script.writeOpCode(OP.OP_EQUALVERIFY)
        script.writeOpCode(OP.OP_CHECKSIG)

        return script
      }
    }
  }

  /**
   * Create unlocking script
   *
   * Provides: <password> <signature> <pubkey>
   */
  unlock(privateKey: PrivateKey, password: string): UnlockingScript {
    return {
      sign: async (tx: Transaction, inputIndex: number) => {
        const script = new Script()

        // Get locking script for this input
        const input = tx.inputs[inputIndex]
        const lockingScript = input.sourceTransaction?.outputs[input.sourceOutputIndex]?.lockingScript

        if (!lockingScript) {
          throw new Error('Cannot find locking script for input')
        }

        // Create signature
        const preimage = input.getPreimage(lockingScript)
        const signature = privateKey.sign(preimage)

        // Build unlocking script: <sig> <pubkey> <password>
        script.writeBin(signature.toDER())
        script.writeBin(privateKey.toPublicKey().encode())
        script.writeBin(Buffer.from(password, 'utf8'))

        return script
      },
      estimateLength: async () => {
        return 150 // Approximate length
      }
    }
  }

  /**
   * Hash password for script
   */
  private hashPassword(password: string): string {
    const hash = require('crypto')
      .createHash('sha256')
      .update(password)
      .digest()

    return require('crypto')
      .createHash('sha256')
      .update(hash)
      .digest('hex')
  }
}

/**
 * Usage Example
 */
async function basicTemplateExample() {
  const template = new PasswordLockTemplate('my-secret-password')
  const privateKey = PrivateKey.fromRandom()

  // Create a transaction with password-locked output
  const tx = new Transaction()

  tx.addInput({
    sourceTXID: 'funding-tx...',
    sourceOutputIndex: 0,
    unlockingScriptTemplate: template.unlock(privateKey, 'my-secret-password'),
    sequence: 0xffffffff
  })

  tx.addOutput({
    satoshis: 50000,
    lockingScript: template.lock(privateKey.toPublicKey().toHash()).script()
  })

  await tx.sign()

  console.log('Password-locked transaction created')
  console.log('Transaction ID:', tx.id('hex'))
}

Timelock Template

Multisig Template

Conditional Template

See Also

SDK Components:

Learning Paths:

Last updated