Skip to content

[Solana] useWallets incompatible with standard Web3 SDKs (Metaplex Umi / Irys) / TypeError: t is not iterable #160

@tayelroy

Description

@tayelroy

Description:
When using a Privy connected Solana wallet (via useWallets()) as a provider for standard Solana Web3 SDKs like @metaplex-foundation/umi or @irys/sdk, transactions fail with a fatal serialization error (TypeError: t is not iterable).

The Root Cause:
The standard @solana/wallet-adapter-react ecosystem (which Metaplex and Irys rely on) expects signTransaction to accept and return fully constructed @solana/web3.js Transaction or VersionedTransaction objects.

However, Privy's v3 ConnectedStandardSolanaWallet strict implementation expects signTransaction to receive a serialized byte array wrapped in an object: { transaction: Uint8Array }. When an SDK passes an object instead of a byte array, the underlying wallet extension (e.g., Backpack) fails to iterate over it during deserialization, resulting in the crash.

Steps to Reproduce:

  1. Retrieve the active Solana wallet via const { wallets } = useWallets().
  2. Map it to the standard adapter shape to use as a signer for Metaplex Umi (createSignerFromWalletAdapter) or WebIrys.
  3. Trigger an on-chain action (e.g., minting a Metaplex Core Asset).
  4. The transaction fails immediately at the serialization layer before reaching the browser extension UI.

Current Workaround:
To bypass the crash, developers are forced to build a custom adapterShim that intercepts the Web3 transaction objects, serializes them to raw bytes for Privy, and then deserializes them back into objects for the SDKs.

// The necessary workaround to bridge Privy to Metaplex Umi / Irys
const activeWallet = solanaWallets[0];

const adapterShim = {
  publicKey: new PublicKey(activeWallet.address),

  signMessage: async (msg: Uint8Array) => {
    const result = await activeWallet.signMessage({ message: msg });
    return new Uint8Array(result.signature);
  },

  signTransaction: async <T extends Transaction | VersionedTransaction>(tx: T): Promise<T> => {
    const isVersioned = 'version' in tx;

    // 1. Serialize to raw bytes for Privy's Wallet Standard interface
    const serialized: Uint8Array = isVersioned
      ? (tx as VersionedTransaction).serialize()
      : (tx as Transaction).serialize({ requireAllSignatures: false });

    // 2. Pass bytes to Privy
    const { signedTransaction } = await activeWallet.signTransaction({ transaction: serialized });

    // 3. Deserialize back to Web3 object for Metaplex/Irys
    return (isVersioned
      ? VersionedTransaction.deserialize(signedTransaction)
      : Transaction.from(signedTransaction)) as T;
  },

  signAllTransactions: async <T extends Transaction | VersionedTransaction>(txs: T[]): Promise<T[]> => {
    return Promise.all(txs.map((tx) => adapterShim.signTransaction(tx)));
  },
};

Expected Behavior:
Ideally, Privy should export an official "Adapter Shim" hook (e.g., useSolanaAdapter()) that automatically handles this byte array serialization/deserialization under the hood, allowing Privy wallets to drop seamlessly into existing @solana/wallet-adapter-react contexts without crashing downstream SDKs.

Environment:

  • @privy-io/react-auth: ^3.23.1
  • @metaplex-foundation/umi: ^1.5.1
  • @irys/sdk: ^0.2.11
  • @solana/web3.js: ^1.98.4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions