Update trezor-connect.js to v4 (#856)

* Update trezor connect.

* Re-ignore trezor connect, fix typings.
This commit is contained in:
William O'Beirne 2018-01-18 00:16:47 -05:00 committed by Daniel Ternyak
parent 9c9d6c2f61
commit 1713e3fa29
3 changed files with 923 additions and 968 deletions

View File

@ -1,5 +1,5 @@
import DPATHS from 'config/dpaths'; import DPATHS from 'config/dpaths';
import { TrezorWallet } from 'libs/wallet'; import { TrezorWallet, TREZOR_MINIMUM_FIRMWARE } from 'libs/wallet';
import React, { Component } from 'react'; import React, { Component } from 'react';
import translate, { translateRaw } from 'translations'; import translate, { translateRaw } from 'translations';
import TrezorConnect from 'vendor/trezor-connect'; import TrezorConnect from 'vendor/trezor-connect';
@ -97,7 +97,7 @@ export class TrezorDecrypt extends Component<Props, State> {
error: null error: null
}); });
TrezorConnect.getXPubKey( (TrezorConnect as any).getXPubKey(
dPath, dPath,
res => { res => {
if (res.success) { if (res.success) {
@ -114,7 +114,7 @@ export class TrezorDecrypt extends Component<Props, State> {
}); });
} }
}, },
'1.5.2' TREZOR_MINIMUM_FIRMWARE
); );
}; };

View File

@ -10,6 +10,8 @@ import mapValues from 'lodash/mapValues';
import { IFullWallet } from '../IWallet'; import { IFullWallet } from '../IWallet';
import { translateRaw } from 'translations'; import { translateRaw } from 'translations';
export const TREZOR_MINIMUM_FIRMWARE = '1.5.2';
export class TrezorWallet extends DeterministicWallet implements IFullWallet { export class TrezorWallet extends DeterministicWallet implements IFullWallet {
public signRawTransaction(tx: EthTx): Promise<Buffer> { public signRawTransaction(tx: EthTx): Promise<Buffer> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -17,7 +19,7 @@ export class TrezorWallet extends DeterministicWallet implements IFullWallet {
// stripHexPrefixAndLower identical to ethFuncs.getNakedAddress // stripHexPrefixAndLower identical to ethFuncs.getNakedAddress
const cleanedTx = mapValues(mapValues(strTx, stripHexPrefixAndLower), padLeftEven); const cleanedTx = mapValues(mapValues(strTx, stripHexPrefixAndLower), padLeftEven);
TrezorConnect.ethereumSignTx( (TrezorConnect as any).ethereumSignTx(
// Args // Args
this.getPath(), this.getPath(),
cleanedTx.nonce, cleanedTx.nonce,
@ -51,7 +53,6 @@ export class TrezorWallet extends DeterministicWallet implements IFullWallet {
public signMessage = () => Promise.reject(new Error('Signing via Trezor not yet supported.')); public signMessage = () => Promise.reject(new Error('Signing via Trezor not yet supported.'));
// trezor-connect.js doesn't provide the promise return type
public displayAddress = (dPath?: string, index?: number): Promise<any> => { public displayAddress = (dPath?: string, index?: number): Promise<any> => {
if (!dPath) { if (!dPath) {
dPath = this.dPath; dPath = this.dPath;
@ -59,7 +60,20 @@ export class TrezorWallet extends DeterministicWallet implements IFullWallet {
if (!index) { if (!index) {
index = this.index; index = this.index;
} }
return TrezorConnect.ethereumGetAddress(dPath + '/' + index);
return new Promise((resolve, reject) => {
(TrezorConnect as any).ethereumGetAddress(
dPath + '/' + index,
res => {
if (res.error) {
reject(res.error);
} else {
resolve(res);
}
},
TREZOR_MINIMUM_FIRMWARE
);
});
}; };
public getWalletType(): string { public getWalletType(): string {

View File

@ -1,12 +1,12 @@
/* prettier-ignore */
/* eslint-ignore */ /* eslint-ignore */
/* prettier-ignore */
/** /**
* (C) 2017 SatoshiLabs * (C) 2017 SatoshiLabs
* *
* GPLv3 * GPLv3
*/ */
var VERSION = 3; var VERSION = 4;
if (!Array.isArray) { if (!Array.isArray) {
Array.isArray = function(arg) { Array.isArray = function(arg) {
@ -24,7 +24,8 @@ function _fwStrFix(obj, fw) {
return obj; return obj;
} }
('use strict');
'use strict';
var chrome = window.chrome; var chrome = window.chrome;
var IS_CHROME_APP = chrome && chrome.app && chrome.app.window; var IS_CHROME_APP = chrome && chrome.app && chrome.app.window;
@ -37,44 +38,32 @@ var ERR_CHROME_NOT_CONNECTED = 'Internal Chrome popup is not responding.';
var DISABLE_LOGIN_BUTTONS = window.TREZOR_DISABLE_LOGIN_BUTTONS || false; var DISABLE_LOGIN_BUTTONS = window.TREZOR_DISABLE_LOGIN_BUTTONS || false;
var CHROME_URL = window.TREZOR_CHROME_URL || './chrome/wrapper.html'; var CHROME_URL = window.TREZOR_CHROME_URL || './chrome/wrapper.html';
var POPUP_URL =
window.TREZOR_POPUP_URL || 'https://connect.trezor.io/' + VERSION + '/popup/popup.html';
var POPUP_PATH = window.TREZOR_POPUP_PATH || 'https://connect.trezor.io/' + VERSION;
var POPUP_ORIGIN = window.TREZOR_POPUP_ORIGIN || 'https://connect.trezor.io'; var POPUP_ORIGIN = window.TREZOR_POPUP_ORIGIN || 'https://connect.trezor.io';
var POPUP_PATH = window.TREZOR_POPUP_PATH || POPUP_ORIGIN + '/' + VERSION;
var INSIGHT_URLS = window.TREZOR_INSIGHT_URLS || [ if (window.location.hostname === 'localhost' && !window.TREZOR_POPUP_ORIGIN) {
'https://btc-bitcore1.trezor.io/api/', // development settings
'https://btc-bitcore3.trezor.io/api/' //POPUP_ORIGIN = window.location.origin;
]; //POPUP_PATH = POPUP_ORIGIN;
}
var POPUP_URL = window.TREZOR_POPUP_URL || POPUP_PATH + '/popup/popup.html';
var POPUP_INIT_TIMEOUT = 15000; var POPUP_INIT_TIMEOUT = 15000;
/** /**
* Public API. * Public API.
*/ */
function TrezorConnect() {
class TrezorConnect { var manager = new PopupManager();
constructor() {
this.manager = new PopupManager(); /**
this.LOGIN_CSS = '<style>@import url("@connect_path@/login_buttons.css")</style>'; * Popup errors.
this.LOGIN_ONCLICK = */
'TrezorConnect.requestLogin(' + this.ERR_TIMED_OUT = ERR_TIMED_OUT;
"'@hosticon@','@challenge_hidden@','@challenge_visual@','@callback@'" + this.ERR_WINDOW_CLOSED = ERR_WINDOW_CLOSED;
')'; this.ERR_WINDOW_BLOCKED = ERR_WINDOW_BLOCKED;
this.LOGIN_HTML = this.ERR_ALREADY_WAITING = ERR_ALREADY_WAITING;
'<div id="trezorconnect-wrapper">' + this.ERR_CHROME_NOT_CONNECTED = ERR_CHROME_NOT_CONNECTED;
' <a id="trezorconnect-button" onclick="' +
this.LOGIN_ONCLICK +
'">' +
' <span id="trezorconnect-icon"></span>' +
' <span id="trezorconnect-text">@text@</span>' +
' </a>' +
' <span id="trezorconnect-info">' +
' <a id="trezorconnect-infolink" href="https://www.buytrezor.com/"' +
' target="_blank">What is TREZOR?</a>' +
' </span>' +
'</div>';
}
/** /**
* Open the popup for further communication. All API functions open the * Open the popup for further communication. All API functions open the
@ -82,7 +71,7 @@ class TrezorConnect {
* asynchronously, use `open` first to avoid popup blockers. * asynchronously, use `open` first to avoid popup blockers.
* @param {function(?Error)} callback * @param {function(?Error)} callback
*/ */
open(callback) { this.open = function (callback) {
var onchannel = function (result) { var onchannel = function (result) {
if (result instanceof Error) { if (result instanceof Error) {
callback(result); callback(result);
@ -90,41 +79,65 @@ class TrezorConnect {
callback(); callback();
} }
}; };
this.manager.waitForChannel(onchannel); manager.waitForChannel(onchannel);
} };
/** /**
* Close the opened popup, if any. * Close the opened popup, if any.
*/ */
close() { this.close = function () { manager.close(); };
this.manager.close();
}
/** /**
* Enable or disable closing the opened popup after a successful call. * Enable or disable closing the opened popup after a successful call.
* @param {boolean} value * @param {boolean} value
*/ */
closeAfterSuccess(value) { this.closeAfterSuccess = function (value) { manager.closeAfterSuccess = value; };
this.manager.closeAfterSuccess = value;
}
/** /**
* Enable or disable closing the opened popup after a failed call. * Enable or disable closing the opened popup after a failed call.
* @param {boolean} value * @param {boolean} value
*/ */
closeAfterFailure(value) { this.closeAfterFailure = function (value) { manager.closeAfterFailure = value; };
this.manager.closeAfterFailure = value;
}
/** /**
* Set bitcore server * Set bitcore server
* @param {string|Array<string>} value * @param {string|Array<string>} value
*/ */
setBitcoreURLS(value) { this.setBitcoreURLS = function(value) {
if (typeof value === 'string') { if (typeof value === 'string') {
this.manager.bitcoreURLS = [value]; manager.bitcoreURLS = [ value ];
}else if (value instanceof Array) { }else if (value instanceof Array) {
this.manager.bitcoreURLS = value; manager.bitcoreURLS = value;
}
}
/**
* Set currency. Human readable coin name
* @param {string|Array<string>} value
*/
this.setCurrency = function(value) {
if (typeof value === 'string') {
manager.currency = value;
}
}
/**
* Set currency units (mBTC, BTC)
* @param {string|Array<string>} value
*/
this.setCurrencyUnits = function(value) {
if (typeof value === 'string') {
manager.currencyUnits = value;
}
}
/**
* Set coin info json url
* @param {string|Array<string>} value
*/
this.setCoinInfoURL = function(value) {
if (typeof value === 'string') {
manager.coinInfoURL = value;
} }
} }
@ -132,24 +145,27 @@ class TrezorConnect {
* Set max. limit for account discovery * Set max. limit for account discovery
* @param {number} value * @param {number} value
*/ */
setAccountDiscoveryLimit(value) { this.setAccountDiscoveryLimit = function(value) {
if (!isNaN(value)) this.manager.accountDiscoveryLimit = value; if(!isNaN(value))
manager.accountDiscoveryLimit = value;
} }
/** /**
* Set max. gap for account discovery * Set max. gap for account discovery
* @param {number} value * @param {number} value
*/ */
setAccountDiscoveryGapLength(value) { this.setAccountDiscoveryGapLength = function(value) {
if (!isNaN(value)) this.manager.accountDiscoveryGapLength = value; if(!isNaN(value))
manager.accountDiscoveryGapLength = value;
} }
/** /**
* Set discovery BIP44 coin type * Set discovery BIP44 coin type
* @param {number} value * @param {number} value
*/ */
setAccountDiscoveryBip44CoinType(value) { this.setAccountDiscoveryBip44CoinType = function(value) {
if (!isNaN(value)) this.manager.accountDiscoveryBip44CoinType = value; if(!isNaN(value))
manager.accountDiscoveryBip44CoinType = value;
} }
/** /**
@ -172,87 +188,56 @@ class TrezorConnect {
* @param {function(XPubKeyResult)} callback * @param {function(XPubKeyResult)} callback
* @param {?(string|array<number>)} requiredFirmware * @param {?(string|array<number>)} requiredFirmware
*/ */
getXPubKey(path, callback, requiredFirmware) { this.getXPubKey = function (path, callback, requiredFirmware) {
if (typeof path === 'string') { if (typeof path === 'string') {
path = parseHDPath(path); path = parseHDPath(path);
} }
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'xpubkey', type: 'xpubkey',
path: path path: path
}, }, requiredFirmware), callback);
requiredFirmware };
),
callback
);
}
getFreshAddress(callback, requiredFirmware) { this.getFreshAddress = function (callback, requiredFirmware) {
var wrapperCallback = function (result) { var wrapperCallback = function (result) {
if (result.success) { if (result.success) {
callback({success: true, address: result.freshAddress}); callback({success: true, address: result.freshAddress});
} else { } else {
callback(result); callback(result);
} }
};
this.manager.sendWithChannel(
_fwStrFix(
{
type: 'accountinfo'
},
requiredFirmware
),
wrapperCallback
);
} }
getAccountInfo(input, callback, requiredFirmware) { manager.sendWithChannel(_fwStrFix({
type: 'accountinfo'
}, requiredFirmware), wrapperCallback);
}
this.getAccountInfo = function (input, callback, requiredFirmware) {
try { try {
var description = parseAccountInfoInput(input); manager.sendWithChannel(_fwStrFix({
this.manager.sendWithChannel(
_fwStrFix(
{
type: 'accountinfo', type: 'accountinfo',
description: description description: input
}, }, requiredFirmware), callback);
requiredFirmware
),
callback
);
} catch(e) { } catch(e) {
callback({success: false, error: e}); callback({success: false, error: e});
} }
} }
getAllAccountsInfo(callback, requiredFirmware) { this.getAllAccountsInfo = function(callback, requiredFirmware){
try { try {
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'allaccountsinfo', type: 'allaccountsinfo',
description: 'all' description: 'all'
}, }, requiredFirmware), callback);
requiredFirmware
),
callback
);
} catch(e) { } catch(e) {
callback({success: false, error: e}); callback({success: false, error: e});
} }
} }
getBalance(callback, requiredFirmware) { this.getBalance = function (callback, requiredFirmware) {
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'accountinfo' type: 'accountinfo'
}, }, requiredFirmware), callback)
requiredFirmware
),
callback
);
} }
/** /**
@ -274,28 +259,22 @@ class TrezorConnect {
* *
* @see https://github.com/trezor/trezor-common/blob/master/protob/types.proto * @see https://github.com/trezor/trezor-common/blob/master/protob/types.proto
*/ */
signTx(inputs, outputs, callback, requiredFirmware, coin) { this.signTx = function (inputs, outputs, callback, requiredFirmware, coin) {
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'signtx', type: 'signtx',
inputs: inputs, inputs: inputs,
outputs: outputs, outputs: outputs,
coin: coin coin: coin
}, }, requiredFirmware), callback);
requiredFirmware };
),
callback
);
}
// new implementation with ethereum at beginnig // new implementation with ethereum at beginnig
ethereumSignTx() { this.ethereumSignTx = function() {
this.signEthereumTx.apply(this, arguments); this.signEthereumTx.apply(this, arguments);
} }
// old fallback // old fallback
signEthereumTx( this.signEthereumTx = function (
address_n, address_n,
nonce, nonce,
gas_price, gas_price,
@ -313,9 +292,7 @@ class TrezorConnect {
if (typeof address_n === 'string') { if (typeof address_n === 'string') {
address_n = parseHDPath(address_n); address_n = parseHDPath(address_n);
} }
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'signethtx', type: 'signethtx',
address_n: address_n, address_n: address_n,
nonce: nonce, nonce: nonce,
@ -324,13 +301,9 @@ class TrezorConnect {
to: to, to: to,
value: value, value: value,
data: data, data: data,
chain_id: chain_id chain_id: chain_id,
}, }, requiredFirmware), callback);
requiredFirmware };
),
callback
);
}
/** /**
* @typedef TxRecipient * @typedef TxRecipient
@ -348,18 +321,12 @@ class TrezorConnect {
* @param {function(SignTxResult)} callback * @param {function(SignTxResult)} callback
* @param {?(string|array<number>)} requiredFirmware * @param {?(string|array<number>)} requiredFirmware
*/ */
composeAndSignTx(recipients, callback, requiredFirmware) { this.composeAndSignTx = function (recipients, callback, requiredFirmware) {
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'composetx', type: 'composetx',
recipients: recipients recipients: recipients
}, }, requiredFirmware), callback);
requiredFirmware };
),
callback
);
}
/** /**
* @typedef RequestLoginResult * @typedef RequestLoginResult
@ -380,7 +347,13 @@ class TrezorConnect {
* *
* @see https://github.com/trezor/trezor-common/blob/master/protob/messages.proto * @see https://github.com/trezor/trezor-common/blob/master/protob/messages.proto
*/ */
requestLogin(hosticon, challenge_hidden, challenge_visual, callback, requiredFirmware) { this.requestLogin = function (
hosticon,
challenge_hidden,
challenge_visual,
callback,
requiredFirmware
) {
if (typeof callback === 'string') { if (typeof callback === 'string') {
// special case for a login through <trezor:login> button. // special case for a login through <trezor:login> button.
// `callback` is name of global var // `callback` is name of global var
@ -389,19 +362,13 @@ class TrezorConnect {
if (!callback) { if (!callback) {
throw new TypeError('TrezorConnect: login callback not found'); throw new TypeError('TrezorConnect: login callback not found');
} }
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'login', type: 'login',
icon: hosticon, icon: hosticon,
challenge_hidden: challenge_hidden, challenge_hidden: challenge_hidden,
challenge_visual: challenge_visual challenge_visual: challenge_visual
}, }, requiredFirmware), callback);
requiredFirmware };
),
callback
);
}
/** /**
* @typedef SignMessageResult * @typedef SignMessageResult
@ -421,7 +388,13 @@ class TrezorConnect {
* @param {?(string|array<number>)} requiredFirmware * @param {?(string|array<number>)} requiredFirmware
* *
*/ */
signMessage(path, message, callback, opt_coin, requiredFirmware) { this.signMessage = function (
path,
message,
callback,
opt_coin,
requiredFirmware
) {
if (typeof path === 'string') { if (typeof path === 'string') {
path = parseHDPath(path); path = parseHDPath(path);
} }
@ -431,19 +404,13 @@ class TrezorConnect {
if (!callback) { if (!callback) {
throw new TypeError('TrezorConnect: callback not found'); throw new TypeError('TrezorConnect: callback not found');
} }
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'signmsg', type: 'signmsg',
path: path, path: path,
message: message, message: message,
coin: { coin_name: opt_coin } coin: opt_coin,
}, }, requiredFirmware), callback);
requiredFirmware };
),
callback
);
}
/** /**
* Sign an Ethereum message * Sign an Ethereum message
@ -454,25 +421,24 @@ class TrezorConnect {
* @param {?(string|array<number>)} requiredFirmware * @param {?(string|array<number>)} requiredFirmware
* *
*/ */
ethereumSignMessage(path, message, callback, requiredFirmware) { this.ethereumSignMessage = function (
path,
message,
callback,
requiredFirmware
) {
if (typeof path === 'string') { if (typeof path === 'string') {
path = parseHDPath(path); path = parseHDPath(path);
} }
if (!callback) { if (!callback) {
throw new TypeError('TrezorConnect: callback not found'); throw new TypeError('TrezorConnect: callback not found');
} }
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'signethmsg', type: 'signethmsg',
path: path, path: path,
message: message message: message,
}, }, requiredFirmware), callback);
requiredFirmware };
),
callback
);
}
/** /**
* Verify message * Verify message
@ -485,27 +451,28 @@ class TrezorConnect {
* @param {?(string|array<number>)} requiredFirmware * @param {?(string|array<number>)} requiredFirmware
* *
*/ */
verifyMessage(address, signature, message, callback, opt_coin, requiredFirmware) { this.verifyMessage = function (
address,
signature,
message,
callback,
opt_coin,
requiredFirmware
) {
if (!opt_coin) { if (!opt_coin) {
opt_coin = 'Bitcoin'; opt_coin = 'Bitcoin';
} }
if (!callback) { if (!callback) {
throw new TypeError('TrezorConnect: callback not found'); throw new TypeError('TrezorConnect: callback not found');
} }
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'verifymsg', type: 'verifymsg',
address: address, address: address,
signature: signature, signature: signature,
message: message, message: message,
coin: { coin_name: opt_coin } coin: {coin_name: opt_coin},
}, }, requiredFirmware), callback);
requiredFirmware };
),
callback
);
}
/** /**
* Verify ethereum message * Verify ethereum message
@ -517,23 +484,23 @@ class TrezorConnect {
* @param {?(string|array<number>)} requiredFirmware * @param {?(string|array<number>)} requiredFirmware
* *
*/ */
ethereumVerifyMessage(address, signature, message, callback, requiredFirmware) { this.ethereumVerifyMessage = function (
address,
signature,
message,
callback,
requiredFirmware
) {
if (!callback) { if (!callback) {
throw new TypeError('TrezorConnect: callback not found'); throw new TypeError('TrezorConnect: callback not found');
} }
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'verifyethmsg', type: 'verifyethmsg',
address: address, address: address,
signature: signature, signature: signature,
message: message message: message,
}, }, requiredFirmware), callback);
requiredFirmware };
),
callback
);
}
/** /**
* Symmetric key-value encryption * Symmetric key-value encryption
@ -548,7 +515,7 @@ class TrezorConnect {
* @param {?(string|array<number>)} requiredFirmware * @param {?(string|array<number>)} requiredFirmware
* *
*/ */
cipherKeyValue( this.cipherKeyValue = function (
path, path,
key, key,
value, value,
@ -564,7 +531,7 @@ class TrezorConnect {
if (typeof value !== 'string') { if (typeof value !== 'string') {
throw new TypeError('TrezorConnect: Value must be a string'); throw new TypeError('TrezorConnect: Value must be a string');
} }
if (!/^[0-9A-Fa-f]*$/.test(value)) { if (!(/^[0-9A-Fa-f]*$/.test(value))) {
throw new TypeError('TrezorConnect: Value must be hexadecimal'); throw new TypeError('TrezorConnect: Value must be hexadecimal');
} }
if (value.length % 32 !== 0) { if (value.length % 32 !== 0) {
@ -574,9 +541,7 @@ class TrezorConnect {
if (!callback) { if (!callback) {
throw new TypeError('TrezorConnect: callback not found'); throw new TypeError('TrezorConnect: callback not found');
} }
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'cipherkeyvalue', type: 'cipherkeyvalue',
path: path, path: path,
key: key, key: key,
@ -584,50 +549,62 @@ class TrezorConnect {
encrypt: !!encrypt, encrypt: !!encrypt,
ask_on_encrypt: !!ask_on_encrypt, ask_on_encrypt: !!ask_on_encrypt,
ask_on_decrypt: !!ask_on_decrypt ask_on_decrypt: !!ask_on_decrypt
}, }, requiredFirmware), callback);
};
this.nemGetAddress = function (
address_n,
network,
callback,
requiredFirmware requiredFirmware
), ) {
callback if (requiredFirmware == null) {
); requiredFirmware = '1.6.0'; // first firmware that supports NEM
}
if (typeof address_n === 'string') {
address_n = parseHDPath(address_n);
}
manager.sendWithChannel(_fwStrFix({
type: 'nemGetAddress',
address_n: address_n,
network: network,
}, requiredFirmware), callback);
} }
pushTransaction(rawTx, callback) { this.nemSignTx = function (
if (!/^[0-9A-Fa-f]*$/.test(rawTx)) { address_n,
transaction,
callback,
requiredFirmware
) {
if (requiredFirmware == null) {
requiredFirmware = '1.6.0'; // first firmware that supports NEM
}
if (typeof address_n === 'string') {
address_n = parseHDPath(address_n);
}
manager.sendWithChannel(_fwStrFix({
type: 'nemSignTx',
address_n: address_n,
transaction: transaction
}, requiredFirmware), callback);
}
this.pushTransaction = function (
rawTx,
callback
) {
if (!(/^[0-9A-Fa-f]*$/.test(rawTx))) {
throw new TypeError('TrezorConnect: Transaction must be hexadecimal'); throw new TypeError('TrezorConnect: Transaction must be hexadecimal');
} }
if (!callback) { if (!callback) {
throw new TypeError('TrezorConnect: callback not found'); throw new TypeError('TrezorConnect: callback not found');
} }
var tryUrl = function(i) { manager.sendWithChannel({
var insight_url = INSIGHT_URLS[i]; type: 'pushtx',
var xhr = new XMLHttpRequest(); rawTx: rawTx,
var method = 'POST'; }, callback);
var url = insight_url + '/tx/send';
var data = {
rawtx: rawTx
};
xhr.open(method, url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var txid = JSON.parse(xhr.responseText).txid;
callback({ success: true, txid: txid });
} else {
if (i === INSIGHT_URLS.length - 1) {
callback({ error: new Error(xhr.responseText) });
} else {
tryUrl(i + 1);
}
}
}
};
xhr.send(JSON.stringify(data));
};
tryUrl(0);
} }
/** /**
@ -639,63 +616,65 @@ class TrezorConnect {
* @param {?(string|array<number>)} requiredFirmware * @param {?(string|array<number>)} requiredFirmware
* *
*/ */
getAddress(address, coin, segwit, callback, requiredFirmware) { this.getAddress = function (address, coin, segwit, callback, requiredFirmware) {
if (typeof address === 'string') { if (typeof address === 'string') {
address = parseHDPath(address); address = parseHDPath(address);
} }
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'getaddress', type: 'getaddress',
address_n: address, address_n: address,
coin: coin, coin: coin,
segwit: segwit segwit: segwit
}, }, requiredFirmware), callback);
requiredFirmware
),
callback
);
} }
/** /**
* Display ethereum address on device * Display ethereum address on device
* *
* @param {string|array} address * @param {array} address
* @param {string|array<number>} requiredFirmware * @param {?(string|array<number>)} requiredFirmware
* @param {function()} callback
* *
*/ */
ethereumGetAddress(address, requiredFirmware = undefined) { this.ethereumGetAddress = function (address, callback, requiredFirmware) {
if (typeof address === 'string') { if (typeof address === 'string') {
address = parseHDPath(address); address = parseHDPath(address);
} }
return new Promise((resolve, reject) => {
this.manager.sendWithChannel( manager.sendWithChannel(_fwStrFix({
_fwStrFix(
{
type: 'ethgetaddress', type: 'ethgetaddress',
address_n: address address_n: address,
}, }, requiredFirmware), callback);
requiredFirmware
),
response => {
if (response.error) {
reject(response.error.message);
} else {
resolve(response);
}
}
);
});
} }
var LOGIN_CSS =
'<style>@import url("@connect_path@/login_buttons.css")</style>';
var LOGIN_ONCLICK =
'TrezorConnect.requestLogin('
+ "'@hosticon@','@challenge_hidden@','@challenge_visual@','@callback@'"
+ ')';
var LOGIN_HTML =
'<div id="trezorconnect-wrapper">'
+ ' <a id="trezorconnect-button" onclick="' + LOGIN_ONCLICK + '">'
+ ' <span id="trezorconnect-icon"></span>'
+ ' <span id="trezorconnect-text">@text@</span>'
+ ' </a>'
+ ' <span id="trezorconnect-info">'
+ ' <a id="trezorconnect-infolink" href="https://www.buytrezor.com/"'
+ ' target="_blank">What is TREZOR?</a>'
+ ' </span>'
+ '</div>';
/** /**
* Find <trezor:login> elements and replace them with login buttons. * Find <trezor:login> elements and replace them with login buttons.
* It's not required to use these special elements, feel free to call * It's not required to use these special elements, feel free to call
* `TrezorConnect.requestLogin` directly. * `TrezorConnect.requestLogin` directly.
*/ */
renderLoginButtons() { this.renderLoginButtons = function () {
var elements = document.getElementsByTagName('trezor:login'); var elements = document.getElementsByTagName('trezor:login');
for (var i = 0; i < elements.length; i++) { for (var i = 0; i < elements.length; i++) {
@ -709,7 +688,8 @@ class TrezorConnect {
// it's not valid to put markup into attributes, so let users // it's not valid to put markup into attributes, so let users
// supply a raw text and make TREZOR bold // supply a raw text and make TREZOR bold
text = text.replace('TREZOR', '<strong>TREZOR</strong>'); text = text.replace('TREZOR', '<strong>TREZOR</strong>');
e.outerHTML = (this.LOGIN_CSS + this.LOGIN_HTML) e.outerHTML =
(LOGIN_CSS + LOGIN_HTML)
.replace('@text@', text) .replace('@text@', text)
.replace('@callback@', callback) .replace('@callback@', callback)
.replace('@hosticon@', hosticon) .replace('@hosticon@', hosticon)
@ -717,7 +697,7 @@ class TrezorConnect {
.replace('@challenge_visual@', challenge_visual) .replace('@challenge_visual@', challenge_visual)
.replace('@connect_path@', POPUP_PATH); .replace('@connect_path@', POPUP_PATH);
} }
} };
} }
/* /*
@ -728,9 +708,7 @@ function parseHDPath(string) {
return string return string
.toLowerCase() .toLowerCase()
.split('/') .split('/')
.filter(function(p) { .filter(function (p) { return p !== 'm'; })
return p !== 'm';
})
.map(function (p) { .map(function (p) {
var hardened = false; var hardened = false;
if (p[p.length - 1] === "'") { if (p[p.length - 1] === "'") {
@ -741,51 +719,13 @@ function parseHDPath(string) {
throw new Error('Not a valid path.'); throw new Error('Not a valid path.');
} }
var n = parseInt(p); var n = parseInt(p);
if (hardened) { if (hardened) { // hardened index
// hardened index
n = (n | 0x80000000) >>> 0; n = (n | 0x80000000) >>> 0;
} }
return n; return n;
}); });
} }
function getIdFromPath(path) {
if (path.length !== 3) {
throw new Error();
}
if (path[0] >>> 0 !== (44 | HD_HARDENED) >>> 0) {
throw new Error();
}
if (path[1] >>> 0 !== (0 | HD_HARDENED) >>> 0) {
throw new Error();
}
return (path[2] & ~HD_HARDENED) >>> 0;
}
// parses first argument from getAccountInfo
function parseAccountInfoInput(input) {
if (input == null) {
return null;
}
if (typeof input === 'string') {
if (input.substr(0, 4) === 'xpub') {
return input;
}
if (isNaN(input)) {
var parsedPath = parseHDPath(input);
return getIdFromPath(parsedPath);
} else {
return parseInt(input);
}
} else if (Array.isArray(input)) {
return getIdFromPath(input);
} else if (typeof input === 'number') {
return input;
}
throw new Error('Unknown input format.');
}
/* /*
* Popup management * Popup management
*/ */
@ -870,14 +810,10 @@ function Popup(url, origin, name, width, height) {
var left = (screen.width - width) / 2; var left = (screen.width - width) / 2;
var top = (screen.height - height) / 2; var top = (screen.height - height) / 2;
var opts = var opts =
'width=' + 'width=' + width +
width + ',height=' + height +
',height=' + ',left=' + left +
height + ',top=' + top +
',left=' +
left +
',top=' +
top +
',menubar=no' + ',menubar=no' +
',toolbar=no' + ',toolbar=no' +
',location=no' + ',location=no' +
@ -903,6 +839,7 @@ function Popup(url, origin, name, width, height) {
} }
function Channel(popup, waiting) { function Channel(popup, waiting) {
var respond = function (data) { var respond = function (data) {
if (waiting) { if (waiting) {
var w = waiting; var w = waiting;
@ -912,7 +849,10 @@ function Channel(popup, waiting) {
}; };
var receive = function (event) { var receive = function (event) {
if (event.source === popup.window && event.origin === popup.origin) { var org1 = event.origin.match(/^.+\:\/\/[^\/]+/)[0];
var org2 = popup.origin.match(/^.+\:\/\/[^\/]+/)[0];
//if (event.source === popup.window && event.origin === popup.origin) {
if (event.source === popup.window && org1 === org2) {
respond(event.data); respond(event.data);
} }
}; };
@ -936,6 +876,7 @@ function Channel(popup, waiting) {
} }
function ConnectedChannel(p) { function ConnectedChannel(p) {
var ready = function () { var ready = function () {
clearTimeout(this.timeout); clearTimeout(this.timeout);
this.popup.onclose = null; this.popup.onclose = null;