Use xo-cash/utils sse. Add vending machine template. Greatly improve startup times.

This commit is contained in:
2026-05-24 19:35:50 +02:00
parent def261b568
commit 85746c3306
11 changed files with 367 additions and 31 deletions

View File

@@ -0,0 +1,277 @@
import type { XOTemplate } from '@xo-cash/types';
/**
* Vending machine payment template.
*
* Merchant creates a purchaseItems invitation with receipt variables;
* customer funds and signs the composable transaction.
*/
export const vendingMachineTemplate: XOTemplate = {
$schema: 'https://libauth.org/schemas/wallet-template-v0.schema.json',
name: 'Vending Machine',
description: 'Purchase items from a vending machine with an itemized receipt.',
icon: 'wallet',
version: '1',
supported: ['BCH_2023_05', 'BCH_2024_05', 'BCH_2025_05', 'BCH_2026_05'],
defaults: {
change: {
output: 'changeOutput',
role: 'merchant',
generate: ['merchantKey'],
},
},
roles: {
merchant: {
name: 'Merchant',
description: 'The vending machine operator receiving payment.',
icon: 'owner',
},
customer: {
name: 'Customer',
description: 'The customer paying for items.',
icon: 'sender',
},
},
start: [
{
action: 'purchaseItems',
role: 'merchant',
generate: ['merchantKey'],
},
],
actions: {
purchaseItems: {
name: 'Purchase Items',
description: 'Purchase: $(<receiptSummary>) for $(<totalSatoshis>) sats',
icon: 'request',
roles: {
merchant: {
name: 'Sell Items',
description: 'Receive payment for $(<receiptSummary>)',
icon: 'request',
requirements: {
secrets: ['merchantKey'],
variables: [
'totalSatoshis',
'orderId',
'merchantName',
'receiptSummary',
'lineItemsJson',
],
},
},
customer: {
name: 'Pay',
description: 'Pay $(<totalSatoshis>) sats for $(<receiptSummary>)',
icon: 'send',
requirements: {},
},
},
requirements: {
participants: [
{ role: 'merchant', slots: { min: 1, max: 1 } },
{ role: 'customer', slots: { min: 1 } },
],
},
transaction: 'purchaseItemsTransaction',
},
},
transactions: {
purchaseItemsTransaction: {
name: 'Vending Purchase',
description: 'Order $(<orderId>): $(<receiptSummary>)',
icon: 'request',
roles: {
merchant: {
name: 'Received Payment',
description: 'Received $(<totalSatoshis>) sats from $(<merchantName>) sale',
icon: 'receive',
},
customer: {
name: 'Sent Payment',
description: 'Paid $(<totalSatoshis>) sats for $(<receiptSummary>)',
icon: 'send',
},
},
inputs: [],
outputs: [{ output: 'purchaseOutput' }],
version: 2,
locktime: 0,
composable: true,
},
},
/** No custom input templates — customer UTXOs are selected at funding time. */
inputs: {},
outputs: {
changeOutput: {
name: 'Change',
description: 'Funds returned as change.',
icon: 'receive',
lockingScript: 'merchantReceivingLockingScript',
},
purchaseOutput: {
name: 'Purchase Payment',
description: '$(<totalSatoshis>) sats to $(<merchantName>)',
icon: 'request',
roles: {
merchant: {
name: 'Payment Received',
description: 'Received $(<totalSatoshis>) sats for $(<receiptSummary>)',
},
customer: {
name: 'Payment Sent',
description: 'Sent $(<totalSatoshis>) sats for $(<receiptSummary>)',
},
},
lockingScript: 'merchantReceivingLockingScript',
valueSatoshis: '$(<totalSatoshis>)',
token: null,
},
},
lockingScripts: {
merchantReceivingLockingScript: {
name: 'Merchant Receive',
description: 'Funds received by the vending machine merchant.',
icon: 'address',
lockingType: 'p2pkh',
lockingBytecode: 'lockMerchantP2PKH',
unlockingBytecode: 'unlockMerchantP2PKH',
actions: [],
state: { variables: [], secrets: [] },
balance: {},
roles: {
merchant: {
state: {
variables: [],
secrets: ['merchantKey'],
},
actions: [],
balance: {
satoshis: true,
fungibleTokens: true,
nonfungibleTokens: true,
},
selectable: true,
},
},
},
},
scripts: {
lockMerchantP2PKH:
'OP_DUP OP_HASH160 <$(<merchantKey.public_key> OP_HASH160)> OP_EQUALVERIFY OP_CHECKSIG',
unlockMerchantP2PKH:
'<merchantKey.schnorr_signature.all_outputs> <merchantKey.public_key>',
},
constants: {
dustLimit: {
name: 'Dust Limit',
description: 'Minimum satoshis for P2PKH outputs.',
type: 'integer',
value: 546,
},
},
variables: {
merchantKey: {
name: 'Merchant Private Key',
description: 'Private key for the vending machine merchant wallet.',
type: 'bytes',
hint: 'private_key',
},
totalSatoshis: {
name: 'Total Price',
description: 'Total purchase price in satoshis',
type: 'integer',
hint: 'satoshis',
},
orderId: {
name: 'Order ID',
description: 'Unique order identifier',
type: 'string',
},
merchantName: {
name: 'Merchant Name',
description: 'Display name of the vending machine',
type: 'string',
},
receiptSummary: {
name: 'Receipt Summary',
description: 'Human-readable list of purchased items',
type: 'string',
},
lineItemsJson: {
name: 'Line Items',
description: 'JSON-encoded line items for the purchase',
type: 'string',
},
},
icons: [
{ name: 'wallet', hash: '0000000000000000000000' },
{ name: 'owner', hash: '0000000000000000000000' },
{ name: 'sender', hash: '0000000000000000000000' },
{ name: 'request', hash: '0000000000000000000000' },
{ name: 'receive', hash: '0000000000000000000000' },
{ name: 'send', hash: '0000000000000000000000' },
],
scenarios: [
{
name: 'purchase items happy path',
description: 'Merchant requests payment for vending machine items.',
action: 'purchaseItems',
roles: [
{
role: 'merchant',
values: {
generated: {
merchantKey: 'KyRQa5pEXuzVcDwnXRLpYAascjchQW5DoxVRMbj4DTxS83573mz8',
},
variables: {
totalSatoshis: 3500,
orderId: 'order-demo-1',
merchantName: 'XO Snack Machine',
receiptSummary: '2× Cola, 1× Chips',
lineItemsJson: '[{"name":"Cola","qty":2},{"name":"Chips","qty":1}]',
},
secrets: {},
inputs: [],
outputs: [
{
lockingBytecode: '76a91475c715ecb74178fe87933e57e947e5e92d904b8188ac',
valueSatoshis: 3500,
},
],
},
},
{
role: 'customer',
values: {
generated: {},
variables: {},
secrets: {},
inputs: [],
outputs: [],
},
},
],
},
],
};