RGB-Tools / RGB-Tools/rgb-lightning-node
Proposal: RGB asset-only payments with refundable carrier HTLCs
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 50
- Forks
- 48
- Avg merge
- 8h 52m
- Merged PRs (30d)
- 2
Description
Problem
Today an RGB Lightning payment still needs to carry some bitcoin.
This is necessary because RGB needs a real, non-trimmed HTLC output to attach the asset allocation to. As a result, even if the user only wants to send USDT or another RGB asset, the payment also moves sats in the same direction.
Over time, repeated RGB payments consume the channel's directional BTC liquidity.
The goal of this proposal is to allow asset-only RGB payments without moving BTC from payer to receiver, while keeping normal Lightning commitment and force-close behavior.
Proposed approach
Add a new opt-in BOLT11 mode called rgb_asset_only.
An asset-only invoice contains:
rgb_contract_idrgb_amountrgb_asset_only- payment secret / payment hash
It does not contain a bitcoin amount.
The payer still creates a normal positive HTLC. However, part of that HTLC is marked as a refundable BTC carrier.
The carrier exists only because we need a non-trimmed HTLC output capable of carrying the RGB state. It should not become part of the actual payment.
Every hop uses the same rgb_refundable_carrier_msat.
Example
Assume the route needs a 1,000 sat carrier.
There are two forwarding fees:
- Hop A: 10 sats
- Hop B: 5 sats
The HTLC amounts would look like this:
payer -> A: 1,015 sats
A -> B: 1,005 sats
B -> receiver: 1,000 sats
While the HTLC is pending, these are normal Lightning HTLCs and the full amounts remain locked.
When the payment succeeds off-chain, the 1,000 sat carrier is excluded from settlement accounting.
The final result is:
payer: -15 sats
A: +10 sats
B: +5 sats
receiver: 0 sats
The RGB asset still moves to the receiver.
So the payer pays normal routing fees, but the carrier itself is reusable for the next RGB payment.
This special accounting applies only to irrevocable off-chain fulfillment.
Failure and all on-chain paths continue using normal Lightning behavior.
Carrier size
The carrier cannot simply be a fixed dust value such as 546 sats.
It must be large enough that the RGB HTLC is non-trimmed in both commitment transaction views, including the fee required by the second-stage HTLC transaction.
For an outbound HTLC, the minimum should be calculated approximately as:
max(
holder_dust_limit + htlc_timeout_fee,
counterparty_dust_limit + htlc_success_fee
)
The calculation must use the actual channel type and the same feerate/dust rules used by LDK.
Channels supporting this feature should advertise their directional minimum carrier.
During routing, the payer chooses the smallest carrier that is valid for every channel in the route.
The route also needs to account for forwarding fees, since each hop sees:
carrier + downstream routing fees
and that amount must remain non-trimmed and fit within the channel's available liquidity.
Protocol changes
The feature should be explicitly negotiated. It must not be inferred simply because an RGB invoice has no BTC amount.
We need feature bits / TLVs for:
- invoice support
- node support
- channel type support
- channel announcement
update_add_htlc- onion payload
The carrier should be included both in update_add_htlc and in the authenticated onion payload.
Each forwarding node verifies that:
- the channel negotiated this feature;
- the payment contains RGB metadata;
carrier > 0;carrier <= amount_msat;- the carrier in the onion matches the carrier in the HTLC;
- the carrier is unchanged when forwarding;
- the HTLC is above the current non-trim threshold.
The final recipient additionally checks:
amount_msat == carrier
and verifies that the RGB contract and amount exactly match the stored invoice.
Carriers must be rejected for normal/legacy invoices.
Settlement accounting
While an HTLC is pending, the full HTLC amount is reserved and treated normally.
Only when fulfillment becomes irrevocable do we change the off-chain balance accounting.
Instead of settling:
amount_msat
we settle:
amount_msat - carrier
on both sides of the forwarding operation.
Because every hop uses the same carrier, it cancels out across the route.
Normal routing fees remain.
The receiver gets the RGB asset but receives zero sats.
On-chain behavior
On-chain behavior should not understand or refund the carrier.
If the payment reaches a commitment transaction, HTLC-success, HTLC-timeout, or sweep path, the existing Lightning rules apply to the complete HTLC amount.
This is intentional.
The refundable carrier is an off-chain accounting optimization, not a new Bitcoin script or on-chain HTLC type.
This also means we must never sign a commitment state where a pending RGB HTLC becomes trimmed.
Fee updates, splices, or other channel changes that would make an existing RGB HTLC unsafe must wait until the affected HTLC settles or fails.
RGB host-output reserve
The carrier solves the HTLC-output problem, but it does not remove the need for sats used by RGB host outputs.
The implementation therefore needs to reserve enough BTC for the required RGB host outputs in both commitment views.
Initial channel seeding is not sufficient because the relevant balance can change over the lifetime of the channel.
Persistence and downgrade protection
Invoice authorization and carrier information must survive:
- restart
- reconnect
- retransmission
- holding cells
- commitment updates
The channel/database serialization should be versioned so that an absent carrier cannot accidentally be interpreted as a legacy payment after the feature has been enabled.
If refundable-carrier state exists, peers must not be allowed to silently downgrade or disable the feature.
Wallet/API reporting should expose separately:
RGB amount
net sats transferred
carrier amount
routing fees
For the final receiver, net sats transferred should be zero.
Initial scope
For version 1, keep the feature deliberately narrow.
Support only:
- BOLT11
- single-part payments
- new RGB channels
- public routes
Do not support it initially for:
- MPP
- keysend
- BOLT12
- swaps
- virtual channels
- private routes
- blinded routes
- async/HODL payments
- fee skimming
These can be considered separately after the basic accounting model is proven.
Existing payments must continue to behave exactly as they do today.
Tests / acceptance criteria
At minimum we should cover:
- normal asset-only invoice creation and payment;
- attempting to inject a carrier into a legacy invoice;
- missing carrier;
- zero carrier;
- carrier larger than the HTLC;
- different carrier values between onion and HTLC;
- changing the carrier between hops;
- incorrect RGB contract or amount at the receiver;
- HTLC exactly at the non-trim threshold;
- HTLC one msat below the threshold;
- different supported channel types;
- fee-update races;
- splice races;
- insufficient RGB host-output reserve;
- single-hop payment;
- multi-hop payment;
- payment failure;
- retry;
- node restart;
- reconnect;
- unsupported channel in the middle of a route;
- attempted feature downgrade;
- force-close before preimage;
- force-close after preimage;
- HTLC timeout and success transactions.
The main acceptance test is simple:
A successful RGB asset-only payment transfers the RGB asset, charges the payer only the normal routing fees, pays each forwarding node exactly its fee, transfers zero sats to the receiver, and leaves the BTC carrier available for reuse.
Alternatives
Zero-value or trimmed HTLC
This does not work because RGB needs an actual commitment output carrying the RGB allocation.
The positive carrier preserves the existing RGB transaction model.
Detect asset-only mode from an amountless invoice
An amountless invoice normally means that the payer chooses the BTC amount.
Using the absence of an amount to activate special settlement behavior would therefore be ambiguous and could create downgrade/injection problems.
The invoice should explicitly authorize rgb_asset_only.
Refund the complete HTLC
The HTLC also contains accumulated routing fees.
Refunding the whole amount would refund those fees as well.
Only the common carrier should be removed from settlement accounting.
Separate debt/refund ledger
Another option would be to settle the payment normally and track a separate BTC debt or compensating payment.
That adds another state machine and creates recovery and atomicity problems.
Keeping the carrier inside the existing HTLC flow avoids this additional synchronization.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
The proposal names no repository files, tests, or entry points. Start by locating the existing RGB/Lightning invoice, HTLC, onion, routing, channel-state, persistence, and transaction paths, then map the listed acceptance criteria to tests. Done means the negotiated carrier behavior, accounting, persistence, downgrade protection, and excluded scope are covered without changing legacy or on-chain behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend-api-design, blockchain, payments
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100