|
// |
|
// Based on https://gist.github.com/miguelmota/62559d02a1b99cb291635de4b224349c |
|
// |
|
|
|
const Transport = require('@ledgerhq/hw-transport-node-hid').default |
|
|
|
// https://github.com/LedgerHQ/ledgerjs/ |
|
// https://www.npmjs.com/package/@ledgerhq/hw-app-eth |
|
const AppEth = require('@ledgerhq/hw-app-eth').default |
|
|
|
// https://github.com/ethereumjs/ethereumjs-tx |
|
const Tx = require('ethereumjs-tx').Transaction |
|
const Web3 = require('web3') |
|
|
|
// Use your personal Infura.io URL here |
|
const web3 = new Web3('https://mainnet.infura.io/v3/xxx') |
|
|
|
// Get this from etherscan IO |
|
const STUCK_NOCE = 12 |
|
|
|
// 50 Gwei for gas should be good as writing of this |
|
const GOOD_GAS_PRICE = 50e9 |
|
|
|
// Get your Ledger address |
|
// Note that the derivate path "44'/60'/0'/0/0" or m/44'/60'/0' has changed between Ledger app v1 and v2 |
|
// https://github.com/gnosis/MultiSigWallet/issues/199 |
|
|
|
//const DERIVATE_PATH = "44'/60'/0'/0/0" |
|
const DERIVATE_PATH = "m/44'/60'/0'/0" |
|
|
|
async function main() { |
|
const devices = await Transport.list() |
|
if (devices.length === 0) throw 'No Ledger connected? Please connect Ledger to USB, unlock, open Ethereum app' |
|
|
|
console.log("Check your Ledger screen if the script gets stuck here") |
|
const transport = await Transport.create() |
|
const eth = new AppEth(transport) |
|
|
|
// https://www.npmjs.com/package/@ledgerhq/hw-app-eth#getaddress |
|
const addressData = await eth.getAddress(DERIVATE_PATH) |
|
console.log("Using address", addressData.address, "from derivate path", DERIVATE_PATH) |
|
const balance = await web3.eth.getBalance(addressData.address) |
|
console.log("We have", web3.utils.fromWei(balance, 'ether'), "ETH") |
|
|
|
// Construct a transaction that replaces currenc stuck TX |
|
// with one that sends 1 wei to yourself |
|
const txData = { |
|
nonce: web3.utils.toHex(STUCK_NOCE), |
|
gasLimit: web3.utils.toHex(200000), |
|
gasPrice: web3.utils.toHex(GOOD_GAS_PRICE), |
|
to: addressData.address, |
|
from: addressData.address, |
|
value: web3.utils.toHex(3), // 3 wei |
|
} |
|
|
|
console.log("Knocker off transaction parameters are", txData) |
|
|
|
const tx = new Tx(txData, { chain: 'mainnet'}) |
|
|
|
console.log(tx.toJSON()); |
|
|
|
const unsignedTx = tx.serialize().toString('hex') |
|
|
|
console.log("Sign the transaction on your ledger screen") |
|
console.log("Raw unsigned transaction", unsignedTx) |
|
// https://www.npmjs.com/package/@ledgerhq/hw-app-eth#signtransaction |
|
const sig = await eth.signTransaction(DERIVATE_PATH, unsignedTx) |
|
|
|
console.log("Sig", sig) |
|
txData.v = Buffer.from(sig.v, "hex") |
|
txData.r = Buffer.from(sig.r, "hex") |
|
txData.s = Buffer.from(sig.s, "hex") |
|
|
|
console.log(txData.v, txData.r, txData.s) |
|
|
|
console.log("Signing transaction"); |
|
const signedTx = new Tx(txData) |
|
|
|
// BigNum lib cheatsheet https://github.com/indutny/bn.js/ |
|
const gasPrice = web3.utils.toBN(signedTx.gasPrice.toString('hex')) |
|
const gasLimit = web3.utils.toBN(signedTx.gasLimit.toString('hex')) |
|
const gasCostWei = gasPrice.mul(gasLimit) |
|
const gasCost = web3.utils.fromWei(gasCostWei, 'ether') |
|
|
|
console.log("Gas price:", gasPrice.toString(), "Gas limit:", gasLimit.toString(), "Max gas cost:", gasCost, "ETH") |
|
|
|
console.log(signedTx, signedTx.toJSON(), signedTx.value, signedTx.getSenderAddress()); |
|
const signedSerializedTx = signedTx.serialize().toString('hex') |
|
|
|
if(!signedTx.validate()) { |
|
throw "Signature is not valid?" |
|
} |
|
|
|
const signedByAddress = "0x" + signedTx.getSenderAddress().toString('hex'); |
|
if(signedByAddress.toLowerCase() != addressData.address.toLowerCase()) { |
|
console.lo |
|
throw "Ledger signed transaction using wrong address:" + signedByAddress + " vs. " + addressData.address; |
|
} |
|
|
|
// Useful for debugging if signedSerializedTx gives broadcasting errors: |
|
// https://flightwallet.org/decode-eth-tx/ |
|
console.log("Broadcasting the transaction", signedSerializedTx) |
|
const txHash = await web3.eth.sendSignedTransaction('0x' + signedSerializedTx) |
|
console.log("Broadcasted the transaction, view https://etherscan.io/tx/" + txHash) |
|
} |
|
|
|
main() |