API Reference
Complete API reference for @tetherto/wdk-react-native-core
This reference covers @tetherto/wdk-react-native-core@1.0.0-beta.21.
| Export | Type | Description |
|---|---|---|
WdkAppProvider | Component | Root provider for WDK initialization |
useWdkApp | Hook | App-level state (discriminated union) |
useWalletManager | Hook | Wallet lifecycle (create, restore, lock, unlock) |
useAccount | Hook | Account operations (send, sign, verify, estimateFee) |
useProtocol | Hook | Protocol method calls through the active worklet account |
useModule | Hook | Named generic-module calls and event subscriptions |
ModuleService | Class | Low-level static gateway for generic-module calls and events |
useAddresses | Hook | Load and query wallet addresses |
useBalance | Hook | Single asset balance with TanStack Query |
useBalancesForWallet | Hook | Bulk balance fetch for multiple assets |
useBalancesForWallets | Hook | Bulk balance fetch for multiple assets across account indices |
useRefreshBalance | Hook | Invalidate and refetch balances |
BaseAsset | Class | Default IAsset implementation |
validateMnemonic | Utility | Validate BIP39 mnemonic phrases |
balanceQueryKeys | Utility | TanStack Query key factory |
createSecureStorage | Function | Access keychain-backed wallet credentials directly |
DEFAULT_IDENTIFIER | Constant | Reserved identifier for the predecessor's no-identifier storage slot |
SecureStorageError and subclasses | Classes | Storage validation, timeout, and keychain failures |
SecureStorage | Type | Direct credential-storage interface |
WdkAppProviderProps | Type | Props for WdkAppProvider |
WdkAppContextValue | Type | Provider context containing the lifecycle state |
UseWdkAppResult | Type | Return type of useWdkApp, including manual reset methods |
UseAccountParams | Type | Parameters for useAccount |
UseAccountReturn | Type | Return type of useAccount |
UseProtocolParams | Type | Parameters for useProtocol |
UseModuleProxy | Type | Typed asynchronous module proxy returned by useModule |
ModuleEventListener | Type | Listener for parsed module-event payloads |
UseAddressesReturn | Type | Return type of useAddresses |
UseWalletManagerResult | Type | Return type of useWalletManager |
WdkAppProvider
The root provider component that orchestrates WDK initialization. Wraps your app with TanStack Query, Zustand stores, and the worklet runtime.
Must be placed at the root of your component tree. All hooks in this library require WdkAppProvider as an ancestor.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
bundle | BundleConfig | required | Worklet bundle configuration |
wdkConfigs | WdkConfigs | required | Network and protocol configurations |
children | React.ReactNode | required | Child components |
The app owns wallet identity, background-locking behavior, and biometric or other authentication checks.
Example
import { WdkAppProvider } from '@tetherto/wdk-react-native-core'
import { bundle } from './.wdk'
const wdkConfigs = {
networks: {
ethereum: {
blockchain: 'ethereum',
config: {
chainId: 1,
provider: 'https://eth.drpc.org',
},
},
},
}
export default function App() {
return (
<WdkAppProvider
bundle={{ bundle }}
wdkConfigs={wdkConfigs}
>
<Navigation />
</WdkAppProvider>
)
}useWdkApp
Hook to access app-level state. Returns a discriminated union representing the current state of the WDK lifecycle.
Must be used within WdkAppProvider.
Returns
| Property | Type | Description |
|---|---|---|
state | WdkAppState | Current app state (discriminated union) |
reinitializeWdk | () => Promise<void> | Request reinitialization of an already started and initialized worklet |
resetWallets | (networkKeys: string[]) => Promise<void> | Reset wallet modules for exact keys from wdkConfigs.networks |
Both methods can resolve without completing the requested reset. reinitializeWdk() returns early when lifecycle preconditions are not met and catches initialization failures. resetWallets() ignores an empty list or unknown network keys, returns early while loading, and catches underlying reset failures. Treat the resolved promise as request completion, not proof of a reset; observe the expected lifecycle transition and verify the resulting module state.
For example, with networks: { sepolia: { blockchain: 'ethereum', ... } }, call resetWallets(['sepolia']). Passing ['ethereum'] does nothing because ethereum is the blockchain field, not the configured network key.
WdkAppState
The state property is a discriminated union on the status field:
| Status | Additional Fields | Description |
|---|---|---|
'INITIALIZING' | - | Worklet is starting, or the library does not yet know enough to select a more specific state |
'REINITIALIZING' | - | reinitializeWdk() is manually reinitializing the worklet |
'NO_WALLET' | - | Worklet is ready, no wallet has been created |
'LOCKED' | walletId?: string | No wallet is unlocked; the optional ID is a hint when a specific wallet is being targeted |
'READY' | walletId: string | The active wallet identity matches the wallet loaded in the worklet |
'ERROR' | error: Error | Worklet initialization or a wallet lifecycle operation failed |
Example
import { useWdkApp, useWalletManager } from '@tetherto/wdk-react-native-core'
function AppRouter({
walletId,
authenticate,
}: {
walletId: string
authenticate: () => Promise<boolean>
}) {
const { state } = useWdkApp()
const { createWallet, unlock } = useWalletManager()
const create = async () => {
if (await authenticate()) await createWallet(walletId)
}
const open = async () => {
if (await authenticate()) await unlock(walletId)
}
switch (state.status) {
case 'INITIALIZING':
case 'REINITIALIZING':
return <LoadingScreen />
case 'NO_WALLET':
return <Button title="Create Wallet" onPress={create} />
case 'LOCKED':
return <Button title="Unlock" onPress={open} />
case 'READY':
return <MainApp walletId={state.walletId} />
case 'ERROR':
return <ErrorScreen error={state.error} />
}
}useWalletManager
Hook for wallet lifecycle operations: create, restore, lock, unlock, delete, and manage wallets.
Returns
| Property | Type | Description |
|---|---|---|
activeWalletId | string | null | Currently active wallet identifier |
status | 'LOCKED' | 'UNLOCKED' | 'NO_WALLET' | 'LOADING' | 'ERROR' | Current wallet status |
wallets | WalletInfo[] | List of wallets managed by the device |
createWallet | (walletId: string) => Promise<void> | Create and activate a new wallet with the supplied identity |
restoreWallet | (mnemonic: string, walletId: string) => Promise<string> | Restore a wallet from a seed phrase. Returns the wallet ID |
deleteWallet | (walletId: string) => Promise<void> | Delete stored data, send a one-way full WDK disposal request, and reset local lifecycle state; the worklet and IPC keep running |
lock | () => Promise<void> | When a wallet is active, send a one-way full WDK disposal request and clear local wallet state; the worklet and IPC keep running |
unlock | (walletId: string) => Promise<void> | Explicitly decrypt and load the selected wallet; the ID has no implicit fallback |
switchWallet | (walletId: string) => Promise<void> | Under one mutex, lock the current wallet and then unlock the selected wallet; a failed unlock leaves the prior wallet locked |
generateMnemonic | (wordCount?: 12 | 24) => Promise<string> | Generate a new BIP39 mnemonic phrase |
getMnemonic | (walletId: string) => Promise<string | null> | Reconstruct the plaintext mnemonic from the selected wallet's stored encrypted entropy and encryption key; returns null when either is missing |
createTemporaryWallet | (walletId: string, mnemonic?: string) => Promise<string> | Create an in-memory wallet for previewing addresses. Returns the temporary wallet ID |
clearTemporaryWallet | () => void | Clear the temporary wallet session. If it is active, also send a one-way full WDK disposal request and clear shared initialization and address state |
clearCache | () => void | Clear cached balance data |
Only one wallet can be active. Await lock() before creating or restoring a different identity. To select an existing wallet, use switchWallet() or lock and then unlock it explicitly. Switching locks the previous wallet first; a failed unlock leaves it locked.
createWallet(), restoreWallet(), deleteWallet(), lock(), unlock(), switchWallet(), and createTemporaryWallet() share a fail-fast mutex: overlapping calls reject instead of queuing, and a timeout does not cancel the operation. clearTemporaryWallet() is unguarded; serialize it with lifecycle calls because clearing an active temporary wallet also resets shared lifecycle state.
Core does not prompt for authentication. Authenticate in your app before lifecycle or key-access calls. When backgrounding, close the authenticated UI immediately. If an in-flight operation makes lock() reject, wait for it to settle and retry the lock before treating the SDK session as locked.
Deleting even an inactive wallet resets shared lifecycle state after deleting its credentials. A storage failure can leave partial deletion and skip that reset. Check the target's remaining credentials before retrying; a rejected deletion does not mean the wallet was left intact.
Locking an active wallet, successfully deleting a wallet, and clearing an active temporary wallet request full WDK disposal without waiting for worklet cleanup. The worklet and IPC keep running, so completion is not proof that every secret copy was erased. If wallet initialization fails, restart the app before retrying; a rejected initialization does not confirm cleanup.
Advanced Crypto Methods
These methods provide lower-level access to wallet cryptographic operations. Most apps won't need these directly.
| Property | Type | Description |
|---|---|---|
getEncryptionKey | (walletId: string) => Promise<string | null> | Get the selected wallet's base64 encryption key |
getEncryptedSeed | (walletId: string) => Promise<string | null> | Get the base64 encrypted seed from storage |
getEncryptedEntropy | (walletId: string) => Promise<string | null> | Get the base64 encrypted entropy from storage |
generateEntropyAndEncrypt | (wordCount?: 12 | 24) => Promise<{ encryptionKey: string, encryptedSeedBuffer: string, encryptedEntropyBuffer: string }> | Generate and encrypt entropy for wallet creation; all returned strings are base64 |
getMnemonicFromEntropy | (encryptedEntropy: string, encryptionKey: string) => Promise<{ mnemonic: string }> | Derive a mnemonic from base64 encrypted entropy and key strings |
getSeedAndEntropyFromMnemonic | (mnemonic: string) => Promise<{ encryptionKey: string, encryptedSeedBuffer: string, encryptedEntropyBuffer: string }> | Get encrypted seed and entropy from a mnemonic phrase; all returned strings are base64 |
Returned secret values are immutable JavaScript strings. Discard references when finished; method completion does not confirm that every copy was erased.
Example
import { useWalletManager } from '@tetherto/wdk-react-native-core'
function OnboardingScreen({
walletId,
authenticate,
presentMnemonicForBackup,
}: {
walletId: string
authenticate: () => Promise<boolean>
presentMnemonicForBackup: (mnemonic: string) => Promise<void>
}) {
const { createWallet, restoreWallet, generateMnemonic } = useWalletManager()
const handleCreate = async () => {
if (!(await authenticate())) return
await createWallet(walletId)
}
const handleRestore = async (mnemonic: string) => {
if (!(await authenticate())) return
await restoreWallet(mnemonic, walletId)
}
const handlePreview = async () => {
if (!(await authenticate())) return
const mnemonic = await generateMnemonic(12)
await presentMnemonicForBackup(mnemonic)
}
return (
<View>
<Button title="Create Wallet" onPress={handleCreate} />
<Button title="Generate Mnemonic" onPress={handlePreview} />
</View>
)
}useAccount
Hook to interact with a specific blockchain account. Always returns an object - check address or account for readiness.
Parameters
| Parameter | Type | Description |
|---|---|---|
params.network | string | Network identifier (e.g., 'ethereum', 'bitcoin', 'tron') |
params.accountIndex | number | Account index (0-based, following BIP-44 derivation) |
Returns UseAccountReturn<T>
| Property | Type | Description |
|---|---|---|
address | string | null | The derived public address (null if not loaded yet) |
isLoading | boolean | true if the account address is being derived |
error | Error | null | Error if address derivation failed |
account | { accountIndex, network, walletId } | null | Account identifier (null if no active wallet or address not loaded) |
getBalance | (tokens: IAsset[]) => Promise<BalanceFetchResult[]> | Fetch balances directly from the network (no cache) |
send | (params: TransactionParams) => Promise<TransactionResult> | Send a transaction (native or token transfer) |
sign | (message: string) => Promise<UseAccountResponse & { signature: string }> | Sign a UTF-8 message with the account's private key |
verify | (message: string, signature: string) => Promise<UseAccountResponse & { verified: boolean }> | Verify a signature |
estimateFee | (params: TransactionParams) => Promise<Omit<TransactionResult, 'hash'>> | Estimate the fee for a transaction |
extension | () => T | Access chain-specific methods not in the core API |
UseAccountResponse
Base response type for account operations:
interface UseAccountResponse {
success: boolean
error?: string
}TransactionParams
| Field | Type | Description |
|---|---|---|
to | string | Recipient address |
asset | IAsset | Asset to send |
amount | string | Amount in smallest denomination (e.g., wei, satoshi) |
TransactionResult
Extends UseAccountResponse:
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the transaction succeeded |
hash | string | Transaction hash |
fee | string | Fee paid |
error | string? | Error message (only if success is false) |
Example
This example uses Ethereum mainnet. Configure the ethereum wallet for that chain and pass your intended, validated destination as recipientAddress before quoting or sending.
import { Button, Text, View } from 'react-native'
import { useAccount, BaseAsset } from '@tetherto/wdk-react-native-core'
const usdt = new BaseAsset({
id: 'usdt-ethereum',
network: 'ethereum',
symbol: 'USDT',
name: 'Tether USD',
decimals: 6,
isNative: false,
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
})
function SendScreen({ recipientAddress }: { recipientAddress: string }) {
const { address, isLoading, send, estimateFee } = useAccount({ network: 'ethereum', accountIndex: 0 })
if (isLoading) return <Text>Deriving address...</Text>
if (!address) return <Text>No account available</Text>
const handleSend = async () => {
const txParams = {
to: recipientAddress,
asset: usdt,
amount: '1000000', // 1 USDt (6 decimals)
}
// Estimate fee first
const estimate = await estimateFee(txParams)
console.log('Estimated fee:', estimate.fee)
// Send the transaction
const result = await send(txParams)
if (result.success) {
console.log('TX hash:', result.hash)
} else {
console.error('TX failed:', result.error)
}
}
return (
<View>
<Text>Address: {address}</Text>
<Button title="Send 1 USDt" onPress={handleSend} />
</View>
)
}Extension API
Use extension() to access chain-specific methods that aren't part of the core interface. The returned proxy waits for wallet initialization automatically:
// Type parameter provides type-safe access to chain-specific methods
const { extension } = useAccount<{ getTransfers: () => Promise<Transfer[]> }>({
network: 'bitcoin',
accountIndex: 0,
})
// Safe to call at any time - waits for wallet initialization internally
const btcApi = extension()
const transfers = await btcApi.getTransfers()Account extension calls preserve a legitimate null result from the underlying account method. Handle nullable outcomes according to that module's return contract; malformed JSON still rejects.
useProtocol
Hook to call configured protocol modules through the active WDK worklet account. It returns a typed proxy, so each method you access is forwarded to the protocol registered for the requested account, network, type, and name.
Parameters
| Parameter | Type | Description |
|---|---|---|
params.network | string | Network identifier configured in wdkConfigs |
params.accountIndex | number | Account index to use for the protocol call |
params.protocolType | 'bridge' | 'swap' | 'swidge' | 'lending' | 'fiat' | Protocol category |
params.protocolName | string | Registered protocol name |
Returns
Returns T, a proxy for the protocol methods you type with the hook generic. Each method returns a promise from the worklet call.
If no active account is available, protocol method calls log an error and return undefined. A successfully called protocol method can return null when that is its underlying contract.
The swidge protocol type requires a non-empty protocolName and forwards calls through the matching Swidge protocol wrapper on the selected account.
Example
import { useProtocol } from '@tetherto/wdk-react-native-core'
type SwapProtocol = {
quoteSwap: (params: {
tokenIn: string
tokenOut: string
tokenInAmount: string
}) => Promise<{ tokenOutAmount: string; fee: string }>
}
function SwapQuoteScreen() {
const swap = useProtocol<SwapProtocol>({
network: 'ethereum',
accountIndex: 0,
protocolType: 'swap',
protocolName: 'velora',
})
const loadQuote = async () => {
const quote = await swap.quoteSwap({
tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
tokenInAmount: '1000000',
})
console.log('Quoted output:', quote?.tokenOutAmount)
}
return <Button title="Quote" onPress={loadQuote} />
}useModule
Hook that creates a typed proxy for one named generic module in the active HRPC worklet. Every method access is forwarded through ModuleService.callModule(). The proxy also exposes on(event, listener) for module events.
Parameters
| Parameter | Type | Description |
|---|---|---|
moduleName | string | Name shared by the bundler's build-time modules map and runtime wdkConfigs.modules map |
Returns
Returns UseModuleProxy<T>. Define module methods as asynchronous in T because every proxy call crosses the worklet boundary and returns a promise.
type UseModuleProxy<T extends object> = T & {
on: (event: string, listener: ModuleEventListener) => () => void
}
type ModuleEventListener = (payload: unknown) => voidExample
import { useEffect } from 'react'
import { useModule } from '@tetherto/wdk-react-native-core'
type PreferencesModule = {
getTheme: () => Promise<'light' | 'dark'>
setTheme: (theme: 'light' | 'dark') => Promise<void>
}
function PreferencesScreen() {
const preferences = useModule<PreferencesModule>('preferences')
useEffect(() => {
return preferences.on('changed', payload => {
console.log('Preferences changed:', payload)
})
}, [preferences])
// Module calls are asynchronous.
// const theme = await preferences.getTheme()
}Subscriptions are removed automatically on unmount or when moduleName changes. Calling the returned unsubscribe function removes a subscription earlier.
ModuleService
Low-level static service used by useModule(). Use it when a hook is not appropriate but the React Native worklet has already initialized.
| Method | Signature | Description |
|---|---|---|
callModule() | callModule(moduleName: string, method: string, ...args: unknown[]): Promise<unknown> | Serialize positional arguments, call the named module method, and parse the JSON response |
onModuleEvent() | onModuleEvent(moduleName: string, event: string, listener: ModuleEventListener): () => void | Subscribe to one named event and return an unsubscribe function |
callModule() requires non-empty module and method names plus an initialized worklet. It returns undefined when the worklet response is absent or empty. Arguments and results must be safely JSON-serializable; bigint arguments are serialized as strings, while circular and non-standard-prototype objects are rejected.
onModuleEvent() parses JSON event payloads when possible and otherwise delivers the raw string. Listener failures do not stop delivery to other listeners.
useAddresses
Hook to load and query wallet addresses across all networks.
Returns
| Property | Type | Description |
|---|---|---|
data | AddressInfo[] | undefined | Flattened array of all loaded addresses for the active wallet |
isLoading | boolean | true if any address is currently being loaded |
loadAddresses | (accountIndices: number[], networks?: string[]) => Promise<AddressInfoResult[]> | Fetch addresses for given account indices. Returns results for each address load attempt |
getAddressesForNetwork | (network: string) => Array<{ address: string, accountIndex: number }> | Filter addresses by network |
getAccountInfoFromAddress | (address: string) => AddressInfo | undefined | Reverse-lookup: resolve an address string to its account info (case-insensitive) |
AddressInfo
| Field | Type | Description |
|---|---|---|
address | string | The public address |
network | string | Network identifier |
accountIndex | number | Account index |
AddressInfoResult
Discriminated union indicating success or failure for each address load:
type AddressInfoResult =
| { network: string; accountIndex: number; success: true; address: string }
| { network: string; accountIndex: number; success: false; reason: Error }Example
import { useAddresses } from '@tetherto/wdk-react-native-core'
function AddressesScreen() {
const { data, isLoading, loadAddresses, getAddressesForNetwork } = useAddresses()
useEffect(() => {
loadAddresses([0]).then((results) => {
const failed = results.filter((r) => !r.success)
if (failed.length > 0) {
console.warn('Some addresses failed to load:', failed)
}
})
}, [])
const ethAddresses = getAddressesForNetwork('ethereum')
if (isLoading) return <Text>Loading addresses...</Text>
return (
<View>
{ethAddresses.map(({ address, accountIndex }) => (
<Text key={address}>Account {accountIndex}: {address}</Text>
))}
</View>
)
}useBalance
Hook to fetch a single asset balance using TanStack Query. The network is derived from the asset's getNetwork() method. Automatically reads cached data from Zustand on first render, then fetches fresh data from the network.
Parameters
| Parameter | Type | Description |
|---|---|---|
accountIndex | number | Account index |
asset | IAsset | Asset to fetch balance for (network is derived from asset.getNetwork()) |
options | BalanceQueryOptions | Optional query configuration |
Returns UseBalanceResult
A composite result combining address loading state with TanStack Query state:
type UseBalanceResult = Omit<UseQueryResult<BalanceFetchResult | undefined, Error>, 'isLoading' | 'error'> & {
isLoading: boolean
error: Error | null
}| Property | Type | Description |
|---|---|---|
data | BalanceFetchResult | undefined | Balance result |
isLoading | boolean | true while address is loading or first balance fetch is in progress |
isFetching | boolean | Any fetch in progress (including refetch) |
isError | boolean | Query encountered an error |
error | Error | null | Error from address loading or balance query |
refetch | () => Promise<...> | Manually trigger refetch |
Example
import { useBalance, BaseAsset } from '@tetherto/wdk-react-native-core'
const eth = new BaseAsset({
id: 'eth',
network: 'ethereum',
symbol: 'ETH',
name: 'Ether',
decimals: 18,
isNative: true,
})
function BalanceDisplay() {
const { data: balance, isLoading } = useBalance(0, eth, {
refetchInterval: 30000, // Refresh every 30s
})
if (isLoading) return <Text>Loading...</Text>
if (balance?.success) {
return <Text>ETH Balance: {balance.balance}</Text>
}
return <Text>Failed to fetch balance</Text>
}useBalancesForWallet
Hook to fetch balances for multiple assets in a single query. Automatically loads addresses for all required networks before fetching balances. For non-native assets, the hook tries the wallet module's batch token balance method first, then falls back to individual token balance calls when batch fetching is not available.
Parameters
| Parameter | Type | Description |
|---|---|---|
accountIndex | number | Account index |
assetConfigs | IAsset[] | Array of assets to fetch balances for |
options | BalanceQueryOptions | Optional query configuration |
Returns UseBalancesForWalletResult
Same shape as UseBalanceResult but data is BalanceFetchResult[].
type UseBalancesForWalletResult = Omit<UseQueryResult<BalanceFetchResult[], Error>, 'isLoading' | 'error'> & {
isLoading: boolean
error: Error | null
}Example
import { useBalancesForWallet, BaseAsset } from '@tetherto/wdk-react-native-core'
const assets = [
new BaseAsset({ id: 'eth', network: 'ethereum', symbol: 'ETH', name: 'Ether', decimals: 18, isNative: true }),
new BaseAsset({ id: 'usdt-eth', network: 'ethereum', symbol: 'USDT', name: 'Tether USD', decimals: 6, isNative: false, address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' }),
]
function PortfolioScreen() {
const { data: balances, isLoading } = useBalancesForWallet(0, assets, {
refetchInterval: 60000,
})
if (isLoading) return <Text>Loading balances...</Text>
return (
<View>
{balances?.map((b) => (
<Text key={b.assetId}>
{b.assetId}: {b.success ? b.balance : 'Error'}
</Text>
))}
</View>
)
}useBalancesForWallets
Hook to fetch balances for multiple assets across multiple account indices in a single TanStack Query. The hook loads addresses for every required account/network pair before fetching balances, reuses cached balance data as initial query data, and returns per-account token failures instead of failing the entire query when one account fetch fails.
Parameters
| Parameter | Type | Description |
|---|---|---|
accountIndices | number[] | Account indices to fetch balances for |
assetConfigs | IAsset[] | Array of assets to fetch for each account index |
options | BalanceQueryOptions & { concurrencyLimit?: number } | Optional query configuration and per-account fetch concurrency. Defaults to 2 concurrent account fetches |
Returns UseBalancesForWalletsResult
Same shape as UseBalancesForWalletResult; data is a flattened BalanceFetchResult[] for all requested account indices and assets.
type UseBalancesForWalletsResult = Omit<UseQueryResult<BalanceFetchResult[], Error>, 'isLoading' | 'error'> & {
isLoading: boolean
error: Error | null
}Example
import { useBalancesForWallets, BaseAsset } from '@tetherto/wdk-react-native-core'
const assets = [
new BaseAsset({ id: 'eth', network: 'ethereum', symbol: 'ETH', name: 'Ether', decimals: 18, isNative: true }),
new BaseAsset({ id: 'usdt-eth', network: 'ethereum', symbol: 'USDT', name: 'Tether USD', decimals: 6, isNative: false, address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' }),
]
function AccountsPortfolioScreen() {
const { data: balances, isLoading } = useBalancesForWallets([0, 1, 2], assets, {
concurrencyLimit: 2,
refetchInterval: 60000,
})
if (isLoading) return <Text>Loading balances...</Text>
return (
<View>
{balances?.map((b) => (
<Text key={`${b.accountIndex}-${b.assetId}`}>
Account {b.accountIndex} {b.assetId}: {b.success ? b.balance : 'Error'}
</Text>
))}
</View>
)
}useRefreshBalance
Hook that returns a TanStack Query mutation for invalidating and refetching balances.
Returns
Standard TanStack Query mutation result (UseMutationResult).
Call mutate(params) with:
| Field | Type | Description |
|---|---|---|
accountIndex | number | Account index |
network | string? | Network (required for 'token' and 'network' types) |
assetId | string? | Asset ID (required for 'token' type) |
type | 'token' | 'wallet' | 'network' | 'all'? | Refresh scope (default: 'token') |
walletId | string? | Wallet ID override (defaults to active wallet) |
Example
import { useRefreshBalance } from '@tetherto/wdk-react-native-core'
function RefreshButton() {
const { mutate: refreshBalance, isPending } = useRefreshBalance()
return (
<Button
title="Refresh All Balances"
disabled={isPending}
onPress={() => refreshBalance({ accountIndex: 0, type: 'wallet' })}
/>
)
}BaseAsset
Default implementation of the IAsset interface. Wraps an AssetConfig object.
Constructor
new BaseAsset(config: AssetConfig)Methods
| Method | Returns | Description |
|---|---|---|
getId() | string | Unique asset identifier |
getNetwork() | string | Network this asset belongs to |
getSymbol() | string | Ticker symbol (e.g., 'USDT') |
getName() | string | Human-readable name |
getDecimals() | number | Decimal places |
isNative() | boolean | true for native chain currency (ETH, BTC, etc.) |
getContractAddress() | string | null | Token contract address (null for native assets) |
Example
import { BaseAsset } from '@tetherto/wdk-react-native-core'
const usdt = new BaseAsset({
id: 'usdt-ethereum',
network: 'ethereum',
symbol: 'USDT',
name: 'Tether USD',
decimals: 6,
isNative: false,
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
})
console.log(usdt.getSymbol()) // 'USDT'
console.log(usdt.isNative()) // false
console.log(usdt.getContractAddress()) // '0xdAC17F958D2ee523a2206206994597C13D831ec7'IAsset
Interface that all assets must implement. BaseAsset provides a default implementation, but you can create custom asset classes for advanced use cases.
interface IAsset {
getId(): string
getNetwork(): string
getSymbol(): string
getName(): string
getDecimals(): number
isNative(): boolean
getContractAddress(): string | null
}Types
WdkConfigs
Root configuration object passed to WdkAppProvider. Defines network and protocol configurations for the WDK worklet. Extends WdkWorkletConfig from @tetherto/pear-wrk-wdk.
interface WdkConfigs<TNetwork = Record<string, unknown>, TProtocol = Record<string, unknown>> extends WdkWorkletConfig {
networks: {
[blockchain: string]: WdkNetworkConfig<TNetwork>
}
protocols?: {
[protocolName: string]: WdkProtocolConfig<TProtocol>
}
modules?: {
[moduleName: string]: Record<string, unknown>
}
}modules contains runtime configuration for generic modules. It is distinct from the Worklet Bundler's build-time modules map, which selects package names, factories, and forwarded events. Use the same module name in both maps.
WdkNetworkConfig
Wrapper around NetworkConfig with typed config field.
interface WdkNetworkConfig<T = Record<string, unknown>> extends NetworkConfig {
blockchain: string
config: T
}WdkProtocolConfig
Wrapper around ProtocolConfig with typed config field.
interface WdkProtocolConfig<T = Record<string, unknown>> extends ProtocolConfig {
blockchain: string
protocolName: string
config: T
}AssetConfig
Raw configuration object for defining an asset. Pass this to the BaseAsset constructor.
type AssetConfig<T = Record<string, unknown>> = T & {
id: string // Unique identifier
network: string // Network this asset belongs to
symbol: string // Ticker symbol
name: string // Human-readable name
decimals: number // Decimal places
isNative: boolean // true for native chain currency
address?: string | null // Token contract address (null for native)
}BalanceFetchResult
Result of a balance fetch operation.
interface BalanceFetchResult {
success: boolean // Whether the fetch succeeded
network: string // Network name
accountIndex: number // Account index
assetId: string // Asset identifier
balance: string | null // Balance as string (null if failed)
error?: string // Error message (only if success is false)
}BundleConfig
Configuration for the worklet bundle.
interface BundleConfig {
bundle: string // Compiled JavaScript bundle for the worklet runtime
}BalanceQueryOptions
Options for balance query hooks.
interface BalanceQueryOptions {
enabled?: boolean // Whether the query is enabled (default: true)
refetchInterval?: number | false // Refetch interval in ms (false to disable)
staleTime?: number // Stale time in ms
}WalletInfo
Information about a wallet stored on the device.
interface WalletInfo {
identifier: string // Wallet identifier (e.g., user email)
exists: boolean // Whether wallet exists in secure storage
}AccountInfo
Information about a wallet account.
interface AccountInfo {
accountIndex: number // Account index (0-based)
addresses: Record<string, string> // Address for each network
}Direct Credential Storage
Most apps should use useWalletManager for wallet lifecycle operations. The direct API supports existing keychain integrations and migrations; it does not update the wallet inventory or initialize, lock, or dispose a worklet.
createSecureStorage
createSecureStorage(): SecureStorage returns the same config-free singleton on every call. It has no biometric, cache, or timeout configuration arguments. Authenticate in your app before reading or changing wallet credentials. No WdkAppProvider is needed for this direct API, but the native keychain and Expo Crypto runtime must be available.
Import runtime values from the package root before any separate type-only imports:
import { createSecureStorage } from '@tetherto/wdk-react-native-core'
import type { SecureStorage } from '@tetherto/wdk-react-native-core'
const storage: SecureStorage = createSecureStorage()Each credential method requires a wallet identifier. Normal identifiers are trimmed and lowercased before hashing into the keychain service name, so case or surrounding whitespace does not create a separate slot. Use a stable unique identifier of at most 256 characters containing letters, digits, ., -, _, +, or the supported email format. Values must be non-empty strings of at most 10,240 characters.
| Method | Return | Purpose |
|---|---|---|
setEncryptionKey(key, identifier) | Promise<void> | Store an encryption-key string |
getEncryptionKey(identifier) | Promise<string | null> | Read the encryption key |
setEncryptedSeed(encryptedSeed, identifier) | Promise<void> | Store an encrypted-seed string |
getEncryptedSeed(identifier) | Promise<string | null> | Read the encrypted seed |
setEncryptedEntropy(encryptedEntropy, identifier) | Promise<void> | Store an encrypted-entropy string |
getEncryptedEntropy(identifier) | Promise<string | null> | Read the encrypted entropy |
getAllEncrypted(identifier) | Promise<{ encryptedSeed: string | null, encryptedEntropy: string | null, encryptionKey: string | null }> | Read all three values |
hasWallet(identifier) | Promise<boolean> | Check for the encrypted-seed entry |
deleteWallet(identifier) | Promise<void> | Delete the three credential entries |
cleanup() | void | No operation; does not delete stored credentials or clear returned values |
All setter values and identifiers above are string. The storage layer does not encrypt an input seed for you or verify its encoding. Core's wallet hooks store base64 strings here; preserve that format when interoperating with them.
- Keychain accessibility: encryption keys use
WHEN_UNLOCKED. On iOS, encrypted seeds and entropy useWHEN_UNLOCKED_THIS_DEVICE_ONLY, so those entries do not transfer to another device through an encrypted backup restore. - Reading credentials:
getAllEncrypted()includes the raw encryption key; do not log it or treat the object as an encrypted backup. Its separate reads are not an atomic snapshot, so serialize credential mutations when consistency matters.hasWallet()checks only for an encrypted-seed entry; it does not prove that the other credentials exist or recovery will succeed. - Deleting credentials: deletion is not atomic and can leave partial data. Check remaining credentials after a failure before retrying. Direct deletion does not update Core's wallet inventory or reset its worklet.
Storage Errors
Catch these runtime classes from the package root:
| Export | code | Meaning |
|---|---|---|
SecureStorageError | Operation-specific string | Base storage error; failed deletion uses WALLET_DELETE_ERROR |
KeychainReadError | KEYCHAIN_READ_ERROR | Keychain read or existence-check failure |
KeychainWriteError | KEYCHAIN_WRITE_ERROR | Keychain write failure |
ValidationError | VALIDATION_ERROR | Invalid identifier or stored value |
TimeoutError | TIMEOUT_ERROR | A keychain operation exceeded 30 seconds |
Errors expose message, code, and optional cause. The timeout rejects the caller without cancelling the native operation; a timed-out write or delete can still finish later. Recheck state before retrying or continuing a migration.
Legacy Storage Compatibility
When migrating from @tetherto/wdk-react-native-secure-storage, existing normalized named-identifier slots are compatible with Core and need no credential copy. Update imports to Core and provide app-owned authentication. Core requires an explicit identifier and accepts no biometric or cache configuration options; do not forward the standalone package's options.
DEFAULT_IDENTIFIER is the reserved string __default__. It selects the predecessor's bare keychain service names used when no identifier was supplied. Use it only to inspect or migrate that legacy slot, never as the identifier for a new user's wallet.
After authenticating in your app, check for the legacy slot without reading its secrets:
import {
createSecureStorage,
DEFAULT_IDENTIFIER,
} from '@tetherto/wdk-react-native-core'
const storage = createSecureStorage()
const hasLegacyWallet = await storage.hasWallet(DEFAULT_IDENTIFIER)Copy credentials only when moving the legacy default slot or intentionally changing identifiers. Serialize writes and preserve every available credential field. Verify recovery from the destination before deleting the source; a timeout or partial write must stop the migration. Moving keychain values does not populate Core's wallet inventory.
Utilities
validateMnemonic
Validates a BIP39 mnemonic phrase. Checks that it contains exactly 12 or 24 non-empty words.
function validateMnemonic(mnemonic: string): booleanExample
import { validateMnemonic } from '@tetherto/wdk-react-native-core'
validateMnemonic('word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12')
// true
validateMnemonic('only three words')
// falsebalanceQueryKeys
Factory for constructing TanStack Query keys for balance queries. Useful for manual cache invalidation.
const balanceQueryKeys = {
all: ['balances'],
byWallet: (walletId: string, accountIndex: number) => [...],
byNetwork: (network: string) => [...],
byWalletAndNetwork: (walletId: string, accountIndex: number, network: string) => [...],
byToken: (walletId: string, accountIndex: number, network: string, assetId: string) => [...],
}Example
import { balanceQueryKeys } from '@tetherto/wdk-react-native-core'
import { useQueryClient } from '@tanstack/react-query'
const queryClient = useQueryClient()
// Invalidate all balances for a specific wallet
queryClient.invalidateQueries({
queryKey: balanceQueryKeys.byWallet('user@example.com', 0),
})