logo
logo
Sign in

How to Sign Ledger using Solana Wallet Adapter

avatar
arslan siddiqui
How to Sign Ledger using Solana Wallet Adapter

The official Solana wallet adapter (@solana/wallet-adapter) doesn’t yet offer native support for Ledger signMessage functionality. So we have to go for alternative solutions. One way is to build and sign transactions on-client, then send them to a backend for verification and broadcasting. This keeps private keys off the client but involves server-side implementation. For more information related to the emerging platform, visit our Solana blockchain development services.

Signing Ledger using Solana Wallet Adapter

Prerequisites

  • Ledger hardware wallet (e.g., Ledger Nano S or X)
  • Solana app installed on your Ledger device (version 1.3.0 or later)
  • Solana wallet adapter integrated into the web application or a dApp you’re using
  • Node.js > 18 installed on the device

Step 1.

Create a next app and install the following dependencies:

  • @solana/wallet-adapter-base
  • @solana/wallet-adapter-react
  • @solana/wallet-adapter-react-ui
  • @solana/wallet-adapter-wallets
  • @solana/web3.js

Step 2.

Create the following files

.env

NEXT_PUBLIC_SOLANA_RPC_HOST=https://api.devnet.solana.com

NEXT_PUBLIC_NETWORK=devnet

src/contexts/ContextProvider.js

import {

ConnectionProvider,

WalletProvider,

} from ‘@solana/wallet-adapter-react’;

import {

PhantomWalletAdapter,

SolflareWalletAdapter,

} from ‘@solana/wallet-adapter-wallets’;

import { useCallback, useMemo } from ‘react’;

import { AutoConnectProvider, useAutoConnect } from ‘./AutoConnectProvider’;

import { NetworkConfigurationProvider } from ‘./NetworkConfigurationProvider’;

import dynamic from ‘next/dynamic’;

import React from ‘react’;

const ReactUIWalletModalProviderDynamic = dynamic(

async () =>

(await import(‘@solana/wallet-adapter-react-ui’)).WalletModalProvider,

{ ssr: false }

);

const WalletContextProvider = ({ children }) => {

const { autoConnect } = useAutoConnect();

const network = process.env.NEXT_PUBLIC_NETWORK;

const endpoint = process.env.NEXT_PUBLIC_SOLANA_RPC_HOST;

const wallets = useMemo(

() => [new PhantomWalletAdapter(), new SolflareWalletAdapter()],

[network]

);

const onError = useCallback((error) => {

console.error(error);

}, []);

return (

{children}

);

};

export const ContextProvider = ({ children }) => {

return (

<>

{children}

);

};

src/contexts/AutoConnectProvider.js

import React, {

createContext,

useContext,

useMemo,

} from ‘react’;

import { useLocalStorage } from ‘@solana/wallet-adapter-react’;

export const AutoConnectContext = createContext({});

export function useAutoConnect() {

return useContext(AutoConnectContext);

}

export const AutoConnectProvider = ({ children }) => {

const [autoConnect, setAutoConnect] = useLocalStorage(‘autoConnect’, true);

const ContextValues = useMemo(

() => ({

autoConnect,

setAutoConnect,

}),

[autoConnect, setAutoConnect]

);

return (

{children}

);

};

src/contexts/NetworkConfigurationProvider.js

import { useLocalStorage } from ‘@solana/wallet-adapter-react’;

import { createContext, useContext } from ‘react’;

export const NetworkConfigurationContext = createContext({});

export function useNetworkConfiguration() {

return useContext(NetworkConfigurationContext);

}

export const NetworkConfigurationProvider = ({ children }) => {

const [networkConfiguration, setNetworkConfiguration] = useLocalStorage(

‘network’,

process.env.NEXT_PUBLIC_NETWORK

);

return (

{children}

);

};

src/pages/_app.js

import ‘@/styles/globals.css’;

import { ContextProvider } from ‘@/contexts/ContextProvider’;

require(‘@solana/wallet-adapter-react-ui/styles.css’);

export default function App({ Component, pageProps }) {

return (

);

}

src/pages/index.js

import ConnectWalletButton from ‘@/components/ConnectWalletButton.js’;

export default function Home() {

return (

);

}

src/components/ConnectWalletButton.js

import { useConnection, useWallet } from ‘@solana/wallet-adapter-react’;

import {

SystemProgram,

TransactionMessage,

VersionedTransaction,

} from ‘@solana/web3.js’;

import dynamic from ‘next/dynamic’;

import React, { useCallback, useEffect, useState } from ‘react’;

const WalletMultiButtonDynamic = dynamic(

async () =>

(await import(‘@solana/wallet-adapter-react-ui’)).WalletMultiButton,

{ ssr: false }

);

const ConnectWalletButton = () => {

const { publicKey, connected, sendTransaction } = useWallet();

const { connection } = useConnection();

const [signature, setSignature] = useState(‘’);

const solanaSign = useCallback(async () => {

const latestBlockhash = await connection.getLatestBlockhash();

const instructions = [

SystemProgram.transfer({

fromPubkey: publicKey,

toPubkey: publicKey,

lamports: 1,

}),

];

const messageLegacy = new TransactionMessage({

payerKey: publicKey,

recentBlockhash: latestBlockhash.blockhash,

instructions,

}).compileToLegacyMessage();

const transaction = new VersionedTransaction(messageLegacy);

try {

const signature = await sendTransaction(transaction, connection);

setSignature(signature);

} catch (error) {}

});

useEffect(() => {

if (!connected) return;

solanaSign();

}, [connected]);

return (

{signature}

);

};

export default ConnectWalletButton;

Step 3.

Start the next app by running the following command:

npm run dev

Step 4.

In your wallet select the account that is connected to the ledger. Then go to http://localhost:3000 and click on Select Wallet. it will connect and sign a transaction from your ledger.

Reference

Source Code — https://github.com/Vishal-oodles/ledger-signing

If you are looking to explore Solana blockchain development services for your project, hire Solana developers to get started.

collect
0
avatar
arslan siddiqui
guide
Zupyak is the world’s largest content marketing community, with over 400 000 members and 3 million articles. Explore and get your content discovered.
Read more