> ## Documentation Index
> Fetch the complete documentation index at: https://docs.portalhq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# buildBitcoinP2wpkhTransaction

> Builds an unsigned Bitcoin P2WPKH transaction (a PSBT) for native BTC transfers via the Portal backend.

## Function Signature

```dart theme={null}
Future<PortalBuildBitcoinP2wpkhTransactionResponse> buildBitcoinP2wpkhTransaction({
  required String chainId,
  required String to,
  required String token,
  required String amount,
})
```

## Description

Builds an unsigned Bitcoin P2WPKH transaction via the Portal backend. The returned `transaction`
is a Partially Signed Bitcoin Transaction (PSBT) as hex, together with the list of signature
hashes — one per input — that must be signed before the transaction can be broadcast.

This is the first step of a three-step flow:

1. `buildBitcoinP2wpkhTransaction` — build the PSBT.
2. [`rawSign`](./rawsign) — sign **every** entry of `transaction.signatureHashes`.
3. [`broadcastBitcoinP2wpkhTransaction`](./broadcastbitcoinp2wpkhtransaction) — submit the signed transaction.

If you do not need control over the individual steps, [`sendAsset`](./sendasset) performs all
three for you and returns the transaction hash directly.

## Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `chainId` | `String` | Yes | The chain ID in CAIP-2 format. Must be one of the two supported P2WPKH references (see below). |
| `to` | `String` | Yes | The recipient's Bitcoin address. |
| `token` | `String` | Yes | Must be `"NATIVE"` — only native BTC transfers are supported. |
| `amount` | `String` | Yes | The human-readable amount in BTC (e.g. `"0.0001"`). The backend converts this to satoshis. |

### Supported `chainId` values

| Network | Chain ID |
| - | - |
| Bitcoin Mainnet | `bip122:000000000019d6689c085ae165831e93-p2wpkh` |
| Bitcoin Testnet | `bip122:000000000933ea01ad0ee984209779ba-p2wpkh` |

Any other `bip122:` reference is rejected. See
[Chain ID formatting](../../../resources/chain-id-formatting) for the full CAIP-2 reference.

## Returns

**`PortalBuildBitcoinP2wpkhTransactionResponse`** — the unsigned PSBT plus metadata.

| Property | Type | Description |
| - | - | - |
| `transaction` | `PortalBitcoinP2wpkhTransaction` | The PSBT and the hashes that need signing. |
| `metadata` | `PortalBuildTransactionMetadata` | Token info, formatted amounts, and the resolved sender / recipient addresses. |
| `error` | `String?` | Optional backend error message. Normally `null` — failures are thrown (see below). |

### PortalBitcoinP2wpkhTransaction

| Property | Type | Description |
| - | - | - |
| `rawTxHex` | `String` | The Partially Signed Bitcoin Transaction (PSBT) as hex. Pass this through to the broadcast call unchanged. |
| `signatureHashes` | `List<String>` | The hashes to be signed — one per transaction input. Sign every one, in order. |

See [`buildEip155Transaction`](./buildeip155transaction#portalbuildtransactionmetadata) for the
`PortalBuildTransactionMetadata` shape. For Bitcoin, `tokenDecimals` is always `8` and
`tokenSymbol` is always `"BTC"`.

## Examples

### Build a testnet BTC transfer

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

final portal = Portal();

final response = await portal.buildBitcoinP2wpkhTransaction(
  chainId: 'bip122:000000000933ea01ad0ee984209779ba-p2wpkh', // Bitcoin Testnet
  to: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx',
  token: 'NATIVE',
  amount: '0.0001',
);

if (response.error != null) {
  throw Exception('Failed to build transaction: ${response.error}');
}

print('Amount: ${response.metadata.amount} ${response.metadata.tokenSymbol ?? 'BTC'}');
print('From: ${response.metadata.fromAddress}');
print('To: ${response.metadata.toAddress}');
print('PSBT: ${response.transaction.rawTxHex}');
print('Hashes to sign: ${response.transaction.signatureHashes.length}');
```

### Build, then sign and broadcast

```dart theme={null}
const chainId = 'bip122:000000000933ea01ad0ee984209779ba-p2wpkh';

final built = await portal.buildBitcoinP2wpkhTransaction(
  chainId: chainId,
  to: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx',
  token: 'NATIVE',
  amount: '0.0001',
);

if (built.error != null) {
  throw Exception(built.error);
}

// Sign every signature hash, in order.
final signatures = <String>[];
for (final hash in built.transaction.signatureHashes) {
  signatures.add(await portal.rawSign(chainId: chainId, message: hash));
}

final broadcast = await portal.broadcastBitcoinP2wpkhTransaction(
  chainId: chainId,
  signatures: signatures,
  rawTxHex: built.transaction.rawTxHex,
);

print('Transaction hash: ${broadcast.txHash}');
```

## Errors

Throws a `PortalException` on failure:

| Code | Description |
| - | - |
| `NOT_INITIALIZED` | Portal was not initialized. |
| `BUILD_BITCOIN_P2WPKH_TRANSACTION_ERROR` | The backend could not build the transaction (insufficient balance, invalid recipient, unsupported chain ID). |

```dart theme={null}
try {
  final response = await portal.buildBitcoinP2wpkhTransaction(
    chainId: 'bip122:000000000933ea01ad0ee984209779ba-p2wpkh',
    to: recipient,
    token: 'NATIVE',
    amount: '0.0001',
  );

  if (response.error != null) {
    throw Exception(response.error);
  }
} on PortalException catch (e) {
  print('Build failed: ${e.code} - ${e.message}');
}
```

<Note>
  **Backend failures are thrown on both platforms.** On iOS and Android alike, a failed build
  (insufficient balance, invalid recipient, unsupported chain ID) is thrown as a `PortalException`
  with code `BUILD_BITCOIN_P2WPKH_TRANSACTION_ERROR`, so `response.error` is normally `null`.
  Checking `response.error` is optional and only a defensive measure.
</Note>

## Implementation Notes

* `amount` is the **human-readable** value. Pass `"0.0001"` for 0.0001 BTC, not `"10000"` satoshis.
* `token` must be `"NATIVE"`. Bitcoin has no token contracts, so there is no other valid value.
* Only P2WPKH (native SegWit) is supported. There is no P2TR/taproot, P2SH, or legacy P2PKH support.
* Sign **all** of `signatureHashes`, in the order returned — there is one per transaction input,
  and the broadcast call expects the signatures in matching order.
* Bitcoin chains need no entry in `rpcConfig`. Raw signing does not use an RPC provider.
* Bitcoin uses the SECP256K1 curve, the same key share as EVM chains.

## Related

* [broadcastBitcoinP2wpkhTransaction](./broadcastbitcoinp2wpkhtransaction)
* [rawSign](./rawsign)
* [sendAsset](./sendasset)
* [getClient](./getclient) — where the wallet's Bitcoin addresses live
* [buildEip155Transaction](./buildeip155transaction)
* [buildSolanaTransaction](./buildsolanatransaction)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.