FundPsbt
FundPsbt creates a fully populated PSBT that contains enough inputs to fund the outputs specified in the template. There are three ways a user can specify what we call the template (a list of inputs and outputs to use in the PSBT): Either as a PSBT packet directly with no coin selection (using the legacy "psbt" field), a PSBT with advanced coin selection support (using the new "coin_select" field) or as a raw RPC message (using the "raw" field). The legacy "psbt" and "raw" modes, the following restrictions apply:
- If there are no inputs specified in the template, coin selection is performed automatically.
- If the template does contain any inputs, it is assumed that full coin selection happened externally and no additional inputs are added. If the specified inputs aren't enough to fund the outputs with the given fee rate, an error is returned.
The new "coin_select" mode does not have these restrictions and allows the user to specify a PSBT with inputs and outputs and still perform coin selection on top of that. For all modes this RPC requires any inputs that are specified to be locked by the user (if they belong to this node in the first place).
After either selecting or verifying the inputs, all input UTXOs are locked with an internal app ID.
NOTE: If this method returns without an error, it is the caller's responsibility to either spend the locked UTXOs (by finalizing and then publishing the transaction) or to unlock/release the locked UTXOs in case of an error on the caller's side.
Source: walletrpc/walletkit.proto
gRPC
rpc FundPsbt (FundPsbtRequest) returns (FundPsbtResponse);
REST
HTTP Method | Path |
---|---|
POST | /v2/wallet/psbt/fund |
Code Samples
- gRPC
- REST
- Shell
- Javascript
- Python
const fs = require('fs');
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const GRPC_HOST = 'localhost:10009'
const MACAROON_PATH = 'LND_DIR/data/chain/bitcoin/regtest/admin.macaroon'
const TLS_PATH = 'LND_DIR/tls.cert'
const loaderOptions = {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
};
const packageDefinition = protoLoader.loadSync(['lightning.proto', 'walletrpc/walletkit.proto'], loaderOptions);
const walletrpc = grpc.loadPackageDefinition(packageDefinition).walletrpc;
process.env.GRPC_SSL_CIPHER_SUITES = 'HIGH+ECDSA';
const tlsCert = fs.readFileSync(TLS_PATH);
const sslCreds = grpc.credentials.createSsl(tlsCert);
const macaroon = fs.readFileSync(MACAROON_PATH).toString('hex');
const macaroonCreds = grpc.credentials.createFromMetadataGenerator(function(args, callback) {
let metadata = new grpc.Metadata();
metadata.add('macaroon', macaroon);
callback(null, metadata);
});
let creds = grpc.credentials.combineChannelCredentials(sslCreds, macaroonCreds);
let client = new walletrpc.WalletKit(GRPC_HOST, creds);
let request = {
psbt: <bytes>,
raw: <TxTemplate>,
coin_select: <PsbtCoinSelect>,
target_conf: <uint32>,
sat_per_vbyte: <uint64>,
sat_per_kw: <uint64>,
account: <string>,
min_confs: <int32>,
spend_unconfirmed: <bool>,
change_type: <ChangeAddressType>,
coin_selection_strategy: <CoinSelectionStrategy>,
max_fee_ratio: <double>,
};
client.fundPsbt(request, function(err, response) {
console.log(response);
});
// Console output:
// {
// "funded_psbt": <bytes>,
// "change_output_index": <int32>,
// "locked_utxos": <UtxoLease>,
// }
import codecs, grpc, os
# Generate the following 2 modules by compiling the walletrpc/walletkit.proto with the grpcio-tools.
# See https://github.com/lightningnetwork/lnd/blob/master/docs/grpc/python.md for instructions.
import walletkit_pb2 as walletrpc, walletkit_pb2_grpc as walletkitstub
GRPC_HOST = 'localhost:10009'
MACAROON_PATH = 'LND_DIR/data/chain/bitcoin/regtest/admin.macaroon'
TLS_PATH = 'LND_DIR/tls.cert'
# create macaroon credentials
macaroon = codecs.encode(open(MACAROON_PATH, 'rb').read(), 'hex')
def metadata_callback(context, callback):
callback([('macaroon', macaroon)], None)
auth_creds = grpc.metadata_call_credentials(metadata_callback)
# create SSL credentials
os.environ['GRPC_SSL_CIPHER_SUITES'] = 'HIGH+ECDSA'
cert = open(TLS_PATH, 'rb').read()
ssl_creds = grpc.ssl_channel_credentials(cert)
# combine macaroon and SSL credentials
combined_creds = grpc.composite_channel_credentials(ssl_creds, auth_creds)
# make the request
channel = grpc.secure_channel(GRPC_HOST, combined_creds)
stub = walletkitstub.WalletKitStub(channel)
request = walletrpc.FundPsbtRequest(
psbt=<bytes>,
raw=<TxTemplate>,
coin_select=<PsbtCoinSelect>,
target_conf=<uint32>,
sat_per_vbyte=<uint64>,
sat_per_kw=<uint64>,
account=<string>,
min_confs=<int32>,
spend_unconfirmed=<bool>,
change_type=<ChangeAddressType>,
coin_selection_strategy=<CoinSelectionStrategy>,
max_fee_ratio=<double>,
)
response = stub.FundPsbt(request)
print(response)
# {
# "funded_psbt": <bytes>,
# "change_output_index": <int32>,
# "locked_utxos": <UtxoLease>,
# }
- Javascript
- Python
const fs = require('fs');
const request = require('request');
const REST_HOST = 'localhost:8080'
const MACAROON_PATH = 'LND_DIR/data/chain/bitcoin/regtest/admin.macaroon'
let requestBody = {
psbt: <string>, // <bytes> (base64 encoded)
raw: <object>, // <TxTemplate>
coin_select: <object>, // <PsbtCoinSelect>
target_conf: <integer>, // <uint32>
sat_per_vbyte: <string>, // <uint64>
sat_per_kw: <string>, // <uint64>
account: <string>, // <string>
min_confs: <integer>, // <int32>
spend_unconfirmed: <boolean>, // <bool>
change_type: <string>, // <ChangeAddressType>
coin_selection_strategy: <string>, // <CoinSelectionStrategy>
max_fee_ratio: <number>, // <double>
};
let options = {
url: `https://${REST_HOST}/v2/wallet/psbt/fund`,
// Work-around for self-signed certificates.
rejectUnauthorized: false,
json: true,
headers: {
'Grpc-Metadata-macaroon': fs.readFileSync(MACAROON_PATH).toString('hex'),
},
form: JSON.stringify(requestBody),
}
request.post(options, function(error, response, body) {
console.log(body);
});
// Console output:
// {
// "funded_psbt": <string>, // <bytes>
// "change_output_index": <integer>, // <int32>
// "locked_utxos": <array>, // <UtxoLease>
// }
import base64, codecs, json, requests
REST_HOST = 'localhost:8080'
MACAROON_PATH = 'LND_DIR/data/chain/bitcoin/regtest/admin.macaroon'
TLS_PATH = 'LND_DIR/tls.cert'
url = f'https://{REST_HOST}/v2/wallet/psbt/fund'
macaroon = codecs.encode(open(MACAROON_PATH, 'rb').read(), 'hex')
headers = {'Grpc-Metadata-macaroon': macaroon}
data = {
'psbt': base64.b64encode(<bytes>),
'raw': <TxTemplate>,
'coin_select': <PsbtCoinSelect>,
'target_conf': <uint32>,
'sat_per_vbyte': <uint64>,
'sat_per_kw': <uint64>,
'account': <string>,
'min_confs': <int32>,
'spend_unconfirmed': <bool>,
'change_type': <ChangeAddressType>,
'coin_selection_strategy': <CoinSelectionStrategy>,
'max_fee_ratio': <double>,
}
r = requests.post(url, headers=headers, data=json.dumps(data), verify=TLS_PATH)
print(r.json())
# {
# "funded_psbt": <bytes>,
# "change_output_index": <int32>,
# "locked_utxos": <UtxoLease>,
# }
$ lncli wallet psbt fund --help
NAME:
lncli wallet psbt fund - Fund a Partially Signed Bitcoin Transaction (PSBT).
USAGE:
lncli wallet psbt fund [command options] [--template_psbt=T | [--outputs=O [--inputs=I]]] [--conf_target=C | --sat_per_vbyte=S | --sat_per_kw=K] [--change_type=A]
DESCRIPTION:
The fund command creates a fully populated PSBT that contains enough
inputs to fund the outputs specified in either the PSBT or the
--outputs flag.
If there are no inputs specified in the template (or --inputs flag),
coin selection is performed automatically. If inputs are specified, the
wallet assumes that full coin selection happened externally and it will
not add any additional inputs to the PSBT. If the specified inputs
aren't enough to fund the outputs with the given fee rate, an error is
returned.
After either selecting or verifying the inputs, all input UTXOs are
locked with an internal app ID.
The 'outputs' flag decodes addresses and the amount to send respectively
in the following JSON format:
--outputs='{"ExampleAddr": NumCoinsInSatoshis, "SecondAddr": Sats}'
The optional 'inputs' flag decodes a JSON list of UTXO outpoints as
returned by the listunspent command for example:
--inputs='["<txid1>:<output-index1>","<txid2>:<output-index2>",...]
The optional '--change-type' flag permits to choose the address type
for the change for default accounts and single imported public keys.
The custom address type can only be p2tr at the moment (p2wkh will be
used by default). No custom address type should be provided for custom
accounts as we will always generate the change address using the coin
selection key scope.
OPTIONS:
--template_psbt value the outputs to fund and optional inputs to spend provided in the base64 PSBT format
--outputs value a JSON compatible map of destination addresses to amounts to send, must not include a change address as that will be added automatically by the wallet
--inputs value an optional JSON compatible list of UTXO outpoints to use as the PSBT's inputs
--conf_target value the number of blocks that the transaction should be confirmed on-chain within (default: 6)
--sat_per_vbyte value a manual fee expressed in sat/vbyte that should be used when creating the transaction (default: 0)
--sat_per_kw value a manual fee expressed in sat/kw that should be used when creating the transaction (default: 0)
--account value (optional) the name of the account to use to create/fund the PSBT
--change_type value (optional) the type of the change address to use to create/fund the PSBT. If no address type is provided, p2wpkh will be used for default accounts and single imported public keys. No custom address type should be provided for custom accounts as we will always use the coin selection key scope to generate the change address
--min_confs value (optional) the minimum number of confirmations each input used for the PSBT transaction must satisfy (default: 1)
--coin_selection_strategy value (optional) the strategy to use for selecting coins. Possible values are 'largest', 'random', or 'global-config'. If either 'largest' or 'random' is specified, it will override the globally configured strategy in lnd.conf (default: "global-config")
--max_fee_ratio value the maximum fee to total output amount ratio that this psbt should adhere to (default: 0.2)
Messages
walletrpc.FundPsbtRequest
Source: walletrpc/walletkit.proto
Field | gRPC Type | REST Type | REST Placement |
---|---|---|---|
psbt | bytes | string | body |
raw | TxTemplate | object | body |
coin_select | PsbtCoinSelect | object | body |
target_conf | uint32 | integer | body |
sat_per_vbyte | uint64 | string | body |
sat_per_kw | uint64 | string | body |
account | string | string | body |
min_confs | int32 | integer | body |
spend_unconfirmed | bool | boolean | body |
change_type | ChangeAddressType | string | body |
coin_selection_strategy | CoinSelectionStrategy | string | body |
max_fee_ratio | double | number | body |
walletrpc.FundPsbtResponse
Source: walletrpc/walletkit.proto
Field | gRPC Type | REST Type |
---|---|---|
funded_psbt | bytes | string |
change_output_index | int32 | integer |
locked_utxos | UtxoLease[] | array |
Nested Messages
lnrpc.OutPoint
Field | gRPC Type | REST Type |
---|---|---|
txid_bytes | bytes | string |
txid_str | string | string |
output_index | uint32 | integer |
walletrpc.PsbtCoinSelect
Field | gRPC Type | REST Type |
---|---|---|
psbt | bytes | string |
existing_output_index | int32 | integer |
add | bool | boolean |
walletrpc.TxTemplate
Field | gRPC Type | REST Type |
---|---|---|
inputs | OutPoint[] | array |
outputs | OutputsEntry[] | object |
walletrpc.TxTemplate.OutputsEntry
Field | gRPC Type | REST Type |
---|---|---|
key | string | unknown |
value | uint64 | unknown |
walletrpc.UtxoLease
Field | gRPC Type | REST Type |
---|---|---|
id | bytes | string |
outpoint | OutPoint | object |
expiration | uint64 | string |
pk_script | bytes | string |
value | uint64 | string |
Enums
lnrpc.CoinSelectionStrategy
Name | Number |
---|---|
STRATEGY_USE_GLOBAL_CONFIG | 0 |
STRATEGY_LARGEST | 1 |
STRATEGY_RANDOM | 2 |
walletrpc.ChangeAddressType
Name | Number |
---|---|
CHANGE_ADDRESS_TYPE_UNSPECIFIED | 0 |
CHANGE_ADDRESS_TYPE_P2TR | 1 |