Investor and venue quickstart
The guide explains the shortest path from LWK initialization to wallet registration and PSET cosigning. The venue creates an AMP2-connected wallet for asset receipt and transfers.
This guide covers the shortest path from LWK initialization to a registered wallet and a cosigned PSET. By the end, you will have an AMP2 connected wallet ready to receive and transfer assets.
Prerequisites
- Rust toolchain (1.70+)
- LWK dependencies (
lwk_wollet,lwk_signer,lwk_common) - AMP2 Rust client crate or HTTP access to an AMP2 server
- A keyorigin xpub (your own extended public key with derivation metadata)
- AMP2 testnet URL:
https://amp.enterprise.blockstream.com/
1. Create a signer
Create an LWK software signer from a BIP 39 mnemonic. This gives you the key material needed to build descriptors and sign PSETs:
use lwk_signer::SwSigner;
use lwk_common::Bip;
let signer = SwSigner::new("your mnemonic words here", false).unwrap();
let my_xpub = signer.keyorigin_xpub(&Bip::new_bip84()).unwrap();The false parameter indicates testnet. Set to true for mainnet.
2. Initialize the AMP2 client
The Amp2 client needs the AMP2 server's keyorigin xpub and the AMP2 server URL. The server xpub is provided in the invite data you receive during onboarding (see Environments).
let amp_xpub = "[b805d768/87h/1h/0h]tpubDCYEgnLyCH2okSittQNNB8JHLwPgmoEAoKcMrJDHP9dFVamsadPAFJQ77C1htgR8ksie3VksLXoryng9AUaPZSF8FwTwEv6CaHp8j2YCrds";
let amp_url = "https://amp.enterprise.blockstream.com/";
let amp2 = Amp2::new(amp_xpub.to_string(), amp_url.to_string()).unwrap();3. Build a wallet descriptor
AMP2 wallets use confidential descriptors that define a 2-of-2 multisig between your key and AMP2's server key. The descriptor format looks like this:
ct(elip151,elwsh(multi(2,
[<your_fingerprint>/<path>]<your_xpub>/<0;1>/*,
[<amp_fingerprint>/<path>]<amp_xpub>/<0;1>/*
)))You can construct this from a string or build it programmatically:
let descriptor_str = "ct(elip151,elwsh(multi(2,[b805d768/87h/1h/0h]tpubDCYEgnLyCH2okSittQNNB8JHLwPgmoEAoKcMrJDHP9dFVamsadPAFJQ77C1htgR8ksie3VksLXoryng9AUaPZSF8FwTwEv6CaHp8j2YCrds/<0;1>/*,[c67f5991/87'/1'/0']tpubDC4SUtWGWcMQPtwjgQQ4DYnFmAYhiKxw3f3KKCvMGT9sojZNvHsQ4rVW6nQeCPtk4rLAxGKeuAzMmBmH92X3HDgLho3nRWpvuJrpCmYgeQj/<0;1>/*)))";
let descriptor = ConfidentialDescriptor::from_str(descriptor_str).unwrap();The ct(elip151) wrapper provides Confidential Transaction blinding using ELIP-151 key derivation. The elwsh(multi(2,...)) defines a 2-of-2 Elements witness script hash multisig.
4. Register the wallet
Submit the descriptor to AMP2. The server derives a wallet ID (wid) deterministically from the descriptor:
let registration = amp2.register(descriptor).await.unwrap();
assert!(!registration.wid.is_empty());
println!("Registered wallet: wid={}", registration.wid);Idempotency: Registering the same descriptor with the same blinding keys returns the existing wid without error. Registering with different blinding keys for an already known descriptor returns a 403 error.
The wid is how AMP2 identifies your wallet for all subsequent operations: transfers, balance queries, and restriction lookups.
5. Build and cosign a PSET
To transfer assets, build a PSET locally using TxBuilder, then request AMP2 cosigning:
// Build the transaction
let mut builder = TxBuilder::new(amp2.elements_network());
builder.add_recipient(recipient_address, satoshi, asset_id).unwrap();
let pset = builder.finish(&_wallet.inner);
// Request AMP2 cosigning
let response = amp2.cosign(pset).await.unwrap();
let cosigned_pset = response.pset;AMP2 validates the transaction against restriction policies before cosigning. If the transfer violates a policy (e.g., destination not whitelisted), cosigning is refused.
6. Sign and broadcast
After AMP2 cosigns, sign with your own signer and broadcast:
// Sign locally
signer.sign(&mut cosigned_pset).unwrap();
// Broadcast to Liquid
// Use your standard Liquid broadcast flow (e.g., via Esplora or Elements RPC)7. Verify it worked
Check your wallet balance using LWK and Esplora. Create a Wollet from the descriptor and sync it with the network:
use lwk_wollet::{Wollet, ElementsNetwork, ElectrumClient, ElectrumUrl};
let wollet = Wollet::new(ElementsNetwork::LiquidTestnet, &descriptor_str).unwrap();
let electrum_url = ElectrumUrl::new("blockstream.info:465", true, true);
let mut client = ElectrumClient::new(&electrum_url).unwrap();
let update = client.full_scan(&wollet).unwrap();
if let Some(update) = update {
wollet.apply_update(update).unwrap();
}
let balance = wollet.balance();
println!("Wallet balance: {:?}", balance);You can also check your balance on Esplora by looking up the wallet's receive addresses.
What just happened
- You created a signer and derived your keyorigin xpub.
- You initialized an AMP2 client with the server's xpub.
- You built a confidential 2-of-2 multisig descriptor.
- You registered the descriptor and received a wallet ID.
- You built a PSET locally and requested AMP2 cosigning.
- You signed with your own keys and broadcast to Liquid.
Next steps
- LWK Setup: detailed LWK configuration
- Wallet Registration: descriptor details and edge cases
- Signing PSETs: complete transaction flow
- Proxy Integration: exposing plain JSON to wallet clients
Issuer quickstart
The guide explains the shortest path from invite data to first AMP2 wallet registration for an issuer. The issuer creates an authenticated SDK session and receives a WID.
Assets and asset types
Every AMP2 asset uses a Liquid issued asset as a native token on the Liquid Network. AMP2 adds tracking, lifecycle management, and policy enforcement.