Skip to main content
The Phantom Connect React Native SDK provides chain-specific hooks (useSolana and useEthereum) for signing and sending transactions optimized for mobile platforms.

Chain-specific transaction hooks

Solana transactions (useSolana)

import React from "react";
import { View, Button, Alert } from "react-native";
import { useSolana } from "@phantom/react-native-sdk";

function SolanaTransactions() {
  const { solana } = useSolana();

  const sendTransaction = async () => {
    try {
      // Sign and send transaction
      const result = await solana.signAndSendTransaction(transaction);
      Alert.alert("Success", `Transaction sent: ${result.hash}`);
    } catch (error) {
      Alert.alert("Error", `Transaction failed: ${error.message}`);
    }
  };

  const signOnly = async () => {
    try {
      // Just sign (without sending)
      const signedTx = await solana.signTransaction(transaction);
      Alert.alert("Success", "Transaction signed!");
    } catch (error) {
      Alert.alert("Error", `Signing failed: ${error.message}`);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <Button title="Send Transaction" onPress={sendTransaction} />
      <Button title="Sign Only" onPress={signOnly} />
    </View>
  );
}

Ethereum transactions (useEthereum)

import React from "react";
import { View, Button, Alert } from "react-native";
import { useEthereum } from "@phantom/react-native-sdk";

function EthereumTransactions() {
  const { ethereum } = useEthereum();

  const sendTransaction = async () => {
    try {
      const result = await ethereum.sendTransaction({
        to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
        value: "1000000000000000000", // 1 ETH in wei
        gas: "21000",
      });
      Alert.alert("Success", `ETH sent: ${result.hash}`);
    } catch (error) {
      Alert.alert("Error", `Transaction failed: ${error.message}`);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <Button title="Send ETH" onPress={sendTransaction} />
    </View>
  );
}

Complete mobile examples

Sequential transactions for embedded wallets: When using embedded wallets with Phantom Connect, bundled or parallel transaction execution (such as Jito bundles) isn’t supported. Each transaction must be submitted individually and confirmed before the next can be sent. This is required for spending limit policy enforcement, as the backend must simulate each transaction independently to accurately calculate spending limits.

Solana transaction with mobile UI

import React, { useState } from "react";
import { View, Button, TextInput, Alert, Text, StyleSheet } from "react-native";
import { useSolana } from "@phantom/react-native-sdk";
import { Transaction, SystemProgram, PublicKey, LAMPORTS_PER_SOL, Connection } from "@solana/web3.js";

function SolanaMobileTransfer() {
  const { solana } = useSolana();
  const [recipient, setRecipient] = useState("");
  const [amount, setAmount] = useState("0.001");
  const [isLoading, setIsLoading] = useState(false);

  const sendSOL = async () => {
    if (!recipient || !amount) {
      Alert.alert("Error", "Please fill in all fields");
      return;
    }

    setIsLoading(true);
    try {
      // Get connection and recent blockhash
      const connection = new Connection("https://api.mainnet-beta.solana.com");
      const { blockhash } = await connection.getLatestBlockhash();
      
      const fromAddress = await solana.getPublicKey();
      const transferInstruction = SystemProgram.transfer({
        fromPubkey: new PublicKey(fromAddress),
        toPubkey: new PublicKey(recipient),
        lamports: parseFloat(amount) * LAMPORTS_PER_SOL,
      });

      const transaction = new Transaction({
        recentBlockhash: blockhash,
        feePayer: new PublicKey(fromAddress),
      }).add(transferInstruction);

      const result = await solana.signAndSendTransaction(transaction);
      Alert.alert(
        "Success!", 
        `Sent ${amount} SOL\nTransaction: ${result.hash}`,
        [{ text: "OK" }]
      );
    } catch (error) {
      Alert.alert("Error", `Failed to send SOL: ${error.message}`);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Send Solana</Text>
      
      <TextInput
        style={styles.input}
        placeholder="Recipient Address"
        value={recipient}
        onChangeText={setRecipient}
        multiline
      />
      
      <TextInput
        style={styles.input}
        placeholder="Amount (SOL)"
        value={amount}
        onChangeText={setAmount}
        keyboardType="decimal-pad"
      />
      
      <Button
        title={isLoading ? "Sending..." : "Send SOL"}
        onPress={sendSOL}
        disabled={isLoading}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 20,
    gap: 15,
  },
  title: {
    fontSize: 20,
    fontWeight: "bold",
    marginBottom: 10,
  },
  input: {
    borderWidth: 1,
    borderColor: "#ccc",
    borderRadius: 8,
    padding: 12,
    fontSize: 16,
  },
});

Ethereum transaction with mobile UI

import React, { useState } from "react";
import { View, Button, TextInput, Alert, Text, StyleSheet } from "react-native";
import { useEthereum } from "@phantom/react-native-sdk";

function EthereumMobileTransfer() {
  const { ethereum } = useEthereum();
  const [recipient, setRecipient] = useState("");
  const [amount, setAmount] = useState("0.001");
  const [isLoading, setIsLoading] = useState(false);

  const sendETH = async () => {
    if (!recipient || !amount) {
      Alert.alert("Error", "Please fill in all fields");
      return;
    }

    setIsLoading(true);
    try {
      const weiAmount = (parseFloat(amount) * 1e18).toString(); // Convert ETH to wei
      
      const result = await ethereum.sendTransaction({
        to: recipient,
        value: weiAmount,
        gas: "21000",
      });
      
      Alert.alert(
        "Success!",
        `Sent ${amount} ETH\nTransaction: ${result.hash}`,
        [{ text: "OK" }]
      );
    } catch (error) {
      Alert.alert("Error", `Failed to send ETH: ${error.message}`);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Send Ethereum</Text>
      
      <TextInput
        style={styles.input}
        placeholder="Recipient Address (0x...)"
        value={recipient}
        onChangeText={setRecipient}
        autoCapitalize="none"
      />
      
      <TextInput
        style={styles.input}
        placeholder="Amount (ETH)"
        value={amount}
        onChangeText={setAmount}
        keyboardType="decimal-pad"
      />
      
      <Button
        title={isLoading ? "Sending..." : "Send ETH"}
        onPress={sendETH}
        disabled={isLoading}
      />
    </View>
  );
}