UNPKG

trezor-web3-subprovider

Version:
197 lines (196 loc) 9.55 kB
"use strict"; // Based on source from: // https://github.com/web3modal/trezor-provider // Package update and code upgrade by: Filipe Soccol var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TrezorSubprovider = void 0; const utils_1 = require("./utils"); const EthereumTx = require("ethereumjs-tx"); const HDNode = require("hdkey"); const types_1 = require("./types"); const utils_2 = require("./utils"); const baseProvider_1 = require("./baseProvider"); const PRIVATE_KEY_PATH = `44'/60'/0'/0`; const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10; const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000; class TrezorSubprovider extends baseProvider_1.BaseWalletSubprovider { /** * Instantiates a TrezorSubprovider. Defaults to private key path set to `44'/60'/0'/0/`. * Must be initialized with trezor-connect API module https://github.com/trezor/connect. * @param TrezorSubprovider config object containing trezor-connect API * @return TrezorSubprovider instance */ constructor(config) { super(); this._privateKeyPath = PRIVATE_KEY_PATH; this._trezorConnectClientApi = config.trezorConnectClientApi; this._networkId = config.networkId; this._addressSearchLimit = config.accountFetchingConfigs !== undefined && config.accountFetchingConfigs.addressSearchLimit !== undefined ? config.accountFetchingConfigs.addressSearchLimit : DEFAULT_ADDRESS_SEARCH_LIMIT; } /** * Retrieve a users Trezor account. This method is automatically called * when issuing a `eth_accounts` JSON RPC request via your providerEngine * instance. * @return An array of accounts */ getAccountsAsync(numberOfAccounts = DEFAULT_NUM_ADDRESSES_TO_FETCH) { return __awaiter(this, void 0, void 0, function* () { const initialDerivedKeyInfo = yield this._initialDerivedKeyInfoAsync(); const derivedKeyInfos = utils_2.walletUtils.calculateDerivedHDKeyInfos(initialDerivedKeyInfo, numberOfAccounts); const accounts = derivedKeyInfos.map(k => k.address); return accounts; }); } /** * Signs a transaction on the Trezor with the account specificed by the `from` field in txParams. * If you've added the TrezorSubprovider to your app's provider, you can simply send an `eth_sendTransaction` * JSON RPC request, and this method will be called auto-magically. If you are not using this via a ProviderEngine * instance, you can call it directly. * @param txParams Parameters of the transaction to sign * @return Signed transaction hex string */ signTransactionAsync(txData) { return __awaiter(this, void 0, void 0, function* () { if (txData.from === undefined || !utils_1.addressUtils.isAddress(txData.from)) { throw new Error(types_1.WalletSubproviderErrors.FromAddressMissingOrInvalid); } txData.value = txData.value ? txData.value : '0x0'; txData.data = txData.data ? txData.data : '0x'; txData.gas = txData.gas ? txData.gas : '0x0'; txData.gasPrice = txData.gasPrice ? txData.gasPrice : '0x0'; const initialDerivedKeyInfo = yield this._initialDerivedKeyInfoAsync(); const derivedKeyInfo = this._findDerivedKeyInfoForAddress(initialDerivedKeyInfo, txData.from); const fullDerivationPath = derivedKeyInfo.derivationPath; const response = yield this._trezorConnectClientApi.ethereumSignTransaction({ path: fullDerivationPath, transaction: { to: txData.to, value: txData.value, data: txData.data, chainId: this._networkId, nonce: txData.nonce, gasLimit: txData.gas, gasPrice: txData.gasPrice, }, }); if (response.success) { const payload = response.payload; const tx = new EthereumTx.Transaction(txData); // Set the EIP155 bits const vIndex = 6; tx.raw[vIndex] = Buffer.from([1]); // v const rIndex = 7; tx.raw[rIndex] = Buffer.from([]); // r const sIndex = 8; tx.raw[sIndex] = Buffer.from([]); // s // slice off leading 0x tx.v = Buffer.from(payload.v.slice(2), 'hex'); tx.r = Buffer.from(payload.r.slice(2), 'hex'); tx.s = Buffer.from(payload.s.slice(2), 'hex'); return `0x${tx.serialize().toString('hex')}`; } else { const payload = response.payload; throw new Error(payload.error); } }); } /** * Sign a personal Ethereum signed message. The signing account will be the account * associated with the provided address. If you've added the TrezorSubprovider to * your app's provider, you can simply send an `eth_sign` or `personal_sign` JSON RPC * request, and this method will be called auto-magically. * If you are not using this via a ProviderEngine instance, you can call it directly. * @param data Hex string message to sign * @param address Address of the account to sign with * @return Signature hex string (order: rsv) */ signPersonalMessageAsync(data, address) { return __awaiter(this, void 0, void 0, function* () { if (data === undefined) { throw new Error(types_1.WalletSubproviderErrors.DataMissingForSignPersonalMessage); } const initialDerivedKeyInfo = yield this._initialDerivedKeyInfoAsync(); const derivedKeyInfo = this._findDerivedKeyInfoForAddress(initialDerivedKeyInfo, address); const fullDerivationPath = derivedKeyInfo.derivationPath; const response = yield this._trezorConnectClientApi.ethereumSignMessage({ path: fullDerivationPath, message: data, hex: true, }); if (response.success) { const payload = response.payload; return `0x${payload.signature}`; } else { const payload = response.payload; throw new Error(payload.error); } }); } /** * TODO:: eth_signTypedData is currently not supported on Trezor devices. * @param address Address of the account to sign with * @param data the typed data object * @return Signature hex string (order: rsv) */ // tslint:disable-next-line:prefer-function-over-method signTypedDataAsync(address, typedData) { return __awaiter(this, void 0, void 0, function* () { throw new Error(types_1.WalletSubproviderErrors.MethodNotSupported); }); } _initialDerivedKeyInfoAsync() { return __awaiter(this, void 0, void 0, function* () { if (this._initialDerivedKeyInfo) { return this._initialDerivedKeyInfo; } else { const parentKeyDerivationPath = `m/${this._privateKeyPath}`; const response = yield this._trezorConnectClientApi.getPublicKey({ path: parentKeyDerivationPath, }); if (response.success) { const payload = response.payload; const hdKey = new HDNode(); hdKey.publicKey = new Buffer(payload.publicKey, 'hex'); hdKey.chainCode = new Buffer(payload.chainCode, 'hex'); const address = utils_2.walletUtils.addressOfHDKey(hdKey); const initialDerivedKeyInfo = { hdKey, address, derivationPath: parentKeyDerivationPath, baseDerivationPath: this._privateKeyPath, }; this._initialDerivedKeyInfo = initialDerivedKeyInfo; return initialDerivedKeyInfo; } else { const payload = response.payload; throw new Error(payload.error); } } }); } _findDerivedKeyInfoForAddress(initalHDKey, address) { const matchedDerivedKeyInfo = utils_2.walletUtils.findDerivedKeyInfoForAddressIfExists(address, initalHDKey, this._addressSearchLimit); if (matchedDerivedKeyInfo === undefined) { throw new Error(`${types_1.WalletSubproviderErrors.AddressNotFound}: ${address}`); } return matchedDerivedKeyInfo; } } exports.TrezorSubprovider = TrezorSubprovider;