libtokensv

Token scripts for libspiffy wallets, as libspiffy plugins, and a client for BSV overlay services. libspiffy itself is not changed: the plugins go through its ScriptPlugin / TransactionBuilderPlugin interface, as tstokenlib does.

PushDrop

PushDrop is the token format BRC-100 overlays use (message box host advertisements, SHIP and SLAP advertisements, and others): data fields in a spendable output, locked to a key.

<lockingPublicKey> OP_CHECKSIG <field 1> ... <field n> OP_2DROP ... [OP_DROP]

PushDrop.lock and PushDrop.decode write and read these scripts exactly as @bsv/sdk's PushDrop template does (the tests check against vectors it wrote).

In a wallet

Register the plugin in the isolate the libspiffy wallet runs in:

PluginRegistry().register(PushDropPlugin(network: NetworkType.MAIN));

and pay its output spec with the wallet's PayInvoiceCommand:

final spec = PushDropPlugin.outputSpec(
  outputs: [PushDropOutput(lockingScript: newToken)],          // tokens to make
  spends: [PushDropSpend(txid: ..., vout: 0, satoshis: BigInt.one,
                         lockingScript: oldToken)],             // tokens to spend
);
// PayInvoiceCommand(walletId: ..., invoiceId: ..., outputs: [spec], ...)

The wallet funds the fee and gets the change. Replacing a token is spending the old one and making the new one in one transaction; withdrawing one is spending it alone.

A token is spent with a signature by its locking key, which the wallet must hold: one of its HD keys, or a BRC-42 (type-42) key it has recorded with the coordinator's RecordType42AddressesCommand (libspiffy 5.5.0). The plugin's extractMetadata names the address of the locking key as the token's owner, so the wallet tracks a token locked to a key it holds as its own, without counting it in the balance or spending it as change.

Signed fields, and the key a token is locked to

BRC-100 tokens usually carry a signature over their fields, made with a BRC-42 child of the identity key (createSignature), and the reference client locks the token to that same child. A libspiffy wallet refuses to sign data with a key it holds coins under (such a signature can be a spend signature), so once the locking key is recorded it can no longer sign the fields of the next token. Lock the token to a different child instead: the overlays check the field signature, not the lock. For a message box advertisement: fields signed with protocol [1, 'messagebox advertisement'], key 1, counterparty anyone, as tm_messagebox verifies; the token locked to the same protocol and key with counterparty self, recorded as the derivation with the wallet's anchor key as both anchor and sender and the invoice number 1-messagebox advertisement-1. Both derive from the identity, so a restored wallet finds its tokens again.

Overlay services

OverlayClient asks BRC-24 lookup services and submits BRC-22 transactions, finding hosts as @bsv/sdk's LookupResolver and TopicBroadcaster do: the hosts of a lookup service through the SLAP trackers (ls_slap), the hosts of a topic through its SHIP advertisements (ls_ship).

final overlay = OverlayClient.forNetwork(OverlayNetwork.mainnet);
final outputs = await overlay.lookup('ls_messagebox', {'identityKey': key});
final result = await overlay.submit(beef, ['tm_messagebox']);
if (!result.acknowledgedIn('tm_messagebox')) { /* not admitted */ }

Libraries

libtokensv
BSV token scripts for libspiffy wallets, and a client for BSV overlay services.