1

I'm trying to create a signer for Trezor hardware wallet.

I'm using library ethers.js

In case of Metamask wallet, this is how I created the signer:

export const createMetamaskSigner = async () => {
  let networkId = Number(window.web3.version.network)
  let provider = new providers.Web3Provider(window.web3.currentProvider, { chainId: networkId })
  let signer = provider.getSigner()
  addMethodsToSigner(signer)

  let address = await signer.getAddress()
  window.signer = { instance: signer, type: 'metamask' }

  return { address, networkId }
}

How can I do this in case of Trezor hardware wallet?

Thanks

1 Answer 1

0

This is how I did in my project:

You need to implement the Signer interface: https://docs.ethers.io/ethers.js/html/api-wallet.html#signer-api

And in order to implement this interface for Trezor hardware wallet, you need to use this library https://github.com/trezor/connect

It has functions such as:

https://github.com/trezor/connect/blob/develop/docs/methods/ethereumSignTransaction.md

https://github.com/trezor/connect/blob/develop/docs/methods/signMessage.md

NOTE: There are some functions with "ethereum" prefix. They are what you should use if you are creating signer for Ethereum.

Function signTransaction (no prefix) is for Bitcoin. It confused me at the beginning because the parameters for Bitcoin and Ethereum are completely different.

Check discusstion here for more detail: https://github.com/ethers-io/ethers.js/issues/118#issuecomment-443574917

Sample code:

import { Signer, providers, utils } from 'ethers';
import TrezorConnect from 'trezor-connect';

import { NETWORK_URL } from '../../../config/url';
import { addMethodsToSigner } from './index';

const defaultDPath = "m/44'/60'/0'/0";

export class TrezorSigner extends Signer {
    constructor(path = defaultDPath) {
        super();
        const networkId = 8888;
        this.provider = new providers.JsonRpcProvider(NETWORK_URL, {
            chainId: networkId
        });
        this.address = null;
        window.signer = { instance: this, type: 'hardwareWallet' };
        addMethodsToSigner(this);
    }

    getPublicKey = async (path = defaultDPath) => {
        this.path = path;
        let result = await TrezorConnect.getPublicKey({
            path
        });
        if (result.success) {
            return result.payload;
        }

        console.log(result);
        throw new Error(result.payload.error);
    };

    getAddress = () => {
        return this.address;
    };

    setAddress = (address) => {
        this.address = address;
    }

    signMessage = async message => {
        return new Promise(async (resolve, reject) => {
            let result = await TrezorConnect.ethereumSignMessage({
                path: this.path + '/0',
                message
            });

            if (result.success) {
                resolve(result.payload.signature);
            } else {
                console.error('Error:', result.payload.error); // error message
                reject(result.payload.error);
            }
        });
    };

    sign = async (transaction) => {
        if (transaction.value) {
            transaction.value = utils.hexlify(transaction.value);
        }
        if (transaction.gasPrice) {
            transaction.gasPrice = utils.hexlify(transaction.gasPrice);
        }
        if (transaction.gasLimit) {
            transaction.gasLimit = utils.hexlify(transaction.gasLimit);
        }

        transaction.nonce = utils.hexlify(
            await this.provider.getTransactionCount(this.address)
        );

        let result = await TrezorConnect.ethereumSignTransaction({
            path: this.path + '/0',
            transaction
        });

        if (result.success) {
            let sig = {
                v: parseInt(result.payload.v.substring(2), 16),
                r: result.payload.r,
                s: result.payload.s
            };

            let serializedTransaction = await utils.serializeTransaction(
                transaction,
                sig
            );

            return serializedTransaction;
        }

        throw new Error(result.payload.error);
    };

    sendTransaction = async (transaction) => {
        if (Promise.resolve(transaction.to) === transaction.to) {
            transaction.to = await transaction.to;
        }

        if (!transaction.value) {
            transaction.value = utils.parseEther('0.0');
        }

        let signedTx = await this.sign(transaction);

        return this.provider.sendTransaction(signedTx);
    };
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.