merigo-labs / solana-wallet-adapter

MIT License
6 stars 1 forks source link

sending sols #1

Closed kevin-nemo74 closed 1 year ago

kevin-nemo74 commented 1 year ago

i tried sending sol from one wallet to another by creating a transaction using solana_web3 package and changing it to String before passing it to the signAndSendTransactions method i get request contains an invalid payloads entry error

import 'dart:convert'; import 'dart:typed_data';

import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:solana_wallet_adapter/solana_wallet_adapter.dart'; import 'package:solana_web3/solana_web3.dart'; import 'package:solana_web3/solana_web3.dart' as web3; import 'package:solana_web3/programs/system.dart';

import '../dashboard/widgets/appBar.dart';

// 1. Create instance of [SolanaWalletAdapter]. final adapter = SolanaWalletAdapter( const AppIdentity(), // NOTE: CONNECT THE WALLET APPLICATION // TO THE SAME NETWORK. cluster: Cluster.devnet, );

class AuthorizeButton extends StatefulWidget { const AuthorizeButton({super.key}); @override State createState() => _AuthorizeButtonState(); }

class _AuthorizeButtonState extends State { Object? out; @override Widget build(BuildContext context) { return Scaffold( bottomNavigationBar: GNavBar( selectedIndex: 1, ), body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () { // 2. Authorize application with wallet.

            adapter
                .authorize()
                .then((result) => setState(() => out = result.toJson()))
                .catchError((error) => setState(() => out = error));
          },
          child: const Text('Send')),
      TextButton(
        onPressed: () async {
          // 2. Authorize application with wallet.

          final receivinadd = "WTuHGrE2eia5JAa3KTY2k1DkUsHua2SJDdGWadPksKE";
          final sendingadd = "DuR26WphWX3o3S3kVBhNsv7gYpGXBfRwS5CXfFuZGAb1";
          final rcv = web3.PublicKey.fromString(receivinadd);
          final snd = web3.PublicKey.fromString(receivinadd);

          print('creating transaction');

          final transaction = web3.Transaction();
          transaction.add(
            SystemProgram.transfer(
              fromPublicKey: snd,
              toPublicKey: rcv,
              lamports: web3.solToLamports(1),
            ),
          );
          final arr = [];
          arr.add(transaction);
          final tt = transaction.toString();
          print('creating transaction string $tt');

          await adapter.signAndSendTransactions(transactions: [tt]);
          print('tried sending transaction');
        },
        child: const Text('Authorize'),
      ),
      if (out != null) Text(out.toString()),
    ],
  ),
);

} }

here s my code if anyone can help !

merigo-labs commented 1 year ago

Hi @kevin-nemo74,

You need to add blockhash information to the transaction and serialize it with transaction.serialize().

Here's the updated section with comments trailing the added lines:

...
print('creating transaction');
final connection = web3.Connection(Cluster.devnet);       // Connect to same cluster as adapter.
final blockhash = await connection.getLatestBlockhash();  // Get latest blockhash.
final transaction = web3.Transaction(                     // Add blockhash info to tx.
  recentBlockhash: blockhash.blockhash,
  lastValidBlockHeight: blockhash.lastValidBlockHeight,
  feePayer: snd,                                          // Your wallet address.
);
transaction.add(
  SystemProgram.transfer(
    fromPublicKey: snd,
    toPublicKey: rcv,
    lamports: web3.solToLamports(1),
  ),
);
final arr = [];
arr.add(transaction);
const config = web3.SerializeConfig(requireAllSignatures: false);               // Allow wallet to sign tx.
final tt = transaction.serialize(config).getString(web3.BufferEncoding.base64); // Serialize tx to base-64.
print('creating transaction string $tt');
...

Let me know if you need anything else.

Thanks! Merigo

merigo-labs commented 1 year ago

The snd address also needs updating in the example.

final receivinadd = "WTuHGrE2eia5JAa3KTY2k1DkUsHua2SJDdGWadPksKE";
final sendingadd = "DuR26WphWX3o3S3kVBhNsv7gYpGXBfRwS5CXfFuZGAb1";
final rcv = web3.PublicKey.fromString(receivinadd);
final snd = web3.PublicKey.fromString(sendingadd); // Your wallet app address.
kevin-nemo74 commented 1 year ago

Thanks you so much for replying

i made the changes am currently testing it and am getting a failed transaction from solflare wallet and the reason is "Transaction signature verification failure" also the snd address is the same as my wallet public key (address) i had to type it manually because when i access it from the adapter it gives a totally random address i have no idea why here s the updated code that give me the verification failure `` final receivinadd = "WTuHGrE2eia5JAa3KTY2k1DkUsHua2SJDdGWadPksKE"; final sendingadd = "DuR26WphWX3o3S3kVBhNsv7gYpGXBfRwS5CXfFuZGAb1"; final rcv = web3.PublicKey.fromString(receivinadd); final snd = web3.PublicKey.fromString(receivinadd);

          print('creating transaction');
          final connection = web3.Connection(
              Cluster.devnet); // Connect to same cluster as adapter.
          final blockhash = await connection
              .getLatestBlockhash(); // Get latest blockhash.
          final transaction = web3.Transaction(
            // Add blockhash info to tx.
            recentBlockhash: blockhash.blockhash,
            lastValidBlockHeight: blockhash.lastValidBlockHeight,
            feePayer: snd, // Your wallet address.
          );
          transaction.add(
            SystemProgram.transfer(
              fromPublicKey: snd,
              toPublicKey: rcv,
              lamports: web3.solToLamports(1),
            ),
          );
          final arr = [];
          arr.add(transaction);
          const config = web3.SerializeConfig(
              requireAllSignatures: false); // Allow wallet to sign tx.
          final tt = transaction.serialize(config).getString(
              web3.BufferEncoding.base64); // Serialize tx to base-64.
          print('creating transaction string $tt');

          await adapter.signAndSendTransactions(transactions: [tt]);
          print('tried sending transaction');
          ``

Thanks again

kevin-nemo74 commented 1 year ago

Also before the failure message it shows that it failed to simulate transaction

Thanks!

kevin-nemo74 commented 1 year ago

@merigo-labs can you check the code to see if am doin anything wrong ! am using this package and the documentations dont offer anything useful Thanks

merigo-labs commented 1 year ago

Hi @kevin-nemo74,

The mobile adapter returns the address in base-64 format (as defined by the specification).

Make sure Solflare is connected to Devnet (Settings > General > Network).

Here's a complete example:

import 'package:flutter/material.dart';
import 'package:solana_wallet_adapter/solana_wallet_adapter.dart';
import 'package:solana_web3/solana_web3.dart' as web3;
import 'package:solana_web3/programs/system.dart';

void main() {
  runApp(const MaterialApp(
    home: AuthorizeButton(),
  ));
}

/// 1. Get the cluster.
final Cluster cluster = Cluster.devnet;

/// 2. Create instance of [SolanaWalletAdapter] for [cluster].
final adapter = SolanaWalletAdapter(
  const AppIdentity(),
  cluster: cluster,
);

/// 3. Create a connection to [cluster].
final connection = web3.Connection(
  cluster,
);

class AuthorizeButton extends StatefulWidget {
  const AuthorizeButton({super.key});
  @override
  State createState() => _AuthorizeButtonState();
}

class _AuthorizeButtonState extends State {

  Object? out;

  Future<void> _authorize() async {
    try {
      final result = await adapter.authorize();
      out = result.toJson();
    } catch (error) {
      out = error;
    } finally {
      setState(() {});
    }     
  }

  Future<void> _signAndSendTransaction() async {
    try {
      final connectedAccount = adapter.connectedAccount?.address;
      if (connectedAccount == null) throw Exception('Connect wallet before sending tx.');

      // Mobile wallet displays the address as base-58.
      final rcv = web3.PublicKey.fromBase58("WTuHGrE2eia5JAa3KTY2k1DkUsHua2SJDdGWadPksKE");

      // Mobile wallet adapter specification returns address as base-64
      final snd = web3.PublicKey.fromBase64(connectedAccount);
      // final snd = web3.PublicKey.fromBase58("DuR26WphWX3o3S3kVBhNsv7gYpGXBfRwS5CXfFuZGAb1");

      print('creating transaction');

      // Get latest blockhash info.
      final blockhash = await connection.getLatestBlockhash();  // Get latest blockhash.

      // Create transfer tx.
      final transaction = web3.Transaction(    
        feePayer: snd, // The account that will sign the tx (and pay any fees). 
        recentBlockhash: blockhash.blockhash,
        lastValidBlockHeight: blockhash.lastValidBlockHeight,
        instructions: [
          SystemProgram.transfer(
            fromPublicKey: snd,
            toPublicKey: rcv,
            lamports: web3.solToLamports(1),
          ),
        ],
      );

      // Serialize tx.
      const config = web3.SerializeConfig(requireAllSignatures: false);
      final tt = transaction.serialize(config).getString(web3.BufferEncoding.base64);
      print('creating transaction string $tt');

      // Sign and send tx.
      final result = await adapter.signAndSendTransactions(transactions: [tt]);
      setState(() => out = result.toJson());

      // Wait for confirmation.
      await connection.confirmTransaction(base64ToBase58(result.signatures.first!));

      print('transaction success ${result.signatures.first}');

    } catch (error) {
      print('error sending tx $error');
    }
  }

  @override
  Widget build(final BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          TextButton(
            onPressed: adapter.isAuthorized ? null : _authorize,
            child: const Text('Authorize'),
          ),
          TextButton(
            onPressed: adapter.isAuthorized ? _signAndSendTransaction : null,
            child: const Text('Send'),
          ),
          if (out != null) Text(out.toString()),
        ],
      ),
    );
  }
}

Will look to provide better docs very soon!

Thanks Merigo

kevin-nemo74 commented 1 year ago

@merigo-labs Thank you so much ! it worked

WilfredAlmeida commented 1 year ago

I tried running the exact same code and got the following error when I tried to transact using Solflare

W/FlutterJNI(31006): Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: 90
W/FlutterJNI(31006): Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: 91
W/FlutterJNI(31006): Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: 93
W/FlutterJNI(31006): Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: 94
E/flutter (31006): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: MissingPluginException(No implementation found for method callAsyncJavaScript on channel com.pichillilorenzo/flutter_inappwebview_122212122589139161956711721439613127213153)
E/flutter (31006): #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:294)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #1      InAppWebViewController.callAsyncJavaScript (package:flutter_inappwebview/src/in_app_webview/in_app_webview_controller.dart:2300)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #2      StakingService.getStakeAccounts (package:solflare/blockchain/solana/sdk/action/staking/staking_service.dart:22)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #3      StakingFragmentCubit._getStakingAccounts (package:solflare/staking/fragment/bloc/staking_fragment_cubit.dart:62)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #4      StakingFragmentCubit.onReload (package:solflare/staking/fragment/bloc/staking_fragment_cubit.dart:56)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): 
E/flutter (31006): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: MissingPluginException(No implementation found for method callAsyncJavaScript on channel com.pichillilorenzo/flutter_inappwebview_122212122589139161956711721439613127213153)
E/flutter (31006): #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:294)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #1      InAppWebViewController.callAsyncJavaScript (package:flutter_inappwebview/src/in_app_webview/in_app_webview_controller.dart:2300)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #2      SolanaSimulation.getSimulatedBalanceChanges (package:solflare/blockchain/solana/sdk/action/simulation/solana_simulation.dart:22)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #3      Future.wait.<anonymous closure> (dart:async/future.dart:522)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #4      ConfirmTransactionCubit._loadSimulationInitially (package:solflare/transaction/bloc/confirm_transaction/confirm_transaction_cubit.dart:150)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): 
E/flutter (31006): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: MissingPluginException(No implementation found for method callAsyncJavaScript on channel com.pichillilorenzo/flutter_inappwebview_122212122589139161956711721439613127213153)
E/flutter (31006): #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:294)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #1      InAppWebViewController.callAsyncJavaScript (package:flutter_inappwebview/src/in_app_webview/in_app_webview_controller.dart:2300)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #2      NameService.getTwitterHandleForPublicKey (package:solflare/blockchain/solana/sdk/action/name_service/name_service.dart:28)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #3      PortfolioCubit._getTwitterHandle (package:solflare/portfolio/fragment/bloc/portfolio_cubit.dart:227)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #4      Future.wait.<anonymous closure> (dart:async/future.dart:522)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #5      PortfolioCubit._getPortfolioData (package:solflare/portfolio/fragment/bloc/portfolio_cubit.dart:143)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #6      PortfolioCubit.setTokenAccountCubit.<anonymous closure> (package:solflare/portfolio/fragment/bloc/portfolio_cubit.dart:130)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): 
E/flutter (31006): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: MissingPluginException(No implementation found for method callAsyncJavaScript on channel com.pichillilorenzo/flutter_inappwebview_240182255151794369312015193167229232190250)
E/flutter (31006): #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:294)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #1      InAppWebViewController.callAsyncJavaScript (package:flutter_inappwebview/src/in_app_webview/in_app_webview_controller.dart:2300)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #2      ActivityService.initialize (package:solflare/blockchain/solana/sdk/action/activity/activity_service.dart:32)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006): #3      TokenAccountCubit._onRefresh (package:solflare/shared/bloc/token_account/token_account_cubit.dart:95)
E/flutter (31006): <asynchronous suspension>
E/flutter (31006):