Building Seamless dApp-to-Wallet Experiences with Flutter and Solana Mobile Stack(SMS)
A detailed guide into how to use Solana Mobile Stack(SMS) with flutter dApps.
Table of contents
- Solana Mobile Stack
- Solana Mobile Wallet Adapter (MWA)
- Prerequisites
- Getting Started
- Integrating Solana Mobile Wallet Adapter (MWA)
- Signing, Sending, and Confirming Transactions
- Step 1: Sign the Transaction
- Step 2: Wait for Confirmation
- Creating a Subscription Client
- Creating Associated Token Account
- Creating Token Account
- Fetching Associated Token Account
- Getting Minimum Balance for Mint Rent Exemption
- Fetching Token Mint Information
- Fetching Token Balance
- Checking for Associated Token Account
- Fetch Account Information
- Get Token Balance
- Sending SOL (Solana's Native Token)
Solana Mobile Stack
Solana Mobile Stack (SMS) empowers developers to create mobile decentralized applications (dApps) on the lightning-fast Solana blockchain. It's your ticket to harnessing Solana's power for mobile users. Among various other tools developed by the team like seed vault, the solana team has developed a very useful tool called the Solana Mobile Wallet Adapter.
Solana Mobile Wallet Adapter (MWA)
The Mobile Wallet Adapter is an essential bridge between Solana-based mobile dApps and the user's preferred mobile wallet. It simplifies the user experience by allowing seamless integration with popular mobile wallets, enabling users to interact with dApps using their existing wallet accounts. This adapter streamlines the authentication and transaction signing processes, making it convenient for users to engage with Solana-powered mobile applications while maintaining control over their assets and private keys.
For this tutorial, we will be covering the integration of Solana Mobile Wallet Adapter with flutter dApps. This tutorial will give you insight on how to use the flutter sdk with the Solana Mobile Stack (SMS). Have your favourite brew by your side, and let's get started! 😁
Prerequisites
To understand this tutorial, you must have a basic understanding of :
Getting Started
Let's start by setting up your Flutter project:
Install Flutter: If you haven't already, install Flutter by following the instructions in the official Flutter installation guide.
Create a New Flutter Project: Open a terminal window and run the following command to create a new Flutter project:
flutter create my_solana_dapp
Navigate to Your Project Directory: Move into your project directory:
cd my_solana_dapp
Integrating Solana Mobile Wallet Adapter (MWA)
Now that you have your Flutter project set up, let's dive into integrating the Solana Mobile Wallet Adapter into your dApp. Solana Mobile Wallet Adapter comes with two use cases one for wallet app and another on the client side. We will be considering the client side implementation of Solana Wallet Adapter for this tutorial.
Add Dependency
Open the pubspec.yaml
file in your Flutter project and add the Solana Mobile Client as a dependency:
dependencies:
solana_mobile_client: ^0.1.1
Run flutter pub get
to fetch the package.
Import the Library
In the Dart file where you plan to use the MWA, import the library:
import 'package:solana_mobile_client/solana_mobile_client.dart';
Creating a SolanaClient Instance
To start using the Solana Mobile Client, you need to create an instance of the SolanaClient
class. Here's how you can do it:
SolanaClient solanaClient = SolanaClient(
rpcUrl: Uri.parse('https://api.testnet.solana.com'),
websocketUrl: Uri.parse('wss://api.testnet.solana.com'),
);
In the code snippet above, we create a SolanaClient
instance with the necessary RPC and WebSocket URLs for connecting to the Solana network. You can customize these URLs based on your network requirements.
Signing, Sending, and Confirming Transactions
One of the core functionalities of the Solana Mobile Client is to sign, send, and confirm transactions. This is essential for interacting with the Solana blockchain. Let's break down the process step by step.
Step 1: Sign the Transaction
To sign a transaction, you'll need a Message
object and a list of Ed25519HDKeyPair
signers. Here's how you can sign a transaction:
Message message = ...; // Create your message
List<Ed25519HDKeyPair> signers = [...]; // List of signers
TransactionId signedTransaction = await solanaClient.sendAndConfirmTransaction(
message: message,
signers: signers,
commitment: Commitment.processed, // Specify the commitment level
);
In this code snippet, we call the sendAndConfirmTransaction
function, passing the Message
object, the list of signers, and the desired commitment level. This function signs the transaction and returns a TransactionId
once it's confirmed.
Step 2: Wait for Confirmation
Now that the transaction is signed, we need to wait for it to be confirmed on the Solana network. We can use the waitForSignatureStatus
function for this purpose:
await solanaClient.waitForSignatureStatus(
signedTransaction.signature,
status: ConfirmationStatus.finalized, // Specify the desired status
);
The waitForSignatureStatus
function takes the transaction signature and the desired status as parameters. It waits for the transaction to reach the specified status and throws an exception if it fails.
Creating a Subscription Client
The Solana Mobile Client also allows you to create a subscription client for real-time updates. This is useful for receiving notifications about changes on the Solana network.
SubscriptionClient subscriptionClient = solanaClient.createSubscriptionClient(
pingInterval: Duration(seconds: 10), // Set ping interval (optional)
connectTimeout: Duration(seconds: 30), // Set connect timeout (optional)
);
In this example, we create a subscriptionClient
with optional parameters like ping interval
and connect timeout
. This client can be used to listen for events and updates from the Solana network.
Creating Associated Token Account
To work with token accounts on the Solana blockchain, you often need to create associated token accounts. These accounts are associated with a specific token mint and owned by a particular user. Here's how you can create an associated token account using the initialized solanaClient
instance:
// Define the mint and funder accounts
String mint = 'Your_Mint_Public_Key';
String funder = 'Your_Funder_Public_Key';
// Create the associated token account
String associatedTokenAccount = await solanaClient.createAssociatedTokenAccount(
mint: mint,
funder: funder,
);
In this code snippet:
mint
should be replaced with the actual public key of the token mint you want to associate with.funder
is the account that will fund the creation of the associated token account.associatedTokenAccount
will store the public key of the newly created associated token account.
Creating Token Account
If you want to create a non-associated token account, you can use the following code:
// Define the mint, account, and creator accounts
String mint = 'Your_Mint_Public_Key';
String account = 'Your_Account_Public_Key';
String creator = 'Your_Creator_Public_Key';
// Create the token account
String tokenAccount = await solanaClient.createTokenAccount(
mint: mint,
account: account,
creator: creator,
);
In this code snippet:
mint
is the public key of the token mint for the new token account.account
is the public key of the account that will own the new token account.creator
is the public key of the account that will create the new token account.tokenAccount
will store the public key of the newly created token account.
Fetching Associated Token Account
To retrieve information about an associated token account, you can use the following code:
String owner = 'Your_Owner_Public_Key';
String mint = 'Your_Mint_Public_Key';
String associatedTokenAccount = await solanaClient.getAssociatedTokenAccount(
owner: owner,
mint: mint,
);
In this code snippet:
owner
is the public key of the account that owns the associated token account.mint
is the public key of the token mint associated with the token account.associatedTokenAccount
will store the public key of the associated token account if it exists.
Getting Minimum Balance for Mint Rent Exemption
To calculate the minimum balance required to create a token account for a specific token mint without paying rent, you can use the following code:
String mintAddress = 'Your_Mint_Public_Key';
int minimumBalance = await solanaClient.getMinimumBalanceForMintRentExemption(
mint: mintAddress,
);
In this code snippet:
mintAddress
is the public key of the token mint for which you want to calculate the minimum balance.minimumBalance
will store the calculated minimum balance.
Fetching Token Mint Information
To retrieve information about a token mint, you can use the following code:
String mintAddress = 'Your_Mint_Public_Key';
TokenMintInfo mintInfo = await solanaClient.getMint(address: mintAddress);
In this code snippet:
mintAddress
is the public key of the token mint for which you want to fetch information.mintInfo
will store the details about the token mint, including supply and decimals.
Fetching Token Balance
To get the balance of a specific token for a particular owner account, you can use the following code:
String owner = 'Your_Owner_Public_Key';
String mint = 'Your_Mint_Public_Key';
TokenBalance balance = await solanaClient.getTokenBalance(
owner: owner,
mint: mint,
);
In this code snippet:
owner
is the public key of the account for which you want to check the token balance.mint
is the public key of the token mint.balance
will store the token balance information, including the balance amount.
Checking for Associated Token Account
To check if an associated token account exists for a specific owner and mint, you can use the following code:
String owner = 'Your_Owner_Public_Key';
String mint = 'Your_Mint_Public_Key';
bool hasAssociatedAccount = await solanaClient.hasAssociatedTokenAccount(
owner: owner,
mint: mint,
);
In this code snippet:
owner
is the public key of the account.mint
is the public key of the token mint.hasAssociatedAccount
will betrue
if an associated token account exists; otherwise, it will befalse
.
Fetch Account Information
Now, let's retrieve the account information using the initialized solanaClient
. The key function here is getAccountInfo()
, which is provided by the rpcClient
within solanaClient
. It takes a single parameter: the public key of the Solana account for which you want to fetch information. Here's how to use it:
final pubKey = 'Your_Solana_Public_Key_Here';
final accountInfo = await solanaClient.rpcClient.getAccountInfo(pubKey);
In this code snippet:
pubKey
should be replaced with the actual Solana account's public key that you want to query.await
is used to indicate that the operation is asynchronous, and the program will wait for the account information to be fetched before proceeding.accountInfo
is a variable where the fetched account information will be stored.
Get Token Balance
Future<void> checkBalance() async {
final balance = await solanaClient.getTokenBalance(owner: owner, mint: mint);
print('Account Balance: $balance SOL');
}
The checkBalance()
function is an asynchronous function that returns a Future<void>
, which means that it will return a Future
object that will eventually resolve to void
. This is because the function needs to wait for the solanaClient.getTokenBalance()
call to return before it can print the account balance.
The solanaClient.getTokenBalance()
call is also asynchronous, which means that it will return a Future<u64>
, which is a Future
object that will eventually resolve to a u64
integer representing the account balance.
The await
keyword is used to wait for the solanaClient.getTokenBalance()
call to return before the checkBalance()
function continues executing. This ensures that the balance
variable will always contain the account balance before it is printed.
The print()
statement is used to print the account balance to the console.
Sending SOL (Solana's Native Token)
To send SOL from one account to another using the MWA, you can follow these steps:
Future<void> sendSOL(double amount, String recipientAddress) async {
try {
// Create a transaction
final transaction = solanaClient.createTransaction();
// Add a transfer instruction to the transaction
transaction.addTransferInstruction(
amount: amount, // Amount of SOL to send
recipientAddress: recipientAddress, // Recipient's SOL address
);
// Sign and send the transaction
final signedTransaction = await solanaClient.signAndSendTransaction(transaction);
// Transaction sent successfully
print('Transaction sent: ${signedTransaction.signature}');
} catch (e) {
// Handle any errors
print('Transaction failed: $e');
}
}
This code creates a transaction, adds a transfer instruction (specifying the amount of SOL to send and the recipient's address), signs the transaction with the connected wallet, and sends it to the Solana network.
Congratulations! You've successfully learned to how to integrate the Solana Mobile Client into your Flutter dApp, allowing users to connect their preferred mobile wallets and interact seamlessly with the Solana blockchain.
You can find an example app using solana mobile client here: https://github.com/Dhruv-Varshney-developer/my_solana_dapp
If you like this article, consider sharing it with people who might need it.
You can tag my social profiles that will give you more reach!
Meet you in some tutorial,
With ❤️,
Signing off,
Dhruv Varshney