A demonstration of integrating @ton/walletkit into a React Native application using Expo. This project shows how to properly setup the required polyfills and configure WalletKit for cross-platform mobile development.
- Full @ton/walletkit integration with React Native
- Cross-platform storage adapter (SecureStore on native, fallback for web)
- TON Connect support with bridge communication
- Wallet management (create, restore, delete)
- Transaction signing and approval flow
- Balance checking
- Node.js 18+
- Expo CLI
- iOS Simulator or Android Emulator (or physical device)
@ton/walletkit depends on several Node.js APIs that are not available in React Native. This project implements three critical polyfills:
Located in lib/polyfills/buffer.js, this polyfill:
- Installs the
bufferpackage - Sets up global
Bufferobject for bothglobalandglobalThis - Enhances
Buffer.prototype.subarray()to return a Buffer instead of plain Uint8Array (required for @ton/core compatibility)
import { Buffer as BufferPolyfill } from 'buffer';
globalThis.Buffer = BufferPolyfill;
global.Buffer = BufferPolyfill;
// Enhance subarray to return Buffer
const originalSubarray = Buffer.prototype.subarray;
Buffer.prototype.subarray = function(start, end) {
const view = originalSubarray.call(this, start, end);
return Buffer.from(view.buffer, view.byteOffset, view.byteLength);
};Located in lib/polyfills/crypto.js, provides:
crypto.getRandomValues()viareact-native-get-random-valuescrypto.randomUUID()viaexpo-crypto
import { randomUUID } from 'expo-crypto';
import 'react-native-get-random-values';
if (typeof global.crypto === 'undefined') {
global.crypto = {};
}
global.crypto.randomUUID = randomUUID;Located in lib/polyfills/eventsource.js, enables Server-Sent Events (SSE) required for TON Connect bridge.
All polyfills are loaded in lib/polyfills/index.ts and imported in the app entry point (lib/index.js) before any other code runs.
npm installKey dependencies:
@ton/walletkit- TON wallet integration SDKbuffer- Buffer polyfillreact-native-get-random-values- Crypto random valuesexpo-crypto- UUID generationreact-native-sse- EventSource for bridge communicationexpo-secure-store- Secure storage for native platformsreact-native-fast-pbkdf2- PBKDF2 implementation for key derivation
Create the polyfill structure in your project:
lib/
├── polyfills/
│ ├── buffer.js # Buffer polyfill with subarray fix
│ ├── crypto.js # Crypto API polyfill
│ ├── eventsource.js # EventSource polyfill
│ └── index.ts # Import all polyfills
└── index.js # Entry point (imports polyfills first)
Import polyfills before any other code in your app entry point:
// lib/index.js
import 'expo-router/entry';
import './polyfills/index'; // Must be imported firstImplement a storage adapter compatible with WalletKit's interface. See lib/SecureStoreAdapter.ts for a example implementation:
import * as SecureStore from 'expo-secure-store';
import { Platform } from 'react-native';
export class SecureStoreAdapter {
private prefix: string;
private fallbackStorage: Map<string, any> = new Map();
private useSecureStore: boolean = Platform.OS !== 'web';
async get<T>(key: string): Promise<T | null> {
const fullKey = this.makeKey(key);
if (this.useSecureStore) {
const available = await SecureStore.isAvailableAsync();
if (available) {
const value = await SecureStore.getItemAsync(fullKey);
return value ? JSON.parse(value) : null;
}
}
return this.fallbackStorage.get(fullKey) || null;
}
async set<T>(key: string, value: T): Promise<void> {
const fullKey = this.makeKey(key);
const serialized = JSON.stringify(value);
if (this.useSecureStore) {
const available = await SecureStore.isAvailableAsync();
if (available) {
await SecureStore.setItemAsync(fullKey, serialized);
return;
}
}
this.fallbackStorage.set(fullKey, value);
}
// ... implement remove() and clear() similarly
}import { TonWalletKit, CHAIN, Signer, WalletV5R1Adapter } from '@ton/walletkit';
import { SecureStoreAdapter } from './lib/SecureStoreAdapter';
// Create storage adapter
const storage = new SecureStoreAdapter({ prefix: 'tonwalletkit:' });
// Initialize WalletKit
const kit = new TonWalletKit({
storage,
bridge: {
bridgeUrl: 'https://bridge.tonapi.io/bridge',
heartbeatInterval: 15000,
reconnectInterval: 15000,
},
network: CHAIN.MAINNET,
});
// Wait for initialization
await kit.waitForReady();
// Create wallet from mnemonic
const signer = await Signer.fromMnemonic(mnemonicPhrase);
const wallet = await WalletV5R1Adapter.create(signer, {
client: kit.getApiClient(),
network: CHAIN.MAINNET,
});
// Add wallet to kit
await kit.addWallet(wallet);Handle TON Connect events:
// Connection requests
kit.onConnectRequest((event) => {
const wallets = kit.getWallets();
event.walletAddress = wallets[0].getAddress();
kit.approveConnectRequest(event);
});
// Transaction requests
kit.onTransactionRequest((event) => {
// Show approval UI to user
Alert.alert('Approve Transaction?', '', [
{
text: 'Approve',
onPress: () => kit.approveTransactionRequest(event),
},
{
text: 'Cancel',
onPress: () => kit.rejectTransactionRequest(event),
},
]);
});
// Sign data requests
kit.onSignDataRequest((event) => {
// Handle sign data request
});
// Disconnect
kit.onDisconnect(() => {
// Handle disconnect
});npx expo startThen press:
ifor iOS simulatorafor Android emulatorwfor web
Or:
# iOS
npx expo run:ios
# Android
npx expo run:androidlib/polyfills/- All required polyfills for @ton/walletkitlib/SecureStoreAdapter.ts- Cross-platform storage adapterapp/(tabs)/index.tsx- Main WalletKit integration exampleapp.json- Expo configuration with required plugins
- Polyfills must be loaded first - Import them in your entry point before any other code
- Buffer.subarray enhancement is critical - Without it, @ton/core operations will fail
- EventSource is required for bridge - TON Connect won't work without it
- SecureStore is iOS/Android only - Provide a fallback (memory or localStorage) for web
- Development builds required - Native modules prevent using Expo Go
MIT