This file provides guidance to AI agents when working with code in this repository.
RN CLI Wallet (React Native CLI Wallet) is a comprehensive wallet application demonstrating integration with WalletConnect WalletKit SDK. It showcases how to build a multi-chain wallet supporting:
- EIP155 (Ethereum & 17 EVM-compatible chains)
- Sui blockchain
- TON blockchain
- Tron blockchain
This is a production-ready reference implementation for developers building wallet applications with WalletConnect.
- React Native: 0.82.0
- React: 19.1.1
- TypeScript: ^5.8.3
- @reown/walletkit: Main WalletConnect wallet SDK
- @walletconnect/react-native-compat: React Native compatibility layer
- @walletconnect/pay: WalletConnect Pay integration
- ethers (5.8.0): Ethereum/EVM interactions
- @mysten/sui: Sui blockchain support
- @ton/core, @ton/crypto, @ton/ton: TON blockchain
- tronweb: Tron blockchain
- @react-navigation: Navigation (stack, bottom-tabs)
- valtio: Reactive state management (proxy-based)
- react-native-mmkv: Fast key-value storage
- react-native-quick-crypto: Cryptographic operations
- bip39: Mnemonic seed phrase generation
- react-native-vision-camera: QR code scanning
- @sentry/react-native: Error tracking
rn_cli_wallet/
├── src/
│ ├── screens/
│ │ ├── App.tsx # Root component with initialization
│ │ ├── Connections/ # Active wallet connections
│ │ ├── Scan/ # QR code scanning
│ │ ├── SessionDetail/ # Session management
│ │ ├── Settings/ # Wallet & chain settings
│ │ └── LogList/ # Event logging
│ ├── modals/
│ │ ├── SessionSignModal.tsx
│ │ ├── SessionSendTransactionModal.tsx
│ │ ├── SessionAuthenticateModal.tsx
│ │ ├── ImportWalletModal.tsx
│ │ ├── SessionSuiSignAndExecuteTransactionModal.tsx
│ │ ├── SessionTonSignDataModal.tsx
│ │ ├── SessionTonSendMessageModal.tsx
│ │ ├── SessionSignTronModal.tsx
│ │ ├── PaymentOptionsModal/ # WalletConnect Pay
│ │ └── RequestModal.tsx
│ ├── navigators/
│ │ ├── RootStackNavigator.tsx
│ │ ├── HomeTabNavigator.tsx
│ │ ├── ConnectionsStack.tsx
│ │ └── SettingsStack.tsx
│ ├── components/
│ │ ├── Modal/
│ │ ├── Button.tsx # Primary pressable (use instead of TouchableOpacity)
│ │ ├── ActionButton.tsx
│ │ ├── Card.tsx
│ │ ├── ConnectButton.tsx
│ │ ├── VerifyInfoBox.tsx # EIP-4361 verification
│ │ └── Text.tsx
│ ├── store/
│ │ ├── SettingsStore.ts # App state (addresses, sessions)
│ │ ├── ModalStore.ts # Modal state
│ │ └── PaymentStore.ts # Payment state
│ ├── lib/ # Chain-specific wallet implementations
│ │ ├── EIP155Lib.ts # Ethereum wallet (ethers.js)
│ │ ├── SuiLib.ts # Sui wallet
│ │ ├── TonLib.ts # TON wallet
│ │ └── TronLib.ts # Tron wallet
│ ├── utils/
│ │ ├── WalletKitUtil.ts # WalletKit initialization
│ │ ├── EIP155RequestHandlerUtil.ts
│ │ ├── SuiRequestHandlerUtil.ts
│ │ ├── TonRequestHandlerUtil.ts
│ │ ├── TronRequestHandlerUtil.ts
│ │ └── ...
│ ├── hooks/
│ │ ├── useInitializeWalletKit.ts
│ │ ├── useInitializePaySDK.ts
│ │ ├── useWalletKitEventsManager.ts
│ │ └── ...
│ ├── constants/ # Chain definitions
│ │ ├── Eip155.ts # 17 EVM chains
│ │ ├── Sui.ts
│ │ ├── Ton.ts
│ │ └── Tron.ts
│ └── assets/
├── android/
├── ios/
├── scripts/
└── package.json
Uses Valtio (proxy-based reactive state):
- SettingsStore: Wallet addresses, sessions, chain settings, device ID
- ModalStore: Modal visibility and request data
- PaymentStore: WalletConnect Pay state
React Navigation with:
- Bottom tabs: Connections, Scan, Settings, Logs
- Stack navigators for each tab section
- Modal screens for signing requests
EIP155 (17 chains)
- Ethereum, Polygon, Arbitrum, Optimism, Base
- Avalanche, BSC, Fantom, zkSync, Gnosis
- Moonbeam, Celo, Aurora, Zora, and more
Other Chains
- Sui (mainnet, testnet, devnet)
- TON (mainnet, testnet)
- Tron
- Import wallet via mnemonic or private key
- HD key derivation for multiple accounts
- Transaction signing for all supported chains
- Message signing (personal_sign, eth_signTypedData, etc.)
- Chain switching and dynamic updates
- Session management and pairing via QR code
- Multi-chain session support
- Deep linking (WalletConnect URI parsing)
- WalletConnect Pay SDK
- EIP-4361 (Sign-In with Ethereum) verification
SessionSignModal: Message signingSessionSendTransactionModal: EVM transactionsSessionAuthenticateModal: SIWE authenticationSessionSuiSignAndExecuteTransactionModal: Sui transactionsSessionTonSignDataModal/SessionTonSendMessageModal: TONSessionSignTronModal: Tron signing
Required in .env:
ENV_PROJECT_ID="" # WalletConnect Project ID (required)
ENV_SENTRY_DSN="" # Sentry error tracking (optional)
ENV_TON_CENTER_API_KEY="" # TON blockchain API key (optional)
ENV_BLOCKCHAIN_API_URL="" # Blockchain API URL (to get wallet balances)
SENTRY_DISABLE_AUTO_UPLOAD=true # Disable Sentry auto upload for Android builds- Node.js >= 20
- Yarn 3.8.7
- Ruby 3.3.0 (for iOS/CocoaPods)
- Xcode & Android Studio
yarn install
cp .env.example .env
# Edit .env with your project ID
yarn android # or yarn iosyarn start: Start Metro bundleryarn android: Run on Android (debug)yarn ios: Run on iOS (debug scheme)yarn ios:internal: Run iOS internal schemeyarn android:build: Build release APKyarn lint: Run ESLintyarn test: Run Jest testsyarn e2e: Run Maestro E2E tests
yarn run copy:debug # Debug configuration
yarn run copy:internal # Internal testing
yarn run copy:production # Production release// hooks/useInitializeWalletKit.ts
import { WalletKit } from '@reown/walletkit';
const walletKit = await WalletKit.init({
core,
metadata: {
name: 'RN Wallet',
// ...
},
});// utils/EIP155RequestHandlerUtil.ts
export async function approveEIP155Request(
requestEvent: SignClientTypes.EventArguments['session_request']
) {
const { params, id } = requestEvent;
// Handle different methods: personal_sign, eth_sendTransaction, etc.
}// lib/EIP155Lib.ts
import { Wallet } from 'ethers';
class EIP155Lib {
wallet: Wallet;
signMessage(message: string) {
return this.wallet.signMessage(message);
}
sendTransaction(transaction: TransactionRequest) {
return this.wallet.sendTransaction(transaction);
}
}import { proxy, useSnapshot } from 'valtio';
export const SettingsStore = proxy({
eip155Address: '',
suiAddress: '',
tonAddress: '',
tronAddress: '',
sessions: [],
});
// In component
const { eip155Address } = useSnapshot(SettingsStore);Always use the custom Button component instead of TouchableOpacity or Pressable.
The app uses a custom Button component that wraps PressableScale from the pressto package, providing smooth scale animations on press.
import { Button } from '@/components/Button';
// Basic usage
<Button onPress={handlePress} style={styles.myButton}>
<Text>Press me</Text>
</Button>
// With disabled state
<Button onPress={handlePress} disabled={isLoading}>
<Text>Submit</Text>
</Button>
// With hitSlop for larger touch targets
<Button onPress={handlePress} hitSlop={40}>
<Icon name="close" />
</Button>Button Props:
children: React.ReactNode (required)onPress: () => void (required)style: StyleProp (optional)disabled: boolean (optional, default: false)hitSlop: number | Insets (optional)testID: string (optional)
Why not TouchableOpacity?
PressableScaleprovides a native-feeling scale animation- Consistent UX across the app
- Better performance with Reanimated-powered animations
Important: The app wraps the root and modal content with GestureHandlerRootView (required for pressto to work on Android).
Always increment the versionCode when creating a new feature, fix, or PR:
The Android versionCode is located in android/app/build.gradle. Before submitting a PR, increment this value by 1:
android {
defaultConfig {
versionCode 57 // Increment this value
}
}This ensures each build has a unique version identifier for distribution and updates.
Always run these checks and fix any errors before committing:
yarn format # Format code with Prettier
yarn lint # Check for ESLint errors
npx tsc --noEmit # Check for TypeScript errors- Use TypeScript strict mode
- Follow ESLint + Prettier configuration
- Use path aliases (
@/) for imports - Handle all blockchain errors gracefully
- Show toast notifications for user feedback
- Log important events via
LogStore(e.g.,LogStore.log(),LogStore.warn(),LogStore.error()) - Test signing flows on testnets before mainnet