Backdrop filter not working after enabling View Transitions API and assigning view-transition-name

I’m implementing smooth page transitions using the View Transitions API in my svelte app.

Here’s the issue I’m facing: after setting a custom view transition root and giving view-transition-name to my main container and bottom navigation bar, the CSS backdrop-filter (and -webkit-backdrop-filter) on my bottom navigation stops working entirely.

It worked perfectly before adding the view-transition-name property.

before
enter image description here

after
enter image description here

 .main-container {
    position: relative;
    height: 100dvh;
    overflow: hidden;
    isolation: isolate;
    z-index: 2;
    view-transition-name: main;
  }
  .bottomBar_wrapper {
    position: relative;
    overflow: hidden;
    border-top: var(--theme-nav-border);
    isolation: isolate;
    z-index: 3;
    view-transition-name: bottom-navigation;
  }

  .bottomBar_wrapper::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    z-index: 1;
    isolation: isolate;
    background: var(--theme-bottom-navbar-background-before) !important;
    backdrop-filter: var(--theme-nav-backdrop-filter-blur) !important;
    -webkit-backdrop-filter: var(--theme-nav-backdrop-filter-blur) !important;
  }

  .bottomBar_wrapper::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    z-index: 100;
    isolation: isolate;
    background: var(--theme-bottom-navbar-background-after) !important;
  }
html:active-view-transition-type(forwards) {
  &::view-transition-old(main) {
    animation-name: slide-out-to-left;
  }
  &::view-transition-new(main) {
    animation-name: slide-in-from-right;
  }
}
html:active-view-transition-type(backwards) {
  &::view-transition-old(main) {
    animation-name: slide-out-to-right;
  }
  &::view-transition-new(main) {
    animation-name: slide-in-from-left;
  }
}

How to emulate aspect ratio change using CSS animation?

I’ve tried researching using a couple of sources, but none of them focused on my problem specifically. I want to use CSS, if possible, to get a border effect that emulates some aspect ratio changes, going from something like a 2.76:1 to 1.90:1.

Something like this: https://www.tiktok.com/@reeseirby/video/7535567446086864159?

I used some code from w3schools, but I only found animation that has both borders going in the same direction, while I’d like them to go in opposite directions. I’m new to this so there’s definitely something I’m missing, but I can’t find the necessary information. Code I’m currently using:

<!DOCTYPE html>
<html>
<head>
<style> 
#myDIV {
  width: 300px;
  height: 200px;
  border: 1px solid black;
  animation: mymove 5s infinite;
}

@keyframes mymove {
  50% {border-top-width: 15px;}
  50% {border-bottom-width: 15px;}
}
</style>
</head>
<body>

<h1>Animation of border-top-width</h1>

<p>Gradually change the border-top-width property from 1px to 15px, then back:<p>
<div id="myDIV"></div>

</body>
</html>

Thanks for any help!

Chart.JS overflowing labels

I am using the ChartJS library with a Vue Chart wrapper library.

Playground/Demo

My current issue is, when you open the playground, you can see the date labels on the X axis sort of “overflow” to the sides. This happens because there is too much of them. The goal is to make the labels not render past the red lines shown on the image:

Labels overflowing

I can’t find an optimal solution to prevent this from happening. Ideally, I want to find a way to automatically hide/resize/rotate/reposition labels, if they do not fit.

I’ve checked all configuration options of ChartJS and everything I could find had some drawbacks.

How to optimize writing all of an class’s methods to a plain object?

As part of an ETL pipeline, I am converting JSON data to a nested class model. The models expose methods, which correspond to the properties I eventually want to convert to JSON and write to an Elasticsearch index. Serializing the classes into JSON is taking a very long time, and I’m wondering if there is an obvious way to optimize this process.

This is a simplified sketch of how the models interact:

class ParentModel {

  children () {
    this.rawChildren.map(child => new ChildModel(rawChild))
  }

  property1(){
  // some logic to build out the property
  }
  property2(){
  // some logic to build out the property
  }

}

Both child and parent models inherit from a base model with the following method to turn the models into JSON to send to elastic search:

  modelToJson () {
    // Apply to-json to this object:
    const plainObject = baseToJSON(this)
    // Recursively apply to-json to any child entity that implements toJson:
    Object.entries(plainObject).forEach(([key, value]) => {
      // Handle case where ES model method returns a single ES model:
      if (value && value.toJson) {
        plainObject[key] = value.toJson()
        // Handle case where ES model method returns an array of ES models:
      } else if (Array.isArray(value) && value.some((v) => v && v.toJson)) {
        plainObject[key] = value.map((subModel) => subModel && subModel.toJson())
      }
    })
    return plainObject
  }

const baseToJSON = (model) => {
  const methods = _publicMethods(model)
  const objectToJson = {}
  methods.forEach((method) => {
    // Initialize the property first so that the resulting object keys are
    // created in the same alpha-sorted order they're found, even if some of
    // the values are returned async:
    objectToJson[method] = null

    // Call the model method, awaiting if async:
    objectToJson[method] = model[method]()

    // If method returns null, don't include property:
    if (objectToJson[method] === null) delete objectToJson[method]
  })

  return objectToJson
}

Token Problem in next auth and express js

I create authTeken in my backend express js app i need authtificate my route so i create authentificateToken.js

const jwt = require("jsonwebtoken");

const authenticateToken = (req, res, next) => {
 const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
if (!token) {
    return res.status(401).json({ message: "Access token required" });
  }
  jwt.verify(token, process.env.NEXTAUTH_SECRET, (err, user) => {
 if (err) {
      return res.status(403).json({ message: "Invalid token" });
    }
req.user = user;
 next();

  });
};

module.exports = authenticateToken;

and i add axios instance to check this token and added to header in my front,
Note that i use Nextauth for my nextjs App

import axios from 'axios'

import { getSession} from 'next-auth/react'

const axiosInstance = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL
})

axiosInstance.interceptors.request.use(
  async config => {

    if (typeof window === 'undefined') return config

    let session = null

    try {
  
      session = await getSession().catch(err => {
        console.error('getSession failed (non-fatal):', err)

        return null
      })
    } catch (err) {
      console.error('getSession unexpected error (ignored):', err)
      return config
    }

    console.log('Available session', session)

    if (session?.accessToken) {
      config.headers.Authorization = `Bearer ${session.accessToken}`
    } else {
      console.log('Available session keys:', session ? Object.keys(session) : 'No session')
    }
    return config
  },
  error => {
    console.error('Request interceptor error:', error)
    return Promise.reject(error)
  }
)

axiosInstance.interceptors.response.use(
  response => {
   

    return response
  },
  async error => {
    console.error('Response error:', error.response?.status, error.message)

    if (error.response?.status === 401 || error.response?.status === 403) {
     console.log('token not.......');
     
    }

    return Promise.reject(error)
  }
)

export default axiosInstance

the probleme it always error that token is invalid so the fetch of data not work

i d’ont have the problem when i work in local but when i deploy in vercel it make this problem

How can I detect if a user’s device is in Low Power Mode (macOS, iOS, Android, Windows) using JavaScript in the browser? [duplicate]

I’m trying to detect when a user’s device is running in Low Power Mode (or Battery Saver mode) from a web page using JavaScript.

For example:

On iOS/macOS, Apple devices have a “Low Power Mode” that reduces background activity and performance.

On Android, there’s a “Battery Saver” mode.

On Windows, there’s also a “Battery Saver” feature when battery is low.

I’d like to adapt my web app’s behavior (e.g., reduce animations, pause background tasks, etc.) when the user is in one of these modes.

k6: gracb url from previous response and use it on next request

I’m trying to catch a raw url into response and use it as a next request url.
I mean, this is my code snippet:

...
let uploadUrl = presignedRes.body.trim();
console.log(`Upload URL: ${uploadUrl}`);

  response = http.put(
    uploadUrl,...
...

Using code above, I’m getting an non-OK response.

However, when I hardcode url it works well:

  uploadUrl = 'https://prs-dev-s3-ew1-guarduty-000.s3.eu-west-1.amazonaws.com/6b395a25-23ff-4bd9-a7dd-e11d09fa9afc?X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEIaCWV1LXdlc3QtMSJHMEUCIGPXxtllOQTDIWLqiFX6dvXuDFamNZLCb%2FTZtEAQTXICAiEAjRDEfQKrswLIKEcaFoJms0Fvi%2FdMgIIy1wcLff949okqogMI2%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARAAGgwwNTgyNjQ1NTUxMjEiDMgyQMh%2ByHeOJFUeWyr2AhLy6pSyWC5e4HjiQW7JvMoh3KQA826lB%2Fjn9VunAK%2BLeJUCp%2FyYlW9Ka63M8fAqL9eiIEVpQVx6VirqlpLIdxyOPTYSY%2FVJaFYafS7FXniKILKXFhwmWAJ3wunCrZA%2FXpdwdfb%2BPu0D2STYOFAc5lCyT8ya5tlrKa6uJEkSYQXjeeeJYeDcq%2BQ5EvXNp5UJ6%2FFydup1Stqnmy2r5yj7BLCvE5a8LhTXKFNzicmbRqKA0Uajb77MHBYljCBnvvaONik2354vW2j%2FsYju88mx1GH2SfyB8SZdkYVrwmrXEaNf97VvBpc2XCDPnd5W9vclzi0bXqU%2FXQF46GSVX8dPL8ddqFnEO9LSU42a3aYldobENtayGPkEt0S8IEY0PoZkZZw7uci6W%2Be1mPv1cp%2FfuYT3o6O0U1xq49b1AY4UisD8Akn7qqTLdyOAGqxblZsE22zhqaH7QVZ6Y7Dvj0fLNDhFUkzDRy%2BXZfXHZgaCoGfppT2d%2Bv5pMI7gn8cGOp0BKm3sotfYqX%2BL4QPQf9eg2SCMpSbvL2fWxYJNoeQGNeSrej2pfHF2TaLzL2yUe0JPDdr64KTymdSvBorvzt3VX62CkPDmH5qxUnkj7inBUrJbHy63YEixNZXOlmtbR0xwgv%2Bwz5iPuuQjhUI2SONzQF4oGLzlCESWF4np668md855bSky5yaI0rhBm3n61tE5338ZZeGznZCqncoNaw%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20251009T172743Z&X-Amz-SignedHeaders=content-length%3Bhost%3Bx-amz-checksum-sha256%3Bx-amz-sdk-checksum-algorithm&X-Amz-Credential=ASIAQ3EGWRZY6JLGKC5N%2F20251009%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Expires=18000&X-Amz-Signature=cfb7e9a831528d809bb509e1a0eac1809e53327923233561724878542ac1b236';

  response = http.put(
    uploadUrl,...

Any ideas?

How to access the actual Google Maps 3D canvas? (Needed for streaming)

I want to get the actual canvas of the Google Maps 3D html element to call a captureStream on it, because I want to stream the footage somewhere.

I am using the google maps beta channel.

Now the problem is that this canvas is within a shadow_root (closed) which apparently means that my Code cannot query this area of the page (see screenshot for overview):

Browser Elements with gmp-map-3d

I also could not find anything useful int the documentation on how to access the canvas.

I tried a selector query with all sorts of parameters, but that does not work with the closed shadow_root.
I tried to somehow access the canvas with a Google WebGL Overlay, but this is apparently only available for the standard 2D Google Maps API (that information is nowhere mentioned by the way).

I am expecting that the API is at least somehow giving access to the canvas.
But really all I need is to capture the Google Maps view automatically (without any user verification pop-up).

Proxify apache ozone-recon UI (ozone 2.0.0) with knox 2.0.0

I have been trying to proxify the ozone-recon UI using knox.
When using a rewrite.xml file with no rules, url’s are correctly rewritten by implicit rules to (gateway_name)/(topology_name)/ozone-recon/
The page won’t load because a js file (index-74f1039b.js) tries to load another js file (overview-9d62e6a6.js) using the import js function :

if(l.payload=i,window.dispatchEvent(l),!l.defaultPrevented)throw i})},ii=k.lazy(()=>q(()=>import("./overview-9d62e6a6.js"),["static/js/overview-9d62e6a6.js","static/js/vendor-4ae33da8.js","static/js/eChart-4387f178.js","static/css/overview-57f83ddc.css"]))

Here we can see it tries to load ./overview-9d62e6a6.js also referenced as static/js/overview-9d62e6a6.js. Variables and URLs are rewritten by knox out of the box but the paths in the import statement are not.

I’m tried to use a filter to rewrite that js file so it’s able to get other js files using (gateway_name)/(topology_name)/ozone-recon/static/js instead of static/js/ to no avail, here are the service.xml and rewrite.xml files I used :

service.xml

<service role="OZONE-RECON" name="ozone-recon" version="2.0.0">
<metadata>
    <type>UI</type>
    <context>/ozone-recon/index.html</context>
    <shortDesc>OZONE Recon UI</shortDesc>
</metadata>
<routes>
    <route path="/ozone-recon"/>
    <route path="/ozone-recon/**">
        <rewrite apply="OZONE-RECON/filter/staticLinks" to="response.body"/>
    </route>
</routes>

rewrite.xml

<rules>
<rule dir="OUT" name="OZONE-RECON/rule/js-overview">
    <rewrite template="{$frontend[gateway.name]}/{$frontend[topology]}/ozone-recon/static/js/"/>
</rule>

<filter name="OZONE-RECON/filter/staticLinks">
    <content type="application/javascript">
        <apply path="static/js/" rule="OZONE-RECON/rule/js-overview"/>
    </content>
</filter>

I set knox’s log level to TRACE and saw that this rewrite rule is never applied.

What am I doing wrong ?

Getting “Malformed UTF-8” on Crypto JS

I’m getting this error. I’ve already asked the AI ​​wizards for help, but it keeps giving me the same error. Can you help me or even improve this code? Thanks.

This happens when I try to call CryptoJS.AES.decrypt(…).toString(CryptoJS.enc.Utf8).

Here’s a minimal example of my code:

import { ref } from 'vue';
import CryptoJS from 'crypto-js';

interface CryptoKeys {
  publicKey: string;
  privateKey: string;
}

export function useCrypto() {


  function encryptData(data: string, key: string, iv?: string): { ciphertext: string; iv: string } {
    const ivValue = iv || CryptoJS.lib.WordArray.random(16).toString(CryptoJS.enc.Hex);
    const encrypted = CryptoJS.AES.encrypt(data, CryptoJS.enc.Hex.parse(key), {
      iv: CryptoJS.enc.Hex.parse(ivValue),
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7,
    });
    const ciphertext = encrypted.toString();
    return { ciphertext, iv: ivValue };
  }

  function decryptData(ciphertext: string, iv: string, key: string): string {
    console.log('Decrypting - Raw Inputs:', { ciphertext, iv, key });
    try {
      if (!ciphertext || !iv || !key) {
        throw new Error('Missing or invalid decryption parameters');
      }

      const parsedCiphertext = CryptoJS.enc.Base64.parse(ciphertext);
      const parsedIv = CryptoJS.enc.Hex.parse(iv);
      const parsedKey = CryptoJS.enc.Hex.parse(key);

      console.log('Decrypting - Parsed Inputs:', { parsedCiphertext, parsedIv, parsedKey });

      const decrypted = CryptoJS.AES.decrypt(
        { ciphertext: parsedCiphertext },
        parsedKey,
        {
          iv: parsedIv,
          mode: CryptoJS.mode.CBC,
          padding: CryptoJS.pad.Pkcs7,
        }
      );
      let decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
      if (!decryptedStr) {
        console.warn('UTF-8 conversion failed, trying raw data');
        decryptedStr = decrypted.toString();
      }
      if (!decryptedStr) {
        throw new Error('Decryption failed: No valid data');
      }
      console.log('Decrypted Data:', decryptedStr);
      return decryptedStr;
    } catch (err) {
      console.error('Decryption error:', err);
      throw err;
    }
  }


  return {
    initialized,
    generateSalt,
    generateKeyPair,
    deriveKey,
    encryptData,
    decryptData,
    exportRecoveryKey,
    downloadRecoveryKey,
  };
}

Google, ChatGPT, etc

Prevent the Fire Fox Browser refresh

I am building a React + Vite application and have a scenario where a process is in progress. When I click the browser refresh, the default browser confirmation dialog appears. I have written a custom hook to prevent browser back navigation and show a warning modal. However, on refresh, the default browser dialog still appears.

Here’s the hook I wrote:

import { useEffect } from "react"
import { useBlocker } from "react-router-dom"

const useBrowserActions = (props: TProps) => {
const { getBlock, onBlockedState, showWaring } = props
const { state, proceed, reset } = useBlocker(({ currentLocation, nextLocation }) =>
getBlock(currentLocation?.pathname, nextLocation?.pathname)
)

    useEffect(() => {
        if (state === "blocked") onBlockedState()
    }, [state])
    
    useEffect(() => {
        const handleBeforeUnload = (e: BeforeUnloadEvent) => {
            if (!showWaring) return
            e.preventDefault()
            e.returnValue = ""
            return ""
        }
    
        window.addEventListener("beforeunload", handleBeforeUnload)
        return () => window.removeEventListener("beforeunload", handleBeforeUnload)
    }, [showWaring])
    
    return { proceed, reset, state }

}

export default useBrowserActions

type TProps = {
getBlock: (currentLocation: string, nextLocation: string) => boolean
onBlockedState: () => void | Promise<void>
showWaring?: boolean
}

This works fine in Chrome and Edge. In Firefox, the browser back handling works, and the default dialog shows on refresh. However, if I click refresh again, the page reloads immediately without showing the dialog. This behavior does not happen in Chrome or Edge.

If anyone has encountered this issue in Firefox and has a solution, please help!

In Remotion, Image Crop option is not working

I’m trying to generate a video using remotion library.
I have used the following two images

enter image description here

enter image description here

I have cropped the top option of image and places it on after another vertically. Please check the following screenshot which is the expected output.

enter image description here

But once i generate the video, then images are not cropped and its displaying the entire image. please check following screenshot which is the current output and did not crop the image.

enter image description here

I’m using the following code.

import { renderMediaOnLambda } from '@remotion/lambda/client';

const jsonData = {
"design": {
    "id": "UmNk7eWwdkH37Qxy",
    "size": {
        "width": 1080,
        "height": 1920
    },
    "fps": 30,
    "tracks": [
        {
            "id": "SSx719iOIirwB-aa5ESoG",
            "accepts": [
                "text",
                "image",
                "video",
                "audio",
                "composition",
                "caption",
                "template",
                "customTrack",
                "customTrack2",
                "illustration",
                "custom",
                "main",
                "shape",
                "linealAudioBars",
                "radialAudioBars",
                "progressFrame",
                "progressBar",
                "rect",
                "progressSquare"
            ],
            "type": "audio",
            "items": [
                "qAwgySht9x1V1pnr"
            ],
            "magnetic": false,
            "static": false
        },
        {
            "id": "Mlw-cxwDNpD-u0Z08IqyW",
            "accepts": [
                "text",
                "image",
                "video",
                "audio",
                "composition",
                "caption",
                "template",
                "customTrack",
                "customTrack2",
                "illustration",
                "custom",
                "main",
                "shape",
                "linealAudioBars",
                "radialAudioBars",
                "progressFrame",
                "progressBar",
                "rect",
                "progressSquare"
            ],
            "type": "image",
            "items": [
                "m8EJrAJsCnrBnGYo"
            ],
            "magnetic": false,
            "static": false
        },
        {
            "id": "7ZjQ3_osOfYs4xSB1nroU",
            "accepts": [
                "text",
                "image",
                "video",
                "audio",
                "composition",
                "caption",
                "template",
                "customTrack",
                "customTrack2",
                "illustration",
                "custom",
                "main",
                "shape",
                "linealAudioBars",
                "radialAudioBars",
                "progressFrame",
                "progressBar",
                "rect",
                "progressSquare"
            ],
            "type": "image",
            "items": [
                "HaNeBaS5lOVIFwFK"
            ],
            "magnetic": false,
            "static": false
        }
    ],
    "trackItemIds": [
        "HaNeBaS5lOVIFwFK",
        "m8EJrAJsCnrBnGYo",
        "qAwgySht9x1V1pnr"
    ],
    "trackItemsMap": {
        "HaNeBaS5lOVIFwFK": {
            "id": "HaNeBaS5lOVIFwFK",
            "type": "image",
            "name": "image",
            "display": {
                "from": 0,
                "to": 5000
            },
            "playbackRate": 1,
            "details": {
                "src": "https://ik.imagekit.io/wombo/images/img1.jpg",
                "width": 1280,
                "height": 853,
                "opacity": 100,
                "transform": "scale(0.84375)",
                "border": "none",
                "borderRadius": 0,
                "boxShadow": {
                    "color": "#000000",
                    "x": 0,
                    "y": 0,
                    "blur": 0
                },
                "top": 113.99651720007375,
                "left": 208.45281875000006,
                "borderWidth": 0,
                "borderColor": "#000000",
                "blur": 0,
                "brightness": 100,
                "flipX": false,
                "flipY": false,
                "rotate": "0deg",
                "visibility": "visible",
                "crop": {
                    "x": 84.2642857142857,
                    "y": 77.866567194206,
                    "width": 407.6642857142857,
                    "height": 604.8566255364807
                }
            },
            "metadata": {
                "previewUrl": "https://ik.imagekit.io/wombo/images/img1.jpg?tr=w-190"
            },
            "isMain": false
        },
        "m8EJrAJsCnrBnGYo": {
            "id": "m8EJrAJsCnrBnGYo",
            "type": "image",
            "name": "image",
            "display": {
                "from": 0,
                "to": 5000
            },
            "playbackRate": 1,
            "details": {
                "src": "https://ik.imagekit.io/wombo/images/img4.jpg",
                "width": 1280,
                "height": 1920,
                "opacity": 100,
                "transform": "scale(0.84375)",
                "border": "none",
                "borderRadius": 0,
                "boxShadow": {
                    "color": "#000000",
                    "x": 0,
                    "y": 0,
                    "blur": 0
                },
                "top": "1022.48px",
                "left": "424.206px",
                "borderWidth": 0,
                "borderColor": "#000000",
                "blur": 0,
                "brightness": 100,
                "flipX": false,
                "flipY": false,
                "rotate": "0deg",
                "visibility": "visible",
                "crop": {
                    "x": 463.48265895953756,
                    "y": 142.8894230769231,
                    "width": 485.9971098265896,
                    "height": 914.9134615384615
                }
            },
            "metadata": {
                "previewUrl": "https://ik.imagekit.io/wombo/images/img4.jpg?tr=w-190"
            },
            "isMain": false
        },
        "qAwgySht9x1V1pnr": {
            "id": "qAwgySht9x1V1pnr",
            "name": "Dawn of change",
            "type": "audio",
            "display": {
                "from": 0,
                "to": 4966.666666666667
            },
            "trim": {
                "from": 0,
                "to": 4966.666666666667
            },
            "playbackRate": 1,
            "details": {
                "src": "https://cdn.designcombo.dev/audio/Dawn%20of%20change.mp3",
                "volume": 100
            },
            "metadata": {
                "author": "Roman Senyk"
            },
            "duration": 117242.833,
            "isMain": false
        }
    },
    "transitionIds": [],
    "transitionsMap": {},
    "scale": {
        "index": 7,
        "unit": 300,
        "zoom": 0.0033333333333333335,
        "segments": 5
    },
    "duration": 5000,
    "activeIds": [
        "HaNeBaS5lOVIFwFK"
    ],
    "structure": [],
    "background": {
        "type": "color",
        "value": "transparent"
    }
},
"options": {
    "fps": 30,
    "size": {
        "width": 1080,
        "height": 1920
    },
    "format": "mp4"
}
};

const { renderId, bucketName } = await renderMediaOnLambda(
  { region       : "us-east-1"
  , functionName : config.lambdaFunction
  , serveUrl     : config.serverUrl
  , composition  : composition
  , inputProps   : jsonData
  , codec        : "h264"
  , audioCodec   : "mp3"
  , audioBitrate : "1M"
  , videoBitrate : "1M"
  , outName: 
      { bucketName : config.bucket
      , key        : outputFileName
      }
  , imageFormat     : "jpeg"
  , maxRetries      : 1
  , framesPerLambda : 1000
  , privacy         : "public"
});

Any help would be highly appreciated.

Is it safe to mix BIP32 (`secp256k1`) and `ed25519` to derive Exodus style Solana keys (`m/44’/501’/0’/0/0`)?

I’m implementing BIP32-Ed25519 derivation and I need to support the Exodus wallet.

According to their docs, Exodus uses the following derivation path for Solana:

m/44'/501'/0'/0/0

However, Solana uses the ed25519 curve, and the path above contains non-hardened (soft) derivation steps (/0/0), which are not valid for ed25519 (SLIP-0010).

As a workaround, I tried the following approach:

  1. Derive the path using a secp256k1 BIP32 implementation (bitcoinjs-lib / bip32)

  2. Take the left 32 bytes (IL) of the derived private key

  3. Use that as the ed25519 private key seed

  4. Compute the ed25519 public key

Here is the code:

import base58 from "bs58";
import { getPublicKey } from "ed25519-hd-key";
import { mnemonicToSeedSync } from "bip39";
import { BIP32Factory } from "bip32";
import { ECPairFactory } from "ecpair";
import * as ecc from "tiny-secp256k1";
import { initEccLib } from "bitcoinjs-lib";

initEccLib(ecc);

const mnemonic = "deposit potato belt enroll space involve sing angry marine shop ostrich midnight";
const seed = mnemonicToSeedSync(mnemonic);
const bip32Factory = BIP32Factory(ecc)
const bip32RootKey = bip32Factory.fromSeed(Uint8Array.from(seed));
const node = bip32RootKey.derivePath("m/44'/501'/0'/0/0");
const IL = node.privateKey.slice(0, 32);
const publicKeyBuffer = getPublicKey(Buffer.from(IL), false);
const privateKey = base58.encode(Buffer.concat([IL, publicKeyBuffer));
const publicKey = base58.encode(rawPublicKey);

This produces the Solana address that matching Exodus.

Question:

Is this approach safe and valid, or is it risky to mix secp256k1 BIP32 derivation and ed25519 key generation like this?
Could this break in edge cases, or is this an acceptable way to replicate what Exodus is doing?

Any insight into how Exodus handles this internally or whether there is a standard for this kind of “curve mixing” would be very helpful.