2017-03-31 01:06:27 +02:00
const assert = require ( 'assert' )
2018-05-28 23:29:31 +02:00
const EventEmitter = require ( 'events' )
2017-03-30 23:23:23 +02:00
const ethUtil = require ( 'ethereumjs-util' )
const EthTx = require ( 'ethereumjs-tx' )
2017-02-03 05:59:47 +01:00
const ObservableStore = require ( 'obs-store' )
2017-05-17 03:16:18 +02:00
const sinon = require ( 'sinon' )
2018-05-21 14:59:26 +02:00
const TransactionController = require ( '../../../../../app/scripts/controllers/transactions' )
2018-10-26 06:42:59 +02:00
const {
TRANSACTION _TYPE _RETRY ,
} = require ( '../../../../../app/scripts/controllers/transactions/enums' )
2019-05-14 20:14:07 +02:00
const {
TOKEN _METHOD _APPROVE ,
TOKEN _METHOD _TRANSFER ,
SEND _ETHER _ACTION _KEY ,
DEPLOY _CONTRACT _ACTION _KEY ,
CONTRACT _INTERACTION _KEY ,
} = require ( '../../../../../ui/app/helpers/constants/transactions.js' )
2018-05-21 14:59:26 +02:00
const { createTestProviderTools , getTestAccounts } = require ( '../../../../stub/provider' )
2017-08-15 04:15:09 +02:00
2017-02-03 05:20:13 +01:00
const noop = ( ) => true
2017-03-30 23:23:23 +02:00
const currentNetworkId = 42
2018-12-13 20:14:46 +01:00
const netStore = new ObservableStore ( currentNetworkId )
2017-08-15 04:15:09 +02:00
2017-08-02 17:34:45 +02:00
describe ( 'Transaction Controller' , function ( ) {
2018-07-03 02:12:50 +02:00
let txController , provider , providerResultStub , fromAccount
2016-12-14 21:56:53 +01:00
2017-05-04 23:35:10 +02:00
beforeEach ( function ( ) {
2018-01-18 06:43:34 +01:00
providerResultStub = {
// 1 gwei
eth _gasPrice : '0x0de0b6b3a7640000' ,
// by default, all accounts are external accounts (not contracts)
eth _getCode : '0x' ,
}
2018-04-28 02:12:52 +02:00
provider = createTestProviderTools ( { scaffold : providerResultStub } ) . provider
fromAccount = getTestAccounts ( ) [ 0 ]
2018-05-28 23:29:31 +02:00
const blockTrackerStub = new EventEmitter ( )
blockTrackerStub . getCurrentBlock = noop
2018-05-29 10:08:55 +02:00
blockTrackerStub . getLatestBlock = noop
2017-05-16 19:27:41 +02:00
txController = new TransactionController ( {
2017-08-09 00:30:22 +02:00
provider ,
2018-11-26 20:29:14 +01:00
getGasPrice : function ( ) { return '0xee6b2800' } ,
2018-12-13 20:14:46 +01:00
networkStore : netStore ,
2016-12-16 19:33:36 +01:00
txHistoryLimit : 10 ,
2018-05-28 23:29:31 +02:00
blockTracker : blockTrackerStub ,
2017-03-30 23:23:23 +02:00
signTransaction : ( ethTx ) => new Promise ( ( resolve ) => {
2018-04-28 02:12:52 +02:00
ethTx . sign ( fromAccount . key )
2017-03-30 23:23:23 +02:00
resolve ( )
2017-05-04 23:35:10 +02:00
} ) ,
2016-12-14 21:56:53 +01:00
} )
2017-06-15 08:44:02 +02:00
txController . nonceTracker . getNonceLock = ( ) => Promise . resolve ( { nextNonce : 0 , releaseLock : noop } )
2017-07-26 20:56:52 +02:00
} )
2017-09-08 23:24:40 +02:00
describe ( '#getState' , function ( ) {
2017-09-23 01:15:18 +02:00
it ( 'should return a state object with the right keys and datat types' , function ( ) {
2017-09-08 23:24:40 +02:00
const exposedState = txController . getState ( )
assert ( 'unapprovedTxs' in exposedState , 'state should have the key unapprovedTxs' )
assert ( 'selectedAddressTxList' in exposedState , 'state should have the key selectedAddressTxList' )
assert ( typeof exposedState . unapprovedTxs === 'object' , 'should be an object' )
assert ( Array . isArray ( exposedState . selectedAddressTxList ) , 'should be an array' )
} )
} )
describe ( '#getUnapprovedTxCount' , function ( ) {
it ( 'should return the number of unapproved txs' , function ( ) {
txController . txStateManager . _saveTxList ( [
2018-11-16 19:34:08 +01:00
{ id : 1 , status : 'unapproved' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
{ id : 2 , status : 'unapproved' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
{ id : 3 , status : 'unapproved' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
2017-09-08 23:24:40 +02:00
] )
const unapprovedTxCount = txController . getUnapprovedTxCount ( )
assert . equal ( unapprovedTxCount , 3 , 'should be 3' )
} )
} )
2017-09-13 23:07:22 +02:00
describe ( '#getPendingTxCount' , function ( ) {
it ( 'should return the number of pending txs' , function ( ) {
txController . txStateManager . _saveTxList ( [
2018-11-16 19:34:08 +01:00
{ id : 1 , status : 'submitted' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
{ id : 2 , status : 'submitted' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
{ id : 3 , status : 'submitted' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
2017-09-13 23:07:22 +02:00
] )
const pendingTxCount = txController . getPendingTxCount ( )
assert . equal ( pendingTxCount , 3 , 'should be 3' )
} )
} )
2017-09-23 01:15:18 +02:00
describe ( '#getConfirmedTransactions' , function ( ) {
let address
beforeEach ( function ( ) {
address = '0xc684832530fcbddae4b4230a47e991ddcec2831d'
const txParams = {
'from' : address ,
'to' : '0xc684832530fcbddae4b4230a47e991ddcec2831d' ,
}
txController . txStateManager . _saveTxList ( [
2018-11-16 19:34:08 +01:00
{ id : 0 , status : 'confirmed' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
{ id : 1 , status : 'confirmed' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
{ id : 2 , status : 'confirmed' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
{ id : 3 , status : 'unapproved' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
{ id : 4 , status : 'rejected' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
{ id : 5 , status : 'approved' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
{ id : 6 , status : 'signed' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
{ id : 7 , status : 'submitted' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
{ id : 8 , status : 'failed' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
2017-09-23 01:15:18 +02:00
] )
} )
2017-09-26 04:47:03 +02:00
2017-09-23 01:15:18 +02:00
it ( 'should return the number of confirmed txs' , function ( ) {
assert . equal ( txController . nonceTracker . getConfirmedTransactions ( address ) . length , 3 )
} )
} )
2017-09-13 23:07:22 +02:00
2017-08-03 00:58:05 +02:00
describe ( '#newUnapprovedTransaction' , function ( ) {
let stub , txMeta , txParams
beforeEach ( function ( ) {
txParams = {
2017-09-23 01:15:18 +02:00
'from' : '0xc684832530fcbddae4b4230a47e991ddcec2831d' ,
'to' : '0xc684832530fcbddae4b4230a47e991ddcec2831d' ,
2017-09-26 04:47:03 +02:00
}
2017-08-03 00:58:05 +02:00
txMeta = {
status : 'unapproved' ,
id : 1 ,
metamaskNetworkId : currentNetworkId ,
txParams ,
2018-11-16 19:34:08 +01:00
history : [ { } ] ,
2017-08-03 00:58:05 +02:00
}
2017-08-11 23:19:35 +02:00
txController . txStateManager . _saveTxList ( [ txMeta ] )
2018-01-14 23:01:37 +01:00
stub = sinon . stub ( txController , 'addUnapprovedTransaction' ) . callsFake ( ( ) => {
txController . emit ( 'newUnapprovedTx' , txMeta )
return Promise . resolve ( txController . txStateManager . addTx ( txMeta ) )
2019-07-31 22:17:11 +02:00
} )
2017-08-03 00:58:05 +02:00
2019-07-31 22:17:11 +02:00
afterEach ( function ( ) {
txController . txStateManager . _saveTxList ( [ ] )
stub . restore ( )
} )
2017-08-03 00:58:05 +02:00
} )
it ( 'should resolve when finished and status is submitted and resolve with the hash' , function ( done ) {
2017-11-06 13:35:51 +01:00
txController . once ( 'newUnapprovedTx' , ( txMetaFromEmit ) => {
2017-08-03 00:58:05 +02:00
setTimeout ( ( ) => {
txController . setTxHash ( txMetaFromEmit . id , '0x0' )
2017-08-11 23:19:35 +02:00
txController . txStateManager . setTxStatusSubmitted ( txMetaFromEmit . id )
2017-08-03 00:58:05 +02:00
} , 10 )
} )
txController . newUnapprovedTransaction ( txParams )
2019-07-31 22:17:11 +02:00
. then ( ( hash ) => {
assert ( hash , 'newUnapprovedTransaction needs to return the hash' )
done ( )
} )
. catch ( done )
2017-08-03 00:58:05 +02:00
} )
it ( 'should reject when finished and status is rejected' , function ( done ) {
2017-11-06 13:35:51 +01:00
txController . once ( 'newUnapprovedTx' , ( txMetaFromEmit ) => {
2017-08-03 00:58:05 +02:00
setTimeout ( ( ) => {
2017-08-11 23:19:35 +02:00
txController . txStateManager . setTxStatusRejected ( txMetaFromEmit . id )
2017-08-03 00:58:05 +02:00
} , 10 )
} )
txController . newUnapprovedTransaction ( txParams )
2019-07-31 22:17:11 +02:00
. catch ( ( err ) => {
if ( err . message === 'MetaMask Tx Signature: User denied transaction signature.' ) done ( )
else done ( err )
} )
2017-08-03 00:58:05 +02:00
} )
} )
2017-07-26 20:56:52 +02:00
describe ( '#addUnapprovedTransaction' , function ( ) {
2018-10-10 20:41:17 +02:00
const selectedAddress = '0x1678a085c290ebd122dc42cba69373b5953b831d'
let getSelectedAddress
beforeEach ( function ( ) {
getSelectedAddress = sinon . stub ( txController , 'getSelectedAddress' ) . returns ( selectedAddress )
} )
afterEach ( function ( ) {
getSelectedAddress . restore ( )
} )
2018-01-14 23:01:37 +01:00
2017-07-26 20:56:52 +02:00
it ( 'should add an unapproved transaction and return a valid txMeta' , function ( done ) {
2018-10-10 20:41:17 +02:00
txController . addUnapprovedTransaction ( { from : selectedAddress } )
2019-07-31 22:17:11 +02:00
. then ( ( txMeta ) => {
assert ( ( 'id' in txMeta ) , 'should have a id' )
assert ( ( 'time' in txMeta ) , 'should have a time stamp' )
assert ( ( 'metamaskNetworkId' in txMeta ) , 'should have a metamaskNetworkId' )
assert ( ( 'txParams' in txMeta ) , 'should have a txParams' )
assert ( ( 'history' in txMeta ) , 'should have a history' )
const memTxMeta = txController . txStateManager . getTx ( txMeta . id )
assert . deepEqual ( txMeta , memTxMeta , ` txMeta should be stored in txController after adding it \n expected: ${ txMeta } \n got: ${ memTxMeta } ` )
done ( )
} ) . catch ( done )
2017-07-26 20:56:52 +02:00
} )
2018-01-14 23:01:37 +01:00
it ( 'should emit newUnapprovedTx event and pass txMeta as the first argument' , function ( done ) {
providerResultStub . eth _gasPrice = '4a817c800'
txController . once ( 'newUnapprovedTx' , ( txMetaFromEmit ) => {
assert ( txMetaFromEmit , 'txMeta is falsey' )
done ( )
} )
2018-10-10 20:41:17 +02:00
txController . addUnapprovedTransaction ( { from : selectedAddress } )
2019-07-31 22:17:11 +02:00
. catch ( done )
2018-01-14 23:01:37 +01:00
} )
2018-05-30 16:24:40 +02:00
it ( 'should fail if recipient is public' , function ( done ) {
txController . networkStore = new ObservableStore ( 1 )
2018-10-10 20:41:17 +02:00
txController . addUnapprovedTransaction ( { from : selectedAddress , to : '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' } )
2019-07-31 22:17:11 +02:00
. catch ( ( err ) => {
if ( err . message === 'Recipient is a public account' ) done ( )
else done ( err )
} )
2018-05-30 16:24:40 +02:00
} )
2018-10-10 20:41:17 +02:00
it ( 'should fail if the from address isn\'t the selected address' , function ( done ) {
txController . addUnapprovedTransaction ( { from : '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' } )
. then ( function ( ) {
assert . fail ( 'transaction should not have been added' )
done ( )
} )
. catch ( function ( ) {
assert . ok ( 'pass' )
done ( )
} )
} )
2018-05-30 16:24:40 +02:00
it ( 'should not fail if recipient is public but not on mainnet' , function ( done ) {
txController . once ( 'newUnapprovedTx' , ( txMetaFromEmit ) => {
assert ( txMetaFromEmit , 'txMeta is falsey' )
done ( )
} )
2018-10-10 20:41:17 +02:00
txController . addUnapprovedTransaction ( { from : selectedAddress , to : '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' } )
2019-07-31 22:17:11 +02:00
. catch ( done )
2018-05-30 16:24:40 +02:00
} )
2018-12-13 20:14:46 +01:00
it ( 'should fail if netId is loading' , function ( done ) {
txController . networkStore = new ObservableStore ( 'loading' )
txController . addUnapprovedTransaction ( { from : selectedAddress , to : '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' } )
2019-07-31 22:17:11 +02:00
. catch ( ( err ) => {
if ( err . message === 'MetaMask is having trouble connecting to the network' ) done ( )
else done ( err )
} )
2018-12-13 20:14:46 +01:00
} )
2016-12-14 21:56:53 +01:00
} )
2018-04-13 21:38:07 +02:00
describe ( '#addTxGasDefaults' , function ( ) {
2018-05-25 07:41:09 +02:00
it ( 'should add the tx defaults if their are none' , async ( ) => {
2017-09-23 01:15:18 +02:00
const txMeta = {
2018-05-25 07:41:09 +02:00
txParams : {
from : '0xc684832530fcbddae4b4230a47e991ddcec2831d' ,
to : '0xc684832530fcbddae4b4230a47e991ddcec2831d' ,
2017-08-02 17:34:45 +02:00
} ,
2018-11-16 19:34:08 +01:00
history : [ { } ] ,
2017-08-02 17:34:45 +02:00
}
2018-05-25 07:41:09 +02:00
providerResultStub . eth _gasPrice = '4a817c800'
providerResultStub . eth _getBlockByNumber = { gasLimit : '47b784' }
providerResultStub . eth _estimateGas = '5209'
const txMetaWithDefaults = await txController . addTxGasDefaults ( txMeta )
assert ( txMetaWithDefaults . txParams . value , '0x0' , 'should have added 0x0 as the value' )
assert ( txMetaWithDefaults . txParams . gasPrice , 'should have added the gas price' )
assert ( txMetaWithDefaults . txParams . gas , 'should have added the gas field' )
2017-08-02 17:34:45 +02:00
} )
} )
2017-05-04 23:35:10 +02:00
describe ( '#addTx' , function ( ) {
2017-08-11 23:19:35 +02:00
it ( 'should emit updates' , function ( done ) {
2017-05-16 22:22:03 +02:00
const txMeta = {
id : '1' ,
status : 'unapproved' ,
metamaskNetworkId : currentNetworkId ,
2017-09-23 01:15:18 +02:00
txParams : { } ,
2017-05-16 22:22:03 +02:00
}
2017-09-27 01:52:08 +02:00
const eventNames = [ 'update:badge' , '1:unapproved' ]
2017-08-11 23:19:35 +02:00
const listeners = [ ]
eventNames . forEach ( ( eventName ) => {
listeners . push ( new Promise ( ( resolve ) => {
txController . once ( eventName , ( arg ) => {
resolve ( arg )
} )
} ) )
} )
Promise . all ( listeners )
2019-07-31 22:17:11 +02:00
. then ( ( returnValues ) => {
assert . deepEqual ( returnValues . pop ( ) , txMeta , 'last event 1:unapproved should return txMeta' )
done ( )
} )
. catch ( done )
2017-05-16 22:22:03 +02:00
txController . addTx ( txMeta )
2016-12-14 21:56:53 +01:00
} )
} )
2017-05-17 03:16:18 +02:00
describe ( '#approveTransaction' , function ( ) {
let txMeta , originalValue
beforeEach ( function ( ) {
originalValue = '0x01'
txMeta = {
id : '1' ,
status : 'unapproved' ,
metamaskNetworkId : currentNetworkId ,
txParams : {
nonce : originalValue ,
gas : originalValue ,
gasPrice : originalValue ,
} ,
}
} )
2017-07-13 21:25:43 +02:00
it ( 'does not overwrite set values' , function ( done ) {
2017-06-16 00:25:22 +02:00
this . timeout ( 15000 )
2017-05-17 03:16:18 +02:00
const wrongValue = '0x05'
txController . addTx ( txMeta )
2017-08-09 00:30:22 +02:00
providerResultStub . eth _gasPrice = wrongValue
providerResultStub . eth _estimateGas = '0x5209'
2017-05-17 03:16:18 +02:00
2017-07-26 20:56:52 +02:00
const signStub = sinon . stub ( txController , 'signTransaction' ) . callsFake ( ( ) => Promise . resolve ( ) )
2017-05-17 03:16:18 +02:00
2017-07-26 20:56:52 +02:00
const pubStub = sinon . stub ( txController , 'publishTransaction' ) . callsFake ( ( ) => {
txController . setTxHash ( '1' , originalValue )
2017-08-11 23:19:35 +02:00
txController . txStateManager . setTxStatusSubmitted ( '1' )
2017-07-26 20:56:52 +02:00
} )
2017-05-17 03:16:18 +02:00
2017-07-13 21:25:43 +02:00
txController . approveTransaction ( txMeta . id ) . then ( ( ) => {
2017-08-11 23:19:35 +02:00
const result = txController . txStateManager . getTx ( txMeta . id )
2017-05-17 03:16:18 +02:00
const params = result . txParams
assert . equal ( params . gas , originalValue , 'gas unmodified' )
assert . equal ( params . gasPrice , originalValue , 'gas price unmodified' )
2017-07-13 21:25:43 +02:00
assert . equal ( result . hash , originalValue , ` hash was set \n got: ${ result . hash } \n expected: ${ originalValue } ` )
2018-11-14 22:34:07 +01:00
assert . equal ( result . status , 'submitted' , 'Should have reached the submitted status.' )
2017-05-17 03:16:18 +02:00
signStub . restore ( )
pubStub . restore ( )
2017-07-13 21:25:43 +02:00
done ( )
} ) . catch ( done )
2017-05-17 03:16:18 +02:00
} )
} )
2017-05-04 23:35:10 +02:00
describe ( '#sign replay-protected tx' , function ( ) {
2017-06-13 02:42:54 +02:00
it ( 'prepares a tx with the chainId set' , function ( done ) {
2017-05-16 19:27:41 +02:00
txController . addTx ( { id : '1' , status : 'unapproved' , metamaskNetworkId : currentNetworkId , txParams : { } } , noop )
2017-07-13 21:25:43 +02:00
txController . signTransaction ( '1' ) . then ( ( rawTx ) => {
2017-03-30 23:23:23 +02:00
const ethTx = new EthTx ( ethUtil . toBuffer ( rawTx ) )
assert . equal ( ethTx . getChainId ( ) , currentNetworkId )
2017-06-13 02:42:54 +02:00
done ( )
2017-07-13 21:25:43 +02:00
} ) . catch ( done )
2017-03-30 23:23:23 +02:00
} )
} )
2017-09-23 01:15:18 +02:00
2017-09-26 04:47:03 +02:00
describe ( '#updateAndApproveTransaction' , function ( ) {
let txMeta
2018-04-28 02:12:52 +02:00
beforeEach ( ( ) => {
2017-09-26 04:47:03 +02:00
txMeta = {
id : 1 ,
status : 'unapproved' ,
txParams : {
2018-04-28 02:12:52 +02:00
from : fromAccount . address ,
2017-09-26 04:47:03 +02:00
to : '0x1678a085c290ebd122dc42cba69373b5953b831d' ,
gasPrice : '0x77359400' ,
gas : '0x7b0d' ,
nonce : '0x4b' ,
} ,
metamaskNetworkId : currentNetworkId ,
}
} )
2018-04-28 02:12:52 +02:00
it ( 'should update and approve transactions' , async ( ) => {
2017-09-23 01:15:18 +02:00
txController . txStateManager . addTx ( txMeta )
2018-04-28 02:12:52 +02:00
const approvalPromise = txController . updateAndApproveTransaction ( txMeta )
2017-09-26 04:47:03 +02:00
const tx = txController . txStateManager . getTx ( 1 )
assert . equal ( tx . status , 'approved' )
2018-04-28 02:12:52 +02:00
await approvalPromise
2017-09-23 01:15:18 +02:00
} )
2017-09-26 04:47:03 +02:00
} )
2017-09-23 01:15:18 +02:00
2017-09-26 04:47:03 +02:00
describe ( '#getChainId' , function ( ) {
it ( 'returns 0 when the chainId is NaN' , function ( ) {
txController . networkStore = new ObservableStore ( NaN )
assert . equal ( txController . getChainId ( ) , 0 )
2017-09-23 01:15:18 +02:00
} )
} )
describe ( '#cancelTransaction' , function ( ) {
beforeEach ( function ( ) {
2017-09-26 04:47:03 +02:00
txController . txStateManager . _saveTxList ( [
{ id : 0 , status : 'unapproved' , txParams : { } , metamaskNetworkId : currentNetworkId , history : [ { } ] } ,
{ id : 1 , status : 'rejected' , txParams : { } , metamaskNetworkId : currentNetworkId , history : [ { } ] } ,
{ id : 2 , status : 'approved' , txParams : { } , metamaskNetworkId : currentNetworkId , history : [ { } ] } ,
{ id : 3 , status : 'signed' , txParams : { } , metamaskNetworkId : currentNetworkId , history : [ { } ] } ,
{ id : 4 , status : 'submitted' , txParams : { } , metamaskNetworkId : currentNetworkId , history : [ { } ] } ,
{ id : 5 , status : 'confirmed' , txParams : { } , metamaskNetworkId : currentNetworkId , history : [ { } ] } ,
{ id : 6 , status : 'failed' , txParams : { } , metamaskNetworkId : currentNetworkId , history : [ { } ] } ,
] )
2017-09-23 01:15:18 +02:00
} )
2018-06-26 01:16:51 +02:00
it ( 'should emit a status change to rejected' , function ( done ) {
txController . once ( 'tx:status-update' , ( txId , status ) => {
try {
assert . equal ( status , 'rejected' , 'status should e rejected' )
assert . equal ( txId , 0 , 'id should e 0' )
done ( )
} catch ( e ) { done ( e ) }
} )
txController . cancelTransaction ( 0 )
2017-09-23 01:15:18 +02:00
} )
} )
2018-10-26 06:42:59 +02:00
describe ( '#createSpeedUpTransaction' , ( ) => {
let addTxSpy
let approveTransactionSpy
let txParams
let expectedTxParams
beforeEach ( ( ) => {
addTxSpy = sinon . spy ( txController , 'addTx' )
approveTransactionSpy = sinon . spy ( txController , 'approveTransaction' )
txParams = {
nonce : '0x00' ,
from : '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4' ,
to : '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4' ,
gas : '0x5209' ,
gasPrice : '0xa' ,
}
txController . txStateManager . _saveTxList ( [
2018-11-27 18:30:41 +01:00
{ id : 1 , status : 'submitted' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
2018-10-26 06:42:59 +02:00
] )
expectedTxParams = Object . assign ( { } , txParams , { gasPrice : '0xb' } )
} )
afterEach ( ( ) => {
addTxSpy . restore ( )
approveTransactionSpy . restore ( )
} )
it ( 'should call this.addTx and this.approveTransaction with the expected args' , async ( ) => {
await txController . createSpeedUpTransaction ( 1 )
assert . equal ( addTxSpy . callCount , 1 )
const addTxArgs = addTxSpy . getCall ( 0 ) . args [ 0 ]
assert . deepEqual ( addTxArgs . txParams , expectedTxParams )
const { lastGasPrice , type } = addTxArgs
assert . deepEqual ( { lastGasPrice , type } , {
lastGasPrice : '0xa' ,
type : TRANSACTION _TYPE _RETRY ,
} )
} )
it ( 'should call this.approveTransaction with the id of the returned tx' , async ( ) => {
const result = await txController . createSpeedUpTransaction ( 1 )
assert . equal ( approveTransactionSpy . callCount , 1 )
const approveTransactionArg = approveTransactionSpy . getCall ( 0 ) . args [ 0 ]
assert . equal ( result . id , approveTransactionArg )
} )
it ( 'should return the expected txMeta' , async ( ) => {
const result = await txController . createSpeedUpTransaction ( 1 )
assert . deepEqual ( result . txParams , expectedTxParams )
const { lastGasPrice , type } = result
assert . deepEqual ( { lastGasPrice , type } , {
lastGasPrice : '0xa' ,
type : TRANSACTION _TYPE _RETRY ,
} )
} )
} )
2017-09-23 01:15:18 +02:00
describe ( '#publishTransaction' , function ( ) {
2017-09-26 04:47:03 +02:00
let hash , txMeta
2017-09-23 01:15:18 +02:00
beforeEach ( function ( ) {
2017-09-26 04:47:03 +02:00
hash = '0x2a5523c6fa98b47b7d9b6c8320179785150b42a16bcff36b398c5062b65657e8'
2017-09-23 01:15:18 +02:00
txMeta = {
id : 1 ,
2017-09-26 04:47:03 +02:00
status : 'unapproved' ,
2017-09-23 01:15:18 +02:00
txParams : { } ,
metamaskNetworkId : currentNetworkId ,
}
2017-09-26 04:47:03 +02:00
providerResultStub . eth _sendRawTransaction = hash
2017-09-23 01:15:18 +02:00
} )
2017-09-26 04:47:03 +02:00
2017-09-23 01:15:18 +02:00
it ( 'should publish a tx, updates the rawTx when provided a one' , async function ( ) {
2018-07-31 05:18:01 +02:00
const rawTx = '0x477b2e6553c917af0db0388ae3da62965ff1a184558f61b749d1266b2e6d024c'
2017-09-23 01:15:18 +02:00
txController . txStateManager . addTx ( txMeta )
2018-07-31 05:18:01 +02:00
await txController . publishTransaction ( txMeta . id , rawTx )
2017-09-27 03:16:00 +02:00
const publishedTx = txController . txStateManager . getTx ( 1 )
2017-09-26 04:47:03 +02:00
assert . equal ( publishedTx . hash , hash )
assert . equal ( publishedTx . status , 'submitted' )
2017-09-23 01:15:18 +02:00
} )
2019-10-30 22:40:33 +01:00
it ( 'should ignore the error "Transaction Failed: known transaction" and be as usual' , async function ( ) {
providerResultStub [ 'eth_sendRawTransaction' ] = async ( _ , _ _ , _ _ _ , end ) => { end ( 'Transaction Failed: known transaction' ) }
const rawTx = '0xf86204831e848082520894f231d46dd78806e1dd93442cf33c7671f853874880802ca05f973e540f2d3c2f06d3725a626b75247593cb36477187ae07ecfe0a4db3cf57a00259b52ee8c58baaa385fb05c3f96116e58de89bcc165cb3bfdfc708672fed8a'
txController . txStateManager . addTx ( txMeta )
await txController . publishTransaction ( txMeta . id , rawTx )
const publishedTx = txController . txStateManager . getTx ( 1 )
assert . equal ( publishedTx . hash , '0x2cc5a25744486f7383edebbf32003e5a66e18135799593d6b5cdd2bb43674f09' )
assert . equal ( publishedTx . status , 'submitted' )
} )
2017-09-23 01:15:18 +02:00
} )
2018-03-13 22:30:59 +01:00
describe ( '#retryTransaction' , function ( ) {
2018-11-26 20:29:14 +01:00
it ( 'should create a new txMeta with the same txParams as the original one but with a higher gasPrice' , function ( done ) {
2018-07-03 00:49:33 +02:00
const txParams = {
2018-11-26 20:29:14 +01:00
gasPrice : '0xee6b2800' ,
2018-03-13 22:30:59 +01:00
nonce : '0x00' ,
from : '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4' ,
to : '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4' ,
data : '0x0' ,
}
txController . txStateManager . _saveTxList ( [
2018-11-16 19:34:08 +01:00
{ id : 1 , status : 'submitted' , metamaskNetworkId : currentNetworkId , txParams , history : [ { } ] } ,
2018-03-13 22:30:59 +01:00
] )
txController . retryTransaction ( 1 )
2019-07-31 22:17:11 +02:00
. then ( ( txMeta ) => {
assert . equal ( txMeta . txParams . gasPrice , '0x10642ac00' , 'gasPrice should have a %10 gasPrice bump' )
assert . equal ( txMeta . txParams . nonce , txParams . nonce , 'nonce should be the same' )
assert . equal ( txMeta . txParams . from , txParams . from , 'from should be the same' )
assert . equal ( txMeta . txParams . to , txParams . to , 'to should be the same' )
assert . equal ( txMeta . txParams . data , txParams . data , 'data should be the same' )
assert . ok ( ( 'lastGasPrice' in txMeta ) , 'should have the key `lastGasPrice`' )
assert . equal ( txController . txStateManager . getTxList ( ) . length , 2 )
done ( )
} ) . catch ( done )
2018-03-13 22:30:59 +01:00
} )
} )
describe ( '#_markNonceDuplicatesDropped' , function ( ) {
it ( 'should mark all nonce duplicates as dropped without marking the confirmed transaction as dropped' , function ( ) {
txController . txStateManager . _saveTxList ( [
{ id : 1 , status : 'confirmed' , metamaskNetworkId : currentNetworkId , history : [ { } ] , txParams : { nonce : '0x01' } } ,
{ id : 2 , status : 'submitted' , metamaskNetworkId : currentNetworkId , history : [ { } ] , txParams : { nonce : '0x01' } } ,
{ id : 3 , status : 'submitted' , metamaskNetworkId : currentNetworkId , history : [ { } ] , txParams : { nonce : '0x01' } } ,
{ id : 4 , status : 'submitted' , metamaskNetworkId : currentNetworkId , history : [ { } ] , txParams : { nonce : '0x01' } } ,
{ id : 5 , status : 'submitted' , metamaskNetworkId : currentNetworkId , history : [ { } ] , txParams : { nonce : '0x01' } } ,
{ id : 6 , status : 'submitted' , metamaskNetworkId : currentNetworkId , history : [ { } ] , txParams : { nonce : '0x01' } } ,
{ id : 7 , status : 'submitted' , metamaskNetworkId : currentNetworkId , history : [ { } ] , txParams : { nonce : '0x01' } } ,
] )
txController . _markNonceDuplicatesDropped ( 1 )
const confirmedTx = txController . txStateManager . getTx ( 1 )
const droppedTxs = txController . txStateManager . getFilteredTxList ( { nonce : '0x01' , status : 'dropped' } )
assert . equal ( confirmedTx . status , 'confirmed' , 'the confirmedTx should remain confirmed' )
assert . equal ( droppedTxs . length , 6 , 'their should be 6 dropped txs' )
} )
} )
2017-09-23 01:15:18 +02:00
2019-05-14 20:14:07 +02:00
describe ( '#_determineTransactionCategory' , function ( ) {
it ( 'should return a simple send transactionCategory when to is truthy but data is falsey' , async function ( ) {
const result = await txController . _determineTransactionCategory ( {
to : '0xabc' ,
data : '' ,
} )
assert . deepEqual ( result , { transactionCategory : SEND _ETHER _ACTION _KEY , getCodeResponse : undefined } )
} )
it ( 'should return a token transfer transactionCategory when data is for the respective method call' , async function ( ) {
const result = await txController . _determineTransactionCategory ( {
to : '0xabc' ,
data : '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a' ,
} )
assert . deepEqual ( result , { transactionCategory : TOKEN _METHOD _TRANSFER , getCodeResponse : undefined } )
} )
it ( 'should return a token approve transactionCategory when data is for the respective method call' , async function ( ) {
const result = await txController . _determineTransactionCategory ( {
to : '0xabc' ,
data : '0x095ea7b30000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C9700000000000000000000000000000000000000000000000000000000000000005' ,
} )
assert . deepEqual ( result , { transactionCategory : TOKEN _METHOD _APPROVE , getCodeResponse : undefined } )
} )
it ( 'should return a contract deployment transactionCategory when to is falsey and there is data' , async function ( ) {
const result = await txController . _determineTransactionCategory ( {
to : '' ,
data : '0xabd' ,
} )
assert . deepEqual ( result , { transactionCategory : DEPLOY _CONTRACT _ACTION _KEY , getCodeResponse : undefined } )
} )
it ( 'should return a simple send transactionCategory with a 0x getCodeResponse when there is data and but the to address is not a contract address' , async function ( ) {
const result = await txController . _determineTransactionCategory ( {
to : '0x9e673399f795D01116e9A8B2dD2F156705131ee9' ,
data : '0xabd' ,
} )
assert . deepEqual ( result , { transactionCategory : SEND _ETHER _ACTION _KEY , getCodeResponse : '0x' } )
} )
it ( 'should return a simple send transactionCategory with a null getCodeResponse when to is truthy and there is data and but getCode returns an error' , async function ( ) {
const result = await txController . _determineTransactionCategory ( {
to : '0xabc' ,
data : '0xabd' ,
} )
assert . deepEqual ( result , { transactionCategory : SEND _ETHER _ACTION _KEY , getCodeResponse : null } )
} )
it ( 'should return a contract interaction transactionCategory with the correct getCodeResponse when to is truthy and there is data and it is not a token transaction' , async function ( ) {
const _providerResultStub = {
// 1 gwei
eth _gasPrice : '0x0de0b6b3a7640000' ,
// by default, all accounts are external accounts (not contracts)
eth _getCode : '0xa' ,
}
const _provider = createTestProviderTools ( { scaffold : _providerResultStub } ) . provider
const _fromAccount = getTestAccounts ( ) [ 0 ]
const _blockTrackerStub = new EventEmitter ( )
_blockTrackerStub . getCurrentBlock = noop
_blockTrackerStub . getLatestBlock = noop
const _txController = new TransactionController ( {
provider : _provider ,
getGasPrice : function ( ) { return '0xee6b2800' } ,
networkStore : new ObservableStore ( currentNetworkId ) ,
txHistoryLimit : 10 ,
blockTracker : _blockTrackerStub ,
signTransaction : ( ethTx ) => new Promise ( ( resolve ) => {
ethTx . sign ( _fromAccount . key )
resolve ( )
} ) ,
} )
const result = await _txController . _determineTransactionCategory ( {
to : '0x9e673399f795D01116e9A8B2dD2F156705131ee9' ,
data : 'abd' ,
} )
assert . deepEqual ( result , { transactionCategory : CONTRACT _INTERACTION _KEY , getCodeResponse : '0x0a' } )
} )
2019-10-07 21:29:37 +02:00
it ( 'should return a contract interaction transactionCategory with the correct getCodeResponse when to is a contract address and data is falsey' , async function ( ) {
const _providerResultStub = {
// 1 gwei
eth _gasPrice : '0x0de0b6b3a7640000' ,
// by default, all accounts are external accounts (not contracts)
eth _getCode : '0xa' ,
}
const _provider = createTestProviderTools ( { scaffold : _providerResultStub } ) . provider
const _fromAccount = getTestAccounts ( ) [ 0 ]
const _blockTrackerStub = new EventEmitter ( )
_blockTrackerStub . getCurrentBlock = noop
_blockTrackerStub . getLatestBlock = noop
const _txController = new TransactionController ( {
provider : _provider ,
getGasPrice : function ( ) { return '0xee6b2800' } ,
networkStore : new ObservableStore ( currentNetworkId ) ,
txHistoryLimit : 10 ,
blockTracker : _blockTrackerStub ,
signTransaction : ( ethTx ) => new Promise ( ( resolve ) => {
ethTx . sign ( _fromAccount . key )
resolve ( )
} ) ,
} )
const result = await _txController . _determineTransactionCategory ( {
to : '0x9e673399f795D01116e9A8B2dD2F156705131ee9' ,
data : '' ,
} )
assert . deepEqual ( result , { transactionCategory : CONTRACT _INTERACTION _KEY , getCodeResponse : '0x0a' } )
} )
2019-05-14 20:14:07 +02:00
} )
2017-09-23 01:15:18 +02:00
describe ( '#getPendingTransactions' , function ( ) {
beforeEach ( function ( ) {
txController . txStateManager . _saveTxList ( [
{ id : 1 , status : 'unapproved' , metamaskNetworkId : currentNetworkId , txParams : { } } ,
2018-11-16 19:34:08 +01:00
{ id : 2 , status : 'rejected' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
{ id : 3 , status : 'approved' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
{ id : 4 , status : 'signed' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
{ id : 5 , status : 'submitted' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
{ id : 6 , status : 'confirmed' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
{ id : 7 , status : 'failed' , metamaskNetworkId : currentNetworkId , txParams : { } , history : [ { } ] } ,
2017-09-23 01:15:18 +02:00
] )
} )
2018-11-14 22:34:07 +01:00
it ( 'should show only submitted and approved transactions as pending transasction' , function ( ) {
assert ( txController . pendingTxTracker . getPendingTransactions ( ) . length , 2 )
const states = txController . pendingTxTracker . getPendingTransactions ( ) . map ( tx => tx . status )
assert ( states . includes ( 'approved' ) , 'includes approved' )
assert ( states . includes ( 'submitted' ) , 'includes submitted' )
2017-09-23 01:15:18 +02:00
} )
} )
2017-09-22 23:19:14 +02:00
} )