@tetherto/wdk-react-native-provider
v1.0.0-beta.3
Published
React Native library providing wallet context and WDK service integration
Readme
@tetherto/wdk-react-native-provider
A React Native library providing wallet context and WDK (Wallet Development Kit) service integration for building secure, multi-chain cryptocurrency wallets.
Features
- Multi-chain Support: Bitcoin, Ethereum, Polygon, Arbitrum, TON, Solana, and Tron
- Multi-asset Management: BTC, USDT, XAUT, and more
- Secure Seed Management: Encrypted seed phrase storage using native keychain
- React Context API: Easy-to-use wallet context provider and hooks
- Account Management: Create, import, and unlock wallets
- Balance & Transactions: Real-time balance updates and transaction history
- Send & Quote: Transaction sending and fee estimation
- TypeScript Support: Full type definitions included
Requirements
- Android minSdkVersion: 29 or higher
- iOS Deployment Target: 15.1 or higher
- React Native: 0.81.0+
Installation
npm install @tetherto/wdk-react-native-providerAndroid Configuration
The library requires Android minSdkVersion 29 to properly run react-native-bare-kit.
For Expo projects with prebuild:
Add to your app.json or app.config.js:
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"android": {
"minSdkVersion": 29
}
}
]
]
}
}For bare React Native projects:
Update android/build.gradle:
buildscript {
ext {
minSdkVersion = 29
// ... other config
}
}Peer Dependencies
This library requires several peer dependencies. Install them using:
npm install \
@craftzdog/react-native-buffer \
@react-native-async-storage/async-storage \
@tetherto/pear-wrk-wdk \
@tetherto/wdk-secret-manager \
b4a \
bip39 \
browserify-zlib \
decimal.js \
events \
http2-wrapper \
https-browserify \
nice-grpc-web \
path-browserify \
process \
querystring-es3 \
react-native-bare-kit \
react-native-crypto \
react-native-device-info \
react-native-get-random-values \
react-native-keychain \
react-native-tcp-socket \
react-native-url-polyfill \
sodium-javascript \
stream-browserify \
stream-httpMetro Configuration
The library requires Node.js core module polyfills for React Native. Configure your Metro bundler using the provided helper function.
For Expo projects:
Update your metro.config.js:
const { getDefaultConfig } = require('expo/metro-config');
const { configureMetroForWDK } = require('@tetherto/wdk-react-native-provider/metro-polyfills');
const config = getDefaultConfig(__dirname);
const configWdk = configureMetroForWDK(config);
module.exports = configWdk;Manual configuration (if needed):
If you prefer to manually configure the polyfills:
const { getDefaultConfig } = require('expo/metro-config');
const { getMetroPolyfills } = require('@tetherto/wdk-react-native-provider/metro-polyfills');
const config = getDefaultConfig(__dirname);
config.resolver.extraNodeModules = {
...config.resolver.extraNodeModules,
...getMetroPolyfills(),
};
module.exports = config;Note: Runtime polyfills for Buffer, process, and crypto are automatically initialized when you import from @tetherto/wdk-react-native-provider. No additional setup is required.
Quick Start
1. Setup the WalletProvider
Wrap your app with the WalletProvider and provide the required configuration.
For Expo Router projects:
Add the provider to your app/_layout.tsx file to make the useWallet hook accessible throughout your app:
// app/_layout.tsx
import { WalletProvider } from '@tetherto/wdk-react-native-provider';
import { Stack } from 'expo-router';
// Define your chain configurations
const CHAINS_CONFIG = {
ethereum: {
chainId: 1,
blockchain: 'ethereum',
provider: 'https://mainnet.gateway.tenderly.co/YOUR_KEY',
bundlerUrl: 'https://api.candide.dev/public/v3/ethereum',
paymasterUrl: 'https://api.candide.dev/public/v3/ethereum',
paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
entrypointAddress: '0x0000000071727De22E5E9d8BAf0edAc6f37da032',
transferMaxFee: 5000000,
swapMaxFee: 5000000,
bridgeMaxFee: 5000000,
paymasterToken: {
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
},
},
bitcoin: {
host: 'api.ordimint.com',
port: 50001,
},
// ... add other chains as needed
};
export default function RootLayout() {
return (
<WalletProvider
config={{
indexer: {
apiKey: 'your-api-key-here',
url: 'https://your-indexer-url.com',
},
chains: CHAINS_CONFIG,
enableCaching: true, // Optional: enable caching for better performance
}}
>
<Stack />
</WalletProvider>
);
}For standard React Native projects:
Wrap your root component:
// App.tsx
import { WalletProvider } from '@tetherto/wdk-react-native-provider';
const CHAINS_CONFIG = {
ethereum: {
chainId: 1,
blockchain: 'ethereum',
provider: 'https://mainnet.gateway.tenderly.co/YOUR_KEY',
bundlerUrl: 'https://api.candide.dev/public/v3/ethereum',
paymasterUrl: 'https://api.candide.dev/public/v3/ethereum',
paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
entrypointAddress: '0x0000000071727De22E5E9d8BAf0edAc6f37da032',
transferMaxFee: 5000000,
swapMaxFee: 5000000,
bridgeMaxFee: 5000000,
paymasterToken: {
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
},
},
bitcoin: {
host: 'api.ordimint.com',
port: 50001,
},
// ... other chains
};
function App() {
return (
<WalletProvider
config={{
indexer: {
apiKey: 'your-api-key-here',
url: 'https://your-indexer-url.com',
},
chains: CHAINS_CONFIG,
enableCaching: true, // Optional: enable caching for balances and transactions
}}
>
<YourApp />
</WalletProvider>
);
}2. Use the Wallet Context
Access wallet functionality using the useWallet hook:
import { useWallet } from '@tetherto/wdk-react-native-provider';
function WalletScreen() {
const {
wallet,
balances,
transactions,
isLoading,
isInitialized,
isUnlocked,
createWallet,
unlockWallet,
refreshWalletBalance,
refreshTransactions,
} = useWallet();
// Create a new wallet
const handleCreateWallet = async () => {
try {
const wallet = await createWallet({
name: 'Imported Wallet',
mnemonic: 'your twelve word seed phrase goes here',
});
console.log('Wallet created:', wallet);
} catch (error) {
console.error('Failed to create wallet:', error);
}
};
// Unlock wallet
const handleUnlockWallet = async () => {
try {
await unlockWallet();
console.log('Wallet unlocked');
} catch (error) {
console.error('Failed to unlock wallet:', error);
}
};
if (!isInitialized) {
return <Text>Initializing...</Text>;
}
if (!wallet) {
return (
<View>
<Button title="Create Wallet" onPress={handleCreateWallet} />
<Button title="Import Wallet" onPress={handleImportWallet} />
</View>
);
}
if (!isUnlocked) {
return <Button title="Unlock Wallet" onPress={handleUnlockWallet} />;
}
return (
<View>
<Text>Wallet Name: {wallet.name}</Text>
<Button title="Refresh Balance" onPress={refreshWalletBalance} />
<Button title="Refresh Transactions" onPress={refreshTransactions} />
</View>
);
}API Reference
WalletProvider
The main provider component that manages wallet state.
Props:
config.indexer(object, required): Indexer service configurationconfig.indexer.apiKey(string, required): API key for the indexer serviceconfig.indexer.url(string, required): URL of the indexer serviceconfig.indexer.version(string, optional): API version (defaults to 'v1')
config.chains(ChainsConfig, required): Chain configuration object containing network-specific settingsconfig.enableCaching(boolean, optional): Enable caching for balances and transactions to improve performance
See Chain Configuration for detailed configuration options.
useWallet()
Hook to access wallet context and functionality.
Returns:
{
// State
wallet?: Wallet | null;
addresses?: AddressMap;
balances: {
list: Amount[];
map: BalanceMap;
isLoading: boolean;
};
transactions: {
list: Transaction[];
map: TransactionMap;
isLoading: boolean;
};
isLoading: boolean;
error: string | null;
isInitialized: boolean;
isUnlocked: boolean;
// Actions
createWallet: (params: { name: string; mnemonic?: string }) => Promise<Wallet | null>;
clearWallet: () => Promise<void>;
clearError: () => void;
refreshWalletBalance: () => Promise<void>;
refreshTransactions: () => Promise<void>;
unlockWallet: () => Promise<boolean | undefined>;
}WDKService
Low-level service for direct wallet operations. Available as a singleton.
import { WDKService } from '@tetherto/wdk-react-native-provider';
// Initialize WDK
await WDKService.initialize();
// Create seed
const seed = await WDKService.createSeed({ prf: 'passkey' });
// Import seed phrase
await WDKService.importSeedPhrase({
prf: 'passkey',
seedPhrase: 'your mnemonic here',
});
// Create wallet
const wallet = await WDKService.createWallet({
walletName: 'My Wallet',
prf: 'passkey',
});
// Get balances
const balances = await WDKService.resolveWalletBalances(
enabledAssets,
addressMap
);
// Send transaction
const result = await WDKService.sendByNetwork(
NetworkType.ETHEREUM,
0, // account index
100, // amount
'0x...', // recipient address
AssetTicker.USDT
);Chain Configuration
The library supports multiple blockchain networks, each with its own configuration structure.
Chains Configuration Structure
The chains configuration object supports the following blockchain networks:
const chains = {
ethereum?: EVMChainConfig;
arbitrum?: EVMChainConfig;
polygon?: EVMChainConfig;
ton?: TONChainConfig;
bitcoin?: BitcoinChainConfig;
tron?: TronChainConfig;
}EVM Chain Configuration
For Ethereum, Polygon, and Arbitrum:
const ethereumConfig = {
chainId: 1,
blockchain: 'ethereum',
provider: 'https://mainnet.gateway.tenderly.co/YOUR_KEY',
bundlerUrl: 'https://api.candide.dev/public/v3/ethereum',
paymasterUrl: 'https://api.candide.dev/public/v3/ethereum',
paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
entrypointAddress: '0x0000000071727De22E5E9d8BAf0edAc6f37da032',
transferMaxFee: 5000000,
swapMaxFee: 5000000,
bridgeMaxFee: 5000000,
paymasterToken: {
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
},
safeModulesVersion: '0.3.0', // Optional, for Polygon
};TON Chain Configuration
const tonConfig = {
tonApiClient: {
url: 'https://tonapi.io',
},
tonClient: {
url: 'https://toncenter.com/api/v2/jsonRPC',
},
paymasterToken: {
address: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs',
},
transferMaxFee: 1000000000,
};Bitcoin Chain Configuration
const bitcoinConfig = {
host: 'api.ordimint.com',
port: 50001,
};Tron Chain Configuration
const tronConfig = {
chainId: 3448148188,
provider: 'https://trongrid.io',
gasFreeProvider: 'https://gasfree.io',
apiKey: 'your-api-key',
apiSecret: 'your-api-secret',
serviceProvider: 'TKtWbdzEq5ss9vTS9kwRhBp5mXmBfBns3E',
verifyingContract: 'THQGuFzL87ZqhxkgqYEryRAd7gqFqL5rdc',
transferMaxFee: 10000000,
swapMaxFee: 1000000,
bridgeMaxFee: 1000000,
paymasterToken: {
address: 'TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf',
},
};Complete Configuration Example
import { WalletProvider } from '@tetherto/wdk-react-native-provider';
const chains = {
ethereum: {
chainId: 1,
blockchain: 'ethereum',
provider: 'https://mainnet.gateway.tenderly.co/YOUR_KEY',
bundlerUrl: 'https://api.candide.dev/public/v3/ethereum',
paymasterUrl: 'https://api.candide.dev/public/v3/ethereum',
paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
entrypointAddress: '0x0000000071727De22E5E9d8BAf0edAc6f37da032',
transferMaxFee: 5000000,
swapMaxFee: 5000000,
bridgeMaxFee: 5000000,
paymasterToken: {
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
},
},
polygon: {
chainId: 137,
blockchain: 'polygon',
provider: 'https://polygon.gateway.tenderly.co/YOUR_KEY',
bundlerUrl: 'https://api.candide.dev/public/v3/polygon',
paymasterUrl: 'https://api.candide.dev/public/v3/polygon',
paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
entrypointAddress: '0x0000000071727De22E5E9d8BAf0edAc6f37da032',
transferMaxFee: 5000000,
swapMaxFee: 5000000,
bridgeMaxFee: 5000000,
paymasterToken: {
address: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
},
safeModulesVersion: '0.3.0',
},
arbitrum: {
chainId: 42161,
blockchain: 'arbitrum',
provider: 'https://arbitrum.gateway.tenderly.co/YOUR_KEY',
bundlerUrl: 'https://api.candide.dev/public/v3/arbitrum',
paymasterUrl: 'https://api.candide.dev/public/v3/arbitrum',
paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
entrypointAddress: '0x0000000071727De22E5E9d8BAf0edAc6f37da032',
transferMaxFee: 5000000,
swapMaxFee: 5000000,
bridgeMaxFee: 5000000,
paymasterToken: {
address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
},
},
ton: {
tonApiClient: {
url: 'https://tonapi.io',
},
tonClient: {
url: 'https://toncenter.com/api/v2/jsonRPC',
},
paymasterToken: {
address: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs',
},
transferMaxFee: 1000000000,
},
bitcoin: {
host: 'api.ordimint.com',
port: 50001,
},
tron: {
chainId: 3448148188,
provider: 'https://trongrid.io',
gasFreeProvider: 'https://gasfree.io',
apiKey: 'your-api-key',
apiSecret: 'your-api-secret',
serviceProvider: 'TKtWbdzEq5ss9vTS9kwRhBp5mXmBfBns3E',
verifyingContract: 'THQGuFzL87ZqhxkgqYEryRAd7gqFqL5rdc',
transferMaxFee: 10000000,
swapMaxFee: 1000000,
bridgeMaxFee: 1000000,
paymasterToken: {
address: 'TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf',
},
},
};
function App() {
return (
<WalletProvider
config={{
indexer: {
apiKey: 'your-indexer-api-key',
url: 'https://your-indexer-url.com',
},
chains,
enableCaching: true, // Optional: enable caching for better performance
}}
>
<YourApp />
</WalletProvider>
);
}Advanced Usage
Accessing Balances and Transactions
const { wallet, addresses, balances, transactions } = useWallet();
if (wallet) {
// Addresses
if (addresses) {
Object.entries(addresses).forEach(([ticker, addressList]) => {
console.log(`${ticker}: ${addressList[0]?.value}`);
});
}
// Balances - available as both list and map
balances.list.forEach(balance => {
console.log(`${balance.denomination}: ${balance.value}`);
});
// Or access by ticker from the map
const usdtBalance = balances.map.USDT?.[0];
console.log('USDT Balance:', usdtBalance?.value);
// Check loading state
if (balances.isLoading) {
console.log('Loading balances...');
}
// Transactions - available as both list and map
transactions.list.forEach(tx => {
console.log('Transaction:', tx);
});
// Or access by ticker from the map
const usdtTransactions = transactions.map.USDT;
console.log('USDT Transactions:', usdtTransactions);
// Check loading state
if (transactions.isLoading) {
console.log('Loading transactions...');
}
}Network Types
import { NetworkType } from '@tetherto/wdk-react-native-provider';
// Available networks:
NetworkType.SEGWIT // Bitcoin
NetworkType.ETHEREUM // Ethereum
NetworkType.POLYGON // Polygon
NetworkType.ARBITRUM // Arbitrum
NetworkType.TON // TON
NetworkType.SOLANA // Solana
NetworkType.TRON // TronAsset Tickers
import { AssetTicker } from '@tetherto/wdk-react-native-provider';
// Available assets:
AssetTicker.BTC // Bitcoin
AssetTicker.USDT // Tether USD
AssetTicker.XAUT // Tether GoldTypeScript Support
This library is written in TypeScript and includes complete type definitions. Import types as needed:
import type {
// Provider configuration
WalletProviderConfig,
// Wallet types
Amount,
Transaction,
Wallet,
// Enums (also available as values)
AssetTicker,
NetworkType,
} from '@tetherto/wdk-react-native-provider';Note: Chain configuration types (ChainsConfig, EVMChainConfig, TONChainConfig, etc.) are defined in the underlying @tetherto/pear-wrk-wdk package. TypeScript will infer these types when you use them in the WalletProviderConfig, so explicit imports are typically not needed.
Security Considerations
- Seed Phrase Storage: Seed phrases are encrypted and stored securely using device-specific encryption
- Passkey/PRF: Uses device unique ID by default. In production, integrate with biometric authentication
- Never Log Seeds: Never log or display seed phrases in production code
- Secure Communication: All API calls use HTTPS and require API keys
Development
See CONTRIBUTING.md for development workflow and guidelines.
Build
npm run prepareType Check
npm run typecheckLint
npm run lintTest
npm testTroubleshooting
Setup Checklist
If you're experiencing issues, verify you've completed all setup steps:
For Expo projects:
- ✅ Install the package:
npm install @tetherto/wdk-react-native-provider - ✅ Install all peer dependencies (see Peer Dependencies)
- ✅ Configure Android minSdkVersion to 29 in
app.json:{ "expo": { "plugins": [ ["expo-build-properties", { "android": { "minSdkVersion": 29 } }] ] } } - ✅ Configure Metro polyfills in
metro.config.js:const { configureMetroForWDK } = require('@tetherto/wdk-react-native-provider/metro-polyfills'); const config = getDefaultConfig(__dirname); const configWdk = configureMetroForWDK(config); module.exports = configWdk; - ✅ Add
WalletProvidertoapp/_layout.tsxwith proper config - ✅ Use
useWallet()hook in your components - ✅ Check
isInitializedbefore creating wallets
For bare React Native projects:
- ✅ Install the package and peer dependencies
- ✅ Set minSdkVersion to 29 in
android/build.gradle - ✅ Configure Metro polyfills in
metro.config.js - ✅ Wrap your root component with
WalletProvider - ✅ Rebuild native code:
npx react-native run-androidornpx react-native run-ios
"WDK Manager not initialized"
The WDK service is initialized automatically when the WalletProvider mounts. If you see this error, ensure:
- Your component is wrapped with
WalletProvider - The provider's config is properly set
- You're checking
isInitializedbefore performing wallet operations:
const { isInitialized, createWallet } = useWallet();
if (isInitialized) {
await createWallet({ name: 'My Wallet' });
}"No wallet found"
Ensure a wallet has been created or imported before attempting transactions:
const { wallet, createWallet } = useWallet();
if (!wallet) {
await createWallet({ name: 'My Wallet' });
}Metro bundler errors or "Unable to resolve module"
If you see errors like Unable to resolve "stream" or other Node.js core modules:
- Ensure you've configured Metro polyfills correctly (see Metro Configuration)
- Clear Metro cache:
npx expo start --clearornpx react-native start --reset-cache - Delete
node_modulesand reinstall:rm -rf node_modules && npm install - For Expo: Run prebuild if using custom native modules:
npx expo prebuild --clean
Android build fails with "Execution failed for task ':app:checkDebugAarMetadata'"
This usually means minSdkVersion is too low. Ensure you've set minSdkVersion to 29:
For Expo:
{
"expo": {
"plugins": [
["expo-build-properties", { "android": { "minSdkVersion": 29 } }]
]
}
}Then rebuild: npx expo run:android or npx expo prebuild --clean && npx expo run:android
TypeScript errors about missing types
Some peer dependencies may not have type definitions. You can ignore these by adding to your tsconfig.json:
{
"compilerOptions": {
"skipLibCheck": true
}
}Contributing
See the contributing guide to learn how to contribute to the repository and the development workflow.
License
Apache-2.0
Made with ❤️ by Tether
