phantom / deep-link-demo-app

179 stars 25 forks source link

SignAndSendTransaction Deep link error -32603 #33

Closed 0xlucyy closed 3 months ago

0xlucyy commented 3 months ago

Summary

I am following this guide https://docs.phantom.app/phantom-deeplinks/provider-methods/signandsendtransaction.

I have a complete implementation for the creation of Connect and SignAndSendTransaction deep links but I am getting an unhelpful error message from SignAndSendTransaction deep link: errorCode=-32603 and errorMessage=Unexpected+error.

I see from this page https://docs.phantom.app/solana/errors that error -32603 is an Internal Server Error and Something went wrong within Phantom..

That gives me almost nothing to go off. I have no idea if it is something I am doing wrong.

Connect deep link

2024-08-14T18:41:04.179Z - server.js:209 - INFO: Request query: {"phantom_encryption_public_key":"8zG...","nonce":"15Xc...","data":"5PKA..."}
2024-08-14T18:41:04.180Z - server.js:216 - INFO: Decoding.
2024-08-14T18:41:04.206Z - server.js:226 - INFO: Getting decoded data.
2024-08-14T18:41:04.207Z - server.js:228 - INFO: Decoded data: {"public_key":"3oAX...","session":"22tnTd..."}
2024-08-14T18:41:04.208Z - server.js:235 - INFO: Saving into temp storage
2024-08-14T18:41:04.209Z - server.js:243 - INFO: Saved temp storage:
Key: 3oAX... Value: {
  "session": "22tnTdk...",
  "nonce": "15Xc...",
  "phantom_encryption_public_key": "8zG...",
  "shared_secret": "cO3..."
}

Here is the snippet of code which generated the logs above

    const phantom_encryption_public_key = req.query.phantom_encryption_public_key;
    const nonce = req.query.nonce;
    const data = req.query.data;

    logWithFileAndLine('info', `Decoding.`);
    const dapp_private_key = bs58.decode(process.env.PHANTOM_PRIV_KEY);
    const shared_secret = nacl.box.before(bs58.decode(phantom_encryption_public_key), dapp_private_key);
    const decrypted_data = nacl.box.open.after(bs58.decode(data), bs58.decode(nonce), shared_secret);

    if (!decrypted_data) {
      logWithFileAndLine('error', `Failed to decoding`);
      throw new Error('Failed to decrypt data');
    }

    logWithFileAndLine('info', `Getting decoded data.`);
    const decrypted_json = JSON.parse(new TextDecoder().decode(decrypted_data));
    logWithFileAndLine('info', `Decoded data: ${JSON.stringify(decrypted_json)}`);

    const user_public_key = decrypted_json.public_key;
    const session = decrypted_json.session;

    // Store session and nonce temporarily
    const shared_secret_bytes = Buffer.from(shared_secret);
    logWithFileAndLine('info', `Setting temp storage`);
    tempStorage.set(user_public_key, { 
      session,
      nonce,
      phantom_encryption_public_key,
      shared_secret: shared_secret_bytes.toString('base64')  // Store as base64 string
    });

SignAndSendTransaction deeplink

Code snippet of my logic

async def create_sign_and_send_deeplink(public_key: str):
    try:
        secret_id = "1234567899"
        logger.info(f"Creating sign and send deeplink for public key: {public_key}")

        # Fetch wallet data (session, nonce, phantom_encryption_public_key, and shared_secret)
        wallet_data = await get_wallet_data(public_key)
        logger.info(f"Fetched wallet data: {wallet_data}")

        if not wallet_data:
            logger.error("Failed to retrieve wallet data")
            raise Exception("Failed to retrieve wallet data")
        else:
            session = wallet_data.get('session')
            phantom_encryption_public_key = wallet_data.get('phantom_encryption_public_key')
            shared_secret_base64 = wallet_data.get('shared_secret')
            shared_secret = base64.b64decode(shared_secret_base64) if shared_secret_base64 else None

            logger.info(f"Extracted session: {session[:10]}...")
            logger.info(f"Extracted phantom_encryption_public_key: {phantom_encryption_public_key}")
            logger.info(f"Extracted shared_secret: {shared_secret_base64[:10]}..." if shared_secret_base64 else "No shared_secret")

        # Get serialized transaction
        logger.info(f"Fetching serialized transaction with secret_id: {secret_id} and public_key: {public_key}")
        serialized_tx = await get_serialized_transaction(secret_id, public_key)
        logger.info(f"Received serialized transaction: {serialized_tx[:50]}..." if serialized_tx else "No serialized transaction received")

        if not serialized_tx:
            raise Exception("Failed to get serialized transaction")

        # Prepare payload
        payload = {
            "transaction": serialized_tx,
            "session": session
        }
        logger.info(f"Prepared payload with transaction (first 50 chars): {serialized_tx[:50]}... and session")

        # Encrypt the payload using the shared secret
        logger.info("Encrypting payload")
        nonce = nacl_random(SecretBox.NONCE_SIZE)
        secret_box = SecretBox(shared_secret)
        encrypted_payload = secret_box.encrypt(json.dumps(payload).encode(), nonce)
        logger.info(f"Encrypted payload. Nonce: {base58.b58encode(nonce).decode()}, Encrypted payload length: {len(encrypted_payload)}")

        # Create the params
        params = {
            "dapp_encryption_public_key": base58.b58encode(PHANTOM_PUB_KEY.encode()).decode(),
            "nonce": base58.b58encode(nonce).decode(),
            "redirect_link": f"https://xxxx.ngrok-free.app/api/phantom-redirect",
            "payload": base58.b58encode(encrypted_payload).decode(),
        }
        logger.info(f"Created params: {params}")

        # Construct the deeplink
        base_url = "https://phantom.app/ul/v1/signAndSendTransaction"
        deeplink = f"{base_url}?{urllib.parse.urlencode(params)}"
        logger.info(f"Constructed deeplink: {deeplink}")

        return deeplink

    except Exception as e:
        logger.error(f"Error creating sign and send deeplink: {str(e)}")
        logger.exception("Exception details:")
        return None

My redirect_link endpoint is very simple, and takes the user back to the telegram channel

/*
* Send and confirm transaction
*/
app.get('/api/phantom-redirect', async (req, res) => {
  logWithFileAndLine('info', `In phantom-redirect`);
  logWithFileAndLine('info', `Method: ${req.method}`);
  logWithFileAndLine('info', `URL: ${req.url}`);
  logWithFileAndLine('info', `Query parameters: ${JSON.stringify(req.query, null, 2)}`);

  const signature = req.query.signature;
  logWithFileAndLine('info', `signature: ${signature}`);

  res.redirect(`https://t.me/${process.env.TELEGRAM_BOT_NAME}?start=${signature}`);
});

However, here is the issue:

2024-08-14T18:41:18.712Z - server.js:283 - INFO: In phantom-redirect
2024-08-14T18:41:18.712Z - server.js:286 - INFO: Method: GET
2024-08-14T18:41:18.713Z - server.js:287 - INFO: URL: /api/phantom-redirect?errorCode=-32603&errorMessage=Unexpected+error
2024-08-14T18:41:18.713Z - server.js:289 - INFO: Query parameters: {
  "errorCode": "-32603",
  "errorMessage": "Unexpected error"
}

This error code and error message indicate that this issue is with phantom itself and not my implementation but it is hard to tell since the error message is so vague.

I need direction in this regard. Please let me know if there is any other information I can provide which would assist you in helping me!

Thank you very much!

Example

No response

Steps to Reproduce

Phantom Version

24.13.0

Is there an existing discussion for this?