diff options
author | kumavis <kumavis@users.noreply.github.com> | 2016-04-21 13:07:00 +0800 |
---|---|---|
committer | kumavis <kumavis@users.noreply.github.com> | 2016-04-21 13:07:00 +0800 |
commit | db85827b2be19d7bfc7dfaeec4786c4d051b6629 (patch) | |
tree | 39075d39ddcfbcb80016d61a22942451dbc745b9 | |
parent | f82a4de725e384ab4bb39c57f83a3dbb406c8db6 (diff) | |
parent | 532edf670e8c30db958765141974f3fb0f5ec44c (diff) | |
download | tangerine-wallet-browser-db85827b2be19d7bfc7dfaeec4786c4d051b6629.tar tangerine-wallet-browser-db85827b2be19d7bfc7dfaeec4786c4d051b6629.tar.gz tangerine-wallet-browser-db85827b2be19d7bfc7dfaeec4786c4d051b6629.tar.bz2 tangerine-wallet-browser-db85827b2be19d7bfc7dfaeec4786c4d051b6629.tar.lz tangerine-wallet-browser-db85827b2be19d7bfc7dfaeec4786c4d051b6629.tar.xz tangerine-wallet-browser-db85827b2be19d7bfc7dfaeec4786c4d051b6629.tar.zst tangerine-wallet-browser-db85827b2be19d7bfc7dfaeec4786c4d051b6629.zip |
Merge pull request #125 from MetaMask/TransactionList
Add transaction history to account detail view
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | app/scripts/lib/config-manager.js | 18 | ||||
-rw-r--r-- | app/scripts/lib/idStore.js | 8 | ||||
-rw-r--r-- | app/scripts/popup.js | 11 | ||||
-rw-r--r-- | test/unit/config-manager-test.js | 10 | ||||
-rw-r--r-- | test/unit/util_test.js | 68 | ||||
-rw-r--r-- | ui/app/account-detail.js | 7 | ||||
-rw-r--r-- | ui/app/components/transaction-list.js | 39 | ||||
-rw-r--r-- | ui/app/send.js | 10 | ||||
-rw-r--r-- | ui/app/util.js | 12 | ||||
-rw-r--r-- | ui/index.js | 5 | ||||
-rw-r--r-- | ui/lib/explorer-link.js | 12 |
12 files changed, 162 insertions, 40 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a4e7fd3c..c2bcf006f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ - Pending transactions are now persisted to localStorage and resume even after browser is closed. - Completed transactions are now persisted and can be displayed via UI. +- Added transaction list to account detail view. - Fix bug on config screen where current RPC address was always displayed wrong. +- Fixed bug where entering a decimal value when sending a transaction would result in sending the wrong amount. # 1.5.1 2016-04-15 diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 356d53c22..c79dc7a8f 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -170,14 +170,26 @@ ConfigManager.prototype.rejectTx = function(txId) { } ConfigManager.prototype._setTxStatus = function(txId, status) { + var tx = this.getTx(txId) + tx.status = status + this.updateTx(tx) +} + +ConfigManager.prototype.updateTx = function(tx) { var transactions = this.getTxList() - transactions.forEach((tx) => { - if (tx.id === txId) { - tx.status = status + var found, index + transactions.forEach((otherTx, i) => { + if (otherTx.id === tx.id) { + found = true + index = i } }) + if (found) { + transactions[index] = tx + } this._saveTxList(transactions) } + ConfigManager.prototype.unconfirmedTxs = function() { var transactions = this.getTxList() return transactions.filter(tx => tx.status === 'unconfirmed') diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index b451fd6d4..7763d33d8 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -135,6 +135,7 @@ IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){ // create txData obj with parameters and meta data var time = (new Date()).getTime() var txId = createId() + txParams.metamaskId = txId var txData = { id: txId, txParams: txParams, @@ -337,6 +338,13 @@ function IdManagement(opts) { txParams.gasLimit = ethUtil.addHexPrefix(txParams.gasLimit || txParams.gas) txParams.nonce = ethUtil.addHexPrefix(txParams.nonce) var tx = new Transaction(txParams) + + // Add the tx hash to the persisted meta-tx object + var hash = '0x' + tx.hash().toString('hex') + var metaTx = configManager.getTx(txParams.metamaskId) + metaTx.hash = hash + configManager.updateTx(metaTx) + var rawTx = '0x'+tx.serialize().toString('hex') return '0x'+LightwalletSigner.signTx(this.keyStore, this.derivedKey, rawTx, txParams.from, this.hdPathString) } diff --git a/app/scripts/popup.js b/app/scripts/popup.js index 85b3e30f9..6a39da661 100644 --- a/app/scripts/popup.js +++ b/app/scripts/popup.js @@ -17,7 +17,7 @@ injectCss(css) async.parallel({ currentDomain: getCurrentDomain, accountManager: connectToAccountManager, -}, setupApp) +}, getNetworkVersion) function connectToAccountManager(cb){ // setup communication with background @@ -65,6 +65,13 @@ function getCurrentDomain(cb){ }) } +function getNetworkVersion(cb, results) { + web3.version.getNetwork(function(err, result) { + results.networkVersion = result + setupApp(err, results) + }) +} + function setupApp(err, opts){ if (err) { alert(err.stack) @@ -78,6 +85,6 @@ function setupApp(err, opts){ container: container, accountManager: opts.accountManager, currentDomain: opts.currentDomain, + networkVersion: opts.networkVersion, }) - } diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js index 84632b0ea..e414ecb9e 100644 --- a/test/unit/config-manager-test.js +++ b/test/unit/config-manager-test.js @@ -126,6 +126,16 @@ describe('config-manager', function() { }) }) + describe('#updateTx', function() { + it('replaces the tx with the same id', function() { + configManager.addTx({ id: '1', status: 'unconfirmed' }) + configManager.addTx({ id: '2', status: 'confirmed' }) + configManager.updateTx({ id: '1', status: 'blah', hash: 'foo' }) + var result = configManager.getTx('1') + assert.equal(result.hash, 'foo') + }) + }) + describe('#unconfirmedTxs', function() { it('returns unconfirmed txs in a hash', function() { configManager.addTx({ id: '1', status: 'unconfirmed' }) diff --git a/test/unit/util_test.js b/test/unit/util_test.js index 7f8103d3b..3f46d4e9b 100644 --- a/test/unit/util_test.js +++ b/test/unit/util_test.js @@ -82,33 +82,47 @@ describe('util', function() { }) - describe('#normalizeToWei', function() { - it('should convert an eth to the appropriate equivalent values', function() { - var valueTable = { - wei: '1000000000000000000', - kwei: '1000000000000000', - mwei: '1000000000000', - gwei: '1000000000', - szabo: '1000000', - finney:'1000', - ether: '1', - kether:'0.001', - mether:'0.000001', - // AUDIT: We're getting BN numbers on these ones. - // I think they're big enough to ignore for now. - // gether:'0.000000001', - // tether:'0.000000000001', - } - var oneEthBn = new ethUtil.BN(ethInWei, 10) - - for(var currency in valueTable) { - - var value = new ethUtil.BN(valueTable[currency], 10) - var output = util.normalizeToWei(value, currency) - assert.equal(output.toString(10), valueTable.wei, `value of ${output.toString(10)} ${currency} should convert to ${oneEthBn}`) - - } + describe('normalizing values', function() { + + describe('#normalizeToWei', function() { + it('should convert an eth to the appropriate equivalent values', function() { + var valueTable = { + wei: '1000000000000000000', + kwei: '1000000000000000', + mwei: '1000000000000', + gwei: '1000000000', + szabo: '1000000', + finney:'1000', + ether: '1', + // kether:'0.001', + // mether:'0.000001', + // AUDIT: We're getting BN numbers on these ones. + // I think they're big enough to ignore for now. + // gether:'0.000000001', + // tether:'0.000000000001', + } + var oneEthBn = new ethUtil.BN(ethInWei, 10) + + for(var currency in valueTable) { + + var value = new ethUtil.BN(valueTable[currency], 10) + var output = util.normalizeToWei(value, currency) + assert.equal(output.toString(10), valueTable.wei, `value of ${output.toString(10)} ${currency} should convert to ${oneEthBn}`) + } + }) }) - }) + describe('#normalizeNumberToWei', function() { + + it('should convert a kwei number to the appropriate equivalent wei', function() { + var result = util.normalizeNumberToWei(1.111, 'kwei') + assert.equal(result.toString(10), '1111', 'accepts decimals') + }) + + it('should convert a ether number to the appropriate equivalent wei', function() { + var result = util.normalizeNumberToWei(1.111, 'ether') + assert.equal(result.toString(10), '1111000000000000000', 'accepts decimals') + }) + }) + }) }) diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index 6ed23d482..025644efe 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -5,6 +5,7 @@ const connect = require('react-redux').connect const copyToClipboard = require('copy-to-clipboard') const actions = require('./actions') const AccountPanel = require('./components/account-panel') +const transactionList = require('./components/transaction-list') module.exports = connect(mapStateToProps)(AccountDetailScreen) @@ -15,6 +16,8 @@ function mapStateToProps(state) { accounts: state.metamask.accounts, address: state.appState.currentView.context, accountDetail: accountDetail, + transactions: state.metamask.transactions, + networkVersion: state.networkVersion, } } @@ -29,6 +32,7 @@ AccountDetailScreen.prototype.render = function() { var identity = state.identities[state.address] var account = state.accounts[state.address] var accountDetail = state.accountDetail + var transactions = state.transactions return ( @@ -71,6 +75,9 @@ AccountDetailScreen.prototype.render = function() { ]), ]), + transactionList(transactions + .filter(tx => tx.txParams.from === state.address) + .sort((a, b) => b.time - a.time), state.networkVersion), this.exportedAccount(accountDetail), // transaction table diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js new file mode 100644 index 000000000..99a325e35 --- /dev/null +++ b/ui/app/components/transaction-list.js @@ -0,0 +1,39 @@ +const h = require('react-hyperscript') +const formatBalance = require('../util').formatBalance +const addressSummary = require('../util').addressSummary +const explorerLink = require('../../lib/explorer-link') + +module.exports = function(transactions, network) { + return h('details', [ + + h('summary', [ + h('div.font-small', {style: {display: 'inline'}}, 'Transactions'), + ]), + + h('.flex-row.flex-space-around', [ + h('div.font-small','To'), + h('div.font-small','Amount'), + ]), + + h('.tx-list', { + style: { + overflowY: 'auto', + height: '180px', + }, + }, + + transactions.map((transaction) => { + return h('.tx.flex-row.flex-space-around', [ + h('a.font-small', + { + href: explorerLink(transaction.hash, parseInt(network)), + target: '_blank', + }, + addressSummary(transaction.txParams.to)), + h('div.font-small', formatBalance(transaction.txParams.value)) + ]) + }) + ) + + ]) +} diff --git a/ui/app/send.js b/ui/app/send.js index 3cb56cb6e..d34accddc 100644 --- a/ui/app/send.js +++ b/ui/app/send.js @@ -96,7 +96,7 @@ SendTransactionScreen.prototype.render = function() { }, 'Send') ]), - state.warning ? h('span.error', state.warning) : null, + state.warning ? h('span.error', state.warning.split('.')[0]) : null, ]) ) } @@ -108,11 +108,11 @@ SendTransactionScreen.prototype.back = function() { SendTransactionScreen.prototype.onSubmit = function(event) { var recipient = document.querySelector('input.address').value - var amount = new ethUtil.BN(document.querySelector('input.ether').value, 10) + + var inputAmount = parseFloat(document.querySelector('input.ether').value) var currency = document.querySelector('select.currency').value - var txData = document.querySelector('textarea.txData').value + var value = util.normalizeNumberToWei(inputAmount, currency) - var value = util.normalizeToWei(amount, currency) var balance = this.props.balance if (value.gt(balance)) { @@ -132,6 +132,8 @@ SendTransactionScreen.prototype.onSubmit = function(event) { from: this.props.address, value: '0x' + value.toString(16), } + + var txData = document.querySelector('textarea.txData').value if (txData) txParams.data = txData this.props.dispatch(actions.signTx(txParams)) diff --git a/ui/app/util.js b/ui/app/util.js index 18862fade..5dbcffa7e 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -28,6 +28,7 @@ module.exports = { ethToWei: ethToWei, weiToEth: weiToEth, normalizeToWei: normalizeToWei, + normalizeNumberToWei: normalizeNumberToWei, valueTable: valueTable, bnTable: bnTable, } @@ -85,13 +86,18 @@ function dataSize(data) { // returns a BN in wei function normalizeToWei(amount, currency) { try { - var ether = amount.div(bnTable[currency]) - var wei = ether.mul(bnTable.wei) - return wei + return amount.mul(bnTable.wei).div(bnTable[currency]) } catch (e) {} return amount } +var multiple = new ethUtil.BN('1000', 10) +function normalizeNumberToWei(n, currency) { + var enlarged = n * 1000 + var amount = new ethUtil.BN(String(enlarged), 10) + return normalizeToWei(amount, currency).div(multiple) +} + function readableDate(ms) { var date = new Date(ms) var month = date.getMonth() diff --git a/ui/index.js b/ui/index.js index 05d30d8d3..4ecce2fbe 100644 --- a/ui/index.js +++ b/ui/index.js @@ -32,7 +32,10 @@ function startApp(metamaskState, accountManager, opts){ // appState represents the current tab's popup state appState: { currentDomain: opts.currentDomain, - } + }, + + // Which blockchain we are using: + networkVersion: opts.networkVersion, }) // if unconfirmed txs, start on txConf page diff --git a/ui/lib/explorer-link.js b/ui/lib/explorer-link.js new file mode 100644 index 000000000..a2e7872f9 --- /dev/null +++ b/ui/lib/explorer-link.js @@ -0,0 +1,12 @@ +module.exports = function(hash, network) { + let prefix + switch (network) { + case 1: // main net + prefix = '' + case 2: // morden test net + prefix = 'testnet.' + default: + prefix = '' + } + return `http://${prefix}etherscan.io/tx/${hash}` +} |