commit
9a658ee53d
@ -0,0 +1,73 @@ |
|||||||
|
const ObservableStore = require('obs-store') |
||||||
|
const extend = require('xtend') |
||||||
|
|
||||||
|
class AppStateController { |
||||||
|
/** |
||||||
|
* @constructor |
||||||
|
* @param opts |
||||||
|
*/ |
||||||
|
constructor (opts = {}) { |
||||||
|
const {initState, onInactiveTimeout, preferencesStore} = opts |
||||||
|
const {preferences} = preferencesStore.getState() |
||||||
|
|
||||||
|
this.onInactiveTimeout = onInactiveTimeout || (() => {}) |
||||||
|
this.store = new ObservableStore(extend({ |
||||||
|
timeoutMinutes: 0, |
||||||
|
}, initState)) |
||||||
|
this.timer = null |
||||||
|
|
||||||
|
preferencesStore.subscribe(state => { |
||||||
|
this._setInactiveTimeout(state.preferences.autoLogoutTimeLimit) |
||||||
|
}) |
||||||
|
|
||||||
|
this._setInactiveTimeout(preferences.autoLogoutTimeLimit) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the last active time to the current time |
||||||
|
* @return {void} |
||||||
|
*/ |
||||||
|
setLastActiveTime () { |
||||||
|
this._resetTimer() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the inactive timeout for the app |
||||||
|
* @param {number} timeoutMinutes the inactive timeout in minutes |
||||||
|
* @return {void} |
||||||
|
* @private |
||||||
|
*/ |
||||||
|
_setInactiveTimeout (timeoutMinutes) { |
||||||
|
this.store.putState({ |
||||||
|
timeoutMinutes, |
||||||
|
}) |
||||||
|
|
||||||
|
this._resetTimer() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resets the internal inactive timer |
||||||
|
* |
||||||
|
* If the {@code timeoutMinutes} state is falsy (i.e., zero) then a new |
||||||
|
* timer will not be created. |
||||||
|
* |
||||||
|
* @return {void} |
||||||
|
* @private |
||||||
|
*/ |
||||||
|
_resetTimer () { |
||||||
|
const {timeoutMinutes} = this.store.getState() |
||||||
|
|
||||||
|
if (this.timer) { |
||||||
|
clearTimeout(this.timer) |
||||||
|
} |
||||||
|
|
||||||
|
if (!timeoutMinutes) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
this.timer = setTimeout(() => this.onInactiveTimeout(), timeoutMinutes * 60 * 1000) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = AppStateController |
||||||
|
|
@ -1,136 +0,0 @@ |
|||||||
const ObservableStore = require('obs-store') |
|
||||||
const extend = require('xtend') |
|
||||||
const PhishingDetector = require('eth-phishing-detect/src/detector') |
|
||||||
const log = require('loglevel') |
|
||||||
|
|
||||||
// compute phishing lists
|
|
||||||
const PHISHING_DETECTION_CONFIG = require('eth-phishing-detect/src/config.json') |
|
||||||
// every four minutes
|
|
||||||
const POLLING_INTERVAL = 4 * 60 * 1000 |
|
||||||
|
|
||||||
class BlacklistController { |
|
||||||
|
|
||||||
/** |
|
||||||
* Responsible for polling for and storing an up to date 'eth-phishing-detect' config.json file, while |
|
||||||
* exposing a method that can check whether a given url is a phishing attempt. The 'eth-phishing-detect' |
|
||||||
* config.json file contains a fuzzylist, whitelist and blacklist. |
|
||||||
* |
|
||||||
* |
|
||||||
* @typedef {Object} BlacklistController |
|
||||||
* @param {object} opts Overrides the defaults for the initial state of this.store |
|
||||||
* @property {object} store The the store of the current phishing config |
|
||||||
* @property {object} store.phishing Contains fuzzylist, whitelist and blacklist arrays. @see |
|
||||||
* {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json}
|
|
||||||
* @property {object} _phishingDetector The PhishingDetector instantiated by passing store.phishing to |
|
||||||
* PhishingDetector. |
|
||||||
* @property {object} _phishingUpdateIntervalRef Id of the interval created to periodically update the blacklist |
|
||||||
* |
|
||||||
*/ |
|
||||||
constructor (opts = {}) { |
|
||||||
const initState = extend({ |
|
||||||
phishing: PHISHING_DETECTION_CONFIG, |
|
||||||
whitelist: [], |
|
||||||
}, opts.initState) |
|
||||||
this.store = new ObservableStore(initState) |
|
||||||
// phishing detector
|
|
||||||
this._phishingDetector = null |
|
||||||
this._setupPhishingDetector(initState.phishing) |
|
||||||
// polling references
|
|
||||||
this._phishingUpdateIntervalRef = null |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Adds the given hostname to the runtime whitelist |
|
||||||
* @param {string} hostname the hostname to whitelist |
|
||||||
*/ |
|
||||||
whitelistDomain (hostname) { |
|
||||||
if (!hostname) { |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
const { whitelist } = this.store.getState() |
|
||||||
this.store.updateState({ |
|
||||||
whitelist: [...new Set([hostname, ...whitelist])], |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Given a url, returns the result of checking if that url is in the store.phishing blacklist |
|
||||||
* |
|
||||||
* @param {string} hostname The hostname portion of a url; the one that will be checked against the white and |
|
||||||
* blacklists of store.phishing |
|
||||||
* @returns {boolean} Whether or not the passed hostname is on our phishing blacklist |
|
||||||
* |
|
||||||
*/ |
|
||||||
checkForPhishing (hostname) { |
|
||||||
if (!hostname) return false |
|
||||||
|
|
||||||
const { whitelist } = this.store.getState() |
|
||||||
if (whitelist.some((e) => e === hostname)) { |
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
const { result } = this._phishingDetector.check(hostname) |
|
||||||
return result |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Queries `https://api.infura.io/v2/blacklist` for an updated blacklist config. This is passed to this._phishingDetector |
|
||||||
* to update our phishing detector instance, and is updated in the store. The new phishing config is returned |
|
||||||
* |
|
||||||
* |
|
||||||
* @returns {Promise<object>} Promises the updated blacklist config for the phishingDetector |
|
||||||
* |
|
||||||
*/ |
|
||||||
async updatePhishingList () { |
|
||||||
// make request
|
|
||||||
let response |
|
||||||
try { |
|
||||||
response = await fetch('https://api.infura.io/v2/blacklist') |
|
||||||
} catch (err) { |
|
||||||
log.error(new Error(`BlacklistController - failed to fetch blacklist:\n${err.stack}`)) |
|
||||||
return |
|
||||||
} |
|
||||||
// parse response
|
|
||||||
let rawResponse |
|
||||||
let phishing |
|
||||||
try { |
|
||||||
const rawResponse = await response.text() |
|
||||||
phishing = JSON.parse(rawResponse) |
|
||||||
} catch (err) { |
|
||||||
log.error(new Error(`BlacklistController - failed to parse blacklist:\n${rawResponse}`)) |
|
||||||
return |
|
||||||
} |
|
||||||
// update current blacklist
|
|
||||||
this.store.updateState({ phishing }) |
|
||||||
this._setupPhishingDetector(phishing) |
|
||||||
return phishing |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Initiates the updating of the local blacklist at a set interval. The update is done via this.updatePhishingList(). |
|
||||||
* Also, this method store a reference to that interval at this._phishingUpdateIntervalRef |
|
||||||
* |
|
||||||
*/ |
|
||||||
scheduleUpdates () { |
|
||||||
if (this._phishingUpdateIntervalRef) return |
|
||||||
this.updatePhishingList() |
|
||||||
this._phishingUpdateIntervalRef = setInterval(() => { |
|
||||||
this.updatePhishingList() |
|
||||||
}, POLLING_INTERVAL) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Sets this._phishingDetector to a new PhishingDetector instance. |
|
||||||
* @see {@link https://github.com/MetaMask/eth-phishing-detect}
|
|
||||||
* |
|
||||||
* @private |
|
||||||
* @param {object} config A config object like that found at {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json}
|
|
||||||
* |
|
||||||
*/ |
|
||||||
_setupPhishingDetector (config) { |
|
||||||
this._phishingDetector = new PhishingDetector(config) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = BlacklistController |
|
@ -1,19 +0,0 @@ |
|||||||
const BlockTracker = require('eth-block-tracker') |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a block tracker that sends platform events on success and failure |
|
||||||
*/ |
|
||||||
module.exports = function createBlockTracker (args, platform) { |
|
||||||
const blockTracker = new BlockTracker(args) |
|
||||||
blockTracker.on('latest', () => { |
|
||||||
if (platform && platform.sendMessage) { |
|
||||||
platform.sendMessage({ action: 'ethereum-ping-success' }) |
|
||||||
} |
|
||||||
}) |
|
||||||
blockTracker.on('error', () => { |
|
||||||
if (platform && platform.sendMessage) { |
|
||||||
platform.sendMessage({ action: 'ethereum-ping-error' }) |
|
||||||
} |
|
||||||
}) |
|
||||||
return blockTracker |
|
||||||
} |
|
@ -1,180 +0,0 @@ |
|||||||
const ObservableStore = require('obs-store') |
|
||||||
const extend = require('xtend') |
|
||||||
const log = require('loglevel') |
|
||||||
|
|
||||||
// every three seconds when an incomplete tx is waiting
|
|
||||||
const POLLING_INTERVAL = 3000 |
|
||||||
|
|
||||||
class ShapeshiftController { |
|
||||||
|
|
||||||
/** |
|
||||||
* Controller responsible for managing the list of shapeshift transactions. On construction, it initiates a poll |
|
||||||
* that queries a shapeshift.io API for updates to any pending shapeshift transactions |
|
||||||
* |
|
||||||
* @typedef {Object} ShapeshiftController |
|
||||||
* @param {object} opts Overrides the defaults for the initial state of this.store |
|
||||||
* @property {array} opts.initState initializes the the state of the ShapeshiftController. Can contain an |
|
||||||
* shapeShiftTxList array. |
|
||||||
* @property {array} shapeShiftTxList An array of ShapeShiftTx objects |
|
||||||
* |
|
||||||
*/ |
|
||||||
constructor (opts = {}) { |
|
||||||
const initState = extend({ |
|
||||||
shapeShiftTxList: [], |
|
||||||
}, opts.initState) |
|
||||||
this.store = new ObservableStore(initState) |
|
||||||
this.pollForUpdates() |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Represents, and contains data about, a single shapeshift transaction. |
|
||||||
* @typedef {Object} ShapeShiftTx |
|
||||||
* @property {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the |
|
||||||
* user's Metamask account |
|
||||||
* @property {string} depositType - An abbreviation of the type of crypto currency to be deposited. |
|
||||||
* @property {string} key - The 'shapeshift' key differentiates this from other types of txs in Metamask |
|
||||||
* @property {number} time - The time at which the tx was created |
|
||||||
* @property {object} response - Initiated as an empty object, which will be replaced by a Response object. @see {@link |
|
||||||
* https://developer.mozilla.org/en-US/docs/Web/API/Response}
|
|
||||||
*/ |
|
||||||
|
|
||||||
//
|
|
||||||
// PUBLIC METHODS
|
|
||||||
//
|
|
||||||
|
|
||||||
/** |
|
||||||
* A getter for the shapeShiftTxList property |
|
||||||
* |
|
||||||
* @returns {array<ShapeShiftTx>} |
|
||||||
* |
|
||||||
*/ |
|
||||||
getShapeShiftTxList () { |
|
||||||
const shapeShiftTxList = this.store.getState().shapeShiftTxList |
|
||||||
return shapeShiftTxList |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* A getter for all ShapeShiftTx in the shapeShiftTxList that have not successfully completed a deposit. |
|
||||||
* |
|
||||||
* @returns {array<ShapeShiftTx>} Only includes ShapeShiftTx which has a response property with a status !== complete |
|
||||||
* |
|
||||||
*/ |
|
||||||
getPendingTxs () { |
|
||||||
const txs = this.getShapeShiftTxList() |
|
||||||
const pending = txs.filter(tx => tx.response && tx.response.status !== 'complete') |
|
||||||
return pending |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* A poll that exists as long as there are pending transactions. Each call attempts to update the data of any |
|
||||||
* pendingTxs, and then calls itself again. If there are no pending txs, the recursive call is not made and |
|
||||||
* the polling stops. |
|
||||||
* |
|
||||||
* this.updateTx is used to attempt the update to the pendingTxs in the ShapeShiftTxList, and that updated data |
|
||||||
* is saved with saveTx. |
|
||||||
* |
|
||||||
*/ |
|
||||||
pollForUpdates () { |
|
||||||
const pendingTxs = this.getPendingTxs() |
|
||||||
|
|
||||||
if (pendingTxs.length === 0) { |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
Promise.all(pendingTxs.map((tx) => { |
|
||||||
return this.updateTx(tx) |
|
||||||
})) |
|
||||||
.then((results) => { |
|
||||||
results.forEach(tx => this.saveTx(tx)) |
|
||||||
this.timeout = setTimeout(this.pollForUpdates.bind(this), POLLING_INTERVAL) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Attempts to update a ShapeShiftTx with data from a shapeshift.io API. Both the response and time properties |
|
||||||
* can be updated. The response property is updated with every call, but the time property is only updated when |
|
||||||
* the response status updates to 'complete'. This will occur once the user makes a deposit as the ShapeShiftTx |
|
||||||
* depositAddress |
|
||||||
* |
|
||||||
* @param {ShapeShiftTx} tx The tx to update |
|
||||||
* |
|
||||||
*/ |
|
||||||
async updateTx (tx) { |
|
||||||
try { |
|
||||||
const url = `https://shapeshift.io/txStat/${tx.depositAddress}` |
|
||||||
const response = await fetch(url) |
|
||||||
const json = await response.json() |
|
||||||
tx.response = json |
|
||||||
if (tx.response.status === 'complete') { |
|
||||||
tx.time = new Date().getTime() |
|
||||||
} |
|
||||||
return tx |
|
||||||
} catch (err) { |
|
||||||
log.warn(err) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Saves an updated to a ShapeShiftTx in the shapeShiftTxList. If the passed ShapeShiftTx is not in the |
|
||||||
* shapeShiftTxList, nothing happens. |
|
||||||
* |
|
||||||
* @param {ShapeShiftTx} tx The updated tx to save, if it exists in the current shapeShiftTxList |
|
||||||
* |
|
||||||
*/ |
|
||||||
saveTx (tx) { |
|
||||||
const { shapeShiftTxList } = this.store.getState() |
|
||||||
const index = shapeShiftTxList.indexOf(tx) |
|
||||||
if (index !== -1) { |
|
||||||
shapeShiftTxList[index] = tx |
|
||||||
this.store.updateState({ shapeShiftTxList }) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Removes a ShapeShiftTx from the shapeShiftTxList |
|
||||||
* |
|
||||||
* @param {ShapeShiftTx} tx The tx to remove |
|
||||||
* |
|
||||||
*/ |
|
||||||
removeShapeShiftTx (tx) { |
|
||||||
const { shapeShiftTxList } = this.store.getState() |
|
||||||
const index = shapeShiftTxList.indexOf(index) |
|
||||||
if (index !== -1) { |
|
||||||
shapeShiftTxList.splice(index, 1) |
|
||||||
} |
|
||||||
this.updateState({ shapeShiftTxList }) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a new ShapeShiftTx, adds it to the shapeShiftTxList, and initiates a new poll for updates of pending txs |
|
||||||
* |
|
||||||
* @param {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the |
|
||||||
* user's Metamask account |
|
||||||
* @param {string} depositType - An abbreviation of the type of crypto currency to be deposited. |
|
||||||
* |
|
||||||
*/ |
|
||||||
createShapeShiftTx (depositAddress, depositType) { |
|
||||||
const state = this.store.getState() |
|
||||||
let { shapeShiftTxList } = state |
|
||||||
|
|
||||||
var shapeShiftTx = { |
|
||||||
depositAddress, |
|
||||||
depositType, |
|
||||||
key: 'shapeshift', |
|
||||||
time: new Date().getTime(), |
|
||||||
response: {}, |
|
||||||
} |
|
||||||
|
|
||||||
if (!shapeShiftTxList) { |
|
||||||
shapeShiftTxList = [shapeShiftTx] |
|
||||||
} else { |
|
||||||
shapeShiftTxList.push(shapeShiftTx) |
|
||||||
} |
|
||||||
|
|
||||||
this.store.updateState({ shapeShiftTxList }) |
|
||||||
this.pollForUpdates() |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
module.exports = ShapeshiftController |
|
@ -1,161 +0,0 @@ |
|||||||
const EthQuery = require('ethjs-query') |
|
||||||
const assert = require('assert') |
|
||||||
const Mutex = require('await-semaphore').Mutex |
|
||||||
/** |
|
||||||
@param opts {Object} |
|
||||||
@param {Object} opts.provider a ethereum provider |
|
||||||
@param {Function} opts.getPendingTransactions a function that returns an array of txMeta |
|
||||||
whosee status is `submitted` |
|
||||||
@param {Function} opts.getConfirmedTransactions a function that returns an array of txMeta |
|
||||||
whose status is `confirmed` |
|
||||||
@class |
|
||||||
*/ |
|
||||||
class NonceTracker { |
|
||||||
|
|
||||||
constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) { |
|
||||||
this.provider = provider |
|
||||||
this.blockTracker = blockTracker |
|
||||||
this.ethQuery = new EthQuery(provider) |
|
||||||
this.getPendingTransactions = getPendingTransactions |
|
||||||
this.getConfirmedTransactions = getConfirmedTransactions |
|
||||||
this.lockMap = {} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
@returns {Promise<Object>} with the key releaseLock (the gloabl mutex) |
|
||||||
*/ |
|
||||||
async getGlobalLock () { |
|
||||||
const globalMutex = this._lookupMutex('global') |
|
||||||
// await global mutex free
|
|
||||||
const releaseLock = await globalMutex.acquire() |
|
||||||
return { releaseLock } |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @typedef NonceDetails |
|
||||||
* @property {number} highestLocallyConfirmed - A hex string of the highest nonce on a confirmed transaction. |
|
||||||
* @property {number} nextNetworkNonce - The next nonce suggested by the eth_getTransactionCount method. |
|
||||||
* @property {number} highestSuggested - The maximum between the other two, the number returned. |
|
||||||
*/ |
|
||||||
|
|
||||||
/** |
|
||||||
this will return an object with the `nextNonce` `nonceDetails` of type NonceDetails, and the releaseLock |
|
||||||
Note: releaseLock must be called after adding a signed tx to pending transactions (or discarding). |
|
||||||
|
|
||||||
@param address {string} the hex string for the address whose nonce we are calculating |
|
||||||
@returns {Promise<NonceDetails>} |
|
||||||
*/ |
|
||||||
async getNonceLock (address) { |
|
||||||
// await global mutex free
|
|
||||||
await this._globalMutexFree() |
|
||||||
// await lock free, then take lock
|
|
||||||
const releaseLock = await this._takeMutex(address) |
|
||||||
try { |
|
||||||
// evaluate multiple nextNonce strategies
|
|
||||||
const nonceDetails = {} |
|
||||||
const networkNonceResult = await this._getNetworkNextNonce(address) |
|
||||||
const highestLocallyConfirmed = this._getHighestLocallyConfirmed(address) |
|
||||||
const nextNetworkNonce = networkNonceResult.nonce |
|
||||||
const highestSuggested = Math.max(nextNetworkNonce, highestLocallyConfirmed) |
|
||||||
|
|
||||||
const pendingTxs = this.getPendingTransactions(address) |
|
||||||
const localNonceResult = this._getHighestContinuousFrom(pendingTxs, highestSuggested) || 0 |
|
||||||
|
|
||||||
nonceDetails.params = { |
|
||||||
highestLocallyConfirmed, |
|
||||||
highestSuggested, |
|
||||||
nextNetworkNonce, |
|
||||||
} |
|
||||||
nonceDetails.local = localNonceResult |
|
||||||
nonceDetails.network = networkNonceResult |
|
||||||
|
|
||||||
const nextNonce = Math.max(networkNonceResult.nonce, localNonceResult.nonce) |
|
||||||
assert(Number.isInteger(nextNonce), `nonce-tracker - nextNonce is not an integer - got: (${typeof nextNonce}) "${nextNonce}"`) |
|
||||||
|
|
||||||
// return nonce and release cb
|
|
||||||
return { nextNonce, nonceDetails, releaseLock } |
|
||||||
} catch (err) { |
|
||||||
// release lock if we encounter an error
|
|
||||||
releaseLock() |
|
||||||
throw err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
async _globalMutexFree () { |
|
||||||
const globalMutex = this._lookupMutex('global') |
|
||||||
const releaseLock = await globalMutex.acquire() |
|
||||||
releaseLock() |
|
||||||
} |
|
||||||
|
|
||||||
async _takeMutex (lockId) { |
|
||||||
const mutex = this._lookupMutex(lockId) |
|
||||||
const releaseLock = await mutex.acquire() |
|
||||||
return releaseLock |
|
||||||
} |
|
||||||
|
|
||||||
_lookupMutex (lockId) { |
|
||||||
let mutex = this.lockMap[lockId] |
|
||||||
if (!mutex) { |
|
||||||
mutex = new Mutex() |
|
||||||
this.lockMap[lockId] = mutex |
|
||||||
} |
|
||||||
return mutex |
|
||||||
} |
|
||||||
|
|
||||||
async _getNetworkNextNonce (address) { |
|
||||||
// calculate next nonce
|
|
||||||
// we need to make sure our base count
|
|
||||||
// and pending count are from the same block
|
|
||||||
const blockNumber = await this.blockTracker.getLatestBlock() |
|
||||||
const baseCountBN = await this.ethQuery.getTransactionCount(address, blockNumber) |
|
||||||
const baseCount = baseCountBN.toNumber() |
|
||||||
assert(Number.isInteger(baseCount), `nonce-tracker - baseCount is not an integer - got: (${typeof baseCount}) "${baseCount}"`) |
|
||||||
const nonceDetails = { blockNumber, baseCount } |
|
||||||
return { name: 'network', nonce: baseCount, details: nonceDetails } |
|
||||||
} |
|
||||||
|
|
||||||
_getHighestLocallyConfirmed (address) { |
|
||||||
const confirmedTransactions = this.getConfirmedTransactions(address) |
|
||||||
const highest = this._getHighestNonce(confirmedTransactions) |
|
||||||
return Number.isInteger(highest) ? highest + 1 : 0 |
|
||||||
} |
|
||||||
|
|
||||||
_getHighestNonce (txList) { |
|
||||||
const nonces = txList.map((txMeta) => { |
|
||||||
const nonce = txMeta.txParams.nonce |
|
||||||
assert(typeof nonce, 'string', 'nonces should be hex strings') |
|
||||||
return parseInt(nonce, 16) |
|
||||||
}) |
|
||||||
const highestNonce = Math.max.apply(null, nonces) |
|
||||||
return highestNonce |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
@typedef {object} highestContinuousFrom |
|
||||||
@property {string} - name the name for how the nonce was calculated based on the data used |
|
||||||
@property {number} - nonce the next suggested nonce |
|
||||||
@property {object} - details the provided starting nonce that was used (for debugging) |
|
||||||
*/ |
|
||||||
/** |
|
||||||
@param txList {array} - list of txMeta's |
|
||||||
@param startPoint {number} - the highest known locally confirmed nonce |
|
||||||
@returns {highestContinuousFrom} |
|
||||||
*/ |
|
||||||
_getHighestContinuousFrom (txList, startPoint) { |
|
||||||
const nonces = txList.map((txMeta) => { |
|
||||||
const nonce = txMeta.txParams.nonce |
|
||||||
assert(typeof nonce, 'string', 'nonces should be hex strings') |
|
||||||
return parseInt(nonce, 16) |
|
||||||
}) |
|
||||||
|
|
||||||
let highest = startPoint |
|
||||||
while (nonces.includes(highest)) { |
|
||||||
highest++ |
|
||||||
} |
|
||||||
|
|
||||||
return { name: 'local', nonce: highest, details: { startPoint, highest } } |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
module.exports = NonceTracker |
|
@ -1,17 +0,0 @@ |
|||||||
const MessageManager = require('./lib/message-manager') |
|
||||||
const PersonalMessageManager = require('./lib/personal-message-manager') |
|
||||||
const TypedMessageManager = require('./lib/typed-message-manager') |
|
||||||
|
|
||||||
class UserActionController { |
|
||||||
|
|
||||||
constructor (opts = {}) { |
|
||||||
|
|
||||||
this.messageManager = new MessageManager() |
|
||||||
this.personalMessageManager = new PersonalMessageManager() |
|
||||||
this.typedMessageManager = new TypedMessageManager() |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
module.exports = UserActionController |
|
@ -0,0 +1,26 @@ |
|||||||
|
const { |
||||||
|
getMetaMetricState, |
||||||
|
} = require('../../../ui/app/selectors/selectors') |
||||||
|
const { |
||||||
|
sendMetaMetricsEvent, |
||||||
|
} = require('../../../ui/app/helpers/utils/metametrics.util') |
||||||
|
|
||||||
|
const inDevelopment = process.env.NODE_ENV === 'development' |
||||||
|
|
||||||
|
const METAMETRICS_TRACKING_URL = inDevelopment |
||||||
|
? 'http://www.metamask.io/metametrics' |
||||||
|
: 'http://www.metamask.io/metametrics-prod' |
||||||
|
|
||||||
|
function backEndMetaMetricsEvent (metaMaskState, eventData) { |
||||||
|
const stateEventData = getMetaMetricState({ metamask: metaMaskState }) |
||||||
|
|
||||||
|
if (stateEventData.participateInMetaMetrics) { |
||||||
|
sendMetaMetricsEvent({ |
||||||
|
...stateEventData, |
||||||
|
...eventData, |
||||||
|
url: METAMETRICS_TRACKING_URL + '/backend', |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = backEndMetaMetricsEvent |
@ -0,0 +1,16 @@ |
|||||||
|
module.exports = createDnodeRemoteGetter |
||||||
|
|
||||||
|
function createDnodeRemoteGetter (dnode) { |
||||||
|
let remote |
||||||
|
|
||||||
|
dnode.once('remote', (_remote) => { |
||||||
|
remote = _remote |
||||||
|
}) |
||||||
|
|
||||||
|
async function getRemote () { |
||||||
|
if (remote) return remote |
||||||
|
return await new Promise(resolve => dnode.once('remote', resolve)) |
||||||
|
} |
||||||
|
|
||||||
|
return getRemote |
||||||
|
} |
@ -1,16 +0,0 @@ |
|||||||
module.exports = createProviderMiddleware |
|
||||||
|
|
||||||
/** |
|
||||||
* Forwards an HTTP request to the current Web3 provider |
|
||||||
* |
|
||||||
* @param {{ provider: Object }} config Configuration containing current Web3 provider |
|
||||||
*/ |
|
||||||
function createProviderMiddleware ({ provider }) { |
|
||||||
return (req, res, next, end) => { |
|
||||||
provider.sendAsync(req, (err, _res) => { |
|
||||||
if (err) return end(err) |
|
||||||
res.result = _res.result |
|
||||||
end() |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,71 @@ |
|||||||
|
## Creating Metrics Events |
||||||
|
|
||||||
|
The `metricsEvent` method is made available to all components via context. This is done in `metamask-extension/ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js`. As such, it can be called in all components by first adding it to the context proptypes: |
||||||
|
|
||||||
|
``` |
||||||
|
static contextTypes = { |
||||||
|
t: PropTypes.func, |
||||||
|
metricsEvent: PropTypes.func, |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
and then accessing it on `this.context`. |
||||||
|
|
||||||
|
Below is an example of a metrics event call: |
||||||
|
|
||||||
|
``` |
||||||
|
this.context.metricsEvent({ |
||||||
|
eventOpts: { |
||||||
|
category: 'Navigation', |
||||||
|
action: 'Main Menu', |
||||||
|
name: 'Switched Account', |
||||||
|
}, |
||||||
|
}) |
||||||
|
``` |
||||||
|
|
||||||
|
### Base Schema |
||||||
|
|
||||||
|
Every `metricsEvent` call is passed an object that must have an `eventOpts` property. This property is an object that itself must have three properties: |
||||||
|
- category: categorizes events according to the schema we have set up in our matomo.org instance |
||||||
|
- action: usually describes the page on which the event takes place, or sometimes a significant subsections of a page |
||||||
|
- name: a very specific descriptor of the event |
||||||
|
|
||||||
|
### Implicit properties |
||||||
|
|
||||||
|
All metrics events send the following data when called: |
||||||
|
- network |
||||||
|
- environmentType |
||||||
|
- activeCurrency |
||||||
|
- accountType |
||||||
|
- numberOfTokens |
||||||
|
- numberOfAccounts |
||||||
|
|
||||||
|
These are added to the metrics event via the metametrics provider. |
||||||
|
|
||||||
|
### Custom Variables |
||||||
|
|
||||||
|
Metrics events can include custom variables. These are included within the `customVariables` property that is a first-level property within first param passed to `metricsEvent`. |
||||||
|
|
||||||
|
For example: |
||||||
|
``` |
||||||
|
this.context.metricsEvent({ |
||||||
|
eventOpts: { |
||||||
|
category: 'Settings', |
||||||
|
action: 'Custom RPC', |
||||||
|
name: 'Error', |
||||||
|
}, |
||||||
|
customVariables: { |
||||||
|
networkId: newRpc, |
||||||
|
chainId, |
||||||
|
}, |
||||||
|
}) |
||||||
|
``` |
||||||
|
|
||||||
|
Custom variables can have custom property names and values can be strings or numbers. |
||||||
|
|
||||||
|
**To include a custom variable, there are a set of necessary steps you must take.** |
||||||
|
|
||||||
|
1. First you must declare a constant equal to the desired name of the custom variable property in `metamask-extension/ui/app/helpers/utils/metametrics.util.js` under `//Custom Variable Declarations` |
||||||
|
1. Then you must add that name to the `customVariableNameIdMap` declaration |
||||||
|
1. The id must be between 1 and 5 |
||||||
|
1. There can be no more than 5 custom variables assigned ids on a given url |
@ -0,0 +1,5 @@ |
|||||||
|
# MetaMask Design System |
||||||
|
|
||||||
|
A design system is a series of components that can be reused in different combinations. Design systems allow you to manage design at scale. |
||||||
|
|
||||||
|
Design System [Figma File](https://www.figma.com/file/aWgwMrzdAuv9VuPdtst64uuw/Style-Guide?node-id=211%3A0) |
@ -0,0 +1,5 @@ |
|||||||
|
# Google Chrome/Brave Limited Site Access for Extensions |
||||||
|
|
||||||
|
Problem: MetaMask doesn't work with limited site access enabled under Chrome's extensions. |
||||||
|
|
||||||
|
Solution: In addition to the site you wish to whitelist, you must add 'api.infura.io' as another domain, so the MetaMask extension is authorized to make RPC calls to Infura. |
@ -0,0 +1,9 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
|
||||||
|
set -e |
||||||
|
set -u |
||||||
|
set -o pipefail |
||||||
|
|
||||||
|
export PATH="$PATH:./node_modules/.bin" |
||||||
|
|
||||||
|
shell-parallel -s 'static-server test/web3 --port 8080' -x 'sleep 5 && mocha test/e2e/beta/web3.spec' |
@ -0,0 +1,365 @@ |
|||||||
|
const path = require('path') |
||||||
|
const assert = require('assert') |
||||||
|
const webdriver = require('selenium-webdriver') |
||||||
|
const { By } = webdriver |
||||||
|
const { |
||||||
|
delay, |
||||||
|
buildChromeWebDriver, |
||||||
|
buildFirefoxWebdriver, |
||||||
|
installWebExt, |
||||||
|
getExtensionIdChrome, |
||||||
|
getExtensionIdFirefox, |
||||||
|
} = require('../func') |
||||||
|
const { |
||||||
|
checkBrowserForConsoleErrors, |
||||||
|
closeAllWindowHandlesExcept, |
||||||
|
findElement, |
||||||
|
findElements, |
||||||
|
openNewPage, |
||||||
|
switchToWindowWithTitle, |
||||||
|
verboseReportOnFailure, |
||||||
|
waitUntilXWindowHandles, |
||||||
|
} = require('./helpers') |
||||||
|
const fetchMockResponses = require('./fetch-mocks.js') |
||||||
|
|
||||||
|
|
||||||
|
describe('Using MetaMask with an existing account', function () { |
||||||
|
let extensionId |
||||||
|
let driver |
||||||
|
|
||||||
|
const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress' |
||||||
|
const regularDelayMs = 1000 |
||||||
|
const largeDelayMs = regularDelayMs * 2 |
||||||
|
|
||||||
|
const button = async (x) => { |
||||||
|
const buttoncheck = x |
||||||
|
await buttoncheck.click() |
||||||
|
await delay(largeDelayMs) |
||||||
|
const [results] = await findElements(driver, By.css('#results')) |
||||||
|
const resulttext = await results.getText() |
||||||
|
var parsedData = JSON.parse(resulttext) |
||||||
|
|
||||||
|
return (parsedData) |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
this.timeout(0) |
||||||
|
this.bail(true) |
||||||
|
|
||||||
|
before(async function () { |
||||||
|
let extensionUrl |
||||||
|
switch (process.env.SELENIUM_BROWSER) { |
||||||
|
case 'chrome': { |
||||||
|
const extensionPath = path.resolve('dist/chrome') |
||||||
|
driver = buildChromeWebDriver(extensionPath) |
||||||
|
extensionId = await getExtensionIdChrome(driver) |
||||||
|
await delay(regularDelayMs) |
||||||
|
extensionUrl = `chrome-extension://${extensionId}/home.html` |
||||||
|
break |
||||||
|
} |
||||||
|
case 'firefox': { |
||||||
|
const extensionPath = path.resolve('dist/firefox') |
||||||
|
driver = buildFirefoxWebdriver() |
||||||
|
await installWebExt(driver, extensionPath) |
||||||
|
await delay(regularDelayMs) |
||||||
|
extensionId = await getExtensionIdFirefox(driver) |
||||||
|
extensionUrl = `moz-extension://${extensionId}/home.html` |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
// Depending on the state of the application built into the above directory (extPath) and the value of
|
||||||
|
// METAMASK_DEBUG we will see different post-install behaviour and possibly some extra windows. Here we
|
||||||
|
// are closing any extraneous windows to reset us to a single window before continuing.
|
||||||
|
const [tab1] = await driver.getAllWindowHandles() |
||||||
|
await closeAllWindowHandlesExcept(driver, [tab1]) |
||||||
|
await driver.switchTo().window(tab1) |
||||||
|
await driver.get(extensionUrl) |
||||||
|
}) |
||||||
|
|
||||||
|
beforeEach(async function () { |
||||||
|
await driver.executeScript( |
||||||
|
'window.origFetch = window.fetch.bind(window);' + |
||||||
|
'window.fetch = ' + |
||||||
|
'(...args) => { ' + |
||||||
|
'if (args[0] === "https://ethgasstation.info/json/ethgasAPI.json") { return ' + |
||||||
|
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' + |
||||||
|
'(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' + |
||||||
|
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } else if ' + |
||||||
|
'(args[0].match(/chromeextensionmm/)) { return ' + |
||||||
|
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.metametrics + '\')) }); } else if ' + |
||||||
|
'(args[0] === "https://dev.blockscale.net/api/gasexpress.json") { return ' + |
||||||
|
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.gasExpress + '\')) }); } ' + |
||||||
|
'return window.origFetch(...args); };' + |
||||||
|
'function cancelInfuraRequest(requestDetails) {' + |
||||||
|
'console.log("Canceling: " + requestDetails.url);' + |
||||||
|
'return {' + |
||||||
|
'cancel: true' + |
||||||
|
'};' + |
||||||
|
' }' + |
||||||
|
'window.chrome && window.chrome.webRequest && window.chrome.webRequest.onBeforeRequest.addListener(' + |
||||||
|
'cancelInfuraRequest,' + |
||||||
|
'{urls: ["https://*.infura.io/*"]},' + |
||||||
|
'["blocking"]' + |
||||||
|
');' |
||||||
|
) |
||||||
|
}) |
||||||
|
|
||||||
|
afterEach(async function () { |
||||||
|
if (process.env.SELENIUM_BROWSER === 'chrome') { |
||||||
|
const errors = await checkBrowserForConsoleErrors(driver) |
||||||
|
if (errors.length) { |
||||||
|
const errorReports = errors.map(err => err.message) |
||||||
|
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}` |
||||||
|
console.error(new Error(errorMessage)) |
||||||
|
} |
||||||
|
} |
||||||
|
if (this.currentTest.state === 'failed') { |
||||||
|
await verboseReportOnFailure(driver, this.currentTest) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
after(async function () { |
||||||
|
await driver.quit() |
||||||
|
}) |
||||||
|
|
||||||
|
describe('First time flow starting from an existing seed phrase', () => { |
||||||
|
it('clicks the continue button on the welcome screen', async () => { |
||||||
|
await findElement(driver, By.css('.welcome-page__header')) |
||||||
|
const welcomeScreenBtn = await findElement(driver, By.css('.first-time-flow__button')) |
||||||
|
welcomeScreenBtn.click() |
||||||
|
await delay(largeDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('clicks the "Import Wallet" option', async () => { |
||||||
|
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`)) |
||||||
|
customRpcButton.click() |
||||||
|
await delay(largeDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => { |
||||||
|
const optOutButton = await findElement(driver, By.css('.btn-default')) |
||||||
|
optOutButton.click() |
||||||
|
await delay(largeDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('imports a seed phrase', async () => { |
||||||
|
const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea')) |
||||||
|
await seedTextArea.sendKeys(testSeedPhrase) |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
const [password] = await findElements(driver, By.id('password')) |
||||||
|
await password.sendKeys('correct horse battery staple') |
||||||
|
const [confirmPassword] = await findElements(driver, By.id('confirm-password')) |
||||||
|
confirmPassword.sendKeys('correct horse battery staple') |
||||||
|
|
||||||
|
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox')) |
||||||
|
await tosCheckBox.click() |
||||||
|
|
||||||
|
const [importButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`)) |
||||||
|
await importButton.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('clicks through the success screen', async () => { |
||||||
|
await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`)) |
||||||
|
const doneButton = await findElement(driver, By.css('button.first-time-flow__button')) |
||||||
|
await doneButton.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
|
||||||
|
describe('opens dapp', () => { |
||||||
|
|
||||||
|
it('switches to mainnet', async () => { |
||||||
|
const networkDropdown = await findElement(driver, By.css('.network-name')) |
||||||
|
await networkDropdown.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
const [mainnet] = await findElements(driver, By.xpath(`//span[contains(text(), 'Main Ethereum Network')]`)) |
||||||
|
await mainnet.click() |
||||||
|
await delay(largeDelayMs * 2) |
||||||
|
}) |
||||||
|
|
||||||
|
it('', async () => { |
||||||
|
await openNewPage(driver, 'http://127.0.0.1:8080/') |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
await waitUntilXWindowHandles(driver, 3) |
||||||
|
const windowHandles = await driver.getAllWindowHandles() |
||||||
|
|
||||||
|
const extension = windowHandles[0] |
||||||
|
const popup = await switchToWindowWithTitle(driver, 'MetaMask Notification', windowHandles) |
||||||
|
const dapp = windowHandles.find(handle => handle !== extension && handle !== popup) |
||||||
|
|
||||||
|
await delay(regularDelayMs) |
||||||
|
const approveButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`)) |
||||||
|
await approveButton.click() |
||||||
|
|
||||||
|
await driver.switchTo().window(dapp) |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
|
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
describe('testing web3 methods', async () => { |
||||||
|
|
||||||
|
|
||||||
|
it('testing hexa methods', async () => { |
||||||
|
|
||||||
|
|
||||||
|
var List = await driver.findElements(By.className('hexaNumberMethods')) |
||||||
|
|
||||||
|
for (let i = 0; i < List.length; i++) { |
||||||
|
try { |
||||||
|
|
||||||
|
var parsedData = await button(List[i]) |
||||||
|
console.log(parsedData) |
||||||
|
var result = parseInt(parsedData.result, 16) |
||||||
|
|
||||||
|
assert.equal((typeof result === 'number'), true) |
||||||
|
await delay(regularDelayMs) |
||||||
|
} catch (err) { |
||||||
|
console.log(err) |
||||||
|
assert(false) |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
it('testing booleanMethods', async () => { |
||||||
|
|
||||||
|
var List = await driver.findElements(By.className('booleanMethods')) |
||||||
|
|
||||||
|
for (let i = 0; i < List.length; i++) { |
||||||
|
try { |
||||||
|
|
||||||
|
var parsedData = await button(List[i]) |
||||||
|
console.log(parsedData) |
||||||
|
var result = parsedData.result |
||||||
|
|
||||||
|
assert.equal(result, false) |
||||||
|
await delay(regularDelayMs) |
||||||
|
} catch (err) { |
||||||
|
console.log(err) |
||||||
|
assert(false) |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}) |
||||||
|
|
||||||
|
it('testing transactionMethods', async () => { |
||||||
|
|
||||||
|
var List = await driver.findElements(By.className('transactionMethods')) |
||||||
|
|
||||||
|
for (let i = 0; i < List.length; i++) { |
||||||
|
try { |
||||||
|
|
||||||
|
var parsedData = await button(List[i]) |
||||||
|
|
||||||
|
console.log(parsedData.result.blockHash) |
||||||
|
|
||||||
|
var result = [] |
||||||
|
result.push(parseInt(parsedData.result.blockHash, 16)) |
||||||
|
result.push(parseInt(parsedData.result.blockNumber, 16)) |
||||||
|
result.push(parseInt(parsedData.result.gas, 16)) |
||||||
|
result.push(parseInt(parsedData.result.gasPrice, 16)) |
||||||
|
result.push(parseInt(parsedData.result.hash, 16)) |
||||||
|
result.push(parseInt(parsedData.result.input, 16)) |
||||||
|
result.push(parseInt(parsedData.result.nonce, 16)) |
||||||
|
result.push(parseInt(parsedData.result.r, 16)) |
||||||
|
result.push(parseInt(parsedData.result.s, 16)) |
||||||
|
result.push(parseInt(parsedData.result.v, 16)) |
||||||
|
result.push(parseInt(parsedData.result.to, 16)) |
||||||
|
result.push(parseInt(parsedData.result.value, 16)) |
||||||
|
|
||||||
|
|
||||||
|
result.forEach((value) => { |
||||||
|
assert.equal((typeof value === 'number'), true) |
||||||
|
}) |
||||||
|
|
||||||
|
|
||||||
|
} catch (err) { |
||||||
|
|
||||||
|
console.log(err) |
||||||
|
assert(false) |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}) |
||||||
|
|
||||||
|
it('testing blockMethods', async () => { |
||||||
|
|
||||||
|
var List = await driver.findElements(By.className('blockMethods')) |
||||||
|
|
||||||
|
for (let i = 0; i < List.length; i++) { |
||||||
|
try { |
||||||
|
|
||||||
|
var parsedData = await button(List[i]) |
||||||
|
console.log(JSON.stringify(parsedData) + i) |
||||||
|
|
||||||
|
console.log(parsedData.result.parentHash) |
||||||
|
|
||||||
|
var result = parseInt(parsedData.result.parentHash, 16) |
||||||
|
|
||||||
|
assert.equal((typeof result === 'number'), true) |
||||||
|
await delay(regularDelayMs) |
||||||
|
} catch (err) { |
||||||
|
|
||||||
|
console.log(err) |
||||||
|
assert(false) |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
it('testing methods', async () => { |
||||||
|
|
||||||
|
var List = await driver.findElements(By.className('methods')) |
||||||
|
var parsedData |
||||||
|
var result |
||||||
|
|
||||||
|
for (let i = 0; i < List.length; i++) { |
||||||
|
try { |
||||||
|
|
||||||
|
if (i === 2) { |
||||||
|
|
||||||
|
parsedData = await button(List[i]) |
||||||
|
console.log(parsedData.result.blockHash) |
||||||
|
|
||||||
|
result = parseInt(parsedData.result.blockHash, 16) |
||||||
|
|
||||||
|
assert.equal((typeof result === 'number' || (result === 0)), true) |
||||||
|
await delay(regularDelayMs) |
||||||
|
} else { |
||||||
|
parsedData = await button(List[i]) |
||||||
|
console.log(parsedData.result) |
||||||
|
|
||||||
|
result = parseInt(parsedData.result, 16) |
||||||
|
|
||||||
|
assert.equal((typeof result === 'number' || (result === 0)), true) |
||||||
|
await delay(regularDelayMs) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} catch (err) { |
||||||
|
|
||||||
|
console.log(err) |
||||||
|
assert(false) |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
|
||||||
|
}) |
||||||
|
|
||||||
|
|
||||||
|
}) |
@ -1,56 +0,0 @@ |
|||||||
const assert = require('assert') |
|
||||||
const BlacklistController = require('../../../../app/scripts/controllers/blacklist') |
|
||||||
|
|
||||||
describe('blacklist controller', function () { |
|
||||||
let blacklistController |
|
||||||
|
|
||||||
before(() => { |
|
||||||
blacklistController = new BlacklistController() |
|
||||||
}) |
|
||||||
|
|
||||||
describe('whitelistDomain', function () { |
|
||||||
it('should add hostname to the runtime whitelist', function () { |
|
||||||
blacklistController.whitelistDomain('foo.com') |
|
||||||
assert.deepEqual(blacklistController.store.getState().whitelist, ['foo.com']) |
|
||||||
|
|
||||||
blacklistController.whitelistDomain('bar.com') |
|
||||||
assert.deepEqual(blacklistController.store.getState().whitelist, ['bar.com', 'foo.com']) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('checkForPhishing', function () { |
|
||||||
it('should not flag whitelisted values', function () { |
|
||||||
const result = blacklistController.checkForPhishing('www.metamask.io') |
|
||||||
assert.equal(result, false) |
|
||||||
}) |
|
||||||
it('should flag explicit values', function () { |
|
||||||
const result = blacklistController.checkForPhishing('metamask.com') |
|
||||||
assert.equal(result, true) |
|
||||||
}) |
|
||||||
it('should flag levenshtein values', function () { |
|
||||||
const result = blacklistController.checkForPhishing('metmask.io') |
|
||||||
assert.equal(result, true) |
|
||||||
}) |
|
||||||
it('should not flag not-even-close values', function () { |
|
||||||
const result = blacklistController.checkForPhishing('example.com') |
|
||||||
assert.equal(result, false) |
|
||||||
}) |
|
||||||
it('should not flag the ropsten faucet domains', function () { |
|
||||||
const result = blacklistController.checkForPhishing('faucet.metamask.io') |
|
||||||
assert.equal(result, false) |
|
||||||
}) |
|
||||||
it('should not flag the mascara domain', function () { |
|
||||||
const result = blacklistController.checkForPhishing('zero.metamask.io') |
|
||||||
assert.equal(result, false) |
|
||||||
}) |
|
||||||
it('should not flag the mascara-faucet domain', function () { |
|
||||||
const result = blacklistController.checkForPhishing('zero-faucet.metamask.io') |
|
||||||
assert.equal(result, false) |
|
||||||
}) |
|
||||||
it('should not flag whitelisted domain', function () { |
|
||||||
blacklistController.whitelistDomain('metamask.com') |
|
||||||
const result = blacklistController.checkForPhishing('metamask.com') |
|
||||||
assert.equal(result, false) |
|
||||||
}) |
|
||||||
}) |
|
||||||
}) |
|
@ -1,238 +0,0 @@ |
|||||||
const assert = require('assert') |
|
||||||
const NonceTracker = require('../../../../../app/scripts/controllers/transactions/nonce-tracker') |
|
||||||
const MockTxGen = require('../../../../lib/mock-tx-gen') |
|
||||||
const providerResultStub = {} |
|
||||||
|
|
||||||
describe('Nonce Tracker', function () { |
|
||||||
let nonceTracker, pendingTxs, confirmedTxs |
|
||||||
|
|
||||||
describe('#getNonceLock', function () { |
|
||||||
|
|
||||||
describe('with 3 confirmed and 1 pending', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 }) |
|
||||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 1 }) |
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x1') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return 4', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '4', `nonce should be 4 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
|
|
||||||
it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4') |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('sentry issue 476304902', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
pendingTxs = txGen.generate({ status: 'submitted' }, { |
|
||||||
fromNonce: 3, |
|
||||||
count: 29, |
|
||||||
}) |
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x3') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return 9', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '32', `nonce should be 32 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('issue 3670', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
pendingTxs = txGen.generate({ status: 'submitted' }, { |
|
||||||
fromNonce: 6, |
|
||||||
count: 3, |
|
||||||
}) |
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x6') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return 9', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '9', `nonce should be 9 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('with no previous txs', function () { |
|
||||||
beforeEach(function () { |
|
||||||
nonceTracker = generateNonceTrackerWith([], []) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return 0', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '0', `nonce should be 0 returned ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('with multiple previous txs with same nonce', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 1 }) |
|
||||||
pendingTxs = txGen.generate({ |
|
||||||
status: 'submitted', |
|
||||||
txParams: { nonce: '0x01' }, |
|
||||||
}, { count: 5 }) |
|
||||||
|
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x0') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return nonce after those', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('when local confirmed count is higher than network nonce', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 }) |
|
||||||
nonceTracker = generateNonceTrackerWith([], confirmedTxs, '0x1') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return nonce after those', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '3', `nonce should be 3 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('when local pending count is higher than other metrics', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 }) |
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, []) |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return nonce after those', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('when provider nonce is higher than other metrics', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 }) |
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x05') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return nonce after those', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('when there are some pending nonces below the remote one and some over.', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 5 }) |
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x03') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return nonce after those', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('when there are pending nonces non sequentially over the network nonce.', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
txGen.generate({ status: 'submitted' }, { count: 5 }) |
|
||||||
// 5 over that number
|
|
||||||
pendingTxs = txGen.generate({ status: 'submitted' }, { count: 5 }) |
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x00') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return nonce after network nonce', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '0', `nonce should be 0 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('When all three return different values', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 10 }) |
|
||||||
pendingTxs = txGen.generate({ |
|
||||||
status: 'submitted', |
|
||||||
nonce: 100, |
|
||||||
}, { count: 1 }) |
|
||||||
// 0x32 is 50 in hex:
|
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x32') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return nonce after network nonce', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '50', `nonce should be 50 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('Faq issue 67', function () { |
|
||||||
beforeEach(function () { |
|
||||||
const txGen = new MockTxGen() |
|
||||||
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 64 }) |
|
||||||
pendingTxs = txGen.generate({ |
|
||||||
status: 'submitted', |
|
||||||
}, { count: 10 }) |
|
||||||
// 0x40 is 64 in hex:
|
|
||||||
nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x40') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should return nonce after network nonce', async function () { |
|
||||||
this.timeout(15000) |
|
||||||
const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') |
|
||||||
assert.equal(nonceLock.nextNonce, '74', `nonce should be 74 got ${nonceLock.nextNonce}`) |
|
||||||
await nonceLock.releaseLock() |
|
||||||
}) |
|
||||||
}) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') { |
|
||||||
const getPendingTransactions = () => pending |
|
||||||
const getConfirmedTransactions = () => confirmed |
|
||||||
providerResultStub.result = providerStub |
|
||||||
const provider = { |
|
||||||
sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, |
|
||||||
} |
|
||||||
const blockTracker = { |
|
||||||
getCurrentBlock: () => '0x11b568', |
|
||||||
getLatestBlock: async () => '0x11b568', |
|
||||||
} |
|
||||||
return new NonceTracker({ |
|
||||||
provider, |
|
||||||
blockTracker, |
|
||||||
getPendingTransactions, |
|
||||||
getConfirmedTransactions, |
|
||||||
}) |
|
||||||
} |
|
@ -0,0 +1,105 @@ |
|||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Web3 Test Dapp</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div style="display: flex; flex-flow: column;"> |
||||||
|
<div style="display: flex; font-size: 1.25rem;">hexaNumberMethods</div> |
||||||
|
<div style="display: flex;"> |
||||||
|
<button id="eth_blockNumber" class="hexaNumberMethods">eth_blockNumber</button> |
||||||
|
|
||||||
|
<button id="eth_gasPrice" class="hexaNumberMethods">eth_gasPrice</button> |
||||||
|
<button id="eth_newBlockFilter" class="hexaNumberMethods">eth_newBlockFilter</button> |
||||||
|
<button id="eth_newPendingTransactionFilter" class="hexaNumberMethods"> |
||||||
|
eth_newPendingTransactionFilter |
||||||
|
</button> |
||||||
|
<button id="eth_getUncleCountByBlockHash" class="hexaNumberMethods"> |
||||||
|
eth_getUncleCountByBlockHash |
||||||
|
</button> |
||||||
|
<button id="eth_getBlockTransactionCountByHash" class="hexaNumberMethods"> |
||||||
|
getBlockTransactionCountByHash |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
<div style="display: flex ;"> |
||||||
|
<button id="eth_getTransactionCount" class="hexaNumberMethods">eth_getTransactionCount</button> |
||||||
|
<button id="eth_getBalance" class="hexaNumberMethods">eth_getBalance</button> |
||||||
|
<button id="eth_estimateGas" class="hexaNumberMethods">eth_estimateGas</button> |
||||||
|
</div> |
||||||
|
<div style="display: flex ;"> |
||||||
|
|
||||||
|
<button id="eth_getUncleCountByBlockNumber" class="hexaNumberMethods"> |
||||||
|
eth_getUncleCountByBlockNumber |
||||||
|
</button> |
||||||
|
<button id='eth_getBlockTransactionCountByNumber' class="hexaNumberMethods"> |
||||||
|
eth_getBlockTransactionCountByNumber |
||||||
|
</button> |
||||||
|
<button id="eth_protocolVersion" class="hexaNumberMethods">eth_protocolVersion</button> |
||||||
|
|
||||||
|
<button id="eth_getCode" class="hexaNumberMethods">eth_getCode</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div style="display: flex; flex-flow: column;"> |
||||||
|
<div style="display: flex; font-size: 1.25rem;">booleanMethods</div> |
||||||
|
<div style="display: flex ;"> |
||||||
|
<button id="eth_uninstallFilter" class = 'booleanMethods'>eth_uninstallFilter</button> |
||||||
|
<button id="eth_mining" class = 'booleanMethods'>eth_mining</button> |
||||||
|
<button id="eth_syncing" class = 'booleanMethods'>eth_syncing</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div style="display: flex; flex-flow: column;"> |
||||||
|
<div style="display: flex; font-size: 1.25rem;" >transactionMethods</div> |
||||||
|
<div style="display: flex ;"> |
||||||
|
<button id="eth_getTransactionByHash" class='transactionMethods'>eth_getTransactionByHash</button> |
||||||
|
<button id="eth_getTransactionByBlockHashAndIndex" class = 'transactionMethods'> |
||||||
|
eth_getTransactionByBlockHashAndIndex |
||||||
|
</button> |
||||||
|
<button id="eth_getTransactionByBlockNumberAndIndex" class="transactionMethods"> |
||||||
|
eth_getTransactionByBlockNumberAndIndex |
||||||
|
</button> |
||||||
|
|
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div style="display: flex; flex-flow: column;"> |
||||||
|
<div style="display: flex; font-size: 1.25rem;">blockMethods</div> |
||||||
|
|
||||||
|
<div style="display: flex ;"> |
||||||
|
|
||||||
|
|
||||||
|
<button id="eth_getUncleByBlockHashAndIndex" class="blockMethods"> |
||||||
|
eth_getUncleByBlockHashAndIndex |
||||||
|
</button> |
||||||
|
<button id="eth_getBlockByHash" class="blockMethods">eth_getBlockByHash</button> |
||||||
|
</div> |
||||||
|
<div style="display: flex ;"> |
||||||
|
<button id="eth_getBlockByNumber" class="blockMethods">eth_getBlockByNumber</button> |
||||||
|
|
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div style="display: flex; flex-flow: column;"> |
||||||
|
<div style="display: flex; font-size: 1.25rem;">Methods</div> |
||||||
|
<div style="display: flex ;"> |
||||||
|
<button id="eth_call" class = 'methods'>eth_call</button> |
||||||
|
<button id="eth_getStorageAt" class="methods">eth_getStorageAt</button> |
||||||
|
<button id="eth_getTransactionReceipt" class="methods"> |
||||||
|
eth_getTransactionReceipt |
||||||
|
</button> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div style="display: flex; flex-flow: column;"> |
||||||
|
<div id='results'></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div> |
||||||
|
<script src="schema.js"></script> |
||||||
|
<script src="web3.js"></script> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,209 @@ |
|||||||
|
/* eslint no-unused-vars: 0 */ |
||||||
|
|
||||||
|
var params = { |
||||||
|
// diffrent params used in the methods
|
||||||
|
param: [], |
||||||
|
blockHashParams: '0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35', |
||||||
|
filterParams: ['0xfe704947a3cd3ca12541458a4321c869'], |
||||||
|
transactionHashParams: [ |
||||||
|
'0xbb3a336e3f823ec18197f1e13ee875700f08f03e2cab75f0d0b118dabb44cba0', |
||||||
|
], |
||||||
|
blockHashAndIndexParams: [ |
||||||
|
'0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35', |
||||||
|
'0x0', |
||||||
|
], |
||||||
|
uncleByBlockNumberAndIndexParams: ['0x29c', '0x0'], |
||||||
|
blockParameterParams: '0x5bad55', |
||||||
|
data: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675', |
||||||
|
addressParams: '0xc94770007dda54cF92009BFF0dE90c06F603a09f', |
||||||
|
getStorageAtParams: [ |
||||||
|
'0x295a70b2de5e3953354a6a8344e616ed314d7251', |
||||||
|
'0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9', |
||||||
|
'0x65a8db', |
||||||
|
], |
||||||
|
getCodeParams: ['0x06012c8cf97bead5deae237070f9587f8e7a266d', '0x65a8db'], |
||||||
|
estimateTransaction: { |
||||||
|
from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155', |
||||||
|
to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567', |
||||||
|
gas: '0x76c0', |
||||||
|
gasPrice: '0x9184e72a000', |
||||||
|
value: '0x9184e72a', |
||||||
|
data: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675', |
||||||
|
}, |
||||||
|
filterGetLogs: [{'blockHash': '0x7c5a35e9cb3e8ae0e221ab470abae9d446c3a5626ce6689fc777dcffcab52c70', 'topics': ['0x241ea03ca20251805084d27d4440371c34a0b85ff108f6bb5611248f73818b80']}], |
||||||
|
block: { |
||||||
|
__required: [], |
||||||
|
number: 'Q', |
||||||
|
hash: 'D32', |
||||||
|
parentHash: 'D32', |
||||||
|
nonce: 'D', |
||||||
|
sha3Uncles: 'D', |
||||||
|
logsBloom: 'D', |
||||||
|
transactionsRoot: 'D', |
||||||
|
stateRoot: 'D', |
||||||
|
receiptsRoot: 'D', |
||||||
|
miner: 'D', |
||||||
|
difficulty: 'Q', |
||||||
|
totalDifficulty: 'Q', |
||||||
|
extraData: 'D', |
||||||
|
size: 'Q', |
||||||
|
gasLimit: 'Q', |
||||||
|
gasUsed: 'Q', |
||||||
|
timestamp: 'Q', |
||||||
|
transactions: ['DATA|Transaction'], |
||||||
|
uncles: ['D'], |
||||||
|
}, |
||||||
|
transaction: { |
||||||
|
__required: [], |
||||||
|
hash: 'D32', |
||||||
|
nonce: 'Q', |
||||||
|
blockHash: 'D32', |
||||||
|
blockNumber: 'Q', |
||||||
|
transactionIndex: 'Q', |
||||||
|
from: 'D20', |
||||||
|
to: 'D20', |
||||||
|
value: 'Q', |
||||||
|
gasPrice: 'Q', |
||||||
|
gas: 'Q', |
||||||
|
input: 'D', |
||||||
|
}, |
||||||
|
receipt: { |
||||||
|
__required: [], |
||||||
|
transactionHash: 'D32', |
||||||
|
transactionIndex: 'Q', |
||||||
|
blockHash: 'D32', |
||||||
|
blockNumber: 'Q', |
||||||
|
cumulativeGasUsed: 'Q', |
||||||
|
gasUsed: 'Q', |
||||||
|
contractAddress: 'D20', |
||||||
|
logs: ['FilterChange'], |
||||||
|
}, |
||||||
|
|
||||||
|
filterChange: { |
||||||
|
__required: [], |
||||||
|
removed: 'B', |
||||||
|
logIndex: 'Q', |
||||||
|
transactionIndex: 'Q', |
||||||
|
transactionHash: 'D32', |
||||||
|
blockHash: 'D32', |
||||||
|
blockNumber: 'Q', |
||||||
|
address: 'D20', |
||||||
|
data: 'Array|DATA', |
||||||
|
topics: ['D'], |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
var methods = { |
||||||
|
hexaNumberMethods: { |
||||||
|
// these are the methods which have output in the form of hexa decimal numbers
|
||||||
|
eth_blockNumber: ['eth_blockNumber', params.param, 'Q'], |
||||||
|
eth_gasPrice: ['eth_gasPrice', params.param, 'Q'], |
||||||
|
eth_newBlockFilter: ['eth_newBlockFilter', params.param, 'Q'], |
||||||
|
eth_newPendingTransactionFilter: [ |
||||||
|
'eth_newPendingTransactionFilter', |
||||||
|
params.param, |
||||||
|
'Q', |
||||||
|
], |
||||||
|
eth_getUncleCountByBlockHash: [ |
||||||
|
'eth_getUncleCountByBlockHash', |
||||||
|
[params.blockHashParams], |
||||||
|
'Q', |
||||||
|
1, |
||||||
|
], |
||||||
|
eth_getBlockTransactionCountByHash: [ |
||||||
|
'eth_getBlockTransactionCountByHash', |
||||||
|
[params.blockHashParams], |
||||||
|
'Q', |
||||||
|
1, |
||||||
|
], |
||||||
|
eth_getTransactionCount: [ |
||||||
|
'eth_getTransactionCount', |
||||||
|
[params.addressParams, params.blockParameterParams], |
||||||
|
'Q', |
||||||
|
1, |
||||||
|
2, |
||||||
|
], |
||||||
|
eth_getBalance: ['eth_getBalance', [params.addressParams, 'latest'], 'Q', 1, 2], |
||||||
|
eth_estimateGas: ['eth_estimateGas', [params.estimateTransaction], 'Q', 1], |
||||||
|
eth_getUncleCountByBlockNumber: [ |
||||||
|
'eth_getUncleCountByBlockNumber', |
||||||
|
[params.blockParameterParams], |
||||||
|
'Q', |
||||||
|
1, |
||||||
|
], |
||||||
|
eth_getBlockTransactionCountByNumber: [ |
||||||
|
'eth_getBlockTransactionCountByNumber', |
||||||
|
['latest'], |
||||||
|
'Q', |
||||||
|
1, |
||||||
|
], |
||||||
|
eth_protocolVersion: ['eth_protocolVersion', params.param, 'S'], |
||||||
|
eth_getCode: ['eth_getCode', params.getCodeParams, 'D', 1, 2], |
||||||
|
}, |
||||||
|
booleanMethods: { |
||||||
|
// these are the methods which have output in the form of boolean
|
||||||
|
eth_uninstallFilter: ['eth_uninstallFilter', params.filterParams, 'B', 1], |
||||||
|
eth_mining: ['eth_mining', params.param, 'B'], |
||||||
|
eth_syncing: ['eth_syncing', params.param, 'B|EthSyncing'], |
||||||
|
}, |
||||||
|
transactionMethods: { |
||||||
|
// these are the methods which have output in the form of transaction object
|
||||||
|
eth_getTransactionByHash: [ |
||||||
|
'eth_getTransactionByHash', |
||||||
|
params.transactionHashParams, |
||||||
|
params.transaction, |
||||||
|
1, |
||||||
|
], |
||||||
|
eth_getTransactionByBlockHashAndIndex: [ |
||||||
|
'eth_getTransactionByBlockHashAndIndex', |
||||||
|
params.blockHashAndIndexParams, |
||||||
|
params.transaction, |
||||||
|
2, |
||||||
|
], |
||||||
|
eth_getTransactionByBlockNumberAndIndex: [ |
||||||
|
'eth_getTransactionByBlockNumberAndIndex', |
||||||
|
[params.blockParameterParams, '0x0'], |
||||||
|
params.transaction, |
||||||
|
2, |
||||||
|
], |
||||||
|
|
||||||
|
}, |
||||||
|
blockMethods: { |
||||||
|
// these are the methods which have output in the form of a block
|
||||||
|
|
||||||
|
eth_getUncleByBlockNumberAndIndex: [ |
||||||
|
'eth_getUncleByBlockNumberAndIndex', |
||||||
|
params.uncleByBlockNumberAndIndexParams, |
||||||
|
params.block, |
||||||
|
2, |
||||||
|
], |
||||||
|
eth_getBlockByHash: [ |
||||||
|
'eth_getBlockByHash', |
||||||
|
[params.params, false], |
||||||
|
params.block, |
||||||
|
2, |
||||||
|
], |
||||||
|
eth_getBlockByNumber: [ |
||||||
|
'eth_getBlockByNumber', |
||||||
|
[params.blockParameterParams, false], |
||||||
|
params.block, |
||||||
|
2, |
||||||
|
], |
||||||
|
}, |
||||||
|
|
||||||
|
methods: { |
||||||
|
// these are the methods which have output in the form of bytes data
|
||||||
|
|
||||||
|
eth_call: ['eth_call', [params.estimateTransaction, 'latest'], 'D', 1, 2], |
||||||
|
eth_getStorageAt: ['eth_getStorageAt', params.getStorageAtParams, 'D', 2, 2], |
||||||
|
eth_getTransactionReceipt: [ |
||||||
|
'eth_getTransactionReceipt', |
||||||
|
params.transactionHashParams, |
||||||
|
params.receipt, |
||||||
|
1, |
||||||
|
], |
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,34 @@ |
|||||||
|
/* eslint no-undef: 0 */ |
||||||
|
|
||||||
|
var json = methods |
||||||
|
|
||||||
|
web3.currentProvider.enable().then(() => { |
||||||
|
|
||||||
|
Object.keys(json).forEach(methodGroupKey => { |
||||||
|
|
||||||
|
console.log(methodGroupKey) |
||||||
|
const methodGroup = json[methodGroupKey] |
||||||
|
console.log(methodGroup) |
||||||
|
Object.keys(methodGroup).forEach(methodKey => { |
||||||
|
|
||||||
|
const methodButton = document.getElementById(methodKey) |
||||||
|
methodButton.addEventListener('click', function () { |
||||||
|
|
||||||
|
window.ethereum.sendAsync({ |
||||||
|
method: methodKey, |
||||||
|
params: methodGroup[methodKey][1], |
||||||
|
}, function (err, result) { |
||||||
|
if (err) { |
||||||
|
console.log(err) |
||||||
|
console.log(methodKey) |
||||||
|
} else { |
||||||
|
document.getElementById('results').innerHTML = JSON.stringify(result) |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
}) |
||||||
|
|
||||||
|
}) |
||||||
|
}) |
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue