mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Merge pull request #125 from MetaMask/TransactionList
Add transaction history to account detail view
This commit is contained in:
commit
db85827b2b
@ -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
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -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' })
|
||||
|
@ -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)
|
||||
describe('normalizing values', function() {
|
||||
|
||||
for(var currency in valueTable) {
|
||||
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)
|
||||
|
||||
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}`)
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
@ -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
|
||||
|
39
ui/app/components/transaction-list.js
Normal file
39
ui/app/components/transaction-list.js
Normal file
@ -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))
|
||||
])
|
||||
})
|
||||
)
|
||||
|
||||
])
|
||||
}
|
@ -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 currency = document.querySelector('select.currency').value
|
||||
var txData = document.querySelector('textarea.txData').value
|
||||
|
||||
var value = util.normalizeToWei(amount, currency)
|
||||
var inputAmount = parseFloat(document.querySelector('input.ether').value)
|
||||
var currency = document.querySelector('select.currency').value
|
||||
var value = util.normalizeNumberToWei(inputAmount, 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))
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
12
ui/lib/explorer-link.js
Normal file
12
ui/lib/explorer-link.js
Normal file
@ -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}`
|
||||
}
|
Loading…
Reference in New Issue
Block a user