Shyft
Start BuildingSupportWebsite
  • Welcome
    • 👋Introducing Shyft
    • 🏗️Start Building
  • Solana Infrastructure
    • 🚁Shyft RPCs
  • Yellowstone gRPC Network
    • Decoding gRPC Latency
    • ⚡gRPC Docs
      • Introduction
      • Authentication
      • Subscribe Requests
      • FAQ
      • Getting Started
        • Initializing the Yellowstone Client
        • Making a gRPC connection
        • Adding a Reconnection Mechanism
        • 🔥Replaying Slots with Solana yellowstone gRPCs
        • Modifying your Subscribe Request
        • Closing a gRPC Connection
      • Subscribing to Transactions
        • All Transactions of an address
        • Subscribing to all transactions of a Liquidity Pool
        • Subscribing to all transactions of multiple addresses
        • Subscribing to all transactions of a Token
      • Subscribing to Accounts
        • Account Updates for a Program
        • Account Updates for an Address
        • Account updates using memcmp
      • Streaming Blocks & BlocksMeta
        • Streaming Block Updates
        • Subscribing to BlocksMeta
      • Modifying & Unsubscribing
  • Solana defi data
    • DeFI APIs
      • Get Pool By Address
      • Get Pools By Token Pair
      • Get All Pools for a Token
      • Get Liquidity Details of a Pool
  • Callbacks
    • ☎️What are Callbacks?
      • Transaction Callbacks
      • Account Callbacks
    • 📔Callback APIs
      • Response Structure
      • List Callbacks
      • Register callback
      • Remove callback
      • 🔥Pause a callback
      • 🔥Resume a callback
      • Update Callbacks
      • Add Addresses
      • Remove addresses
  • Solana Super Indexers
    • 🌩️GraphQL APIs
      • Getting Started
      • Building Queries
      • Paginating Response
      • Applying Filters
      • Ordering and Sorting Data
    • 📀Case Studies
      • Tensor
        • Get Active Listings of a Wallet
        • Get Active Bids of a Wallet
        • Get Active Listings of a Collection
        • Get all Bids of a Collection
        • Get all Pools of a Margin Account
        • Get all Pools by Owner
      • Raydium
        • Get Pool By Address
        • Get Pools By Token Address
        • Get Pools Created Between Time
        • Get Pool Burn Percentage
        • Get Liquidity Details of a Pool
        • Get Pool and OpenBook Market Info
        • Get Token Supply Percentage In Pool
      • Orca Whirlpool
        • Get Pool by Address
        • Get Pool by Token Address
        • Get Positions for a Pool
        • Get Positions for a Wallet
        • Get Liquidity Details of a Pool
      • Kamino
        • Get Borrow Details of a Wallet
        • Get Deposit Details of a Wallet
        • Get Reserve Details
      • Cross Marketplace Queries
        • Get active listings across marketplaces for a wallet
        • Get listings for a collection across marketplaces
        • Get floor price of a collection
      • Cross Defi Queries
        • Fetch Liquidity Pools for Token
      • Native Staking
        • Get Stakes for a Wallet
        • Get Stakes For Validator
      • Governance/Realms
        • Get DAO Token Owners
        • Get Proposals For Governing Mint
        • Get All Proposals For DAO
        • Get DAO Treasury Info
        • Get All Active Proposals For Wallet
      • Meteora
        • Get All LB Position Pairs
        • Get Position of a User Wallet
        • Get Pool by Token Addresses
        • Get All Deposits for a User
        • Get All Withdraws for a User
        • Get All Fees Claimed by a User
        • Get All User Positions and Deposits for a Pool
        • Get All User Positions and Withdrawals for a Pool
      • Fluxbeam
        • Get Pool by Address
        • Get Pool by Token Addresses
      • Drift
        • Get User account for Delegate
        • Get User accounts based on authority
        • Get User details based on Referrer
        • Get Borrow/Deposit Amount for an User
        • Get PrepPositions for an User Account
        • Getting OrderId and userOrderId
        • Get OpenOrders for a User Account
      • 🔥Pumpswap
        • 🔥Get Pool by Address
        • 🔥Get Pool by Creator Address
        • 🔥Get Pools by Token Addresses
      • Raydium Launchpad
        • Get Bonding Curve Details by Pool Address
        • Get All Pools for a Creator
        • Get Pools by Token Addresses
        • Get Migration details of a Pool
  • Solana APIs
    • API Reference
    • Transactions
      • Parsed Transaction Structure
      • Transaction APIs
        • History
        • Parse Signature
        • Parse Multiple Signatures
        • Send
        • Send Multiple
    • NFT
      • 🔥Create Gasless
      • Create
      • Read All
      • Burn
      • 🔥Burn Multiple NFTs V2
      • Update
      • 🔥Create NFT from Metadata
      • 🔥Read Wallet Nfts
      • 🔥Read Selected NFTs
      • 🔥Get NFT Owners
      • 🔥Update NFT Metadata Uri
      • 🔥Update V2
      • Search
      • Transfer
      • Transfer Multiple NFTs
      • Mint
      • Read
    • Wallet
      • Get Balance
      • Get Token Balance
      • Get All Tokens Balance
      • Get Portfolio
      • Resolve Address
      • Get All Domains
      • Get Stake Accounts
    • Fungible Tokens
      • Create
      • Mint
      • Burn
      • 🔥Update
      • Get Token Info
      • Transfer
      • Airdrop
  • 📘Dev Guides
    • 📀gRPC Case Studies
      • Pumpfun
        • Streaming and Parsing Pump.fun Transactions
        • Streaming and Parsing Pump.fun Accounts
        • Detecting new Token launches on Pump.fun
        • Detecting Buy/Sell Transactions on Pump.fun
        • Detecting tokens migrating from Pump.fun to Pump Swap AMM
      • PumpSwap AMM
        • Streaming and Parsing Pump Swap Transactions
        • Streaming and Parsing Pump Swap AMM Accounts
        • Detecting Buy/Sell Transactions on Pump Swap AMM
        • Detecting tokens migrating from Pump.fun to Pump Swap AMM
Powered by GitBook
On this page
  • Before getting started
  • Signing Transactions: Two Signers
  • Signing Transaction: More than Two Signers
  • Signing multiple Transactions with one wallet
  • Resources

Was this helpful?

  1. Dev Guides
  2. Solana
  3. Transactions

How to sign Transactions using multiple signers on Solana

Last updated 2 years ago

Was this helpful?

Program execution on Solana Blockchain begins with a Transaction being submitted to the cluster. Transactions are a group of instructions that a client (typically a wallet) signs using one or more key pairs and then is executed atomically with only two possible outcomes: success or failure. The Solana runtime will execute a program to process each of the instructions contained in the transaction, in order, and atomically.

Each transaction contains a compact array of signatures, followed by a message. Each item in the signatures array is a digital signature of the given message. The Solana runtime verifies that each signature was signed by the private key corresponding to the public key at the same index in the message's account addresses array.

In some cases, Transactions require a single signature, and in other cases, they require more than one signature. A few APIs from , such as the , and the return encoded_transaction in their response, which in most cases require signatures from two or more wallets. In this blog, we will see how we can sign transactions that require more than one signer.

Before getting started

To get started, we will need a few things.

Phantom Wallet

We will need the Phantom wallet browser extension, you can download it from the link below.

.

.

Once done, set up your Phantom wallet account. On-screen tips are available, which will guide you through setting up and getting started. You can also find a detailed guide related to this .

Authentication: Getting your Shyft API key

x-api-key is an authentication parameter, which gives you access to SHYFT APIs. You can get your own API Key from the . Just signup with your email id and you can get it for free. This is not really required for signing transactions, but this finds its application when making the API call which returns the encoded_transaction.

Read SHYFT API Documentation .

Signing Transactions: Two Signers

Let’s consider the following scenario, we are making a API call which to mint a new NFT, but the NFT creator (creator_walletparameter) and the fee payer (fee_payer parameter) for the create operation are different wallets. Such situations arise, in cases such as where an application is allowing users to create NFTs in a gasless manner, where the application is paying the gas fees (fee_payer) and the end user is the NFT creator (creator_address).

In such scenarios, the encoded_transaction received will need two signatures to be successfully executed by the Solana Runtime; first using the private_key of the fee_payer wallet, and next using the wallet of the end user. First, we attempt to sign the encoded_transaction using the private_key of the fee payer wallet, which in this case is the service provider, (Assuming the private_key of the service provided is available in the environment variable of the application or embedded somewhere in the application) using the partialSign() method . Once that is successful, we go on and sign the recoveredTransaction using the user’s wallet (creator_wallet).

The function for signing using both wallets is illustrated below

import {clusterApiUrl, Connection, Keypair, Transaction } from '@solana/web3.js';

import { decode } from 'bs58';
import { Buffer } from 'buffer';

export async function partialSignWithKeyAndWallet(connection,encodedTransaction,privateKey,wallet)
{
    const feePayer = Keypair.fromSecretKey(decode(privateKey));
    const recoveredTransaction = Transaction.from(
      Buffer.from(encodedTransaction, 'base64')
    );
    recoveredTransaction.partialSign(feePayer); //partially signing using private key of fee_payer wallet
    const signedTx = await wallet.signTransaction(recoveredTransaction); // signing the recovered transaction using the creator_wall
    const confirmTransaction = await connection.sendRawTransaction(
      signedTx.serialize()
    );
    return confirmTransaction;
  
}

This function accepts the connection to the user’s wallet, the encodedTransaction received in response of the API call, privateKey of the fee_payer, and the wallet object. We can get the connection and the wallet object for phantom in the following manner.

const phantom = new PhantomWalletAdapter();
await phantom.connect();
const rpcUrl = clusterApiUrl(network);
const connection = new Connection(rpcUrl, "confirmed");

This connection and wallet objects is then passed on to the partialSignWithKeyAndWallet() function, along with the encodedTransaction and the privateKey in the following manner.

const finalizedTransaction = await partialSignWithKeyAndWallet(connection, recoveredTransaction, phantom);

This will sign the transaction both via the fee_payer’s private key and the user's wallet. Once the user clicks approve, the transaction will be signed and the instruction will be executed.

We can verify if the instruction execution is complete or not, using the following method, where callback is a function, which is executed once the instruction has completed execution.

connection.onSignature(finalizedTransaction, callback, "finalized");

Now that we have seen how we can sign transactions using two different signer wallets, let’s see how we can sign transactions when there are more than two wallets.

Signing Transaction: More than Two Signers

In some instances, it may happen that, the fee_payer, creator_wallet and the collection owner( owner of the collection whose collection_address is used) are three different wallets, and the encoded_transaction generated needs 3 different signatures (or n different signatures). In such scenarios, we will use the fee_payer private key and the collection owner's private key to partially sign the transaction from the backend, and then we will use the end user’s wallet (creator_wallet) to sign the transaction finally.

We use a tweaked version of the same function to partially sign the transaction using the private keys of fee_payer and the collection owner. This function can be used in the previous case as well, as this function can partially sign transactions using n number of wallets as long as their private keys are available. Once that step is complete we go on and sign the recoveredTransaction using the user’s wallet (creator_wallet)

export async function partialSignWithKeysAndWallet(connection,encodedTransaction,privateKeys,wallet)
{
    const recoveredTransaction = Transaction.from(
      Buffer.from(encodedTransaction, 'base64')
    );
    const keys = privateKeys.map((k) => {
      return Keypair.fromSecretKey(decode(k));
    });
    
    recoveredTransaction.partialSign(...keys); //sign transaction with all private keys
   
    const signedTx = await wallet.signTransaction(recoveredTransaction);
    const confirmTransaction = await connection.sendRawTransaction(
      signedTx.serialize()
    );
    return confirmTransaction;
  
}

Similar to its previous version, this function accepts the connection to the user’s wallet, the encodedTransaction received in the response of the API call and the wallet object. The only difference is, along with these parameters, this function also takes in an array of private keys, which can contain all the private keys required to partially sign the transaction.

The wallet object and the connection to the user’s wallet can be obtained in the same manner as shown in the previous step.

This will also allow the user to sign the transaction via their wallet, once they click approve, the transaction will be signed and the instruction will be executed.

Signing multiple Transactions with one wallet

In certain situations, instead of one encoded_transaction SHYFT APIs return more than one, or an array of encoded_transactions, which will need a sign from the wallet in the front end. This happens in API endpoints such as transfer_many API, where the encoded_transactions returned is an array of encoded_transaction. In such cases, we will use the following function to sign the transaction from the front end:

export async function confirmTransactionsFromFrontend(connection, encodedTransactions, wallet) {
  
    const recoveredTransactions = encodedTransactions.map((tx) => {
      return Transaction.from(
        Buffer.from(tx, 'base64')
      );
    });
  
    const signedTx = await wallet.signAllTransactions(recoveredTransactions); //signs all the transactions in the recoveredTransactions array in one go
    
    var sentTxns = [];
    for await(const tx of signedTx)
    {
      const confirmTransaction = await connection.sendRawTransaction(
        tx.serialize()
      );
      sentTxns.push(confirmTransaction);
    }

    return sentTxns;
    
  }

This function accepts connection, the encoded_transactions (an array of encoded_transaction) from the SHYFT API response and the wallet object. Then all the transactions in the encoded_transactions array will be signed from the wallet in one go using the signAllTransactions() method. The wallet object and the connection to the user’s wallet can be obtained in the same manner as shown in the previous step.

Resources

That’s pretty much all about signing transactions using wallet and private keys on Solana. If you liked this blog, feel free to checkout our blog on or . We hope you have a great time building dApps on Solana using s. Happy Hacking.

Join our

📘
SHYFT
create_v2
update_v2
Chrome/Brave
Firefox
here
SHYFT website
here
here
create_v2
Check out Minting Gasless NFTs with SHYFT APIs.
😇
adding NFTs to Collections on Solana
getting all NFT collections from a wallet
SHYFT API
SHYFT API Documentation
Shyft Website
Get API Key
Github
Discord
Try out our APIs on Swagger UI
Users signing transaction from their wallet