Skip to content

NavidHosseini/flutter_phantom

Folders and files

NameName
Last commit message
Last commit date

Latest commit

author
navidhosseini
Nov 28, 2022
319b509 · Nov 28, 2022

History

14 Commits
Nov 26, 2022
Nov 28, 2022
Nov 26, 2022
Nov 26, 2022
Nov 26, 2022
Nov 28, 2022
Nov 26, 2022
Nov 26, 2022
Nov 26, 2022
Nov 26, 2022
Nov 28, 2022
Nov 26, 2022
Nov 28, 2022

Repository files navigation

flutter_phantom

  • flutter_phantomt is a package based on react native phantom wallet example that allows users to connect to Phantom Wallet from their Application.
  • This package is used to generate deeplink urls for Phantom Wallet to connect to your application.

Features

This package has all these provider methods implemented for easy to use:

Getting Started

We need to have deeplink for our application for handling returned data from phantom.

Usage

add flutter_phantom to pubspec.yaml

import 'package:flutter_phantom/flutter_phantom.dart';

initialise required Parameters.

  • appUrl A url used to fetch app metadata i.e. title, icon.
  • deepLink The URI where Phantom should redirect the user upon connection.(Deep Link)
  final FlutterPhantom phantom = FlutterPhantom(
    appUrl: "https://phantom.app",
    deepLink: "app://mydeapp",
  );

Example

 import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_phantom/flutter_phantom.dart';
import 'package:uni_links/uni_links.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:solana_web3/solana_web3.dart' as web3;
import 'package:solana_web3/programs/system.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Uri? _latestUri;
  late StreamSubscription _sub;
  String logger = "";
  bool isLoading = false;
  ScrollController scrollController = ScrollController();

  final FlutterPhantom phantom = FlutterPhantom(
    appUrl: "https://phantom.app",
    deepLink: "app://mydeapp",
  );

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

  @override
  void dispose() {
    _sub.cancel();
    super.dispose();
  }

  Future<void> _handleIncomingLinks() async {
    _sub = uriLinkStream.listen((Uri? link) {
      _latestUri = link;
      final queryParams = _latestUri?.queryParametersAll.entries.toList();

      if (queryParams!.isNotEmpty) {
        switch (queryParams[0].value[0].toString()) {
          case "onConnect":
            Map dataConnect = phantom.onConnectToWallet(queryParams);
            inspect(dataConnect);
            textLogger(dataConnect.toString());
            break;
          case "onSignAndSendTransaction":
            Map dataSignAndSendTransaction =
                phantom.onCreateSignAndSendTransactionReceive(queryParams);
            inspect(dataSignAndSendTransaction);
            textLogger(dataSignAndSendTransaction.toString());
            break;
          case "onDisconnect":
            String dataDisconnect = phantom.onDisconnectReceive();
            textLogger(dataDisconnect.toString());
            break;
          case "onSignTransaction":
            web3.Transaction dataTransactionWithSign =
                phantom.onSignTransactionReceive(queryParams);
            inspect(dataTransactionWithSign);
            textLogger("transaction sign");
            break;
          case "onSignAllTransaction":
            List<web3.Transaction> dataSignAllTransaction =
                phantom.onSignAllTransactionReceive(queryParams);
            inspect(dataSignAllTransaction);
            textLogger("All transactions sign");
            break;
          case "onSignMessage":
            Map dataOnSignMessage = phantom.onSignMessageReceive(queryParams);
            inspect(dataOnSignMessage);
            textLogger(dataOnSignMessage.toString());
            break;
          default:
        }
      }
    }, onError: (err) {
      // Handle exception by warning the user their action did not succeed
    });
  }

  void textLogger(String text) {
    if (logger.isEmpty) {
      setState(() {
        logger = '$text'
            '\n'
            '--------------------------------------------------------'
            '\n';
      });
    } else {
      setState(() {
        logger = '$logger'
            '$text'
            '\n'
            '--------------------------------------------------------'
            '\n';
      });
      Future.delayed(const Duration(milliseconds: 500), () {
        scrollController.animateTo(
          scrollController.position.maxScrollExtent,
          curve: Curves.easeOut,
          duration: const Duration(milliseconds: 500),
        );
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final cluster = web3.Cluster.devnet;
    final connection = web3.Connection(cluster);
    void connectToWallet() {
      try {
        setState(() {
          isLoading = true;
        });
        Uri uri = phantom.generateConnectUri(
            cluster: "devnet", redirect: "onConnect");
        setState(() {
          isLoading = false;
        });
        launchUrl(
          uri,
          mode: LaunchMode.externalNonBrowserApplication,
        );
      } catch (e) {
        setState(() {
          isLoading = false;
        });
        textLogger("error, $e");
      }
    }

    Future<web3.Transaction> createTransactionTransfer() async {
      final transaction = web3.Transaction(
          feePayer: phantom.phantomWalletPublicKey,
          recentBlockhash: (await connection.getLatestBlockhash()).blockhash);
      transaction.add(
        SystemProgram.transfer(
          fromPublicKey: phantom.phantomWalletPublicKey,
          toPublicKey: phantom.phantomWalletPublicKey,
          lamports: web3.solToLamports(1),
        ),
      );
      return transaction;
    }

    void signAndSendTransaction() async {
      try {
        setState(() {
          isLoading = true;
        });
        web3.Transaction transaction = await createTransactionTransfer();
        web3.Buffer transactionSerialize = transaction
            .serialize(const web3.SerializeConfig(requireAllSignatures: false));

        final url = phantom.generateSignAndSendTransactionUri(
            transaction: transactionSerialize,
            redirect: "onSignAndSendTransaction");
        setState(() {
          isLoading = false;
        });
        launchUrl(
          url,
          mode: LaunchMode.externalNonBrowserApplication,
        );
      } catch (e) {
        setState(() {
          isLoading = false;
        });
        textLogger("error,  make sure your connect to wallet \n \n <<$e>> ");
      }
    }

    void signAndSendTransactionToProgram() async {
      try {
        setState(() {
          isLoading = true;
        });
        final web3.PublicKey programId = web3.PublicKey.fromString(
            "DWX4sCH7wFiNPXDxyJBMCT5m84xXFznbhhq1rqi1Fxuk");
        var instruction = web3.TransactionInstruction(
          keys: [
            web3.AccountMeta(
              phantom.phantomWalletPublicKey,
              isSigner: true,
              isWritable: false,
            )
          ],
          programId: programId,
        );

        final transaction = web3.Transaction(
            feePayer: phantom.phantomWalletPublicKey,
            recentBlockhash: (await connection.getLatestBlockhash()).blockhash);
        transaction.add(instruction);

        web3.Buffer transactionSerialize = transaction
            .serialize(const web3.SerializeConfig(requireAllSignatures: false));
        final url = phantom.generateSignAndSendTransactionUri(
            transaction: transactionSerialize,
            redirect: "onSignAndSendTransaction");
        setState(() {
          isLoading = false;
        });
        launchUrl(
          url,
          mode: LaunchMode.externalNonBrowserApplication,
        );
      } catch (e) {
        setState(() {
          isLoading = false;
        });
        textLogger("error,  make sure your connect to wallet \n \n <<$e>> ");
      }
    }

    void disconnectToPhantom() {
      try {
        setState(() {
          isLoading = true;
        });
        var url = phantom.generateDisconnectUri(redirect: "onDisconnect");
        setState(() {
          isLoading = false;
        });
        launchUrl(
          url,
          mode: LaunchMode.externalNonBrowserApplication,
        );
      } catch (e) {
        setState(() {
          isLoading = false;
        });
        textLogger("error,  you are not connect to wallet");
      }
    }

    void signTransaction() async {
      try {
        setState(() {
          isLoading = true;
        });
        final web3.PublicKey programId = web3.PublicKey.fromString(
            "DWX4sCH7wFiNPXDxyJBMCT5m84xXFznbhhq1rqi1Fxuk");
        var instruction = web3.TransactionInstruction(
          keys: [
            web3.AccountMeta(
              phantom.phantomWalletPublicKey,
              isSigner: true,
              isWritable: false,
            ),
          ],
          programId: programId,
        );

        final transaction = web3.Transaction(
            feePayer: phantom.phantomWalletPublicKey,
            recentBlockhash: (await connection.getLatestBlockhash()).blockhash);
        transaction.add(instruction);

        String transactionString = web3.base58Encode(transaction
            .serialize(const web3.SerializeConfig(requireAllSignatures: false))
            .asUint8List());

        final url = phantom.generateSignTransactionUri(
            transaction: transactionString, redirect: "onSignTransaction");
        setState(() {
          isLoading = false;
        });
        launchUrl(
          url,
          mode: LaunchMode.externalNonBrowserApplication,
        );
      } catch (e) {
        setState(() {
          isLoading = false;
        });
        textLogger("error,  make sure your connect to wallet \n \n <<$e>> ");
      }
    }

    void signAllTransaction() async {
      try {
        setState(() {
          isLoading = true;
        });
        web3.Transaction transaction1 = await createTransactionTransfer();
        web3.Transaction transaction2 = await createTransactionTransfer();
        List<web3.Transaction> transactions = [transaction1, transaction2];
        List<String> serializeTransactions = transactions
            .map(
              (e) => web3.base58Encode(e
                  .serialize(
                      const web3.SerializeConfig(requireAllSignatures: false))
                  .asUint8List()),
            )
            .toList();
        final url = phantom.generateUriSignAllTransactions(
            transactions: serializeTransactions,
            redirect: "onSignAllTransaction");
        setState(() {
          isLoading = false;
        });
        launchUrl(
          url,
          mode: LaunchMode.externalNonBrowserApplication,
        );
      } catch (e) {
        setState(() {
          isLoading = false;
        });
        textLogger("error,  make sure your connect to wallet \n \n <<$e>> ");
      }
    }

    void signMessage() {
      try {
        setState(() {
          isLoading = true;
        });
        final url = phantom.generateSignMessageUri(
            redirect: "onSignMessage", message: "hello from flutter");
        setState(() {
          isLoading = false;
        });
        launchUrl(
          url,
          mode: LaunchMode.externalNonBrowserApplication,
        );
      } catch (e) {
        setState(() {
          isLoading = false;
        });
        textLogger("error,  make sure your connect to wallet \n \n <<$e>> ");
      }
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text("flutter phantom demo"),
      ),
      body: Column(
        children: <Widget>[
          Container(
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height * .3,
            color: Colors.black,
            child: SingleChildScrollView(
              controller: scrollController,
              child: Text(
                logger,
                style: const TextStyle(color: Colors.white),
              ),
            ),
          ),
          isLoading
              ? const Expanded(
                  child: Center(child: CircularProgressIndicator()))
              : Column(
                  children: [
                    Button(
                      onPress: connectToWallet,
                      text: "connect",
                      isLoading: isLoading,
                    ),
                    Button(
                      onPress: disconnectToPhantom,
                      text: "disconnect",
                      isLoading: isLoading,
                    ),
                    Button(
                      onPress: signTransaction,
                      text: "signTransaction",
                      isLoading: isLoading,
                    ),
                    Button(
                      onPress: signAllTransaction,
                      text: "sign All Transaction",
                      isLoading: isLoading,
                    ),
                    Button(
                      onPress: signAndSendTransaction,
                      text: "signAndSendTransaction (Transfer)",
                      isLoading: isLoading,
                    ),
                    Button(
                      onPress: signAndSendTransactionToProgram,
                      text: "signAndSendTransaction (program)",
                      isLoading: isLoading,
                    ),
                    Button(
                      onPress: signMessage,
                      text: "SignMessage",
                      isLoading: isLoading,
                    ),
                  ],
                )
        ],
      ),
    );
  }
}

class Button extends StatelessWidget {
  final void Function() onPress;
  final String text;
  final bool isLoading;
  const Button(
      {Key? key,
      required this.onPress,
      required this.text,
      required this.isLoading})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(bottom: 5),
      width: MediaQuery.of(context).size.width,
      child: ElevatedButton(
        onPressed: isLoading ? null : onPress,
        child: Text(text),
      ),
    );
  }
}
  • An full example of how to use this package here.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages