Sending assets
Asset send operations move tokens from one wallet to one or more recipient wallets. The page explains request setup, PSET handling, restrictions, fees, and verification.
Sending moves existing tokens from one wallet to one or more recipient wallets. It follows the same PSET lifecycle as issuance: request PSET -> sign -> cosign -> broadcast.
Prerequisites
- The source wallet holds the asset being sent.
- The source wallet has LBTC for transaction fees.
- Recipient wallets are registered in AMP2 (see Wallet Registration).
- If restrictions are set, all recipient WIDs must be in the source wallet's
toWhitelist.
Build the send request
import {
AmpSendAssetRequestSchema,
AmpActions,
} from '@blockstream/amp-registry';
const sendReq = AmpSendAssetRequestSchema.parse({
action: 'add',
resource: `/amp/assets/${assetId}/send`,
details: {
wid: sourceWid,
recipients: [
{ satoshi: 50_000, wid: investorWid1 },
{ satoshi: 25_000, wid: investorWid2 },
],
},
});Multi recipient support
A single send request can include multiple recipients. AMP2 constructs a single transaction with one output per recipient, which is more fee efficient than separate transfers.
Request the PSET
const sendResponse = await broadcastRequest<
typeof AmpActions.send
>(sendReq, broadcaster);
if (sendResponse.status !== 'success') {
throw new Error(
`Send PSET request failed: ec=${sendResponse.error_code} message=${sendResponse.message}`
);
}
const pset = sendResponse.details.pset;Sign, cosign, and broadcast
The completion flow is identical to issuance:
import {
AmpSignPsetRequestSchema,
AmpBroadcastRequestSchema,
} from '@blockstream/amp-registry';
// 1. Sign locally with LWK
const signedPset = signWithLwk(pset);
// 2. Request AMP2 cosigning
const cosignResponse = await broadcastRequest<
typeof AmpActions.sign
>(
AmpSignPsetRequestSchema.parse({
action: 'add',
resource: '/amp/wallets/sign',
details: { pset: signedPset.toString() },
}),
broadcaster,
);
if (cosignResponse.status !== 'success') {
throw new Error(`Cosign failed: ${cosignResponse.message}`);
}
// 3. Broadcast
const broadcastResponse = await broadcastRequest<
typeof AmpActions.broadcast
>(
AmpBroadcastRequestSchema.parse({
action: 'add',
resource: '/amp/wallets/broadcast',
details: { pset: cosignResponse.details.pset },
}),
broadcaster,
);
if (broadcastResponse.status !== 'success') {
throw new Error(`Broadcast failed: ${broadcastResponse.message}`);
}
const txid = broadcastResponse.details.txid;Restriction enforcement
When useToWhitelist is enabled on the source wallet's restriction group:
- AMP2 constructs the PSET with the requested recipients.
- At cosigning, the HSM checks every output address against the restriction group's
toWhitelist. - If any recipient WID is not in the whitelist, the HSM refuses to cosign and returns an error.
This enforcement is at the cryptographic level. There is no override.
LBTC fees
The source wallet pays the Liquid transaction fee in LBTC. If the wallet does not have enough LBTC, the PSET construction fails. Ensure wallets are funded before sending.
Verify the transfer
After broadcast, confirm the transfer:
const txsResponse = await broadcastRequest<
typeof AmpActions.listAssetTxs
>(
{
action: 'get',
resource: `/amp/assets/${assetId}/txs`,
details: {},
},
broadcaster,
);Next steps
- Managing Restrictions: configure who can receive transfers
- Reissue and Burn: supply management operations
- Monitoring: track transaction state
Asset issuance
Asset issuance creates a new Liquid asset from a funded wallet. The page explains issuance request setup, PSET handling, cosigning, broadcast, and verification.
Restriction management
Restrictions control asset destinations and burn permissions. The page explains restriction concepts, queries, updates, enforcement, and operational guidance.