question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Truffle Wallet Provider should support Websockets

See original GitHub issue

Issue

The truffle-hdwallet-provider currently only supports http connections.

I’m using drizzle connected to Pantheon. For my test environment, I’ve spun it up so that none of my accounts need any eth to interact with the dapp. I like how your hooked wallet implementation can create ephemeral keys from a set mnemonic. It would be nice to be able to use that without forking.

Steps to Reproduce

Pass any WebSocket provider into truffle-hdwallet-provider and attach it to drizzle and it will tell you that subscriptions aren’t supported. This is because the SubproviderProvider used in the engine doesn’t support subscriptions. Any sub-provider that does, will be ignored.

Expected Behavior

Passing in a provider will yield the full feature set of the provider, not just being able to send a request.

Workaround

Right now, by editing the code to add a subprovider instead of creating a SubproviderProvider (phew) everything works as expected.

This could likely either be fixed by addressing what a SubProviderProvider can do, or by tweaking the initialization of your provider engine. The latter seemed better to me. I’d be happy to make a pr if you’ll accept.

Example ```js
const bip39 = require('bip39');
const ethJSWallet = require('ethereumjs-wallet');
const hdkey = require('ethereumjs-wallet/hdkey');
const ProviderEngine = require('web3-provider-engine');
const FiltersProvider = require('web3-provider-engine/subproviders/filters.js');
const WebsocketProvider = require('web3-provider-engine/subproviders/websocket.js');
const NonceProvider = require('web3-provider-engine/subproviders/nonce-tracker');
const HookedProvider = require('web3-provider-engine/subproviders/hooked-wallet.js');
const Transaction = require('ethereumjs-tx');
const ethUtil = require('ethereumjs-util');

export default (mnemonic, address_index = 0, num_addresses = 10) => {
  let hdwallet;
  const wallet_hdpath = "m/44'/60'/0'/0/";
  const wallets = {};
  const addresses = [];
  const engine = new ProviderEngine();

  // private helper to normalize given mnemonic
  const normalizePrivateKeys = mnemonic => {
    if (Array.isArray(mnemonic)) return mnemonic;
    else if (mnemonic && !mnemonic.includes(' ')) return [mnemonic];
    // if truthy, but no spaces in mnemonic
    else return false; // neither an array nor valid value passed;
  };

  // private helper to check if given mnemonic uses BIP39 passphrase protection
  const checkBIP39Mnemonic = mnemonic => {
    hdwallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(mnemonic));

    if (!bip39.validateMnemonic(mnemonic)) {
      throw new Error('Mnemonic invalid or undefined');
    }

    // crank the addresses out
    for (let i = address_index; i < address_index + num_addresses; i++) {
      const wallet = hdwallet.derivePath(wallet_hdpath + i).getWallet();
      const addr = `0x${wallet.getAddress().toString('hex')}`;
      addresses.push(addr);
      wallets[addr] = wallet;
    }
  };

  // private helper leveraging ethUtils to populate wallets/addresses
  const ethUtilValidation = privateKeys => {
    // crank the addresses out
    for (let i = address_index; i < address_index + num_addresses; i++) {
      const privateKey = Buffer.from(privateKeys[i].replace('0x', ''), 'hex');
      if (ethUtil.isValidPrivate(privateKey)) {
        const wallet = ethJSWallet.fromPrivateKey(privateKey);
        const address = wallet.getAddressString();
        addresses.push(address);
        wallets[address] = wallet;
      }
    }
  };

  const privateKeys = normalizePrivateKeys(mnemonic);

  if (!privateKeys) checkBIP39Mnemonic(mnemonic);
  else ethUtilValidation(privateKeys);

  const tmp_accounts = addresses;
  const tmp_wallets = wallets;

  engine.addProvider(
    new HookedProvider({
      getAccounts(cb) {
        cb(null, tmp_accounts);
      },
      getPrivateKey(address, cb) {
        if (!tmp_wallets[address]) {
          return cb('Account not found');
        } else {
          cb(null, tmp_wallets[address].getPrivateKey().toString('hex'));
        }
      },
      signTransaction(txParams, cb) {
        let pkey;
        const from = txParams.from.toLowerCase();
        if (tmp_wallets[from]) {
          pkey = tmp_wallets[from].getPrivateKey();
        } else {
          cb('Account not found');
        }
        const tx = new Transaction(txParams);
        tx.sign(pkey);
        const rawTx = `0x${tx.serialize().toString('hex')}`;
        cb(null, rawTx);
      },
      signMessage({ data, from }, cb) {
        const dataIfExists = data;
        if (!dataIfExists) {
          cb('No data to sign');
        }
        if (!tmp_wallets[from]) {
          cb('Account not found');
        }
        const pkey = tmp_wallets[from].getPrivateKey();
        const dataBuff = ethUtil.toBuffer(dataIfExists);
        const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
        const sig = ethUtil.ecsign(msgHashBuff, pkey);
        const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s);
        cb(null, rpcSig);
      },
      signPersonalMessage(...args) {
        this.signMessage(...args);
      }
    })
  );

  engine.addProvider(new NonceProvider());
  engine.addProvider(new FiltersProvider());
  // Interesting bit is here
  engine.addProvider(new WebsocketProvider({ rpcUrl: 'ws://127.0.0.1:8546' }));
  engine.start(); // Required by the provider engine.

  return engine;
};
</details>

## Environment

* Operating System: osx
* Ethereum client: pantheon/Drizzle
* Truffle version (`truffle version`): 5.0.18
* node version (`node --version`): 10.15
* npm version (`npm --version`): 6.9.0

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
eggplantzzzcommented, Mar 11, 2020

Is this still an issue with the latest version of @truffle/hdwallet-provider?

0reactions
gnidancommented, Apr 15, 2020

Closing this for issue maintenance since it appears that @truffle/hdwallet-provider does support websockets.

If there’s still an issue, well, let us know, and we’ll be happy to re-open! Thanks y’all 🙇

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to connect truffle using HDWallet provider to Websocket ...
Now I want to connect to websocket-powered node but keep my HD Wallet provider. I cannot find how to do that. All I...
Read more >
Truffle-hdwallet-provider-websocket NPM - npm.io
HD Wallet-enabled Web3 provider. Use it to sign transactions for addresses derived from a 12-word mnemonic. :warning: This repo is deprecated :warning: Truffle...
Read more >
truffle-hdwallet-provider-websocket - npm
HD Wallet-enabled Web3 provider. ... Start using truffle-hdwallet-provider-websocket in your project by running `npm i ...
Read more >
Configuration - Truffle Suite
If you need an HTTP provider, we recommend using host and port , or url , while if you need a custom provider...
Read more >
TruffleHDWalletProvider Alternative That works with Websockets
I've setup web3js with a infura websocket on the rinkeby network but I'm ... and the opposite was the case using truffle-hdwallet-provider.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found