magic-js icon indicating copy to clipboard operation
magic-js copied to clipboard

Solana `signTransaction` drops signatures from transactions which are partially signed

Open bmeeder22 opened this issue 3 years ago • 15 comments

✅ Prerequisites

  • [x] Did you perform a cursory search of open issues? Is this bug already reported elsewhere?
  • [x] Are you running the latest SDK version?
  • [x] Are you reporting to the correct repository (magic-sdk)?

🐛 Description

If Solana transactions need multiple signers then calling transaction.partialSign(Keypair) is necessary.

If a Solana transaction is partially signed before calling magic.solana.signTransaction the returned rawTransaction will return with the signatures array having the signature for that previously partially signed key as undefined, and the rawTransaction will not include the partially signed method.

A really good example of a use case would be in order for a transaction to have another wallet be the fee payer of the transaction. So it would need to be partiallySigned by a wallet on a server, sent back to the magic client, who would then sign and send the transaction. Since magic signing strips the previous signatures from the transaction, the transaction would not be valid.

🧩 Steps to Reproduce

Create a Solana transaction that requires partial signing and sign with magic.

🤔 Expected behavior

The rawTransaction should include the partially signed method.

😮 Actual behavior

Transaction will fail to due signature verification error.

💻 Code Sample

const metadata = await magic.user.getMetadata();
    const recipientPubKey = new web3.PublicKey(destinationAddress);
    const payer = new web3.PublicKey(metadata.publicAddress);
    const connection = new web3.Connection(rpcUrl);

    const hash = await connection.getRecentBlockhash();


    let transactionMagic = new web3.Transaction({
      feePayer: payer,
      recentBlockhash: hash.blockhash
    });

    const transaction = web3.SystemProgram.transfer({
      fromPubkey: payer,
      toPubkey: recipientPubKey,
      lamports: sendAmount,
    });

    transactionMagic.add(...([transaction]));

    transactionMagic.partialSign(Keypair.generate());

    const serializeConfig = {
      requireAllSignatures: false,
      verifySignatures: true
    };

    const signedTransaction = await magic.solana.signTransaction(transactionMagic, serializeConfig);

🌎 Environment

Software Version(s)
magic-sdk 8.0.1
Browser Chrome
yarn any
Operating System any

bmeeder22 avatar Apr 18 '22 21:04 bmeeder22

This issue can be fixed on the magic server by instead of calling transaction.sign(keypair) you instead call transaction.partialSign(keypair) which will preserve all of the signatures during serialization.

bmeeder22 avatar Apr 18 '22 21:04 bmeeder22

@harryEth Could you take a look?

Ethella avatar Apr 20 '22 21:04 Ethella

@bmeeder22 or @harryEth have you found a solution or workaround to this? Wallets like Phantom solved this recently but if users use the magic wallet we are forced to do it through magic Solana.

edmbn avatar Jun 03 '22 01:06 edmbn

@Ethella @harryEth it would be as simple as adding the extra method to the sdk right? So we can have a magic.solana.signTransaction() and a magic.solana.partialSignTransaction(). It is a functionality that's being used more and it's quite blocking since looks like there is no workaround.

edmbn avatar Jun 03 '22 14:06 edmbn

@edmbn Forwarded your request to the stakeholder

Ethella avatar Jun 03 '22 21:06 Ethella

@Ethella thank you... communication is really appreciated. Through Discord has been very difficult getting some answers or information about this topic. If you need anything ask us about the problem and will try to help as much as possible.

edmbn avatar Jun 03 '22 21:06 edmbn

In case it helps to understand a clear use case for this: With Solana Pay specification users receive a partially signed transaction that must then sign and send. If we can't handle partially signed transactions users are blocked from using Solana Pay with existing Magic wallets.

edmbn avatar Jun 07 '22 10:06 edmbn

@edmbn Thx for the clarification. It seems it's a must-have use case. Based on the current bandwidth we have, I can only say we'll support it by start of the next month

Ethella avatar Jun 08 '22 02:06 Ethella

@Ethella can we expect to have this fix anytime soon? Wouldn't be worried if it was anything else but this is super blocking for any e-commerce trying to use Solana Pay. Thank you for your time.

edmbn avatar Jul 08 '22 22:07 edmbn

@harryEth Any updates?

Ethella avatar Jul 10 '22 05:07 Ethella

@Ethella I know the team may have been busy with the Magic Connect launch but is there any way we can get more info on this issue? It is something that most popular wallets have already solved yet and it is becoming increasingly necessary for Magic too. Thank you!

edmbn avatar Jul 21 '22 04:07 edmbn

@Ethella I want to make a pull request to add the client part of the partialSignTransaction, which is very easy but if this helps speed the whole development I want to do it. Do I need extra permission to upload a branch, don't I?

edmbn avatar Jul 27 '22 14:07 edmbn

@edmbn I just struck with this and find a workaround to it. The Transaction class allows you add the signature you had from the original partially signed transaction. Here's a snippet that allows you to re-add it to the transaction that Magic signs.

  async signTransaction(tx: Transaction): Promise<Transaction> {
    const serializeConfig = {
      requireAllSignatures: false,
      verifySignatures: true,
    };
    const signedTransaction = await magic.solana.signTransaction(
      tx,
      serializeConfig
    );
    const transaction = Transaction.from(signedTransaction.rawTransaction);

    // add missing signers from original transaction to the newly created one
    const missingSigners = transaction.signatures
      .filter((s) => !s.signature)
      .map((s) => s.publicKey);
    missingSigners.forEach((publicKey) => {
      const signature = tx.signatures.find((s) =>
        publicKey.equals(s.publicKey)
      );
      transaction.addSignature(publicKey, signature!.signature!);
    });

    return transaction;
  }

cryptorrivem avatar Aug 17 '22 19:08 cryptorrivem

@cryptorrivem Sorry for getting to you late. Yes, this is a valid workaround. Also, you can make your second transaction signature on the backend and the transaction will have both signatures. Still, this latest workaround won't work with Solana Pay since the transaction already comes with the signature. Hopefully, this gets addressed soon. Thank you for posting your solution.

edmbn avatar Sep 13 '22 12:09 edmbn

Hello folks, do you have any deadline for implementing partialSign transactions? Thanks for your support! <3

jdnichollsc avatar Jun 01 '23 18:06 jdnichollsc