Hello World Example
Build your first Bitcoin application with LaserEyes in this step-by-step guide.
Let's build a simple application with LaserEyes to connect a wallet, display the balance, and send a transaction. This example will demonstrate the core features of LaserEyes in a practical way.
Complete Example
This example demonstrates a basic React application that:
Wallet Connection
Connects to multiple Bitcoin wallets seamlessly
Balance Display
Shows wallet address and current BTC balance
Send BTC
Enables sending BTC to other addresses
Complete Example
import { useState } from 'react'
import { LaserEyesProvider } from '@omnisat/lasereyes-react'
import { useLaserEyes } from '@omnisat/lasereyes-react'
import {
MAINNET,
UNISAT,
XVERSE,
OYL,
LEATHER,
MAGIC_EDEN,
OKX,
PHANTOM,
WIZZ,
ORANGE
} from '@omnisat/lasereyes-core'
// Main App Component
export default function App() {
return (
<LaserEyesProvider config={{ network: MAINNET }}>
<div className="min-h-screen flex flex-col items-center justify-center p-4">
<h1 className="text-2xl font-bold mb-8">LaserEyes Hello World</h1>
<WalletDemo />
</div>
</LaserEyesProvider>
)
}
// Wallet Demo Component
function WalletDemo() {
const {
connect,
disconnect,
connected,
address,
balance,
sendBTC
} = useLaserEyes()
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [recipient, setRecipient] = useState('')
const [amount, setAmount] = useState('')
const [txId, setTxId] = useState('')
// Connect wallet function
const connectWallet = async (provider) => {
setError('')
setLoading(true)
try {
await connect(provider)
} catch (error) {
setError(error.message || 'Failed to connect wallet')
} finally {
setLoading(false)
}
}
// Format balance from satoshis to BTC
const formatBalance = () => {
if (!balance) return '0'
return (Number(balance) / 100000000).toFixed(8)
}
// Send BTC function
const handleSendBTC = async () => {
if (!recipient || !amount) {
setError('Please enter recipient address and amount')
return
}
setError('')
setTxId('')
setLoading(true)
try {
// Convert amount to satoshis
const satoshis = Math.floor(parseFloat(amount) * 100000000)
const result = await sendBTC(recipient, satoshis)
setTxId(result)
setRecipient('')
setAmount('')
} catch (error) {
setError(error.message || 'Transaction failed')
} finally {
setLoading(false)
}
}
if (!connected) {
return (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<button
onClick={() => connectWallet(UNISAT)}
disabled={loading}
className="px-4 py-2 bg-orange-500 text-white rounded hover:bg-orange-600 disabled:opacity-50"
>
Connect UniSat
</button>
<button
onClick={() => connectWallet(XVERSE)}
disabled={loading}
className="px-4 py-2 bg-orange-500 text-white rounded hover:bg-orange-600 disabled:opacity-50"
>
Connect Xverse
</button>
</div>
{error && <p className="text-red-500">{error}</p>}
</div>
)
}
return (
<div className="space-y-6 w-full max-w-md">
<div className="bg-gray-100 dark:bg-gray-800 p-4 rounded">
<h2 className="text-lg font-semibold mb-2">Wallet Info</h2>
<p className="mb-2">Address: {address}</p>
<p className="mb-4">Balance: {formatBalance()} BTC</p>
<button
onClick={disconnect}
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
>
Disconnect
</button>
</div>
<div className="bg-gray-100 dark:bg-gray-800 p-4 rounded">
<h2 className="text-lg font-semibold mb-4">Send BTC</h2>
<div className="space-y-4">
<div>
<label className="block mb-1">Recipient Address</label>
<input
type="text"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
className="w-full p-2 border rounded dark:bg-gray-700"
placeholder="Enter Bitcoin address"
/>
</div>
<div>
<label className="block mb-1">Amount (BTC)</label>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
className="w-full p-2 border rounded dark:bg-gray-700"
placeholder="0.00000000"
step="0.00000001"
/>
</div>
<button
onClick={handleSendBTC}
disabled={loading}
className="w-full px-4 py-2 bg-orange-500 text-white rounded hover:bg-orange-600 disabled:opacity-50"
>
Send
</button>
{error && <p className="text-red-500">{error}</p>}
{txId && (
<p className="text-green-500">
Transaction sent! TXID: {txId}
</p>
)}
</div>
</div>
</div>
)
}
Step-by-Step Explanation
Setting up the Provider
First, wrap your application with the LaserEyesProvider component to enable wallet functionality:
<LaserEyesProvider config={{ network: MAINNET }}>
<div className="min-h-screen flex flex-col items-center justify-center p-4">
<h1 className="text-2xl font-bold mb-8">LaserEyes Hello World</h1>
<WalletDemo />
</div>
</LaserEyesProvider>
Connecting to a Wallet
Use the useLaserEyes hook to access wallet connection methods:
const { connect } = useLaserEyes()
// Connect wallet function
const connectWallet = async (provider) => {
setError('')
setLoading(true)
try {
await connect(provider)
} catch (error) {
setError(error.message || 'Failed to connect wallet')
} finally {
setLoading(false)
}
}
Reading Wallet Balance
Access and format the wallet balance using the hook:
const { balance } = useLaserEyes()
// Get balance in BTC
const formatBalance = () => {
if (!balance) return '0'
// Convert from satoshis to BTC
return (Number(balance) / 100000000).toFixed(8)
}
Sending a Transaction
Implement BTC sending functionality with proper error handling:
const { sendBTC } = useLaserEyes()
// Send BTC function
const handleSendBTC = async () => {
if (!recipient || !amount) {
setError('Please enter recipient address and amount')
return
}
setError('')
setTxId('')
setLoading(true)
try {
// Convert amount to satoshis
const satoshis = Math.floor(parseFloat(amount) * 100000000)
const result = await sendBTC(recipient, satoshis)
setTxId(result)
} catch (error) {
setError(error.message || 'Transaction failed')
} finally {
setLoading(false)
}
}
Testing Transactions
When testing transactions, it's recommended to use a testnet network to avoid using real BTC. You can configure LaserEyes to use testnet by setting network: TESTNET
in the provider config.
Working with Inscriptions
Inscriptions Example
LaserEyes also supports working with Bitcoin Ordinals and inscriptions. Here's a complete example showing how to list inscriptions and create new ones:
import { useState, useEffect } from 'react'
import { useLaserEyes } from '@omnisat/lasereyes-react'
function InscriptionsList() {
const {
getInscriptions,
inscribe,
connected,
address
} = useLaserEyes()
const [inscriptions, setInscriptions] = useState([])
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [content, setContent] = useState('')
const [inscribing, setInscribing] = useState(false)
const [txId, setTxId] = useState('')
// Load inscriptions
useEffect(() => {
if (connected) {
loadInscriptions()
}
}, [connected, address])
const loadInscriptions = async () => {
setLoading(true)
setError('')
try {
const result = await getInscriptions()
setInscriptions(result)
} catch (error) {
setError(error.message || 'Failed to load inscriptions')
} finally {
setLoading(false)
}
}
// Create new inscription
const handleInscribe = async () => {
if (!content) {
setError('Please enter content to inscribe')
return
}
setInscribing(true)
setError('')
setTxId('')
try {
const result = await inscribe(content, 'text/plain')
setTxId(result)
setContent('')
// Reload inscriptions after successful inscription
await loadInscriptions()
} catch (error) {
setError(error.message || 'Failed to create inscription')
} finally {
setInscribing(false)
}
}
if (!connected) {
return <p>Please connect your wallet first</p>
}
return (
<div className="space-y-8 w-full max-w-md">
{/* Create New Inscription */}
<div className="bg-gray-100 dark:bg-gray-800 p-4 rounded">
<h2 className="text-lg font-semibold mb-4">Create Inscription</h2>
<div className="space-y-4">
<div>
<label className="block mb-1">Content</label>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
className="w-full p-2 border rounded dark:bg-gray-700"
placeholder="Enter text to inscribe"
rows={4}
/>
</div>
<button
onClick={handleInscribe}
disabled={inscribing || !content}
className="w-full px-4 py-2 bg-orange-500 text-white rounded hover:bg-orange-600 disabled:opacity-50"
>
{inscribing ? 'Creating Inscription...' : 'Create Inscription'}
</button>
{error && <p className="text-red-500">{error}</p>}
{txId && (
<p className="text-green-500">
Inscription created! TXID: {txId}
</p>
)}
</div>
</div>
{/* List Inscriptions */}
<div className="bg-gray-100 dark:bg-gray-800 p-4 rounded">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold">Your Inscriptions</h2>
<button
onClick={loadInscriptions}
disabled={loading}
className="px-3 py-1 text-sm bg-gray-200 dark:bg-gray-700 rounded hover:bg-gray-300 dark:hover:bg-gray-600"
>
Refresh
</button>
</div>
{loading ? (
<p>Loading inscriptions...</p>
) : inscriptions.length > 0 ? (
<div className="space-y-4">
{inscriptions.map((inscription: any) => (
<div
key={inscription.id}
className="border dark:border-gray-700 rounded p-3"
>
<p className="text-sm mb-1">
ID: {inscription.id}
</p>
<p className="text-sm mb-1">
Number: {inscription.number}
</p>
<p className="text-sm">
Content Type: {inscription.contentType}
</p>
{inscription.contentType.startsWith('text/') && (
<p className="mt-2 p-2 bg-gray-200 dark:bg-gray-700 rounded text-sm">
{inscription.content}
</p>
)}
</div>
))}
</div>
) : (
<p>No inscriptions found</p>
)}
</div>
</div>
)
}
Inscription Fees
Creating inscriptions requires paying Bitcoin network fees. Make sure you have enough BTC in your wallet to cover both the inscription and the network fees. The exact fee will depend on network conditions and inscription size.
Key Features
- List all inscriptions owned by the connected wallet
- Create new text inscriptions
- Display inscription details including ID, number, and content
- Handle loading states and errors
- Automatic refresh after creating new inscriptions
- Support for viewing text-based inscription content