From ccfaf3fd705b400f43215af329c539ab7b43e894 Mon Sep 17 00:00:00 2001 From: Harvmaster Date: Mon, 4 May 2026 04:45:31 +0000 Subject: [PATCH] Update to use published packages. Update types. Update readme. Fix tests. --- package-lock.json | 165 +- package.json | 6 +- readme.md | 3 + src/services/invitation.ts | 23 +- src/utils/invitation-flow.ts | 9 +- tests/cli/mocks/engine.ts | 4 +- tests/cli/mocks/template-p2pkh.ts | 2523 ++++++++++++++--------------- 7 files changed, 1352 insertions(+), 1381 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1f98eb0..1e3807c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,11 +12,11 @@ "@bitauth/libauth": "^3.0.0", "@electrum-cash/protocol": "^2.3.1", "@generalprotocols/oracle-client": "^0.0.1-development.11945476152", - "@xo-cash/crypto": "file:../crypto", + "@xo-cash/crypto": "^0.0.1", "@xo-cash/engine": "file:../engine", "@xo-cash/state": "file:../state", - "@xo-cash/templates": "file:../templates", - "@xo-cash/types": "file:../types", + "@xo-cash/templates": "^0.0.1", + "@xo-cash/types": "^0.0.1", "better-sqlite3": "^12.6.2", "clipboardy": "^5.1.0", "ink": "^6.6.0", @@ -41,38 +41,6 @@ "vitest": "^4.1.2" } }, - "../crypto": { - "name": "@xo-cash/crypto", - "version": "0.0.1", - "license": "MIT", - "dependencies": { - "@bitauth/libauth": "^3.1.0-next.8", - "@xo-cash/primitives": "0.0.1", - "@xo-cash/types": "0.0.1" - }, - "devDependencies": { - "@chalp/eslint-airbnb": "^1.3.0", - "@generalprotocols/cspell-dictionary": "^1.0.1", - "@stylistic/eslint-plugin": "^5.7.0", - "@typescript-eslint/eslint-plugin": "^8.53.1", - "@typescript-eslint/parser": "^8.53.1", - "@vitest/coverage-v8": "^4.0.17", - "@viz-kit/esbuild-analyzer": "^1.0.0", - "@xo-cash/eslint-config": "1.0.1", - "cspell": "^9.6.0", - "eslint": "^9.39.2", - "prettier": "^3.6.2", - "tsdown": "^0.20.0-beta.4", - "typedoc": "^0.28.16", - "typedoc-plugin-coverage": "^4.0.2", - "typescript": "^5.3.2", - "typescript-eslint": "^8.53.1", - "vitest": "^4.0.17" - }, - "engines": { - "node": ">=18.0.0" - } - }, "../engine": { "name": "@xo-cash/engine", "version": "0.0.1", @@ -85,7 +53,8 @@ "@electrum-cash/servers": "^3.1.0", "@xo-cash/crypto": "0.0.1", "@xo-cash/primitives": "0.0.1", - "@xo-cash/state": "0.0.1", + "@xo-cash/state": "0.0.2", + "@xo-cash/templates": "0.0.1", "@xo-cash/types": "0.0.1", "@xo-cash/utils": "0.0.1", "eventemitter3": "^5.0.1" @@ -108,14 +77,11 @@ "typescript": "^5.3.2", "typescript-eslint": "^8.53.1", "vitest": "^4.0.17" - }, - "engines": { - "node": ">=18.0.0" } }, "../state": { "name": "@xo-cash/state", - "version": "0.0.1", + "version": "0.0.2", "license": "MIT", "dependencies": { "@bitauth/libauth": "^3.1.0-next.8", @@ -147,60 +113,6 @@ "vitest": "^4.0.17" } }, - "../templates": { - "name": "@xo-cash/templates", - "version": "0.0.1", - "license": "MIT", - "dependencies": { - "@xo-cash/types": "0.0.1-development.13504604083" - }, - "devDependencies": { - "@chalp/eslint-airbnb": "^1.3.0", - "@generalprotocols/cspell-dictionary": "^1.0.1", - "@stylistic/eslint-plugin": "^5.7.0", - "@typescript-eslint/eslint-plugin": "^8.53.1", - "@typescript-eslint/parser": "^8.53.1", - "@vitest/coverage-v8": "^4.0.17", - "@viz-kit/esbuild-analyzer": "^1.0.0", - "@xo-cash/eslint-config": "1.0.1", - "cspell": "^9.6.0", - "eslint": "^9.39.2", - "prettier": "^3.6.2", - "tsdown": "^0.20.0-beta.4", - "typedoc": "^0.28.16", - "typedoc-plugin-coverage": "^4.0.2", - "typescript": "^5.3.2", - "typescript-eslint": "^8.53.1", - "vitest": "^4.0.17" - } - }, - "../types": { - "name": "@xo-cash/types", - "version": "0.0.1", - "license": "MIT", - "dependencies": { - "@bitauth/libauth": "^3.1.0-next.8" - }, - "devDependencies": { - "@chalp/eslint-airbnb": "^1.3.0", - "@generalprotocols/cspell-dictionary": "^1.0.1", - "@stylistic/eslint-plugin": "^5.7.0", - "@typescript-eslint/eslint-plugin": "^8.53.1", - "@typescript-eslint/parser": "^8.53.1", - "@vitest/coverage-v8": "^4.0.17", - "@viz-kit/esbuild-analyzer": "^1.0.0", - "@xo-cash/eslint-config": "1.0.1", - "cspell": "^9.6.0", - "eslint": "^9.39.2", - "prettier": "^3.6.2", - "tsdown": "^0.20.0-beta.4", - "typedoc": "^0.28.16", - "typedoc-plugin-coverage": "^4.0.2", - "typescript": "^5.3.2", - "typescript-eslint": "^8.53.1", - "vitest": "^4.0.17" - } - }, "node_modules/@alcalzone/ansi-tokenize": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.2.4.tgz", @@ -971,24 +883,77 @@ } }, "node_modules/@xo-cash/crypto": { - "resolved": "../crypto", - "link": true + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@xo-cash/crypto/-/crypto-0.0.1.tgz", + "integrity": "sha512-ZIa9MHAVCBJqo5uxyx/Tx/jTSyyJw1cfYfI48gEHqBIl5wyyxiZDx4eZvVWSr8uKgS5Tm3FXUkKQybvk5QGRIQ==", + "license": "MIT", + "dependencies": { + "@bitauth/libauth": "^3.1.0-next.8", + "@xo-cash/primitives": "0.0.1", + "@xo-cash/types": "0.0.1" + } + }, + "node_modules/@xo-cash/crypto/node_modules/@bitauth/libauth": { + "version": "3.1.0-next.8", + "resolved": "https://registry.npmjs.org/@bitauth/libauth/-/libauth-3.1.0-next.8.tgz", + "integrity": "sha512-Pm+Ju+YP3JeBLLTiVrBnia2wwE4G17r4XqpvPRMcklElJTe8J6x3JgKRg1by0Xm3ZY6UFxACkEAoSA+x419/zA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } }, "node_modules/@xo-cash/engine": { "resolved": "../engine", "link": true }, + "node_modules/@xo-cash/primitives": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@xo-cash/primitives/-/primitives-0.0.1.tgz", + "integrity": "sha512-medxVK9Sawj7oIDhWvTjTgzwf6BjGao6CXtQYJOUFi6NOO1eclb1PDjEmkG/4NeK3v7LQIN8QS60mTAGyS9FXg==", + "license": "MIT", + "dependencies": { + "@bitauth/libauth": "^3.1.0-next.8" + } + }, + "node_modules/@xo-cash/primitives/node_modules/@bitauth/libauth": { + "version": "3.1.0-next.8", + "resolved": "https://registry.npmjs.org/@bitauth/libauth/-/libauth-3.1.0-next.8.tgz", + "integrity": "sha512-Pm+Ju+YP3JeBLLTiVrBnia2wwE4G17r4XqpvPRMcklElJTe8J6x3JgKRg1by0Xm3ZY6UFxACkEAoSA+x419/zA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/@xo-cash/state": { "resolved": "../state", "link": true }, "node_modules/@xo-cash/templates": { - "resolved": "../templates", - "link": true + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@xo-cash/templates/-/templates-0.0.1.tgz", + "integrity": "sha512-v5f0YeH9Bw6lNThdE0fI878T4L2jbM8RI1quxdKxnvqHn9hu2jzebqvveEB2TfJWG3sP1GpE1go0Yn87R4sXfw==", + "license": "MIT", + "dependencies": { + "@xo-cash/types": "0.0.1" + } }, "node_modules/@xo-cash/types": { - "resolved": "../types", - "link": true + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@xo-cash/types/-/types-0.0.1.tgz", + "integrity": "sha512-BMwh2Y9+LqnTXYmdA7Nxi1NuK+AcsNWFoFGJVAvuY5TBfsbNIzWppjmrI2fAyj/RlSE3tATMxam+6CJb3RnDIA==", + "license": "MIT", + "dependencies": { + "@bitauth/libauth": "^3.1.0-next.8" + } + }, + "node_modules/@xo-cash/types/node_modules/@bitauth/libauth": { + "version": "3.1.0-next.8", + "resolved": "https://registry.npmjs.org/@bitauth/libauth/-/libauth-3.1.0-next.8.tgz", + "integrity": "sha512-Pm+Ju+YP3JeBLLTiVrBnia2wwE4G17r4XqpvPRMcklElJTe8J6x3JgKRg1by0Xm3ZY6UFxACkEAoSA+x419/zA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } }, "node_modules/ansi-escapes": { "version": "7.2.0", diff --git a/package.json b/package.json index 5e95750..d6d8e9f 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,11 @@ "@bitauth/libauth": "^3.0.0", "@electrum-cash/protocol": "^2.3.1", "@generalprotocols/oracle-client": "^0.0.1-development.11945476152", - "@xo-cash/crypto": "file:../crypto", + "@xo-cash/crypto": "^0.0.1", "@xo-cash/engine": "file:../engine", "@xo-cash/state": "file:../state", - "@xo-cash/templates": "file:../templates", - "@xo-cash/types": "file:../types", + "@xo-cash/templates": "^0.0.1", + "@xo-cash/types": "^0.0.1", "better-sqlite3": "^12.6.2", "clipboardy": "^5.1.0", "ink": "^6.6.0", diff --git a/readme.md b/readme.md index 4e607b8..2be3f81 100644 --- a/readme.md +++ b/readme.md @@ -13,6 +13,9 @@ git clone git@gitlab.com:GeneralProtocols/xo/engine.git # Move into teh engine directory cd engine +# Checkout the cli-test branch +git checkout cli-test + # Install the dependencies npm ci diff --git a/src/services/invitation.ts b/src/services/invitation.ts index ed5b0d3..400b9c4 100644 --- a/src/services/invitation.ts +++ b/src/services/invitation.ts @@ -2,7 +2,7 @@ import type { AcceptInvitationParameters, AppendInvitationParameters, Engine, - FindSuitableResourcesParameters, + GetSpendableResourcesParameters, } from "@xo-cash/engine"; import { hasInvitationExpired, mergeInvitationCommits } from "@xo-cash/engine"; import type { @@ -483,12 +483,27 @@ export class Invitation extends EventEmitter { } async findSuitableResources( - options: Partial = {}, + options: Partial = {}, ): Promise { + const templateIdentifier = + options.templateIdentifier ?? this.data.templateIdentifier; + const template = await this.engine.getTemplate(templateIdentifier); + const fallbackOutputIdentifier = Object.keys(template?.outputs ?? {})[0]; + if (!fallbackOutputIdentifier && !options.outputIdentifier) { + throw new Error( + `No output identifiers found for template: ${templateIdentifier}`, + ); + } + + const resolvedOptions: GetSpendableResourcesParameters = { + templateIdentifier, + outputIdentifier: options.outputIdentifier ?? fallbackOutputIdentifier ?? "", + }; + // Find the suitable resources - const { unspentOutputs } = await this.engine.findSuitableResources( + const { unspentOutputs } = await this.engine.getSpendableResources( this.data, - options, + resolvedOptions, ); // Update the status of the invitation diff --git a/src/utils/invitation-flow.ts b/src/utils/invitation-flow.ts index b32f122..0ddff14 100644 --- a/src/utils/invitation-flow.ts +++ b/src/utils/invitation-flow.ts @@ -132,13 +132,12 @@ export const resolveProvidedLockingBytecodeHex = ( variableValues: Record, ): string | undefined => { const outputDefinition = template.outputs?.[outputIdentifier]; - if (!outputDefinition || typeof outputDefinition.lockscript !== "string") + if (!outputDefinition || typeof outputDefinition.lockingScript !== "string") { return undefined; + } - const lockingScriptDefinition = ( - template.lockingScripts as Record | undefined - )?.[outputDefinition.lockscript] as { lockingScript?: string } | undefined; - const scriptIdentifier = lockingScriptDefinition?.lockingScript; + const lockingScriptDefinition = template.lockingScripts?.[outputDefinition.lockingScript]; + const scriptIdentifier = lockingScriptDefinition?.lockingBytecode; if (!scriptIdentifier) return undefined; const scriptExpression = ( diff --git a/tests/cli/mocks/engine.ts b/tests/cli/mocks/engine.ts index 0181680..18ee48f 100644 --- a/tests/cli/mocks/engine.ts +++ b/tests/cli/mocks/engine.ts @@ -73,7 +73,7 @@ export const addFakeResource = async ( outpointTransactionHash: options.outpointTransactionHash ?? randomTxHash(), minedAtHeight: options.minedAtHeight ?? 800000, valueSatoshis: options.valueSatoshis ?? 10000, - lockingBytecode: + scriptHash: options.lockingBytecode ?? "76a914000000000000000000000000000000000000000088ac", reservedBy: options.reservedBy, @@ -131,7 +131,7 @@ export const unreserveResource = async ( export const createMockEngine = async (seed: string) => { // Create the in-memory storage adapter. const storage = await createStorageAdapter({ - storageType: StorageType.INMEMORY, + storageType: "inmemory", accountHash: binToHex(sha256.hash(convertMnemonicToSeedBytes(seed))), }); diff --git a/tests/cli/mocks/template-p2pkh.ts b/tests/cli/mocks/template-p2pkh.ts index 1e1e7ce..0cb3033 100644 --- a/tests/cli/mocks/template-p2pkh.ts +++ b/tests/cli/mocks/template-p2pkh.ts @@ -1,1451 +1,1440 @@ import type { XOTemplate } from "@xo-cash/types"; -import { generateTemplateIdentifier } from "@xo-cash/engine"; +import { generateTemplateIdentifier, parseTemplate } from "@xo-cash/engine"; export const p2pkhTemplate: XOTemplate = { - $schema: "https://libauth.org/schemas/wallet-template-v0.schema.json", + $schema: 'https://libauth.org/schemas/wallet-template-v0.schema.json', // Name for this template. - name: "Wallet (P2PKH)", + name: 'Wallet (P2PKH)', // Description for this template. - description: - "A standard single-factor wallet template that uses Pay-to-Public-Key-Hash (P2PKH) locking scripts.", + description: 'A standard single-factor wallet template that uses Pay-to-Public-Key-Hash (P2PKH) locking scripts.', // Icon for this template. - icon: "wallet", + icon: 'wallet', // Version number for this template. - version: 0, + version: '1', // List of VM versions that can be used to run this template. - supported: ["BCH_2023_05", "BCH_2024_05", "BCH_2025_05", "BCH_2026_05"], + supported: [ 'BCH_2023_05', 'BCH_2024_05', 'BCH_2025_05', 'BCH_2026_05' ], + + // Sets optional default values to be used with this template. + defaults: { + // Configures a default intent structure for creating change outputs and locking scripts. + // NOTE: This is used when the engine needs to make change for an output created by + // this template and there is no other policy provided elsewhere that takes precedence. + // NOTE: It is recommended that templates that create outputs with the 'selectable' property + // either provides a change policy here, or a comment that explain that they + // intentionally omit the change policy and why. + change: { + output: 'changeOutput', + role: 'receiver', + generate: [ 'ownerKey' ], + }, + }, // Describe a list of roles that are used in this template. + // NOTE: Template roles are held only for the duration of one specific action/invitation. + // For example, the same entity that acts as 'receiver' when creating/receiving an output + // can later act as 'sender' (or 'owner') when performing a follow-up action. roles: { - owner: { - name: "Wallet Owner", - description: "The party who can spend from this wallet.", - icon: "owner", - }, - receiver: { - name: "Receiver", - description: "A party that is receiving value.", - icon: "receiver", - }, - sender: { - name: "Sender", - description: "A party that is sending value.", - icon: "sender", - }, + owner: { + name: 'Wallet Owner', + description: 'The party who can spend from this wallet.', + icon: 'owner', + }, + receiver: { + name: 'Receiver', + description: 'A party that is receiving value.', + icon: 'receiver', + }, + sender: { + name: 'Sender', + description: 'A party that is sending value.', + icon: 'sender', + }, }, // Define a list of entrypoints supported by this template. start: [ - { - action: "receive", - role: "receiver", - }, - { - action: "requestSatoshis", - role: "receiver", - }, - { - action: "requestFungibleTokens", - role: "receiver", - }, - { - action: "requestNonfungibleTokens", - role: "receiver", - }, + { + action: 'receive', + role: 'receiver', + generate: [ 'ownerKey' ], + }, + { + action: 'requestSatoshis', + role: 'receiver', + generate: [ 'ownerKey' ], + }, + { + action: 'requestFungibleTokens', + role: 'receiver', + generate: [ 'ownerKey' ], + }, + { + action: 'requestNonfungibleTokens', + role: 'receiver', + generate: [ 'ownerKey' ], + }, ], // Define a list of actions that can be taken by this template. // NOTE: There is no action to generate an address, but a wallet can create an invitation to a receive action and // extract the generated lockscript as needed as the engine will track all lockscripts it generates. actions: { - receive: { - // TODO: Consider rewriting to be generic/role-less. - name: "Receive", - description: - "Receive an unspecified amount of cash and/or tokens from one or more senders.", - icon: "receive", + receive: { + // TODO: Consider rewriting to be generic/role-less. + name: 'Receive', + description: 'Receive an unspecified amount of cash and/or tokens from one or more senders.', + icon: 'receive', - roles: { - receiver: { - name: "Receive", - description: - "Receive an unspecified amount of cash and/or tokens from one or more senders.", - icon: "receive", + roles: { + receiver: { + name: 'Receive', + description: 'Receive an unspecified amount of cash and/or tokens from one or more senders.', + icon: 'receive', + + requirements: { + secrets: [ 'ownerKey' ], + }, + }, + sender: { + name: 'Send', + description: 'Send an unspecified amount of cash and/or tokens to the provided receiver.', + icon: 'send', + + // The sender only need to provide blockchain-level requirements. + // NOTE: This field is not required when empty, but shown here for illustrative purposes. + requirements: {}, + }, + }, requirements: { - generate: ["ownerKey"], + participants: [ + { + role: 'receiver', + slots: { min: 1, max: 1 }, + }, + { + role: 'sender', + slots: { min: 1, max: undefined }, + }, + ], + // variables: [ 'requestedSatoshis' ], }, - }, - sender: { - name: "Send", - description: - "Send an unspecified amount of cash and/or tokens to the provided receiver.", - icon: "send", - }, + + transaction: 'receiveTransaction', }, + requestSatoshis: { + // TODO: Consider rewriting to be generic/role-less. + name: 'Request Satoshis', + description: 'Requests a specific amount of Bitcoin Cash from one or more senders.', + icon: 'request', - requirements: { - roles: [ - { - role: "receiver", - slots: { min: 1, max: 1 }, + roles: { + receiver: { + name: 'Request Satoshis', + description: 'Requests a specific amount of Bitcoin Cash from one or more senders.', + icon: 'request', + + requirements: { + secrets: [ 'ownerKey' ], + variables: [ 'requestedSatoshis' ], + }, + }, + sender: { + name: 'Send', + description: 'Send a specific amount of Bitcoin Cash to the provided receiver.', + icon: 'send', + + // The sender only need to provide blockchain-level requirements. + // NOTE: This field is not required when empty, but shown here for illustrative purposes. + requirements: {}, + }, }, - { - role: "sender", - slots: { min: 1, max: undefined }, - }, - ], - variables: ["requestedSatoshis"], - }, - - transaction: "receiveTransaction", - }, - requestSatoshis: { - // TODO: Consider rewriting to be generic/role-less. - name: "Request Satoshis", - description: - "Requests a specific amount of Bitcoin Cash from one or more senders.", - icon: "request", - - roles: { - receiver: { - name: "Request Satoshis", - description: - "Requests a specific amount of Bitcoin Cash from one or more senders.", - icon: "request", requirements: { - generate: ["ownerKey"], - variables: ["requestedSatoshis"], + participants: [ + { + role: 'receiver', + slots: { min: 1, max: 1 }, + }, + { + role: 'sender', + slots: { min: 1, max: undefined }, + }, + ], }, - }, - sender: { - name: "Send", - description: - "Send a specific amount of Bitcoin Cash to the provided receiver.", - icon: "send", - }, + + transaction: 'requestSatoshisTransaction', }, + requestFungibleTokens: { + // TODO: Consider rewriting to be generic/role-less. + name: 'Request Fungible Tokens', + description: 'Requests a specific amount of a fungible tokens from one or more senders.', + icon: 'request', - requirements: { - roles: [ - { - role: "receiver", - slots: { min: 1, max: 1 }, + roles: { + receiver: { + name: 'Request Fungible Tokens', + description: 'Requests a specific amount of a fungible tokens from one or more senders.', + icon: 'request', + + requirements: { + secrets: [ 'ownerKey' ], + variables: [ 'requestedTokenCategory', 'requestedTokenAmount' ], + }, + }, + sender: { + name: 'Send', + description: 'Send a specific amount of fungible tokens to the provided receiver.', + icon: 'send', + + // The sender only need to provide blockchain-level requirements. + // NOTE: This field is not required when empty, but shown here for illustrative purposes. + requirements: {}, + }, }, - { - role: "sender", - slots: { min: 1, max: undefined }, - }, - ], - }, - - transaction: "requestSatoshisTransaction", - }, - requestFungibleTokens: { - // TODO: Consider rewriting to be generic/role-less. - name: "Request Fungible Tokens", - description: - "Requests a specific amount of a fungible tokens from one or more senders.", - icon: "request", - - roles: { - receiver: { - name: "Request Fungible Tokens", - description: - "Requests a specific amount of a fungible tokens from one or more senders.", - icon: "request", requirements: { - generate: ["ownerKey"], - variables: ["requestedTokenCategory", "requestedTokenAmount"], + participants: [ + { + role: 'receiver', + slots: { min: 1, max: 1 }, + }, + { + role: 'sender', + slots: { min: 1, max: undefined }, + }, + ], }, - }, - sender: { - name: "Send", - description: - "Send a specific amount of fungible tokens to the provided receiver.", - icon: "send", - }, + + transaction: 'requestFungibleTokensTransaction', }, + requestNonfungibleTokens: { + // TODO: Consider rewriting to be generic/role-less. + name: 'Request a Non-fungible Token', + description: 'Requests a non-fungible token from one or more senders.', + icon: 'request', - requirements: { - roles: [ - { - role: "receiver", - slots: { min: 1, max: 1 }, + roles: { + receiver: { + name: 'Request a Non-fungible Token', + description: 'Requests a non-fungible token from one or more senders.', + icon: 'request', + + requirements: { + secrets: [ 'ownerKey' ], + variables: [ 'requestedTokenCategory', 'requestedTokenCapability', 'requestedTokenCommitment' ], + }, + }, + sender: { + name: 'Send', + description: 'Send a non-fungible token to the provided receiver.', + icon: 'send', + + // The sender only need to provide blockchain-level requirements. + // NOTE: This field is not required when empty, but shown here for illustrative purposes. + requirements: {}, + }, }, - { - role: "sender", - slots: { min: 1, max: undefined }, - }, - ], - }, - - transaction: "requestFungibleTokensTransaction", - }, - requestNonfungibleTokens: { - // TODO: Consider rewriting to be generic/role-less. - name: "Request a Non-fungible Token", - description: "Requests a non-fungible token from one or more senders.", - icon: "request", - - roles: { - receiver: { - name: "Request a Non-fungible Token", - description: - "Requests a non-fungible token from one or more senders.", - icon: "request", requirements: { - generate: ["ownerKey"], - variables: [ - "requestedTokenCategory", - "requestedTokenCapability", - "requestedTokenCommitment", - ], + participants: [ + { + role: 'receiver', + slots: { min: 1, max: 1 }, + }, + { + role: 'sender', + slots: { min: 1, max: undefined }, + }, + ], }, - }, - sender: { - name: "Send", - description: "Send a non-fungible token to the provided receiver.", - icon: "send", - }, + + transaction: 'requestNonfungibleTokensTransaction', }, - requirements: { - roles: [ - { - role: "receiver", - slots: { min: 1, max: 1 }, + // NOTE: Sending value can be done without explicit template support. + // NOTE: This feature is explicitly defined in this template to demonstrate how versatile templates can be, and + // to ensure the feature is discoverable by the user from outputs that hold value. + sendSatoshis: { + name: 'Send Satoshis', + description: 'Sends a specific amount of Bitcoin Cash to a given recipient.', + icon: 'send', + + roles: { + sender: { + requirements: { + variables: [ 'transferredSatoshis', 'recipientLockingscript' ], + secrets: [ 'ownerKey' ], + }, + }, }, - { - role: "sender", - slots: { min: 1, max: undefined }, - }, - ], - }, - transaction: "requestNonfungibleTokensTransaction", - }, - - // NOTE: Sending value can be done without explicit template support. - // NOTE: This feature is explicitly defined in this template to demonstrate how versatile templates can be, and - // to ensure the feature is discoverable by the user from outputs that hold value. - sendSatoshis: { - name: "Send Satoshis", - description: - "Sends a specific amount of Bitcoin Cash to a given recipient.", - icon: "send", - - roles: { - sender: { requirements: { - variables: ["transferredSatoshis", "recipientLockingscript"], - secrets: ["ownerKey"], + participants: [ + { + role: 'sender', + slots: { min: 1, max: 1 }, + }, + ], }, - }, - }, - requirements: { - roles: [ - { - role: "sender", - slots: { min: 1, max: 1 }, + // Sending is only available for outputs that have sufficient satoshis on them. + // NOTE: Dust is enforced here according to standardness rules. + conditions: [ '$(OP_INPUTINDEX OP_UTXOVALUE OP_GREATERTHAN)' ], + + transaction: 'transferSatoshisTransaction', + }, + sendFungibleTokens: { + name: 'Send Fungible Tokens', + description: 'Send a specific amount of a fungible token to a given recipient.', + icon: 'send', + + roles: { + sender: { + requirements: { + variables: [ 'transferredTokenCategory', 'transferredTokenAmount', 'recipientLockingscript' ], + secrets: [ 'ownerKey' ], + }, + }, }, - ], - }, - // Sending is only available for outputs that have sufficient satoshis on them. - // NOTE: Dust is enforced here according to standardness rules. - condition: "$(OP_INPUTINDEX OP_UTXOVALUE OP_GREATERTHAN)", - - transaction: "transferSatoshisTransaction", - }, - sendFungibleTokens: { - name: "Send Fungible Tokens", - description: - "Send a specific amount of a fungible token to a given recipient.", - icon: "send", - - roles: { - sender: { requirements: { - variables: [ - "transferredTokenCategory", - "transferredTokenAmount", - "recipientLockingscript", - ], - secrets: ["ownerKey"], + participants: [ + { + role: 'sender', + slots: { min: 1, max: 1 }, + }, + ], }, - }, - }, - requirements: { - roles: [ - { - role: "sender", - slots: { min: 1, max: 1 }, + // Sending is only available for outputs that have fungible tokens on them. + conditions: [ '$(OP_INPUTINDEX OP_UTXOTOKENAMOUNT <0> OP_GREATERTHAN)' ], + + transaction: 'transferFungibleTokensTransaction', + }, + sendNonfungibleTokens: { + name: 'Send a Non-fungible Token', + description: 'Send a non-fungible token to a given recipient.', + icon: 'send', + + roles: { + sender: { + requirements: { + variables: [ 'transferredTokenCategory', 'transferredTokenCapability', 'transferredTokenCommitment', 'recipientLockingscript' ], + secrets: [ 'ownerKey' ], + }, + }, }, - ], - }, - // Sending is only available for outputs that have fungible tokens on them. - condition: "$(OP_INPUTINDEX OP_UTXOTOKENAMOUNT <0> OP_GREATERTHAN)", - - transaction: "transferFungibleTokensTransaction", - }, - sendNonfungibleTokens: { - name: "Send a Non-fungible Token", - description: "Send a non-fungible token to a given recipient.", - icon: "send", - - roles: { - sender: { requirements: { - variables: [ - "transferredTokenCategory", - "transferredTokenCapability", - "transferredTokenCommitment", - "recipientLockingscript", - ], - secrets: ["ownerKey"], + participants: [ + { + role: 'sender', + slots: { min: 1, max: 1 }, + }, + ], }, - }, + + // Sending is only available for outputs that have a non-fungible token on them. + conditions: [ '$(OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_SIZE OP_NIP <32> OP_GREATERTHAN)' ], + + transaction: 'transferNonfungibleTokensTransaction', }, - requirements: { - roles: [ - { - role: "sender", - slots: { min: 1, max: 1 }, + // NOTE: Burning tokens can be done without explicit template support. + // NOTE: This feature is explicitly defined in this template to demonstrate how versatile templates can be, and + // to ensure the feature is discoverable by the user from outputs that hold tokens. + burnFungibleTokens: { + name: 'Delete Fungible Tokens', + description: 'Permanently and irreversibly deletes one or more fungible tokens.', + icon: 'burn', + + roles: { + owner: { + requirements: { + variables: [ 'burnedTokenCategory', 'burnedTokenAmount' ], + secrets: [ 'ownerKey' ], + }, + }, }, - ], - }, - // Sending is only available for outputs that have a non-fungible token on them. - condition: - "$(OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_SIZE OP_NIP <32> OP_GREATERTHAN)", - - transaction: "transferNonfungibleTokensTransaction", - }, - - // NOTE: Burning tokens can be done without explicit template support. - // NOTE: This feature is explicitly defined in this template to demonstrate how versatile templates can be, and - // to ensure the feature is discoverable by the user from outputs that hold tokens. - burnFungibleTokens: { - name: "Delete Fungible Tokens", - description: - "Permanently and irreversibly deletes one or more fungible tokens.", - icon: "burn", - - roles: { - owner: { requirements: { - variables: ["burnedTokenCategory", "burnedTokenAmount"], - secrets: ["ownerKey"], + participants: [ + { + role: 'owner', + slots: { min: 1, max: 1 }, + }, + ], }, - }, - }, - requirements: { - roles: [ - { - role: "owner", - slots: { min: 1, max: 1 }, + // Burning is only available for outputs that have fungible tokens on them. + conditions: [ '$(OP_INPUTINDEX OP_UTXOTOKENAMOUNT <0> OP_GREATERTHAN)' ], + + transaction: 'burnFungibleTokensTransaction', + }, + burnNonfungibleTokens: { + name: 'Delete a Non-fungible Token', + description: 'Permanently and irreversibly deletes one non-fungible token.', + icon: 'burn', + + roles: { + owner: { + requirements: { + variables: [ 'burnedTokenCategory', 'burnedTokenCapability', 'burnedTokenCommitment' ], + secrets: [ 'ownerKey' ], + }, + }, }, - ], - }, - // Burning is only available for outputs that have fungible tokens on them. - condition: "$(OP_INPUTINDEX OP_UTXOTOKENAMOUNT <0> OP_GREATERTHAN)", - - transaction: "burnFungibleTokensTransaction", - }, - burnNonfungibleTokens: { - name: "Delete a Non-fungible Token", - description: - "Permanently and irreversibly deletes one non-fungible token.", - icon: "burn", - - roles: { - owner: { requirements: { - variables: [ - "burnedTokenCategory", - "burnedTokenCapability", - "burnedTokenCommitment", - ], - secrets: ["ownerKey"], + participants: [ + { + role: 'owner', + slots: { min: 1, max: 1 }, + }, + ], }, - }, + + // Burning is only available for outputs that have non-fungible tokens on them. + conditions: [ '$(OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_SIZE OP_NIP <32> OP_GREATERTHAN)' ], + + transaction: 'burnNonfungibleTokenTransaction', }, - requirements: { - roles: [ - { - role: "owner", - slots: { min: 1, max: 1 }, + sign: { + name: 'Sign Message', + description: 'Signs a provided message using the Bitcoin message signing protocol.', + icon: 'sign', + + roles: { + owner: { + requirements: { + variables: [ 'messageToSign' ], + secrets: [ 'ownerKey' ], + }, + }, }, - ], - }, - // Burning is only available for outputs that have non-fungible tokens on them. - condition: - "$(OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_SIZE OP_NIP <32> OP_GREATERTHAN)", - - transaction: "burnNonfungibleTokenTransaction", - }, - - sign: { - name: "Sign Message", - description: - "Signs a provided message using the Bitcoin message signing protocol.", - icon: "sign", - - roles: { - owner: { requirements: { - variables: ["messageToSign"], - secrets: ["ownerKey"], + participants: [ + { + role: 'owner', + slots: { min: 1, max: 1 }, + }, + ], }, - }, - }, - requirements: { - roles: [ - { - role: "owner", - slots: { min: 1, max: 1 }, + data: 'messageSignature', + }, + verify: { + name: 'Verify Message Signature', + description: 'Verifies a provided message signature according to the Bitcoin message signing protocol.', + icon: 'verify', + + roles: { + owner: { + requirements: { + variables: [ 'messageSignature', 'messageToVerify' ], + secrets: [ 'ownerKey' ], + }, + }, }, - ], - }, - data: ["messageSignature"], - }, - verify: { - name: "Verify Message Signature", - description: - "Verifies a provided message signature according to the Bitcoin message signing protocol.", - icon: "verify", - - roles: { - owner: { requirements: { - variables: ["messageSignature", "messageToVerify"], - secrets: ["ownerKey"], + participants: [ + { + role: 'owner', + slots: { min: 1, max: 1 }, + }, + ], }, - }, - }, - requirements: { - roles: [ - { - role: "owner", - slots: { min: 1, max: 1 }, - }, - ], + data: 'messageSignatureValidity', }, - - data: ["messageSignatureValidity"], - }, }, // Define a set of data that can be used in this template. data: { - messageSignature: { - // Evaluate CashASM expression to get the signature needed. - // NOTE: Pushes the prefix and message, then concatenates them together to form the data to sign. - // NOTE: In libauth today, it seems that this is done by defining the signature as a variable, and tying it to the key and message, - // and so this is different - // TODO: Check with Jason and see if there is any reason why this cannot be done like this. - value: - "$( OP_CAT )", - type: "bytes", - hint: "signature", - }, - messageSignatureValidity: { - // Evaluate the validity of the message with the owners public key. - value: - "$( OP_CAT OP_CHECKDATASIG)", - type: "integer", - hint: "script_boolean", - }, + messageSignature: { + // Evaluate CashASM expression to get the signature needed. + // NOTE: Pushes the prefix and message, then concatenates them together to form the data to sign. + // NOTE: In libauth today, it seems that this is done by defining the signature as a variable, and tying it to the key and message, + // and so this is different + // TODO: Check with Jason and see if there is any reason why this cannot be done like this. + value: '$( OP_CAT )', + type: 'bytes', + hint: 'signature', + }, + messageSignatureValidity: { + // Evaluate the validity of the message with the owners public key. + value: '$( OP_CAT OP_CHECKDATASIG)', + type: 'integer', + hint: 'script_boolean', + }, }, // Define a set of transactions that can be used in this template. transactions: { - receiveTransaction: { - name: "Transfer request", - description: - "Transfer request for an unspecified amount of cash and/or tokens.", - icon: "request", + receiveTransaction: { + name: 'Transfer Completed', + description: 'Transferred an unspecified amount of cash and/or tokens.', + icon: 'request', - roles: { - receiver: { - name: "Received", - description: "Received an unspecified amount of cash and/or tokens.", - icon: "receive", - }, - sender: { - name: "Sent", - description: "Sent an unspecified amount of cash and/or tokens.", - icon: "send", - }, + roles: { + receiver: { + name: 'Received', + description: 'Received an unspecified amount of cash and/or tokens.', + icon: 'receive', + }, + sender: { + name: 'Sent', + description: 'Sent an unspecified amount of cash and/or tokens.', + icon: 'send', + }, + }, + + // Inputs and outputs that must exist in the transaction. + // NOTE: There is no inputs required, but the engine should detect that there is not sufficient input value to + // match the output and thus generate an invitation to participate in this action. + // When the invitation is shared, the other parties can add as many inputs and change outputs as needed. + inputs: [], + outputs: [ + { + output: 'receiveOutput', + outputIndex: undefined, + }, + ], + + // Standard transaction without a locktime. + version: 2, + locktime: 0, + + // ... + composable: true, + }, + requestSatoshisTransaction: { + name: 'Satoshis Transferred', + description: 'Transferred $() satoshis.', + icon: 'request', + + roles: { + receiver: { + name: 'Received', + description: 'Received $() satoshis.', + icon: 'receive', + }, + sender: { + name: 'Sent', + description: 'Sent $() satoshis.', + icon: 'send', + }, + }, + + // Inputs and outputs that must exist in the transaction. + // NOTE: There is no inputs required, but the engine should detect that there is not sufficient input value to + // match the output and thus generate an invitation to participate in this action. + // When the invitation is shared, the other party can add as many inputs and outputs as needed since this transaction is composable. + inputs: [], + outputs: [ + { + output: 'requestSatoshisOutput', + outputIndex: undefined, + }, + ], + + // Standard transaction without a locktime. + version: 2, + locktime: 0, + + // ... + composable: true, + }, + requestFungibleTokensTransaction: { + name: 'Fungible Tokens Transferred', + description: + 'Transferred $( OP_DIV).$( OP_MOD) $() tokens.', + icon: 'request', + + roles: { + receiver: { + name: 'Received', + description: + 'Received $( OP_DIV).$( OP_MOD) $() tokens.', + icon: 'receive', + }, + sender: { + name: 'Sent', + description: + 'Sent $( OP_DIV).$( OP_MOD) $() tokens.', + icon: 'send', + }, + }, + + // Inputs and outputs that must exist in the transaction. + // NOTE: There is no inputs required, but the engine should detect that there is not sufficient input value to + // match the output and thus generate an invitation to participate in this action. + // When the invitation is shared, the other party can add as many inputs and outputs as needed since this transaction is composable. + inputs: [], + outputs: [ + { + output: 'requestFungibleTokensOutput', + outputIndex: undefined, + }, + ], + + // Standard transaction without a locktime. + version: 2, + locktime: 0, + + // ... + composable: true, + }, + requestNonfungibleTokensTransaction: { + name: 'Non-Fungible Token Transferred', + description: + 'Transferred one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', + icon: 'request', + + roles: { + receiver: { + name: 'Received', + description: + 'Received one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', + icon: 'receive', + }, + sender: { + name: 'Sent', + description: + 'Sent the requested non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', + icon: 'send', + }, + }, + + // Inputs and outputs that must exist in the transaction. + // NOTE: There is no inputs required, but the engine should detect that there is not sufficient input value to + // match the output and thus generate an invitation to participate in this action. + // When the invitation is shared, the other party can add as many inputs and outputs as needed since this transaction is composable. + inputs: [], + outputs: [ + { + output: 'requestNonfungibleTokensOutput', + outputIndex: undefined, + }, + ], + + // Standard transaction without a locktime. + version: 2, + locktime: 0, + + // ... + composable: true, }, - // Inputs and outputs that must exist in the transaction. - // NOTE: There is no inputs required, but the engine should detect that there is not sufficient input value to - // match the output and thus generate an invitation to participate in this action. - // When the invitation is shared, the other parties can add as many inputs and change outputs as needed. - inputs: [], - outputs: [ - { - output: "receiveOutput", - index: undefined, - }, - ], + transferSatoshisTransaction: { + name: 'Satoshis Transferred', + description: '$() satoshis were transferred to a recipient.', + icon: 'send', - // Standard transaction without a locktime. - version: 2, - locktime: 0, + roles: { + receiver: { + name: 'Received', + description: 'Received $() satoshis.', + icon: 'receive', + }, + sender: { + name: 'Sent', + description: 'Sent $() satoshis.', + icon: 'send', + }, + }, - // ... - composable: true, - }, - requestSatoshisTransaction: { - name: "Transfer request", - description: "Transfer request for $() satoshis.", - icon: "request", + // Enforce the inputs and outputs required by the transaction. + // NOTE: The input is provided from the action since it is only available on outputs with satoshis. + inputs: [], + outputs: [ + { + output: 'transferSatoshisOutput', + outputIndex: undefined, + }, + ], - roles: { - receiver: { - name: "Received", - description: "Received $() satoshis.", - icon: "receive", - }, - sender: { - name: "Sent", - description: "Sent $() satoshis.", - icon: "send", - }, + // Standard transaction without a locktime. + version: 2, + locktime: 0, + + // ... + composable: true, + }, + transferFungibleTokensTransaction: { + name: 'Fungible Tokens Transferred', + description: + '$( OP_DIV).$( OP_MOD) $() tokens were transferred to a recipient.', + icon: 'send', + + roles: { + receiver: { + name: 'Received', + description: + 'Received $( OP_DIV).$( OP_MOD) $() tokens.', + icon: 'receive', + }, + sender: { + name: 'Sent', + description: + 'Sent $( OP_DIV).$( OP_MOD) $() tokens.', + icon: 'send', + }, + }, + + // Enforce the inputs and outputs required by the transaction. + // NOTE: The input is provided from the action since it is only available on outputs with fungible tokens. + inputs: [], + outputs: [ + { + output: 'transferFungibleTokensOutput', + outputIndex: undefined, + }, + ], + + // Standard transaction without a locktime. + version: 2, + locktime: 0, + + // ... + composable: true, + }, + transferNonfungibleTokensTransaction: { + name: 'Non-fungible Token Transferred', + description: + 'One non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token was transferred to a recipient, with $() commitment.', + icon: 'send', + + roles: { + receiver: { + name: 'Received', + description: + 'Received one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', + icon: 'receive', + }, + sender: { + name: 'Sent', + description: + 'Sent one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', + icon: 'send', + }, + }, + + // Enforce the inputs and outputs required by the transaction. + // NOTE: The input is provided from the action since it is only available on outputs with a non-fungible token. + inputs: [], + outputs: [ + { + output: 'transferNonfungibleTokenOutput', + outputIndex: undefined, + }, + ], + + // Standard transaction without a locktime. + version: 2, + locktime: 0, + + // ... + composable: true, }, - // Inputs and outputs that must exist in the transaction. - // NOTE: There is no inputs required, but the engine should detect that there is not sufficient input value to - // match the output and thus generate an invitation to participate in this action. - // When the invitation is shared, the other party can add as many inputs and outputs as needed since this transaction is composable. - inputs: [], - outputs: [ - { - output: "requestSatoshisOutput", - index: undefined, - }, - ], - - // Standard transaction without a locktime. - version: 2, - locktime: 0, - - // ... - composable: true, - }, - requestFungibleTokensTransaction: { - name: "Transfer request", - description: - "Transfer request for $( OP_DIV).$( OP_MOD) $() tokens.", - icon: "request", - - roles: { - receiver: { - name: "Received", + burnFungibleTokensTransaction: { + name: 'Deleted fungible tokens', description: - "Received $( OP_DIV).$( OP_MOD) $() tokens.", - icon: "receive", - }, - sender: { - name: "Sent", - description: - "Sent $( OP_DIV).$( OP_MOD) $() tokens.", - icon: "send", - }, + 'Permanently and irreversibly deleted $( OP_DIV).$( OP_MOD) $() tokens.', + icon: 'burn', + + // Inputs and outputs that must exist in the transaction. + // NOTE: There is no defined outputs as any non-burned value is automatically returned as change. + inputs: [ + { + input: 'burnFungibleTokensInput', + inputIndex: undefined, + }, + ], + + outputs: [], + + // Standard transaction without a locktime. + version: 2, + locktime: 0, + + // ... + composable: true, }, - - // Inputs and outputs that must exist in the transaction. - // NOTE: There is no inputs required, but the engine should detect that there is not sufficient input value to - // match the output and thus generate an invitation to participate in this action. - // When the invitation is shared, the other party can add as many inputs and outputs as needed since this transaction is composable. - inputs: [], - outputs: [ - { - output: "requestFungibleTokensOutput", - index: undefined, - }, - ], - - // Standard transaction without a locktime. - version: 2, - locktime: 0, - - // ... - composable: true, - }, - requestNonfungibleTokensTransaction: { - name: "Transfer request", - description: - 'Transfer request for one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', - icon: "request", - - roles: { - receiver: { - name: "Received", + burnNonfungibleTokenTransaction: { + name: 'Deleted non fungible token', description: - 'Received one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', - icon: "receive", - }, - sender: { - name: "Sent", - description: - 'Sent the requested non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', - icon: "send", - }, + 'Permanently and irreversibly deleted a non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', + icon: 'burn', + + // Inputs and outputs that must exist in the transaction. + // NOTE: There is no defined outputs as any non-burned value is automatically returned as change. + inputs: [ + { + input: 'burnNonfungibleTokenInput', + inputIndex: undefined, + }, + ], + outputs: [], + + // Standard transaction without a locktime. + version: 2, + locktime: 0, + + // ... + composable: true, }, - - // Inputs and outputs that must exist in the transaction. - // NOTE: There is no inputs required, but the engine should detect that there is not sufficient input value to - // match the output and thus generate an invitation to participate in this action. - // When the invitation is shared, the other party can add as many inputs and outputs as needed since this transaction is composable. - inputs: [], - outputs: [ - { - output: "requestNonfungibleTokensOutput", - index: undefined, - }, - ], - - // Standard transaction without a locktime. - version: 2, - locktime: 0, - - // ... - composable: true, - }, - - transferSatoshisTransaction: { - name: "Satoshis Transferred", - description: - "$() satoshis were transferred to a recipient.", - icon: "send", - - roles: { - receiver: { - name: "Received", - description: "Received $() satoshis.", - icon: "receive", - }, - sender: { - name: "Sent", - description: "Sent $() satoshis.", - icon: "send", - }, - }, - - // Enforce the inputs and outputs required by the transaction. - // NOTE: The input is provided from the action since it is only available on outputs with satoshis. - inputs: [], - outputs: [ - { - output: "transferSatoshisOutput", - index: undefined, - }, - ], - - // Standard transaction without a locktime. - version: 2, - locktime: 0, - - // ... - composable: true, - }, - transferFungibleTokensTransaction: { - name: "Fungible Tokens Transferred", - description: - "$( OP_DIV).$( OP_MOD) $() tokens were transferred to a recipient.", - icon: "send", - - roles: { - receiver: { - name: "Received", - description: - "Received $( OP_DIV).$( OP_MOD) $() tokens.", - icon: "receive", - }, - sender: { - name: "Sent", - description: - "Sent $( OP_DIV).$( OP_MOD) $() tokens.", - icon: "send", - }, - }, - - // Enforce the inputs and outputs required by the transaction. - // NOTE: The input is provided from the action since it is only available on outputs with fungible tokens. - inputs: [], - outputs: [ - { - output: "transferFungibleTokensOutput", - index: undefined, - }, - ], - - // Standard transaction without a locktime. - version: 2, - locktime: 0, - - // ... - composable: true, - }, - transferNonfungibleTokensTransaction: { - name: "Non-fungible Token Transferred", - description: - 'One non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token was transferred to a recipient, with $() commitment.', - icon: "send", - - roles: { - receiver: { - name: "Received", - description: - 'Received one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', - icon: "receive", - }, - sender: { - name: "Sent", - description: - 'Sent one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', - icon: "send", - }, - }, - - // Enforce the inputs and outputs required by the transaction. - // NOTE: The input is provided from the action since it is only available on outputs with a non-fungible token. - inputs: [], - outputs: [ - { - output: "transferNonfungibleTokenOutput", - index: undefined, - }, - ], - - // Standard transaction without a locktime. - version: 2, - locktime: 0, - - // ... - composable: true, - }, - - burnFungibleTokensTransaction: { - name: "Deleted fungible tokens", - description: - "Permanently and irreversibly deleted $( OP_DIV).$( OP_MOD) $() tokens.", - icon: "burn", - - // Inputs and outputs that must exist in the transaction. - // NOTE: There is no defined outputs as any non-burned value is automatically returned as change. - inputs: [ - { - input: "burnFungibleTokensInput", - index: undefined, - }, - ], - - outputs: [], - - // Standard transaction without a locktime. - version: 2, - locktime: 0, - - // ... - composable: true, - }, - burnNonfungibleTokenTransaction: { - name: "Deleted non fungible token", - description: - 'Permanently and irreversibly deleted a non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) () token, with $() commitment.', - icon: "burn", - - // Inputs and outputs that must exist in the transaction. - // NOTE: There is no defined outputs as any non-burned value is automatically returned as change. - inputs: [ - { - input: "burnNonfungibleTokenInput", - index: undefined, - }, - ], - outputs: [], - - // Standard transaction without a locktime. - version: 2, - locktime: 0, - - // ... - composable: true, - }, }, // Define a set of outputs that can be used within transactions in this template. outputs: { - receiveOutput: { - name: "Recipient output", - description: - "Transferred an unspecified amount of cash and/or tokens to a recipient.", - icon: "receive", + changeOutput: { + name: 'Change', + description: 'Funds returned as change.', + icon: 'receive', - roles: { - receiver: { - name: "Received", - description: "Received an unspecified amount of cash and/or tokens.", - }, - sender: { - name: "Sent", - description: "Sent an unspecified amount of cash and/or tokens.", - }, + // Defines how the requested funds should be locked. + lockingScript: 'receivingLockingScript', }, + receiveOutput: { + name: 'Recipient output', + description: 'Transferred an unspecified amount of cash and/or tokens to a recipient.', + icon: 'receive', - // Defines how the requested funds should be locked. - lockscript: "receivingLockingScript", - }, - requestSatoshisOutput: { - name: "Recipient output", - description: - "Transferred $() satoshis to a recipient.", - icon: "request", + roles: { + receiver: { + name: 'Received', + description: 'Received an unspecified amount of cash and/or tokens.', + }, + sender: { + name: 'Sent', + description: 'Sent an unspecified amount of cash and/or tokens.', + }, + }, - roles: { - receiver: { - name: "Received", - description: "Received $() satoshis.", - }, - sender: { - name: "Sent", - description: "Sent $() satoshis.", - }, + // Defines how the requested funds should be locked. + lockingScript: 'receivingLockingScript', }, + requestSatoshisOutput: { + name: 'Satoshis', + description: '$() satoshis.', + icon: 'request', - // Defines how the requested funds should be locked. - lockscript: "receivingLockingScript", + roles: { + receiver: { + name: 'Satoshis Received', + description: 'Received $() satoshis.', + }, + sender: { + name: 'Satoshis Sent', + description: 'Sent $() satoshis.', + }, + }, - // Require the specified number of satoshis and no tokens. - valueSatoshis: "$()", - token: null, - }, - requestFungibleTokensOutput: { - name: "Recipient output", - description: - "Transferred $( OP_DIV).$( OP_MOD) $() tokens to a recipient.", - icon: "request", + // Defines how the requested funds should be locked. + lockingScript: 'receivingLockingScript', - roles: { - receiver: { - name: "Received", + // Require the specified number of satoshis and no tokens. + valueSatoshis: '$()', + token: null, + }, + requestFungibleTokensOutput: { + name: 'Fungible $() Tokens', description: - "Received $( OP_DIV).$( OP_MOD) $() tokens.", - }, - sender: { - name: "Sent", + '$( OP_DIV).$( OP_MOD) $() tokens.', + icon: 'request', + + roles: { + receiver: { + name: 'Fungible $() Tokens Received', + description: + 'Received $( OP_DIV).$( OP_MOD) $() tokens.', + }, + sender: { + name: 'Fungible $() Tokens Sent', + description: + 'Sent $( OP_DIV).$( OP_MOD) $() tokens.', + }, + }, + + // Defines how the requested funds should be locked. + lockingScript: 'receivingLockingScript', + + // Require a flat 1000 satoshis to ensure the fungible tokens remains transferrable. + valueSatoshis: '1000', + + // Require only the specified amount and type of fungible tokens. + // NOTE: This can be composed with a request for a non-fungible token, but will always result in two separate outputs. + token: { + category: '$()', + amount: '$()', + nft: null, + }, + }, + requestNonfungibleTokensOutput: { + name: 'Non-fungible $() Token', description: - "Sent $( OP_DIV).$( OP_MOD) $() tokens.", - }, + 'Transferred one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token to a recipient, with $() commitment.', + icon: 'request', + + roles: { + receiver: { + name: 'Non-fungible $() Token Received', + description: + 'Received one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token, with $() commitment.', + }, + sender: { + name: 'Non-fungible $() Token Sent', + description: + 'Sent the requested non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token, with $() commitment.', + }, + }, + + // Defines how the requested funds should be locked. + lockingScript: 'receivingLockingScript', + + // Require a flat 1000 satoshis to ensure the non-fungible token remains transferrable. + valueSatoshis: '1000', + + // Require only an NFT with specified category, capability and commitment. + // NOTE: This can be composed with a request for fungible token amounts, but will always result in two separate outputs. + token: { + category: '$()', + amount: null, + nft: { + capability: '$()', + commitment: '$()', + }, + }, }, - // Defines how the requested funds should be locked. - lockscript: "receivingLockingScript", + transferSatoshisOutput: { + name: 'Recipient output', + description: 'Transferred $() satoshis to a recipient.', + icon: 'send', - // Require a flat 1000 satoshis to ensure the fungible tokens remains transferrable. - valueSatoshis: "1000", + roles: { + receiver: { + name: 'Received', + description: 'Received $() satoshis.', + }, + sender: { + name: 'Sent', + description: 'Sent $() satoshis.', + }, + }, - // Require only the specified amount and type of fungible tokens. - // NOTE: This can be composed with a request for a non-fungible token, but will always result in two separate outputs. - token: { - category: "$()", - amount: "$()", - nft: null, + // Use the recipients lockscript. + lockingScript: 'sendingLockingscript', + + // Set the amount of satoshis to transfer. + valueSatoshis: '$()', }, - }, - requestNonfungibleTokensOutput: { - name: "Recipient output", - description: - 'Transferred one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token to a recipient, with $() commitment.', - icon: "request", - - roles: { - receiver: { - name: "Received", + transferFungibleTokensOutput: { + name: 'Recipient output', description: - 'Received one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token, with $() commitment.', - }, - sender: { - name: "Sent", + 'Transferred $( OP_DIV).$( OP_MOD) $() tokens to a recipient.', + icon: 'send', + + roles: { + receiver: { + name: 'Received', + description: + 'Received $( OP_DIV).$( OP_MOD) $() tokens.', + }, + sender: { + name: 'Sent', + description: + 'Sent $( OP_DIV).$( OP_MOD) $() tokens.', + }, + }, + + // Use the recipients lockscript. + lockingScript: 'sendingLockingscript', + + // Set the amount of fungible tokens to transfer. + token: { + category: '$()', + amount: '$()', + }, + }, + transferNonfungibleTokenOutput: { + name: 'Recipient output', description: - 'Sent the requested non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token, with $() commitment.', - }, + 'Transferred one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token to a recipient, with $() commitment.', + icon: 'send', + + roles: { + receiver: { + name: 'Received', + description: + 'Received one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token, with $() commitment.', + }, + sender: { + name: 'Sent', + description: + 'Sent one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token, with $() commitment.', + }, + }, + + // Use the recipients lockscript. + lockingScript: 'sendingLockingscript', + + // Set the non-fungible token to transfer. + token: { + category: '$()', + nft: { + capability: '$()', + commitment: '$()', + }, + }, }, - - // Defines how the requested funds should be locked. - lockscript: "receivingLockingScript", - - // Require a flat 1000 satoshis to ensure the non-fungible token remains transferrable. - valueSatoshis: "1000", - - // Require only an NFT with specified category, capability and commitment. - // NOTE: This can be composed with a request for fungible token amounts, but will always result in two separate outputs. - token: { - category: "$()", - amount: null, - nft: { - capability: "$()", - commitment: "$()", - }, - }, - }, - - transferSatoshisOutput: { - name: "Recipient output", - description: - "Transferred $() satoshis to a recipient.", - icon: "send", - - roles: { - receiver: { - name: "Received", - description: "Received $() satoshis.", - }, - sender: { - name: "Sent", - description: "Sent $() satoshis.", - }, - }, - - // Use the recipients lockscript. - lockscript: "sendingLockingscript", - - // Set the amount of satoshis to transfer. - valueSatoshis: "$()", - }, - transferFungibleTokensOutput: { - name: "Recipient output", - description: - "Transferred $( OP_DIV).$( OP_MOD) $() tokens to a recipient.", - icon: "send", - - roles: { - receiver: { - name: "Received", - description: - "Received $( OP_DIV).$( OP_MOD) $() tokens.", - }, - sender: { - name: "Sent", - description: - "Sent $( OP_DIV).$( OP_MOD) $() tokens.", - }, - }, - - // Use the recipients lockscript. - lockscript: "sendingLockingscript", - - // Set the amount of fungible tokens to transfer. - token: { - category: "$()", - amount: "$()", - }, - }, - transferNonfungibleTokenOutput: { - name: "Recipient output", - description: - 'Transferred one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token to a recipient, with $() commitment.', - icon: "send", - - roles: { - receiver: { - name: "Received", - description: - 'Received one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token, with $() commitment.', - }, - sender: { - name: "Sent", - description: - 'Sent one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) $() token, with $() commitment.', - }, - }, - - // Use the recipients lockscript. - lockscript: "sendingLockingscript", - - // Set the non-fungible token to transfer. - token: { - category: "$()", - nft: { - capability: "$()", - commitment: "$()", - }, - }, - }, }, inputs: { - burnFungibleTokensInput: { - name: "Deleted fungible tokens", - description: - "Permanently and irreversibly deleted $() $().", - icon: "burn", + burnFungibleTokensInput: { + name: 'Deleted fungible tokens', + description: 'Permanently and irreversibly deleted $() $().', + icon: 'burn', - // Define which unlocking script unlocks this input. - unlockingScript: "unlockP2PKH", + // Define which unlocking script unlocks this input. + unlockingScript: 'unlockP2PKH', - // Require a fungible token of the requested token category, with an amount larger than or equal to the requested amount to burn. - token: { - category: "$()", - amount: - "$( OP_GREATERTHANOREQUAL OP_IF OP_ENDIF)", + // Require a fungible token of the requested token category, with an amount larger than or equal to the requested amount to burn. + token: { + category: '$()', + amount: '$( OP_GREATERTHANOREQUAL OP_IF OP_ENDIF)', + }, + + // Ignore the burned token amount when determining change for this output. + // NOTE: The engine must check that this does not exceed the input value. + omitChangeAmounts: { + fungibleTokens: '${}', + }, }, + burnNonfungibleTokenInput: { + name: 'Deleted non-fungible token', + description: + 'Permanently and irreversibly burned one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) token of category $(), with a $() commitment.', + icon: 'burn', - // Ignore the burned token amount when determining change for this output. - // NOTE: The engine must check that this does not exceed the input value. - omitChangeAmounts: { - fungibleTokens: "${}", + // Define which unlocking script unlocks this input. + unlockingScript: 'unlockP2PKH', + + // Require a non-fungible token of the specified category, capability and commitment to burn. + token: { + category: '$()', + nft: { + capability: '$()', + commitment: '$()', + }, + }, + + // Ignore the burned token when determining change for this output. + // NOTE: The engine must check that this does not exceed the input value. + omitChangeAmounts: { + nonfungibleTokens: 1, + }, }, - }, - burnNonfungibleTokenInput: { - name: "Deleted non-fungible token", - description: - 'Permanently and irreversibly burned one non-fungible $( <0x02> OP_EQUAL OP_IF <"minting"> OP_ELSE <0x01> OP_EQUAL OP_IF <"mutable"> OP_ELSE <"immutable"> OP_ENDIF OP_ENDIF) token of category $(), with a $() commitment.', - icon: "burn", - - // Define which unlocking script unlocks this input. - unlockingScript: "unlockP2PKH", - - // Require a non-fungible token of the specified category, capability and commitment to burn. - token: { - category: "$()", - nft: { - capability: "$()", - commitment: "$()", - }, - }, - - // Ignore the burned token when determining change for this output. - // NOTE: The engine must check that this does not exceed the input value. - omitChangeAmounts: { - nonfungibleTokens: 1, - }, - }, }, // Define locking scripts used by this template. // NOTE: Template supported wallets should automatically track all generated lockscripts for on-chain events. lockingScripts: { - sendingLockingscript: { - // TODO: This currently describes outputs locked to this script, but all actions creating such outputs already have descriptions. - // This can either be dropped, or maybe more importantly, should be disambiguated so that lockscripts can provide separate - // descriptions for their script and the outputs that are locked to them. Leaving as a TODO for now and will address later. - name: "Sent", - description: "Funds sent to an external recipient", - icon: "address", + sendingLockingscript: { + // TODO: This currently describes outputs locked to this script, but all actions creating such outputs already have descriptions. + // This can either be dropped, or maybe more importantly, should be disambiguated so that lockscripts can provide separate + // descriptions for their script and the outputs that are locked to them. Leaving as a TODO for now and will address later. + name: 'Sent', + description: 'Funds sent to an external recipient', + icon: 'address', - // ... - lockingType: "standard", - lockingScript: "lockToRecipient", + // ... + lockingType: 'p2pkh', + lockingBytecode: 'lockToRecipient', - // Indicate that the sent output does not belong to the initiating user. - // NOTE: These values default to false/empty, but added here for additional clarity. - actions: [], - state: [], - secrets: [], - balance: false, - selectable: false, - privacy: false, - }, - receivingLockingScript: { - // NOTE: Outputs to this lockscript by external actors defaults to this description when detected on-chain. - name: "Received", - description: "Funds received without wallet coordination.", - icon: "address", - - // Defines how spending future received funds should be locked. - lockingType: "standard", - lockingScript: "lockP2PKH", - - // Define a default unlocking script to be used when no action provided script is present. - unlockingScript: "unlockP2PKH", - - roles: { - receiver: { - // The only state that is required to be persisted when receiving funds is the owners private key. - // NOTE: This is defined as a secret to not leak when creating invitations for others to participate in request or send actions. - state: { - variables: [], - secrets: ["ownerKey"], - }, - - // List actions that can be taken with the ownerKey for each address/lockscript. - actions: [ - { - action: "sign", - role: "owner", - secrets: ["ownerKey"], - }, - { - action: "verify", - role: "owner", - secrets: ["ownerKey"], - }, - { - action: "sendSatoshis", - role: "sender", - secrets: ["ownerKey"], - }, - { - action: "sendFungibleTokens", - role: "sender", - secrets: ["ownerKey"], - }, - { - action: "sendNonfungibleTokens", - role: "sender", - secrets: ["ownerKey"], - }, - { - action: "burnFungibleTokens", - role: "sender", - secrets: ["ownerKey"], - }, - { - action: "burnNonfungibleTokens", - role: "sender", - secrets: ["ownerKey"], - }, - ], - - // Indicates how much of received funds should be part of a wallets total balance. - // NOTE: Evaluates in the context of the source transaction, with the current outputs technical data available under this.output.* - // NOTE: This can either evaluate to a boolean or a number. If evaluated to a boolean, then true means 100% of the output. - // NOTE: Since we know that the owner controls all assets, we short-cut the evaluations by setting true directly. - balance: { - satoshis: true, - fungibleTokens: true, - // nonFungibleToken: true, - }, - - // Indicate when received funds should be considered in automatic coin selection to meet input requirements of other transactions. - // NOTE: Evaluates in the context of the source transaction, with the current outputs technical data available under this.output.* - // NOTE: This evaluates to a boolean. If non-true, the output should not be considered for automatic coin selection. - // NOTE: Since we know that the secret key exist for the owner, this template short-cuts the evaluation by setting true directly. - selectable: true, - - // Indicate that received funds are not considered good privacy. - // NOTE: This information allows wallets to make better privacy decisions, like avoid mixing private and non-private outputs. - privacy: false, - }, + // Indicate that the sent output does not belong to the initiating user. + // NOTE: These values default to false/empty, but added here for additional clarity. + actions: [], + state: { variables: [], secrets: [] }, + balance: {}, + selectable: false, + }, + receivingLockingScript: { + // NOTE: Outputs to this lockscript by external actors defaults to this description when detected on-chain. + name: 'Received', + description: 'Funds received without wallet coordination.', + icon: 'address', + + // Defines how spending future received funds should be locked. + lockingType: 'p2pkh', + lockingBytecode: 'lockP2PKH', + + // Define a default unlocking script to be used when no action provided script is present. + unlockingBytecode: 'unlockP2PKH', + + // Participants without a role or observers cannot take any further actions. + // NOTE: This is not required but shown here for illustrative purposes. + actions: [], + + roles: { + receiver: { + // The only state that is required to be persisted when receiving funds is the owners private key. + // NOTE: This is defined as a secret to not leak when creating invitations for others to participate in request or send actions. + state: { + variables: [], + secrets: [ 'ownerKey' ], + }, + + // List actions that can be taken with the ownerKey for each address/lockscript. + actions: [ + { + action: 'sign', + role: 'owner', + secrets: [{ ownerKey: null }], + }, + { + action: 'verify', + role: 'owner', + secrets: [{ ownerKey: null }], + }, + { + action: 'sendSatoshis', + role: 'sender', + secrets: [{ ownerKey: null }], + }, + { + action: 'sendFungibleTokens', + role: 'sender', + secrets: [{ ownerKey: null }], + }, + { + action: 'sendNonfungibleTokens', + role: 'sender', + secrets: [{ ownerKey: null }], + }, + { + action: 'burnFungibleTokens', + role: 'sender', + secrets: [{ ownerKey: null }], + }, + { + action: 'burnNonfungibleTokens', + role: 'sender', + secrets: [{ ownerKey: null }], + }, + ], + + // Indicates how much of received funds should be part of a wallets total balance. + // NOTE: Evaluates in the context of the source transaction, with the current outputs technical data available under this.output.* + // NOTE: A CashASM expression of '1' means include 100% of this asset in the balance. + // NOTE: Since we know that the owner controls all assets, we short-cut the evaluations by setting 1 directly. + balance: { + satoshis: true, + fungibleTokens: true, + nonfungibleTokens: true, + }, + + // Indicate when received funds should be considered in automatic coin selection to meet input requirements of other transactions. + // NOTE: Evaluates in the context of the source transaction, with the current outputs technical data available under this.output.* + // NOTE: This evaluates to a boolean. If non-true, the output should not be considered for automatic coin selection. + // NOTE: Since we know that the secret key exist for the owner, this template short-cuts the evaluation by setting true directly. + selectable: true, + }, + }, }, - }, }, // Define a set of scripts that can be used in this template. scripts: { - lockP2PKH: - "OP_DUP OP_HASH160 <$( OP_HASH160)> OP_EQUALVERIFY OP_CHECKSIG", - unlockP2PKH: - " ", - lockToRecipient: "", + lockP2PKH: 'OP_DUP OP_HASH160 <$( OP_HASH160)> OP_EQUALVERIFY OP_CHECKSIG', + unlockP2PKH: ' ', + lockToRecipient: '', }, // TODO: Add icons constants: { - dustLimit: { - name: "Dust Limit", - description: - "Standard required minimum satoshis for Pay to Public Key Hash outputs.", - type: "integer", - value: 546, - }, + dustLimit: { + name: 'Dust Limit', + description: 'Standard required minimum satoshis for Pay to Public Key Hash outputs.', + type: 'integer', + value: 546, + }, - // Define a message prefix for use in arbitrary message signing. - messagePrefix: { - name: "Message Prefix", - description: - "Standard message prefix used in the bitcoin signed message protocol.", - type: "bytes", + // Define a message prefix for use in arbitrary message signing. + messagePrefix: { + name: 'Message Prefix', + description: 'Standard message prefix used in the bitcoin signed message protocol.', + type: 'bytes', - // Value is enforced to the bitcoin signing magic string: - // "\x18Bitcoin Signed Message:\n" - value: "0x18426974636f696e205369676e6564204d6573736167653a0a", - }, + // Value is enforced to the bitcoin signing magic string: + // "\x18Bitcoin Signed Message:\n" + value: '0x18426974636f696e205369676e6564204d6573736167653a0a', + }, }, // TODO: Add icons variables: { - // Describe the secret private key. - ownerKey: { - name: "Owners Private Key", - description: - "The private key used to authorize spending of received funds.", - type: "bytes", - hint: "private_key", - }, + // Describe the secret private key. + ownerKey: { + name: 'Owners Private Key', + description: 'The private key used to authorize spending of received funds.', + type: 'bytes', + hint: 'private_key', + }, - messageToSign: { - name: "Message", - description: "The text message to sign.", - type: "string", - }, - messageToVerify: { - name: "Message", - description: "The text message to verify.", - type: "string", - }, - messageSignature: { - name: "Message Signature", - description: "The signature for the message.", - type: "bytes", - hint: "signature", - }, + messageToSign: { + name: 'Message', + description: 'The text message to sign.', + type: 'string', + }, + messageToVerify: { + name: 'Message', + description: 'The text message to verify.', + type: 'string', + }, + messageSignature: { + name: 'Message Signature', + description: 'The signature for the message.', + type: 'bytes', + hint: 'signature', + }, - // Describe the parameters used when requesting value. - requestedSatoshis: { - name: "Requested Amount", - description: "The Bitcoin Cash amount requested", - type: "integer", - hint: "satoshis", - }, - requestedTokenCategory: { - name: "Requested Token Category", - description: "The token category requested", - type: "bytes", - hint: "token_category", - }, - requestedTokenAmount: { - name: "Requested Token Amount", - description: "The fungible token amount requested", - type: "integer", - hint: "token_amount", - }, - requestedTokenCapability: { - name: "Requested Token Capability", - description: "The non-fungible token capability requested", - type: "bytes", - hint: "token_capability", - }, - requestedTokenCommitment: { - name: "Requested Token Commitment", - description: "The non-fungible token commitment requested", - type: "bytes", - hint: "token_commitment", - }, - transferredTokenCategory: { - name: "Sending Token Category", - description: "The token category of the token(s) to send", - type: "bytes", - hint: "token_category", - }, - transferredTokenAmount: { - name: "Sending Token Amount", - description: "The fungible token amount to send", - type: "integer", - hint: "token_amount", - }, - transferredTokenCapability: { - name: "Sending Token Capability", - description: "The token capability for the non-fungible token to send", - type: "bytes", - hint: "token_capability", - }, - transferredTokenCommitment: { - name: "Sending Token Commitment", - description: "The token commitment for the non-fungible token to send", - type: "bytes", - hint: "token_commitment", - }, - burnedTokenCategory: { - name: "Deleted Token Category", - description: "The token category of the token(s) to delete", - type: "bytes", - hint: "token_category", - }, - burnedTokenAmount: { - name: "Deleted Token Amount", - description: "The fungible token amount to delete", - type: "integer", - hint: "token_amount", - }, - burnedTokenCapability: { - name: "Deleted Token Capability", - description: "The token capability for the non-fungible token to delete", - type: "bytes", - hint: "token_capability", - }, - burnedTokenCommitment: { - name: "Deleted Token Commitment", - description: "The token commitment for the non-fungible token to delete", - type: "bytes", - hint: "token_commitment", - }, + // Describe the parameters used when requesting value. + requestedSatoshis: { + name: 'Requested Amount', + description: 'The Bitcoin Cash amount requested', + type: 'integer', + hint: 'satoshis', + }, + requestedTokenCategory: { + name: 'Requested Token Category', + description: 'The token category requested', + type: 'bytes', + hint: 'token_category', + }, + requestedTokenAmount: { + name: 'Requested Token Amount', + description: 'The fungible token amount requested', + type: 'integer', + hint: 'token_amount', + }, + requestedTokenCapability: { + name: 'Requested Token Capability', + description: 'The non-fungible token capability requested', + type: 'bytes', + hint: 'token_capability', + }, + requestedTokenCommitment: { + name: 'Requested Token Commitment', + description: 'The non-fungible token commitment requested', + type: 'bytes', + hint: 'token_commitment', + }, + transferredTokenCategory: { + name: 'Sending Token Category', + description: 'The token category of the token(s) to send', + type: 'bytes', + hint: 'token_category', + }, + transferredTokenAmount: { + name: 'Sending Token Amount', + description: 'The fungible token amount to send', + type: 'integer', + hint: 'token_amount', + }, + transferredTokenCapability: { + name: 'Sending Token Capability', + description: 'The token capability for the non-fungible token to send', + type: 'bytes', + hint: 'token_capability', + }, + transferredTokenCommitment: { + name: 'Sending Token Commitment', + description: 'The token commitment for the non-fungible token to send', + type: 'bytes', + hint: 'token_commitment', + }, + burnedTokenCategory: { + name: 'Deleted Token Category', + description: 'The token category of the token(s) to delete', + type: 'bytes', + hint: 'token_category', + }, + burnedTokenAmount: { + name: 'Deleted Token Amount', + description: 'The fungible token amount to delete', + type: 'integer', + hint: 'token_amount', + }, + burnedTokenCapability: { + name: 'Deleted Token Capability', + description: 'The token capability for the non-fungible token to delete', + type: 'bytes', + hint: 'token_capability', + }, + burnedTokenCommitment: { + name: 'Deleted Token Commitment', + description: 'The token commitment for the non-fungible token to delete', + type: 'bytes', + hint: 'token_commitment', + }, }, // Define a list of re-usable icons that can be used as part of metadata. // NOTE: the actual icons are not embedded in the template but only referenced by hashes here, // and can be distributed either as an asset pack or looked up through something like IPFS. icons: [ - { - name: "wallet", - hash: "0000000000000000000000", - }, - { - name: "owner", - hash: "0000000000000000000000", - }, - { - name: "sender", - hash: "0000000000000000000000", - }, - { - name: "address", - hash: "0000000000000000000000", - }, - { - name: "receive", - hash: "0000000000000000000000", - }, - { - name: "request", - hash: "0000000000000000000000", - }, - { - name: "send", - hash: "0000000000000000000000", - }, - { - name: "burn", - hash: "0000000000000000000000", - }, - { - name: "sign", - hash: "0000000000000000000000", - }, - { - name: "verify", - hash: "0000000000000000000000", - }, + { + name: 'wallet', + hash: '0000000000000000000000', + }, + { + name: 'owner', + hash: '0000000000000000000000', + }, + { + name: 'sender', + hash: '0000000000000000000000', + }, + { + name: 'address', + hash: '0000000000000000000000', + }, + { + name: 'receive', + hash: '0000000000000000000000', + }, + { + name: 'request', + hash: '0000000000000000000000', + }, + { + name: 'send', + hash: '0000000000000000000000', + }, + { + name: 'burn', + hash: '0000000000000000000000', + }, + { + name: 'sign', + hash: '0000000000000000000000', + }, + { + name: 'verify', + hash: '0000000000000000000000', + }, ], scenarios: [ - { - name: "requesting satoshis", - description: "happy-path evaluation for requesting satoshis.", + { + name: 'requesting satoshis', + description: 'happy-path evaluation for requesting satoshis.', - // The action being run in the scenario. - action: "requestSatoshis", + // The action being run in the scenario. + action: 'requestSatoshis', - // List of roles taken in this scenario, and the resources they provided. - roles: [ - { - role: "receiver", + // List of roles taken in this scenario, and the resources they provided. + roles: [ + { + role: 'receiver', + values: { + generated: { + ownerKey: 'KyRQa5pEXuzVcDwnXRLpYAascjchQW5DoxVRMbj4DTxS83573mz8', + }, + variables: { + requestedSatoshis: 2000, + }, + secrets: { + // This scenario does not carry any secrets. + }, + inputs: [], + outputs: [ + { + lockingBytecode: '76a91475c715ecb74178fe87933e57e947e5e92d904b8188ac', + valueSatoshis: 2000, + }, + ], + }, + }, + { + role: 'sender', + values: { + // The sender provides no raw data to the action. + generated: {}, + variables: {}, + secrets: {}, + + // The sender does provide input and change. + inputs: [ + { + outpointTransactionHash: '4ef28553a31a266719e66ba97fee3aeecd6d1788f7ff6ab12f8ebceda49660c0', + outpointIndex: 0, + sequenceNumber: 0, + unlockingBytecode: + '41226b2be7c2890c8bbde2f79e79640e56d866843f2e822ec51c469019d13db04a422c9ee49f5eefd26fee24e91910edbbb032b90cc54c34da80a61e69b0ee3d22412103e7ab26c36a7c7f45b2c26f33c08b0fa43a633268700f47216646d4cb37ae5696', + }, + ], + outputs: [ + { + lockingBytecode: '76a91475c715ecb74178fe87933e57e947e5e92d904b8188ac', + valueSatoshis: 2000, + }, + ], + }, + }, + ], + + // List of resources provided outside the context of a role. values: { - generated: { - ownerKey: "KyRQa5pEXuzVcDwnXRLpYAascjchQW5DoxVRMbj4DTxS83573mz8", - }, - variables: { - requestedSatoshis: 2000, - }, - secrets: { - // This scenario does not carry any secrets. - }, - inputs: [], - outputs: [ - { - lockingBytecode: - "76a91475c715ecb74178fe87933e57e947e5e92d904b8188ac", - valueSatoshis: 2000, + generated: { + // This scenario does not have any non-role generated values. + }, + variables: { + // This scenario does not have any non-role variables. + }, + secrets: { + // This scenario does not have any non-role secrets. }, - ], }, - }, - { - role: "sender", - values: { - // The sender provides no raw data to the action. - generated: {}, - variables: {}, - secrets: {}, - // The sender does provide input and change. - inputs: [ - { - outpointTransactionHash: - "4ef28553a31a266719e66ba97fee3aeecd6d1788f7ff6ab12f8ebceda49660c0", - outpointIndex: 0, - sequenceNumber: 0, - unlockingBytecode: - "41226b2be7c2890c8bbde2f79e79640e56d866843f2e822ec51c469019d13db04a422c9ee49f5eefd26fee24e91910edbbb032b90cc54c34da80a61e69b0ee3d22412103e7ab26c36a7c7f45b2c26f33c08b0fa43a633268700f47216646d4cb37ae5696", + // Outcomes provides the set of resulting values created from the action. + outcome: { + roles: { + receiver: { + name: 'Request', + description: 'Requested a specific amount of satoshis from one or more senders.', + icon: 'request', + }, + sender: { + name: 'Send', + description: 'Sent a specific amount of satoshis to the provided receiver.', + icon: 'send', + }, }, - ], - outputs: [ - { - lockingBytecode: - "76a91475c715ecb74178fe87933e57e947e5e92d904b8188ac", - valueSatoshis: 2000, - }, - ], + + transactions: [ + { + transaction: + '0200000001c06096a4edbc8e2fb16afff788176dcdee3aee7fa96be61967261aa35385f24e000000006441226b2be7c2890c8bbde2f79e79640e56d866843f2e822ec51c469019d13db04a422c9ee49f5eefd26fee24e91910edbbb032b90cc54c34da80a61e69b0ee3d22412103e7ab26c36a7c7f45b2c26f33c08b0fa43a633268700f47216646d4cb37ae5696000000000267530300000000001976a91475c715ecb74178fe87933e57e947e5e92d904b8188acd0070000000000001976a91475c715ecb74178fe87933e57e947e5e92d904b8188ac00000000', + value: '', + }, + ], }, - }, - ], - - // List of resources provided outside the context of a role. - values: { - generated: { - // This scenario does not have any non-role generated values. - }, - variables: { - // This scenario does not have any non-role variables. - }, - secrets: { - // This scenario does not have any non-role secrets. - }, }, - - // Outcomes provides the set of resulting values created from the action. - outcome: { - roles: { - receiver: { - name: "Request", - description: - "Requested a specific amount of satoshis from one or more senders.", - icon: "request", - }, - sender: { - name: "Send", - description: - "Sent a specific amount of satoshis to the provided receiver.", - icon: "send", - }, - }, - - transactions: [ - { - transaction: - "0200000001c06096a4edbc8e2fb16afff788176dcdee3aee7fa96be61967261aa35385f24e000000006441226b2be7c2890c8bbde2f79e79640e56d866843f2e822ec51c469019d13db04a422c9ee49f5eefd26fee24e91910edbbb032b90cc54c34da80a61e69b0ee3d22412103e7ab26c36a7c7f45b2c26f33c08b0fa43a633268700f47216646d4cb37ae5696000000000267530300000000001976a91475c715ecb74178fe87933e57e947e5e92d904b8188acd0070000000000001976a91475c715ecb74178fe87933e57e947e5e92d904b8188ac00000000", - value: "", - }, - ], - }, - }, ], }; export const p2pkhTemplateIdentifier = - generateTemplateIdentifier(p2pkhTemplate); + generateTemplateIdentifier(parseTemplate(p2pkhTemplate));