Transactions
Hooks for sending UserOperations, tracking transaction status, and viewing transaction history.
The useSendTransaction hook builds and submits a UserOperation through the account abstraction bundler. Combine it with useUserOpStatus to track confirmation state.
Import
import { useSendTransaction, useUserOpStatus } from '@embarkai/ui-kit'Basic Usage
import { useSendTransaction } from '@embarkai/ui-kit'
function SendForm() {
const { sendTransaction, isLoading, error, userOpHash } = useSendTransaction()
const handleSend = async () => {
await sendTransaction({
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
value: '0.1',
})
}
return (
<div>
<button onClick={handleSend} disabled={isLoading}>
{isLoading ? 'Sending...' : 'Send 0.1 LUMIA'}
</button>
{error && <p className="error">{error}</p>}
{userOpHash && <p>UserOp: {userOpHash}</p>}
</div>
)
}SendTransactionParams
| Parameter | Type | Required | Description |
|---|---|---|---|
to | `0x${string}` | Yes | Recipient address. |
value | string | Yes | Amount to send (human-readable, e.g., '1.5'). |
data | `0x${string}` | No | Calldata for contract interactions. |
assetType | 'native' | 'erc20' | 'erc721' | 'erc1155' | 'erc3643' | No | Token standard. Defaults to 'native'. |
tokenAddress | `0x${string}` | No | Contract address of the token (required for non-native). |
tokenId | string | No | Token ID for ERC-721 and ERC-1155 transfers. |
decimals | number | No | Token decimals (required for ERC-20 if not auto-detected). |
Return Value
| Property | Type | Description |
|---|---|---|
sendTransaction | (params: SendTransactionParams) => Promise<string | null> | Submit a UserOperation. Returns the UserOp hash on success. |
isLoading | boolean | true while the operation is being signed and submitted. |
error | string | null | Error message if the operation failed. |
userOpHash | string | null | The hash of the submitted UserOperation. |
reset | () => void | Clear the current error and userOpHash state. |
Asset Type Examples
Native Token Transfer
await sendTransaction({
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
value: '1.0',
})ERC-20 Transfer
await sendTransaction({
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
value: '100',
assetType: 'erc20',
tokenAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
decimals: 18,
})ERC-721 (NFT) Transfer
await sendTransaction({
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
value: '1',
assetType: 'erc721',
tokenAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
tokenId: '42',
})ERC-1155 Transfer
await sendTransaction({
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
value: '5',
assetType: 'erc1155',
tokenAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
tokenId: '7',
})Tracking with useUserOpStatus
After submitting a transaction, use useUserOpStatus to poll for confirmation:
import { useSendTransaction, useUserOpStatus } from '@embarkai/ui-kit'
function TrackedSend() {
const { sendTransaction, userOpHash, isLoading } = useSendTransaction()
const { state, txHash, receipt } = useUserOpStatus({
userOpHash,
pollMs: 1000,
maxPollTimeMs: 60000,
onStateChange: (state) => console.log('State:', state),
onTxHash: (hash) => console.log('Tx hash:', hash),
onReceipt: (receipt) => console.log('Confirmed:', receipt),
})
return (
<div>
<button
onClick={() => sendTransaction({ to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', value: '0.01' })}
disabled={isLoading}
>
Send
</button>
{state !== 'waiting' && <p>Status: {state}</p>}
{txHash && <p>Transaction: {txHash}</p>}
</div>
)
}useUserOpStatus Options
| Option | Type | Default | Description |
|---|---|---|---|
userOpHash | string | null | — | The UserOperation hash to track. |
pollMs | number | 1000 | Polling interval in milliseconds. |
maxPollTimeMs | number | 60000 | Maximum time to poll before timeout. |
enabled | boolean | true | Set to false to pause polling. |
onStateChange | (state: UserOpState) => void | — | Callback fired on every state transition. |
onReceipt | (receipt) => void | — | Callback fired when the operation is included on-chain. |
onTxHash | (txHash: string) => void | — | Callback fired when the transaction hash is available. |
UserOpState Values
| State | Description |
|---|---|
'waiting' | No userOpHash provided or polling not started. |
'pending' | UserOp is in the mempool, waiting for inclusion. |
'included' | Transaction has been included in a block. |
'failed' | Transaction reverted on-chain. |
'rejected' | UserOp was rejected by the bundler. |
'timeout' | Polling exceeded maxPollTimeMs. |
useTransactions
Low-level helpers for reading transactions of the connected account via viem. The hook returns functions, not a list:
import { useEffect, useState } from 'react'
import { useTransactions } from '@embarkai/ui-kit'
import type { Transaction } from '@embarkai/ui-kit'
function TransactionHistory() {
const { getTransactionHistory, address, isConnected } = useTransactions()
const [history, setHistory] = useState<Transaction[]>([])
useEffect(() => {
if (!isConnected) return
getTransactionHistory(address, 20).then(setHistory)
}, [address, isConnected, getTransactionHistory])
if (!history.length) return <p>No transactions yet.</p>
return (
<ul>
{history.map((tx, i) => (
<li key={i}>
→ {tx.to}: {tx.value} wei
</li>
))}
</ul>
)
}Return Value
| Property | Type | Description |
|---|---|---|
sendTransaction(params) | (params: { to, value, data?, gasLimit? }) => Promise<{ hash, wait() }> | Send a raw transaction and get a wait() helper for the receipt. |
getTransaction(hash) | (hash) => Promise<viem Transaction> | Fetch a transaction by hash. |
getTransactionReceipt(hash) | (hash) => Promise<TransactionReceipt> | Fetch a receipt by hash. |
getTransactionHistory(address, limit?) | (address, limit?) => Promise<Transaction[]> | Recent transactions; Transaction is { to, value, data?, gasLimit? }. |
address / isConnected | `0x${string}` / boolean | Connected account. |
useSmartAccountTransactions
Reads UserOperations of a smart account from the EntryPoint logs:
import { useEffect, useState } from 'react'
import { useAddress, useSmartAccountTransactions } from '@embarkai/ui-kit'
import type { UserOperation } from '@embarkai/ui-kit'
function SmartAccountHistory() {
const address = useAddress()
const { getRecentUserOperations } = useSmartAccountTransactions()
const [ops, setOps] = useState<UserOperation[]>([])
useEffect(() => {
getRecentUserOperations(address, 10).then(setOps)
}, [address, getRecentUserOperations])
return (
<div>
<h3>Smart Account Activity</h3>
{ops.map((op) => (
<div key={op.hash}>
<span>{op.success ? 'ok' : 'failed'}</span>
<span>{op.transactionHash}</span>
<span>{new Date(op.timestamp * 1000).toLocaleString()}</span>
</div>
))}
</div>
)
}Return Value
| Property | Type | Description |
|---|---|---|
getUserOperations(address, fromBlock?, toBlock?) | => Promise<UserOperation[]> | UserOperations in a block range. |
getRecentUserOperations(address, limit?) | => Promise<UserOperation[]> | Most recent UserOperations. |
getTransactionDetails(txHash) | => Promise<{ transaction, receipt }> | The bundle transaction and its receipt. |
UserOperation fields: hash, sender, nonce, success, actualGasCost, blockNumber, timestamp, transactionHash.
useErc3643Compliance
Check whether a specific ERC-3643 (regulated security token) transfer is allowed before sending it:
import { useErc3643Compliance } from '@embarkai/ui-kit'
function ComplianceCheck({ tokenAddress, to }: { tokenAddress: `0x${string}`; to: `0x${string}` }) {
const { canTransfer, isLoading, error, refetch } = useErc3643Compliance({
tokenAddress,
to,
amount: 1_000_000_000_000_000_000n, // 1 token with 18 decimals
})
if (isLoading) return <p>Checking compliance...</p>
if (error) return <p className="error">{error} <button onClick={refetch}>Retry</button></p>
return (
<p>
{canTransfer
? 'This transfer passes the token compliance rules.'
: 'This transfer is not allowed for the recipient.'}
</p>
)
}Options
| Option | Type | Required | Description |
|---|---|---|---|
tokenAddress | `0x${string}` | undefined | Yes | ERC-3643 token contract. |
to | `0x${string}` | undefined | Yes | Recipient to check. |
amount | bigint | undefined | Yes | Amount in token units. |
enabled | boolean | No | Pause the check (defaults to on when all inputs are set). |
chainId | number | No | Chain to query; defaults to the active chain. |
Return Value
| Property | Type | Description |
|---|---|---|
canTransfer | boolean | undefined | Whether the transfer passes compliance. undefined until checked. |
isLoading | boolean | true while the compliance check is in progress. |
error | string | null | Error message if the check failed. |
refetch | () => void | Re-run the check. |
Error Handling
Always check for errors and provide a way to retry:
import { useSendTransaction } from '@embarkai/ui-kit'
function SendWithRetry() {
const { sendTransaction, isLoading, error, reset } = useSendTransaction()
const handleSend = async () => {
reset()
const hash = await sendTransaction({
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
value: '0.1',
})
if (!hash) {
console.error('Transaction was not submitted')
}
}
return (
<div>
<button onClick={handleSend} disabled={isLoading}>Send</button>
{error && (
<div className="error">
<p>{error}</p>
<button onClick={reset}>Dismiss</button>
</div>
)}
</div>
)
}Next Steps
- useBalance — check balances before sending
- Components — use the
UserOpStatuscomponent for a built-in status display - useSession — verify the user is connected before sending