This is an RGB sandbox and demo based on RGB version 0.10. It is based on the original rgb-node demo by St333p (version 0.1), grunch's guide and previous rgb-node sandbox versions.
The underlying Bitcoin network is regtest
.
RGB is operated via the rgb-contracts crate. BDK is used for walleting.
This sandbox can help explore RGB features in a self-contained environment or can be used as a demo of the main RGB functionalities for fungible assets.
Two versions of the demo are available:
- an automated one
- a manual one
The automated version is meant to provide a quick and easy way to see an RGB token be created and transferred. The manual version is meant to provide a hands-on experience with an RGB token and gives step-by-step instructions on how to operate all the required components.
Commands are to be executed in a bash shell. Example output is provided to allow following the links between the steps. Actual output when executing the procedure will be different each time.
Clone the repository, including (shallow) submodules:
git clone https://github.com/RGB-Tools/rgb-sandbox --recurse-submodules --shallow-submodules
The default setup assumes the user and group IDs are 1000
. If that's not the
case, the MYUID
and MYGID
environment variables in the
docker-compose.yml
file need to be updated accordingly.
The automated demo does not require any other setup steps.
The manual version requires handling of data directories and services, see the dedicated section for instructions.
Both versions will leave bdk-cli
and rgb-contracts
installed, in the
respective directories under the project root. These directories can be safely
removed to start from scratch, doing so will just require the rust crates to be
re-installed on the next run.
- git
- cargo
- docker
- docker compose
- sqlite3 development library (e.g.
libsqlite3-dev
on Debian-like systems)
The services started with docker compose simulate a small network with a bitcoin node and an explorer. These can be used to support testing and exploring the basic functionality of an RGB ecosystem.
Check out the manual demo below to get started with example commands. Refer to each command's help documentation for additional information.
To check out the automated demo, run:
bash demo.sh
The automated script will install the required rust crates, create empty service data directories, start the required services, prepare the wallets, issue assets, execute a series of asset transfers, then stop the services and remove the data directories.
For more verbose output during the automated demo, add the -v
option (bash demo.sh -v
), which shows the commands being run and additional
information (including output from additional inspection commands).
Following the manual demo and executing all the required steps is a rather long and error-prone process.
To ease the task of following the steps, a recording of the manual demo execution is available:
Note: this has not yet been updated to the 0.10 version.
The manual demo shows how to issue an asset and transfer some to a recipient.
At the beginning of the demo, some shell command aliases and common variables need to be set, then a series of steps are briefly described and illustrated with example shell commands.
During each step, commands either use literal values, ones that the user needs to fill in, or variables. Some variables (uppercase) are the ones set at the beginning of the demo, others (lowercase) need to be set based on the output of the commands as they are run.
Values that need to be filled in with command output follow the command
invocation that produces the required output and the example value is
ellipsized (...
), meaning the instruction should not be copied verbatim and
the value should instead be replaced with the actual output received while
following the steps.
Create data directories and start the required services in Docker containers:
# create data directories
mkdir data{0,1,core,index}
# start services (first time docker images need to be downloaded...)
docker compose up -d
To get a list of the running services you can run:
docker compose ps
To get their respective logs you can run, for instance:
docker compose logs bitcoind
Once finished and in order to clean up containers and data to start the demo from scratch, run:
# stop services and remove containers
docker compose down
# remove data directories
rm -fr data{0,1,core,index}
The rgb-contracts CLI tool does not handle wallet-related functionality, it performs RGB-specific tasks over data that is provided by an external wallet, such as BDK. In particular, in order to demonstrate a basic workflow with issuance and transfer, from the bitcoin wallets we will need:
- an outpoint_issue to which the issuer will allocate the new asset
- an outpoint_receive where the recipient will receive the asset transfer
- an addr_change where the sender will receive the bitcoin and asset change
- a partially signed bitcoin transaction (PSBT) to anchor the transfer
Wallets will be handled with BDK. We install its CLI to the bdk-cli
directory
inside the project directory:
cargo install bdk-cli --version "0.27.1" --root "./bdk-cli" --features electrum --locked
RGB functionality will be handled with rgb-contracts
. We install its CLI to
the rgb-contracts
directory inside the project directory:
cargo install rgb-contracts --version "0.10.0-rc.5" --root "./rgb-contracts" --all-features --locked
We setup aliases to ease CLI calls:
alias bcli="docker compose exec -u blits bitcoind bitcoin-cli -regtest"
alias bdk="bdk-cli/bin/bdk-cli"
alias rgb0="rgb-contracts/bin/rgb -n regtest -d data0"
alias rgb1="rgb-contracts/bin/rgb -n regtest -d data1"
We set some environment variables:
CLOSING_METHOD="opret1st"
DERIVE_PATH="m/86'/1'/0'/9"
DESC_TYPE="wpkh"
ELECTRUM="localhost:50001"
ELECTRUM_DOCKER="electrs:50001"
CONSIGNMENT="consignment.rgb"
PSBT="tx.psbt"
IFACE="RGB20"
We prepare the Bitcoin wallets using Bitcoin Core and BDK:
# Bitcoin Core wallet
bcli createwallet miner
bcli -generate 103
# if there are any bdk wallets from previous runs, they need to be removed
rm -fr ~/.bdk-bitcoin/{issuer,receiver}
# issuer/sender BDK wallet
bdk key generate
# example output:
# {
# "fingerprint": "a83fc09c",
# "mnemonic": "frozen nest frown retire wolf clinic tent know culture mad season whip impulse adjust hand change stomach meat wreck brick foam broken start reform",
# "xprv": "tprv8ZgxMBicQKsPey8NKMEpFemmWPMtYb4znAKtWVKr48Q1uDvemxT3RRW5m6NpToMoiVYSwVS16xKkeMueVhxnUsE7X7TpgzzxLSg7jBS1ma2"
# }
xprv_0="tprv8Zgx..."
bdk key derive -p "$DERIVE_PATH" -x "$xprv_0"
# example output:
# {
# "xprv": "[a83fc09c/86'/1'/0'/9]tprv8iqTQS7ksLhSaJNCfach4uTD6NJtoypuYnSEM5no1Km6YkCt8ciaYxtNL8pR68KU8a7GDSMhRTsrjaH7QR5bsx2e4287tjBa7SdFGyStGPR/*",
# "xpub": "[a83fc09c/86'/1'/0'/9]tpubDFXVYrA11iP7TmPzZEHHUK7KfPppyK1p8631dbq6RbZVPETem1YAjTWEWK6xkwQpJpcvcX6vGZ8xoK6yLE7CcRnm4514mhHGfJ1UNLHVxXG/*"
# }
xprv_der_0="[a83fc09c/86'/1'/0'/9]tprv8iqT..."
xpub_der_0="[a83fc09c/86'/1'/0'/9]tpubDFXV..."
# receiver BDK wallet
bdk key generate
# example output:
# {
# "fingerprint": "2976d70f",
# "mnemonic": "kick detail chronic crime unusual nut legal viable limb elegant always tent envelope betray comfort human famous boat garment shallow hunt brass mind bomb",
# "xprv": "tprv8ZgxMBicQKsPdorFhbmRFNu9tWMy2xxLLRYvxE5bpSvhymTpUmHdgaZiXN3ndATpSRTyyaxpnve3xYwhdoUhW1DwCW85MW9KwuJzV2xX6gV"
# }
xprv_1="tprv8Zgx..."
bdk key derive -p "$DERIVE_PATH" -x "$xprv_1"
# example output:
# {
# "xprv": "[2976d70f/86'/1'/0'/9]tprv8j496FRBryAmENLx7h66pKcgtet1o7fJN5yv4jVvZ8cpcDVTWajqqmyNFmrv9buLR7UkhqFKuvshcZQxdzfCgN1Qg1m5UcHLA5PRumTBvv7/*",
# "xpub": "[2976d70f/86'/1'/0'/9]tpubDFkBEfTS1LrS7qNk1LkhDjGoTgPwxSrCwPahMFYDyQRDShkE8yZS2GbERvepZ9mNAK5R4ejNmDoFFv1EHZ8QgJwqkXFmn6C1spUa6VUwr1x/*"
# }
xprv_der_1="[2976d70f/86'/1'/0'/9]tprv8j49..."
xpub_der_1="[2976d70f/86'/1'/0'/9]tpubDFkB..."
# generate addresses
bdk -n regtest wallet -w issuer -d "$DESC_TYPE($xpub_der_0)" get_new_address
# example output:
# {
# "address": "bcrt1q67z8nmswgvs38n64yl80plsejcs6vt867c2y22"
# }
addr_issue="bcrt1q67..."
bdk -n regtest wallet -w issuer -d "$DESC_TYPE($xpub_der_0)" get_new_address
# example output:
# {
# "address": "bcrt1qr36fkwcvaqkg4v5e2hdh9x4vrxhqysf6wk4hcn"
# }
addr_change="bcrt1qr3..."
bdk -n regtest wallet -w receiver -d "$DESC_TYPE($xpub_der_1)" get_new_address
# example output:
# {
# "address": "bcrt1q87w6s0anaugzksgmq9adwcgw9wyt6ekj7u8qc6"
# }
addr_receive="bcrt1q87..."
# fund wallets
bcli -rpcwallet=miner sendtoaddress "$addr_issue" 1
bcli -rpcwallet=miner sendtoaddress "$addr_receive" 1
bcli -rpcwallet=miner -generate 1
# sync wallets
bdk -n regtest wallet -w issuer -d "$DESC_TYPE($xpub_der_0)" -s "$ELECTRUM" sync
bdk -n regtest wallet -w receiver -d "$DESC_TYPE($xpub_der_1)" -s "$ELECTRUM" sync
# list wallet unspents and gather the outpoints
bdk -n regtest wallet -w issuer -d "$DESC_TYPE($xpub_der_0)" list_unspent
# example output:
# [
# {
# "is_spent": false,
# "keychain": "External",
# "outpoint": "6f6343401fc57c3f6a30043c61023e62311ee2b5d321823843af9cbcbfb2ac7e:1",
# "txout": {
# "script_pubkey": "0014d78479ee0e432113cf5527cef0fe199621a62cfa",
# "value": 100000000
# }
# }
# ]
outpoint_issue="6f6...c7e:1"
bdk -n regtest wallet -w receiver -d "$DESC_TYPE($xpub_der_1)" list_unspent
# example output:
# [
# {
# "is_spent": false,
# "keychain": "External",
# "outpoint": "bbc274a1f145552a6f22cab912c9b1903fb30333128b3b0f22212f2aa87772e2:0",
# "txout": {
# "script_pubkey": "00143f9da83fb3ef102b411b017ad7610e2b88bd66d2",
# "value": 100000000
# }
# }
# ]
outpoint_receive="bbc...2e2:0"
We setup the RGB clients, importing schema and interface implementation:
# 1st client
rgb0 import rgb-schemata/schemata/NonInflatableAssets.rgb
# example output:
# Stock file not found, creating default stock
# Wallet file not found, creating new wallet list
# Schema urn:lnp-bp:sc:BEiLYE-am9WhTW1-oK8cpvw4-FEMtzMrf-mKocuGZn-qWK6YF#ginger-parking-nirvana imported to the stash
rgb0 import rgb-schemata/schemata/NonInflatableAssets-RGB20.rgb
# example output:
# Implementation urn:lnp-bp:im:9EUGHC-wpuiyrQE-NdPBVyiv-VX4sVRBs-9yKfteug-HtqnGb#titanic-easy-citizen of interface urn:lnp-bp:if:48hc4i-m9JRcYQA-uUSzwFCK-VNEa9eZf-nhepU8QJ-pqosXS#laptop-domingo-cool for schema urn:lnp-bp:sc:BEiLYE-am9WhTW1-oK8cpvw4-FEMtzMrf-mKocuGZn-qWK6YF#ginger-parking-nirvana imported to the stash
# 2nd client (same output as 1st client)
rgb1 import rgb-schemata/schemata/NonInflatableAssets.rgb
rgb1 import rgb-schemata/schemata/NonInflatableAssets-RGB20.rgb
We retrieve the schema ID and set it as environment variable:
rgb0 schemata
# example output:
# urn:lnp-bp:sc:BEiLYE-am9WhTW1-oK8cpvw4-FEMtzMrf-mKocuGZn-qWK6YF#ginger-parking-nirvana RGB20
schema="urn:lnp-bp:sc:BEiLYE-am...ing-nirvana"
To issue an asset, we first need to prepare a contract definition file, then use it to actually carry out the issuance.
To prepare the contract file, we copy the provided template and modify the copy to set the required data:
- issued supply
- created timestamp
- closing method
- issuance txid and vout
We do this with a single command (which reads the template file, modifies the given properties and writes the result to the contract definition file):
sed \
-e "s/issued_supply/1000/" \
-e "s/created_timestamp/$(date +%s)/" \
-e "s/closing_method/$CLOSING_METHOD/" \
-e "s/txid:vout/$outpoint_issue/" \
contracts/usdt.yaml.template > contracts/usdt.yaml
To actually issue the asset, run:
rgb0 issue "$schema" "$IFACE" contracts/usdt.yaml
# example output:
# A new contract rgb:2Q7p6zS-JUCTP8pMJ-7fk8QBjYp-ngvHnJiuw-9jh8PPuvy-scKUUkd is issued and added to the stash.
# Use `export` command to export the contract.
contract_id="rgb:2Q7...Ukd"
This will create a new genesis that includes the asset metadata and the
allocation of the initial amount to outpoint_issue
.
You can list known contracts:
rgb0 contracts
# example output:
# rgb:2Q7p6zS-JUCTP8pMJ-7fk8QBjYp-ngvHnJiuw-9jh8PPuvy-scKUUkd
You can show the current known state for the contract:
rgb0 state "$contract_id" "$IFACE"
# example output:
# Global:
# spec := (naming=(ticker=("USDT"), name=("USD Tether"), details=~), precision=0)
# data := (terms=("demo RGB20 asset"), media=~)
# issuedSupply := (1000)
# created := (1691496693)
#
# Owned:
# assetOwner:
# amount=1000, utxo=6f6343401fc57c3f6a30043c61023e62311ee2b5d321823843af9cbcbfb2ac7e:1, witness=~ # owner unknown
In order to receive assets, the receiver needs to provide an invoice to the
sender. The receiver generates an invoice providing the amount to be received
(here 100
) and the outpoint where the assets should be allocated:
rgb1 invoice "$contract_id" "$IFACE" 100 "$CLOSING_METHOD:$outpoint_receive"
# example output:
# rgb:2Q7p6zS-JUCTP8pMJ-7fk8QBjYp-ngvHnJiuw-9jh8PPuvy-scKUUkd/RGB20/100+utxob:ZCTkvDN-mrwDXLSkx-1PKfHuaGH-R7cLf79Rg-YfSUGYn6i-YAS4Ts
invoice="rgb:2Q7...Ukd/RGB20/100+utxob:ZCT...4Ts"
Note: this will blind the given outpoint and the invoice will contain a blinded
UTXO in place of the original outpoint (see the utxob:
part of the invoice).
To send assets, the sender needs to create a consignment and commit to it into a bitcoin transaction. We need to create a PSBT and then modify it to include the commitment.
We create the PSBT, using outpoint_issue
as input and addr_change
for the
change (RGB and BTC):
bdk -n regtest wallet -w issuer -d "$DESC_TYPE($xpub_der_0)" create_tx \
-f 5 --send_all --utxos "$outpoint_issue" --to "$addr_change:0" \
--add_string opret
# example output:
# {
# "details": {
# "confirmation_time": null,
# "fee": 630,
# "received": 99999370,
# "sent": 100000000,
# "transaction": null,
# "txid": "26a1250ec087138e663cb7be52e13b18758e0257d9aba79d3f6eb0b0516763dd"
# },
# "psbt": "cHNidP8BAGIBAAAAAX6ssr+8nK9DOIIh07XiHjFiPgJhPAQwaj98xR9AQ2NvAQAAAAD+////Aore9QUAAAAAFgAUHHSbOwzoLIqymVXbcpqsGa4CQToAAAAAAAAAAAdqBW9wcmV0aAAAAAABAN4CAAAAAAEB9PcBuZVCyKTgMO50SVrsjfqdlVTABBB3cMtZaGXMN2MAAAAAAP3///8C/AUQJAEAAAAWABRhzatgiLp38YKt+2na/iB6/Y2MnADh9QUAAAAAFgAU14R57g5DIRPPVSfO8P4ZliGmLPoCRzBEAiAdCmZ/hSk/rZr+G+SGo/Lx8O8ZpAjIihTYcF983h796wIgHnKVCPwfmTIn+EPl8pDXbzHRTVnAn5jBRDCaMYS1SeMBIQPkcWZ9eqvZKlG1QK4t4vplBvahq6fHGp/lezPo9+pznWcAAAABAR8A4fUFAAAAABYAFNeEee4OQyETz1UnzvD+GZYhpiz6IgYClNhrjr91okI1jPK4r97icFId4fmZHKPciyYh7UBuX+MYqD/AnFYAAIABAACAAAAAgAkAAAAAAAAAACICAquWbk66NttJRZuWBIjM4NIbB1bT0/pH5VKyViGyey5TGKg/wJxWAACAAQAAgAAAAIAJAAAAAQAAAAAA"
# }
echo "cHN...AAA" | base64 -d > "data0/$PSBT"
We then modify the PSBT to set the commitment host:
rgb0 set-host --method "$CLOSING_METHOD" "data0/$PSBT"
# PSBT file 'data0/tx.psbt' is updated with opret1st host now set.
We create the transfer, providing the PSBT and the invoice. This generates the consignment:
rgb0 transfer --method "$CLOSING_METHOD" "data0/$PSBT" "$invoice" "data0/$CONSIGNMENT"
# example output:
# Transfer is created and saved into 'data0/consignment.rgb'.
# PSBT file 'data0/tx.psbt' is updated with all required commitments and ready to be signed.
# Stash data are updated.
The consignment can be inspected, but since the output is very long it's best to send the output to a file:
rgb0 inspect "data0/$CONSIGNMENT" > consignment.inspect
To view the result, open the consignment.inspect
file with a text editor.
For the purpose of this demo, copying the file over to the receiving node's data directory is sufficient:
cp data{0,1}/"$CONSIGNMENT"
In real-world scenarios, consignments are exchanged either via RGB HTTP JSON-RPC (e.g. using an RGB proxy) or other consignment exchange services.
Before a transfer can be safely accepted, it needs to be validated:
rgb1 validate "data1/$CONSIGNMENT"
# example output:
# Consignment has non-mined terminal(s)
# Non-mined terminals:
# - f17d544c0ac161f758d379c4366e6ede8f394da9633671908738b415ae5c8fb4
# Validation warnings:
# - terminal witness transaction f17d544c0ac161f758d379c4366e6ede8f394da9633671908738b415ae5c8fb4 is not yet mined.
At this point it's normal that validation reports a warning about the witness transaction not been mined, as the sender has not broadcast it yet. The sender is waiting for approval from the receiver.
Once validation has passed, the receiver can approve the transfer. For this demo let's just assume it happened, in a real-world scenario an RGB proxy would be typically used for this as well.
With the receiver's approval of the transfer, the transaction can be signed and broadcast:
bdk -n regtest wallet -w issuer -d "$DESC_TYPE($xprv_der_0)" \
sign --psbt $(cat data0/$PSBT | base64 | tr -d '\r\n')
# example output:
# {
# "is_finalized": true,
# "psbt": "cHNidP8BAH0BAAAAAX6ssr+8nK9DOIIh07XiHjFiPgJhPAQwaj98xR9AQ2NvAQAAAAD+////Aore9QUAAAAAFgAUHHSbOwzoLIqymVXbcpqsGa4CQToAAAAAAAAAACJqILJNu5OPAn29xMK30LPyEYF8BQzo4m4kMqdwQOf+vFMvaAAAACb8A1JHQgGzGylFw6I59wDVj85LlfJSDWRSj1ETsk8raL8NsrHYvNYAALgv5W39fUCA+5Wg2rcfqnBDyRzss7SzfYf8Km8dWDCRECcAAAABuC/lbf19QID7laDatx+qcEPJHOyztLN9h/wqbx1YMJGgDwAAAAGgDwECAAMAAAAAAAAPvcNrn//UdwiEAwAAAAAAAB1R+iZ9g65EZQH+yo6U6Rd5BR49w6kNrogl9GhgX2lPAkkbzeRxlHm2NcOArOXeqIFc56HypcJNjYqcpHODx2ySCGQAAAAAAAAAU/umctyJSLYssZtL73I+o2LndtWGDjf0niWCCTgYfVgAAAEA3gIAAAAAAQH09wG5lULIpOAw7nRJWuyN+p2VVMAEEHdwy1loZcw3YwAAAAAA/f///wL8BRAkAQAAABYAFGHNq2CIunfxgq37adr+IHr9jYycAOH1BQAAAAAWABTXhHnuDkMhE89VJ87w/hmWIaYs+gJHMEQCIB0KZn+FKT+tmv4b5Iaj8vHw7xmkCMiKFNhwX3zeHv3rAiAecpUI/B+ZMif4Q+XykNdvMdFNWcCfmMFEMJoxhLVJ4wEhA+RxZn16q9kqUbVAri3i+mUG9qGrp8can+V7M+j36nOdZwAAAAEBHwDh9QUAAAAAFgAU14R57g5DIRPPVSfO8P4ZliGmLPoiBgKU2GuOv3WiQjWM8riv3uJwUh3h+Zkco9yLJiHtQG5f4xioP8CcVgAAgAEAAIAAAACACQAAAAAAAAABBwABCGsCRzBEAiAJopaRrp3rkRwyFThM4feKs1/LrqP3oLj/4mxEWEX8NgIgWTtPIDgl2pOHAQHcW8Y8L3+kwPYbUfe5IHgE7Q49dpwBIQKU2GuOv3WiQjWM8riv3uJwUh3h+Zkco9yLJiHtQG5f4yb8A1JHQgO4L+Vt/X1AgPuVoNq3H6pwQ8kc7LO0s32H/CpvHVgwkSCzGylFw6I59wDVj85LlfJSDWRSj1ETsk8raL8NsrHYvAAiAgKrlm5OujbbSUWblgSIzODSGwdW09P6R+VSslYhsnsuUxioP8CcVgAAgAEAAIAAAACACQAAAAEAAAAAKfwGTE5QQlA0ALgv5W39fUCA+5Wg2rcfqnBDyRzss7SzfYf8Km8dWDCRIAjJeTBvKvCk+LhBj7FQmWMaz10SJxpnP3PjjR0/gGtNCfwGTE5QQlA0AQhTJmpjuc0zTwj8BU9QUkVUAAAI/AVPUFJFVAEgsk27k48Cfb3EwrfQs/IRgXwFDOjibiQyp3BA5/68Uy8A"
# }
psbt_signed="cHN...y8A"
bdk -n regtest wallet -w issuer -d "$DESC_TYPE($xpub_der_0)" -s "$ELECTRUM" \
broadcast --psbt "$psbt_signed"
# example output:
# {
# "txid": "f17d544c0ac161f758d379c4366e6ede8f394da9633671908738b415ae5c8fb4"
# }
Now the transaction has been broadcast, let's confirm it:
bcli -rpcwallet=miner -generate 1
In real-world scenarios the parties wait for the transaction to be included in a block.
Once the transaction has been confirmed, the receiver can accept the transfer, which is required to complete the transfer and update the contract state:
rgb1 accept "data1/$CONSIGNMENT"
# example output:
# Consignment is valid
#
# Transfer accepted into the stash
Note that accepting a transfer first validates its consignment.
Let's see the updated contract state, from the receiver's point of view:
rgb1 state "$contract_id" "$IFACE"
# example output:
# Global:
# spec := (naming=(ticker=("USDT"), name=("USD Tether"), details=~), precision=0)
# data := (terms=("demo RGB20 asset"), media=~)
# issuedSupply := (1000)
# created := (1691496693)
#
# Owned:
# assetOwner:
# amount=900, utxo=f17d544c0ac161f758d379c4366e6ede8f394da9633671908738b415ae5c8fb4:0, witness=f17d544c0ac161f758d379c4366e6ede8f394da9633671908738b415ae5c8fb4 # owner unknown
# amount=100, utxo=bbc274a1f145552a6f22cab912c9b1903fb30333128b3b0f22212f2aa87772e2:0, witness=f17d544c0ac161f758d379c4366e6ede8f394da9633671908738b415ae5c8fb4 # owner unknown
# amount=1000, utxo=6f6343401fc57c3f6a30043c61023e62311ee2b5d321823843af9cbcbfb2ac7e:1, witness=~ # owner unknown
The allocations for the original issuance and the transfer can be seen. The
receiver can recognize its allocation from the utxo
, which corresponds to the
outpoint_receive
provided to generate the invoice.
The sender doesn't need to explicitly accept the transfer, as it's automatically accepted when creating it.
The contract state already reflects the updated situation:
rgb0 state "$contract_id" "$IFACE"
# example output:
# Global:
# spec := (naming=(ticker=("USDT"), name=("USD Tether"), details=~), precision=0)
# data := (terms=("demo RGB20 asset"), media=~)
# issuedSupply := (1000)
# created := (1691496693)
#
# Owned:
# assetOwner:
# amount=900, utxo=f17d544c0ac161f758d379c4366e6ede8f394da9633671908738b415ae5c8fb4:0, witness=f17d544c0ac161f758d379c4366e6ede8f394da9633671908738b415ae5c8fb4 # owner unknown
# amount=1000, utxo=6f6343401fc57c3f6a30043c61023e62311ee2b5d321823843af9cbcbfb2ac7e:1, witness=~ # owner unknown
Since the outpoint_receive
was blinded during invoice generation, the payer
has no information on where the asset was allocated by the transfer, so the
receiver's allocation is not visible in the contract state on the sender's
side.