From 9d1bb6bfc589d27f186db3d8b2c7e766a88b0332 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Wed, 15 Oct 2025 12:31:02 -0300 Subject: [PATCH 01/14] feat: Add BTC mainnet support --- .../hello/frontend/src/ConnectedContent.tsx | 4 + .../hello/frontend/src/DynamicAppContent.tsx | 6 +- .../hello/frontend/src/constants/chains.ts | 10 +- .../hello/frontend/src/hooks/useHandleCall.ts | 110 ++++++++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) diff --git a/examples/hello/frontend/src/ConnectedContent.tsx b/examples/hello/frontend/src/ConnectedContent.tsx index 0338f5e1..710f3aa1 100644 --- a/examples/hello/frontend/src/ConnectedContent.tsx +++ b/examples/hello/frontend/src/ConnectedContent.tsx @@ -35,10 +35,14 @@ const DynamicConnectedContent = ({ (wallet) => wallet.chain === 'SOL' )?.id; const evmWallet = userWallets.find((wallet) => wallet.chain === 'EVM')?.id; + const bitcoinWallet = userWallets.find( + (wallet) => wallet.chain === 'BTC' + )?.id; return { EVM: evmWallet || '', SOL: solanaWallet || '', + BTC: bitcoinWallet || '', }; }, [userWallets]); diff --git a/examples/hello/frontend/src/DynamicAppContent.tsx b/examples/hello/frontend/src/DynamicAppContent.tsx index 5981418a..6a3ca87c 100644 --- a/examples/hello/frontend/src/DynamicAppContent.tsx +++ b/examples/hello/frontend/src/DynamicAppContent.tsx @@ -14,13 +14,17 @@ export function DynamicAppContent() { return network; } + if (primaryWallet?.chain === 'BTC') { + return 8332; + } + // Solana Devnet id from `network` property if (network === '103') { return 901; } return null; - }, [network]); + }, [network, primaryWallet?.chain]); const supportedChain = SUPPORTED_CHAINS.find( (chain) => chain.chainId === decimalChainId diff --git a/examples/hello/frontend/src/constants/chains.ts b/examples/hello/frontend/src/constants/chains.ts index 1cdafe0c..2be76262 100644 --- a/examples/hello/frontend/src/constants/chains.ts +++ b/examples/hello/frontend/src/constants/chains.ts @@ -2,7 +2,7 @@ export interface SupportedChain { explorerUrl: (txHash: string) => string; name: string; chainId: number; - chainType: 'EVM' | 'SOL'; + chainType: 'EVM' | 'SOL' | 'BTC'; icon: string; colorHex: string; } @@ -69,6 +69,14 @@ export const SUPPORTED_CHAINS: SupportedChain[] = [ icon: '/logos/solana-logo.svg', colorHex: '#9945FF', }, + { + explorerUrl: (txHash: string) => `https://mempool.space/tx/${txHash}`, + name: 'Bitcoin', + chainId: 8332, + chainType: 'BTC', + icon: '/logos/bitcoin-logo.svg', + colorHex: '#F7931A', + }, ]; export const SUPPORTED_CHAIN_IDS = SUPPORTED_CHAINS.map( diff --git a/examples/hello/frontend/src/hooks/useHandleCall.ts b/examples/hello/frontend/src/hooks/useHandleCall.ts index c1fc744f..84db3da1 100644 --- a/examples/hello/frontend/src/hooks/useHandleCall.ts +++ b/examples/hello/frontend/src/hooks/useHandleCall.ts @@ -1,6 +1,14 @@ +import { + bitcoinMemoCall, + finalizeBitcoinMemoCall, +} from '@zetachain/toolkit/chains/bitcoin'; import { evmCall } from '@zetachain/toolkit/chains/evm'; import { solanaCall } from '@zetachain/toolkit/chains/solana'; import { type PrimaryWallet } from '@zetachain/wallet'; +import { + isBitcoinWallet, + type PrimaryWalletWithBitcoinSigner, +} from '@zetachain/wallet/bitcoin'; import { getSolanaWalletAdapter } from '@zetachain/wallet/solana'; import { ZeroAddress } from 'ethers'; import { useCallback } from 'react'; @@ -112,6 +120,97 @@ async function handleSolanaCall( callbacks.onTransactionConfirmed?.(result); } +/** + * Handles Bitcoin-specific call logic using PSBT signing + */ +async function handleBitcoinCall( + receiver: string, + message: string, + primaryWallet: PrimaryWallet, + gatewayAddress: string, + callbacks: { + onSigningStart?: UseHandleCallParams['onSigningStart']; + onTransactionSubmitted?: UseHandleCallParams['onTransactionSubmitted']; + onTransactionConfirmed?: UseHandleCallParams['onTransactionConfirmed']; + } +): Promise { + if (!primaryWallet || !isBitcoinWallet(primaryWallet)) { + throw new Error('Wallet does not support Bitcoin'); + } + + const btcWallet = primaryWallet as PrimaryWalletWithBitcoinSigner; + + // Step 1: Build the unsigned PSBT + // For depositAndCall operations: + // - Send: depositAmount (2000 sats to mint as ZRC20) + depositFee buffer + // - depositFee buffer (2x estimated) handles wallet fee rate increases + // - ZetaChain deducts actual depositFee, remaining becomes ZRC20 deposit + // - Reasonable network fees (~500-1000 sats at 2-3 sats/vB) + + const psbtInfo = await bitcoinMemoCall({ + userAddress: btcWallet.address, + fromAddress: btcWallet.address, + gatewayAddress, + data: message, + receiver, + }); + + callbacks.onSigningStart?.(); + + // Step 2: Sign the PSBT with the wallet + const signedPsbtResponse = await btcWallet.signPsbt({ + allowedSighash: [1], // SIGHASH_ALL + unsignedPsbtBase64: psbtInfo.psbtBase64, + signature: [ + { + address: psbtInfo.signingAddress, + signingIndexes: psbtInfo.signingIndexes, + }, + ], + }); + + if (!signedPsbtResponse) { + throw new Error('Failed to sign PSBT - wallet returned undefined'); + } + + console.log('Signed PSBT response:', signedPsbtResponse); + + // Handle different response formats from different wallets + // - String directly: "cHNidP8B..." + // - { psbt: string } + // - { signedPsbt: string } (Phantom format) + let signedPsbtBase64: string | undefined; + + if (typeof signedPsbtResponse === 'string') { + signedPsbtBase64 = signedPsbtResponse; + } else if (signedPsbtResponse && typeof signedPsbtResponse === 'object') { + // Try different property names wallets might use + const responseObj = signedPsbtResponse as { + signedPsbt?: string; + psbt?: string; + }; + signedPsbtBase64 = responseObj.signedPsbt || responseObj.psbt; + } + + if (!signedPsbtBase64) { + console.error('Invalid signed PSBT response:', signedPsbtResponse); + throw new Error( + 'Invalid signed PSBT response - expected string, { psbt: string }, or { signedPsbt: string }' + ); + } + + callbacks.onTransactionSubmitted?.(); + + // Step 3: Finalize and broadcast the signed PSBT + const result = await finalizeBitcoinMemoCall(signedPsbtBase64); + + console.log('Bitcoin transaction broadcast:', { + txid: result.txid, + }); + + callbacks.onTransactionConfirmed?.(result.txid); +} + /** * Custom hook for handling cross-chain calls with proper type safety * and separation of concerns between chain-specific logic and UI state management. @@ -188,6 +287,17 @@ export function useHandleCall({ String(supportedChain.chainId), callbacks ); + } else if (walletType === 'BTC') { + if (!primaryWallet) { + throw new Error('Bitcoin transactions require primaryWallet'); + } + await handleBitcoinCall( + receiver, // Universal Contract address (20 bytes) + message, + primaryWallet, + 'bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y', // TODO: Get gateway from config + callbacks + ); } else { throw new Error(`Unsupported chain: ${walletType}`); } From c6cc53ba66961402ddbe25c3850777590736dcf5 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Thu, 16 Oct 2025 21:53:11 -0300 Subject: [PATCH 02/14] Add Unisat support --- examples/hello/frontend/src/App.tsx | 25 +- .../hello/frontend/src/ConnectedContent.tsx | 13 +- .../hello/frontend/src/DynamicAppContent.tsx | 6 +- .../hello/frontend/src/Eip6963AppContent.tsx | 20 +- .../src/components/NetworkSelector.tsx | 19 +- .../hello/frontend/src/constants/chains.ts | 7 +- .../src/context/UnisatWalletProvider.tsx | 296 ++++++++++++++++++ .../hello/frontend/src/hooks/useHandleCall.ts | 166 +++++----- 8 files changed, 452 insertions(+), 100 deletions(-) create mode 100644 examples/hello/frontend/src/context/UnisatWalletProvider.tsx diff --git a/examples/hello/frontend/src/App.tsx b/examples/hello/frontend/src/App.tsx index a154812e..2ede58e2 100644 --- a/examples/hello/frontend/src/App.tsx +++ b/examples/hello/frontend/src/App.tsx @@ -3,21 +3,26 @@ import { UniversalSignInContextProvider } from '@zetachain/wallet/react'; import { AppContent } from './AppContent'; import { Header } from './components/Header'; import { USE_DYNAMIC_WALLET } from './constants/wallets'; +import { UnisatWalletProvider } from './context/UnisatWalletProvider'; import { useTheme } from './hooks/useTheme'; function App() { const { theme } = useTheme(); - return USE_DYNAMIC_WALLET ? ( - -
- - - ) : ( - <> -
- - + return ( + + {USE_DYNAMIC_WALLET ? ( + +
+ + + ) : ( + <> +
+ + + )} + ); } diff --git a/examples/hello/frontend/src/ConnectedContent.tsx b/examples/hello/frontend/src/ConnectedContent.tsx index 710f3aa1..07eeb73b 100644 --- a/examples/hello/frontend/src/ConnectedContent.tsx +++ b/examples/hello/frontend/src/ConnectedContent.tsx @@ -18,6 +18,7 @@ interface ConnectedContentProps { supportedChain: SupportedChain | undefined; primaryWallet?: PrimaryWallet | null; account?: string | null; + onChainSelect?: (chain: SupportedChain) => void; } const DynamicConnectedContent = ({ @@ -35,14 +36,10 @@ const DynamicConnectedContent = ({ (wallet) => wallet.chain === 'SOL' )?.id; const evmWallet = userWallets.find((wallet) => wallet.chain === 'EVM')?.id; - const bitcoinWallet = userWallets.find( - (wallet) => wallet.chain === 'BTC' - )?.id; return { EVM: evmWallet || '', SOL: solanaWallet || '', - BTC: bitcoinWallet || '', }; }, [userWallets]); @@ -89,11 +86,15 @@ const Eip6963ConnectedContent = ({ selectedProvider, supportedChain, account, + onChainSelect, }: ConnectedContentProps) => { const { switchChain } = useSwitchChain(); const handleNetworkSelect = (chain: SupportedChain) => { - switchChain(chain.chainId); + onChainSelect?.(chain); + if (chain.chainType === 'EVM') { + switchChain(chain.chainId); + } }; return ( @@ -130,6 +131,7 @@ export function ConnectedContent({ supportedChain, primaryWallet, account, + onChainSelect, }: ConnectedContentProps) { return USE_DYNAMIC_WALLET ? ( ); } diff --git a/examples/hello/frontend/src/DynamicAppContent.tsx b/examples/hello/frontend/src/DynamicAppContent.tsx index 6a3ca87c..5981418a 100644 --- a/examples/hello/frontend/src/DynamicAppContent.tsx +++ b/examples/hello/frontend/src/DynamicAppContent.tsx @@ -14,17 +14,13 @@ export function DynamicAppContent() { return network; } - if (primaryWallet?.chain === 'BTC') { - return 8332; - } - // Solana Devnet id from `network` property if (network === '103') { return 901; } return null; - }, [network, primaryWallet?.chain]); + }, [network]); const supportedChain = SUPPORTED_CHAINS.find( (chain) => chain.chainId === decimalChainId diff --git a/examples/hello/frontend/src/Eip6963AppContent.tsx b/examples/hello/frontend/src/Eip6963AppContent.tsx index b6b9035b..b36bac00 100644 --- a/examples/hello/frontend/src/Eip6963AppContent.tsx +++ b/examples/hello/frontend/src/Eip6963AppContent.tsx @@ -1,15 +1,28 @@ +import { useEffect, useState } from 'react'; + import { ConnectedContent } from './ConnectedContent'; -import { SUPPORTED_CHAINS } from './constants/chains'; +import { SUPPORTED_CHAINS, type SupportedChain } from './constants/chains'; import { DisconnectedContent } from './DisconnectedContent'; import { useEip6963Wallet } from './hooks/useEip6963Wallet'; export function Eip6963AppContent() { const { selectedProvider, decimalChainId, account } = useEip6963Wallet(); - const supportedChain = SUPPORTED_CHAINS.find( + const walletChain = SUPPORTED_CHAINS.find( (chain) => chain.chainId === decimalChainId ); + const [selectedChain, setSelectedChain] = useState( + walletChain + ); + + // Sync selected chain with wallet chain when wallet changes (for EVM chains) + useEffect(() => { + if (walletChain?.chainType === 'EVM') { + setSelectedChain(walletChain); + } + }, [walletChain]); + const isDisconnected = !selectedProvider; if (isDisconnected) { @@ -19,7 +32,8 @@ export function Eip6963AppContent() { return ( ); diff --git a/examples/hello/frontend/src/components/NetworkSelector.tsx b/examples/hello/frontend/src/components/NetworkSelector.tsx index 7b525cc7..40730c78 100644 --- a/examples/hello/frontend/src/components/NetworkSelector.tsx +++ b/examples/hello/frontend/src/components/NetworkSelector.tsx @@ -2,6 +2,7 @@ import { useMemo } from 'react'; import { SUPPORTED_CHAINS, type SupportedChain } from '../constants/chains'; import { USE_DYNAMIC_WALLET } from '../constants/wallets'; +import { useUnisatWallet } from '../context/UnisatWalletProvider'; import { Dropdown, type DropdownOption } from './Dropdown'; interface NetworkSelectorProps { @@ -19,12 +20,19 @@ export const NetworkSelector = ({ disabled = false, className = '', }: NetworkSelectorProps) => { + const { connect: connectUnisatWallet, switchChain: switchUnisatChain } = + useUnisatWallet(); + // Convert chains to dropdown options const options: DropdownOption[] = useMemo( () => - SUPPORTED_CHAINS.filter( - (chain) => USE_DYNAMIC_WALLET || chain.chainType === 'EVM' - ).map((chain) => ({ + SUPPORTED_CHAINS.filter((chain) => { + if (USE_DYNAMIC_WALLET) { + return chain.chainType === 'EVM' || chain.chainType === 'SOL'; + } else { + return chain.chainType === 'EVM' || chain.chainType === 'BTC'; + } + }).map((chain) => ({ id: chain.chainId, label: chain.name, value: chain, @@ -46,6 +54,11 @@ export const NetworkSelector = ({ ); const handleSelect = (option: DropdownOption) => { + if (option.value.chainType === 'BTC') { + connectUnisatWallet(); + switchUnisatChain('BITCOIN_SIGNET'); + } + onNetworkSelect?.(option.value); }; diff --git a/examples/hello/frontend/src/constants/chains.ts b/examples/hello/frontend/src/constants/chains.ts index 2be76262..b76b59b4 100644 --- a/examples/hello/frontend/src/constants/chains.ts +++ b/examples/hello/frontend/src/constants/chains.ts @@ -70,9 +70,10 @@ export const SUPPORTED_CHAINS: SupportedChain[] = [ colorHex: '#9945FF', }, { - explorerUrl: (txHash: string) => `https://mempool.space/tx/${txHash}`, - name: 'Bitcoin', - chainId: 8332, + explorerUrl: (txHash: string) => + `https://mempool.space/signet/tx/${txHash}`, + name: 'Bitcoin Signet', + chainId: 18333, chainType: 'BTC', icon: '/logos/bitcoin-logo.svg', colorHex: '#F7931A', diff --git a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx new file mode 100644 index 00000000..ad04b2c7 --- /dev/null +++ b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx @@ -0,0 +1,296 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; + +import { USE_DYNAMIC_WALLET } from '../constants/wallets'; + +interface UnisatBitcoinAccount { + address: string; + publicKey: string; + type: 'payment' | 'ordinals'; +} + +type UnisatChain = + | 'BITCOIN_MAINNET' + | 'BITCOIN_TESTNET' + | 'BITCOIN_TESTNET4' + | 'BITCOIN_SIGNET' + | 'FRACTAL_BITCOIN_MAINNET' + | 'FRACTAL_BITCOIN_TESTNET'; + +export interface UnisatChainDetail { + enum: UnisatChain; + name: string; + network: string; +} + +export interface UnisatWalletContextType { + connected: boolean; + connecting: boolean; + accounts: UnisatBitcoinAccount[]; + rdns: string; + ordinalsAccount: UnisatBitcoinAccount | null; + paymentAccount: UnisatBitcoinAccount | null; + connect: () => Promise; + disconnect: () => void; + signPSBT: ( + psbt: string, + inputsToSign: { address: string; signingIndexes: number[] }[] + ) => Promise; + getChain: () => Promise; + switchChain: (chain: UnisatChain) => Promise; +} + +const UnisatWalletContext = createContext( + undefined +); + +interface UnisatProvider { + requestAccounts: () => Promise; + getAccounts: () => Promise; + getPublicKey: () => Promise; + signPsbt: ( + psbtHex: string, + options?: { + autoFinalized?: boolean; + toSignInputs?: { index: number; address?: string }[]; + } + ) => Promise; + switchChain: (chain: UnisatChain) => Promise; + getChain: () => Promise; + on: (event: string, handler: (...args: unknown[]) => void) => void; + removeListener: ( + event: string, + handler: (...args: unknown[]) => void + ) => void; +} + +const getUnisat = () => + (window as unknown as { unisat?: UnisatProvider }).unisat; + +// Stub provider for when USE_DYNAMIC_WALLET is true +const StubUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const stubValue: UnisatWalletContextType = { + connected: false, + connecting: false, + accounts: [], + rdns: '', + ordinalsAccount: null, + paymentAccount: null, + connect: async () => { + throw new Error('Unisat wallet not available with Dynamic wallet'); + }, + disconnect: () => {}, + signPSBT: async () => { + throw new Error('Unisat wallet not available with Dynamic wallet'); + }, + getChain: async () => { + throw new Error('Unisat wallet not available with Dynamic wallet'); + }, + switchChain: async () => { + throw new Error('Unisat wallet not available with Dynamic wallet'); + }, + }; + + return ( + + {children} + + ); +}; + +const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const [connected, setConnected] = useState(false); + const [connecting, setConnecting] = useState(false); + const [accounts, setAccounts] = useState([]); + + const handleAccountUpdate = async (address: string) => { + const unisat = getUnisat(); + if (!unisat) return; + + try { + const publicKey = await unisat.getPublicKey(); + const formattedAccounts: UnisatBitcoinAccount[] = [ + { + address, + publicKey, + type: 'payment', + }, + ]; + setAccounts(formattedAccounts); + setConnected(true); + } catch (error) { + console.error('Failed to get account details:', error); + } + }; + + useEffect(() => { + const unisat = getUnisat(); + if (!unisat) return; + + const handleAccountsChanged = (accounts: unknown) => { + const accountsArray = accounts as string[]; + if (accountsArray.length === 0) { + disconnect(); + } else { + handleAccountUpdate(accountsArray[0]); + } + }; + + const handleNetworkChanged = (network: unknown) => { + console.log('Network changed to:', network); + }; + + unisat.on('accountsChanged', handleAccountsChanged); + unisat.on('networkChanged', handleNetworkChanged); + + return () => { + unisat.removeListener('accountsChanged', handleAccountsChanged); + unisat.removeListener('networkChanged', handleNetworkChanged); + }; + }, []); + + const connect = async () => { + const unisat = getUnisat(); + if (!unisat) { + alert( + 'Unisat wallet not found! Please install Unisat: https://unisat.io/' + ); + window.open('https://unisat.io/', '_blank'); + return; + } + + try { + setConnecting(true); + const accounts = await unisat.requestAccounts(); + + if (accounts.length === 0) { + throw new Error('No accounts returned from Unisat'); + } + + await handleAccountUpdate(accounts[0]); + console.log('Connected to Unisat:', accounts[0]); + } catch (error) { + console.error('Failed to connect to Unisat:', error); + alert('Failed to connect to Unisat wallet'); + } finally { + setConnecting(false); + } + }; + + const disconnect = () => { + setAccounts([]); + setConnected(false); + }; + + const signPSBT = async ( + psbtBase64: string, + inputsToSign: { address: string; signingIndexes: number[] }[] + ): Promise => { + const unisat = getUnisat(); + if (!unisat) { + throw new Error('Unisat wallet not connected'); + } + + try { + // Convert base64 PSBT to hex for Unisat + const psbtHex = Buffer.from(psbtBase64, 'base64').toString('hex'); + + // Convert inputsToSign to Unisat's format + const toSignInputs = inputsToSign.flatMap(({ address, signingIndexes }) => + signingIndexes.map((index) => ({ index, address })) + ); + + // Unisat returns hex-encoded signed PSBT + const signedPsbtHex = await unisat.signPsbt(psbtHex, { + autoFinalized: false, + toSignInputs, + }); + + // Convert hex back to base64 for our functions + const signedPsbtBase64 = Buffer.from(signedPsbtHex, 'hex').toString('base64'); + + return signedPsbtBase64; + } catch (error) { + console.error('Failed to sign PSBT:', error); + throw error; + } + }; + + const getChain = async (): Promise => { + const unisat = getUnisat(); + if (!unisat) { + throw new Error('Unisat wallet not connected'); + } + + try { + return await unisat.getChain(); + } catch (error) { + console.error('Failed to get network:', error); + throw error; + } + }; + + const switchChain = async (chain: UnisatChain): Promise => { + const unisat = getUnisat(); + if (!unisat) { + throw new Error('Unisat wallet not connected'); + } + + try { + await unisat.switchChain(chain); + } catch (error) { + console.error('Failed to switch network:', error); + throw error; + } + }; + + const ordinalsAccount = + accounts.find((acc) => acc.type === 'ordinals') || null; + const paymentAccount = accounts.find((acc) => acc.type === 'payment') || null; + + const rdns = 'io.unisat'; + + return ( + + {children} + + ); +}; + +export const UnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + return USE_DYNAMIC_WALLET ? ( + {children} + ) : ( + {children} + ); +}; + +// eslint-disable-next-line react-refresh/only-export-components +export const useUnisatWallet = () => { + const context = useContext(UnisatWalletContext); + if (context === undefined) { + throw new Error( + 'useUnisatWallet must be used within a UnisatWalletProvider' + ); + } + return context; +}; diff --git a/examples/hello/frontend/src/hooks/useHandleCall.ts b/examples/hello/frontend/src/hooks/useHandleCall.ts index 84db3da1..77722b2c 100644 --- a/examples/hello/frontend/src/hooks/useHandleCall.ts +++ b/examples/hello/frontend/src/hooks/useHandleCall.ts @@ -1,19 +1,18 @@ import { - bitcoinMemoCall, - finalizeBitcoinMemoCall, + broadcastCommitAndBuildRevealPsbt, + buildBitcoinInscriptionCallCommitPsbt, + finalizeBitcoinInscriptionCallReveal, } from '@zetachain/toolkit/chains/bitcoin'; import { evmCall } from '@zetachain/toolkit/chains/evm'; import { solanaCall } from '@zetachain/toolkit/chains/solana'; import { type PrimaryWallet } from '@zetachain/wallet'; -import { - isBitcoinWallet, - type PrimaryWalletWithBitcoinSigner, -} from '@zetachain/wallet/bitcoin'; import { getSolanaWalletAdapter } from '@zetachain/wallet/solana'; import { ZeroAddress } from 'ethers'; import { useCallback } from 'react'; import type { SupportedChain } from '../constants/chains'; +import { USE_DYNAMIC_WALLET } from '../constants/wallets'; +import { useUnisatWallet } from '../context/UnisatWalletProvider'; import type { EIP6963ProviderDetail } from '../types/wallet'; import { getSignerAndProvider } from '../utils/ethersHelpers'; @@ -121,12 +120,12 @@ async function handleSolanaCall( } /** - * Handles Bitcoin-specific call logic using PSBT signing + * Handles Bitcoin-specific call logic using Unisat + Signet */ async function handleBitcoinCall( receiver: string, message: string, - primaryWallet: PrimaryWallet, + unisatWallet: ReturnType, gatewayAddress: string, callbacks: { onSigningStart?: UseHandleCallParams['onSigningStart']; @@ -134,81 +133,89 @@ async function handleBitcoinCall( onTransactionConfirmed?: UseHandleCallParams['onTransactionConfirmed']; } ): Promise { - if (!primaryWallet || !isBitcoinWallet(primaryWallet)) { - throw new Error('Wallet does not support Bitcoin'); + const { paymentAccount, signPSBT, getChain } = unisatWallet; + + if (!paymentAccount) { + throw new Error('No payment account found. Please connect Unisat wallet.'); } - const btcWallet = primaryWallet as PrimaryWalletWithBitcoinSigner; + const chain = await getChain(); + + if (chain.enum !== 'BITCOIN_SIGNET') { + throw new Error('Unisat wallet is not connected to Signet'); + } - // Step 1: Build the unsigned PSBT - // For depositAndCall operations: - // - Send: depositAmount (2000 sats to mint as ZRC20) + depositFee buffer - // - depositFee buffer (2x estimated) handles wallet fee rate increases - // - ZetaChain deducts actual depositFee, remaining becomes ZRC20 deposit - // - Reasonable network fees (~500-1000 sats at 2-3 sats/vB) + // Use Signet testnet + const bitcoinApi = 'https://mempool.space/signet/api'; + const network = 'signet'; + + console.log('Building commit PSBT...', { + paymentAccount, + receiver, + message, + }); - const psbtInfo = await bitcoinMemoCall({ - userAddress: btcWallet.address, - fromAddress: btcWallet.address, + const commitResult = await buildBitcoinInscriptionCallCommitPsbt({ + bitcoinApi, + fromAddress: paymentAccount.address, gatewayAddress, - data: message, + network, + publicKey: paymentAccount.publicKey, receiver, + types: ['string'], + values: [message], }); + console.log('Commit PSBT built:', commitResult); + callbacks.onSigningStart?.(); - // Step 2: Sign the PSBT with the wallet - const signedPsbtResponse = await btcWallet.signPsbt({ - allowedSighash: [1], // SIGHASH_ALL - unsignedPsbtBase64: psbtInfo.psbtBase64, - signature: [ - { - address: psbtInfo.signingAddress, - signingIndexes: psbtInfo.signingIndexes, - }, - ], - }); + // Sign commit PSBT with Unisat + const signedCommitPsbt = await signPSBT(commitResult.commitPsbtBase64, [ + { + address: paymentAccount.address, + signingIndexes: commitResult.signingIndexes, + }, + ]); - if (!signedPsbtResponse) { - throw new Error('Failed to sign PSBT - wallet returned undefined'); - } + console.log('Commit PSBT signed, broadcasting...'); - console.log('Signed PSBT response:', signedPsbtResponse); - - // Handle different response formats from different wallets - // - String directly: "cHNidP8B..." - // - { psbt: string } - // - { signedPsbt: string } (Phantom format) - let signedPsbtBase64: string | undefined; - - if (typeof signedPsbtResponse === 'string') { - signedPsbtBase64 = signedPsbtResponse; - } else if (signedPsbtResponse && typeof signedPsbtResponse === 'object') { - // Try different property names wallets might use - const responseObj = signedPsbtResponse as { - signedPsbt?: string; - psbt?: string; - }; - signedPsbtBase64 = responseObj.signedPsbt || responseObj.psbt; - } + // Broadcast commit and build reveal + const revealResult = await broadcastCommitAndBuildRevealPsbt({ + bitcoinApi, + commitData: commitResult.commitData, + depositFee: commitResult.depositFee, + fromAddress: paymentAccount.address, + gatewayAddress, + network, + revealFee: commitResult.revealFee, + signedCommitPsbtBase64: signedCommitPsbt, + }); - if (!signedPsbtBase64) { - console.error('Invalid signed PSBT response:', signedPsbtResponse); - throw new Error( - 'Invalid signed PSBT response - expected string, { psbt: string }, or { signedPsbt: string }' - ); - } + console.log('Commit broadcasted:', revealResult.commitTxid); + console.log('Signing reveal PSBT...'); + + // Sign reveal PSBT with Unisat + const signedRevealPsbt = await signPSBT(revealResult.revealPsbtBase64, [ + { + address: paymentAccount.address, + signingIndexes: revealResult.signingIndexes, + }, + ]); callbacks.onTransactionSubmitted?.(); - // Step 3: Finalize and broadcast the signed PSBT - const result = await finalizeBitcoinMemoCall(signedPsbtBase64); + console.log('Reveal PSBT signed, broadcasting...'); - console.log('Bitcoin transaction broadcast:', { - txid: result.txid, - }); + // Finalize and broadcast reveal + const finalResult = await finalizeBitcoinInscriptionCallReveal( + signedRevealPsbt, + bitcoinApi + ); - callbacks.onTransactionConfirmed?.(result.txid); + console.log('Reveal broadcasted:', finalResult.revealTxid); + + callbacks.onTransactionConfirmed?.(finalResult.revealTxid); } /** @@ -228,9 +235,16 @@ export function useHandleCall({ onError, onComplete, }: UseHandleCallParams): UseHandleCallReturn { + // Always call the hook - will only be used when USE_DYNAMIC_WALLET is false + const unisatWallet = useUnisatWallet(); + const handleCall = useCallback(async () => { - const walletType = primaryWallet?.chain || 'EVM'; // Default to 'EVM' for EIP6963 route - const walletAddress = primaryWallet?.address || account; + const walletType = + primaryWallet?.chain || supportedChain?.chainType || 'EVM'; + const walletAddress = + primaryWallet?.address || + account || + unisatWallet?.paymentAccount?.address; if (!walletAddress) { const error = new Error('No wallet address available'); @@ -238,6 +252,12 @@ export function useHandleCall({ return; } + if (!walletAddress && !unisatWallet?.paymentAccount) { + const error = new Error('No wallet address available'); + onError?.(error); + return; + } + if (!supportedChain) { const error = new Error('Unsupported chain'); onError?.(error); @@ -288,14 +308,17 @@ export function useHandleCall({ callbacks ); } else if (walletType === 'BTC') { - if (!primaryWallet) { - throw new Error('Bitcoin transactions require primaryWallet'); + if (USE_DYNAMIC_WALLET) { + throw new Error('Bitcoin not supported with Dynamic wallet yet'); + } + if (!unisatWallet?.paymentAccount) { + throw new Error('Bitcoin transactions require Unisat wallet'); } await handleBitcoinCall( receiver, // Universal Contract address (20 bytes) message, - primaryWallet, - 'bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y', // TODO: Get gateway from config + unisatWallet, + 'tb1qy9pqmk2pd9sv63g27jt8r657wy0d9ueeh0nqur', // Signet gateway address callbacks ); } else { @@ -314,6 +337,7 @@ export function useHandleCall({ receiver, message, account, + unisatWallet, onSigningStart, onTransactionSubmitted, onTransactionConfirmed, From f43e42da3d40ac2df6de5f440678d80bbf226f56 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 12:38:42 -0300 Subject: [PATCH 03/14] Awaiting btc wallet connection --- .../hello/frontend/src/components/NetworkSelector.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/hello/frontend/src/components/NetworkSelector.tsx b/examples/hello/frontend/src/components/NetworkSelector.tsx index 40730c78..5f3b04af 100644 --- a/examples/hello/frontend/src/components/NetworkSelector.tsx +++ b/examples/hello/frontend/src/components/NetworkSelector.tsx @@ -53,10 +53,15 @@ export const NetworkSelector = ({ [selectedChain, options] ); - const handleSelect = (option: DropdownOption) => { + const handleSelect = async (option: DropdownOption) => { if (option.value.chainType === 'BTC') { - connectUnisatWallet(); - switchUnisatChain('BITCOIN_SIGNET'); + try { + await connectUnisatWallet(); + await switchUnisatChain('BITCOIN_SIGNET'); + } catch (error) { + console.error('Failed to connect/switch Unisat wallet:', error); + return; // Don't update selection if connection failed + } } onNetworkSelect?.(option.value); From fea430a73cba99e2fb439e0a554627f06b625044 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 12:40:19 -0300 Subject: [PATCH 04/14] Add missing dependency on useEffect --- .../src/context/UnisatWalletProvider.tsx | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx index ad04b2c7..39d7f4f9 100644 --- a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx +++ b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx @@ -1,4 +1,10 @@ -import React, { createContext, useContext, useEffect, useState } from 'react'; +import React, { + createContext, + useCallback, + useContext, + useEffect, + useState, +} from 'react'; import { USE_DYNAMIC_WALLET } from '../constants/wallets'; @@ -126,32 +132,6 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ } }; - useEffect(() => { - const unisat = getUnisat(); - if (!unisat) return; - - const handleAccountsChanged = (accounts: unknown) => { - const accountsArray = accounts as string[]; - if (accountsArray.length === 0) { - disconnect(); - } else { - handleAccountUpdate(accountsArray[0]); - } - }; - - const handleNetworkChanged = (network: unknown) => { - console.log('Network changed to:', network); - }; - - unisat.on('accountsChanged', handleAccountsChanged); - unisat.on('networkChanged', handleNetworkChanged); - - return () => { - unisat.removeListener('accountsChanged', handleAccountsChanged); - unisat.removeListener('networkChanged', handleNetworkChanged); - }; - }, []); - const connect = async () => { const unisat = getUnisat(); if (!unisat) { @@ -180,10 +160,30 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ } }; - const disconnect = () => { + const disconnect = useCallback(() => { setAccounts([]); setConnected(false); - }; + }, []); + + useEffect(() => { + const unisat = getUnisat(); + if (!unisat) return; + + const handleAccountsChanged = (accounts: unknown) => { + const accountsArray = accounts as string[]; + if (accountsArray.length === 0) { + disconnect(); + } else { + handleAccountUpdate(accountsArray[0]); + } + }; + + unisat.on('accountsChanged', handleAccountsChanged); + + return () => { + unisat.removeListener('accountsChanged', handleAccountsChanged); + }; + }, [disconnect]); const signPSBT = async ( psbtBase64: string, @@ -210,7 +210,9 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ }); // Convert hex back to base64 for our functions - const signedPsbtBase64 = Buffer.from(signedPsbtHex, 'hex').toString('base64'); + const signedPsbtBase64 = Buffer.from(signedPsbtHex, 'hex').toString( + 'base64' + ); return signedPsbtBase64; } catch (error) { From c8b707bd68e002696b6ab113dd369e90b5923c2b Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 12:40:40 -0300 Subject: [PATCH 05/14] Remove useless log --- examples/hello/frontend/src/context/UnisatWalletProvider.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx index 39d7f4f9..df183c85 100644 --- a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx +++ b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx @@ -151,7 +151,6 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ } await handleAccountUpdate(accounts[0]); - console.log('Connected to Unisat:', accounts[0]); } catch (error) { console.error('Failed to connect to Unisat:', error); alert('Failed to connect to Unisat wallet'); From aafc43b7f55327203d921a0f517951175596dc96 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 12:42:12 -0300 Subject: [PATCH 06/14] Removed Buffer usage --- .../hello/frontend/src/context/UnisatWalletProvider.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx index df183c85..3f3e596b 100644 --- a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx +++ b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx @@ -1,3 +1,4 @@ +import { getBytes, hexlify } from 'ethers'; import React, { createContext, useCallback, @@ -195,7 +196,9 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ try { // Convert base64 PSBT to hex for Unisat - const psbtHex = Buffer.from(psbtBase64, 'base64').toString('hex'); + const psbtHex = hexlify( + Uint8Array.from(atob(psbtBase64), (c) => c.charCodeAt(0)) + ); // Convert inputsToSign to Unisat's format const toSignInputs = inputsToSign.flatMap(({ address, signingIndexes }) => @@ -209,8 +212,8 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ }); // Convert hex back to base64 for our functions - const signedPsbtBase64 = Buffer.from(signedPsbtHex, 'hex').toString( - 'base64' + const signedPsbtBase64 = btoa( + String.fromCharCode(...getBytes(signedPsbtHex)) ); return signedPsbtBase64; From ffd665503ca6573bd8c5aaebd4090d8e96dc2952 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 12:43:13 -0300 Subject: [PATCH 07/14] Remove ordinalsAccount --- examples/hello/frontend/src/context/UnisatWalletProvider.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx index 3f3e596b..76104682 100644 --- a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx +++ b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx @@ -34,7 +34,6 @@ export interface UnisatWalletContextType { connecting: boolean; accounts: UnisatBitcoinAccount[]; rdns: string; - ordinalsAccount: UnisatBitcoinAccount | null; paymentAccount: UnisatBitcoinAccount | null; connect: () => Promise; disconnect: () => void; @@ -82,7 +81,6 @@ const StubUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ connecting: false, accounts: [], rdns: '', - ordinalsAccount: null, paymentAccount: null, connect: async () => { throw new Error('Unisat wallet not available with Dynamic wallet'); @@ -251,8 +249,6 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ } }; - const ordinalsAccount = - accounts.find((acc) => acc.type === 'ordinals') || null; const paymentAccount = accounts.find((acc) => acc.type === 'payment') || null; const rdns = 'io.unisat'; @@ -265,7 +261,6 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ connected, connecting, disconnect, - ordinalsAccount, paymentAccount, rdns, signPSBT, From c3952ee98d0f7b1ae63f1b359120fd5dcb27e3de Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 12:43:46 -0300 Subject: [PATCH 08/14] Remove useless log --- examples/hello/frontend/src/hooks/useHandleCall.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/hello/frontend/src/hooks/useHandleCall.ts b/examples/hello/frontend/src/hooks/useHandleCall.ts index 77722b2c..a94079dd 100644 --- a/examples/hello/frontend/src/hooks/useHandleCall.ts +++ b/examples/hello/frontend/src/hooks/useHandleCall.ts @@ -149,12 +149,6 @@ async function handleBitcoinCall( const bitcoinApi = 'https://mempool.space/signet/api'; const network = 'signet'; - console.log('Building commit PSBT...', { - paymentAccount, - receiver, - message, - }); - const commitResult = await buildBitcoinInscriptionCallCommitPsbt({ bitcoinApi, fromAddress: paymentAccount.address, From b68d0474724f3b3538848ead608a9613d5422670 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 12:44:14 -0300 Subject: [PATCH 09/14] Remove useless clause --- examples/hello/frontend/src/hooks/useHandleCall.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/hello/frontend/src/hooks/useHandleCall.ts b/examples/hello/frontend/src/hooks/useHandleCall.ts index a94079dd..f48528c7 100644 --- a/examples/hello/frontend/src/hooks/useHandleCall.ts +++ b/examples/hello/frontend/src/hooks/useHandleCall.ts @@ -246,12 +246,6 @@ export function useHandleCall({ return; } - if (!walletAddress && !unisatWallet?.paymentAccount) { - const error = new Error('No wallet address available'); - onError?.(error); - return; - } - if (!supportedChain) { const error = new Error('Unsupported chain'); onError?.(error); From 5e0016eb9f34afc0616155c20286d759e7c6f183 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 13:03:11 -0300 Subject: [PATCH 10/14] Fix Buffer refactor --- examples/hello/frontend/src/context/UnisatWalletProvider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx index 76104682..89479fcb 100644 --- a/examples/hello/frontend/src/context/UnisatWalletProvider.tsx +++ b/examples/hello/frontend/src/context/UnisatWalletProvider.tsx @@ -196,7 +196,7 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ // Convert base64 PSBT to hex for Unisat const psbtHex = hexlify( Uint8Array.from(atob(psbtBase64), (c) => c.charCodeAt(0)) - ); + ).slice(2); // Convert inputsToSign to Unisat's format const toSignInputs = inputsToSign.flatMap(({ address, signingIndexes }) => @@ -211,7 +211,7 @@ const ActualUnisatWalletProvider: React.FC<{ children: React.ReactNode }> = ({ // Convert hex back to base64 for our functions const signedPsbtBase64 = btoa( - String.fromCharCode(...getBytes(signedPsbtHex)) + String.fromCharCode(...getBytes('0x' + signedPsbtHex)) ); return signedPsbtBase64; From bfc181e6b34dd3ebec5239bb607197cbe9c5c790 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 13:03:36 -0300 Subject: [PATCH 11/14] Fix useEffect --- .../hello/frontend/src/Eip6963AppContent.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/hello/frontend/src/Eip6963AppContent.tsx b/examples/hello/frontend/src/Eip6963AppContent.tsx index b36bac00..6b53f452 100644 --- a/examples/hello/frontend/src/Eip6963AppContent.tsx +++ b/examples/hello/frontend/src/Eip6963AppContent.tsx @@ -8,20 +8,22 @@ import { useEip6963Wallet } from './hooks/useEip6963Wallet'; export function Eip6963AppContent() { const { selectedProvider, decimalChainId, account } = useEip6963Wallet(); - const walletChain = SUPPORTED_CHAINS.find( + // Find the EVM chain from wallet + const evmChain = SUPPORTED_CHAINS.find( (chain) => chain.chainId === decimalChainId ); - const [selectedChain, setSelectedChain] = useState( - walletChain - ); + // Track selected chain separately to support non-EVM chains (SOL, BTC) + const [selectedChain, setSelectedChain] = useState< + SupportedChain | undefined + >(evmChain); - // Sync selected chain with wallet chain when wallet changes (for EVM chains) + // Sync with EVM wallet changes (when user switches chains in MetaMask, etc.) useEffect(() => { - if (walletChain?.chainType === 'EVM') { - setSelectedChain(walletChain); + if (evmChain && evmChain.chainType === 'EVM') { + setSelectedChain(evmChain); } - }, [walletChain]); + }, [evmChain]); const isDisconnected = !selectedProvider; From 0bb0e6301fecf43e59098d01143a6b5efd792eef Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 13:19:20 -0300 Subject: [PATCH 12/14] Store signet gateway address as a constant --- examples/hello/frontend/src/constants/chains.ts | 3 +++ examples/hello/frontend/src/hooks/useHandleCall.ts | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/hello/frontend/src/constants/chains.ts b/examples/hello/frontend/src/constants/chains.ts index b76b59b4..472f3200 100644 --- a/examples/hello/frontend/src/constants/chains.ts +++ b/examples/hello/frontend/src/constants/chains.ts @@ -80,6 +80,9 @@ export const SUPPORTED_CHAINS: SupportedChain[] = [ }, ]; +export const BITCOIN_GATEWAY_ADDRESS_SIGNET = + 'tb1qy9pqmk2pd9sv63g27jt8r657wy0d9ueeh0nqur'; + export const SUPPORTED_CHAIN_IDS = SUPPORTED_CHAINS.map( (chain) => chain.chainId ); diff --git a/examples/hello/frontend/src/hooks/useHandleCall.ts b/examples/hello/frontend/src/hooks/useHandleCall.ts index f48528c7..4063369f 100644 --- a/examples/hello/frontend/src/hooks/useHandleCall.ts +++ b/examples/hello/frontend/src/hooks/useHandleCall.ts @@ -10,7 +10,10 @@ import { getSolanaWalletAdapter } from '@zetachain/wallet/solana'; import { ZeroAddress } from 'ethers'; import { useCallback } from 'react'; -import type { SupportedChain } from '../constants/chains'; +import { + BITCOIN_GATEWAY_ADDRESS_SIGNET, + type SupportedChain, +} from '../constants/chains'; import { USE_DYNAMIC_WALLET } from '../constants/wallets'; import { useUnisatWallet } from '../context/UnisatWalletProvider'; import type { EIP6963ProviderDetail } from '../types/wallet'; @@ -306,7 +309,7 @@ export function useHandleCall({ receiver, // Universal Contract address (20 bytes) message, unisatWallet, - 'tb1qy9pqmk2pd9sv63g27jt8r657wy0d9ueeh0nqur', // Signet gateway address + BITCOIN_GATEWAY_ADDRESS_SIGNET, callbacks ); } else { From 2612627775454a71f61c77f2fc022df8e812e2c0 Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Fri, 17 Oct 2025 13:57:37 -0300 Subject: [PATCH 13/14] Removed logs --- examples/hello/frontend/src/hooks/useHandleCall.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/examples/hello/frontend/src/hooks/useHandleCall.ts b/examples/hello/frontend/src/hooks/useHandleCall.ts index 4063369f..f032a0fd 100644 --- a/examples/hello/frontend/src/hooks/useHandleCall.ts +++ b/examples/hello/frontend/src/hooks/useHandleCall.ts @@ -163,8 +163,6 @@ async function handleBitcoinCall( values: [message], }); - console.log('Commit PSBT built:', commitResult); - callbacks.onSigningStart?.(); // Sign commit PSBT with Unisat @@ -175,8 +173,6 @@ async function handleBitcoinCall( }, ]); - console.log('Commit PSBT signed, broadcasting...'); - // Broadcast commit and build reveal const revealResult = await broadcastCommitAndBuildRevealPsbt({ bitcoinApi, @@ -189,9 +185,6 @@ async function handleBitcoinCall( signedCommitPsbtBase64: signedCommitPsbt, }); - console.log('Commit broadcasted:', revealResult.commitTxid); - console.log('Signing reveal PSBT...'); - // Sign reveal PSBT with Unisat const signedRevealPsbt = await signPSBT(revealResult.revealPsbtBase64, [ { @@ -202,16 +195,12 @@ async function handleBitcoinCall( callbacks.onTransactionSubmitted?.(); - console.log('Reveal PSBT signed, broadcasting...'); - // Finalize and broadcast reveal const finalResult = await finalizeBitcoinInscriptionCallReveal( signedRevealPsbt, bitcoinApi ); - console.log('Reveal broadcasted:', finalResult.revealTxid); - callbacks.onTransactionConfirmed?.(finalResult.revealTxid); } @@ -316,7 +305,6 @@ export function useHandleCall({ throw new Error(`Unsupported chain: ${walletType}`); } } catch (error) { - console.error('Transaction error:', error); onError?.(error instanceof Error ? error : new Error('Unknown error')); } finally { onComplete?.(); From c808978a42a3c2f83c135fad318e51473628552a Mon Sep 17 00:00:00 2001 From: Hernan Clich Date: Thu, 23 Oct 2025 12:13:42 -0300 Subject: [PATCH 14/14] Bump toolkit --- examples/hello/frontend/package.json | 2 +- examples/hello/frontend/yarn.lock | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/examples/hello/frontend/package.json b/examples/hello/frontend/package.json index 5757f5c0..9d1d5e80 100644 --- a/examples/hello/frontend/package.json +++ b/examples/hello/frontend/package.json @@ -10,7 +10,7 @@ "preview": "vite preview" }, "dependencies": { - "@zetachain/toolkit": "16.1.4", + "@zetachain/toolkit": "16.2.0", "@zetachain/wallet": "1.0.13", "clsx": "^2.1.1", "ethers": "^6.13.2", diff --git a/examples/hello/frontend/yarn.lock b/examples/hello/frontend/yarn.lock index 967707b2..c57d4e92 100644 --- a/examples/hello/frontend/yarn.lock +++ b/examples/hello/frontend/yarn.lock @@ -663,6 +663,13 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" +"@bitcoinerlab/secp256k1@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@bitcoinerlab/secp256k1/-/secp256k1-1.2.0.tgz#429d043ef4218b9c71915b50172e9aa4a2a8fea4" + integrity sha512-jeujZSzb3JOZfmJYI0ph1PVpCRV5oaexCgy+RvCXV8XlY+XFB/2n3WOcvBsKLsOw78KYgnQrQWb2HrKE4be88Q== + dependencies: + "@noble/curves" "^1.7.0" + "@coinbase/wallet-sdk@4.3.7": version "4.3.7" resolved "https://registry.yarnpkg.com/@coinbase/wallet-sdk/-/wallet-sdk-4.3.7.tgz#e67d47238714a6f288d59d1b83c7c2c9656fecba" @@ -2447,7 +2454,7 @@ resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.3.0.tgz#f64b8ff886c240e644e5573c097f86e5b43676dc" integrity sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw== -"@noble/curves@1.2.0", "@noble/curves@1.4.0", "@noble/curves@1.4.2", "@noble/curves@1.8.0", "@noble/curves@1.8.1", "@noble/curves@1.8.2", "@noble/curves@1.9.1", "@noble/curves@1.9.2", "@noble/curves@1.9.7", "@noble/curves@^1.3.0", "@noble/curves@^1.4.2", "@noble/curves@^1.6.0", "@noble/curves@^1.8.0", "@noble/curves@^1.9.1", "@noble/curves@^1.9.4", "@noble/curves@~1.4.0", "@noble/curves@~1.8.1", "@noble/curves@~1.9.0": +"@noble/curves@1.2.0", "@noble/curves@1.4.0", "@noble/curves@1.4.2", "@noble/curves@1.8.0", "@noble/curves@1.8.1", "@noble/curves@1.8.2", "@noble/curves@1.9.1", "@noble/curves@1.9.2", "@noble/curves@1.9.7", "@noble/curves@^1.3.0", "@noble/curves@^1.4.2", "@noble/curves@^1.6.0", "@noble/curves@^1.7.0", "@noble/curves@^1.8.0", "@noble/curves@^1.9.1", "@noble/curves@^1.9.4", "@noble/curves@~1.4.0", "@noble/curves@~1.8.1", "@noble/curves@~1.9.0": version "1.9.7" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.7.tgz#79d04b4758a43e4bca2cbdc62e7771352fa6b951" integrity sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw== @@ -4739,11 +4746,12 @@ resolved "https://registry.yarnpkg.com/@zetachain/standard-contracts/-/standard-contracts-2.0.1.tgz#aefd28bb81f1f05b183bd73dc62bd68e456a6ebf" integrity sha512-SHV9a1bSgy8litI/LRZ4VIus7Gsjy0wj3n9bZeIsEydn0C5NNZxYO4XW+P06dlEyDQjtcVJQHoQOyHkodBoVsQ== -"@zetachain/toolkit@16.1.4": - version "16.1.4" - resolved "https://registry.yarnpkg.com/@zetachain/toolkit/-/toolkit-16.1.4.tgz#2ab375a589ab0ff8e1d791e61de723e0355702f1" - integrity sha512-wR6ffBHaR9+BWA1rP/5XgswX/+NXZHZi9CM/ytct4iXj4zUGDn6SaZlSV2+rnkhX6C0ZLs9gnMfcNco2/IsKCg== +"@zetachain/toolkit@16.2.0": + version "16.2.0" + resolved "https://registry.yarnpkg.com/@zetachain/toolkit/-/toolkit-16.2.0.tgz#66b1ccb5c065ec869af7cb26e6e63b3fbaf2a061" + integrity sha512-kfLnwyNSdTUwFQqEIF0qlqcjU1hOZ6+SmazAFUqboZqIAUn8VSW83gz8RPpMHPYTxXknY6p3D8lCUpj3bpVV1A== dependencies: + "@bitcoinerlab/secp256k1" "^1.2.0" "@coral-xyz/anchor" "^0.30.1" "@ethersproject/units" "^5.8.0" "@inquirer/prompts" "^2.1.1"