Installation & Setup
Complete guide to integrating the Bako Safe Connector in your dApp.
Installation
npm install bakosafe fuels @fuels/connectors @fuels/reactBasic Setup
1. Configure FuelProvider
Wrap your application with the Fuel provider and include the BSafeConnector:
// main.tsx or _app.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { FuelProvider } from '@fuels/react';
import { BSafeConnector } from 'bakosafe';
import App from './App';
// Create connector instance
const bsafeConnector = new BSafeConnector();
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<FuelProvider
theme="dark"
fuelConfig={{
connectors: [bsafeConnector],
}}
>
<App />
</FuelProvider>
</React.StrictMode>
);2. Connect to Vault
Use Fuel hooks to connect users:
import { useConnectUI, useAccount, useIsConnected, useDisconnect } from '@fuels/react';
function ConnectButton() {
const { connect } = useConnectUI();
const { account } = useAccount();
const { isConnected } = useIsConnected();
const { disconnect } = useDisconnect();
if (isConnected) {
return (
<div>
<p>Connected: {account}</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
return <button onClick={() => connect()}>Connect Vault</button>;
}3. Create Transactions
Once connected, create transactions from the vault:
import { useFuel, useAccount } from '@fuels/react';
import { bn, Provider, Address } from 'fuels';
function TransferForm() {
const { fuel } = useFuel();
const { account } = useAccount();
async function handleTransfer(recipient: string, amount: string) {
if (!account) return;
// Get provider and wallet
const provider = await Provider.create('https://mainnet.fuel.network/v1/graphql');
const wallet = await fuel.getWallet(account, provider);
const baseAssetId = provider.getBaseAssetId();
// Create transfer
const result = await wallet.transfer(
Address.fromString(recipient),
bn.parseUnits(amount),
baseAssetId
);
// Wait for result
const { id, status } = await result.waitForResult();
console.log('Transaction:', { id, status });
}
return (
<form onSubmit={(e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
handleTransfer(form.recipient.value, form.amount.value);
}}>
<input name="recipient" placeholder="Recipient address" />
<input name="amount" placeholder="Amount" type="number" step="0.001" />
<button type="submit">Transfer</button>
</form>
);
}Contract Calls
Call smart contracts through the vault:
import { useFuel, useAccount } from '@fuels/react';
import { Provider, Contract, bn } from 'fuels';
import { MyContractAbi } from './contracts';
function ContractCall() {
const { fuel } = useFuel();
const { account } = useAccount();
async function callContract() {
if (!account) return;
const provider = await Provider.create('https://mainnet.fuel.network/v1/graphql');
const wallet = await fuel.getWallet(account, provider);
// Connect to contract
const contract = new Contract(
'0xYourContractId...',
MyContractAbi,
wallet
);
// Call contract method
const { value, transactionId } = await contract.functions
.your_method(param1, param2)
.call();
console.log('Result:', value);
console.log('Transaction:', transactionId);
}
return <button onClick={callContract}>Call Contract</button>;
}Transaction Flow
When a transaction is created through the connector:
- Transaction Created: dApp creates transfer/contract call
- Sent to Bako Safe: Transaction queued for approval
- Signers Notified: Vault signers receive notifications
- Signatures Collected: Signers approve in Bako Safe app
- Broadcast: After threshold met, transaction is broadcast
- Result Returned: dApp receives transaction result
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ dApp │────▶│ Bako Safe │────▶│ Fuel │
│ │ │ (Approval) │ │ Network │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ Signers │
│ (Approve) │
└─────────────┘Available Hooks
From @fuels/react:
| Hook | Purpose |
|---|---|
useFuel() | Access Fuel instance |
useAccount() | Get connected account |
useIsConnected() | Check connection status |
useConnectUI() | Trigger connect dialog |
useDisconnect() | Disconnect wallet |
useBalance() | Get account balance |
useWallet() | Get wallet instance |
Error Handling
async function safeTransfer(recipient: string, amount: string) {
try {
const provider = await Provider.create(networkUrl);
const wallet = await fuel.getWallet(account!, provider);
const result = await wallet.transfer(
Address.fromString(recipient),
bn.parseUnits(amount),
provider.getBaseAssetId()
);
return await result.waitForResult();
} catch (error) {
if (error.message?.includes('rejected')) {
console.error('Transaction rejected by user');
} else if (error.message?.includes('insufficient')) {
console.error('Insufficient balance');
} else {
console.error('Transaction failed:', error);
}
throw error;
}
}Next.js Setup
For Next.js applications:
// app/providers.tsx
'use client';
import { FuelProvider } from '@fuels/react';
import { BSafeConnector } from 'bakosafe';
const bsafeConnector = new BSafeConnector();
export function Providers({ children }: { children: React.ReactNode }) {
return (
<FuelProvider
theme="dark"
fuelConfig={{
connectors: [bsafeConnector],
}}
>
{children}
</FuelProvider>
);
}// app/layout.tsx
import { Providers } from './providers';
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}Support
If you have questions: