diff --git a/ui/app/components/send-token/index.js b/ui/app/components/send-token/index.js index a3332ca9e..72fb593be 100644 --- a/ui/app/components/send-token/index.js +++ b/ui/app/components/send-token/index.js @@ -141,6 +141,7 @@ SendTokenScreen.prototype.submit = function () { hideWarning, addToAddressBook, signTokenTx, + selectedToken, } = this.props const { nickname = ' ' } = identities[to] || {} @@ -161,7 +162,11 @@ SendTokenScreen.prototype.submit = function () { gasPrice: gasPrice, } - signTokenTx(selectedTokenAddress, to, Number(amount).toString(16), txParams) + const { decimals } = selectedToken || {} + const multiplier = Math.pow(10, Number(decimals || 0)) + const sendAmount = Number(amount * multiplier).toString(16) + + signTokenTx(selectedTokenAddress, to, sendAmount, txParams) } SendTokenScreen.prototype.renderToAddressInput = function () { diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js index 4be8dfcb3..70b4486dd 100644 --- a/ui/app/components/tx-list-item.js +++ b/ui/app/components/tx-list-item.js @@ -1,37 +1,60 @@ const Component = require('react').Component const h = require('react-hyperscript') +const connect = require('react-redux').connect const inherits = require('util').inherits const classnames = require('classnames') +const abi = require('human-standard-token-abi') +const abiDecoder = require('abi-decoder') +abiDecoder.addABI(abi) const prefixForNetwork = require('../../lib/etherscan-prefix-for-network') const Identicon = require('./identicon') const { conversionUtil } = require('../conversion-util') -module.exports = TxListItem +module.exports = connect(mapStateToProps)(TxListItem) + +function mapStateToProps (state) { + return { + tokens: state.metamask.tokens, + } +} inherits(TxListItem, Component) function TxListItem () { Component.call(this) } -TxListItem.prototype.getAddressText = function (address) { - return address - ? `${address.slice(0, 10)}...${address.slice(-4)}` - : 'Contract Published' +TxListItem.prototype.getAddressText = function () { + const { + address, + txParams = {}, + } = this.props + + const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data) + const { name: txDataName, params = [] } = decodedData || {} + const { value } = params[0] || {} + + switch (txDataName) { + case 'transfer': + return `${value.slice(0, 10)}...${value.slice(-4)}` + default: + return address + ? `${address.slice(0, 10)}...${address.slice(-4)}` + : 'Contract Published' + } } -TxListItem.prototype.render = function () { +TxListItem.prototype.getSendEtherTotal = function () { const { - transactionStatus, - onClick, - transActionId, - dateString, - address, transactionAmount, - className, conversionRate, + address, } = this.props + if (!address) { + return {} + } + const totalInUSD = conversionUtil(transactionAmount, { fromNumericBase: 'hex', toNumericBase: 'dec', @@ -49,6 +72,50 @@ TxListItem.prototype.render = function () { numberOfDecimals: 6, }) + return { + total: `${totalInETH} ETH`, + fiatTotal: `${totalInUSD} USD`, + } +} + +TxListItem.prototype.getSendTokenTotal = function () { + const { + txParams = {}, + tokens, + } = this.props + + const toAddress = txParams.to + const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data) + const { params = [] } = decodedData || {} + const { value } = params[1] || {} + const { decimals, symbol } = tokens.filter(({ address }) => address === toAddress)[0] || {} + + const multiplier = Math.pow(10, Number(decimals || 0)) + const total = Number(value / multiplier) + + return { + total: `${total} ${symbol}`, + } +} + +TxListItem.prototype.render = function () { + const { + transactionStatus, + onClick, + transActionId, + dateString, + address, + className, + txParams = {}, + } = this.props + + const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data) + const { name: txDataName } = decodedData || {} + + const { total, fiatTotal } = txDataName === 'transfer' + ? this.getSendTokenTotal() + : this.getSendEtherTotal() + return h(`div${className || ''}`, { key: transActionId, onClick: () => onClick && onClick(transActionId), @@ -90,9 +157,10 @@ TxListItem.prototype.render = function () { }, [ h('span', { className: classnames('tx-list-status', { - 'tx-list-status--rejected': transactionStatus === 'rejected' - }) - }, + 'tx-list-status--rejected': transactionStatus === 'rejected', + 'tx-list-status--failed': transactionStatus === 'failed', + }), + }, transactionStatus, ), ]), @@ -104,15 +172,11 @@ TxListItem.prototype.render = function () { h('span', { className: classnames('tx-list-value', { - 'tx-list-value--confirmed': transactionStatus === 'confirmed' - }) - }, - `${totalInETH} ETH`, - ), - - h('span.tx-list-fiat-value', {}, [ - `${totalInUSD} USD`, - ]), + 'tx-list-value--confirmed': transactionStatus === 'confirmed', + }), + }, total), + + h('span.tx-list-fiat-value', fiatTotal), ]), ]), diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js index e3578646b..7a147e942 100644 --- a/ui/app/components/tx-list.js +++ b/ui/app/components/tx-list.js @@ -49,7 +49,7 @@ TxList.prototype.renderTransaction = function () { const { txsToRender, conversionRate } = this.props return txsToRender.length - ? txsToRender.map((transaction) => this.renderTransactionListItem(transaction, conversionRate)) + ? txsToRender.map((transaction, i) => this.renderTransactionListItem(transaction, conversionRate)) : [h('div.tx-list-item.tx-list-item--empty', [ 'No Transactions' ])] } @@ -77,7 +77,8 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa const { showConfTxPage } = this.props const opts = { - key: transactionHash, + key: transActionId, + txParams: transaction.txParams, transactionStatus, transActionId, dateString, diff --git a/ui/app/css/itcss/components/transaction-list.scss b/ui/app/css/itcss/components/transaction-list.scss index 589d18b72..e3fe1a8b3 100644 --- a/ui/app/css/itcss/components/transaction-list.scss +++ b/ui/app/css/itcss/components/transaction-list.scss @@ -66,11 +66,11 @@ flex-flow: column nowrap; @media screen and (max-width: $break-small) { - padding: 0 1.3em .95em; + padding: 0 1.3em; } @media screen and (min-width: $break-large) { - // margin: 0 2.37em; + padding-bottom: 12px; } } @@ -79,16 +79,12 @@ &:hover { background: rgba($alto, .2); - - .tx-list-details-wrapper { - background: transparent; - } } } .tx-list-pending-item-container { cursor: pointer; - opacity: 0.5; + opacity: .5; } .tx-list-date-wrapper { @@ -96,11 +92,10 @@ @media screen and (max-width: $break-small) { margin-top: 6px; - margin-bottom: 20px; } @media screen and (min-width: $break-large) { - margin-top: 13px; + margin-top: 12px; } } @@ -149,6 +144,7 @@ .tx-list-account-and-status-wrapper { display: flex; flex: 1 1 auto; + flex-flow: row wrap; width: 0; @media screen and (max-width: $break-small) { @@ -184,7 +180,8 @@ text-transform: capitalize; } - .tx-list-status--rejected { + .tx-list-status--rejected, + .tx-list-status--failed { color: $monzo; } } @@ -238,7 +235,6 @@ .tx-list-details-wrapper { overflow: hidden; flex: 0 0 35%; - background: rgba($white, .8); } .tx-list-value {