React Native library providing wallet context and WDK service integration
npm install @spacesops/wdk-react-native-providerA React Native library providing wallet context and WDK (Wallet Development Kit) service integration for building secure, multi-chain cryptocurrency wallets.
- Multi-chain Support: Bitcoin, Ethereum, Polygon, Arbitrum, TON, Solana, and Tron
- Multi-asset Management: BTC, USDT, XAUT, USAT, 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
- Android minSdkVersion: 29 or higher
- iOS Deployment Target: 15.1 or higher
- React Native: 0.81.0+
``sh`
npm install @tetherto/wdk-react-native-provider
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:
`json`
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"android": {
"minSdkVersion": 29
}
}
]
]
}
}
For bare React Native projects:
Update android/build.gradle:
`gradle`
buildscript {
ext {
minSdkVersion = 29
// ... other config
}
}
This library requires several peer dependencies. Install them using:
`sh`
npm install \
@craftzdog/react-native-buffer \
@react-native-async-storage/async-storage \
@spacesops/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-http
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:
`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:
`js
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.
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:
`tsx
// 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 (
indexer: {
apiKey: 'your-api-key-here',
url: 'https://your-indexer-url.com',
},
chains: CHAINS_CONFIG,
enableCaching: true, // Optional: enable caching for better performance
}}
>
);
}
`
For standard React Native projects:
Wrap your root component:
`tsx
// 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 (
indexer: {
apiKey: 'your-api-key-here',
url: 'https://your-indexer-url.com',
},
chains: CHAINS_CONFIG,
enableCaching: true, // Optional: enable caching for balances and transactions
}}
>
);
}
`
Access wallet functionality using the useWallet hook:
`tsx
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
}
if (!wallet) {
return (
);
}
if (!isUnlocked) {
return ;
}
return (
);
}
`
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.
Hook to access wallet context and functionality.
Returns:
`typescript
{
// 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
clearWallet: () => Promise
clearError: () => void;
refreshWalletBalance: () => Promise
refreshTransactions: () => Promise
unlockWallet: () => Promise
}
`
Low-level service for direct wallet operations. Available as a singleton.
`tsx
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
);
`
The library supports multiple blockchain networks, each with its own configuration structure.
The chains configuration object supports the following blockchain networks:
`typescript`
const chains = {
ethereum?: EVMChainConfig;
arbitrum?: EVMChainConfig;
polygon?: EVMChainConfig;
ton?: TONChainConfig;
bitcoin?: BitcoinChainConfig;
tron?: TronChainConfig;
}
For Ethereum, Polygon, and Arbitrum:
`typescript`
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
};
`typescript`
const tonConfig = {
tonApiClient: {
url: 'https://tonapi.io',
},
tonClient: {
url: 'https://toncenter.com/api/v2/jsonRPC',
},
paymasterToken: {
address: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs',
},
transferMaxFee: 1000000000,
};
`typescript`
const bitcoinConfig = {
host: 'api.ordimint.com',
port: 50001,
};
`typescript`
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',
},
};
`typescript
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 (
indexer: {
apiKey: 'your-indexer-api-key',
url: 'https://your-indexer-url.com',
},
chains,
enableCaching: true, // Optional: enable caching for better performance
}}
>
);
}
`
`tsx
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...');
}
}
`
`typescript
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 // Tron
`
`typescript
import { AssetTicker } from '@tetherto/wdk-react-native-provider';
// Available assets:
AssetTicker.BTC // Bitcoin
AssetTicker.USDT // Tether USD
AssetTicker.XAUT // Tether Gold
AssetTicker.USAT // Tether USAT
`
This library is written in TypeScript and includes complete type definitions. Import types as needed:
`typescript
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 @spacesops/pear-wrk-wdk package. TypeScript will infer these types when you use them in the WalletProviderConfig, so explicit imports are typically not needed.
- 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
See CONTRIBUTING.md for development workflow and guidelines.
`sh`
npm run prepare
`sh`
npm run typecheck
`sh`
npm run lint
`sh`
npm test
If you're experiencing issues, verify you've completed all setup steps:
For Expo projects:
1. ✅ Install the package: npm install @tetherto/wdk-react-native-providerapp.json
2. ✅ Install all peer dependencies (see Peer Dependencies)
3. ✅ Configure Android minSdkVersion to 29 in :`
json`
{
"expo": {
"plugins": [
["expo-build-properties", { "android": { "minSdkVersion": 29 } }]
]
}
}
metro.config.js
4. ✅ Configure Metro polyfills in :`
js`
const { configureMetroForWDK } = require('@tetherto/wdk-react-native-provider/metro-polyfills');
const config = getDefaultConfig(__dirname);
const configWdk = configureMetroForWDK(config);
module.exports = configWdk;
WalletProvider
5. ✅ Add to app/_layout.tsx with proper configuseWallet()
6. ✅ Use hook in your componentsisInitialized
7. ✅ Check before creating wallets
For bare React Native projects:
1. ✅ Install the package and peer dependencies
2. ✅ Set minSdkVersion to 29 in android/build.gradlemetro.config.js
3. ✅ Configure Metro polyfills in WalletProvider
4. ✅ Wrap your root component with npx react-native run-android
5. ✅ Rebuild native code: or npx react-native run-ios
The WDK service is initialized automatically when the WalletProvider mounts. If you see this error, ensure:
1. Your component is wrapped with WalletProviderisInitialized
2. The provider's config is properly set
3. You're checking before performing wallet operations:
`tsx
const { isInitialized, createWallet } = useWallet();
if (isInitialized) {
await createWallet({ name: 'My Wallet' });
}
`
Ensure a wallet has been created or imported before attempting transactions:
`tsx`
const { wallet, createWallet } = useWallet();
if (!wallet) {
await createWallet({ name: 'My Wallet' });
}
If you see errors like Unable to resolve "stream" or other Node.js core modules:
1. Ensure you've configured Metro polyfills correctly (see Metro Configuration)
2. Clear Metro cache: npx expo start --clear or npx react-native start --reset-cachenode_modules
3. Delete and reinstall: rm -rf node_modules && npm installnpx expo prebuild --clean
4. For Expo: Run prebuild if using custom native modules:
This usually means minSdkVersion is too low. Ensure you've set minSdkVersion to 29:
For Expo:
`json`
{
"expo": {
"plugins": [
["expo-build-properties", { "android": { "minSdkVersion": 29 } }]
]
}
}
Then rebuild: npx expo run:android or npx expo prebuild --clean && npx expo run:android
Some peer dependencies may not have type definitions. You can ignore these by adding to your tsconfig.json:
`json``
{
"compilerOptions": {
"skipLibCheck": true
}
}
See the contributing guide to learn how to contribute to the repository and the development workflow.
Apache-2.0
---
Made with ❤️ by Tether