Awesome
BOLT12 Prism Plugin
A CLN plugin for creating and interacting with prisms based on BOLT12. Prism payouts may be executed interactively (e.g., manually or via another CLN plugin), or bound to a BOLT12 offer.
BOLT12 is currently experimental and you will need to add the --experimental-offers flag when starting lightningd
WARNING
This software should be considered for Testing and Evaluation purposes only!
Background
This started as the winning hackathon project at bitcoin++ 2023 in Austin, Tx.
Other projects that compliment this one include:
- roygbiv.guide to learn more about prisms.
- lnplay which integrates the prism plugin
Getting Started
Starting the Plugin
There are 3 ways to start a CLN plugin...
Add to Your Config
Find your c-lightning config file and add
plugin=/path/to/bolt12-prism.py
Manually start the plugin
lightning-cli plugin start /path/to/bolt12-prism.py
Run on startup
lightningd --experimental-offers --plugin=/path/to/bolt12-prism.py
Using the plugin
Create a prism
MEMBERS_JSON=
[{"description" : "Drummer", "destination": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrc2qajx2enpw4k8g93pqwtzkujnk4t5hteur6cn8aqyt3v38u300ldryz6z2fw7zwr4gjxcq", "split": 1.0, "payout_threshold_msat": "0", "fees_incurred_by": "local"},{"description" : "Guitarist", "destination": "02dbfb1d0a6fbc717ae3f28e4dc185d812e399ee33055587eeb06469c94e4326e5", "split": 1.0, "payout_threshold_msat": "0", "fees_incurred_by": "remote"},{"description" : "Tuba", "destination": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrc2qajx2enpw4k8g93pqvzk2archeau0slmv7dlsvwlrcra6pxml6mdwxetsml0hhcp9w4cu", "split": 1.0, "payout_threshold_msat": "0", "fees_incurred_by": "local"}]'
lightning-cli prism-create -k description="Band Prism" members="$MEMBERS_JSON" outlay_factor="0.75"
{
"prism_id": "a59afe3927e6cdce218fb82415574204df4f762bb9c2c535a420488b3fa5fbf5",
"description": "Band Prism",
"timestamp": 1720044718,
"outlay_factor": 0.75,
"prism_members": [
{
"member_id": "95256245897c5423f9c319afbcd224dba9902cb829991a2435e2902631810389",
"description": "Drummer",
"destination": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrc2qajx2enpw4k8g93pqwtzkujnk4t5hteur6cn8aqyt3v38u300ldryz6z2fw7zwr4gjxcq",
"split": 1.0,
"fees_incurred_by": "local",
"payout_threshold_msat": 0
},
{
"member_id": "412c79d6feafdf907a348ddb15627ba057d2c3ac3615f8f41680acd176d8bc2d",
"description": "Guitarist",
"destination": "02dbfb1d0a6fbc717ae3f28e4dc185d812e399ee33055587eeb06469c94e4326e5",
"split": 1.0,
"fees_incurred_by": "remote",
"payout_threshold_msat": 0
},
{
"member_id": "ebc13b1be16346e8e08571cbde85f5eb604cd76233181b5edbd559e3f35858ab",
"description": "Tuba",
"destination": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrc2qajx2enpw4k8g93pqvzk2archeau0slmv7dlsvwlrcra6pxml6mdwxetsml0hhcp9w4cu",
"split": 1.0,
"fees_incurred_by": "local",
"payout_threshold_msat": 0
}
]
}
Setting the outlay_factor
to 0.75
means that total outlays will be only 75% of the incoming amount. This is an implicit "pay-to-self" of 25%. This feature allows a Prism member to host the prism.
An
outlay_factor
of1.1
means total outlays will be 110% of incoming events. This can be used for things like "employer matching" or similar schemes.
Split percentages are normalized over all members of the prism. So, in the case above, each prism member will receive an equal split (i.e., 33.33%) of the total outlays.
Note that if
destination
is a node pubkey, keysend is used for payouts instead of BOLT12.
Delete a prism
Need to delete a prism from the database? Use prism-delete prism_id
. Be sure to remove any bindings first (see below)!
List prisms
Run the following command to view a prism policy. (Note that you can add a prism_id
and specific prism will be returned.)
lightning-cli prism-list
{
"prisms": [
{
"prism_id": "4cf1e1b3e7bea1d0b8f9612ed138f164a03abe4541b1e82c8cde16af050b4f53",
"description": "Band Prism",
"timestamp": 1720042032,
"outlay_factor": 0.75,
"prism_members": [
{
"member_id": "547d51d513aec15dc2229047d6d912b63d39416e3682b6dee4d6ae686f88b9b4",
"description": "Drummer",
"destination": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrc2qajx2enpw4k8g93pqwt5tgjxynmyjjx0zjcssjx9z5h377kewwa4k2zqtu0k8k47sscvv",
"split": 1.0,
"fees_incurred_by": "local",
"payout_threshold_msat": 0
},
{
"member_id": "1fdda73dbdd4d3dda4057cf302ec10dedfc55466163133fd236e313a60a52204",
"description": "Guitarist",
"destination": "030e7553729959098adc3b63df389be92fbe0ca756a5469ba71364fc44c1338ac7",
"split": 1.0,
"fees_incurred_by": "remote",
"payout_threshold_msat": 0
},
{
"member_id": "22dad0bd9cfcf4adfa367d2627e80a7a92e531b60a3d7c006495258c57b9db5e",
"description": "Tuba",
"destination": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrc2qajx2enpw4k8g93pq2zlvjv9s9she2fpj0msp6jz0sgrl7wgm425fc6rhdkhcqtqlggks",
"split": 1.0,
"fees_incurred_by": "local",
"payout_threshold_msat": 0
}
]
}
]
}
Prism-Pay - Executes a Payout
lightning-cli prism-pay -k prism_id=a59afe3927e6cdce218fb82415574204df4f762bb9c2c535a420488b3fa5fbf5 amount_msat=10000000
When run, this RPC command will execute (i.e., pay-out) a prism.
WARNING! prism-pay DOES NOT have an associated income event to account for payouts! Consider adding a binding so that fees can be tracked by maintaing a member outlay. Also, Prism payouts via
prism-pay
DO NOT respect thepayment_threshold
since this is only relevant to bindings.
{
"prism_member_payouts": {
"547d51d513aec15dc2229047d6d912b63d39416e3682b6dee4d6ae686f88b9b4": {
"destination": "03a43dce86b0c2bd0b18d97e28f045be5866c94bff3ca8a96e5a4a370f469e26b9",
"payment_hash": "89ea468a04f5747882b21d4f50d13e353b3d346a50d5ba80f8f94000d0596cc1",
"created_at": 1721069260.3575482,
"parts": 1,
"amount_msat": 2500000,
"amount_sent_msat": 2500000,
"payment_preimage": "11eaaa9cc7e0e8a27038130ecad56bb766d0ed5e09d202e313ea06620344abec",
"status": "complete"
},
"1fdda73dbdd4d3dda4057cf302ec10dedfc55466163133fd236e313a60a52204": {
"destination": "0273d97fd40ac7c17fd9bd6476316edf44aafcf25a41ef9c36b941b2fe4396c4de",
"payment_hash": "c773cb2cdb6c1fc5478c972effa75588f2b6e63f11005a475dc052f5c48f82ce",
"created_at": 1721069261.4327104,
"parts": 1,
"amount_msat": 2500000,
"amount_sent_msat": 2500000,
"payment_preimage": "0ba602d4ebd33bead0947c244360963dedead47c2a79eb35ca2afebdfa0bf37d",
"status": "complete"
},
"22dad0bd9cfcf4adfa367d2627e80a7a92e531b60a3d7c006495258c57b9db5e": {
"destination": "02e36cf99a16ac84446bcd60258ce2a0a6e334346b07ee647b88fce18c6d4fe3a0",
"payment_hash": "325de28046cc828644169be6a03e1dceef1896cf30822ccb32ed10ff88eb5ef2",
"created_at": 1721069262.5050325,
"parts": 1,
"amount_msat": 2500000,
"amount_sent_msat": 2500000,
"payment_preimage": "730e0b6ab952cb681ccb5c7b84bc7b507790bb70bbb88156bbbec0d2c7f84f45",
"status": "complete"
}
}
}
The output above shows the result of paying to each BOLT12 offer specified in prism1
. Notice they get equal splits in accordance with the prism policy.
Bindings
Often you will want your prisms to be paid out whenever you have an incoming payment. This is what a binding is for. Note that ONE and ONLY ONE prism can be bound to any given offer.
Create a binding
lightning-cli prism-bindingadd -k offer_id=70761889e73c049ce243b3ac73e06533830e4cdf8dbc3a5717b05bab93c7af64 prism_id=a59afe3927e6cdce218fb82415574204df4f762bb9c2c535a420488b3fa5fbf5
The above command binds a prism (prism_id
) to a bolt12 offer with the specified offer_id
.
{
"status": "must-create",
"timestamp": 1720044718,
"offer_id": "70761889e73c049ce243b3ac73e06533830e4cdf8dbc3a5717b05bab93c7af64",
"prism_id": "a59afe3927e6cdce218fb82415574204df4f762bb9c2c535a420488b3fa5fbf5",
"prism_binding_key": [
"prism",
"v2.1",
"bind",
"bolt12",
"70761889e73c049ce243b3ac73e06533830e4cdf8dbc3a5717b05bab93c7af64"
],
"prism_members": [
{
"member_id": "95256245897c5423f9c319afbcd224dba9902cb829991a2435e2902631810389",
"description": "Drummer",
"destination": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrc2qajx2enpw4k8g93pqwtzkujnk4t5hteur6cn8aqyt3v38u300ldryz6z2fw7zwr4gjxcq",
"split": 1.0,
"fees_incurred_by": "local",
"payout_threshold_msat": 0
},
{
"member_id": "412c79d6feafdf907a348ddb15627ba057d2c3ac3615f8f41680acd176d8bc2d",
"description": "Guitarist",
"destination": "02dbfb1d0a6fbc717ae3f28e4dc185d812e399ee33055587eeb06469c94e4326e5",
"split": 1.0,
"fees_incurred_by": "remote",
"payout_threshold_msat": 0
},
{
"member_id": "ebc13b1be16346e8e08571cbde85f5eb604cd76233181b5edbd559e3f35858ab",
"description": "Tuba",
"destination": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrc2qajx2enpw4k8g93pqvzk2archeau0slmv7dlsvwlrcra6pxml6mdwxetsml0hhcp9w4cu",
"split": 1.0,
"fees_incurred_by": "local",
"payout_threshold_msat": 0
}
]
}
List Prism Bindings
Want to see your bindings? Run prism-bindinglist
. Add an offer_id
to view a specific binding.
lightning-cli -k prism-bindinglist -k offer_id=dfa361b51238e8ffc4c5db74105d618090e1c7fd48442d3e735dcaaf35347b6f
{
"bolt12_prism_bindings": [
{
"offer_id": "dfa361b51238e8ffc4c5db74105d618090e1c7fd48442d3e735dcaaf35347b6f",
"prism_id": "4cf1e1b3e7bea1d0b8f9612ed138f164a03abe4541b1e82c8cde16af050b4f53",
"timestamp": 1720042032,
"member_outlays": [
{
"member_id": "547d51d513aec15dc2229047d6d912b63d39416e3682b6dee4d6ae686f88b9b4",
"outlay_msat": 50000000
},
{
"member_id": "1fdda73dbdd4d3dda4057cf302ec10dedfc55466163133fd236e313a60a52204",
"outlay_msat": 0
},
{
"member_id": "22dad0bd9cfcf4adfa367d2627e80a7a92e531b60a3d7c006495258c57b9db5e",
"outlay_msat": 0
}
]
}
]
}
The outlay_msat
property is how the prism plugin deals with failed payments and to account for payout fees. When a prism binding has an incoming payment, prism member outlays in the binding are increased according the prism policy and incoming amount. When fees_incurred_by=remote
and a payment to a prism member succeeds, the outlay is decremented by the total amount of the payment including fees paid. When fees_incurred_by=local
, fees are paid by the node operator hosting the prism.
Prism member payouts occur when outlays exceed the payout_threshold_msat
in the respective prism policy. Until then, outlays accumulate. Similarly, if a payout to a prism member fails for whatever reason, the outlay remains unchanged and payout is deferred.
Set a Binding Member Outlay
Say you have outstanding outlays that aren't clearing for whatever reason (e.g., unreachable node in offer). If you know the individual, you can always remit the sats with another wallet. In those cases, you will want to first zeroize the outlays for that member in the binding using the prism-setoutlay
method.
lightning-cli prism-setoutlay -k offer_id=dfa361b51238e8ffc4c5db74105d618090e1c7fd48442d3e735dcaaf35347b6f member_id=547d51d513aec15dc2229047d6d912b63d39416e3682b6dee4d6ae686f88b9b4 new_outlay_msat=0
{
"bolt12_prism_bindings": {
"offer_id": "dfa361b51238e8ffc4c5db74105d618090e1c7fd48442d3e735dcaaf35347b6f",
"prism_id": "4cf1e1b3e7bea1d0b8f9612ed138f164a03abe4541b1e82c8cde16af050b4f53",
"timestamp": 1720042032,
"member_outlays": [
{
"member_id": "547d51d513aec15dc2229047d6d912b63d39416e3682b6dee4d6ae686f88b9b4",
"outlay_msat": 0
},
{
"member_id": "1fdda73dbdd4d3dda4057cf302ec10dedfc55466163133fd236e313a60a52204",
"outlay_msat": 0
},
{
"member_id": "22dad0bd9cfcf4adfa367d2627e80a7a92e531b60a3d7c006495258c57b9db5e",
"outlay_msat": 0
}
]
}
}
Note that since we're tracking outlays, if they actually owe us money, we can set the outlay value to a negative number. As income events flow in, the outlay will eventually turn positive again, accounting for what they owe us.
Remove a binding
You can remove a binding by running prism-bindingremove offer_id
.
How to contribute
There is a copy of the startup_regtest script from the c-lightning repo contrib dir for local development. Consider joining our Telegram group at roygbiv.guide/contribute.