@bigmi/core
Version:
TypeScript library for Bitcoin apps.
474 lines (368 loc) • 12.5 kB
Markdown
<div align="center">
<h1 align="center">Bigmi</h1>
<p align="center"><strong>TypeScript library and reactive primitives for Bitcoin apps.</strong></p>
[](/LICENSE.md)
[](https://www.npmjs.com/package/@bigmi/core)
[](https://www.npmjs.com/package/@bigmi/core)
</div>
**Bigmi** (short for *Bitcoin Is Gonna Make It*) is a TypeScript library that provides reactive primitives for building Bitcoin applications. Bigmi simplifies Bitcoin app development by offering:
- Abstractions over the [Bitcoin JSON-RPC API](https://developer.bitcoin.org/reference/rpc/)
- First-class APIs for interacting with the [Bitcoin](https://bitcoin.design/) network, including sending transactions and tracking with [Replace-By-Fee (RBF)](https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki) support
- Connectors for popular Bitcoin wallet extensions
- TypeScript support
Whether you're building a Node.js application or a client-side app, Bigmi provides the tools you need to interact with the Bitcoin.
### Packages
Bigmi is modularized into several packages, each suited to different use cases:
- [/core](https://www.npmjs.com/package/@bigmi/core) - Actions, transports, utilities, and other core primitives for Node.js or client-side applications.
- [/react](https://www.npmjs.com/package/@bigmi/react) - Hooks, providers, and other useful primitives for React applications.
- [/client](https://www.npmjs.com/package/@bigmi/client) - Wallet connectors and other tools to connect wallet extensions with Bitcoin applications.
## Installation
```sh
pnpm add /react
```
```sh
pnpm add /core
```
```sh
pnpm add /client
```
## Getting Started
### Node.js
How to setup Bigmi on the backend with Node.js:
```typescript
// main.ts
import {
createClient,
bitcoin,
blockchair,
sendUTXOTransaction,
waitForTransaction,
getBalance,
} from '/core'
// Create a client for Bitcoin mainnet
const publicClient = createClient({
chain: bitcoin,
rpcSchema: rpcSchema<UTXOSchema>(),
transport: fallback([
blockchair(),
ankr({apiKey: 'YOUR_ANKR_API_KEY'}),
blockcypher(),
mempool(),
]),
})
// Define the Bitcoin address you're working with
const address = 'BITCOIN_ADDRESS';
// Fetch the balance of the address
const balance = await getBalance(publicClient, { address });
console.log(`Balance for ${address}:`, balance);
// Fetch the current block count (height)
const blockCount = await getBlockCount(publicClient);
console.log('Current block count:', blockCount);
// Prepare the transaction hex (as a string)
const txHex = 'TRANSACTION_HEX';
// Send the transaction to the network
const txId = await sendUTXOTransaction(publicClient, { hex: txHex });
console.log('Transaction sent with ID:', txId);
// Wait for the transaction to be confirmed
const transaction = await waitForTransaction(publicClient, {
txId,
txHex,
senderAddress: address,
onReplaced: (response) => {
console.log('Transaction replaced due to:', response.reason);
},
});
console.log('Transaction confirmed:', transaction);
```
### React
Simple bigmi setup with a react app:
```typescript
// App.tsx
import { bitcoin, http, createClient } from '/core'
import { BigmiProvider, createConfig } from '/react'
import { binance, xverse, phantom } from '/client'
const chainId = bitcoin.id
// Create bigmi config object
const config = createConfig({
chains: [bitcoin],
connectors: [binance({chainId}), xverse({chainId}), phantom({chainId})],
client: ({ chain }) => createClient({ chain, transport: http() }),
ssr: true // If using Next.js or SSR
})
function App() {
return (
// Wrap your application with the necessary providers:
<BigmiProvider config={config}>
<YourApp />
</BigmiProvider>
)
}
```
```typescript
// YourApp.tsx
// Import the hooks from bigmi/react library
import { useAccount, useBalance, useConnect } from '/react'
const { address, isConnected } = useAccount()
const { balance } = useBalance()
const { connect } = useConnect()
function YourApp() {
return (
<div>
{isConnected ? (
<p>Connected: {address}: {balance}BTC</p>
) : (
<button onClick={connect}>Connect Wallet</button>
)}
</div>
)
}
```
## Creating Bitcoin Transactions
While Bigmi excels at blockchain data retrieval and transaction broadcasting, it doesn't include transaction creation functions. For generating valid transaction hex, we recommend using **bitcoinjs-lib** (which Bigmi already depends on).
### Basic Transaction Creation
Here's how to create a Bitcoin transaction using bitcoinjs-lib with Bigmi:
```typescript
import * as bitcoin from 'bitcoinjs-lib';
import {
createClient,
getUTXOs,
sendUTXOTransaction,
waitForTransaction,
getBlockStats,
getBlockCount
} from '/core';
async function createAndSendTransaction(
client: Client,
fromAddress: string,
toAddress: string,
amount: number, // in satoshis
privateKey: Buffer
) {
// 1. Get UTXOs for the address using Bigmi
const utxos = await getUTXOs(client, { address: fromAddress });
// 2. Create a new transaction using bitcoinjs-lib
const psbt = new bitcoin.Psbt();
// 3. Add inputs from UTXOs
let inputValue = 0;
const estimatedFee = 1000; // You should calculate this properly
for (const utxo of utxos) {
psbt.addInput({
hash: utxo.txId,
index: utxo.vout,
witnessUtxo: {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
},
});
inputValue += utxo.value;
if (inputValue >= amount + estimatedFee) break;
}
// 4. Add recipient output
psbt.addOutput({
address: toAddress,
value: amount,
});
// 5. Add change output if needed
const change = inputValue - amount - estimatedFee;
if (change > 546) { // Dust threshold
psbt.addOutput({
address: fromAddress,
value: change,
});
}
// 6. Sign the transaction
const keyPair = bitcoin.ECPair.fromPrivateKey(privateKey);
psbt.signAllInputs(keyPair);
psbt.finalizeAllInputs();
// 7. Get the raw transaction hex
const rawTx = psbt.extractTransaction().toHex();
// 8. Broadcast using Bigmi
const txId = await sendUTXOTransaction(client, { hex: rawTx });
// 9. Wait for confirmation using Bigmi
const confirmedTx = await waitForTransaction(client, {
txId,
txHex: rawTx,
senderAddress: fromAddress,
confirmations: 1,
});
return confirmedTx;
}
```
### Fee Estimation
Proper fee estimation is crucial for transaction confirmation:
```typescript
async function estimateFee(
client: Client,
numInputs: number,
numOutputs: number
): Promise<number> {
// Get recent block stats for fee estimation
const blockHeight = await getBlockCount(client);
const blockStats = await getBlockStats(client, {
blockNumber: blockHeight,
stats: ['avgfeerate']
});
// Estimate transaction size
// P2WPKH: ~68 bytes per input, ~31 bytes per output, ~10 bytes overhead
const estimatedSize = (numInputs * 68) + (numOutputs * 31) + 10;
// Calculate fee (satoshis per byte * size)
const feeRate = blockStats.avgfeerate || 1; // fallback to 1 sat/byte
return Math.ceil(feeRate * estimatedSize);
}
```
### Complete Example with Error Handling
```typescript
import * as bitcoin from 'bitcoinjs-lib';
import { createClient, getBalance, getUTXOs, sendUTXOTransaction, waitForTransaction } from '/core';
async function safeSendBitcoin(
client: Client,
fromAddress: string,
toAddress: string,
amount: number,
privateKey: Buffer
) {
try {
// Check balance
const balance = await getBalance(client, { address: fromAddress });
if (balance < amount + 1000) {
throw new Error('Insufficient balance');
}
// Get UTXOs
const utxos = await getUTXOs(client, {
address: fromAddress,
minValue: amount + 1000,
});
if (utxos.length === 0) {
throw new Error('No UTXOs available');
}
// Create transaction
const psbt = new bitcoin.Psbt();
let totalInput = 0;
for (const utxo of utxos) {
psbt.addInput({
hash: utxo.txId,
index: utxo.vout,
witnessUtxo: {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
},
});
totalInput += utxo.value;
}
// Add outputs
psbt.addOutput({
address: toAddress,
value: amount,
});
// Calculate fee and change
const fee = await estimateFee(client, utxos.length, 2);
const change = totalInput - amount - fee;
if (change < 0) {
throw new Error('Insufficient funds for fee');
}
if (change > 546) { // Dust threshold
psbt.addOutput({
address: fromAddress,
value: change,
});
}
// Sign and finalize
const keyPair = bitcoin.ECPair.fromPrivateKey(privateKey);
psbt.signAllInputs(keyPair);
psbt.finalizeAllInputs();
// Get transaction hex
const txHex = psbt.extractTransaction().toHex();
// Broadcast with Bigmi
const txId = await sendUTXOTransaction(client, { hex: txHex });
// Wait for confirmation
const confirmed = await waitForTransaction(client, {
txId,
txHex,
senderAddress: fromAddress,
confirmations: 1,
});
return {
txId,
fee,
confirmed,
};
} catch (error) {
console.error('Transaction failed:', error);
throw error;
}
}
```
### Working with Different Address Types
```typescript
import { getAddressInfo } from '/core';
function createInputForUTXO(utxo: UTXO, addressInfo: AddressInfo) {
const input: any = {
hash: utxo.txId,
index: utxo.vout,
};
switch (addressInfo.type) {
case 'p2wpkh':
case 'p2wsh':
// Witness UTXOs for SegWit
input.witnessUtxo = {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
};
break;
case 'p2pkh':
case 'p2sh':
// Need full transaction for legacy
input.nonWitnessUtxo = Buffer.from(fullTransactionHex, 'hex');
break;
}
return input;
}
```
### RBF (Replace-By-Fee) Support
Create transactions with RBF enabled for fee bumping:
```typescript
const psbt = new bitcoin.Psbt();
// Add inputs with RBF sequence
for (const utxo of utxos) {
psbt.addInput({
hash: utxo.txId,
index: utxo.vout,
sequence: 0xfffffffd, // RBF enabled
witnessUtxo: {
script: Buffer.from(utxo.scriptHex, 'hex'),
value: utxo.value,
},
});
}
```
### Alternative Libraries
While we recommend bitcoinjs-lib, you can also use:
- **/btc-signer** - Modern, audited Bitcoin transaction library
- **bitcore-lib** - Alternative to bitcoinjs-lib
- **bcoin** - Full Bitcoin implementation
### Security Best Practices
1. **Never expose private keys** in client-side code
2. **Use hardware wallets** for production applications
3. **Validate all inputs** before creating transactions
4. **Test on testnet** before mainnet deployment
5. **Implement proper error handling** for all edge cases
## Examples
- [See Node.js examples](./docs/core/examples.md)
- [See React examples](./docs/react/examples.md)
You can explore the [LI.FI Widget](https://github.com/lifinance/widget) and [LI.FI SDK](https://github.com/lifinance/sdk) for detailed production examples.
## Documentation
- [Learn more about Configuration](./docs/core/config.md)
- [See Core Docs](./docs/core/index.md)
- [See Client Docs](./docs/client/index.md)
- [See React Docs](./docs/react/index.md)
- [Want to add support for your wallet?](./docs/client/connectors.md)
## Support
If you encounter any issues or have questions, please open an issue.
## Contributing
We welcome contributions from the community!
## Changelog
The [changelog](/CHANGELOG.md) is regularly updated to reflect what's changed in each new release.
## License
This project is licensed under the terms of the [MIT License](/LICENSE.md).
## Acknowledgments
Bigmi is inspired by the [wevm](https://github.com/wevm) stack. We appreciate the open-source community's contributions to advancing blockchain development.