mirror of
https://github.com/status-im/MyCrypto.git
synced 2025-02-08 17:24:30 +00:00
Ether Unit Types and Send UX Improvements (#174)
* remove 'transate' property and ng-scopes * use bigs (surprised flow did not catch this) * fix dropdown not expanding -- switch to simpledropdown * Don't use generics for no real reason * Create Ether, Wei, and GWei types, and annotate. Also contains refactors and UX improvements 1. clear previously generated rawTX/signedTx when changes to transaction inputs are made. 2. reset generated rawTx/signedTx while new generateTx is loading * add toString helper method and use in place of .amount.toString() * support optional base in toString helper method and use * incorporate PR suggestions (destructure, resolve via callback)
This commit is contained in:
parent
b59298ec0e
commit
38dd22953a
@ -51,7 +51,7 @@ import type {
|
|||||||
} from 'libs/transaction';
|
} from 'libs/transaction';
|
||||||
import type { TransactionWithoutGas } from 'libs/messages';
|
import type { TransactionWithoutGas } from 'libs/messages';
|
||||||
import type { UNIT } from 'libs/units';
|
import type { UNIT } from 'libs/units';
|
||||||
import { toWei } from 'libs/units';
|
import { Wei, Ether, GWei } from 'libs/units';
|
||||||
import {
|
import {
|
||||||
generateCompleteTransaction,
|
generateCompleteTransaction,
|
||||||
getBalanceMinusGasCosts,
|
getBalanceMinusGasCosts,
|
||||||
@ -94,13 +94,13 @@ type Props = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
wallet: BaseWallet,
|
wallet: BaseWallet,
|
||||||
balance: Big,
|
balance: Ether,
|
||||||
node: NodeConfig,
|
node: NodeConfig,
|
||||||
nodeLib: RPCNode,
|
nodeLib: RPCNode,
|
||||||
network: NetworkConfig,
|
network: NetworkConfig,
|
||||||
tokens: Token[],
|
tokens: Token[],
|
||||||
tokenBalances: TokenBalance[],
|
tokenBalances: TokenBalance[],
|
||||||
gasPrice: string,
|
gasPrice: Wei,
|
||||||
broadcastTx: (signedTx: string) => BroadcastTxRequestedAction,
|
broadcastTx: (signedTx: string) => BroadcastTxRequestedAction,
|
||||||
showNotification: (
|
showNotification: (
|
||||||
level: string,
|
level: string,
|
||||||
@ -366,6 +366,7 @@ export class SendTransaction extends React.Component {
|
|||||||
this.estimateGas();
|
this.estimateGas();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
this.setState({ generateDisabled: true });
|
||||||
this.props.showNotification('danger', error.message, 5000);
|
this.props.showNotification('danger', error.message, 5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,36 +405,63 @@ export class SendTransaction extends React.Component {
|
|||||||
this.setState({ gasLimit: value, gasChanged: true });
|
this.setState({ gasLimit: value, gasChanged: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
onAmountChange = (value: string, unit: string) => {
|
handleEverythingAmountChange = (value: string, unit: string): string => {
|
||||||
if (value === 'everything') {
|
|
||||||
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 weiBalance = toWei(balance, 'ether');
|
const weiBalance = balance.toWei();
|
||||||
|
const bigGasLimit = new Big(gasLimit);
|
||||||
value = getBalanceMinusGasCosts(
|
value = getBalanceMinusGasCosts(
|
||||||
new Big(gasLimit),
|
bigGasLimit,
|
||||||
new Big(gasPrice),
|
gasPrice,
|
||||||
weiBalance
|
weiBalance
|
||||||
);
|
).toString();
|
||||||
} else {
|
} else {
|
||||||
const tokenBalance = this.props.tokenBalances.find(
|
const tokenBalance = this.props.tokenBalances.find(
|
||||||
tokenBalance => tokenBalance.symbol === unit
|
tokenBalance => tokenBalance.symbol === unit
|
||||||
);
|
);
|
||||||
if (!tokenBalance) {
|
if (!tokenBalance) {
|
||||||
return;
|
throw new Error(`${unit}: not found in token balances;`);
|
||||||
}
|
}
|
||||||
value = tokenBalance.balance.toString();
|
value = tokenBalance.balance.toString();
|
||||||
}
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
onAmountChange = (value: string, unit: string) => {
|
||||||
|
if (value === 'everything') {
|
||||||
|
value = this.handleEverythingAmountChange(value, unit);
|
||||||
|
}
|
||||||
|
let transaction = this.state.transaction;
|
||||||
|
let generateDisabled = this.state.generateDisabled;
|
||||||
|
if (unit && unit !== this.state.unit) {
|
||||||
|
value = '';
|
||||||
|
transaction = null;
|
||||||
|
generateDisabled = true;
|
||||||
}
|
}
|
||||||
let token = this.props.tokens.find(x => x.symbol === unit);
|
let token = this.props.tokens.find(x => x.symbol === unit);
|
||||||
this.setState({
|
this.setState({
|
||||||
value,
|
value,
|
||||||
unit,
|
unit,
|
||||||
token
|
token,
|
||||||
|
transaction,
|
||||||
|
generateDisabled
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
resetJustTx = async (): Promise<*> => {
|
||||||
|
new Promise(resolve => {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
transaction: null
|
||||||
|
},
|
||||||
|
resolve
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
generateTxFromState = async () => {
|
generateTxFromState = async () => {
|
||||||
|
await this.resetJustTx();
|
||||||
const { nodeLib, wallet, gasPrice, network } = this.props;
|
const { nodeLib, wallet, gasPrice, network } = this.props;
|
||||||
const { token, unit, value, to, data, gasLimit } = this.state;
|
const { token, unit, value, to, data, gasLimit } = this.state;
|
||||||
const chainId = network.chainId;
|
const chainId = network.chainId;
|
||||||
@ -444,12 +472,13 @@ export class SendTransaction extends React.Component {
|
|||||||
to,
|
to,
|
||||||
data
|
data
|
||||||
};
|
};
|
||||||
|
const bigGasLimit = new Big(gasLimit);
|
||||||
try {
|
try {
|
||||||
const signedTx = await generateCompleteTransaction(
|
const signedTx = await generateCompleteTransaction(
|
||||||
wallet,
|
wallet,
|
||||||
nodeLib,
|
nodeLib,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
gasLimit,
|
bigGasLimit,
|
||||||
chainId,
|
chainId,
|
||||||
transactionInput
|
transactionInput
|
||||||
);
|
);
|
||||||
@ -483,13 +512,13 @@ export class SendTransaction extends React.Component {
|
|||||||
function mapStateToProps(state: AppState) {
|
function mapStateToProps(state: AppState) {
|
||||||
return {
|
return {
|
||||||
wallet: state.wallet.inst,
|
wallet: state.wallet.inst,
|
||||||
balance: state.wallet.balance,
|
balance: new Ether(state.wallet.balance),
|
||||||
tokenBalances: getTokenBalances(state),
|
tokenBalances: getTokenBalances(state),
|
||||||
node: getNodeConfig(state),
|
node: getNodeConfig(state),
|
||||||
nodeLib: getNodeLib(state),
|
nodeLib: getNodeLib(state),
|
||||||
network: getNetworkConfig(state),
|
network: getNetworkConfig(state),
|
||||||
tokens: getTokens(state),
|
tokens: getTokens(state),
|
||||||
gasPrice: toWei(new Big(getGasPriceGwei(state)), 'gwei').toString(),
|
gasPrice: new GWei(getGasPriceGwei(state)).toWei(),
|
||||||
transactions: state.wallet.transactions
|
transactions: state.wallet.transactions
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
import Big from 'bignumber.js';
|
import Big from 'bignumber.js';
|
||||||
import type { TransactionWithoutGas } from 'libs/messages';
|
import type { TransactionWithoutGas } from 'libs/messages';
|
||||||
import type { Token } from 'config/data';
|
import type { Token } from 'config/data';
|
||||||
|
import type { Wei } from 'libs/units';
|
||||||
|
|
||||||
export interface INode {
|
export interface INode {
|
||||||
getBalance(_address: string): Promise<Big>,
|
getBalance(_address: string): Promise<Wei>,
|
||||||
getTokenBalance(_address: string, _token: Token): Promise<Big>,
|
getTokenBalance(_address: string, _token: Token): Promise<Big>,
|
||||||
getTokenBalances(_address: string, _tokens: Token[]): Promise<Big>,
|
getTokenBalances(_address: string, _tokens: Token[]): Promise<Big>,
|
||||||
estimateGas(_tx: TransactionWithoutGas): Promise<Big>,
|
estimateGas(_tx: TransactionWithoutGas): Promise<Big>,
|
||||||
|
@ -10,6 +10,7 @@ import RPCClient, {
|
|||||||
sendRawTx
|
sendRawTx
|
||||||
} from './client';
|
} from './client';
|
||||||
import type { Token } from 'config/data';
|
import type { Token } from 'config/data';
|
||||||
|
import { Wei } from 'libs/units';
|
||||||
|
|
||||||
export default class RpcNode implements INode {
|
export default class RpcNode implements INode {
|
||||||
client: RPCClient;
|
client: RPCClient;
|
||||||
@ -17,12 +18,12 @@ export default class RpcNode implements INode {
|
|||||||
this.client = new RPCClient(endpoint);
|
this.client = new RPCClient(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBalance(address: string): Promise<Big> {
|
getBalance(address: string): Promise<Wei> {
|
||||||
return this.client.call(getBalance(address)).then(response => {
|
return this.client.call(getBalance(address)).then(response => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
throw new Error(response.error.message);
|
throw new Error(response.error.message);
|
||||||
}
|
}
|
||||||
return new Big(String(response.result));
|
return new Wei(String(response.result));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,17 +4,15 @@ import translate from 'translations';
|
|||||||
import { padToEven, addHexPrefix, toChecksumAddress } from 'ethereumjs-util';
|
import { padToEven, addHexPrefix, toChecksumAddress } from 'ethereumjs-util';
|
||||||
import { isValidETHAddress } from 'libs/validators';
|
import { isValidETHAddress } from 'libs/validators';
|
||||||
import ERC20 from 'libs/erc20';
|
import ERC20 from 'libs/erc20';
|
||||||
import { toTokenUnit } from 'libs/units';
|
import { stripHex, valueToHex } from 'libs/values';
|
||||||
import { stripHex } from 'libs/values';
|
import { Wei, Ether, toTokenUnit } from 'libs/units';
|
||||||
|
import { RPCNode } from 'libs/nodes';
|
||||||
|
import { TransactionWithoutGas } from 'libs/messages';
|
||||||
import type { INode } from 'libs/nodes/INode';
|
import type { INode } from 'libs/nodes/INode';
|
||||||
import type { BaseWallet } from 'libs/wallet';
|
import type { BaseWallet } from 'libs/wallet';
|
||||||
import type { Token } from 'config/data';
|
import type { Token } from 'config/data';
|
||||||
import type EthTx from 'ethereumjs-tx';
|
import type EthTx from 'ethereumjs-tx';
|
||||||
import { toUnit } from 'libs/units';
|
|
||||||
import { valueToHex } from 'libs/values';
|
|
||||||
import type { UNIT } from 'libs/units';
|
import type { UNIT } from 'libs/units';
|
||||||
import { RPCNode } from 'libs/nodes';
|
|
||||||
import { TransactionWithoutGas } from 'libs/messages';
|
|
||||||
|
|
||||||
export type TransactionInput = {
|
export type TransactionInput = {
|
||||||
token: ?Token,
|
token: ?Token,
|
||||||
@ -34,8 +32,8 @@ export type BaseTransaction = {|
|
|||||||
to: string,
|
to: string,
|
||||||
value: string,
|
value: string,
|
||||||
data: string,
|
data: string,
|
||||||
gasLimit: string,
|
gasLimit: Big,
|
||||||
gasPrice: string,
|
gasPrice: Wei,
|
||||||
chainId: number
|
chainId: number
|
||||||
|};
|
|};
|
||||||
|
|
||||||
@ -84,73 +82,69 @@ export async function generateCompleteTransactionFromRawTransaction(
|
|||||||
wallet: BaseWallet,
|
wallet: BaseWallet,
|
||||||
token: ?Token
|
token: ?Token
|
||||||
): Promise<CompleteTransaction> {
|
): Promise<CompleteTransaction> {
|
||||||
|
const { to, data, gasLimit, gasPrice, from, chainId, nonce } = tx;
|
||||||
// Reject bad addresses
|
// Reject bad addresses
|
||||||
if (!isValidETHAddress(tx.to)) {
|
if (!isValidETHAddress(to)) {
|
||||||
throw new Error(translate('ERROR_5'));
|
throw new Error(translate('ERROR_5', true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject token transactions without data
|
// Reject token transactions without data
|
||||||
if (token && !tx.data) {
|
if (token && !data) {
|
||||||
throw new Error('Tokens must be sent with data');
|
throw new Error('Tokens must be sent with data');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject gas limit under 21000 (Minimum for transaction)
|
// Reject gas limit under 21000 (Minimum for transaction)
|
||||||
// Reject if limit over 5000000
|
// Reject if limit over 5000000
|
||||||
// TODO: Make this dynamic, the limit shifts
|
// TODO: Make this dynamic, the limit shifts
|
||||||
const limitBig = new Big(tx.gasLimit);
|
if (gasLimit.lessThan(21000)) {
|
||||||
if (limitBig.lessThan(21000)) {
|
throw new Error('Gas limit must be at least 21000 for transactions');
|
||||||
throw new Error(
|
|
||||||
translate('Gas limit must be at least 21000 for transactions')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
// Reject gasLimit over 5000000gwei
|
||||||
if (limitBig.greaterThan(5000000)) {
|
if (gasLimit.greaterThan(5000000)) {
|
||||||
throw new Error(translate('GETH_GasLimit'));
|
throw new Error(translate('GETH_GasLimit', true));
|
||||||
}
|
}
|
||||||
|
// Reject gasPrice over 1000gwei (1000000000000)
|
||||||
// Reject gas over 1000gwei (1000000000000)
|
if (gasPrice.amount.greaterThan(new Big('1000000000000'))) {
|
||||||
const gasPriceBig = new Big(tx.gasPrice);
|
|
||||||
if (gasPriceBig.greaterThan(new Big('1000000000000'))) {
|
|
||||||
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.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// build gasCost by multiplying gasPrice * gasLimit
|
||||||
|
const gasCost: Wei = new Wei(gasPrice.amount.times(gasLimit));
|
||||||
// Ensure their balance exceeds the amount they're sending
|
// Ensure their balance exceeds the amount they're sending
|
||||||
// TODO: Include gas price too, tokens should probably check ETH too
|
|
||||||
let value;
|
let value;
|
||||||
let balance;
|
let balance;
|
||||||
|
const ETHBalance: Wei = await node.getBalance(from);
|
||||||
if (token) {
|
if (token) {
|
||||||
value = new Big(ERC20.$transfer(tx.data).value);
|
value = new Big(ERC20.$transfer(tx.data).value);
|
||||||
balance = toTokenUnit(await node.getTokenBalance(tx.from, token), token);
|
balance = toTokenUnit(await node.getTokenBalance(tx.from, token), token);
|
||||||
} else {
|
} else {
|
||||||
value = new Big(tx.value);
|
value = new Big(tx.value);
|
||||||
balance = await node.getBalance(tx.from);
|
balance = ETHBalance.amount;
|
||||||
}
|
}
|
||||||
|
if (value.gt(balance)) {
|
||||||
if (value.gte(balance)) {
|
throw new Error(translate('GETH_Balance', true));
|
||||||
throw new Error(translate('GETH_Balance'));
|
}
|
||||||
|
// ensure gas cost is not greaterThan current eth balance
|
||||||
|
// TODO check that eth balance is not lesser than txAmount + gasCost
|
||||||
|
if (gasCost.amount.gt(ETHBalance.amount)) {
|
||||||
|
throw new Error(
|
||||||
|
`gasCost: ${gasCost.amount} greaterThan ETHBalance: ${ETHBalance.amount}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taken from v3's `sanitizeHex`, ensures that the value is a %2 === 0
|
// Taken from v3's `sanitizeHex`, ensures that the value is a %2 === 0
|
||||||
// prefix'd hex value.
|
// prefix'd hex value.
|
||||||
const cleanHex = hex => addHexPrefix(padToEven(stripHex(hex)));
|
const cleanHex = hex => addHexPrefix(padToEven(stripHex(hex)));
|
||||||
|
|
||||||
const cleanedRawTx = {
|
const cleanedRawTx = {
|
||||||
nonce: cleanHex(tx.nonce),
|
nonce: cleanHex(nonce),
|
||||||
gasPrice: cleanHex(new Big(tx.gasPrice).toString(16)),
|
gasPrice: cleanHex(gasPrice.toString(16)),
|
||||||
gasLimit: cleanHex(new Big(tx.gasLimit).toString(16)),
|
gasLimit: cleanHex(gasLimit.toString(16)),
|
||||||
to: cleanHex(tx.to),
|
to: cleanHex(to),
|
||||||
value: token ? '0x00' : cleanHex(value.toString(16)),
|
value: token ? '0x00' : cleanHex(value.toString(16)),
|
||||||
data: tx.data ? cleanHex(tx.data) : '',
|
data: data ? cleanHex(data) : '',
|
||||||
chainId: tx.chainId || 1
|
chainId: chainId || 1
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sign the transaction
|
// Sign the transaction
|
||||||
const rawTxJson = JSON.stringify(cleanedRawTx);
|
const rawTxJson = JSON.stringify(cleanedRawTx);
|
||||||
const signedTx = await wallet.signRawTransaction(cleanedRawTx);
|
const signedTx = await wallet.signRawTransaction(cleanedRawTx);
|
||||||
|
|
||||||
// Repeat all of this shit for Flow typechecking. Sealed objects don't
|
// Repeat all of this shit for Flow typechecking. Sealed objects don't
|
||||||
// like spreads, so we have to be explicit.
|
// like spreads, so we have to be explicit.
|
||||||
return {
|
return {
|
||||||
@ -174,7 +168,7 @@ export async function formatTxInput(
|
|||||||
return {
|
return {
|
||||||
to,
|
to,
|
||||||
from: await wallet.getAddress(),
|
from: await wallet.getAddress(),
|
||||||
value: valueToHex(value),
|
value: valueToHex(new Ether(value)),
|
||||||
data
|
data
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -182,11 +176,12 @@ export async function formatTxInput(
|
|||||||
throw new Error('No matching token');
|
throw new Error('No matching token');
|
||||||
}
|
}
|
||||||
const bigAmount = new Big(value);
|
const bigAmount = new Big(value);
|
||||||
|
const ERC20Data = ERC20.transfer(to, bigAmount);
|
||||||
return {
|
return {
|
||||||
to: token.address,
|
to: token.address,
|
||||||
from: await wallet.getAddress(),
|
from: await wallet.getAddress(),
|
||||||
value: '0x0',
|
value: '0x0',
|
||||||
data: ERC20.transfer(to, toTokenUnit(bigAmount, token))
|
data: ERC20Data
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,28 +189,26 @@ export async function formatTxInput(
|
|||||||
export async function generateCompleteTransaction(
|
export async function generateCompleteTransaction(
|
||||||
wallet: BaseWallet,
|
wallet: BaseWallet,
|
||||||
nodeLib: RPCNode,
|
nodeLib: RPCNode,
|
||||||
gasPrice: string,
|
gasPrice: Wei,
|
||||||
gasLimit: string,
|
gasLimit: Big,
|
||||||
chainId: number,
|
chainId: number,
|
||||||
transactionInput: TransactionInput
|
transactionInput: TransactionInput
|
||||||
): Promise<CompleteTransaction> {
|
): Promise<CompleteTransaction> {
|
||||||
const { token } = transactionInput;
|
const { token } = transactionInput;
|
||||||
|
const { from, to, value, data } = await formatTxInput(
|
||||||
const formattedTx = await formatTxInput(wallet, transactionInput);
|
wallet,
|
||||||
|
transactionInput
|
||||||
const from = await wallet.getAddress();
|
);
|
||||||
|
|
||||||
const transaction: ExtendedRawTransaction = {
|
const transaction: ExtendedRawTransaction = {
|
||||||
nonce: await nodeLib.getTransactionCount(from),
|
nonce: await nodeLib.getTransactionCount(from),
|
||||||
from,
|
from,
|
||||||
to: formattedTx.to,
|
to,
|
||||||
gasLimit,
|
gasLimit,
|
||||||
value: formattedTx.value,
|
value,
|
||||||
data: formattedTx.data,
|
data,
|
||||||
chainId,
|
chainId,
|
||||||
gasPrice
|
gasPrice
|
||||||
};
|
};
|
||||||
|
|
||||||
return await generateCompleteTransactionFromRawTransaction(
|
return await generateCompleteTransactionFromRawTransaction(
|
||||||
nodeLib,
|
nodeLib,
|
||||||
transaction,
|
transaction,
|
||||||
@ -226,11 +219,11 @@ export async function generateCompleteTransaction(
|
|||||||
|
|
||||||
// TODO determine best place for helper function
|
// TODO determine best place for helper function
|
||||||
export function getBalanceMinusGasCosts(
|
export function getBalanceMinusGasCosts(
|
||||||
weiGasLimit: Big,
|
gasLimit: Big,
|
||||||
weiGasPrice: Big,
|
gasPrice: Wei,
|
||||||
weiBalance: Big
|
balance: Wei
|
||||||
): Big {
|
): Ether {
|
||||||
const weiGasCosts = weiGasPrice.times(weiGasLimit);
|
const weiGasCosts = gasPrice.amount.times(gasLimit);
|
||||||
const weiBalanceMinusGasCosts = weiBalance.minus(weiGasCosts);
|
const weiBalanceMinusGasCosts = balance.amount.minus(weiGasCosts);
|
||||||
return toUnit(weiBalanceMinusGasCosts, 'wei', 'ether');
|
return new Ether(weiBalanceMinusGasCosts);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,53 @@ const UNITS = {
|
|||||||
|
|
||||||
export type UNIT = $Keys<typeof UNITS>;
|
export type UNIT = $Keys<typeof UNITS>;
|
||||||
|
|
||||||
|
class Unit {
|
||||||
|
unit: UNIT;
|
||||||
|
amount: Big;
|
||||||
|
|
||||||
|
constructor(amount: Big, unit: UNIT) {
|
||||||
|
if (!(unit in UNITS)) {
|
||||||
|
throw new Error(`Supplied unit: ${unit} is not a valid unit.`);
|
||||||
|
}
|
||||||
|
this.unit = unit;
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(base?: number) {
|
||||||
|
return this.amount.toString(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
toWei(): Wei {
|
||||||
|
return new Wei(toWei(this.amount, this.unit));
|
||||||
|
}
|
||||||
|
|
||||||
|
toGWei(): GWei {
|
||||||
|
return new GWei(toUnit(this.amount, this.unit, 'gwei'));
|
||||||
|
}
|
||||||
|
|
||||||
|
toEther(): Ether {
|
||||||
|
return new Ether(toUnit(this.amount, this.unit, 'ether'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Ether extends Unit {
|
||||||
|
constructor(amount: Big | number | string) {
|
||||||
|
super(new Big(amount), 'ether');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Wei extends Unit {
|
||||||
|
constructor(amount: Big | number | string) {
|
||||||
|
super(new Big(amount), 'wei');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GWei extends Unit {
|
||||||
|
constructor(amount: Big | number | string) {
|
||||||
|
super(new Big(amount), 'gwei');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getValueOfUnit(unit: UNIT) {
|
function getValueOfUnit(unit: UNIT) {
|
||||||
return new Big(UNITS[unit]);
|
return new Big(UNITS[unit]);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import Big from 'bignumber.js';
|
import { Ether } from 'libs/units';
|
||||||
import { toWei } from 'libs/units';
|
|
||||||
|
|
||||||
export function stripHex(address: string): string {
|
export function stripHex(address: string): string {
|
||||||
return address.replace('0x', '').toLowerCase();
|
return address.replace('0x', '').toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function valueToHex(n: Big | number | string): string {
|
export function valueToHex(value: Ether): string {
|
||||||
// Convert it to a Big to handle any and all values.
|
|
||||||
const big = new Big(n);
|
|
||||||
// Values are in ether, so convert to wei for RPC calls
|
// Values are in ether, so convert to wei for RPC calls
|
||||||
const wei = toWei(big, 'ether');
|
const wei = value.toWei();
|
||||||
// Finally, hex it up!
|
// Finally, hex it up!
|
||||||
return `0x${wei.toString(16)}`;
|
return `0x${wei.toString(16)}`;
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@ export default class TrezorWallet extends DeterministicWallet {
|
|||||||
// Args
|
// Args
|
||||||
this.getPath(),
|
this.getPath(),
|
||||||
stripHex(tx.nonce),
|
stripHex(tx.nonce),
|
||||||
stripHex(tx.gasPrice),
|
stripHex(tx.gasPrice.toString()),
|
||||||
stripHex(tx.gasLimit),
|
stripHex(tx.gasLimit.toString()),
|
||||||
stripHex(tx.to),
|
stripHex(tx.to),
|
||||||
stripHex(tx.value),
|
stripHex(tx.value),
|
||||||
stripHex(tx.data),
|
stripHex(tx.data),
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
} from 'libs/wallet';
|
} from 'libs/wallet';
|
||||||
import { INode } from 'libs/nodes/INode';
|
import { INode } from 'libs/nodes/INode';
|
||||||
import { determineKeystoreType } from 'libs/keystore';
|
import { determineKeystoreType } from 'libs/keystore';
|
||||||
|
import type { Wei } from 'libs/units';
|
||||||
import { getNodeLib } from 'selectors/config';
|
import { getNodeLib } from 'selectors/config';
|
||||||
import { getWalletInst, getTokens } from 'selectors/wallet';
|
import { getWalletInst, getTokens } from 'selectors/wallet';
|
||||||
|
|
||||||
@ -38,8 +38,8 @@ function* updateAccountBalance(): Generator<Yield, Return, Next> {
|
|||||||
const node: INode = yield select(getNodeLib);
|
const node: INode = yield select(getNodeLib);
|
||||||
const address = yield wallet.getAddress();
|
const address = yield wallet.getAddress();
|
||||||
// network request
|
// network request
|
||||||
let balance = yield apply(node, node.getBalance, [address]);
|
let balance: Wei = yield apply(node, node.getBalance, [address]);
|
||||||
yield put(setBalance(balance));
|
yield put(setBalance(balance.amount));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put({ type: 'updateAccountBalance_error', error });
|
yield put({ type: 'updateAccountBalance_error', error });
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user