Marketplace
Complete examples for implementing marketplace functionality with escrow, dispute resolution, and secure payment patterns.
Overview
Marketplace applications require secure payment handling, escrow mechanisms, and dispute resolution. BSV's programmable scripts enable trustless escrow, atomic exchanges, and sophisticated marketplace logic. This guide covers escrow creation, buyer-seller interactions, refunds, and dispute management.
Related SDK Components:
Basic Escrow System
import { Transaction, PrivateKey, PublicKey, Script, OP, P2PKH } from '@bsv/sdk'
/**
* Escrow Manager
*
* Manage escrow transactions for marketplace transactions
*/
class EscrowManager {
/**
* Create escrow transaction
*
* Funds locked until buyer and seller both sign (2-of-2) or arbiter releases
*/
async createEscrow(params: {
buyerKey: PrivateKey
sellerPubKey: PublicKey
arbiterPubKey: PublicKey
amount: number
orderId: string
utxo: {
txid: string
vout: number
satoshis: number
script: Script
}
}): Promise<{
escrowTx: Transaction
escrow: Escrow
}> {
try {
console.log('Creating escrow for order:', params.orderId)
console.log('Amount:', params.amount)
const escrowTx = new Transaction()
// Add buyer's input
escrowTx.addInput({
sourceTXID: params.utxo.txid,
sourceOutputIndex: params.utxo.vout,
unlockingScriptTemplate: new P2PKH().unlock(params.buyerKey),
sequence: 0xffffffff
})
// Create 2-of-3 multisig escrow script
const escrowScript = this.createEscrowScript(
params.buyerKey.toPublicKey(),
params.sellerPubKey,
params.arbiterPubKey
)
// Add escrow output
escrowTx.addOutput({
satoshis: params.amount,
lockingScript: escrowScript
})
// Add change output
const fee = 1000
const change = params.utxo.satoshis - params.amount - fee
if (change >= 546) {
escrowTx.addOutput({
satoshis: change,
lockingScript: new P2PKH().lock(params.buyerKey.toPublicKey().toHash())
})
}
await escrowTx.sign()
const escrow: Escrow = {
orderId: params.orderId,
escrowTxid: escrowTx.id('hex'),
amount: params.amount,
buyerAddress: params.buyerKey.toPublicKey().toAddress(),
sellerAddress: params.sellerPubKey.toAddress(),
arbiterAddress: params.arbiterPubKey.toAddress(),
status: 'locked',
createdAt: Date.now()
}
console.log('Escrow created')
console.log('Escrow TXID:', escrow.escrowTxid)
return { escrowTx, escrow }
} catch (error) {
throw new Error(`Escrow creation failed: ${error.message}`)
}
}
/**
* Release escrow to seller (successful transaction)
*/
async releaseToSeller(
escrow: Escrow,
buyerKey: PrivateKey,
sellerKey: PrivateKey,
escrowUTXO: {
txid: string
vout: number
satoshis: number
lockingScript: Script
}
): Promise<Transaction> {
try {
console.log('Releasing escrow to seller:', escrow.orderId)
const releaseTx = new Transaction()
releaseTx.addInput({
sourceTXID: escrowUTXO.txid,
sourceOutputIndex: escrowUTXO.vout,
unlockingScript: new Script(),
sequence: 0xffffffff
})
// Pay seller
const fee = 500
const sellerAmount = escrowUTXO.satoshis - fee
releaseTx.addOutput({
satoshis: sellerAmount,
lockingScript: Script.fromAddress(escrow.sellerAddress)
})
// Create 2-of-3 multisig unlocking script
// Both buyer and seller sign
const input = releaseTx.inputs[0]
const preimage = input.getPreimage(escrowUTXO.lockingScript)
const buyerSig = buyerKey.sign(preimage)
const sellerSig = sellerKey.sign(preimage)
// Build unlocking script: OP_0 <sig1> <sig2>
const unlockingScript = new Script()
unlockingScript.writeOpCode(OP.OP_0) // OP_CHECKMULTISIG bug
unlockingScript.writeBin(buyerSig.toDER())
unlockingScript.writeBin(sellerSig.toDER())
releaseTx.inputs[0].unlockingScript = unlockingScript
console.log('Escrow released to seller')
console.log('Release TXID:', releaseTx.id('hex'))
escrow.status = 'released'
escrow.releasedAt = Date.now()
escrow.releaseTxid = releaseTx.id('hex')
return releaseTx
} catch (error) {
throw new Error(`Escrow release failed: ${error.message}`)
}
}
/**
* Refund escrow to buyer (cancelled or disputed)
*/
async refundToBuyer(
escrow: Escrow,
buyerKey: PrivateKey,
arbiterKey: PrivateKey,
escrowUTXO: {
txid: string
vout: number
satoshis: number
lockingScript: Script
}
): Promise<Transaction> {
try {
console.log('Refunding escrow to buyer:', escrow.orderId)
const refundTx = new Transaction()
refundTx.addInput({
sourceTXID: escrowUTXO.txid,
sourceOutputIndex: escrowUTXO.vout,
unlockingScript: new Script(),
sequence: 0xffffffff
})
// Refund to buyer
const fee = 500
const refundAmount = escrowUTXO.satoshis - fee
refundTx.addOutput({
satoshis: refundAmount,
lockingScript: Script.fromAddress(escrow.buyerAddress)
})
// Create unlocking script with buyer and arbiter signatures
const input = refundTx.inputs[0]
const preimage = input.getPreimage(escrowUTXO.lockingScript)
const buyerSig = buyerKey.sign(preimage)
const arbiterSig = arbiterKey.sign(preimage)
const unlockingScript = new Script()
unlockingScript.writeOpCode(OP.OP_0)
unlockingScript.writeBin(buyerSig.toDER())
unlockingScript.writeBin(arbiterSig.toDER())
refundTx.inputs[0].unlockingScript = unlockingScript
console.log('Escrow refunded to buyer')
console.log('Refund TXID:', refundTx.id('hex'))
escrow.status = 'refunded'
escrow.refundedAt = Date.now()
escrow.refundTxid = refundTx.id('hex')
return refundTx
} catch (error) {
throw new Error(`Escrow refund failed: ${error.message}`)
}
}
/**
* Create 2-of-3 multisig escrow script
*/
private createEscrowScript(
buyerPubKey: PublicKey,
sellerPubKey: PublicKey,
arbiterPubKey: PublicKey
): Script {
const script = new Script()
// 2-of-3 multisig: 2 <pubkey1> <pubkey2> <pubkey3> 3 OP_CHECKMULTISIG
script.writeOpCode(OP.OP_2)
script.writeBin(buyerPubKey.encode())
script.writeBin(sellerPubKey.encode())
script.writeBin(arbiterPubKey.encode())
script.writeOpCode(OP.OP_3)
script.writeOpCode(OP.OP_CHECKMULTISIG)
return script
}
}
interface Escrow {
orderId: string
escrowTxid: string
amount: number
buyerAddress: string
sellerAddress: string
arbiterAddress: string
status: 'locked' | 'released' | 'refunded' | 'disputed'
createdAt: number
releasedAt?: number
refundedAt?: number
releaseTxid?: string
refundTxid?: string
}
/**
* Usage Example
*/
async function escrowExample() {
const escrowManager = new EscrowManager()
const buyer = PrivateKey.fromRandom()
const seller = PrivateKey.fromRandom()
const arbiter = PrivateKey.fromRandom()
const buyerUTXO = {
txid: 'buyer-utxo...',
vout: 0,
satoshis: 200000,
script: new P2PKH().lock(buyer.toPublicKey().toHash())
}
// Create escrow
const { escrowTx, escrow } = await escrowManager.createEscrow({
buyerKey: buyer,
sellerPubKey: seller.toPublicKey(),
arbiterPubKey: arbiter.toPublicKey(),
amount: 100000,
orderId: 'ORDER-001',
utxo: buyerUTXO
})
console.log('Escrow created:', escrow)
// Later: Release to seller
const escrowUTXO = {
txid: escrowTx.id('hex'),
vout: 0,
satoshis: 100000,
lockingScript: escrowTx.outputs[0].lockingScript
}
const releaseTx = await escrowManager.releaseToSeller(
escrow,
buyer,
seller,
escrowUTXO
)
console.log('Released to seller:', releaseTx.id('hex'))
}Marketplace Order Manager
Atomic Swap Marketplace
Related Examples
See Also
SDK Components:
Transaction - Transaction building
Script - Bitcoin script operations
Script Templates - Template system
Signatures - Digital signatures
Learning Paths:
Last updated
