Skip to main content

XCreateAccount

XCreateAccount is an experimental API that creates a new named account within the wallet, deriving the account's keys from the wallet's master key.

In contrast to ImportAccount, which registers a watch-only account from an externally supplied extended public key, the account created here is fully owned by the wallet: it derives its own addresses and can sign for its own outputs. That makes it usable as an isolated pocket of funds inside a single wallet, because coin selection, change, balance and address derivation can all be scoped to it by name.

NOTE: The X prefix marks this API as experimental: it may change or be removed without the usual deprecation period. It additionally requires i_know_what_i_am_doing on release builds, because a seed-only restore does not rediscover the funds an account created here holds; see the recovery note below. That second gate comes off once recovery handles these accounts, at which point the X can be dropped too.

NOTE: The wallet must be unlocked, as deriving the account key requires access to the master private key.

NOTE: The call is not idempotent, and the account is created before the response is sent. A client that cancels or times out may still have had the account created, in which case its retry fails with "already exists" — indistinguishable from a genuine name clash. Check ListAccounts before retrying.

NOTE: The account's address type is permanent and also fixes the type of its change outputs. lnd resolves a custom account name within the key scope implied by the requested address type, so every later call must ask for the address type that maps to the same scope or the account will appear not to exist. NextAddr and NewAddress take lnrpc.AddressType, which has no HYBRID_NESTED_WITNESS_PUBKEY_HASH member: an account created as HYBRID_NESTED_WITNESS_PUBKEY_HASH must be addressed with NESTED_PUBKEY_HASH, which maps to the same BIP-0049Plus scope. TAPROOT_PUBKEY and WITNESS_PUBKEY_HASH map across unchanged.

NOTE: Funds held in an account created here are not rediscovered by a seed-only recovery, because lnd's recovery scan only rederives addresses for the wallet's default account (btcwallet's RecoveryManager hardcodes waddrmgr.DefaultAccountNum). They are still recoverable, but only by reconstructing the account first, and the account name is not what has to be reproduced: accounts are derived from an index that btcwallet assigns sequentially per key scope, shared with accounts created by ImportAccount.

To keep an account recoverable, record its key scope, the account index (the account's derivation_path in the response), and how many addresses it has issued. To restore: re-create every account in that key scope in their original order so the index counter lands on the same value, re-derive at least as many addresses as were previously issued with NextAddr — a rescan only searches for addresses already present in the wallet database, and a freshly created account has none — and only then rescan with --reset-wallet-transactions.

Source: walletrpc/walletkit.proto

gRPC

rpc XCreateAccount (XCreateAccountRequest) returns (XCreateAccountResponse);

REST

HTTP MethodPath
POST /v2/wallet/accounts/create

Code Samples

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 = {
name: <string>,
address_type: <AddressType>,
i_know_what_i_am_doing: <bool>,
};
client.xCreateAccount(request, function(err, response) {
console.log(response);
});
// Console output:
// {
// "account": <Account>,
// }

Messages

walletrpc.XCreateAccountRequest

Source: walletrpc/walletkit.proto

FieldgRPC TypeREST TypeREST Placement
name
stringstringbody
address_type
AddressTypestringbody
i_know_what_i_am_doing
boolbooleanbody

walletrpc.XCreateAccountResponse

Source: walletrpc/walletkit.proto

FieldgRPC TypeREST Type
account
Accountobject

Nested Messages

walletrpc.Account

FieldgRPC TypeREST Type
name
stringstring
address_type
AddressTypestring
extended_public_key
stringstring
master_key_fingerprint
bytesstring
derivation_path
stringstring
external_key_count
uint32integer
internal_key_count
uint32integer
watch_only
boolboolean

Enums

walletrpc.AddressType

NameNumber
UNKNOWN
0
WITNESS_PUBKEY_HASH
1
NESTED_WITNESS_PUBKEY_HASH
2
HYBRID_NESTED_WITNESS_PUBKEY_HASH
3
TAPROOT_PUBKEY
4