Below is a comprehensive guide to creating a mobile wallet application for Monero cryptocurrency using React Native for the frontend and PHP with MySQL for user authentication. This guide will cover setting up user authentication, integrating Monero transactions, and displaying transaction history.
- Initialize a new React Native project:
npx react-native init MoneroWallet cd MoneroWallet
- Install necessary libraries:
npm install axios monero-javascript
-
Set up your MySQL database and create a table for users:
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(255) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
-
Create your PHP files for user authentication.
config.php
<?php
$host = '127.0.0.1';
$db = 'monero_wallet';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?>
register.php
<?php
require 'config.php';
$username = $_POST['username'];
$email = $_POST['email'];
$password = password_hash($_POST['password'], PASSWORD_BCRYPT);
$stmt = $pdo->prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)");
if ($stmt->execute([$username, $email, $password])) {
echo json_encode(['status' => 'success', 'message' => 'User registered successfully']);
} else {
echo json_encode(['status' => 'error', 'message' => 'Registration failed']);
}
?>
login.php
<?php
require 'config.php';
$username = $_POST['username'];
$password = $_POST['password'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
echo json_encode(['status' => 'success', 'message' => 'Login successful', 'user' => $user]);
} else {
echo json_encode(['status' => 'error', 'message' => 'Invalid credentials']);
}
?>
Create a basic authentication form in React Native to register and log in users.
AuthScreen.js
import React, { useState } from 'react';
import { View, TextInput, Button, Text } from 'react-native';
import axios from 'axios';
const AuthScreen = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [message, setMessage] = useState('');
const handleLogin = () => {
axios.post('http://yourserver.com/login.php', {
username,
password,
})
.then(response => {
if (response.data.status === 'success') {
setMessage('Login successful');
} else {
setMessage('Login failed');
}
})
.catch(error => {
console.error(error);
setMessage('An error occurred');
});
};
return (
<View>
<TextInput
placeholder="Username"
value={username}
onChangeText={setUsername}
autoCapitalize="none"
/>
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button title="Login" onPress={handleLogin} />
{message ? <Text>{message}</Text> : null}
</View>
);
};
export default AuthScreen;
Setting up the Monero wallet:
SendTransaction.js
import React, { useState } from 'react';
import { View, TextInput, Button, Text } from 'react-native';
import { MoneroWalletFull, MoneroNetworkType } from 'monero-javascript';
const SendTransaction = () => {
const [wallet, setWallet] = useState(null);
const [recipientAddress, setRecipientAddress] = useState('');
const [amount, setAmount] = useState('');
const [status, setStatus] = useState('');
// Function to create and send a transaction
const sendTransaction = async () => {
if (!wallet) {
setStatus('Wallet not loaded');
return;
}
try {
const amountAtomicUnits = BigInt(amount) * BigInt(1e12);
const tx = await wallet.createTx({
address: recipientAddress,
amount: amountAtomicUnits.toString(),
accountIndex: 0,
subaddressIndex: 0
});
await wallet.commitTx(tx);
setStatus(`Transaction sent: ${tx.getTxId()}`);
} catch (error) {
setStatus(`Error: ${error.message}`);
}
};
const loadWallet = async () => {
let wallet = await MoneroWalletFull.createWallet({
path: "./wallets/wallet",
password: "supersecretpassword",
networkType: MoneroNetworkType.STAGENET,
mnemonic: "your mnemonic seed",
});
setWallet(wallet);
};
return (
<View>
<TextInput
placeholder="Recipient Address"
value={recipientAddress}
onChangeText={setRecipientAddress}
autoCapitalize="none"
/>
<TextInput
placeholder="Amount (XMR)"
value={amount}
onChangeText={setAmount}
keyboardType="numeric"
/>
<Button title="Load Wallet" onPress={loadWallet} />
<Button title="Send Transaction" onPress={sendTransaction} />
{status ? <Text>{status}</Text> : null}
</View>
);
};
export default SendTransaction;
Displaying transaction history:
TransactionHistory.js
import React, { useState, useEffect } from 'react';
import { View, Text, ScrollView } from 'react-native';
import { MoneroWalletFull, MoneroNetworkType } from 'monero-javascript';
const TransactionHistory = () => {
const [wallet, setWallet] = useState(null);
const [transactions, setTransactions] = useState([]);
const loadTransactions = async () => {
if (!wallet) {
const walletInstance = await MoneroWalletFull.createWallet({
path: "./wallets/wallet",
password: "supersecretpassword",
networkType: MoneroNetworkType.STAGENET,
mnemonic: "your mnemonic seed",
});
setWallet(walletInstance);
}
try {
const txs = await wallet.getTxs();
setTransactions(txs);
} catch (error) {
console.error('Failed to fetch transactions:', error);
}
};
useEffect(() => {
loadTransactions();
}, []);
return (
<ScrollView>
<Text>Transaction History:</Text>
{transactions.map((tx, index) => (
<View key={index} style={{ marginVertical: 10 }}>
<Text>Transaction ID: {tx.getTxId()}</Text>
<Text>Amount: {(tx.getAmount() / BigInt(1e12)).toString()} XMR</Text>
<Text>Fee: {(tx.getFee() / BigInt(1e12)).toString()} XMR</Text>
<Text>Status: {tx.isConfirmed() ? 'Confirmed' : 'Pending'}</Text>
</View>
))}
</ScrollView>
);
};
export default TransactionHistory;
App.js
import React from 'react';
import { View, Button } from 'react-native';
import AuthScreen from './AuthScreen';
import SendTransaction from './SendTransaction';
import TransactionHistory from './TransactionHistory';
const App = () => {
return (
<View style={{ padding: 20 }}>
<AuthScreen />
<Button title="Send Transaction" onPress={() => {/* Navigate to SendTransaction screen */}} />
<Button title="View Transaction History" onPress={() => {/* Navigate to TransactionHistory screen */}} />
</View>
);
};
export default App;
- Encryption: Ensure all sensitive data (like wallet seeds) is encrypted and stored securely.
- HTTPS: Use HTTPS for all communications between the app and your PHP backend.
- Testing: Use Monero’s testnet or stagenet during development to avoid real currency losses.
- Deployment: Test thoroughly on both iOS and Android devices, and deploy the app via app stores.
By following these steps, you will have a fully functional
To integrate Monero Wallet RPC for opening wallets for each user and storing the wallet ID in the database, follow these steps:
Ensure that the Monero Wallet RPC is running on your server. You can start it with the following command:
monero-wallet-rpc --wallet-dir /path/to/wallets --rpc-bind-port 18082 --rpc-login user:password --daemon-address node.moneroworld.com:18089
You'll need to modify the PHP backend to interact with the Monero Wallet RPC, create new wallets for users, and store their wallet IDs in the database.
config.php
<?php
$host = '127.0.0.1';
$db = 'monero_wallet';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
// Monero Wallet RPC configuration
$rpc_host = 'http://localhost:18082';
$rpc_user = 'user';
$rpc_pass = 'password';
function monero_rpc($method, $params = []) {
global $rpc_host, $rpc_user, $rpc_pass;
$data = [
'jsonrpc' => '2.0',
'id' => '0',
'method' => $method,
'params' => $params,
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $rpc_host . '/json_rpc');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_USERPWD, "$rpc_user:$rpc_pass");
$headers = [];
$headers[] = 'Content-Type: application/json';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception('Error: ' . curl_error($ch));
}
curl_close($ch);
return json_decode($result, true);
}
?>
register.php
<?php
require 'config.php';
$username = $_POST['username'];
$email = $_POST['email'];
$password = password_hash($_POST['password'], PASSWORD_BCRYPT);
try {
// Create a new Monero wallet
$wallet_name = "wallet_$username";
$response = monero_rpc('create_wallet', [
'filename' => $wallet_name,
'password' => 'walletpassword',
'language' => 'English',
]);
if (isset($response['error'])) {
throw new Exception($response['error']['message']);
}
// Store user in the database with the wallet ID
$stmt = $pdo->prepare("INSERT INTO users (username, email, password, wallet_id) VALUES (?, ?, ?, ?)");
if ($stmt->execute([$username, $email, $password, $wallet_name])) {
echo json_encode(['status' => 'success', 'message' => 'User registered successfully']);
} else {
throw new Exception('Registration failed');
}
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>
Database Schema Update:
Update your users
table to store the wallet ID:
ALTER TABLE users ADD COLUMN wallet_id VARCHAR(255) NOT NULL;
When a user registers, the wallet is created via the PHP backend, and the wallet ID is stored in the database. Your React Native app will continue to interact with the backend as before, with the added capability of interacting with the user's Monero wallet.
- Secure the RPC: Ensure that your Monero Wallet RPC is properly secured and only accessible from your backend server.
- Encryption: Encrypt sensitive data like wallet passwords.
- HTTPS: Use HTTPS for all communications between the React Native app and the backend.
You can expand this setup to include features like checking the wallet balance, sending transactions, and viewing transaction history by making additional RPC calls from your backend and exposing these as endpoints for the React Native app to consume.
This setup provides a secure and scalable way to manage Monero wallets for each user in your application.