Skip to content

Instantly share code, notes, and snippets.

@losh11
Created April 23, 2026 20:03
Show Gist options
  • Select an option

  • Save losh11/7c88e3ed72e368fcf429ad528a133b1e to your computer and use it in GitHub Desktop.

Select an option

Save losh11/7c88e3ed72e368fcf429ad528a133b1e to your computer and use it in GitHub Desktop.

THE GOAL: Add Jade Hardware Wallet support to Nexus. Jade firmware has been forked to add support for Litecoin + Litecoin MWEB. We have created a NitroModule to interact with the Jade HW in React Native: react-native-nitro-jade (project directory: /Users/loshan/Repos/ltc/react-native-nitro-jade - directory also includes an example app in example/).

react-native-nitro-lndltc supports walletKitImportAccount & walletKitImportMwebScanKey. ImportAccount should be used to import the hardware wallet's HD public key (as watch only), which ImportMwebScanKey should import the hardware wallet's MWEB Scan Key.

Example of walletKitImportMwebScanKey (retrieve scan key, spend pubkey and master key fingerprint from jade hw):

lncli wallet accounts import-mweb-scan-key \
  --name "hw-mweb" \
  --scan_key c6fe1782af72a1987cf87cb7a8fbcfbecefb1d441f46366a90bdb0028e4a502a \
  --spend_pubkey 039fed1f844e48f3aa146f91b0ca0e31f6846d8f11efa518b75c2ca8f7fe352ba9 \
  --master_key_fingerprint e66c70b2 \
  --rescan

In the Nexus Wallet user interface the wallet created by Nexus is considered the Main Wallet. In the Main screen, by tapping the Main Wallet button in the top center, it opens up a liquid glass modal which shows the Main Wallet. This WalletsModalContent will the the entry point to connecting to a Jade HW, or to switch the wallet to showing the transactions/balance of the hardware wallet.

To integrate HW support, in the WalletsModalContent there should be another similarly styled Liquid Glass button to the which allows the user to add HW device. Clicking this Add HW wallet button should open up a new screen (which should probably have a new navigation stack) - which guides the user thought the setup flow... such as select bluetooth device, and then telling the user to follow instructions on the device (which includes creating new wallet [writing down seed phrase] / or recovering wallet, then creating a pin code to unlock the device).

The user should be able to switch between the Main Wallet and HW thorugh the WalletsModalContent.

When you import the HW, with ListAccount rpc you will see two accounts (one for regular litecoin, and another for mweb litecoin), the below shows a mweb account imported using walletKitImportMwebScanKey:

{
    "name":  "hw-mweb",
    "address_type":  "MWEB",
    "extended_public_key":  "xpub661MyMwKB68aQpiuqZfE9gkbyczFJFQ1rwJB4Q6KMRBD2hMNmzbBk7AEymLMpFTXd1aUWyVhSXcARizqbK9wagvqrPE8y5SxidxCAgsjeow",
    "master_key_fingerprint":  "e66c70b2",
    "derivation_path":  "m/0'/100'",
    "external_key_count":  1000,
    "internal_key_count":  3,
    "watch_only":  true
}

The transaction list on the Main screen should show only transactions from the hardware wallet (both regular litecoin & mweb). This is possible as lnrpc.GetTransactionsRequest has an account parameter, where you specify the wallet name you imported as.

When the selected wallet is Main Wallet, only show transactions from the main wallet, when selecting hw wallet, only show transactions from the hw wallet.

Similarly, you should only show the wallet balance from the specified wallet. This is possible as lnrpc.WalletBalanceRequest has param account.

When the HW wallet account is selected, send transactions should only be constructed using psbt pipelines. Here's an example of how you would create an transaction when using the hw wallet account:

# fundpsbt to construct the initial psbt
lncli wallet psbt fund \
  --sat_per_vbyte=1 \
  --outputs='{"ltcmweb1qqfj57uga25yw2rnjs5yff20w6wu4hcjjmugn5fz22rmnlwn8924a5qnnthegrgvfxeuwt4e0dv9a9ygszmls72vmsussrs75r390ukkn5vyde6d5":500000}' \
    --inputs='["c1a3c94a64cbe453f4e69ba5f38d88a626dcdc56e29bef58133310bb05285e86:0"]' \
    --account="hw-mweb"

{
  "psbt": "cHNidP8B+wQCAAAAAQIEAgAAAAEEAQEBBQECAZIBAQABAUtAQg8AAAAAAEIDMqf/jNHuQVdRouVxtsdItpZmdArH1UzQ1IIA5l48XL8DvMOu+Ez5hLrtn+fgLoezj/grQaXtiMoAzW1X+HwQR7QBDiCGXigFuxAzE1jvm+JW3NwmpoiN86Wb5vRT5MtkSsmjwQEPBAAAAAABEAQAAAAAAZAghl4oBbsQMxNY75viVtzcJqaIjfOlm+b0U+TLZErJo8EBkSEJSb/jTyA/PMurR2XAJyKb7esN24IRvhDo2fqR3X8xZXgBkiED3tZolP1HfbQxliK5oon3TwDWtW5hSRcZ/+kO/PVKt5IBlAEBAZYEAAAAAAGXCEBCDwAAAAAAAZkhA3Uc+ux+5O6VtSc1615k/wZJedTsJGthqmSQlpjKIKi1IpoDy+I0HnnmsrWvIYN9X5aFCqxq2+OaoG0tyiTLezHPHeMQsnBs5gAAAIBkAACAAAAAgCKbA5/tH4ROSPOqFG+RsMoOMfaEbY8R76UYt1wsqPf+NSupELJwbOYAAACAZAAAgAEAAIAAIgID4Bu8QrvYjzrV6+NfcI3dSJ6LfYSy9sVQE0rg6snnWD0UAAAAAAAAAIBkAACAAQAAAAIAAAABAwjkkQcAAAAAAAEEQgJS6A8Py7mJfZZSYup8MQ+t+Rppo3m/KXKCTnQ3mm/okwPgG7xCu9iPOtXr419wjd1Inot9hLL2xVATSuDqyedYPQGQQgJS6A8Py7mJfZZSYup8MQ+t+Rppo3m/KXKCTnQ3mm/okwPgG7xCu9iPOtXr419wjd1Inot9hLL2xVATSuDqyedYPQABAwggoQcAAAAAAAEEQgJlT3EdVQjlDnKFCJSp7tO5W+JS3xE6JEpQ9z+6Zyqr2gJzXfKBoYk2eOXXL2sL0pEQFv8PKZuHIQHD1BxK/lrTowGQQgJlT3EdVQjlDnKFCJSp7tO5W+JS3xE6JEpQ9z+6Zyqr2gJzXfKBoYk2eOXXL2sL0pEQFv8PKZuHIQHD1BxK/lrTowABAgg8DwAAAAAAAAA=",
  "change_output_index": 0,
  "locks": [
    {
      "id": "ede19a92ed321a4705f8a1cccc1d4f6182545d4bb4fae08bd5937831b7e38f98",
      "outpoint": "c1a3c94a64cbe453f4e69ba5f38d88a626dcdc56e29bef58133310bb05285e86:0",
      "expiration": 1773371516,
      "pk_script": "AzKn/4zR7kFXUaLlcbbHSLaWZnQKx9VM0NSCAOZePFy/A7zDrvhM+YS67Z/n4C6Hs4/4K0Gl7YjKAM1tV/h8EEe0",
      "value": 1000000
    }
  ]
}

# you must always presign a jade hw psbt, using walletKitPrepareMwebPresign, by taking the psbt value returned from fundpsbt
$ lncli wallet psbt mweb-presign --funded_psbt='cHNidP8B+wQCAAAA.....'

# send the presigned psbt to the jade, using react-native-nitro-jade, to sign
# at this point in the nexus ui you must tell the user to connect their hw wallet (if connection isn't already detected), and then to view the transaction on the hw device and confirm. 

# the jade hw will respond back with a fully signed psbt if the user signs the tx on the hw wallet.
# you then call lnd's finalizepsbt using this, which will return a raw tx which you can then broadcast using existing methods in the app.

When the selected account is hw wallet, the recieve card should also use nitro-jade to get the addresses from the hardware wallet. you must use a unused address (this will mean you will have to record the spent index. in the case the the hw has been used in the past, but not setup with this nexus wallet, during import you will need to determine the last spent index).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment