f-o-a-m / chanterelle

Chanterelle -- a more functional truffle
80 stars 13 forks source link

Signing txs with a hardware wallet #120

Closed joelreymont closed 1 year ago

joelreymont commented 4 years ago

Does Chanterelle let me deploy contracts to mainnet?

What's the best place to plug in signing with the Ledger (or another hardware wallet)?

iostat commented 4 years ago

You have a couple of options of here:

You can either use a Web3 JSON-RPC provider that supports hardware wallets out of the box (e.g., geth). Then, if you're using the chanterelle deploy CLI utility, you can just pass in --node-url http://your.mainnet.node.with.a.ledger.plugged.in:8545.

If you're invoking Chanterelle's deploy function from a custom PureScript module, then you've probably already noticed that you can pass in a node URL as well. In fact, chanterelle deploy is more or less a wrapper around exactly that.

Your other option when using a custom PureScript module to run your DeployM, is to use deployWithProvider. This is as close to "native" ledger support as it gets. As the name suggests, this lets you specify a web3 provider directly. You can think of deploy as

module Chanterelle.Deploy where

import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Network.Ethereum.Web3 (Provider, httpProvider)

deploy :: String -> Int -> (DeployM ~> Aff)
deploy nodeUrl requestTimeout deployScript = do
  provider <- liftEffect $ httpProvider nodeUrl
  deployWithProvider provider requestTimeout deployScript

deployWithProvider :: Provider -> Int -> (DeployM ~> Aff)
deployWithProvider provider timeout deployScript = ...
  -- all the heavy lifting of setting up chanterelle's deploy system and running your script
  -- happens here

So, if you can somehow make a web3 provider that supports the ledger, deployWithProvider becomes your best friend. It's actually fairly similar to how you'd do it in Truffle. In fact, you can even use truffle-ledger-provider (just don't forget to include it in your package.json) :)

Some fairly straightforward FFI bindings in a file called LedgerSupport.js:

"use strict";
var LedgerWalletProvider = require("truffle-ledger-provider");
exports.ledgerHttpProvider = function (providerUrl) {
  return function (ledgerOptions) {
    return function () {
      var provider = new LedgerWalletProvider(ledgerOptions, providerUrl);
      return provider.engine;
    };
  };
};

exports.stopLedgerProvider = function(engine) {
  return function () {
    engine.stop();
  };
};

And some ceremonial foreign imports in a file called LedgerSupport.purs

module LedgerSupport where

import Prelude (Unit)
import Effect (Effect)
import Network.Ethereum.Web3.Types.Provider (Provider)

type LedgerOptions = { networkId      :: Int
                     , path           :: String
                     , askConfirm     :: Boolean
                     , accountsLength :: Int
                     , accountsOffset :: Int
                     }
foreign import ledgerHttpProvider :: String -> LedgerOptions -> Effect Provider

foreign import stopLedgerProvider :: Provider -> Effect Unit

Lets you make quick work of the process:

module Deploy.Mainnet (main) where

import Prelude

import Chanterelle.Deploy (deployWithProvider)
import Deploy.Script (deployScript) as Mainnet
import DApp.LedgerSupport (ledgerHttpProvider, stopLedgerProvider)
import Data.Maybe (fromMaybe)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Node.Process (lookupEnv)

main :: Effect Unit
main = do
  let ledgerOptions = { networkId: 1
                      , path: "44'/60'/0'/0" -- ledger default derivation path
                      , askConfirm: true
                      , accountsLength: 1
                      , accountsOffset: 0
                      }
  nodeUrl <- fromMaybe "http://a.mainnet.node.or.maybe.infura.or.something:8545" <$> lookupEnv "NODE_URL"
  provider <- ledgerHttpProvider nodeUrl ledgerOptions
  launchAff_ $ deployWithProvider provider 60 Mainnet.deployScript
  stopLedgerProvider provider

Hope that helps!

I've actually had a purescript-web3-provider-engine library of sorts sitting on the backburner for a while. If there's interest in it, I could likely polish it up and release it as well. This would allow you to compose all sorts of provider stacks which can get mighty handy. e.g., Truffle's Ledger provider is essentially a web3-provider-engine stack. Ganache is basically glorified provider engine stack, etc.

iostat commented 4 years ago

By the way, right out of the box, purescript-web3 also has metamaskProvider. So, if you wanna do something silly browserify your deployScript and deployWithProvider metamaskProvider using Metamask while looking at log output in your browser's JS console, that's also possible.

(that's mostly a joke, but no reason it shouldn't work :P)