> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getpara.com/llms.txt
> Use this file to discover all available pages before exploring further.

# External Wallets

> Connect and authenticate users with external wallets like MetaMask and Phantom in Flutter.

export const Card = ({imgUrl, title, description, href, horizontal = false, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  const handleClick = e => {
    e.preventDefault();
    if (newTab) {
      window.open(href, '_blank', 'noopener,noreferrer');
    } else {
      window.location.href = href;
    }
  };
  return <div className={`not-prose relative my-2 p-[1px] rounded-xl transition-all duration-300 ${isHovered ? 'bg-gradient-to-r from-[#FF4E00] to-[#874AE3]' : 'bg-gray-200'}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <a href={href} onClick={handleClick} className={`not-prose flex ${horizontal ? 'flex-row' : 'flex-col'} font-normal h-full bg-white overflow-hidden w-full cursor-pointer rounded-[11px] no-underline`}>
        {imgUrl && <div className={`relative overflow-hidden flex-shrink-0 ${horizontal ? 'w-[30%] rounded-l-[11px]' : 'w-full'}`} onClick={e => e.stopPropagation()}>
            <img src={imgUrl} alt={title} className="w-full h-full object-cover pointer-events-none select-none" draggable="false" />
            <div className="absolute inset-0 pointer-events-none" />
          </div>}
        <div className={`flex-grow px-6 py-5 ${horizontal ? 'w-[70%]' : 'w-full'} flex flex-col ${horizontal && imgUrl ? 'justify-center' : 'justify-start'}`}>
          {title && <h2 className="font-semibold text-base text-gray-800 m-0">{title}</h2>}
          {description && <div className={`font-normal text-gray-500 re leading-6 ${horizontal || !imgUrl ? 'mt-0' : 'mt-1'}`}>
              <p className="m-0 text-xs">{description}</p>
            </div>}
        </div>
      </a>
    </div>;
};

export const DemoCallout = ({variant = "card", title = "Interactive Demo", description = "Try the live demo to explore authentication flows and customize your modal before integrating."}) => {
  if (variant === "subtle") {
    return <div className="not-prose my-4 px-4 py-3 rounded-xl border border-orange-200 bg-orange-50/50">
        <div className="flex items-center justify-between gap-4">
          <p className="text-sm text-gray-700 m-0">
            {description}
          </p>
          <a href="https://demo.getpara.com/" target="_blank" rel="noopener noreferrer" className="not-prose flex-shrink-0 inline-flex items-center gap-2 px-3 py-1.5 text-xs font-semibold text-orange-600 bg-white border border-orange-200 rounded-lg hover:bg-orange-50 transition-colors no-underline">
            Try Demo
            <svg width="12" height="12" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path d="M40 472L472 40M472 40H83.2M472 40V428.8" stroke="currentColor" strokeWidth="66.6667" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          </a>
        </div>
      </div>;
  }
  return <a href="https://demo.getpara.com/" target="_blank" rel="noopener noreferrer" className="not-prose block my-4 p-5 rounded-xl bg-gradient-to-r from-orange-600 via-pink-600 to-purple-600 hover:opacity-95 transition-opacity no-underline">
      <div className="flex items-center justify-between gap-4">
        <div>
          <h3 className="text-base font-semibold text-white m-0">{title}</h3>
          <p className="text-xs text-white/80 mt-1 m-0">{description}</p>
        </div>
        <div className="flex-shrink-0 w-8 h-8 rounded-full bg-white/20 flex items-center justify-center">
          <svg width="14" height="14" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M40 472L472 40M472 40H83.2M472 40V428.8" stroke="white" strokeWidth="66.6667" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </div>
      </div>
    </a>;
};

export const Link = ({href, label, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  return <a href={href} target={newTab ? '_blank' : '_self'} rel={newTab ? 'noopener noreferrer' : undefined} className="not-prose inline-block relative text-black font-semibold cursor-pointer border-b-0 no-underline" onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      {label}
      <span className={`absolute left-0 bottom-0 w-full rounded-sm bg-gradient-to-r from-orange-600 to-purple-600 transition-all duration-300 ${isHovered ? 'h-0.5' : 'h-px'}`} />
    </a>;
};

## Overview

Para supports authentication with external wallets, allowing users to connect their existing MetaMask, Phantom, or other wallets to authenticate with your Flutter application. The Flutter SDK maintains blockchain packages (web3dart, solana\_web3) to provide direct access to external wallet functionality while integrating with Para's unified wallet architecture.

This guide covers how to implement external wallet authentication and interaction using Para's connector classes.

## Prerequisites

Before implementing external wallet support, ensure you have:

1. Para SDK set up in your Flutter project (see the [Setup Guide](/v3/flutter/setup))
2. Deep linking configured for your app
3. Target external wallet apps installed on the device

## Deep Link Configuration

External wallet authentication requires deep linking to redirect users back to your app after wallet interaction. Configure this in both iOS and Android.

### Android Configuration

Add an intent filter to your `android/app/src/main/AndroidManifest.xml`:

```xml theme={null}
<activity
    android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
    android:exported="true">
    <intent-filter android:label="flutter_web_auth_2">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="yourapp" />
    </intent-filter>
</activity>
```

### iOS Configuration

Add your URL scheme to `ios/Runner/Info.plist`:

```xml theme={null}
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>yourapp</string>
    </array>
  </dict>
</array>
```

## External Wallet Authentication

Para provides a unified method for external wallet authentication:

```dart theme={null}
import 'package:para/para.dart';

Future<void> authenticateWithExternalWallet(
  String externalAddress,
  String walletType,
) async {
  try {
    // Login with external wallet address
    await para.loginExternalWallet(
      externalAddress: externalAddress,
      type: walletType, // "EVM" or "SOLANA"
    );

    // Check if authentication was successful
    final isActive = await para.isSessionActive().future;
    if (isActive) {
      final wallets = await para.fetchWallets().future;
      print('Authenticated with ${wallets.length} wallets');
    }
  } catch (e) {
    print('External wallet authentication failed: $e');
  }
}
```

## Working with Specific Wallets

Para provides dedicated connectors for popular external wallets:

### MetaMask Integration

Para includes a MetaMask connector for EVM interactions:

```dart theme={null}
import 'package:para/para.dart';

class MetaMaskService {
  late ParaMetaMaskConnector _connector;

  void initialize() {
    _connector = ParaMetaMaskConnector(
      para: para,
      appUrl: 'https://yourapp.com',
      appScheme: 'yourapp',
    );
  }

  Future<void> connectMetaMask() async {
    try {
      await _connector.connect();
      print('MetaMask connected');
    } catch (e) {
      print('Failed to connect MetaMask: $e');
    }
  }

  Future<String> signMessage(String message) async {
    if (_connector.accounts.isEmpty) {
      throw Exception('No accounts connected');
    }

    final signature = await _connector.signMessage(
      message,
      _connector.accounts.first,
    );

    return signature;
  }

  Future<String> sendTransaction({
    required String toAddress,
    required BigInt value,
  }) async {
    if (_connector.accounts.isEmpty) {
      throw Exception('No accounts connected');
    }

    final transaction = Transaction(
      from: EthereumAddress.fromHex(_connector.accounts.first),
      to: EthereumAddress.fromHex(toAddress),
      value: EtherAmount.inWei(value),
      maxGas: 100000,
      gasPrice: EtherAmount.inWei(BigInt.from(20000000000)), // 20 Gwei
    );

    final txHash = await _connector.sendTransaction(
      transaction,
      _connector.accounts.first,
    );

    return txHash;
  }
}
```

### Phantom Integration

Para includes a Phantom connector for Solana interactions:

```dart theme={null}
import 'package:para/para.dart';
import 'package:solana_web3/solana_web3.dart';

class PhantomService {
  late ParaPhantomConnector _connector;

  void initialize() {
    _connector = ParaPhantomConnector(
      para: para,
      appUrl: 'https://yourapp.com',
      appScheme: 'yourapp',
    );
  }

  Future<void> connectPhantom() async {
    try {
      await _connector.connect();
      print('Phantom connected');
    } catch (e) {
      print('Failed to connect Phantom: $e');
    }
  }

  Future<String> signMessage(String message) async {
    final signature = await _connector.signMessage(message);
    return signature;
  }

  /// Sign a transaction using serialized transaction bytes
  /// Returns: Base58 encoded signed transaction that must be sent to the network
  Future<String> signTransactionBytes(Uint8List transactionBytes) async {
    final signedTxBase58 = await _connector.signTransactionBytes(transactionBytes);
    return signedTxBase58;
  }
}
```

## Example: Complete External Wallet Flow

Here's a complete example showing external wallet authentication and usage:

```dart theme={null}
import 'package:flutter/material.dart';
import 'package:para/para.dart';

class ExternalWalletScreen extends StatefulWidget {
  @override
  _ExternalWalletScreenState createState() => _ExternalWalletScreenState();
}

class _ExternalWalletScreenState extends State<ExternalWalletScreen> {
  ParaMetaMaskConnector? _metamaskConnector;
  ParaPhantomConnector? _phantomConnector;
  bool _isConnected = false;

  @override
  void initState() {
    super.initState();
    _initializeConnectors();
  }

  void _initializeConnectors() {
    _metamaskConnector = ParaMetaMaskConnector(
      para: para,
      appUrl: 'https://yourapp.com',
      appScheme: 'yourapp',
    );

    _phantomConnector = ParaPhantomConnector(
      para: para,
      appUrl: 'https://yourapp.com',
      appScheme: 'yourapp',
    );
  }

  Future<void> _connectMetaMask() async {
    try {
      await _metamaskConnector!.connect();
      setState(() => _isConnected = true);

      // Check if Para session is active after connection
      final isActive = await para.isSessionActive().future;
      if (isActive) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('MetaMask connected and authenticated with Para!')),
        );
      }
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to connect MetaMask: $e')),
      );
    }
  }

  Future<void> _connectPhantom() async {
    try {
      await _phantomConnector!.connect();
      setState(() => _isConnected = true);

      // Check if Para session is active after connection
      final isActive = await para.isSessionActive().future;
      if (isActive) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Phantom connected and authenticated with Para!')),
        );
      }
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to connect Phantom: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('External Wallets')),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            Text(
              'Connect an external wallet to get started',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            SizedBox(height: 32),

            // MetaMask Connection
            ElevatedButton.icon(
              onPressed: _connectMetaMask,
              icon: Icon(Icons.account_balance_wallet),
              label: Text('Connect MetaMask'),
              style: ElevatedButton.styleFrom(
                minimumSize: Size(double.infinity, 50),
              ),
            ),
            SizedBox(height: 16),

            // Phantom Connection
            ElevatedButton.icon(
              onPressed: _connectPhantom,
              icon: Icon(Icons.account_balance_wallet),
              label: Text('Connect Phantom'),
              style: ElevatedButton.styleFrom(
                minimumSize: Size(double.infinity, 50),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
```

## Security Considerations

When working with external wallets:

1. **Validate Connections**: Always verify the wallet connection before performing operations
2. **Handle Errors Gracefully**: External wallet apps may not be installed or might reject connections
3. **User Experience**: Provide clear instructions for users on wallet installation and connection
4. **Permissions**: Ensure your app requests appropriate permissions for wallet interactions
5. **Deep Link Security**: Validate deep link callbacks to prevent malicious redirects

## Troubleshooting

Common issues and solutions:

### Connection Failures

```dart theme={null}
Future<void> connectWithRetry(VoidCallback connectFunction) async {
  int retries = 3;

  while (retries > 0) {
    try {
      await connectFunction();
      return;
    } catch (e) {
      retries--;
      if (retries == 0) {
        throw Exception('Failed to connect after 3 attempts: $e');
      }
      await Future.delayed(Duration(seconds: 2));
    }
  }
}
```

### Phantom Transaction Errors

When working with Phantom, use the transaction helper for proper encoding:

```dart theme={null}
import 'package:para/para.dart';

// Convert signed transaction for network submission
final signedTxBase64 = ParaPhantomTransactionHelper.signedTransactionToBase64(signedTxBase58);

// Send to Solana network
final signature = await solanaClient.rpcClient.sendTransaction(signedTxBase64);
```

### Wallet App Not Installed

```dart theme={null}
Future<bool> isWalletInstalled(String walletScheme) async {
  try {
    return await canLaunchUrl(Uri.parse('$walletScheme://'));
  } catch (e) {
    return false;
  }
}

Future<void> openWalletInstallPage(String storeUrl) async {
  if (await canLaunchUrl(Uri.parse(storeUrl))) {
    await launchUrl(Uri.parse(storeUrl));
  }
}
```

### Network Issues

Ensure you have the correct network selected in your external wallet:

* **Phantom**: Check that you're on the intended Solana network (mainnet-beta, devnet, testnet)
* **MetaMask**: Verify you're connected to the correct Ethereum network
* **Transactions**: Use testnet/devnet for development to avoid spending real funds

## Key Features

Para's Flutter SDK provides:

* **Unified Architecture**: External wallets integrate seamlessly with Para's unified wallet system
* **Direct Blockchain Access**: Uses web3dart for Ethereum and solana\_web3 for Solana interactions
* **Deep Link Support**: Handles wallet app redirections automatically
* **Transaction Helpers**: Utility classes for transaction encoding/decoding
* **Error Handling**: Comprehensive error handling for wallet interactions

<Card>
  <h3>⚠️ Important Notes</h3>

  * External wallets use blockchain packages directly for maximum compatibility
  * The Flutter SDK maintains these dependencies to support external wallet features
  * Phantom's `signAndSendTransaction` method is deprecated - use `signTransactionBytes` instead
  * Always validate wallet connections before performing operations
</Card>

## Resources

For more information about external wallet integration:

<CardGroup cols={2}>
  <Card horizontal title="MetaMask Mobile Integration" imgUrl="/images/v3/general-wallets.png" href="https://docs.metamask.io/wallet/how-to/connect/set-up-sdk/mobile/" description="Official MetaMask mobile integration documentation." />

  <Card horizontal title="Phantom Deep Links" imgUrl="/images/v3/general-wallets.png" href="https://docs.phantom.app/integrating/deeplinks-ios-and-android" description="Phantom wallet deep linking documentation for mobile apps." />
</CardGroup>
