Ephemeral Signers

TLDR: Avoid Ephemeral Signers in your transactions if possible. If not, prefer SystemProgram.createAccountWithSeed to SystemProgram.createAccount. If that doesn't work either use the getEphemeralSigners API exposed by SquadsX via the Wallet Standard interface.

Problem

Ephemeral Signer (ES) is a key that is expected to sign the transaction and be discarded right after. An example of when such ephemeral signers are needed is the SystemProgram.createAccount instruction. This instruction requires the new account to be a signer to verify that whoever calls this instruction actually holds authority over this account.

The Keypair.generate() method is typically used by app developers to create ephemeral signers, but this mechanism doesn't work with Smart Wallets that don't execute the transaction immediately. In these cases, the ephemeral keypair will not be available to sign the "execute" transaction because it has already been lost and forgotten.

Solution 1

Quite often you can avoid Ephemeral Signers completely by using SystemProgram.createAccountWithSeed instead of SystemProgram.createAccount. The difference between the two is that the latter requires the new account's signature, whereas the former doesn't because the new account is "controlled" by the basePubkey which most likely is your primary singer anyway.

For example, here's how you can do this in a transaction that typically requires an Ephemeral Signer: wrapping SOL via an auxiliary token account.

// Generate a random seed.
const seed = Keypair.generate().publicKey.toBase58().slice(0, 32);

// Calculate a publicKey that will be controlled by the walletPubkey.
const auxAccountPubkey = await PublicKey.createWithSeed(
  walletPubkey,
  seed,
  TOKEN_PROGRAM_ID
);

// Create token account.
// Using `createAccountWithSeed` allows us to avoid the second signer,
// because this instruction requires signing only from `basePubkey`.
const createAccountIx = SystemProgram.createAccountWithSeed({
  fromPubkey: walletPubkey,
  basePubkey: walletPubkey,
  seed,
  newAccountPubkey: auxAccountPubkey,
  lamports: (await getMinimumBalanceForRentExemptAccount(connection)) + amount,
  space: ACCOUNT_SIZE,
  programId: TOKEN_PROGRAM_ID,
});// Some code

Solution 2

If the Solution 1 doesn't fit your needs, and you absolutely need a second signer in your transaction, we got you covered too. SquadsX exposes the getEphemeralSigners API that allows apps to request any number of ephemeral signers from the wallet. The wallet returns an array of account addresses that the app developer can use in their transactions. The API is exposed via the Wallet Standard feature detection mechanism and can be used as follows.

import { useWallet } from "@solana/wallet-adapter-react";

const { wallet } = useWallet()

// ...
  
<button
  onClick={async () => {
    const adapter = wallet?.adapter;

    const ephemeralSignerAddress = adapter &&
      "standard" in adapter &&
      "fuse:getEphemeralSigners" in adapter.wallet.features &&
      (await adapter.wallet.features["fuse:getEphemeralSigners"].getEphemeralSigners(1))[0]

    const ephemeralSignerPubkey = ephemeralSignerAddress ? new Pubkey(ephemeralSignerAddress) : null

    // Use `ephemeralSignerPubkey` in your transaction.
  })}
/>// Some code

There's an open sRFC with the effort of making this feature into the Wallet Standard, so other wallets can implement it too. Once it happens, we hope that usingKeypair.generate for creating Ephemeral Signers becomes a thing of a past.

Last updated