Address Miscellaneous Todos (#534)

* Fix TODO issues

* Update / Removd old comments

* Update types & Fix todos
This commit is contained in:
James Prado 2017-12-14 19:51:42 -05:00 committed by Daniel Ternyak
parent 43d90c9dae
commit d1174fb324
11 changed files with 50 additions and 165 deletions

View File

@ -38,7 +38,7 @@ export default class AccountInfo extends React.Component<Props, State> {
} }
// TODO: don't use any; // TODO: don't use any;
public toggleShowLongBalance = (e: any) => { public toggleShowLongBalance = (e: React.SyntheticEvent<HTMLSpanElement>) => {
e.preventDefault(); e.preventDefault();
this.setState(state => { this.setState(state => {
return { return {
@ -55,9 +55,7 @@ export default class AccountInfo extends React.Component<Props, State> {
return ( return (
<div className="AccountInfo"> <div className="AccountInfo">
<div className="AccountInfo-section"> <div className="AccountInfo-section">
<h5 className="AccountInfo-section-header"> <h5 className="AccountInfo-section-header">{translate('sidebar_AccountAddr')}</h5>
{translate('sidebar_AccountAddr')}
</h5>
<div className="AccountInfo-address"> <div className="AccountInfo-address">
<div className="AccountInfo-address-icon"> <div className="AccountInfo-address-icon">
<Identicon address={address} size="100%" /> <Identicon address={address} size="100%" />
@ -67,9 +65,7 @@ export default class AccountInfo extends React.Component<Props, State> {
</div> </div>
<div className="AccountInfo-section"> <div className="AccountInfo-section">
<h5 className="AccountInfo-section-header"> <h5 className="AccountInfo-section-header">{translate('sidebar_AccountBal')}</h5>
{translate('sidebar_AccountBal')}
</h5>
<ul className="AccountInfo-list"> <ul className="AccountInfo-list">
<li className="AccountInfo-list-item"> <li className="AccountInfo-list-item">
<span <span
@ -86,20 +82,14 @@ export default class AccountInfo extends React.Component<Props, State> {
/> />
)} )}
</span> </span>
{!balance.isPending ? ( {!balance.isPending ? balance.wei ? <span> {network.name}</span> : null : null}
balance.wei ? (
<span> {network.name}</span>
) : null
) : null}
</li> </li>
</ul> </ul>
</div> </div>
{(!!blockExplorer || !!tokenExplorer) && ( {(!!blockExplorer || !!tokenExplorer) && (
<div className="AccountInfo-section"> <div className="AccountInfo-section">
<h5 className="AccountInfo-section-header"> <h5 className="AccountInfo-section-header">{translate('sidebar_TransHistory')}</h5>
{translate('sidebar_TransHistory')}
</h5>
<ul className="AccountInfo-list"> <ul className="AccountInfo-list">
{!!blockExplorer && ( {!!blockExplorer && (
<li className="AccountInfo-list-item"> <li className="AccountInfo-list-item">

View File

@ -41,11 +41,7 @@ export default class TokenRow extends React.Component<Props, State> {
/> />
)} )}
<span> <span>
<UnitDisplay <UnitDisplay value={balance} decimal={decimal} displayShortBalance={!showLongBalance} />
value={balance}
decimal={decimal}
displayShortBalance={!showLongBalance}
/>
</span> </span>
</td> </td>
<td className="TokenRow-symbol">{symbol}</td> <td className="TokenRow-symbol">{symbol}</td>
@ -53,10 +49,7 @@ export default class TokenRow extends React.Component<Props, State> {
); );
} }
public toggleShowLongBalance = ( public toggleShowLongBalance = (e: React.SyntheticEvent<HTMLTableDataCellElement>) => {
// TODO: don't use any
e: any
) => {
e.preventDefault(); e.preventDefault();
this.setState(state => { this.setState(state => {
return { return {

View File

@ -82,18 +82,14 @@ export class WalletDecrypt extends Component<Props, State> {
password: '' password: ''
}, },
unlock: this.props.unlockKeystore, unlock: this.props.unlockKeystore,
helpLink: `${ helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
knowledgeBaseURL
}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
}, },
'mnemonic-phrase': { 'mnemonic-phrase': {
lid: 'x_Mnemonic', lid: 'x_Mnemonic',
component: MnemonicDecrypt, component: MnemonicDecrypt,
initialParams: {}, initialParams: {},
unlock: this.props.unlockMnemonic, unlock: this.props.unlockMnemonic,
helpLink: `${ helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
knowledgeBaseURL
}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
}, },
'private-key': { 'private-key': {
lid: 'x_PrivKey2', lid: 'x_PrivKey2',
@ -103,9 +99,7 @@ export class WalletDecrypt extends Component<Props, State> {
password: '' password: ''
}, },
unlock: this.props.unlockPrivateKey, unlock: this.props.unlockPrivateKey,
helpLink: `${ helpLink: `${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
knowledgeBaseURL
}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
}, },
'view-only': { 'view-only': {
lid: 'View with Address Only', lid: 'View with Address Only',

View File

@ -14,10 +14,7 @@ export default function Identicon(props: Props) {
? toDataUrl(props.address.toLowerCase()) ? toDataUrl(props.address.toLowerCase())
: ''; : '';
return ( return (
<div <div style={{ position: 'relative', width: size, height: size }} title="Address Identicon">
style={{ position: 'relative', width: size, height: size }}
title="Address Identicon"
>
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
@ -33,7 +30,7 @@ export default function Identicon(props: Props) {
` `
}} }}
/> />
{identiconDataUrl && {identiconDataUrl && (
<img <img
src={identiconDataUrl} src={identiconDataUrl}
style={{ style={{
@ -41,7 +38,8 @@ export default function Identicon(props: Props) {
width: '100%', width: '100%',
height: '100%' height: '100%'
}} }}
/>} />
)}
</div> </div>
); );
} }

View File

@ -104,9 +104,7 @@ export default class DownloadWallet extends Component<Props, State> {
</li> </li>
<li> <li>
<NewTabLink <NewTabLink
href={`${ href={`${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file`}
knowledgeBaseURL
}/private-keys-passwords/difference-beween-private-key-and-keystore-file`}
> >
<strong>{translate('GEN_Help_14')}</strong> <strong>{translate('GEN_Help_14')}</strong>
</NewTabLink> </NewTabLink>

View File

@ -66,9 +66,7 @@ const help = (
</li> </li>
<li> <li>
<NewTabLink <NewTabLink
href={`${ href={`${knowledgeBaseURL}/private-keys-passwords/difference-beween-private-key-and-keystore-file`}
knowledgeBaseURL
}/private-keys-passwords/difference-beween-private-key-and-keystore-file`}
> >
<strong>{translate('GEN_Help_16')}</strong> <strong>{translate('GEN_Help_16')}</strong>
</NewTabLink> </NewTabLink>

View File

@ -31,35 +31,17 @@ import {
import { UnitKey, Wei, getDecimal, toWei } from 'libs/units'; import { UnitKey, Wei, getDecimal, toWei } from 'libs/units';
import { isValidETHAddress } from 'libs/validators'; import { isValidETHAddress } from 'libs/validators';
// LIBS // LIBS
import { import { IWallet, Balance, Web3Wallet, LedgerWallet, TrezorWallet } from 'libs/wallet';
IWallet,
Balance,
Web3Wallet,
LedgerWallet,
TrezorWallet
} from 'libs/wallet';
import pickBy from 'lodash/pickBy'; import pickBy from 'lodash/pickBy';
import React from 'react'; import React from 'react';
// REDUX // REDUX
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { AppState } from 'reducers'; import { AppState } from 'reducers';
import { showNotification, TShowNotification } from 'actions/notifications'; import { showNotification, TShowNotification } from 'actions/notifications';
import { import { broadcastTx, TBroadcastTx, resetWallet, TResetWallet } from 'actions/wallet';
broadcastTx, import { pollOfflineStatus as dPollOfflineStatus, TPollOfflineStatus } from 'actions/config';
TBroadcastTx,
resetWallet,
TResetWallet
} from 'actions/wallet';
import {
pollOfflineStatus as dPollOfflineStatus,
TPollOfflineStatus
} from 'actions/config';
// SELECTORS // SELECTORS
import { import { getGasPriceGwei, getNetworkConfig, getNodeLib } from 'selectors/config';
getGasPriceGwei,
getNetworkConfig,
getNodeLib
} from 'selectors/config';
import { import {
getTokenBalances, getTokenBalances,
getTokens, getTokens,
@ -264,9 +246,7 @@ export class SendTransaction extends React.Component<Props, State> {
const { offline, forceOffline, balance } = this.props; const { offline, forceOffline, balance } = this.props;
const customMessage = customMessages.find(m => m.to === to); const customMessage = customMessages.find(m => m.to === to);
const decimal = const decimal =
unit === 'ether' unit === 'ether' ? getDecimal('ether') : (this.state.token && this.state.token.decimal) || 0;
? getDecimal('ether')
: (this.state.token && this.state.token.decimal) || 0;
const isWeb3Wallet = this.props.wallet instanceof Web3Wallet; const isWeb3Wallet = this.props.wallet instanceof Web3Wallet;
const isLedgerWallet = this.props.wallet instanceof LedgerWallet; const isLedgerWallet = this.props.wallet instanceof LedgerWallet;
const isTrezorWallet = this.props.wallet instanceof TrezorWallet; const isTrezorWallet = this.props.wallet instanceof TrezorWallet;
@ -277,9 +257,7 @@ export class SendTransaction extends React.Component<Props, State> {
title={ title={
<div> <div>
{translate('NAV_SendEther')} {translate('NAV_SendEther')}
{offline || forceOffline ? ( {offline || forceOffline ? <span style={{ color: 'red' }}> (Offline)</span> : null}
<span style={{ color: 'red' }}> (Offline)</span>
) : null}
</div> </div>
} }
allowReadOnly={true} allowReadOnly={true}
@ -314,24 +292,14 @@ export class SendTransaction extends React.Component<Props, State> {
isReadOnly={readOnly} isReadOnly={readOnly}
onUnitChange={this.onUnitChange} onUnitChange={this.onUnitChange}
/> />
<GasField <GasField value={gasLimit} onChange={readOnly ? void 0 : this.onGasChange} />
value={gasLimit}
onChange={readOnly ? void 0 : this.onGasChange}
/>
{(offline || forceOffline) && ( {(offline || forceOffline) && (
<div> <div>
<NonceField <NonceField value={nonce} onChange={this.onNonceChange} placeholder={'0'} />
value={nonce}
onChange={this.onNonceChange}
placeholder={'0'}
/>
</div> </div>
)} )}
{unit === 'ether' && ( {unit === 'ether' && (
<DataField <DataField value={data} onChange={readOnly ? void 0 : this.onDataChange} />
value={data}
onChange={readOnly ? void 0 : this.onDataChange}
/>
)} )}
<CustomMessage message={customMessage} /> <CustomMessage message={customMessage} />
@ -341,9 +309,7 @@ export class SendTransaction extends React.Component<Props, State> {
disabled={this.state.generateDisabled} disabled={this.state.generateDisabled}
className="btn btn-info btn-block" className="btn btn-info btn-block"
onClick={ onClick={
isWeb3Wallet isWeb3Wallet ? this.generateWeb3TxFromState : this.generateTxFromState
? this.generateWeb3TxFromState
: this.generateTxFromState
} }
> >
{isWeb3Wallet {isWeb3Wallet
@ -392,17 +358,12 @@ export class SendTransaction extends React.Component<Props, State> {
/> />
{offline && ( {offline && (
<p> <p>
To broadcast this transaction, paste the above To broadcast this transaction, paste the above into{' '}
into{' '}
<a href="https://myetherwallet.com/pushTx"> <a href="https://myetherwallet.com/pushTx">
{' '} {' '}
myetherwallet.com/pushTx myetherwallet.com/pushTx
</a>{' '} </a>{' '}
or{' '} or <a href="https://etherscan.io/pushTx"> etherscan.io/pushTx</a>
<a href="https://etherscan.io/pushTx">
{' '}
etherscan.io/pushTx
</a>
</p> </p>
)} )}
</div> </div>
@ -432,9 +393,7 @@ export class SendTransaction extends React.Component<Props, State> {
<main className="col-sm-8"> <main className="col-sm-8">
<div className="Tab-content-pane"> <div className="Tab-content-pane">
<h4>Sorry...</h4> <h4>Sorry...</h4>
<p> <p>MetaMask / Mist wallets are not available in offline mode.</p>
MetaMask / Mist wallets are not available in offline mode.
</p>
</div> </div>
</main> </main>
)} )}
@ -579,23 +538,14 @@ export class SendTransaction extends React.Component<Props, State> {
this.setState({ gasLimit: value, gasChanged: true }); this.setState({ gasLimit: value, gasChanged: true });
}; };
public handleEverythingAmountChange = ( public handleEverythingAmountChange = (value: string, unit: string): string => {
value: string,
unit: string
): string => {
if (unit === 'ether') { if (unit === 'ether') {
const { balance, gasPrice } = this.props; const { balance, gasPrice } = this.props;
const { gasLimit } = this.state; const { gasLimit } = this.state;
const bigGasLimit = Wei(gasLimit); const bigGasLimit = Wei(gasLimit);
value = getBalanceMinusGasCosts( value = getBalanceMinusGasCosts(bigGasLimit, gasPrice, balance.wei).toString();
bigGasLimit,
gasPrice,
balance.wei
).toString();
} else { } else {
const tokenBalance = this.props.tokenBalances.find( const tokenBalance = this.props.tokenBalances.find(tBalance => tBalance.symbol === unit);
tBalance => tBalance.symbol === unit
);
if (!tokenBalance) { if (!tokenBalance) {
throw new Error(`${unit}: not found in token balances;`); throw new Error(`${unit}: not found in token balances;`);
} }
@ -673,10 +623,7 @@ export class SendTransaction extends React.Component<Props, State> {
if (network.blockExplorer !== undefined) { if (network.blockExplorer !== undefined) {
this.props.showNotification( this.props.showNotification(
'success', 'success',
<TransactionSucceeded <TransactionSucceeded txHash={txHash} blockExplorer={network.blockExplorer} />,
txHash={txHash}
blockExplorer={network.blockExplorer}
/>,
0 0
); );
} }

View File

@ -1,5 +1,6 @@
// TODO support events, constructors, fallbacks, array slots, types // TODO support events, constructors, fallbacks, array slots, types
import abi from 'ethereumjs-abi'; import abi from 'ethereumjs-abi';
import BN from 'bn.js';
// There are too many to enumerate since they're somewhat dynamic, list here // There are too many to enumerate since they're somewhat dynamic, list here
// https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#types // https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#types
@ -24,8 +25,7 @@ export type ABI = ABIMethod[];
export interface DecodedCall { export interface DecodedCall {
method: ABIMethod; method: ABIMethod;
// TODO: Type this to be an array of BNs when we switch args: BN[];
args: any[];
} }
// Contract helper, returns data for given call // Contract helper, returns data for given call
@ -52,23 +52,17 @@ export default class Contract {
} }
public getMethodSelector(method: ABIMethod): string { public getMethodSelector(method: ABIMethod): string {
return abi return abi.methodID(method.name, this.getMethodTypes(method)).toString('hex');
.methodID(method.name, this.getMethodTypes(method))
.toString('hex');
} }
public call(name: string, args: any[]): string { public call(name: string, args: any[]): string {
const method = this.getMethodAbi(name); const method = this.getMethodAbi(name);
return ( return '0x' + this.getMethodSelector(method) + this.encodeArgs(method, args);
'0x' + this.getMethodSelector(method) + this.encodeArgs(method, args)
);
} }
public $call(data: string): DecodedCall { public $call(data: string): DecodedCall {
const method = this.abi.find( const method = this.abi.find(mth => data.indexOf(this.getMethodSelector(mth)) !== -1);
mth => data.indexOf(this.getMethodSelector(mth)) !== -1
);
if (!method) { if (!method) {
throw new Error('Unknown method'); throw new Error('Unknown method');
@ -89,8 +83,7 @@ export default class Contract {
return abi.rawEncode(inputTypes, args).toString('hex'); return abi.rawEncode(inputTypes, args).toString('hex');
} }
// TODO: Type this return to be an array of BNs when we switch public decodeArgs(method: ABIMethod, argData: string): BN[] {
public decodeArgs(method: ABIMethod, argData: string): any[] {
// Remove method selector from data, if present // Remove method selector from data, if present
argData = argData.replace(`0x${this.getMethodSelector(method)}`, ''); argData = argData.replace(`0x${this.getMethodSelector(method)}`, '');
// Convert argdata to a hex buffer for ethereumjs-abi // Convert argdata to a hex buffer for ethereumjs-abi

View File

@ -1,4 +1,3 @@
// TODO - move this out of transaction; it's only for estimating gas costs
export interface TransactionWithoutGas { export interface TransactionWithoutGas {
to: string; to: string;
value: string; value: string;

View File

@ -2,7 +2,6 @@ import { Token } from 'config/data';
import EthTx from 'ethereumjs-tx'; import EthTx from 'ethereumjs-tx';
import { addHexPrefix, padToEven, toChecksumAddress } from 'ethereumjs-util'; import { addHexPrefix, padToEven, toChecksumAddress } from 'ethereumjs-util';
import ERC20 from 'libs/erc20'; import ERC20 from 'libs/erc20';
import { TransactionWithoutGas } from 'libs/messages';
import { RPCNode } from 'libs/nodes'; import { RPCNode } from 'libs/nodes';
import { INode } from 'libs/nodes/INode'; import { INode } from 'libs/nodes/INode';
import { UnitKey, Wei, TokenValue, toTokenBase } from 'libs/units'; import { UnitKey, Wei, TokenValue, toTokenBase } from 'libs/units';
@ -71,10 +70,7 @@ export function getTransactionFields(tx: EthTx) {
}; };
} }
function getValue( function getValue(token: Token | null | undefined, tx: ExtendedRawTransaction): Wei {
token: Token | null | undefined,
tx: ExtendedRawTransaction
): Wei {
let value; let value;
if (token) { if (token) {
value = Wei(ERC20.$transfer(tx.data).value); value = Wei(ERC20.$transfer(tx.data).value);
@ -93,10 +89,7 @@ async function getBalance(
const ETHBalance = await node.getBalance(from); const ETHBalance = await node.getBalance(from);
let balance: Wei; let balance: Wei;
if (token) { if (token) {
balance = toTokenBase( balance = toTokenBase(await node.getTokenBalance(tx.from, token).toString(), token.decimal);
await node.getTokenBalance(tx.from, token).toString(),
token.decimal
);
} else { } else {
balance = ETHBalance; balance = ETHBalance;
} }
@ -159,9 +152,7 @@ function generateTxValidation(
// Reject gasPrice over 1000gwei (1000000000000) // Reject gasPrice over 1000gwei (1000000000000)
const gwei = Wei('1000000000000'); const gwei = Wei('1000000000000');
if (gasPrice.gt(gwei)) { if (gasPrice.gt(gwei)) {
throw new Error( throw new Error('Gas price too high. Please contact support if this was not a mistake.');
'Gas price too high. Please contact support if this was not a mistake.'
);
} }
} }
@ -215,12 +206,12 @@ export async function generateCompleteTransactionFromRawTransaction(
export async function formatTxInput( export async function formatTxInput(
wallet: IFullWallet, wallet: IFullWallet,
{ token, unit, value, to, data }: TransactionInput { token, unit, value, to, data }: TransactionInput
): Promise<TransactionWithoutGas> { ): Promise<{ to: string; from: string; value: string; data: string }> {
if (unit === 'ether') { if (unit === 'ether') {
return { return {
to, to,
from: await wallet.getAddressString(), from: await wallet.getAddressString(),
value: toHexWei(value), //turn users ether to wei value: toHexWei(value), // turn users ether to wei
data data
}; };
} else { } else {
@ -246,10 +237,7 @@ export async function confirmAndSendWeb3Transaction(
chainId: number, chainId: number,
transactionInput: TransactionInput transactionInput: TransactionInput
): Promise<string> { ): Promise<string> {
const { from, to, value, data } = await formatTxInput( const { from, to, value, data } = await formatTxInput(wallet, transactionInput);
wallet,
transactionInput
);
const transaction: ExtendedRawTransaction = { const transaction: ExtendedRawTransaction = {
nonce: await nodeLib.getTransactionCount(from), nonce: await nodeLib.getTransactionCount(from),
from, from,
@ -276,10 +264,7 @@ export async function generateCompleteTransaction(
offline?: boolean offline?: boolean
): Promise<CompleteTransaction> { ): Promise<CompleteTransaction> {
const { token } = transactionInput; const { token } = transactionInput;
const { from, to, value, data } = await formatTxInput( const { from, to, value, data } = await formatTxInput(wallet, transactionInput);
wallet,
transactionInput
);
const transaction: ExtendedRawTransaction = { const transaction: ExtendedRawTransaction = {
nonce: nonce ? `0x${nonce}` : await nodeLib.getTransactionCount(from), nonce: nonce ? `0x${nonce}` : await nodeLib.getTransactionCount(from),
from, from,
@ -301,20 +286,14 @@ export async function generateCompleteTransaction(
} }
// TODO determine best place for helper function // TODO determine best place for helper function
export function getBalanceMinusGasCosts( export function getBalanceMinusGasCosts(gasLimit: Wei, gasPrice: Wei, balance: Wei): Wei {
gasLimit: Wei,
gasPrice: Wei,
balance: Wei
): Wei {
const weiGasCosts = gasPrice.mul(gasLimit); const weiGasCosts = gasPrice.mul(gasLimit);
const weiBalanceMinusGasCosts = balance.sub(weiGasCosts); const weiBalanceMinusGasCosts = balance.sub(weiGasCosts);
return Wei(weiBalanceMinusGasCosts); return Wei(weiBalanceMinusGasCosts);
} }
export function decodeTransaction(transaction: EthTx, token: Token | false) { export function decodeTransaction(transaction: EthTx, token: Token | false) {
const { to, value, data, gasPrice, nonce, from } = getTransactionFields( const { to, value, data, gasPrice, nonce, from } = getTransactionFields(transaction);
transaction
);
let fixedValue: TokenValue; let fixedValue: TokenValue;
let toAddress; let toAddress;

View File

@ -15,13 +15,9 @@ export function padLeftEven(hex: string) {
return hex.length % 2 !== 0 ? `0${hex}` : hex; return hex.length % 2 !== 0 ? `0${hex}` : hex;
} }
// TODO: refactor to not mutate argument
export function sanitizeHex(hex: string) { export function sanitizeHex(hex: string) {
hex = hex.substring(0, 2) === '0x' ? hex.substring(2) : hex; const hexStr = hex.substring(0, 2) === '0x' ? hex.substring(2) : hex;
if (hex === '') { return hex !== '' ? `0x${padLeftEven(hexStr)}` : '';
return '';
}
return `0x${padLeftEven(hex)}`;
} }
export function networkIdToName(networkId: string | number): string { export function networkIdToName(networkId: string | number): string {