LWK Overview and Examples

The Liquid Wallet Kit (LWK) is a lightweight, easy to install toolkit for building single and multi-signature Liquid wallets. It supports the Jade HWW and also contains a software signer allowing developers to build physical custody solutions, automated software signers, or combinations of the two. LWK can also be used to manage the lifecycle of Liquid assets

The LWK Rust libraries can be used directly, via software wrappers or through an RPC client. LWK is split into different components that can be used independently or as a whole. Mobile app developers may only want to use the wallet and signer while back end developers might want to directly use the RPC client in their systems. LWK uses Blockstream's Electrum server to serve transaction and other blockchain data, meaning you do not need to sync an Elements node yourself, although you can set up your own Electrum and Elements if you want to not rely on another data provider.

The LWK GitHub site provides an overview of installation and use so here we will focus on breaking each step down and providing a worked through example. Our example will cover:

  1. Installation.
  2. Setting up signers.
  3. Creating a wallet (based on a 2-of-2 multi-signature descriptor).
  4. Importing and using the wallet.
  5. Spending from the wallet.
  6. Issuing, reissuing and burning a Liquid asset.

We use a 2-of-2 here just for simplicity but of course it can be any n-of-m scheme such as 2-of-3, 3-of-5 etc. and one or multiple Jades can be part of the quorum.

Installation

πŸ“˜

Prerequisites

You need to install Rust before proceeding. You can download Rust from the Rust website: https://www.rust-lang.org/tools/install.

You will also need to install "libudev-dev":

apt install -y libudev-dev

Once those are set up you can clone the LWK repository and build the binaries:

git clone [email protected]:Blockstream/lwk.git
cd lwk
cargo build --release
alias cli="$(pwd)/target/release/lwk_cli"

If you already have Rust installed and the build fails due to the version you have installed not being current enough you can use rustup to update and set the version of rustc to use using commands such as:

rustup update  
rustup default 1.75.0-x86_64-unknown-linux-gnu

If you prefer not to build from source you could install the latest LWK_CLI release from crates.io

cargo install lwk_cli

and if you want to be able to interact with Jade over serial

cargo install lwk_cli --features serial

You can now call the help menu to see what commands are available with LWK_CLI

lwk_cli --help

πŸ“˜

Available commands with LWK_CLI

This is a list of available commands that can be used with the LWK command line tool to give you an overview of all the actions that are possible:

server

  • start Start the server
  • scan Wait until an entire blockchain scan has been completed
  • stop Stop the server

signer

  • generate Generate a software signer, returns a mnemonic
  • jade-id Probe connected Jades, unlocks and returns IDs that allows to load a Jade
  • load-software Load a software signer giving it a name
  • load-jade Load a Jade signer giving it a name
  • load-external Load a signer (software, serial, external) giving it a name
  • details Details of a signer
  • unload Unload a software signer
  • list List loaded signers
  • sign Sign a transaction
  • singlesig-desc Prints a singlesig descriptor using this signer key
  • xpub Get an extended public key from the signer
  • register-multisig Register a multisig wallet

wallet

  • load Load a wallet with a CT descriptor giving it a name
  • unload Unload a wallet
  • list List existing loaded wallets
  • address Get an address from the given wallet name
  • balance Get the balance of the given wallet name
  • send Create an unsigned transaction (PSET)
  • drain Drain the wallet of the policy asset
  • issue Issue an asset
  • reissue Reissue a previously issued asset, needs ownership of the issuance token
  • burn Burn an asset
  • multisig-desc Print a multisig descriptor
  • broadcast Try to finalize the PSET and broadcast the transaction
  • details Get detailed information about the wallet
  • combine Combine PSETs
  • pset-details Get the details of a PSET
  • utxos Get the wallet unspent transaction outputs
  • txs Get the wallet transactions
  • tx Get a transaction
  • set-tx-memo Set a wallet tx memo
  • set-addr-memo Set a wallet address memo

asset

  • contract Helper to create a valid JSON contract
  • details Get detailed information about an asset
  • list List assets
  • insert Insert an asset
  • remove Remove an asset
  • from-explorer Insert an asset getting data from the block explorer
  • publish Try to publish the contract identified by the given asset id

πŸ“˜

lwk_cli & cli

Keep in mind that if you install LWK_CLI from crates.io and don't set an alias, the commands shown below should start with lwk_cliinstead of cli

Setting up signers

In our example, the 2 signers decide who will act as the proposer/coordinator for asset actions. Note that although any member can initiate an asset action it is probably easier to designate one member to act as the proposer/coordinator. During the initial setup phase both 2 signers will need to input their Xpub. We assume that 1 of the 2 signers will take on the coordinator role and: define the transaction including the LBTC fee, coordinate the exchange of partially signed transactions, and finalize and broadcast the transaction. We'll call the signers S1 and S2 during the example.

Both signers need to start their LWK client and generate a new signer (a mnemonic will be output). You can either run cli server start in one terminal and then use another terminal to run cli signer generate (remember to move to the right directory and set the cli alias) or run them both from the same terminal using the & command as shown below. Running in separate terminals means you can view the server's logs as they are processed.

LWK uses Blockstream's Electrum server to provide blockchain data and carry out blockchain actions like transaction broadcasts etc, although you can set up your own Electrum instance if you want and provide the URL using the --electrum-url optional argument that can be passed to cli server start.

cli server start &
cli signer generate

Example output from Signer 1 running the above:

{
  "mnemonic": "sheriff pass mechanic old near spring over pioneer rural wealth symptom cook"
}

The mnemonic that has been generated now needs loading as a named signer. Both signers need to do this. Here's what Signer 1 does, note they name the signer "S1":

cli signer load-software -s S1 --mnemonic "sheriff pass mechanic old near spring over pioneer rural wealth symptom cook"

By naming the signer both Signer 1 and Signer 2 can reference the correct signer to use when actions are later needed. This helps if they have multiple signers for multiple wallets they may control with other entities and groups of signers.

To see what signers are currently loaded in LWK you can run:

cli signer list

Creating a wallet

In order to create a wallet based on a 2-of-2 multi-signature descriptor we need to extract the xpub from each of the two signers. The xpubs are then used to create the descriptor.

Signer 1 would run the following and Signer 2 would run the same but with the name (the -s argument) of S2:

cli signer xpub -s S1 --kind bip84

Example output:

{
  "keyorigin_xpub": "[2a0b5159/84h/1h/0h]tpubDDdqx3Ytv8SHAvQYqnh3NgoixyND49wSwcpMiaLMGuGFLC7gUZ4ibabz4R3qtTFYvR3G1n8MxFMtpue1qmKBz4i61J54chUxeTJ9Ma8f16M"
}

The coordinator imports the 2 xpubs into LWK to create a descriptor for the multisig. We will refer to subsequent funds held in transaction outputs that are locked by the conditions set out in the descriptor as being within the multi-signature wallet from now on.

πŸ“˜

Notes on descriptors

Notice that keyorigin-xpub is used as many times as needed to fulfil the m from the n-of-m scheme. In our example, we need to use it twice. The descriptor-blinding-key is elip151, the kindis wsh and the threshold for our example is 2 (i.e. 2 signatures from the provided 2 possible signatures are needed to make a transaction valid).

cli wallet multisig-desc --descriptor-blinding-key elip151 --kind wsh --threshold 2 --keyorigin-xpub "[2a0b5159/84h/1h/0h]tpubDDdqx3Ytv8SHAvQYqnh3NgoixyND49wSwcpMiaLMGuGFLC7gUZ4ibabz4R3qtTFYvR3G1n8MxFMtpue1qmKBz4i61J54chUxeTJ9Ma8f16M" --keyorigin-xpub "[6295429d/84h/1h/0h]tpubDDm7RGVu5vLmSfT5tq7gJx6KsYk9Veg6Ytvj3MNL1jTpPFjvM6j185YjXRvgET6owS6PSSkmStHb5bTjCwBMtTH2zp6WrGyN2S3rjbSzuYn"

An example of the output from the above is shown below:

{  
  "descriptor": "ct(elip151,elwsh(multi(2,[2a0b5159/84h/1h/0h]tpubDDdqx3Ytv8SHAvQYqnh3NgoixyND49wSwcpMiaLMGuGFLC7gUZ4ibabz4R3qtTFYvR3G1n8MxFMtpue1qmKBz4i61J54chUxeTJ9Ma8f16M/\<0;1>/_,[6295429d/84h/1h/0h]tpubDDm7RGVu5vLmSfT5tq7gJx6KsYk9Veg6Ytvj3MNL1jTpPFjvM6j185YjXRvgET6owS6PSSkmStHb5bTjCwBMtTH2zp6WrGyN2S3rjbSzuYn/\<0;1>/_)))"  
}

Importing and using the wallet

The coordinator shares the descriptor with the other signer. They must both import it into LWK so that their wallets use the same multi-signature descriptor and can display balances, create transactions, and sign transactions. Notice how in the following, Signer 1 loads the wallet with the name "W1" for future use.

cli wallet load -d "ct(elip151,elwsh(multi(2,[2a0b5159/84h/1h/0h]tpubDDdqx3Ytv8SHAvQYqnh3NgoixyND49wSwcpMiaLMGuGFLC7gUZ4ibabz4R3qtTFYvR3G1n8MxFMtpue1qmKBz4i61J54chUxeTJ9Ma8f16M/<0;1>/*,[6295429d/84h/1h/0h]tpubDDm7RGVu5vLmSfT5tq7gJx6KsYk9Veg6Ytvj3MNL1jTpPFjvM6j185YjXRvgET6owS6PSSkmStHb5bTjCwBMtTH2zp6WrGyN2S3rjbSzuYn/<0;1>/*)))" -w W1

Either of the participants can then generate a receive address, and send an initial LBTC balance to that address. This will be used to pay the network fees for any subsequent transactions. Here we show Signer 1 getting the address. Notice how the wallet "W1" is referenced:

cli wallet address -w W1

An example output is:

{
"address": "tlq1qqvexwnnwkessvc5xukhytv7we5p0td4fsyx7e2lpfm7s7evrmcwcxwawvmrkjpc38uqa650ds5vxkmgu7gq93lnv5dcyx895pwxwg0andrnpr40e42mt",
  "index": 0
}

To check the balance of the descriptor wallet:

cli wallet balance -w W1

This will show the balance of the wallet by asset id. An example output showing 1,000,000 sats of the testnet LBTC asset is shown below. You can get testnet LBTC sent to your wallet to use for testing using https://liquidtestnet.com/faucet.

{
  "balance": {
    "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": 1000000
  }
}

Spending from the wallet

To send from the descriptor wallet, for example to send some LBTC from it to another address we first have one of the wallet owners create the spending transaction. Note that the recipient argument is of the format address:amount:asset and that amount is in sats.

cli wallet send -w W1 --recipient "tlq1qqd4er47y4kh4gc2vc6lfh45ead5h89tuxdxglgwdlek5lg8renysvzmvh0zq5gg3l39rvzffqp56lcks5tykkfm4x8p5mwzfh:50000000:144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49" >> tx1

The output will be a very long PSET of the format:

{
  "pset": "cHNldP8B..etc..TBQBBAAAβ€œ
}

πŸ“˜

Handling PSET data

In the code line above (the wallet sendcommand) we save the returned JSON data to a file named "tx1" by appending >> tx1 to the end of the command. The hex string of the PSET is very long, care must be taken when sending the hex to the signers and it should be noted that some text editors may not handle long continuous strings very well. Make sure the signer can copy the PSET and signs the entire hex.

The creator sends the PSET to the other signer and they each sign it like this (note that they specify the name of their signer, "S1" is used below):

cli signer sign -s S1 --pset "cHNldP8B..etc..TBQBBAAAβ€œ >> signed1

The creator then combines the two signed PSET:

cli wallet combine -w W1 --pset β€œfirst signed pset here” --pset β€œsecond signed pset here” >> combined

Any party can then broadcasts the signed and completed transaction.

cli wallet broadcast -w W1 --pset "cHNldP8B..etc..AQQAAA=="

The above will show the Liquid transaction ID.

Managing the lifecycle of a Liquid asset

You can also use LWK to issue, reissue and burn assets so you can manage the lifecycle of your Liquid assets with a lightweight client.

Issuance

To issue a Liquid asset you first need to create an unsigned PSET that issues a new asset and then pass this unsigned PSET among the quorum of signers so you can eventually broadcast a fully signed PSET. This last part would be similar to the spend example described above.

lwk_cli wallet issue --wallet <WALLET> --satoshi-asset <SATOSHI_ASSET> --satoshi-token <SATOSHI_TOKEN>

πŸ“˜

Options for the issuance command

  • -w, --wallet : name of the wallet that is initiating the issuance
  • --satoshi-asset <SATOSHI_ASSET>: The number of units of the newly issued asset (in sats as unit)
  • --address-asset <ADDRESS_ASSET>: Address receiving the newly issued asset. If not specified an external address of the wallet identified by --wallet will be used
  • --satoshi-token <SATOSHI_TOKEN>: Number of reissuance tokens emitted. Common choice is 0 or 1
  • --address-token <ADDRESS_TOKEN>: Address receiving the reissuance token(s). Must be specified if --satoshi_token is greater than 0.
  • --contract : Specify the JSON contract as string, you can use the included util to generate it
  • --fee-rate <FEE_RATE>: To optionally specify a fee

Let's assume we already have created a 2of2 multisig wallet named treasury using a software signer called soft1 and a Jade called Jade_Orange. As we described above we combined the xpubs of these 2 signers to create this multisig wallet.

lwk_cli signer list
{
  "signers": [
    {
      "fingerprint": "acecd89a",
      "name": "Jade_Orange"
    },
    {
      "fingerprint": "4fc853a4",
      "name": "soft1"
    }
  ]
  
lwk_cli wallet list
{
  "wallets": [
    {
      "descriptor": "ct(392d8e8e05314c041d2ce2ff0753e398b33d96706fbce79ce14143ec5be62f77,elwsh(multi(2,[acecd89a/87'/1'/0']tpubDCfmv5tyD3bCspYL6XxhzQaWNHmmNcd41Q6ee4WPHpA7NNXki5gpttUkcPGLDKKtZs4MYCcqymp8tKX8i5vcJzAQT6TCEc2TP5hvBnamm84/<0;1>/*,[4fc853a4/87'/1'/0']tpubDCjdtM3SXjtUe2bNWEfAR2f7NMVCDMKVScJSsLw6gwsM6Yg435f8vuybVVjpg9d5nFFYKbC9EgHtq255MxNzJB99vGssfdhUffDWuyS5s2J/<0;1>/*)))#k8ssdsu3",
      "name": "treasury"

To initiate the issuance flow we will start by creating the JSON contract that will contain the asset metadata like name, ticker, precision and domain.

lwk_cli asset contract --domain liquidtestnet.com --issuer-pubkey 020202020202020202020202020202020202020202020202020202020202020202 --name stablejuan --precision 2 --ticker STJ
{
  "entity": {
    "domain": "liquidtestnet.com"
  },
  "issuer_pubkey": "020202020202020202020202020202020202020202020202020202020202020202",
  "name": "stablejuan",
  "precision": 2,
  "ticker": "STJ",
  "version": 0
}

Now we can create an unsigned issuance PSET containing these asset metadata

lwk_cli wallet issue -w treasury --satoshi-asset 100000000 --satoshi-token 2 --contract '{"entity":{"domain":"liquidtestnet.com"},"issuer_pubkey":"020202020202020202020202020202020202020202020202020202020202020202","name":"stablejuan","precision":2,"ticker":"STJ","version":0}'
{
  "pset": "cHNldP8BAgQCAAAAAQQBAQEFAQRPAQQ1h88DV6y9foAA..."
}

The output will be a long PSET. We recommend to set variables in the terminal to facilitate handling PSETs data.

We can now analyse this new unsigned issuance PSET to see what we just created

lwk_cli wallet pset-details -w treasury --pset "cHNldP8BAgQCAAAAAQQBAQEFAQRPAQQ1h88DV6y9foAA..." 
{
  "balance": {
    "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172": 100000000,
    "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295": 2,
    "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": -47
  },
  "fee": 47,
  "has_signatures_from": [],
  "issuances": [
    {
      "asset": "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172",
      "asset_satoshi": 100000000,
      "is_confidential": false,
      "prev_txid": "048da567bcc8121d537843034cb51274af74ebea37865b3fa39e9d10c56902bd",
      "prev_vout": 0,
      "token": "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295",
      "token_satoshi": 2,
      "vin": 0
    }
  ],
  "missing_signatures_from": [
    {
      "fingerprint": "4fc853a4",
      "name": "soft1"
    },
    {
      "fingerprint": "acecd89a",
      "name": "Jade_Orange"
    }
  ],
  "reissuances": [],
  "warnings": ""

You can see that the output shows how this PSET is altering the balance of the treasury wallet (e.g. adding 100000000 of the newly issued asset, 2 reissuance tokens and spending 47 Lsats for fees). It also shows the specific issuance details, what signatures have been already provided (none at this point) and what signatures are missing.

We can now go ahead and add the first signature from our software signer (soft1)

lwk_cli signer sign -s soft1 --pset "cHNldP8BAgQCAAAAAQQBAQEFAQRPAQQ1h88DV6y9foAA..."

The output of this command is a new PSET with 1 out of 2 signatures. We can inspect this PSET again to confirm that the software signer signature has been added.

lwk_cli wallet pset-details -w treasury --pset "cHNldP8BAgQCAAAAAQQBAQEFAQRPAQQ1h88DV6y9foAAAAArVHDPh/VROv9qzjKp6GRATyt..."
{
  "balance": {
    "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172": 100000000,
    "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295": 2,
    "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": -47
  },
  "fee": 47,
  "has_signatures_from": [
    {
      "fingerprint": "4fc853a4",
      "name": "soft1"
    }
  ],
  "issuances": [
    {
      "asset": "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172",
      "asset_satoshi": 100000000,
      "is_confidential": false,
      "prev_txid": "048da567bcc8121d537843034cb51274af74ebea37865b3fa39e9d10c56902bd",
      "prev_vout": 0,
      "token": "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295",
      "token_satoshi": 2,
      "vin": 0
    }
  ],
  "missing_signatures_from": [
    {
      "fingerprint": "acecd89a",
      "name": "Jade_Orange"
    }
  ],
  "reissuances": [],
  "warnings": ""

You can see that indeed the software signer signature has been added and only the one from Jade is missing.

We will now repeat the signing process but this time with Jade. Make sure your Jade is connected to your machine and unlocked. You have the option to sign the PSET that already has the 1st signature or use the initial unsigned PSET and then combine them to generate the fully signed PSET. This is useful if you have many signers and want them to sign async without having to wait for their turn (i.e. parallel signing). In this example we will sign the PSET that already has the software signature

lwk_cli signer sign -s Jade_Orange --pset "cHNldP8BAgQCAAAAAQQBAQEFAQRPAQQ1h…”

Validate the information in your Jade device and sign if everything looks correct.

Again, we can inspect the PSET output from this command to confirm that the Jade signature has been added

lwk_cli wallet pset-details -w treasury --pset  "cHNldP8BAgQCAAAAAQQBAQEFAQRPAQQ1h88DV6y9f..."
{
  "balance": {
    "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172": 100000000,
    "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295": 2,
    "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": -47
  },
  "fee": 47,
  "has_signatures_from": [
    {
      "fingerprint": "4fc853a4",
      "name": "soft1"
    },
    {
      "fingerprint": "acecd89a",
      "name": "Jade_Orange"
    }
  ],
  "issuances": [
    {
      "asset": "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172",
      "asset_satoshi": 100000000,
      "is_confidential": false,
      "prev_txid": "048da567bcc8121d537843034cb51274af74ebea37865b3fa39e9d10c56902bd",
      "prev_vout": 0,
      "token": "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295",
      "token_satoshi": 2,
      "vin": 0
    }
  ],
  "missing_signatures_from": [],
  "reissuances": [],
  "warnings": ""

You can see that now the issuance PSET has all the necessary signatures and can be broadcasted

lwk_cli wallet broadcast -treasury --pset "cHNldP8BAgQCAAAAAQQBAQEFAQRPAQQ1h88DV6y9foA..."
{
  "txid": "736aa9c7548d243f82716618b367770dbf49051ba1d14cb05c60bace0e7656c0"
}

You can verify that the transaction was broadcasted by checking a block explorer like Blockstream.info

Finally, if you want to see the unblinded details of this issuance transaction, you can get the unblided_url by getting a list of transactions in the treasury wallet

lwk_cli wallet txs -w treasury
 "txs": [
    {
      "balance": {
        "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172": 100000000,
        "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295": 2,
        "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": -47
      },
      "fee": 47,
      "height": 1814044,
      "memo": "",
      "timestamp": 1741046583,
      "txid": "736aa9c7548d243f82716618b367770dbf49051ba1d14cb05c60bace0e7656c0",
      "type": "issuance",
      "unblinded_url": "https://blockstream.info/liquidtestnet/tx/736aa9c7548d243f82716618b367770dbf49051ba1d14cb05c60bace0e7656c0#blinded=100000,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,b24288833464f8532a244f95a3458978b78a49cf3e4630075a6cfff63b13baa1,e7de9995dccc61d4df4b12eae9d2f2c62ea71e4989874a8fbd2534205a50c028,100000000,0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172,6fa1eecb80771035ff86eff118bc771669e259f6a65324749596bad4581ba50b,4c6bd702edabbe6a941514e2c82bb91d2e4bacdb180297f15f5edae57b2778df,2,0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295,4ede3e6fe3f3b8fc1c237099439eb9c8947a0912368962b9de3c7241bd1a071d,91c175fbc554604f2df4c2d25b3996708c133f5800a0a4082aaf217a917f6495,99953,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,90666eef45c3826c0d036f88a0f83eb905a43594b5d0fb211a7c7daf9fbeccfd,97e6cb9cafb0e92a6f3204d4edc4d2a0e417742ab0c3c356b3dca1835b5fe02f"
    },
....

Reissuance

Reissuing an asset is similar to issuance but in this case the wallet initiating this action has to hold a reissuance token. You will also create a unsigned PSET that will need to be signed by the necessary quorum before it can be broadcasted.

lwk_cli wallet reissue --wallet <WALLET> --asset <ASSET> --satoshi-asset <SATOSHI_ASSET>

πŸ“˜

Options for the reissuance command

  • -w, --wallet : name of the wallet that is initiating the issuance
  • --asset : The asset to re-issue
  • --satoshi-asset <SATOSHI_ASSET>: The amount that will be reissued
  • --address-asset <ADDRESS_ASSET> Address receiving the re-issued asset. If not specified an external address of the wallet identified by --wallet will be used
  • --fee-rate <FEE_RATE>: To optionally specify a fee

Continuing with the same issuance example, let's now try to reissue more of this asset. Similar to issuance we first will create an unsigned reissuance PSET

lwk_cli wallet reissue -w treasury --asset 0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172 --satoshi-asset 100000000

Let's inspect the PSET that is output from this command

lwk_cli wallet pset-details -w treasury --pset "cHNldP8BAgQCAAAAAQQBAgEFAQRPAQQ1h88DV6y9foA..."
{
  "balance": {
    "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172": 100000000,
    "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295": 0,
    "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": -56
  },
  "fee": 56,
  "has_signatures_from": [],
  "issuances": [],
  "missing_signatures_from": [
    {
      "fingerprint": "4fc853a4",
      "name": "soft1"
    },
    {
      "fingerprint": "acecd89a",
      "name": "Jade_Orange"
    }
  ],
  "reissuances": [
    {
      "asset": "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172",
      "asset_satoshi": 100000000,
      "is_confidential": false,
      "token": "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295",
      "vin": 1
    }
  ],
  "warnings": ""
}

We can observe the reissuance details (i.e issuing 100000000 units of asset 0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172 and spending 56 Lsats on this transaction). We can also see what signatures are missing (all at this point).

We will sign this PSET with both our software signer and Jade using the signing command we've use in the previous examples lwk signer sign -s <soft1 or Jade> --pset <PSET>

lwk_cli signer sign -s soft1 --pset "cHNldP8BAgQCAAAAAQQBAgEFAQR….”
lwk_cli signer sign -s Jade_Orange --pset "cHNldP8BAgQCAAAAAQQBAgEFAQR….”

This process can be done in parallel (i.e. each signer can grab the original unsigned PSET and combine it before broadcast it) or it can be done in sequence (i.e each signer receives a PSET with signatures from previous signers)

Let's inspect our fully signed reissuance PSET

lwk_cli wallet pset-details -w treasury --pset "cHNldP8BAgQCAAAAAQQBAgEFAQRPAQQ1h88DV6y9fo..."
{
  "balance": {
    "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172": 100000000,
    "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295": 0,
    "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": -56
  },
  "fee": 56,
  "has_signatures_from": [
    {
      "fingerprint": "4fc853a4",
      "name": "soft1"
    },
    {
      "fingerprint": "acecd89a",
      "name": "Jade_Orange"
    }
  ],
  "issuances": [],
  "missing_signatures_from": [],
  "reissuances": [
    {
      "asset": "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172",
      "asset_satoshi": 100000000,
      "is_confidential": false,
      "token": "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295",
      "vin": 1
    }
  ],
  "warnings": ""

You can see that this reissuance PSET is fully signed because it has no missing signatures. We can now proceed to broadcast this transaction.

lwk_cli wallet broadcast -w treasury --pset "cHNldP8BAgQCAAAAAQQBAgEFAQR...”
{
  "txid": "029438078c8786af1d62293f09a99380ed3c72f79752856deb15900176c892c3"
}

Again, if we list the transactions in the treasury wallet, we will be able to get the unblided_url to see the unblinded details in Blockstream.info

lwk_cli wallet txs -w treasury
{
  "txs": [
    {
      "balance": {
        "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172": 100000000,
        "0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295": 0,
        "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": -56
      },
      "fee": 56,
      "height": 1814076,
      "memo": "",
      "timestamp": 1741048502,
      "txid": "029438078c8786af1d62293f09a99380ed3c72f79752856deb15900176c892c3",
      "type": "reissuance",
      "unblinded_url": "https://blockstream.info/liquidtestnet/tx/029438078c8786af1d62293f09a99380ed3c72f79752856deb15900176c892c3#blinded=99953,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,90666eef45c3826c0d036f88a0f83eb905a43594b5d0fb211a7c7daf9fbeccfd,97e6cb9cafb0e92a6f3204d4edc4d2a0e417742ab0c3c356b3dca1835b5fe02f,2,0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295,4ede3e6fe3f3b8fc1c237099439eb9c8947a0912368962b9de3c7241bd1a071d,91c175fbc554604f2df4c2d25b3996708c133f5800a0a4082aaf217a917f6495,2,0e80ce2defbc2a8fcb644a842a21f9cec2c562cf6611d787526e78a330a7f295,bcbd4698edcfb5808f4f600c4d4331c517bccd174c0b1635a39786108a8e7a7f,d217e1e8ae20836a80fa18630e856f6ba12b1bcc7e04df466ccae9ca8eff659d,100000000,0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172,0d067bf88fc9676bf5f241fc8050174f991bab1732bb1fcb6c3dd817a15b0222,8b37ff6827d34cad33c1d88932fd40c43060eaded576d3e62d86f110bb410f23,99897,144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49,69800244c5226972838ddcf6ff3b68d33205e62ad25fc6691778671e26e9b08e,5150a7b69b6ba1a00f2ea2c8c1c4eba93ffa52801870fc67100b516a7fafbe8c"
      },
..............

Burn

Burning an asset follows the same flow and as reissuance, the main difference is that you don't specify a receiving address. You need to specify from which wallet is the burn initiated (which has to contain the amount of asset that will be burned), the asset that will be burned and the amount. This action will also create a unsigned PSET that will need to be signed by the signing quorum before it can be finalized and broadcasted

lwk_cli wallet burn --wallet <WALLET> --asset <ASSET> --satoshi-asset <SATOSHI_ASSET>

Let's burn 50000000 units of the asset issued & reissued on the examples above

lwk_cli wallet burn -w treasury --asset 0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172 --satoshi-asset 50000000

Let's inspect the resulting unsigned burn PSET

lwk_cli wallet pset-details -w treasury --pset "cHNldP8BAgQCAAAAAQQBAgEFAQRPAQQ1h88DV..."
{
  "balance": {
    "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172": -50000000,
    "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": -46
  },
  "fee": 46,
  "has_signatures_from": [],
  "issuances": [],
  "missing_signatures_from": [
    {
      "fingerprint": "4fc853a4",
      "name": "soft1"
    },
    {
      "fingerprint": "acecd89a",
      "name": "Jade_Orange"
    }
  ],
  "reissuances": [],
  "warnings": ""
}

You can see that this PSET is indeed burning 50000000 units from asset0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172 and spending 46 Lsats for fees. It currently is missing both signatures.

Similarly as in the previous examples, we sign this PSET with the necessary signers (in this case soft1 and Jade_Orange) and we can inspect the resulting burn PSET before broadcasting this burning transaction.

lwk_cli signer sign -s <SIGNER> --pset <PSET>
lwk_cli wallet pset-details -w treasury --pset <PSET>
{
  "balance": {
    "0bb18d8ca2664551993b276d964ac5e50f5f0c7992b0b805b9f655f136fa1172": -50000000,
    "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49": -46
  },
  "fee": 46,
  "has_signatures_from": [
    {
      "fingerprint": "4fc853a4",
      "name": "soft1"
    },
    {
      "fingerprint": "acecd89a",
      "name": "Jade_Orange"
    }
  ],
  "issuances": [],
  "missing_signatures_from": [],
  "reissuances": [],
  "warnings": ""
}

We can see PSET is fully signed, so we can now broadcast this burn transaction

lwk_cli wallet broadcast -w treasury --pset <PSET>
{
  "txid": "59a2b4df9e84a6022c966a08ec4a963600b73498ec976c3cba8f0b3264ceee7a"
}

At the end of this process we will get a TXID and by listing the transactions in our wallet we will get the unblinded_url


When the users have finished they should shut the server down:

lwk_cli server stop

The Liquid Network is a Bitcoin layer-2 enabling the issuance of security tokens and other digital assets.

Β© 2023 Liquid Network
All rights reserved.

Feedback and Content Requests

We'd be happy to hear your suggestions on how we can improve this site.

BuildOnL2 Community

The official BuildOnL2 community lives
at community.liquid.net. Join us and build the future of Bitcoin on Liquid.

Telegram

Community-driven telegram group where
most of the Liquid developers hang out.
Go to t.me/liquid_devel to join.