@asigna/btc-connect v0.0.8
Asigna Bitcoin Connect SDK
The Asigna extension enables connecting user multisig wallets to your Bitcoin applications. This SDK provides the full logic required to connect a user's multisig wallet, build, sign, and execute a Partially Signed Bitcoin Transaction (PSBT), as well as sign messages and verify signatures. Within the Asigna SDK, the BitcoinJS Lib package is used for PSBTs.
Installation
To install the Bitcoin Connect SDK, run:
npm install @asigna/btc-connect
Workflow
1. Connect Asigna Extension with the Asigna Web Interface
To begin, download the Asigna extension and connect it to the Asigna web interface at https://btc.asigna.io.
If you don’t already have a multisig wallet, create one to connect it to your Bitcoin application on your desired network.
2. Connect Bitcoin Application to the Asigna Extension
To connect your application (similar to other Bitcoin wallets), use the useAsignaExtension
hook:
connect()
- opens the connection modal:
const { connect } = useAsignaExtension();
const asignaAuthRequest = async () => {
const info = await connect();
// Save account data to storage
localStorage.setItem('accountData', info);
console.log(info);
};
disconnect()
- disconnects the extension from your application:
const { disconnect } = useAsignaExtension();
const disconnectRequest = () => {
disconnect();
// Clear connected account data
localStorage.setItem('accountData', undefined);
console.log(info);
};
3. Retrieve Multisig Information
useAsignaAddress
- returns the connected multisig address:
const { asignaAddress } = useAsignaAddress();
useAsignaSafeInfo
- returns connected multisig data:
{
address: string,
asignaToken: string, // Token used for API requests within the package
threshold: number,
multisigType: 'WSH' | 'TAPROOT',
users: [
{
address: string;
publicKey?: string;
xPub?: string;
fingerPrint?: string;
derivation?: string;
}
],
psbtInputConstructionFields: {
witnessUtxo: {
script: Buffer;
},
tapLeafScript: [
{
leafVersion: number;
script: Buffer;
controlBlock: Buffer;
}
],
witnessScript: {
script: Buffer,
}
}
}
Example:
const { asignaSafeInfo } = useAsignaSafeInfo();
console.log('Connected:', asignaSafeInfo);
4. UI Modals for Signing PSBT or Messages
To display UI modals for signing a PSBT or message and wait for signatures, use the <AsignaSignActionModals />
component:
// openSignMessage and openSignPsbt calls are async
// You can await them directly in your async functions
// e.g., await openSignMessage(message);
function App() {
return (
<div>
<AsignaSignActionModals
onSignPsbt={(tx) => {
console.log('PSBT Signature:', tx);
}}
onSignMessage={(signature) => {
console.log('Message Signature:', signature);
if (!asignaSafeInfo) return;
console.log(
'Is valid:', validateMessage(
message,
signature,
asignaSafeInfo.multisigType,
asignaSafeInfo.address,
asignaSafeInfo.users.map((x) => x.publicKey || ''),
asignaSafeInfo.users.map((x) => x.address),
asignaSafeInfo.threshold,
bitcoin.networks.testnet
)
);
}}
/>
</div>
);
}
5. Sign Messages
Multisig wallets don't have a single private key to sign messages. Therefore, multiple owners must sign, and their signatures are merged for verification.
openSignMessage(message: string)
- opens the modal to sign a message:
const { openSignMessage } = useAsignaExtension();
<div onClick={async () => await openSignMessage(message)}>
Open Sign Message Modal
</div>
signMessage(message: string)
- requests a message signature without waiting for the modal:
const { signMessage } = useAsignaExtension();
<div onClick={async () => await signMessage(message)}>
Sign message without waiting for merged signature
</div>
validateMessage
- validates the message signature using the full multisig construction information:
import { validateMessage, useAsignaSafeInfo } from '@asigna/core;
const { asignaSafeInfo } = useAsignaSafeInfo();
const message = 'test message';
const signature = await signMessage(message);
const isValid = validateMessage(
message,
signature,
asignaSafeInfo.multisigType,
asignaSafeInfo.address,
asignaSafeInfo.users.map((x) => x.publicKey || ''),
asignaSafeInfo.users.map((x) => x.address),
asignaSafeInfo.threshold,
bitcoin.networks.testnet
);
6. Create Transactions
To create a multisig PSBT, it's crucial to correctly add inputs and calculate fees:
useAsignaFee
- calculates gas:
const { calculateGas } = useAsignaFee();
useAsignaInputs
- maps UTXOs to PSBT inputs based on the multisig type:
const { createInput } = useAsignaInputs();
Example:
const { createInput } = useAsignaInputs();
const { calculateGas } = useAsignaFee();
const { asignaAddress } = useAsignaAddress();
function createTx() {
if (!utxos || !asignaAddress) return;
const psbt = new bitcoin.Psbt({ network: bitcoin.networks.testnet });
let totalOutput = 0;
utxos.forEach((utxo) => {
const input = createInput(utxo);
if (!input) return;
psbt.addInput(input.data);
totalOutput += input.utxo.satoshis;
});
const FEE_RATE = 5;
const fee = (calculateGas(psbt) || 0) * FEE_RATE;
const amountToSend = BigNumber(amount).shiftedBy(8);
psbt.addOutput({ value: amountToSend.toNumber(), address });
psbt.addOutput({
value: totalOutput - amountToSend.toNumber() - fee,
address: asignaAddress
});
openSignPsbt(psbt);
}
7. Sign PSBT
A PSBT requires multiple owner signatures, which may take time. There are two signing options:
signPsbt(psbt: bitcoin.Psbt)
- creates a transaction for signature gathering in the multisig queue (does not return the signed PSBT):
const { signPsbt } = useAsignaExtension();
openSignPsbt(psbt: bitcoin.Psbt, execute?: boolean)
- opens a waiting modal until enough signatures are gathered and the signed PSBT is returned to the app. Execute flag can send the transaction to mempool once there's enough signatures gathered and returns thetxId
back to the application.
const { openSignPsbt } = useAsignaExtension();
This method is recommended when the application needs to retrieve the signed PSBT, otherwise better to use the signPsbt
approach.