Skip to content

Bitcoin CashTemplates

For Dapp and Wallet Development

LibCash

Note

Preview Release

This is a preview release to get developer feedback and (hopefully) identify gaps in the current implementation. CashConnect is still under development and IS NOT yet safe for Production use.

Introduction

CashConnect is a lightweight templating system that aims to provide a versatile and secure interface for Dapp <-> Wallet communications on Bitcoin Cash.

These templates are serializable to JSON and use CashASM to give them advanced scripting capabilities. They have a structure as follows:

ts
{
  name: 'Example Template',
  description: 'This is an example template',
  actions: {
    // ... methods that can be invoked.
  },
  scripts: {
    // ... global scripts that are re-used by Actions.
    //     E.g. Lock/Unlock scripts.
  }
}
More Information

At their bare-minimum, Actions have params (data-in), instructions (processes) and returns (data-out). In this sense, they operate like a function with instructions being a pipeline of operations that should be performed (usually, by the wallet). This pipeline-esque approach allows templates to generalize to many use-cases that might otherwise be difficult to formalize into a fixed schema.

A very simple send action may look like so:

ts
{
  //...
  actions: {
    // A very simple send action that pays a given sats amount to the provided address
    send: {
      params: {
        receivingAddress: Vars.address('Receiving Address'),
        sats: Vars.number('Sats to send'),
      },
      instructions: [
        Instructions.transaction({
          name: 'tx',
          // Use any available UTXOs from parent wallet.
          inputs: ['*'],
          outputs: [
            {
              lockingBytecode: expr`receivingAddress`,
              valueSatoshis: expr`sats`,
            },
            // Append change outputs to parent wallet.
            '*',
          ]
        })
      ],
      returns: {
        txHash: Vars.transactionHash('Resulting Tx Hash'),
      }
    }
  }
}

And then executed as follows:

ts
// ... Instantiate template instance.

// Execute the send action.
const sendResult = templateInstance.executeAction('send', {
  receivingAddress: Address.from('bitcoincash:...').toLockscriptBytes(),
  sats: Satoshis.fromBCH('0.01').toBigInt(),
});

// The returned data (e.g. for DB persistence) is available under:
// sendResult.data;

// Broadcast the resulting transaction(s).
await blockchain.broadcastTransactions(sendResult.transactions);

To ease development and testing of templates, Tooling is provided under a cashconnect-js/templates-dev package. This includes a (nearly full-functional) Mock Blockchain adapter that can be used to test your Template flows.

// Import TestEnv and Example Template from our Templates-Dev package.
import { TestEnv, TemplateDataStore } from '@cashconnect-js/templates-dev';

// Import some primitive tooling for convenience.
import { Bytes } from '@cashconnect-js/core/primitives';

// Instantiate a Test Environment.
const testEnv = await TestEnv.from(TemplateDataStore);

// Create a Test Wallet and Template Instance belonging to the Wallet.
const { wallet: testWallet, templateInstance: walletTemplateInstance } = await testEnv.createWalletP2PKH({
    // Load the Wallet with 1 BCH.
    satsBalance: 100_000_000n
});

// Data to store on the blockchain.
// NOTE: This data stores data in an input - so we are not bound by OP_RETURN-like limits (we have ~1650 bytes to play with - ~10KB after 2026 upgrade)
//       This is plenty to store CashConnect payloads on-chain for Wallet Recovery (coming later)!
const dataToStore = Bytes.generateRandom(1500);

// Execute the Store Action on our Wallet's Template Instance.
const { data, transactions } = await walletTemplateInstance.executeAction('store', {
    data: dataToStore
});

// Broadcast the transaction(s) using our Mock Blockchain.
await testEnv.blockchain.broadcastTransactions(transactions);

// Show data returned by the action.
console.log('Data returned by Action:', data);

// Log the data we put on the blockchain.
console.log(`Stored Data (${dataToStore.length} bytes):`, dataToStore.toHex());
More Information

The intent is that Templates should become their own Repository that includes the Template Building code and a healthy suite of tests to confirm proper functioning of the template.

Auditors can then refer to this repository and compile, test and sign the template so that it can be whitelisted for safe-use.

(In this respect, it's not dissimilar to the current open-source reproducible-builds philosophy in that executables are compiled from source and signed by trusted parties).

See the FAQ for more information.

Once written, Templates can then be leveraged on Dapp frontends (using WalletConnect as a transport) like so:

// NOTE: As this is running inside a Code Sandbox, we are using a Console-Based UI provider.
//       But a proper Vanilla-JS UI provider will also be provided in future for easy, (hopefully) no-fuss, integration into Dapp Frontends too.
import { CashConnectDappConsole } from '@cashconnect-js/dapp';

// Import an example template and a Blockchain Adapter.
import { BlockchainElectrum, TemplateDataStore } from '@cashconnect-js/templates-dev';

// Import some convenience primitives.
import { Bytes } from '@cashconnect-js/core/primitives';

// Instantiate a Blockchain Connection using a Fulcrum Chipnet Server.
// NOTE: ONLY use Chipnet until V1.0 release.
const blockchain = await BlockchainElectrum.from({
    servers: ['chipnet.imaginary.cash'],
    // We are executing in an IFrame that is never visible.
    disableBrowserVisibilityHandling: true,
});

// Instantiate our CashConnect Dapp (using Chipnet).
const dapp = await CashConnectDappConsole.from(TemplateDataStore, 'bch:bchtest');

// Request a session with the wallet.
await dapp.newSession();

// Data to store on the blockchain.
// NOTE: This data stores data in an input - so we are not bound by OP_RETURN-like limits (we have ~1650 bytes to play with - ~10KB after 2026 upgrade)
//       This is plenty to store CashConnect payloads on-chain for Wallet Recovery (coming later)!
const dataToStore = Bytes.generateRandom(1500);

// Execute the store action on our template.
const { data, transactions } = await dapp.executeAction('store', {
    data: dataToStore,
});

// Broadcast the transactions to the blockchain.
await blockchain.broadcastTransactions(transactions);

// Show data returned by the action.
console.log('Data returned by Action:', data);

// Log the data we put on the blockchain.
console.log(`Stored Data (${dataToStore.length} bytes):`, dataToStore.toHex())

// Log a link to a block explorer where user can view the data.
console.log(`View transaction here: https://chipnet.chaingraph.cash/tx/${Bytes.from(data.dataTxHash).toHex()}`);

Uint8Array Params/Returns

Currently, all params must be encoded as Uint8Array - and all data returned as Uint8Array. In future, native JS types (e.g. bigint, string, etc) will be usable but there's some pre-requisite work I still need to do before allowing this. See Roadmap for more information.

INFO

While the initial release of CashConnect targets WalletConnect as a transport, the templates are designed to be transport agnostic. This means that, in future, they can:

  • Be used across HTTP transports (in a flow similar to BIP70/JPP).
  • Be used across P2P transports (e.g. LibP2P).
  • Be used in Backends.
  • Be used Intra-Wallet as Dapps (e.g. as Wallets or Integrated-Dapps).

See Roadmap and FAQ for further information.