mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
commit
b944a63ff8
@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
|
## 3.12.0 2017-10-25
|
||||||
|
|
||||||
- Add support for alternative ENS TLDs (Ethereum Name Service Top-Level Domains).
|
- Add support for alternative ENS TLDs (Ethereum Name Service Top-Level Domains).
|
||||||
- Lower minimum gas price to 0.1 GWEI.
|
- Lower minimum gas price to 0.1 GWEI.
|
||||||
- Remove web3 injection message from production (thanks to @ChainsawBaby)
|
- Remove web3 injection message from production (thanks to @ChainsawBaby)
|
||||||
|
- Add additional debugging info to our state logs, specifically OS version and browser version.
|
||||||
|
|
||||||
## 3.11.2 2017-10-21
|
## 3.11.2 2017-10-21
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "MetaMask",
|
"name": "MetaMask",
|
||||||
"short_name": "Metamask",
|
"short_name": "Metamask",
|
||||||
"version": "4.0.3",
|
"version": "4.0.4",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"author": "https://metamask.io",
|
"author": "https://metamask.io",
|
||||||
"description": "Ethereum Browser Extension",
|
"description": "Ethereum Browser Extension",
|
||||||
|
@ -133,7 +133,7 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
async newUnapprovedTransaction (txParams) {
|
async newUnapprovedTransaction (txParams) {
|
||||||
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
|
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
|
||||||
const txMeta = await this.addUnapprovedTransaction(txParams)
|
const txMeta = await this.addUnapprovedTransaction(txParams)
|
||||||
this.emit('newUnaprovedTx', txMeta)
|
this.emit('newUnapprovedTx', txMeta)
|
||||||
// listen for tx completion (success, fail)
|
// listen for tx completion (success, fail)
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.txStateManager.once(`${txMeta.id}:finished`, (completedTx) => {
|
this.txStateManager.once(`${txMeta.id}:finished`, (completedTx) => {
|
||||||
@ -170,6 +170,7 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
async addTxDefaults (txMeta) {
|
async addTxDefaults (txMeta) {
|
||||||
const txParams = txMeta.txParams
|
const txParams = txMeta.txParams
|
||||||
// ensure value
|
// ensure value
|
||||||
|
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
|
||||||
const gasPrice = txParams.gasPrice || await this.query.gasPrice()
|
const gasPrice = txParams.gasPrice || await this.query.gasPrice()
|
||||||
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
|
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
|
||||||
txParams.value = txParams.value || '0x0'
|
txParams.value = txParams.value || '0x0'
|
||||||
|
@ -128,7 +128,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
blockTracker: this.blockTracker,
|
blockTracker: this.blockTracker,
|
||||||
ethQuery: this.ethQuery,
|
ethQuery: this.ethQuery,
|
||||||
})
|
})
|
||||||
this.txController.on('newUnaprovedTx', opts.showUnapprovedTx.bind(opts))
|
this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts))
|
||||||
|
|
||||||
// computed balances (accounting for pending transactions)
|
// computed balances (accounting for pending transactions)
|
||||||
this.balancesController = new BalancesController({
|
this.balancesController = new BalancesController({
|
||||||
|
@ -17,6 +17,15 @@ class ExtensionPlatform {
|
|||||||
return extension.runtime.getManifest().version
|
return extension.runtime.getManifest().version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPlatformInfo (cb) {
|
||||||
|
try {
|
||||||
|
extension.runtime.getPlatformInfo((platform) => {
|
||||||
|
cb(null, platform)
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
cb(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ExtensionPlatform
|
module.exports = ExtensionPlatform
|
||||||
|
@ -118,8 +118,8 @@ describe('Transaction Controller', function () {
|
|||||||
stub.restore()
|
stub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should emit newUnaprovedTx event and pass txMeta as the first argument', function (done) {
|
it('should emit newUnapprovedTx event and pass txMeta as the first argument', function (done) {
|
||||||
txController.once('newUnaprovedTx', (txMetaFromEmit) => {
|
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
|
||||||
assert(txMetaFromEmit, 'txMeta is falsey')
|
assert(txMetaFromEmit, 'txMeta is falsey')
|
||||||
assert.equal(txMetaFromEmit.id, 1, 'the right txMeta was passed')
|
assert.equal(txMetaFromEmit.id, 1, 'the right txMeta was passed')
|
||||||
done()
|
done()
|
||||||
@ -129,7 +129,7 @@ describe('Transaction Controller', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should resolve when finished and status is submitted and resolve with the hash', function (done) {
|
it('should resolve when finished and status is submitted and resolve with the hash', function (done) {
|
||||||
txController.once('newUnaprovedTx', (txMetaFromEmit) => {
|
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
txController.setTxHash(txMetaFromEmit.id, '0x0')
|
txController.setTxHash(txMetaFromEmit.id, '0x0')
|
||||||
txController.txStateManager.setTxStatusSubmitted(txMetaFromEmit.id)
|
txController.txStateManager.setTxStatusSubmitted(txMetaFromEmit.id)
|
||||||
@ -145,7 +145,7 @@ describe('Transaction Controller', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should reject when finished and status is rejected', function (done) {
|
it('should reject when finished and status is rejected', function (done) {
|
||||||
txController.once('newUnaprovedTx', (txMetaFromEmit) => {
|
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
txController.txStateManager.setTxStatusRejected(txMetaFromEmit.id)
|
txController.txStateManager.setTxStatusRejected(txMetaFromEmit.id)
|
||||||
}, 10)
|
}, 10)
|
||||||
|
@ -75,6 +75,7 @@ AccountImportSubview.prototype.render = function () {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
onChange: (opt) => {
|
onChange: (opt) => {
|
||||||
|
props.dispatch(actions.showImportPage())
|
||||||
this.setState({ type: opt.value })
|
this.setState({ type: opt.value })
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
@ -115,6 +115,7 @@ var actions = {
|
|||||||
TRANSACTION_ERROR: 'TRANSACTION_ERROR',
|
TRANSACTION_ERROR: 'TRANSACTION_ERROR',
|
||||||
NEXT_TX: 'NEXT_TX',
|
NEXT_TX: 'NEXT_TX',
|
||||||
PREVIOUS_TX: 'PREV_TX',
|
PREVIOUS_TX: 'PREV_TX',
|
||||||
|
EDIT_TX: 'EDIT_TX',
|
||||||
signMsg: signMsg,
|
signMsg: signMsg,
|
||||||
cancelMsg: cancelMsg,
|
cancelMsg: cancelMsg,
|
||||||
signPersonalMsg,
|
signPersonalMsg,
|
||||||
@ -129,10 +130,13 @@ var actions = {
|
|||||||
completedTx: completedTx,
|
completedTx: completedTx,
|
||||||
txError: txError,
|
txError: txError,
|
||||||
nextTx: nextTx,
|
nextTx: nextTx,
|
||||||
|
editTx,
|
||||||
previousTx: previousTx,
|
previousTx: previousTx,
|
||||||
cancelAllTx: cancelAllTx,
|
cancelAllTx: cancelAllTx,
|
||||||
viewPendingTx: viewPendingTx,
|
viewPendingTx: viewPendingTx,
|
||||||
VIEW_PENDING_TX: 'VIEW_PENDING_TX',
|
VIEW_PENDING_TX: 'VIEW_PENDING_TX',
|
||||||
|
updateTransactionParams,
|
||||||
|
UPDATE_TRANSACTION_PARAMS: 'UPDATE_TRANSACTION_PARAMS',
|
||||||
// send screen
|
// send screen
|
||||||
estimateGas,
|
estimateGas,
|
||||||
getGasPrice,
|
getGasPrice,
|
||||||
@ -140,19 +144,23 @@ var actions = {
|
|||||||
UPDATE_GAS_PRICE: 'UPDATE_GAS_PRICE',
|
UPDATE_GAS_PRICE: 'UPDATE_GAS_PRICE',
|
||||||
UPDATE_GAS_TOTAL: 'UPDATE_GAS_TOTAL',
|
UPDATE_GAS_TOTAL: 'UPDATE_GAS_TOTAL',
|
||||||
UPDATE_SEND_FROM: 'UPDATE_SEND_FROM',
|
UPDATE_SEND_FROM: 'UPDATE_SEND_FROM',
|
||||||
|
UPDATE_SEND_TOKEN_BALANCE: 'UPDATE_SEND_TOKEN_BALANCE',
|
||||||
UPDATE_SEND_TO: 'UPDATE_SEND_TO',
|
UPDATE_SEND_TO: 'UPDATE_SEND_TO',
|
||||||
UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT',
|
UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT',
|
||||||
UPDATE_SEND_MEMO: 'UPDATE_SEND_MEMO',
|
UPDATE_SEND_MEMO: 'UPDATE_SEND_MEMO',
|
||||||
UPDATE_SEND_ERRORS: 'UPDATE_SEND_ERRORS',
|
UPDATE_SEND_ERRORS: 'UPDATE_SEND_ERRORS',
|
||||||
|
UPDATE_SEND: 'UPDATE_SEND',
|
||||||
CLEAR_SEND: 'CLEAR_SEND',
|
CLEAR_SEND: 'CLEAR_SEND',
|
||||||
updateGasLimit,
|
updateGasLimit,
|
||||||
updateGasPrice,
|
updateGasPrice,
|
||||||
updateGasTotal,
|
updateGasTotal,
|
||||||
|
updateSendTokenBalance,
|
||||||
updateSendFrom,
|
updateSendFrom,
|
||||||
updateSendTo,
|
updateSendTo,
|
||||||
updateSendAmount,
|
updateSendAmount,
|
||||||
updateSendMemo,
|
updateSendMemo,
|
||||||
updateSendErrors,
|
updateSendErrors,
|
||||||
|
updateSend,
|
||||||
clearSend,
|
clearSend,
|
||||||
setSelectedAddress,
|
setSelectedAddress,
|
||||||
// app messages
|
// app messages
|
||||||
@ -584,6 +592,13 @@ function updateGasTotal (gasTotal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSendTokenBalance (tokenBalance) {
|
||||||
|
return {
|
||||||
|
type: actions.UPDATE_SEND_TOKEN_BALANCE,
|
||||||
|
value: tokenBalance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateSendFrom (from) {
|
function updateSendFrom (from) {
|
||||||
return {
|
return {
|
||||||
type: actions.UPDATE_SEND_FROM,
|
type: actions.UPDATE_SEND_FROM,
|
||||||
@ -619,6 +634,13 @@ function updateSendErrors (error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSend (newSend) {
|
||||||
|
return {
|
||||||
|
type: actions.UPDATE_SEND,
|
||||||
|
value: newSend,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function clearSend () {
|
function clearSend () {
|
||||||
return {
|
return {
|
||||||
type: actions.CLEAR_SEND,
|
type: actions.CLEAR_SEND,
|
||||||
@ -659,6 +681,8 @@ function updateAndApproveTx (txData) {
|
|||||||
log.debug(`actions calling background.updateAndApproveTx`)
|
log.debug(`actions calling background.updateAndApproveTx`)
|
||||||
background.updateAndApproveTransaction(txData, (err) => {
|
background.updateAndApproveTransaction(txData, (err) => {
|
||||||
dispatch(actions.hideLoadingIndication())
|
dispatch(actions.hideLoadingIndication())
|
||||||
|
dispatch(actions.updateTransactionParams(txData.id, txData.txParams))
|
||||||
|
dispatch(actions.clearSend())
|
||||||
if (err) {
|
if (err) {
|
||||||
dispatch(actions.txError(err))
|
dispatch(actions.txError(err))
|
||||||
dispatch(actions.goHome())
|
dispatch(actions.goHome())
|
||||||
@ -676,6 +700,14 @@ function completedTx (id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTransactionParams (id, txParams) {
|
||||||
|
return {
|
||||||
|
type: actions.UPDATE_TRANSACTION_PARAMS,
|
||||||
|
id,
|
||||||
|
value: txParams,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function txError (err) {
|
function txError (err) {
|
||||||
return {
|
return {
|
||||||
type: actions.TRANSACTION_ERROR,
|
type: actions.TRANSACTION_ERROR,
|
||||||
@ -939,6 +971,13 @@ function previousTx () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function editTx (txId) {
|
||||||
|
return {
|
||||||
|
type: actions.EDIT_TX,
|
||||||
|
value: txId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function showConfigPage (transitionForward = true) {
|
function showConfigPage (transitionForward = true) {
|
||||||
return {
|
return {
|
||||||
type: actions.SHOW_CONFIG_PAGE,
|
type: actions.SHOW_CONFIG_PAGE,
|
||||||
|
95
ui/app/components/currency-input.js
Normal file
95
ui/app/components/currency-input.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
|
||||||
|
module.exports = CurrencyInput
|
||||||
|
|
||||||
|
inherits(CurrencyInput, Component)
|
||||||
|
function CurrencyInput (props) {
|
||||||
|
Component.call(this)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
value: sanitizeValue(props.value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeNonDigits (str) {
|
||||||
|
return str.match(/\d|$/g).join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes characters that are not digits, then removes leading zeros
|
||||||
|
function sanitizeInteger (val) {
|
||||||
|
return String(parseInt(removeNonDigits(val) || '0', 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeDecimal (val) {
|
||||||
|
return removeNonDigits(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take a single string param and returns a non-negative integer or float as a string.
|
||||||
|
// Breaks the input into three parts: the integer, the decimal point, and the decimal/fractional part.
|
||||||
|
// Removes leading zeros from the integer, and non-digits from the integer and decimal
|
||||||
|
// The integer is returned as '0' in cases where it would be empty. A decimal point is
|
||||||
|
// included in the returned string if one is included in the param
|
||||||
|
// Examples:
|
||||||
|
// sanitizeValue('0') -> '0'
|
||||||
|
// sanitizeValue('a') -> '0'
|
||||||
|
// sanitizeValue('010.') -> '10.'
|
||||||
|
// sanitizeValue('0.005') -> '0.005'
|
||||||
|
// sanitizeValue('22.200') -> '22.200'
|
||||||
|
// sanitizeValue('.200') -> '0.200'
|
||||||
|
// sanitizeValue('a.b.1.c,89.123') -> '0.189123'
|
||||||
|
function sanitizeValue (value) {
|
||||||
|
let [ , integer, point, decimal] = (/([^.]*)([.]?)([^.]*)/).exec(value)
|
||||||
|
|
||||||
|
integer = sanitizeInteger(integer) || '0'
|
||||||
|
decimal = sanitizeDecimal(decimal)
|
||||||
|
|
||||||
|
return `${integer}${point}${decimal}`
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrencyInput.prototype.handleChange = function (newValue) {
|
||||||
|
const { onInputChange } = this.props
|
||||||
|
|
||||||
|
this.setState({ value: sanitizeValue(newValue) })
|
||||||
|
|
||||||
|
onInputChange(sanitizeValue(newValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If state.value === props.value plus a decimal point, or at least one
|
||||||
|
// zero or a decimal point and at least one zero, then this returns state.value
|
||||||
|
// after it is sanitized with getValueParts
|
||||||
|
CurrencyInput.prototype.getValueToRender = function () {
|
||||||
|
const { value } = this.props
|
||||||
|
const { value: stateValue } = this.state
|
||||||
|
|
||||||
|
const trailingStateString = (new RegExp(`^${value}(.+)`)).exec(stateValue)
|
||||||
|
const trailingDecimalAndZeroes = trailingStateString && (/^[.0]0*/).test(trailingStateString[1])
|
||||||
|
|
||||||
|
return sanitizeValue(trailingDecimalAndZeroes
|
||||||
|
? stateValue
|
||||||
|
: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrencyInput.prototype.render = function () {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
placeholder,
|
||||||
|
readOnly,
|
||||||
|
inputRef,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
const inputSizeMultiplier = readOnly ? 1 : 1.2
|
||||||
|
|
||||||
|
const valueToRender = this.getValueToRender()
|
||||||
|
|
||||||
|
return h('input', {
|
||||||
|
className,
|
||||||
|
value: valueToRender,
|
||||||
|
placeholder,
|
||||||
|
size: valueToRender.length * inputSizeMultiplier,
|
||||||
|
readOnly,
|
||||||
|
onChange: e => this.handleChange(e.target.value),
|
||||||
|
ref: inputRef,
|
||||||
|
})
|
||||||
|
}
|
@ -73,6 +73,8 @@ function getOriginalState (props) {
|
|||||||
gasLimit,
|
gasLimit,
|
||||||
gasTotal,
|
gasTotal,
|
||||||
error: null,
|
error: null,
|
||||||
|
priceSigZeros: '',
|
||||||
|
priceSigDec: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +109,6 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
|
|||||||
const {
|
const {
|
||||||
amount,
|
amount,
|
||||||
balance,
|
balance,
|
||||||
primaryCurrency,
|
|
||||||
selectedToken,
|
selectedToken,
|
||||||
amountConversionRate,
|
amountConversionRate,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
@ -116,10 +117,9 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
|
|||||||
let error = null
|
let error = null
|
||||||
|
|
||||||
const balanceIsSufficient = isBalanceSufficient({
|
const balanceIsSufficient = isBalanceSufficient({
|
||||||
amount,
|
amount: selectedToken ? '0' : amount,
|
||||||
gasTotal,
|
gasTotal,
|
||||||
balance,
|
balance,
|
||||||
primaryCurrency,
|
|
||||||
selectedToken,
|
selectedToken,
|
||||||
amountConversionRate,
|
amountConversionRate,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
@ -170,6 +170,13 @@ CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) {
|
|||||||
|
|
||||||
CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
|
CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
|
||||||
const { gasLimit } = this.state
|
const { gasLimit } = this.state
|
||||||
|
const sigZeros = String(newGasPrice).match(/^\d+[.]\d*?(0+)$/)
|
||||||
|
const sigDec = String(newGasPrice).match(/^\d+([.])0*$/)
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
priceSigZeros: sigZeros && sigZeros[1] || '',
|
||||||
|
priceSigDec: sigDec && sigDec[1] || '',
|
||||||
|
})
|
||||||
|
|
||||||
const gasPrice = conversionUtil(newGasPrice, {
|
const gasPrice = conversionUtil(newGasPrice, {
|
||||||
fromNumericBase: 'dec',
|
fromNumericBase: 'dec',
|
||||||
@ -191,15 +198,17 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
|
|||||||
|
|
||||||
CustomizeGasModal.prototype.render = function () {
|
CustomizeGasModal.prototype.render = function () {
|
||||||
const { hideModal } = this.props
|
const { hideModal } = this.props
|
||||||
const { gasPrice, gasLimit, gasTotal, error } = this.state
|
const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state
|
||||||
|
|
||||||
const convertedGasPrice = conversionUtil(gasPrice, {
|
let convertedGasPrice = conversionUtil(gasPrice, {
|
||||||
fromNumericBase: 'hex',
|
fromNumericBase: 'hex',
|
||||||
toNumericBase: 'dec',
|
toNumericBase: 'dec',
|
||||||
fromDenomination: 'WEI',
|
fromDenomination: 'WEI',
|
||||||
toDenomination: 'GWEI',
|
toDenomination: 'GWEI',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
convertedGasPrice += convertedGasPrice.match(/[.]/) ? priceSigZeros : `${priceSigDec}${priceSigZeros}`
|
||||||
|
|
||||||
const convertedGasLimit = conversionUtil(gasLimit, {
|
const convertedGasLimit = conversionUtil(gasLimit, {
|
||||||
fromNumericBase: 'hex',
|
fromNumericBase: 'hex',
|
||||||
toNumericBase: 'dec',
|
toNumericBase: 'dec',
|
||||||
@ -224,7 +233,7 @@ CustomizeGasModal.prototype.render = function () {
|
|||||||
value: convertedGasPrice,
|
value: convertedGasPrice,
|
||||||
min: MIN_GAS_PRICE_GWEI,
|
min: MIN_GAS_PRICE_GWEI,
|
||||||
// max: 1000,
|
// max: 1000,
|
||||||
step: MIN_GAS_PRICE_GWEI,
|
step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10),
|
||||||
onChange: value => this.convertAndSetGasPrice(value),
|
onChange: value => this.convertAndSetGasPrice(value),
|
||||||
title: 'Gas Price (GWEI)',
|
title: 'Gas Price (GWEI)',
|
||||||
copy: 'We calculate the suggested gas prices based on network success rates.',
|
copy: 'We calculate the suggested gas prices based on network success rates.',
|
||||||
|
@ -31,7 +31,7 @@ class Dropdown extends Component {
|
|||||||
containerClassName,
|
containerClassName,
|
||||||
useCssTransition,
|
useCssTransition,
|
||||||
isOpen,
|
isOpen,
|
||||||
zIndex: 30,
|
zIndex: 55,
|
||||||
onClickOutside,
|
onClickOutside,
|
||||||
style,
|
style,
|
||||||
innerStyle: innerStyleDefaults,
|
innerStyle: innerStyleDefaults,
|
||||||
|
@ -75,11 +75,12 @@ NetworkDropdown.prototype.render = function () {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
containerClassName: 'network-droppo',
|
containerClassName: 'network-droppo',
|
||||||
zIndex: 11,
|
zIndex: 55,
|
||||||
style: {
|
style: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '58px',
|
top: '58px',
|
||||||
minWidth: '309px',
|
minWidth: '309px',
|
||||||
|
zIndex: '55px',
|
||||||
},
|
},
|
||||||
innerStyle: {
|
innerStyle: {
|
||||||
padding: '18px 8px',
|
padding: '18px 8px',
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
const Component = require('react').Component
|
const Component = require('react').Component
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
|
const CurrencyInput = require('./currency-input')
|
||||||
const {
|
const {
|
||||||
addCurrencies,
|
addCurrencies,
|
||||||
conversionGTE,
|
conversionGTE,
|
||||||
conversionLTE,
|
conversionLTE,
|
||||||
toNegative,
|
subtractCurrencies,
|
||||||
} = require('../conversion-util')
|
} = require('../conversion-util')
|
||||||
|
|
||||||
module.exports = InputNumber
|
module.exports = InputNumber
|
||||||
@ -17,18 +18,24 @@ function InputNumber () {
|
|||||||
this.setValue = this.setValue.bind(this)
|
this.setValue = this.setValue.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidInput (text) {
|
||||||
|
const re = /^([1-9]\d*|0)(\.|\.\d*)?$/
|
||||||
|
return re.test(text)
|
||||||
|
}
|
||||||
|
|
||||||
InputNumber.prototype.setValue = function (newValue) {
|
InputNumber.prototype.setValue = function (newValue) {
|
||||||
|
if (newValue && !isValidInput(newValue)) return
|
||||||
const { fixed, min = -1, max = Infinity, onChange } = this.props
|
const { fixed, min = -1, max = Infinity, onChange } = this.props
|
||||||
|
|
||||||
newValue = Number(fixed ? newValue.toFixed(4) : newValue)
|
newValue = fixed ? newValue.toFixed(4) : newValue
|
||||||
|
|
||||||
const newValueGreaterThanMin = conversionGTE(
|
const newValueGreaterThanMin = conversionGTE(
|
||||||
{ value: newValue, fromNumericBase: 'dec' },
|
{ value: newValue || '0', fromNumericBase: 'dec' },
|
||||||
{ value: min, fromNumericBase: 'hex' },
|
{ value: min, fromNumericBase: 'hex' },
|
||||||
)
|
)
|
||||||
|
|
||||||
const newValueLessThanMax = conversionLTE(
|
const newValueLessThanMax = conversionLTE(
|
||||||
{ value: newValue, fromNumericBase: 'dec' },
|
{ value: newValue || '0', fromNumericBase: 'dec' },
|
||||||
{ value: max, fromNumericBase: 'hex' },
|
{ value: max, fromNumericBase: 'hex' },
|
||||||
)
|
)
|
||||||
if (newValueGreaterThanMin && newValueLessThanMax) {
|
if (newValueGreaterThanMin && newValueLessThanMax) {
|
||||||
@ -44,11 +51,13 @@ InputNumber.prototype.render = function () {
|
|||||||
const { unitLabel, step = 1, placeholder, value = 0 } = this.props
|
const { unitLabel, step = 1, placeholder, value = 0 } = this.props
|
||||||
|
|
||||||
return h('div.customize-gas-input-wrapper', {}, [
|
return h('div.customize-gas-input-wrapper', {}, [
|
||||||
h('input.customize-gas-input', {
|
h(CurrencyInput, {
|
||||||
placeholder,
|
className: 'customize-gas-input',
|
||||||
type: 'number',
|
|
||||||
value,
|
value,
|
||||||
onChange: (e) => this.setValue(e.target.value),
|
placeholder,
|
||||||
|
onInputChange: newValue => {
|
||||||
|
this.setValue(newValue)
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
h('span.gas-tooltip-input-detail', {}, [unitLabel]),
|
h('span.gas-tooltip-input-detail', {}, [unitLabel]),
|
||||||
h('div.gas-tooltip-input-arrows', {}, [
|
h('div.gas-tooltip-input-arrows', {}, [
|
||||||
@ -57,7 +66,7 @@ InputNumber.prototype.render = function () {
|
|||||||
}),
|
}),
|
||||||
h('i.fa.fa-angle-down', {
|
h('i.fa.fa-angle-down', {
|
||||||
style: { cursor: 'pointer' },
|
style: { cursor: 'pointer' },
|
||||||
onClick: () => this.setValue(addCurrencies(value, toNegative(step))),
|
onClick: () => this.setValue(subtractCurrencies(value, step)),
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
@ -10,20 +10,7 @@ class LoadingIndicator extends Component {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
h('.full-flex-height', {
|
h('.full-flex-height.loading-overlay', {}, [
|
||||||
style: {
|
|
||||||
left: '0px',
|
|
||||||
zIndex: 50,
|
|
||||||
position: 'absolute',
|
|
||||||
flexDirection: 'column',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
background: 'rgba(255, 255, 255, 0.8)',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('img', {
|
h('img', {
|
||||||
src: 'images/loading.svg',
|
src: 'images/loading.svg',
|
||||||
}),
|
}),
|
||||||
|
@ -3,6 +3,7 @@ const h = require('react-hyperscript')
|
|||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('../../actions')
|
const actions = require('../../actions')
|
||||||
|
const networkNames = require('../../../../app/scripts/config.js').networkNames
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
return {
|
return {
|
||||||
@ -22,6 +23,7 @@ function mapDispatchToProps (dispatch) {
|
|||||||
showAccountDetailModal: () => {
|
showAccountDetailModal: () => {
|
||||||
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
|
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
|
||||||
},
|
},
|
||||||
|
toFaucet: network => dispatch(actions.buyEth({ network })),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +34,20 @@ function BuyOptions () {
|
|||||||
|
|
||||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(BuyOptions)
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(BuyOptions)
|
||||||
|
|
||||||
|
BuyOptions.prototype.renderModalContentOption = function (title, header, onClick) {
|
||||||
|
return h('div.buy-modal-content-option', {
|
||||||
|
onClick,
|
||||||
|
}, [
|
||||||
|
h('div.buy-modal-content-option-title', {}, title),
|
||||||
|
h('div.buy-modal-content-option-subtitle', {}, header),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
BuyOptions.prototype.render = function () {
|
BuyOptions.prototype.render = function () {
|
||||||
|
const { network, toCoinbase, address, toFaucet } = this.props
|
||||||
|
const isTestNetwork = ['3', '4', '42'].find(n => n === network)
|
||||||
|
const networkName = networkNames[network]
|
||||||
|
|
||||||
return h('div', {}, [
|
return h('div', {}, [
|
||||||
h('div.buy-modal-content.transfers-subview', {
|
h('div.buy-modal-content.transfers-subview', {
|
||||||
}, [
|
}, [
|
||||||
@ -47,27 +62,20 @@ BuyOptions.prototype.render = function () {
|
|||||||
|
|
||||||
h('div.buy-modal-content-options.flex-column.flex-center', {}, [
|
h('div.buy-modal-content-options.flex-column.flex-center', {}, [
|
||||||
|
|
||||||
h('div.buy-modal-content-option', {
|
isTestNetwork
|
||||||
onClick: () => {
|
? this.renderModalContentOption(networkName, 'Test Faucet', () => toFaucet(network))
|
||||||
const { toCoinbase, address } = this.props
|
: this.renderModalContentOption('Coinbase', 'Deposit with Fiat', () => toCoinbase(address)),
|
||||||
toCoinbase(address)
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('div.buy-modal-content-option-title', {}, 'Coinbase'),
|
|
||||||
h('div.buy-modal-content-option-subtitle', {}, 'Deposit with Fiat'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
// h('div.buy-modal-content-option', {}, [
|
// h('div.buy-modal-content-option', {}, [
|
||||||
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
|
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
|
||||||
// h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'),
|
// h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'),
|
||||||
// ]),
|
// ]),
|
||||||
|
|
||||||
h('div.buy-modal-content-option', {
|
this.renderModalContentOption(
|
||||||
onClick: () => this.goToAccountDetailsModal(),
|
'Direct Deposit',
|
||||||
}, [
|
'Deposit from another account',
|
||||||
h('div.buy-modal-content-option-title', {}, 'Direct Deposit'),
|
() => this.goToAccountDetailsModal()
|
||||||
h('div.buy-modal-content-option-subtitle', {}, 'Deposit from another account'),
|
),
|
||||||
]),
|
|
||||||
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ const MODALS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const BACKDROPSTYLE = {
|
const BACKDROPSTYLE = {
|
||||||
backgroundColor: 'rgba(245, 245, 245, 0.85)',
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
@ -19,6 +19,7 @@ function mapStateToProps (state) {
|
|||||||
conversionRate,
|
conversionRate,
|
||||||
identities,
|
identities,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
|
send,
|
||||||
} = state.metamask
|
} = state.metamask
|
||||||
const accounts = state.metamask.accounts
|
const accounts = state.metamask.accounts
|
||||||
const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
|
const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
|
||||||
@ -27,12 +28,32 @@ function mapStateToProps (state) {
|
|||||||
identities,
|
identities,
|
||||||
selectedAddress,
|
selectedAddress,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
|
send,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
function mapDispatchToProps (dispatch) {
|
||||||
return {
|
return {
|
||||||
backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
|
clearSend: () => dispatch(actions.clearSend()),
|
||||||
|
editTransaction: txMeta => {
|
||||||
|
const { id, txParams } = txMeta
|
||||||
|
const {
|
||||||
|
gas: gasLimit,
|
||||||
|
gasPrice,
|
||||||
|
to,
|
||||||
|
value: amount,
|
||||||
|
} = txParams
|
||||||
|
dispatch(actions.updateSend({
|
||||||
|
gasLimit,
|
||||||
|
gasPrice,
|
||||||
|
gasTotal: null,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
errors: { to: null, amount: null },
|
||||||
|
editingTransactionId: id,
|
||||||
|
}))
|
||||||
|
dispatch(actions.showSendPage())
|
||||||
|
},
|
||||||
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
|
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +178,7 @@ ConfirmSendEther.prototype.getData = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConfirmSendEther.prototype.render = function () {
|
ConfirmSendEther.prototype.render = function () {
|
||||||
const { backToAccountDetail, selectedAddress, currentCurrency } = this.props
|
const { editTransaction, currentCurrency, clearSend } = this.props
|
||||||
const txMeta = this.gatherTxMeta()
|
const txMeta = this.gatherTxMeta()
|
||||||
const txParams = txMeta.txParams || {}
|
const txParams = txMeta.txParams || {}
|
||||||
|
|
||||||
@ -199,8 +220,8 @@ ConfirmSendEther.prototype.render = function () {
|
|||||||
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
||||||
h('h3.flex-center.confirm-screen-header', [
|
h('h3.flex-center.confirm-screen-header', [
|
||||||
h('button.confirm-screen-back-button', {
|
h('button.confirm-screen-back-button', {
|
||||||
onClick: () => backToAccountDetail(selectedAddress),
|
onClick: () => editTransaction(txMeta),
|
||||||
}, 'BACK'),
|
}, 'EDIT'),
|
||||||
h('div.confirm-screen-title', 'Confirm Transaction'),
|
h('div.confirm-screen-title', 'Confirm Transaction'),
|
||||||
h('div.confirm-screen-header-tip'),
|
h('div.confirm-screen-header-tip'),
|
||||||
]),
|
]),
|
||||||
@ -371,7 +392,10 @@ ConfirmSendEther.prototype.render = function () {
|
|||||||
}, [
|
}, [
|
||||||
// Cancel Button
|
// Cancel Button
|
||||||
h('div.cancel.btn-light.confirm-screen-cancel-button', {
|
h('div.cancel.btn-light.confirm-screen-cancel-button', {
|
||||||
onClick: (event) => this.cancel(event, txMeta),
|
onClick: (event) => {
|
||||||
|
clearSend()
|
||||||
|
this.cancel(event, txMeta)
|
||||||
|
},
|
||||||
}, 'CANCEL'),
|
}, 'CANCEL'),
|
||||||
|
|
||||||
// Accept Button
|
// Accept Button
|
||||||
@ -421,6 +445,26 @@ ConfirmSendEther.prototype.gatherTxMeta = function () {
|
|||||||
const state = this.state
|
const state = this.state
|
||||||
const txData = clone(state.txData) || clone(props.txData)
|
const txData = clone(state.txData) || clone(props.txData)
|
||||||
|
|
||||||
|
if (props.send.editingTransactionId) {
|
||||||
|
const {
|
||||||
|
send: {
|
||||||
|
memo,
|
||||||
|
amount: value,
|
||||||
|
gasLimit: gas,
|
||||||
|
gasPrice,
|
||||||
|
},
|
||||||
|
} = props
|
||||||
|
const { txParams: { from, to } } = txData
|
||||||
|
txData.txParams = {
|
||||||
|
from: ethUtil.addHexPrefix(from),
|
||||||
|
to: ethUtil.addHexPrefix(to),
|
||||||
|
memo: memo && ethUtil.addHexPrefix(memo),
|
||||||
|
value: ethUtil.addHexPrefix(value),
|
||||||
|
gas: ethUtil.addHexPrefix(gas),
|
||||||
|
gasPrice: ethUtil.addHexPrefix(gasPrice),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
|
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
|
||||||
return txData
|
return txData
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@ const Component = require('react').Component
|
|||||||
const { connect } = require('react-redux')
|
const { connect } = require('react-redux')
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const abi = require('human-standard-token-abi')
|
const ethAbi = require('ethereumjs-abi')
|
||||||
|
const tokenAbi = require('human-standard-token-abi')
|
||||||
const abiDecoder = require('abi-decoder')
|
const abiDecoder = require('abi-decoder')
|
||||||
abiDecoder.addABI(abi)
|
abiDecoder.addABI(tokenAbi)
|
||||||
const actions = require('../../actions')
|
const actions = require('../../actions')
|
||||||
const clone = require('clone')
|
const clone = require('clone')
|
||||||
const Identicon = require('../identicon')
|
const Identicon = require('../identicon')
|
||||||
@ -15,12 +16,16 @@ const {
|
|||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
addCurrencies,
|
addCurrencies,
|
||||||
} = require('../../conversion-util')
|
} = require('../../conversion-util')
|
||||||
|
const {
|
||||||
|
calcTokenAmount,
|
||||||
|
} = require('../../token-util')
|
||||||
|
|
||||||
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
|
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getTokenExchangeRate,
|
getTokenExchangeRate,
|
||||||
getSelectedAddress,
|
getSelectedAddress,
|
||||||
|
getSelectedTokenContract,
|
||||||
} = require('../../selectors')
|
} = require('../../selectors')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmSendToken)
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmSendToken)
|
||||||
@ -29,6 +34,7 @@ function mapStateToProps (state, ownProps) {
|
|||||||
const { token: { symbol }, txData } = ownProps
|
const { token: { symbol }, txData } = ownProps
|
||||||
const { txParams } = txData || {}
|
const { txParams } = txData || {}
|
||||||
const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data)
|
const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
conversionRate,
|
conversionRate,
|
||||||
identities,
|
identities,
|
||||||
@ -44,6 +50,8 @@ function mapStateToProps (state, ownProps) {
|
|||||||
tokenExchangeRate,
|
tokenExchangeRate,
|
||||||
tokenData: tokenData || {},
|
tokenData: tokenData || {},
|
||||||
currentCurrency: currentCurrency.toUpperCase(),
|
currentCurrency: currentCurrency.toUpperCase(),
|
||||||
|
send: state.metamask.send,
|
||||||
|
tokenContract: getSelectedTokenContract(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +62,33 @@ function mapDispatchToProps (dispatch, ownProps) {
|
|||||||
backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
|
backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
|
||||||
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
|
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
|
||||||
updateTokenExchangeRate: () => dispatch(actions.updateTokenExchangeRate(symbol)),
|
updateTokenExchangeRate: () => dispatch(actions.updateTokenExchangeRate(symbol)),
|
||||||
|
editTransaction: txMeta => {
|
||||||
|
const { token: { address } } = ownProps
|
||||||
|
const { txParams, id } = txMeta
|
||||||
|
const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data)
|
||||||
|
const { params = [] } = tokenData
|
||||||
|
const { value } = params[1] || {}
|
||||||
|
const amount = conversionUtil(value, {
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
})
|
||||||
|
const {
|
||||||
|
gas: gasLimit,
|
||||||
|
gasPrice,
|
||||||
|
to,
|
||||||
|
} = txParams
|
||||||
|
dispatch(actions.setSelectedToken(address))
|
||||||
|
dispatch(actions.updateSend({
|
||||||
|
gasLimit,
|
||||||
|
gasPrice,
|
||||||
|
gasTotal: null,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
errors: { to: null, amount: null },
|
||||||
|
editingTransactionId: id,
|
||||||
|
}))
|
||||||
|
dispatch(actions.showSendTokenPage())
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,16 +100,34 @@ function ConfirmSendToken () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConfirmSendToken.prototype.componentWillMount = function () {
|
ConfirmSendToken.prototype.componentWillMount = function () {
|
||||||
|
const { tokenContract, selectedAddress } = this.props
|
||||||
|
tokenContract && tokenContract
|
||||||
|
.balanceOf(selectedAddress)
|
||||||
|
.then(usersToken => {
|
||||||
|
})
|
||||||
this.props.updateTokenExchangeRate()
|
this.props.updateTokenExchangeRate()
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfirmSendToken.prototype.getAmount = function () {
|
ConfirmSendToken.prototype.getAmount = function () {
|
||||||
const { conversionRate, tokenExchangeRate, token, tokenData } = this.props
|
const {
|
||||||
|
conversionRate,
|
||||||
|
tokenExchangeRate,
|
||||||
|
token,
|
||||||
|
tokenData,
|
||||||
|
send: { amount, editingTransactionId },
|
||||||
|
} = this.props
|
||||||
const { params = [] } = tokenData
|
const { params = [] } = tokenData
|
||||||
const { value } = params[1] || {}
|
let { value } = params[1] || {}
|
||||||
const { decimals } = token
|
const { decimals } = token
|
||||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
|
||||||
const sendTokenAmount = Number(value / multiplier)
|
if (editingTransactionId) {
|
||||||
|
value = conversionUtil(amount, {
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
toNumericBase: 'dec',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendTokenAmount = calcTokenAmount(value, decimals)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fiat: tokenExchangeRate
|
fiat: tokenExchangeRate
|
||||||
@ -240,9 +293,8 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConfirmSendToken.prototype.render = function () {
|
ConfirmSendToken.prototype.render = function () {
|
||||||
const { backToAccountDetail, selectedAddress } = this.props
|
const { editTransaction } = this.props
|
||||||
const txMeta = this.gatherTxMeta()
|
const txMeta = this.gatherTxMeta()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
from: {
|
from: {
|
||||||
address: fromAddress,
|
address: fromAddress,
|
||||||
@ -264,8 +316,8 @@ ConfirmSendToken.prototype.render = function () {
|
|||||||
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
h('div.confirm-screen-wrapper.flex-column.flex-grow', [
|
||||||
h('h3.flex-center.confirm-screen-header', [
|
h('h3.flex-center.confirm-screen-header', [
|
||||||
h('button.confirm-screen-back-button', {
|
h('button.confirm-screen-back-button', {
|
||||||
onClick: () => backToAccountDetail(selectedAddress),
|
onClick: () => editTransaction(txMeta),
|
||||||
}, 'BACK'),
|
}, 'EDIT'),
|
||||||
h('div.confirm-screen-title', 'Confirm Transaction'),
|
h('div.confirm-screen-title', 'Confirm Transaction'),
|
||||||
h('div.confirm-screen-header-tip'),
|
h('div.confirm-screen-header-tip'),
|
||||||
]),
|
]),
|
||||||
@ -387,6 +439,38 @@ ConfirmSendToken.prototype.gatherTxMeta = function () {
|
|||||||
const state = this.state
|
const state = this.state
|
||||||
const txData = clone(state.txData) || clone(props.txData)
|
const txData = clone(state.txData) || clone(props.txData)
|
||||||
|
|
||||||
|
if (props.send.editingTransactionId) {
|
||||||
|
const {
|
||||||
|
send: {
|
||||||
|
memo,
|
||||||
|
amount,
|
||||||
|
gasLimit: gas,
|
||||||
|
gasPrice,
|
||||||
|
},
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const { txParams: { from, to } } = txData
|
||||||
|
|
||||||
|
const tokenParams = {
|
||||||
|
from: ethUtil.addHexPrefix(from),
|
||||||
|
value: '0',
|
||||||
|
gas: ethUtil.addHexPrefix(gas),
|
||||||
|
gasPrice: ethUtil.addHexPrefix(gasPrice),
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = '0xa9059cbb' + Array.prototype.map.call(
|
||||||
|
ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]),
|
||||||
|
x => ('00' + x.toString(16)).slice(-2)
|
||||||
|
).join('')
|
||||||
|
|
||||||
|
txData.txParams = {
|
||||||
|
...tokenParams,
|
||||||
|
to: ethUtil.addHexPrefix(to),
|
||||||
|
memo: memo && ethUtil.addHexPrefix(memo),
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
|
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
|
||||||
return txData
|
return txData
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ module.exports = connect(mapStateToProps)(AccountListItem)
|
|||||||
|
|
||||||
AccountListItem.prototype.render = function () {
|
AccountListItem.prototype.render = function () {
|
||||||
const {
|
const {
|
||||||
|
className,
|
||||||
account,
|
account,
|
||||||
handleClick,
|
handleClick,
|
||||||
icon = null,
|
icon = null,
|
||||||
@ -34,6 +35,7 @@ AccountListItem.prototype.render = function () {
|
|||||||
const { name, address, balance } = account || {}
|
const { name, address, balance } = account || {}
|
||||||
|
|
||||||
return h('div.account-list-item', {
|
return h('div.account-list-item', {
|
||||||
|
className,
|
||||||
onClick: () => handleClick({ name, address, balance }),
|
onClick: () => handleClick({ name, address, balance }),
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const Component = require('react').Component
|
const Component = require('react').Component
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
|
const CurrencyInput = require('../currency-input')
|
||||||
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
|
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
|
||||||
|
|
||||||
module.exports = CurrencyDisplay
|
module.exports = CurrencyDisplay
|
||||||
@ -8,15 +9,6 @@ module.exports = CurrencyDisplay
|
|||||||
inherits(CurrencyDisplay, Component)
|
inherits(CurrencyDisplay, Component)
|
||||||
function CurrencyDisplay () {
|
function CurrencyDisplay () {
|
||||||
Component.call(this)
|
Component.call(this)
|
||||||
|
|
||||||
this.state = {
|
|
||||||
value: null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidInput (text) {
|
|
||||||
const re = /^([1-9]\d*|0)(\.|\.\d*)?$/
|
|
||||||
return re.test(text)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toHexWei (value) {
|
function toHexWei (value) {
|
||||||
@ -39,6 +31,28 @@ CurrencyDisplay.prototype.getAmount = function (value) {
|
|||||||
: toHexWei(value)
|
: toHexWei(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CurrencyDisplay.prototype.getValueToRender = function () {
|
||||||
|
const { selectedToken, conversionRate, value } = this.props
|
||||||
|
|
||||||
|
const { decimals, symbol } = selectedToken || {}
|
||||||
|
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||||
|
|
||||||
|
return selectedToken
|
||||||
|
? conversionUtil(value, {
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
toCurrency: symbol,
|
||||||
|
conversionRate: multiplier,
|
||||||
|
invertConversionRate: true,
|
||||||
|
})
|
||||||
|
: conversionUtil(value, {
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
toNumericBase: 'dec',
|
||||||
|
fromDenomination: 'WEI',
|
||||||
|
numberOfDecimals: 6,
|
||||||
|
conversionRate,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
CurrencyDisplay.prototype.render = function () {
|
CurrencyDisplay.prototype.render = function () {
|
||||||
const {
|
const {
|
||||||
className = 'currency-display',
|
className = 'currency-display',
|
||||||
@ -49,64 +63,41 @@ CurrencyDisplay.prototype.render = function () {
|
|||||||
convertedCurrency,
|
convertedCurrency,
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
inError = false,
|
inError = false,
|
||||||
value: initValue,
|
|
||||||
handleChange,
|
handleChange,
|
||||||
validate,
|
|
||||||
} = this.props
|
} = this.props
|
||||||
const { value } = this.state
|
|
||||||
|
|
||||||
const initValueToRender = conversionUtil(initValue, {
|
const valueToRender = this.getValueToRender()
|
||||||
fromNumericBase: 'hex',
|
|
||||||
toNumericBase: 'dec',
|
|
||||||
fromDenomination: 'WEI',
|
|
||||||
numberOfDecimals: 6,
|
|
||||||
conversionRate,
|
|
||||||
})
|
|
||||||
|
|
||||||
const convertedValue = conversionUtil(value || initValueToRender, {
|
let convertedValue = conversionUtil(valueToRender, {
|
||||||
fromNumericBase: 'dec',
|
fromNumericBase: 'dec',
|
||||||
fromCurrency: primaryCurrency,
|
fromCurrency: primaryCurrency,
|
||||||
toCurrency: convertedCurrency,
|
toCurrency: convertedCurrency,
|
||||||
numberOfDecimals: 2,
|
numberOfDecimals: 2,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
})
|
})
|
||||||
|
convertedValue = Number(convertedValue).toFixed(2)
|
||||||
const inputSizeMultiplier = readOnly ? 1 : 1.2
|
|
||||||
|
|
||||||
return h('div', {
|
return h('div', {
|
||||||
className,
|
className,
|
||||||
style: {
|
style: {
|
||||||
borderColor: inError ? 'red' : null,
|
borderColor: inError ? 'red' : null,
|
||||||
},
|
},
|
||||||
|
onClick: () => this.currencyInput.focus(),
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
h('div.currency-display__primary-row', [
|
h('div.currency-display__primary-row', [
|
||||||
|
|
||||||
h('div.currency-display__input-wrapper', [
|
h('div.currency-display__input-wrapper', [
|
||||||
|
|
||||||
h('input', {
|
h(CurrencyInput, {
|
||||||
className: primaryBalanceClassName,
|
className: primaryBalanceClassName,
|
||||||
value: `${value || initValueToRender}`,
|
value: `${valueToRender}`,
|
||||||
placeholder: '0',
|
placeholder: '0',
|
||||||
size: (value || initValueToRender).length * inputSizeMultiplier,
|
|
||||||
readOnly,
|
readOnly,
|
||||||
onChange: (event) => {
|
onInputChange: newValue => {
|
||||||
let newValue = event.target.value
|
handleChange(this.getAmount(newValue))
|
||||||
|
|
||||||
if (newValue === '') {
|
|
||||||
newValue = '0'
|
|
||||||
} else if (newValue.match(/^0[1-9]$/)) {
|
|
||||||
newValue = newValue.match(/[1-9]/)[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newValue && !isValidInput(newValue)) {
|
|
||||||
event.preventDefault()
|
|
||||||
} else {
|
|
||||||
validate(this.getAmount(newValue))
|
|
||||||
this.setState({ value: newValue })
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onBlur: event => !readOnly && handleChange(this.getAmount(event.target.value)),
|
inputRef: input => { this.currencyInput = input },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h('span.currency-display__currency-symbol', primaryCurrency),
|
h('span.currency-display__currency-symbol', primaryCurrency),
|
||||||
|
@ -35,6 +35,7 @@ FromDropdown.prototype.renderDropdown = function () {
|
|||||||
h('div.send-v2__from-dropdown__list', {}, [
|
h('div.send-v2__from-dropdown__list', {}, [
|
||||||
|
|
||||||
...accounts.map(account => h(AccountListItem, {
|
...accounts.map(account => h(AccountListItem, {
|
||||||
|
className: 'account-list-item__dropdown',
|
||||||
account,
|
account,
|
||||||
handleClick: () => {
|
handleClick: () => {
|
||||||
onSelect(account)
|
onSelect(account)
|
||||||
|
@ -11,6 +11,7 @@ const MIN_GAS_PRICE_GWEI = ethUtil.addHexPrefix(conversionUtil(MIN_GAS_PRICE_HEX
|
|||||||
toDenomination: 'GWEI',
|
toDenomination: 'GWEI',
|
||||||
fromNumericBase: 'hex',
|
fromNumericBase: 'hex',
|
||||||
toNumericBase: 'hex',
|
toNumericBase: 'hex',
|
||||||
|
numberOfDecimals: 1,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
|
const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
const { addCurrencies, conversionGreaterThan } = require('../../conversion-util')
|
const {
|
||||||
|
addCurrencies,
|
||||||
|
conversionUtil,
|
||||||
|
conversionGTE,
|
||||||
|
} = require('../../conversion-util')
|
||||||
|
const {
|
||||||
|
calcTokenAmount,
|
||||||
|
} = require('../../token-util')
|
||||||
|
|
||||||
function isBalanceSufficient ({
|
function isBalanceSufficient ({
|
||||||
amount,
|
amount = '0x0',
|
||||||
gasTotal,
|
gasTotal = '0x0',
|
||||||
balance,
|
balance,
|
||||||
primaryCurrency,
|
primaryCurrency,
|
||||||
selectedToken,
|
|
||||||
amountConversionRate,
|
amountConversionRate,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
}) {
|
}) {
|
||||||
@ -15,7 +21,7 @@ function isBalanceSufficient ({
|
|||||||
toNumericBase: 'hex',
|
toNumericBase: 'hex',
|
||||||
})
|
})
|
||||||
|
|
||||||
const balanceIsSufficient = conversionGreaterThan(
|
const balanceIsSufficient = conversionGTE(
|
||||||
{
|
{
|
||||||
value: balance,
|
value: balance,
|
||||||
fromNumericBase: 'hex',
|
fromNumericBase: 'hex',
|
||||||
@ -26,13 +32,37 @@ function isBalanceSufficient ({
|
|||||||
value: totalAmount,
|
value: totalAmount,
|
||||||
fromNumericBase: 'hex',
|
fromNumericBase: 'hex',
|
||||||
conversionRate: amountConversionRate,
|
conversionRate: amountConversionRate,
|
||||||
fromCurrency: selectedToken || primaryCurrency,
|
fromCurrency: primaryCurrency,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return balanceIsSufficient
|
return balanceIsSufficient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isTokenBalanceSufficient ({
|
||||||
|
amount = '0x0',
|
||||||
|
tokenBalance,
|
||||||
|
decimals,
|
||||||
|
}) {
|
||||||
|
const amountInDec = conversionUtil(amount, {
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
})
|
||||||
|
|
||||||
|
const tokenBalanceIsSufficient = conversionGTE(
|
||||||
|
{
|
||||||
|
value: tokenBalance,
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: calcTokenAmount(amountInDec, decimals),
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return tokenBalanceIsSufficient
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
isBalanceSufficient,
|
isBalanceSufficient,
|
||||||
|
isTokenBalanceSufficient,
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ const {
|
|||||||
getSendFrom,
|
getSendFrom,
|
||||||
getCurrentCurrency,
|
getCurrentCurrency,
|
||||||
getSelectedTokenToFiatRate,
|
getSelectedTokenToFiatRate,
|
||||||
|
getSelectedTokenContract,
|
||||||
} = require('../../selectors')
|
} = require('../../selectors')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther)
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther)
|
||||||
@ -48,6 +49,7 @@ function mapStateToProps (state) {
|
|||||||
convertedCurrency: getCurrentCurrency(state),
|
convertedCurrency: getCurrentCurrency(state),
|
||||||
data,
|
data,
|
||||||
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
|
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
|
||||||
|
tokenContract: getSelectedTokenContract(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,9 +63,13 @@ function mapDispatchToProps (dispatch) {
|
|||||||
dispatch(actions.signTokenTx(tokenAddress, toAddress, amount, txData))
|
dispatch(actions.signTokenTx(tokenAddress, toAddress, amount, txData))
|
||||||
),
|
),
|
||||||
signTx: txParams => dispatch(actions.signTx(txParams)),
|
signTx: txParams => dispatch(actions.signTx(txParams)),
|
||||||
|
updateAndApproveTx: txParams => dispatch(actions.updateAndApproveTx(txParams)),
|
||||||
setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)),
|
setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)),
|
||||||
addToAddressBook: address => dispatch(actions.addToAddressBook(address)),
|
addToAddressBook: address => dispatch(actions.addToAddressBook(address)),
|
||||||
updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)),
|
updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)),
|
||||||
|
updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)),
|
||||||
|
updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)),
|
||||||
|
updateSendTokenBalance: tokenBalance => dispatch(actions.updateSendTokenBalance(tokenBalance)),
|
||||||
updateSendFrom: newFrom => dispatch(actions.updateSendFrom(newFrom)),
|
updateSendFrom: newFrom => dispatch(actions.updateSendFrom(newFrom)),
|
||||||
updateSendTo: newTo => dispatch(actions.updateSendTo(newTo)),
|
updateSendTo: newTo => dispatch(actions.updateSendTo(newTo)),
|
||||||
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
|
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
|
||||||
@ -71,5 +77,6 @@ function mapDispatchToProps (dispatch) {
|
|||||||
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
|
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
|
||||||
goHome: () => dispatch(actions.goHome()),
|
goHome: () => dispatch(actions.goHome()),
|
||||||
clearSend: () => dispatch(actions.clearSend()),
|
clearSend: () => dispatch(actions.clearSend()),
|
||||||
|
backToConfirmScreen: editingTransactionId => dispatch(actions.showConfTxPage({ id: editingTransactionId })),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ ToAutoComplete.prototype.renderDropdown = function () {
|
|||||||
|
|
||||||
...accountsToRender.map(account => h(AccountListItem, {
|
...accountsToRender.map(account => h(AccountListItem, {
|
||||||
account,
|
account,
|
||||||
|
className: 'account-list-item__dropdown',
|
||||||
handleClick: () => {
|
handleClick: () => {
|
||||||
onChange(account.address)
|
onChange(account.address)
|
||||||
closeDropdown()
|
closeDropdown()
|
||||||
@ -88,7 +89,7 @@ ToAutoComplete.prototype.render = function () {
|
|||||||
inError,
|
inError,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return h('div.to-autocomplete', {}, [
|
return h('div.send-v2__to-autocomplete', {}, [
|
||||||
|
|
||||||
h('input.send-v2__to-autocomplete__input', {
|
h('input.send-v2__to-autocomplete__input', {
|
||||||
placeholder: 'Recipient Address',
|
placeholder: 'Recipient Address',
|
||||||
|
@ -151,10 +151,7 @@ TokenList.prototype.componentDidUpdate = function (nextProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TokenList.prototype.updateBalances = function (tokens) {
|
TokenList.prototype.updateBalances = function (tokens) {
|
||||||
const heldTokens = tokens.filter(token => {
|
this.setState({ tokens, isLoading: false })
|
||||||
return token.balance !== '0' && token.string !== '0.000'
|
|
||||||
})
|
|
||||||
this.setState({ tokens: heldTokens, isLoading: false })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenList.prototype.componentWillUnmount = function () {
|
TokenList.prototype.componentWillUnmount = function () {
|
||||||
|
@ -10,6 +10,7 @@ const Identicon = require('./identicon')
|
|||||||
const contractMap = require('eth-contract-metadata')
|
const contractMap = require('eth-contract-metadata')
|
||||||
|
|
||||||
const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
|
const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
|
||||||
|
const { calcTokenAmount } = require('../token-util')
|
||||||
|
|
||||||
const { getCurrentCurrency } = require('../selectors')
|
const { getCurrentCurrency } = require('../selectors')
|
||||||
|
|
||||||
@ -135,8 +136,7 @@ TxListItem.prototype.getSendTokenTotal = async function () {
|
|||||||
const { params = [] } = decodedData || {}
|
const { params = [] } = decodedData || {}
|
||||||
const { value } = params[1] || {}
|
const { value } = params[1] || {}
|
||||||
const { decimals, symbol } = await this.getTokenInfo()
|
const { decimals, symbol } = await this.getTokenInfo()
|
||||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
const total = calcTokenAmount(value, decimals)
|
||||||
const total = Number(value / multiplier)
|
|
||||||
|
|
||||||
const pair = symbol && `${symbol.toLowerCase()}_eth`
|
const pair = symbol && `${symbol.toLowerCase()}_eth`
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber('1000000000')
|
|||||||
|
|
||||||
// Individual Setters
|
// Individual Setters
|
||||||
const convert = R.invoker(1, 'times')
|
const convert = R.invoker(1, 'times')
|
||||||
const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_DOWN)
|
const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_HALF_DOWN)
|
||||||
const invertConversionRate = conversionRate => () => new BigNumber(1.0).div(conversionRate)
|
const invertConversionRate = conversionRate => () => new BigNumber(1.0).div(conversionRate)
|
||||||
const decToBigNumberViaString = n => R.pipe(String, toBigNumber['dec'])
|
const decToBigNumberViaString = n => R.pipe(String, toBigNumber['dec'])
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ const toNormalizedDenomination = {
|
|||||||
}
|
}
|
||||||
const toSpecifiedDenomination = {
|
const toSpecifiedDenomination = {
|
||||||
WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).round(),
|
WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).round(),
|
||||||
GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).round(1),
|
GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).round(9),
|
||||||
}
|
}
|
||||||
const baseChange = {
|
const baseChange = {
|
||||||
hex: n => n.toString(16),
|
hex: n => n.toString(16),
|
||||||
@ -145,6 +145,20 @@ const addCurrencies = (a, b, options = {}) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const subtractCurrencies = (a, b, options = {}) => {
|
||||||
|
const {
|
||||||
|
aBase,
|
||||||
|
bBase,
|
||||||
|
...conversionOptions
|
||||||
|
} = options
|
||||||
|
const value = (new BigNumber(a, aBase)).minus(b, bBase)
|
||||||
|
|
||||||
|
return converter({
|
||||||
|
value,
|
||||||
|
...conversionOptions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const multiplyCurrencies = (a, b, options = {}) => {
|
const multiplyCurrencies = (a, b, options = {}) => {
|
||||||
const {
|
const {
|
||||||
multiplicandBase,
|
multiplicandBase,
|
||||||
@ -169,6 +183,7 @@ const conversionGreaterThan = (
|
|||||||
) => {
|
) => {
|
||||||
const firstValue = converter({ ...firstProps })
|
const firstValue = converter({ ...firstProps })
|
||||||
const secondValue = converter({ ...secondProps })
|
const secondValue = converter({ ...secondProps })
|
||||||
|
|
||||||
return firstValue.gt(secondValue)
|
return firstValue.gt(secondValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,4 +217,5 @@ module.exports = {
|
|||||||
conversionGTE,
|
conversionGTE,
|
||||||
conversionLTE,
|
conversionLTE,
|
||||||
toNegative,
|
toNegative,
|
||||||
|
subtractCurrencies,
|
||||||
}
|
}
|
||||||
|
@ -69,4 +69,15 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__dropdown {
|
||||||
|
&:hover {
|
||||||
|
background: rgba($alto, .2);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: rgba($alto, .1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
@import './confirm.scss';
|
@import './confirm.scss';
|
||||||
|
|
||||||
|
@import './loading-overlay.scss';
|
||||||
|
|
||||||
// Balances
|
// Balances
|
||||||
@import './hero-balance.scss';
|
@import './hero-balance.scss';
|
||||||
|
|
||||||
|
21
ui/app/css/itcss/components/loading-overlay.scss
Normal file
21
ui/app/css/itcss/components/loading-overlay.scss
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.loading-overlay {
|
||||||
|
left: 0px;
|
||||||
|
z-index: 50;
|
||||||
|
position: absolute;
|
||||||
|
flex-direction: column;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
margin-top: 56px;
|
||||||
|
height: calc(100% - 56px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 576px) {
|
||||||
|
margin-top: 75px;
|
||||||
|
height: calc(100% - 75px);
|
||||||
|
}
|
||||||
|
}
|
@ -577,6 +577,7 @@
|
|||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: $tundora;
|
color: $tundora;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&__close-area {
|
&__close-area {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -591,7 +592,7 @@
|
|||||||
z-index: 1050;
|
z-index: 1050;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 220px;
|
height: 220px;
|
||||||
width: 240px;
|
width: 100%;
|
||||||
border: 1px solid $geyser;
|
border: 1px solid $geyser;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
@ -628,6 +629,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__amount-max {
|
||||||
|
color: $curious-blue;
|
||||||
|
font-family: Roboto;
|
||||||
|
font-size: 12px;
|
||||||
|
left: 8px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
&__gas-fee-display {
|
&__gas-fee-display {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
|
const copyToClipboard = require('copy-to-clipboard')
|
||||||
|
|
||||||
//
|
//
|
||||||
// Sub-Reducers take in the complete state and return their sub-state
|
// Sub-Reducers take in the complete state and return their sub-state
|
||||||
@ -41,17 +42,33 @@ function rootReducer (state, action) {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
window.logState = function () {
|
window.logStateString = function (cb) {
|
||||||
const state = window.METAMASK_CACHED_LOG_STATE
|
const state = window.METAMASK_CACHED_LOG_STATE
|
||||||
let version
|
const version = global.platform.getVersion()
|
||||||
try {
|
const browser = window.navigator.userAgent
|
||||||
version = global.platform.getVersion()
|
return global.platform.getPlatformInfo((err, platform) => {
|
||||||
} catch (e) {
|
if (err) {
|
||||||
version = 'unable to load version.'
|
return cb(err)
|
||||||
}
|
}
|
||||||
state.version = version
|
state.version = version
|
||||||
|
state.platform = platform
|
||||||
|
state.browser = browser
|
||||||
const stateString = JSON.stringify(state, removeSeedWords, 2)
|
const stateString = JSON.stringify(state, removeSeedWords, 2)
|
||||||
return stateString
|
return cb(null, stateString)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.logState = function (toClipboard) {
|
||||||
|
return window.logStateString((err, result) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err.message)
|
||||||
|
} else if (toClipboard) {
|
||||||
|
copyToClipboard(result)
|
||||||
|
console.log('State log copied')
|
||||||
|
} else {
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeSeedWords (key, value) {
|
function removeSeedWords (key, value) {
|
||||||
|
@ -175,6 +175,7 @@ function reduceApp (state, action) {
|
|||||||
name: 'import-menu',
|
name: 'import-menu',
|
||||||
},
|
},
|
||||||
transForward: true,
|
transForward: true,
|
||||||
|
warning: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
case actions.SHOW_INFO_PAGE:
|
case actions.SHOW_INFO_PAGE:
|
||||||
|
@ -27,11 +27,13 @@ function reduceMetamask (state, action) {
|
|||||||
gasLimit: null,
|
gasLimit: null,
|
||||||
gasPrice: null,
|
gasPrice: null,
|
||||||
gasTotal: null,
|
gasTotal: null,
|
||||||
|
tokenBalance: null,
|
||||||
from: '',
|
from: '',
|
||||||
to: '',
|
to: '',
|
||||||
amount: '0x0',
|
amount: '0x0',
|
||||||
memo: '',
|
memo: '',
|
||||||
errors: {},
|
errors: {},
|
||||||
|
editingTransactionId: null,
|
||||||
},
|
},
|
||||||
coinOptions: {},
|
coinOptions: {},
|
||||||
}, state.metamask)
|
}, state.metamask)
|
||||||
@ -107,6 +109,14 @@ function reduceMetamask (state, action) {
|
|||||||
}
|
}
|
||||||
return newState
|
return newState
|
||||||
|
|
||||||
|
case actions.EDIT_TX:
|
||||||
|
return extend(metamaskState, {
|
||||||
|
send: {
|
||||||
|
...metamaskState.send,
|
||||||
|
editingTransactionId: action.value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
case actions.SHOW_NEW_VAULT_SEED:
|
case actions.SHOW_NEW_VAULT_SEED:
|
||||||
return extend(metamaskState, {
|
return extend(metamaskState, {
|
||||||
isUnlocked: true,
|
isUnlocked: true,
|
||||||
@ -140,9 +150,9 @@ function reduceMetamask (state, action) {
|
|||||||
case actions.SAVE_ACCOUNT_LABEL:
|
case actions.SAVE_ACCOUNT_LABEL:
|
||||||
const account = action.value.account
|
const account = action.value.account
|
||||||
const name = action.value.label
|
const name = action.value.label
|
||||||
var id = {}
|
const id = {}
|
||||||
id[account] = extend(metamaskState.identities[account], { name })
|
id[account] = extend(metamaskState.identities[account], { name })
|
||||||
var identities = extend(metamaskState.identities, id)
|
const identities = extend(metamaskState.identities, id)
|
||||||
return extend(metamaskState, { identities })
|
return extend(metamaskState, { identities })
|
||||||
|
|
||||||
case actions.SET_CURRENT_FIAT:
|
case actions.SET_CURRENT_FIAT:
|
||||||
@ -196,6 +206,14 @@ function reduceMetamask (state, action) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.UPDATE_SEND_TOKEN_BALANCE:
|
||||||
|
return extend(metamaskState, {
|
||||||
|
send: {
|
||||||
|
...metamaskState.send,
|
||||||
|
tokenBalance: action.value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
case actions.UPDATE_SEND_FROM:
|
case actions.UPDATE_SEND_FROM:
|
||||||
return extend(metamaskState, {
|
return extend(metamaskState, {
|
||||||
send: {
|
send: {
|
||||||
@ -239,20 +257,44 @@ function reduceMetamask (state, action) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.UPDATE_SEND:
|
||||||
|
return extend(metamaskState, {
|
||||||
|
send: {
|
||||||
|
...metamaskState.send,
|
||||||
|
...action.value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
case actions.CLEAR_SEND:
|
case actions.CLEAR_SEND:
|
||||||
return extend(metamaskState, {
|
return extend(metamaskState, {
|
||||||
send: {
|
send: {
|
||||||
gasLimit: null,
|
gasLimit: null,
|
||||||
gasPrice: null,
|
gasPrice: null,
|
||||||
gasTotal: null,
|
gasTotal: null,
|
||||||
|
tokenBalance: null,
|
||||||
from: '',
|
from: '',
|
||||||
to: '',
|
to: '',
|
||||||
amount: '0x0',
|
amount: '0x0',
|
||||||
memo: '',
|
memo: '',
|
||||||
errors: {},
|
errors: {},
|
||||||
|
editingTransactionId: null,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.UPDATE_TRANSACTION_PARAMS:
|
||||||
|
const { id: txId, value } = action
|
||||||
|
let { selectedAddressTxList } = metamaskState
|
||||||
|
selectedAddressTxList = selectedAddressTxList.map(tx => {
|
||||||
|
if (tx.id === txId) {
|
||||||
|
tx.txParams = value
|
||||||
|
}
|
||||||
|
return tx
|
||||||
|
})
|
||||||
|
|
||||||
|
return extend(metamaskState, {
|
||||||
|
selectedAddressTxList,
|
||||||
|
})
|
||||||
|
|
||||||
case actions.PAIR_UPDATE:
|
case actions.PAIR_UPDATE:
|
||||||
const { value: { marketinfo: pairMarketInfo } } = action
|
const { value: { marketinfo: pairMarketInfo } } = action
|
||||||
return extend(metamaskState, {
|
return extend(metamaskState, {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const valuesFor = require('./util').valuesFor
|
const valuesFor = require('./util').valuesFor
|
||||||
|
const abi = require('human-standard-token-abi')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
@ -22,6 +23,7 @@ const selectors = {
|
|||||||
getCurrentCurrency,
|
getCurrentCurrency,
|
||||||
getSendAmount,
|
getSendAmount,
|
||||||
getSelectedTokenToFiatRate,
|
getSelectedTokenToFiatRate,
|
||||||
|
getSelectedTokenContract,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = selectors
|
module.exports = selectors
|
||||||
@ -149,3 +151,10 @@ function getSelectedTokenToFiatRate (state) {
|
|||||||
|
|
||||||
return tokenToFiatRate
|
return tokenToFiatRate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSelectedTokenContract (state) {
|
||||||
|
const selectedToken = getSelectedToken(state)
|
||||||
|
return selectedToken
|
||||||
|
? global.eth.contract(abi).at(selectedToken.address)
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
@ -2,6 +2,8 @@ const { inherits } = require('util')
|
|||||||
const PersistentForm = require('../lib/persistent-form')
|
const PersistentForm = require('../lib/persistent-form')
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
|
|
||||||
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
|
||||||
const Identicon = require('./components/identicon')
|
const Identicon = require('./components/identicon')
|
||||||
const FromDropdown = require('./components/send/from-dropdown')
|
const FromDropdown = require('./components/send/from-dropdown')
|
||||||
const ToAutoComplete = require('./components/send/to-autocomplete')
|
const ToAutoComplete = require('./components/send/to-autocomplete')
|
||||||
@ -9,15 +11,24 @@ const CurrencyDisplay = require('./components/send/currency-display')
|
|||||||
const MemoTextArea = require('./components/send/memo-textarea')
|
const MemoTextArea = require('./components/send/memo-textarea')
|
||||||
const GasFeeDisplay = require('./components/send/gas-fee-display-v2')
|
const GasFeeDisplay = require('./components/send/gas-fee-display-v2')
|
||||||
|
|
||||||
const { MIN_GAS_TOTAL } = require('./components/send/send-constants')
|
const {
|
||||||
|
MIN_GAS_TOTAL,
|
||||||
|
MIN_GAS_PRICE_HEX,
|
||||||
|
MIN_GAS_LIMIT_HEX,
|
||||||
|
} = require('./components/send/send-constants')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
conversionGreaterThan,
|
conversionGreaterThan,
|
||||||
|
subtractCurrencies,
|
||||||
} = require('./conversion-util')
|
} = require('./conversion-util')
|
||||||
|
const {
|
||||||
|
calcTokenAmount,
|
||||||
|
} = require('./token-util')
|
||||||
const {
|
const {
|
||||||
isBalanceSufficient,
|
isBalanceSufficient,
|
||||||
} = require('./components/send/send-utils.js')
|
isTokenBalanceSufficient,
|
||||||
|
} = require('./components/send/send-utils')
|
||||||
const { isValidAddress } = require('./util')
|
const { isValidAddress } = require('./util')
|
||||||
|
|
||||||
module.exports = SendTransactionScreen
|
module.exports = SendTransactionScreen
|
||||||
@ -40,6 +51,36 @@ function SendTransactionScreen () {
|
|||||||
this.validateAmount = this.validateAmount.bind(this)
|
this.validateAmount = this.validateAmount.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getParamsForGasEstimate = function (selectedAddress, symbol, data) {
|
||||||
|
const estimatedGasParams = {
|
||||||
|
from: selectedAddress,
|
||||||
|
gas: '746a528800',
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol) {
|
||||||
|
Object.assign(estimatedGasParams, { value: '0x0' })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
Object.assign(estimatedGasParams, { data })
|
||||||
|
}
|
||||||
|
|
||||||
|
return estimatedGasParams
|
||||||
|
}
|
||||||
|
|
||||||
|
SendTransactionScreen.prototype.updateSendTokenBalance = function (usersToken) {
|
||||||
|
if (!usersToken) return
|
||||||
|
|
||||||
|
const {
|
||||||
|
selectedToken = {},
|
||||||
|
updateSendTokenBalance,
|
||||||
|
} = this.props
|
||||||
|
const { decimals } = selectedToken || {}
|
||||||
|
const tokenBalance = calcTokenAmount(usersToken.balance.toString(), decimals)
|
||||||
|
|
||||||
|
updateSendTokenBalance(tokenBalance)
|
||||||
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.componentWillMount = function () {
|
SendTransactionScreen.prototype.componentWillMount = function () {
|
||||||
const {
|
const {
|
||||||
updateTokenExchangeRate,
|
updateTokenExchangeRate,
|
||||||
@ -49,32 +90,30 @@ SendTransactionScreen.prototype.componentWillMount = function () {
|
|||||||
selectedAddress,
|
selectedAddress,
|
||||||
data,
|
data,
|
||||||
updateGasTotal,
|
updateGasTotal,
|
||||||
|
from,
|
||||||
|
tokenContract,
|
||||||
|
editingTransactionId,
|
||||||
|
gasPrice,
|
||||||
|
gasLimit,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { symbol } = selectedToken || {}
|
const { symbol } = selectedToken || {}
|
||||||
|
|
||||||
const estimateGasParams = {
|
|
||||||
from: selectedAddress,
|
|
||||||
gas: '746a528800',
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol) {
|
if (symbol) {
|
||||||
updateTokenExchangeRate(symbol)
|
updateTokenExchangeRate(symbol)
|
||||||
Object.assign(estimateGasParams, { value: '0x0' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
const estimateGasParams = getParamsForGasEstimate(selectedAddress, symbol, data)
|
||||||
Object.assign(estimateGasParams, { data })
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const tokenBalancePromise = tokenContract && tokenContract.balanceOf(from.address)
|
||||||
|
let newGasTotal
|
||||||
|
if (!editingTransactionId) {
|
||||||
Promise
|
Promise
|
||||||
.all([
|
.all([
|
||||||
getGasPrice(),
|
getGasPrice(),
|
||||||
estimateGas({
|
estimateGas(estimateGasParams),
|
||||||
from: selectedAddress,
|
tokenBalancePromise,
|
||||||
gas: '746a528800',
|
|
||||||
}),
|
|
||||||
])
|
])
|
||||||
.then(([gasPrice, gas]) => {
|
.then(([gasPrice, gas, usersToken]) => {
|
||||||
|
|
||||||
const newGasTotal = multiplyCurrencies(gas, gasPrice, {
|
const newGasTotal = multiplyCurrencies(gas, gasPrice, {
|
||||||
toNumericBase: 'hex',
|
toNumericBase: 'hex',
|
||||||
@ -82,7 +121,44 @@ SendTransactionScreen.prototype.componentWillMount = function () {
|
|||||||
multiplierBase: 16,
|
multiplierBase: 16,
|
||||||
})
|
})
|
||||||
updateGasTotal(newGasTotal)
|
updateGasTotal(newGasTotal)
|
||||||
|
this.updateSendTokenBalance(usersToken)
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
newGasTotal = multiplyCurrencies(gasLimit, gasPrice, {
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
multiplicandBase: 16,
|
||||||
|
multiplierBase: 16,
|
||||||
|
})
|
||||||
|
updateGasTotal(newGasTotal)
|
||||||
|
tokenBalancePromise && tokenBalancePromise.then(
|
||||||
|
usersToken => this.updateSendTokenBalance(usersToken))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SendTransactionScreen.prototype.componentDidUpdate = function (prevProps) {
|
||||||
|
const {
|
||||||
|
from: { balance },
|
||||||
|
gasTotal,
|
||||||
|
tokenBalance,
|
||||||
|
amount,
|
||||||
|
selectedToken,
|
||||||
|
} = this.props
|
||||||
|
const {
|
||||||
|
from: { balance: prevBalance },
|
||||||
|
gasTotal: prevGasTotal,
|
||||||
|
tokenBalance: prevTokenBalance,
|
||||||
|
} = prevProps
|
||||||
|
|
||||||
|
const notFirstRender = [prevBalance, prevGasTotal].every(n => n !== null)
|
||||||
|
|
||||||
|
const balanceHasChanged = balance !== prevBalance
|
||||||
|
const gasTotalHasChange = gasTotal !== prevGasTotal
|
||||||
|
const tokenBalanceHasChanged = selectedToken && tokenBalance !== prevTokenBalance
|
||||||
|
const amountValidationChange = balanceHasChanged || gasTotalHasChange || tokenBalanceHasChanged
|
||||||
|
|
||||||
|
if (notFirstRender && amountValidationChange) {
|
||||||
|
this.validateAmount(amount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderHeaderIcon = function () {
|
SendTransactionScreen.prototype.renderHeaderIcon = function () {
|
||||||
@ -144,12 +220,24 @@ SendTransactionScreen.prototype.renderErrorMessage = function (errorType) {
|
|||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SendTransactionScreen.prototype.handleFromChange = async function (newFrom) {
|
||||||
|
const {
|
||||||
|
updateSendFrom,
|
||||||
|
tokenContract,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
if (tokenContract) {
|
||||||
|
const usersToken = await tokenContract.balanceOf(newFrom.address)
|
||||||
|
this.updateSendTokenBalance(usersToken)
|
||||||
|
}
|
||||||
|
updateSendFrom(newFrom)
|
||||||
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderFromRow = function () {
|
SendTransactionScreen.prototype.renderFromRow = function () {
|
||||||
const {
|
const {
|
||||||
from,
|
from,
|
||||||
fromAccounts,
|
fromAccounts,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
updateSendFrom,
|
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const { fromDropdownOpen } = this.state
|
const { fromDropdownOpen } = this.state
|
||||||
@ -163,7 +251,7 @@ SendTransactionScreen.prototype.renderFromRow = function () {
|
|||||||
dropdownOpen: fromDropdownOpen,
|
dropdownOpen: fromDropdownOpen,
|
||||||
accounts: fromAccounts,
|
accounts: fromAccounts,
|
||||||
selectedAccount: from,
|
selectedAccount: from,
|
||||||
onSelect: updateSendFrom,
|
onSelect: newFrom => this.handleFromChange(newFrom),
|
||||||
openDropdown: () => this.setState({ fromDropdownOpen: true }),
|
openDropdown: () => this.setState({ fromDropdownOpen: true }),
|
||||||
closeDropdown: () => this.setState({ fromDropdownOpen: false }),
|
closeDropdown: () => this.setState({ fromDropdownOpen: false }),
|
||||||
conversionRate,
|
conversionRate,
|
||||||
@ -227,9 +315,41 @@ SendTransactionScreen.prototype.handleAmountChange = function (value) {
|
|||||||
const amount = value
|
const amount = value
|
||||||
const { updateSendAmount } = this.props
|
const { updateSendAmount } = this.props
|
||||||
|
|
||||||
|
this.validateAmount(amount)
|
||||||
updateSendAmount(amount)
|
updateSendAmount(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SendTransactionScreen.prototype.setAmountToMax = function () {
|
||||||
|
const {
|
||||||
|
from: { balance },
|
||||||
|
updateSendAmount,
|
||||||
|
updateSendErrors,
|
||||||
|
updateGasPrice,
|
||||||
|
updateGasLimit,
|
||||||
|
updateGasTotal,
|
||||||
|
tokenBalance,
|
||||||
|
selectedToken,
|
||||||
|
} = this.props
|
||||||
|
const { decimals } = selectedToken || {}
|
||||||
|
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||||
|
|
||||||
|
const maxAmount = selectedToken
|
||||||
|
? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'})
|
||||||
|
: subtractCurrencies(
|
||||||
|
ethUtil.addHexPrefix(balance),
|
||||||
|
ethUtil.addHexPrefix(MIN_GAS_TOTAL),
|
||||||
|
{ toNumericBase: 'hex' }
|
||||||
|
)
|
||||||
|
|
||||||
|
updateSendErrors({ amount: null })
|
||||||
|
if (!selectedToken) {
|
||||||
|
updateGasPrice(MIN_GAS_PRICE_HEX)
|
||||||
|
updateGasLimit(MIN_GAS_LIMIT_HEX)
|
||||||
|
updateGasTotal(MIN_GAS_TOTAL)
|
||||||
|
}
|
||||||
|
updateSendAmount(maxAmount)
|
||||||
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.validateAmount = function (value) {
|
SendTransactionScreen.prototype.validateAmount = function (value) {
|
||||||
const {
|
const {
|
||||||
from: { balance },
|
from: { balance },
|
||||||
@ -239,21 +359,30 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
|
|||||||
primaryCurrency,
|
primaryCurrency,
|
||||||
selectedToken,
|
selectedToken,
|
||||||
gasTotal,
|
gasTotal,
|
||||||
|
tokenBalance,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
const { decimals } = selectedToken || {}
|
||||||
const amount = value
|
const amount = value
|
||||||
|
|
||||||
let amountError = null
|
let amountError = null
|
||||||
|
|
||||||
const sufficientBalance = isBalanceSufficient({
|
const sufficientBalance = isBalanceSufficient({
|
||||||
amount,
|
amount: selectedToken ? '0x0' : amount,
|
||||||
gasTotal,
|
gasTotal,
|
||||||
balance,
|
balance,
|
||||||
primaryCurrency,
|
primaryCurrency,
|
||||||
selectedToken,
|
|
||||||
amountConversionRate,
|
amountConversionRate,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let sufficientTokens
|
||||||
|
if (selectedToken) {
|
||||||
|
sufficientTokens = isTokenBalanceSufficient({
|
||||||
|
tokenBalance,
|
||||||
|
amount,
|
||||||
|
decimals,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const amountLessThanZero = conversionGreaterThan(
|
const amountLessThanZero = conversionGreaterThan(
|
||||||
{ value: 0, fromNumericBase: 'dec' },
|
{ value: 0, fromNumericBase: 'dec' },
|
||||||
{ value: amount, fromNumericBase: 'hex' },
|
{ value: amount, fromNumericBase: 'hex' },
|
||||||
@ -261,6 +390,8 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
|
|||||||
|
|
||||||
if (!sufficientBalance) {
|
if (!sufficientBalance) {
|
||||||
amountError = 'Insufficient funds.'
|
amountError = 'Insufficient funds.'
|
||||||
|
} else if (selectedToken && !sufficientTokens) {
|
||||||
|
amountError = 'Insufficient tokens.'
|
||||||
} else if (amountLessThanZero) {
|
} else if (amountLessThanZero) {
|
||||||
amountError = 'Can not send negative amounts of ETH.'
|
amountError = 'Can not send negative amounts of ETH.'
|
||||||
}
|
}
|
||||||
@ -275,15 +406,20 @@ SendTransactionScreen.prototype.renderAmountRow = function () {
|
|||||||
convertedCurrency,
|
convertedCurrency,
|
||||||
amountConversionRate,
|
amountConversionRate,
|
||||||
errors,
|
errors,
|
||||||
|
amount,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const { amount } = this.state
|
|
||||||
|
|
||||||
return h('div.send-v2__form-row', [
|
return h('div.send-v2__form-row', [
|
||||||
|
|
||||||
h('div.send-v2__form-label', [
|
h('div.send-v2__form-label', [
|
||||||
'Amount:',
|
'Amount:',
|
||||||
this.renderErrorMessage('amount'),
|
this.renderErrorMessage('amount'),
|
||||||
|
!errors.amount && h('div.send-v2__amount-max', {
|
||||||
|
onClick: (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
this.setAmountToMax()
|
||||||
|
},
|
||||||
|
}, [ 'Max' ]),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
h('div.send-v2__form-field', [
|
h('div.send-v2__form-field', [
|
||||||
@ -292,10 +428,9 @@ SendTransactionScreen.prototype.renderAmountRow = function () {
|
|||||||
primaryCurrency,
|
primaryCurrency,
|
||||||
convertedCurrency,
|
convertedCurrency,
|
||||||
selectedToken,
|
selectedToken,
|
||||||
value: amount,
|
value: amount || '0x0',
|
||||||
conversionRate: amountConversionRate,
|
conversionRate: amountConversionRate,
|
||||||
handleChange: this.handleAmountChange,
|
handleChange: this.handleAmountChange,
|
||||||
validate: this.validateAmount,
|
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -335,8 +470,7 @@ SendTransactionScreen.prototype.renderGasRow = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.renderMemoRow = function () {
|
SendTransactionScreen.prototype.renderMemoRow = function () {
|
||||||
const { updateSendMemo } = this.props
|
const { updateSendMemo, memo } = this.props
|
||||||
const { memo } = this.state
|
|
||||||
|
|
||||||
return h('div.send-v2__form-row', [
|
return h('div.send-v2__form-row', [
|
||||||
|
|
||||||
@ -383,7 +517,7 @@ SendTransactionScreen.prototype.renderFooter = function () {
|
|||||||
errors: { amount: amountError, to: toError },
|
errors: { amount: amountError, to: toError },
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const noErrors = amountError === null && toError === null
|
const noErrors = !amountError && toError === null
|
||||||
const errorClass = noErrors ? '' : '__disabled'
|
const errorClass = noErrors ? '' : '__disabled'
|
||||||
|
|
||||||
return h('div.send-v2__footer', [
|
return h('div.send-v2__footer', [
|
||||||
@ -433,11 +567,12 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
|
|||||||
signTokenTx,
|
signTokenTx,
|
||||||
signTx,
|
signTx,
|
||||||
selectedToken,
|
selectedToken,
|
||||||
clearSend,
|
editingTransactionId,
|
||||||
errors: { amount: amountError, to: toError },
|
errors: { amount: amountError, to: toError },
|
||||||
|
backToConfirmScreen,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const noErrors = amountError === null && toError === null
|
const noErrors = !amountError && toError === null
|
||||||
|
|
||||||
if (!noErrors) {
|
if (!noErrors) {
|
||||||
return
|
return
|
||||||
@ -445,6 +580,11 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
|
|||||||
|
|
||||||
this.addToAddressBookIfNew(to)
|
this.addToAddressBookIfNew(to)
|
||||||
|
|
||||||
|
if (editingTransactionId) {
|
||||||
|
backToConfirmScreen(editingTransactionId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const txParams = {
|
const txParams = {
|
||||||
from,
|
from,
|
||||||
value: '0',
|
value: '0',
|
||||||
@ -457,8 +597,6 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
|
|||||||
txParams.to = to
|
txParams.to = to
|
||||||
}
|
}
|
||||||
|
|
||||||
clearSend()
|
|
||||||
|
|
||||||
selectedToken
|
selectedToken
|
||||||
? signTokenTx(selectedToken.address, to, amount, txParams)
|
? signTokenTx(selectedToken.address, to, amount, txParams)
|
||||||
: signTx(txParams)
|
: signTx(txParams)
|
||||||
|
0
ui/app/token-tracker.js
Normal file
0
ui/app/token-tracker.js
Normal file
@ -31,6 +31,15 @@ const tokenInfoGetter = function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calcTokenAmount (value, decimals) {
|
||||||
|
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||||
|
const amount = Number(value / multiplier)
|
||||||
|
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
tokenInfoGetter,
|
tokenInfoGetter,
|
||||||
|
calcTokenAmount,
|
||||||
}
|
}
|
||||||
|
@ -8379,6 +8379,10 @@ redux-logger@^3.0.6:
|
|||||||
dependencies:
|
dependencies:
|
||||||
deep-diff "^0.3.5"
|
deep-diff "^0.3.5"
|
||||||
|
|
||||||
|
redux-test-utils@^0.1.3:
|
||||||
|
version "0.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/redux-test-utils/-/redux-test-utils-0.1.3.tgz#0d89100f100f86c7c7214976eaece88e7e45bf74"
|
||||||
|
|
||||||
redux-thunk@^2.2.0:
|
redux-thunk@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5"
|
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user