1

Getting the following error when trying to send a signed transaction with Magic Link.

WalletSignTransactionError: Versioned messages must be deserialized with VersionedMessage.deserialize()

Magic's signTransaction method

  async signTransaction<
    T extends TransactionOrVersionedTransaction<
      this['supportedTransactionVersions']
    >,
  >(transaction: T): Promise<T> {
    try {
      if (!this._user) throw new WalletNotConnectedError();
      try {
        const config = { requireAllSignatures: false, verifySignatures: true };
        const signedTransaction = await this._magic?.solana.signTransaction(
          transaction,
          config,
        );

        const msg = Buffer.from(
          signedTransaction?.rawTransaction as string,
          'base64',
        );
        return Transaction.from(msg) as T;
      } catch (error: any) {
        throw new WalletSignTransactionError(error?.message, error);
      }
    } catch (error: any) {
      this.emit('error', error);
      throw error;
    }
  }

Doc reference: https://magic.link/docs/blockchains/featured-chains/solana#sign-and-send-transaction

Solution: Update @magic-ext/solana to latest version and handle for versioned txn

  async signTransaction<
    T extends TransactionOrVersionedTransaction<
      this['supportedTransactionVersions']
    >,
  >(transaction: T): Promise<T> {
    try {
      if (!this._user) throw new WalletNotConnectedError();
      try {
        const config = { requireAllSignatures: false, verifySignatures: true };
        const signedTransaction = await this._magic?.solana.signTransaction(
          transaction,
          config,
        );

        let msg: Uint8Array;
        let rawTx: unknown = signedTransaction?.rawTransaction;

        if (rawTx instanceof Uint8Array) {
          msg = rawTx;
        } else if (typeof rawTx === 'string') {
          msg = base64.serialize(rawTx);
        } else {
          throw new Error(`Raw transaction is not a buffer or string`);
        }

        try {
          return Transaction.from(msg) as T;
        } catch {
          return VersionedTransaction.deserialize(msg) as T;
        }
      } catch (error: any) {
        throw new WalletSignTransactionError(error?.message, error);
      }
    } catch (error: any) {
      this.emit('error', error);
      throw error;
    }
  }

1 Answer 1

4

That error is probably coming from web3js: https://github.com/solana-labs/solana-web3.js/blob/f025b33f8f463b640807772e5f3b5e21f54deae9/packages/library-legacy/src/message/legacy.ts#L277

Specifically it's thrown by the Message.from function when you pass it a versioned transaction. That is called by Transaction.from: https://github.com/solana-labs/solana-web3.js/blob/f025b33f8f463b640807772e5f3b5e21f54deae9/packages/library-legacy/src/transaction/legacy.ts#L912

It's also possible that something in this._magic?.solana.signTransaction is calling it, so you might want to verify that. An easy way would be to put a log statement after signedTransaction, if that log prints then you know that line is not responsible for the error. If that holds, then this._magic?.solana.signTransaction is returning a versioned transaction. You can read more about versioned transactions in the docs: https://solana.com/docs/core/transactions/versions

If that's the case then you'll need to switch to using VersionedTransaction.deserialize: https://solana-labs.github.io/solana-web3.js/classes/VersionedTransaction.html#deserialize. This supports both legacy (not versioned) and versioned transactions and will avoid the error.

Note that you'll need to convert your base64 string to a Uint8Array to use VersionedTransaction.deserialize. There are a number of ways to do this, but our recommendation in the new web3js will be to use @solana/codecs-strings. You can install this alongside the existing web3js, but currently need the experimental tag. npm install @solana/codecs-strings@experimental

Then you can do this:

// import { getBase64Encoder } from '@solana/codecs-strings; 

// convert base64 string to Uint8Array
const msg = getBase64Encoder().encode(signedTransaction?.rawTransaction as string);
return VersionedTransaction.deserialize(msg);

Example sandbox showing this: https://stackblitz.com/edit/stackblitz-starters-mpd3gj?file=index.mjs

1
  • 1
    Was using an older version of @magic-ext/solana which didn't handle versioned transaction.
    – Hyetigran
    Commented Jan 30 at 15:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.