diff options
Diffstat (limited to 'test')
49 files changed, 1194 insertions, 1188 deletions
diff --git a/test/data/mock-state.json b/test/data/mock-state.json index 8deff5531..671697182 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -11,6 +11,7 @@ "name": "Test Account 2" } }, + "cachedBalances": {}, "unapprovedTxs": { "8393540981007587": { "id": 8393540981007587, diff --git a/test/e2e/beta/drizzle.spec.js b/test/e2e/beta/drizzle.spec.js index a9d72a9ba..309df952c 100644 --- a/test/e2e/beta/drizzle.spec.js +++ b/test/e2e/beta/drizzle.spec.js @@ -81,22 +81,6 @@ describe('MetaMask', function () { }) it('selects the new UI option', async () => { - try { - const overlay = await findElement(driver, By.css('.full-flex-height')) - await driver.wait(until.stalenessOf(overlay)) - } catch (e) {} - - let button - try { - button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) - } catch (e) { - await loadExtension(driver, extensionId) - await delay(largeDelayMs) - button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) - } - await button.click() - await delay(regularDelayMs) - // Close all other tabs const [tab0, tab1, tab2] = await driver.getAllWindowHandles() await driver.switchTo().window(tab0) @@ -128,7 +112,7 @@ describe('MetaMask', function () { await loadExtension(driver, extensionId) await delay(regularDelayMs) - const continueBtn = await findElement(driver, By.css('.welcome-screen__button')) + const continueBtn = await findElement(driver, By.css('.first-time-flow__button')) await continueBtn.click() await delay(regularDelayMs) }) @@ -136,9 +120,9 @@ describe('MetaMask', function () { describe('Going through the first time flow', () => { it('accepts a secure password', async () => { - const passwordBox = await findElement(driver, By.css('.create-password #create-password')) - const passwordBoxConfirm = await findElement(driver, By.css('.create-password #confirm-password')) - const button = await findElement(driver, By.css('.create-password button')) + const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password')) + const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password')) + const button = await findElement(driver, By.css('.first-time-flow__form button')) await passwordBox.sendKeys('correct horse battery staple') await passwordBoxConfirm.sendKeys('correct horse battery staple') @@ -147,19 +131,21 @@ describe('MetaMask', function () { }) it('clicks through the unique image screen', async () => { - const nextScreen = await findElement(driver, By.css('.unique-image button')) + await findElement(driver, By.css('.first-time-flow__unique-image')) + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) await nextScreen.click() await delay(regularDelayMs) }) it('clicks through the ToS', async () => { // terms of use - const canClickThrough = await driver.findElement(By.css('.tou button')).isEnabled() + await findElement(driver, By.css('.first-time-flow__markdown')) + const canClickThrough = await driver.findElement(By.css('button.first-time-flow__button')).isEnabled() assert.equal(canClickThrough, false, 'disabled continue button') const bottomOfTos = await findElement(driver, By.linkText('Attributions')) await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos) await delay(regularDelayMs) - const acceptTos = await findElement(driver, By.css('.tou button')) + const acceptTos = await findElement(driver, By.css('button.first-time-flow__button')) driver.wait(until.elementIsEnabled(acceptTos)) await acceptTos.click() await delay(regularDelayMs) @@ -167,17 +153,17 @@ describe('MetaMask', function () { it('clicks through the privacy notice', async () => { // privacy notice - const nextScreen = await findElement(driver, By.css('.tou button')) + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) await nextScreen.click() await delay(regularDelayMs) }) it('clicks through the phishing notice', async () => { // phishing notice - const noticeElement = await driver.findElement(By.css('.markdown')) + const noticeElement = await driver.findElement(By.css('.first-time-flow__markdown')) await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement) await delay(regularDelayMs) - const nextScreen = await findElement(driver, By.css('.tou button')) + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) await nextScreen.click() await delay(regularDelayMs) }) @@ -185,24 +171,23 @@ describe('MetaMask', function () { let seedPhrase it('reveals the seed phrase', async () => { - const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button') + const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') await driver.wait(until.elementLocated(byRevealButton, 10000)) const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) await revealSeedPhraseButton.click() await delay(regularDelayMs) - seedPhrase = await driver.findElement(By.css('.backup-phrase__secret-words')).getText() + seedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words')).getText() assert.equal(seedPhrase.split(' ').length, 12) await delay(regularDelayMs) - const nextScreen = await findElement(driver, By.css('.backup-phrase button')) + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) await nextScreen.click() await delay(regularDelayMs) }) async function clickWordAndWait (word) { - const xpathClass = 'backup-phrase__confirm-seed-option backup-phrase__confirm-seed-option--unselected' - const xpath = `//button[@class='${xpathClass}' and contains(text(), '${word}')]` + const xpath = `//div[contains(@class, 'confirm-seed-phrase__seed-word--shuffled') and not(contains(@class, 'confirm-seed-phrase__seed-word--selected')) and contains(text(), '${word}')]` const word0 = await findElement(driver, By.xpath(xpath), 10000) await word0.click() @@ -212,13 +197,13 @@ describe('MetaMask', function () { async function retypeSeedPhrase (words, wasReloaded, count = 0) { try { if (wasReloaded) { - const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button') + const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') await driver.wait(until.elementLocated(byRevealButton, 10000)) const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) await revealSeedPhraseButton.click() await delay(regularDelayMs) - const nextScreen = await findElement(driver, By.css('.backup-phrase button')) + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) await nextScreen.click() await delay(regularDelayMs) } diff --git a/test/e2e/beta/fetch-mocks.js b/test/e2e/beta/fetch-mocks.js new file mode 100644 index 000000000..6b885cc10 --- /dev/null +++ b/test/e2e/beta/fetch-mocks.js @@ -0,0 +1,6 @@ +module.exports = { + ethGasBasic: JSON.stringify({'average': 85.0, 'fastestWait': 0.6, 'fastWait': 0.6, 'fast': 200.0, 'safeLowWait': 4.8, 'blockNum': 6648312, 'avgWait': 4.2, 'block_time': 15.516129032258064, 'speed': 0.7828720873342716, 'fastest': 400.0, 'safeLow': 80.0}), + ethGasPredictTable: JSON.stringify([{'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 7.2632423756, 'pct_remaining5m': 0.0, 'sum': 7.029975, 'tx_atabove': 4136.0, 'hashpower_accepting': 10.4166666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 1.2, 'pct_mined_5m': 0.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 7.433788122, 'pct_remaining5m': 0.0, 'sum': 7.01731875, 'tx_atabove': 4136.0, 'hashpower_accepting': 10.9375, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 1.5, 'pct_mined_5m': 0.0, 'total_seen_5m': 84.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 7.433788122, 'pct_remaining5m': 0.0, 'sum': 7.01731875, 'tx_atabove': 4136.0, 'hashpower_accepting': 10.9375, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 1.7, 'pct_mined_5m': 0.0, 'total_seen_5m': 5.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 7.4638844302, 'pct_remaining5m': 0.0, 'sum': 7.01731875, 'tx_atabove': 4136.0, 'hashpower_accepting': 10.9375, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 1.8, 'pct_mined_5m': 0.0, 'total_seen_5m': 20.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 7.4839486356, 'pct_remaining5m': 0.0, 'sum': 7.01731875, 'tx_atabove': 4136.0, 'hashpower_accepting': 10.9375, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 1.9, 'pct_mined_5m': 0.0, 'total_seen_5m': 8.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 7.7347512039, 'pct_remaining5m': 0.0, 'sum': 7.0046625, 'tx_atabove': 4136.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 2.0, 'pct_mined_5m': 0.0, 'total_seen_5m': 52.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 17.0, 'hashpower_accepting2': 7.7447833066, 'pct_remaining5m': 1.0, 'sum': 7.0046625, 'tx_atabove': 4136.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 2.1, 'pct_mined_5m': 0.0, 'total_seen_5m': 97.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 20.0, 'hashpower_accepting2': 7.7447833066, 'pct_remaining5m': 1.0, 'sum': 7.0040625, 'tx_atabove': 4135.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 6.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 2.2, 'pct_mined_5m': 0.0, 'total_seen_5m': 433.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 68.0, 'hashpower_accepting2': 7.7447833066, 'pct_remaining5m': 0.0, 'sum': 6.9986625, 'tx_atabove': 4126.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 50.0, 'gasprice': 2.3, 'pct_mined_5m': 0.0, 'total_seen_5m': 14.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 7.7447833066, 'pct_remaining5m': 0.0, 'sum': 6.9980625, 'tx_atabove': 4125.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 2.4, 'pct_mined_5m': 0.0, 'total_seen_5m': 4.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 20.0, 'hashpower_accepting2': 7.7447833066, 'pct_remaining5m': 37.0, 'sum': 6.9956625, 'tx_atabove': 4121.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 4.0, 'int2': 6.9238, 'pct_remaining30m': 75.0, 'gasprice': 2.5, 'pct_mined_5m': 0.0, 'total_seen_5m': 45.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 79.0, 'hashpower_accepting2': 7.7447833066, 'pct_remaining5m': 0.0, 'sum': 6.9788625, 'tx_atabove': 4093.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 2.6, 'pct_mined_5m': 0.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 27.5, 'hashpower_accepting2': 7.7447833066, 'pct_remaining5m': 100.0, 'sum': 6.9764625, 'tx_atabove': 4089.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 2.7, 'pct_mined_5m': 0.0, 'total_seen_5m': 3.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 22.5, 'hashpower_accepting2': 7.7447833066, 'pct_remaining5m': 66.0, 'sum': 6.9740625, 'tx_atabove': 4085.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 2.8, 'pct_mined_5m': 0.0, 'total_seen_5m': 6.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 20.0, 'hashpower_accepting2': 7.7548154093, 'pct_remaining5m': 38.0, 'sum': 6.9686625, 'tx_atabove': 4076.0, 'hashpower_accepting': 11.4583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 2.9, 'pct_mined_5m': 2.0, 'total_seen_5m': 36.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 27.0, 'hashpower_accepting2': 11.5268860353, 'pct_remaining5m': 77.0, 'sum': 6.8307, 'tx_atabove': 4057.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': 127.0, 'int2': 6.9238, 'pct_remaining30m': 48.0, 'gasprice': 3.0, 'pct_mined_5m': 0.0, 'total_seen_5m': 322.0, 'pct_mined_30m': 39.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 67.0, 'hashpower_accepting2': 11.5268860353, 'pct_remaining5m': 100.0, 'sum': 6.5697, 'tx_atabove': 3622.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': 85.0, 'int2': 6.9238, 'pct_remaining30m': 98.0, 'gasprice': 3.1, 'pct_mined_5m': 0.0, 'total_seen_5m': 79.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 71.0, 'hashpower_accepting2': 11.5268860353, 'pct_remaining5m': 100.0, 'sum': 6.4311, 'tx_atabove': 3391.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': 7.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 3.2, 'pct_mined_5m': 0.0, 'total_seen_5m': 4.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 62.0, 'hashpower_accepting2': 11.5268860353, 'pct_remaining5m': 100.0, 'sum': 6.4209, 'tx_atabove': 3374.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': 14.0, 'int2': 6.9238, 'pct_remaining30m': 92.0, 'gasprice': 3.3, 'pct_mined_5m': 0.0, 'total_seen_5m': 7.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1472.0, 'hashpower_accepting2': 11.5569823435, 'pct_remaining5m': 100.0, 'sum': 6.3951, 'tx_atabove': 3331.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': 29.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 3.4, 'pct_mined_5m': 0.0, 'total_seen_5m': 27.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 307.0, 'hashpower_accepting2': 11.5670144462, 'pct_remaining5m': 100.0, 'sum': 6.1521, 'tx_atabove': 2926.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 3.7, 'pct_mined_5m': 0.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1399.0, 'hashpower_accepting2': 11.577046549, 'pct_remaining5m': 100.0, 'sum': 6.1395, 'tx_atabove': 2905.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 3.9, 'pct_mined_5m': 0.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1005.0, 'hashpower_accepting2': 11.5971107544, 'pct_remaining5m': 88.0, 'sum': 6.1035, 'tx_atabove': 2845.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': 9.0, 'int2': 6.9238, 'pct_remaining30m': 88.0, 'gasprice': 4.0, 'pct_mined_5m': 0.0, 'total_seen_5m': 9.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1546.0, 'hashpower_accepting2': 11.6171749599, 'pct_remaining5m': null, 'sum': 5.6151, 'tx_atabove': 2031.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 4.1, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1065.0, 'hashpower_accepting2': 11.6171749599, 'pct_remaining5m': 100.0, 'sum': 5.5509, 'tx_atabove': 1924.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 4.3, 'pct_mined_5m': 0.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 459.0, 'hashpower_accepting2': 11.6171749599, 'pct_remaining5m': 50.0, 'sum': 5.5137, 'tx_atabove': 1862.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 4.4, 'pct_mined_5m': 0.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 298.0, 'hashpower_accepting2': 11.6171749599, 'pct_remaining5m': null, 'sum': 5.4903, 'tx_atabove': 1823.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 4.7, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 812.0, 'hashpower_accepting2': 11.6472712681, 'pct_remaining5m': 0.0, 'sum': 5.4831, 'tx_atabove': 1811.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 4.8, 'pct_mined_5m': 0.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 541.0, 'hashpower_accepting2': 11.6472712681, 'pct_remaining5m': 100.0, 'sum': 5.4375, 'tx_atabove': 1735.0, 'hashpower_accepting': 16.6666666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 4.9, 'pct_mined_5m': 0.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1134.0, 'hashpower_accepting2': 11.7375601926, 'pct_remaining5m': 100.0, 'sum': 5.41824375, 'tx_atabove': 1724.0, 'hashpower_accepting': 17.1875, 'hpa_coef2': -0.067, 'total_seen_30m': 6.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 5.0, 'pct_mined_5m': 0.0, 'total_seen_5m': 5.0, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1958.0, 'hashpower_accepting2': 11.7676565008, 'pct_remaining5m': null, 'sum': 4.9567875, 'tx_atabove': 976.0, 'hashpower_accepting': 17.7083333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 5.2, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1203.5, 'hashpower_accepting2': 11.8077849117, 'pct_remaining5m': null, 'sum': 4.9507875, 'tx_atabove': 966.0, 'hashpower_accepting': 17.7083333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 5.3, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 677.5, 'hashpower_accepting2': 11.8378812199, 'pct_remaining5m': null, 'sum': 4.9141875, 'tx_atabove': 905.0, 'hashpower_accepting': 17.7083333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 100.0, 'gasprice': 5.5, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 250.99, 'avgdiff': 0, 'expectedWait': 1000.0, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 3.0, 'hashpower_accepting2': 13.3928571429, 'pct_remaining5m': 0.0, 'sum': 3.16120625, 'tx_atabove': 832.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': 9.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 6.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 12.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.92, 'avgdiff': 1, 'expectedWait': 23.5990451154, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 14.0248796148, 'pct_remaining5m': 0.0, 'sum': 3.10120625, 'tx_atabove': 732.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 6.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.58, 'avgdiff': 1, 'expectedWait': 22.2247437161, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 14.1753611557, 'pct_remaining5m': 0.0, 'sum': 3.09640625, 'tx_atabove': 724.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 6.2, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.55, 'avgdiff': 1, 'expectedWait': 22.1183205662, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 14.3459069021, 'pct_remaining5m': 0.0, 'sum': 3.09580625, 'tx_atabove': 723.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 6.3, 'pct_mined_5m': 100.0, 'total_seen_5m': 4.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.55, 'avgdiff': 1, 'expectedWait': 22.1050535543, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 14.3960674157, 'pct_remaining5m': 0.0, 'sum': 3.09460625, 'tx_atabove': 721.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 6.4, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.54, 'avgdiff': 1, 'expectedWait': 22.0785433993, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 14.5465489567, 'pct_remaining5m': 0.0, 'sum': 3.09460625, 'tx_atabove': 721.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 6.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.54, 'avgdiff': 1, 'expectedWait': 22.0785433993, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 14.5666131621, 'pct_remaining5m': null, 'sum': 3.09460625, 'tx_atabove': 721.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 6.6, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.54, 'avgdiff': 1, 'expectedWait': 22.0785433993, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 2.0, 'hashpower_accepting2': 14.6769662921, 'pct_remaining5m': null, 'sum': 3.09460625, 'tx_atabove': 721.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 6.7, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.54, 'avgdiff': 1, 'expectedWait': 22.0785433993, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 14.7070626003, 'pct_remaining5m': null, 'sum': 3.09400625, 'tx_atabove': 720.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 6.8, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.54, 'avgdiff': 1, 'expectedWait': 22.0653002466, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 14.7271268058, 'pct_remaining5m': 0.0, 'sum': 3.09400625, 'tx_atabove': 720.0, 'hashpower_accepting': 20.3125, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 6.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.54, 'avgdiff': 1, 'expectedWait': 22.0653002466, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 3.0, 'hashpower_accepting2': 15.4795345104, 'pct_remaining5m': 0.0, 'sum': 3.06749375, 'tx_atabove': 718.0, 'hashpower_accepting': 21.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 11.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 7.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 6.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.39, 'avgdiff': 1, 'expectedWait': 21.4879808804, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 15.5898876404, 'pct_remaining5m': 0.0, 'sum': 3.06089375, 'tx_atabove': 707.0, 'hashpower_accepting': 21.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 4.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 7.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.36, 'avgdiff': 1, 'expectedWait': 21.3466271869, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 15.5999197432, 'pct_remaining5m': null, 'sum': 3.06029375, 'tx_atabove': 706.0, 'hashpower_accepting': 21.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 7.2, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.35, 'avgdiff': 1, 'expectedWait': 21.3338230522, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 15.8507223114, 'pct_remaining5m': 0.0, 'sum': 3.05969375, 'tx_atabove': 705.0, 'hashpower_accepting': 21.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 5.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 7.6, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.35, 'avgdiff': 1, 'expectedWait': 21.3210265977, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 15.8607544141, 'pct_remaining5m': null, 'sum': 3.05909375, 'tx_atabove': 704.0, 'hashpower_accepting': 21.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 7.7, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 0.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 5.35, 'avgdiff': 1, 'expectedWait': 21.3082378187, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 18.86035313, 'pct_remaining5m': 0.0, 'sum': 2.8933625, 'tx_atabove': 702.0, 'hashpower_accepting': 28.125, 'hpa_coef2': -0.067, 'total_seen_30m': 30.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 8.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 37.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 4.53, 'avgdiff': 1, 'expectedWait': 18.053913939, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 19.1011235955, 'pct_remaining5m': 0.0, 'sum': 2.85250625, 'tx_atabove': 655.0, 'hashpower_accepting': 28.6458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 8.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 5.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 4.35, 'avgdiff': 1, 'expectedWait': 17.331163684, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 19.1613162119, 'pct_remaining5m': 0.0, 'sum': 2.84890625, 'tx_atabove': 649.0, 'hashpower_accepting': 28.6458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 8.2, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 4.33, 'avgdiff': 1, 'expectedWait': 17.268883666, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 19.231540931, 'pct_remaining5m': 0.0, 'sum': 2.8097375, 'tx_atabove': 647.0, 'hashpower_accepting': 30.2083333333, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 8.4, 'pct_mined_5m': 100.0, 'total_seen_5m': 6.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 4.17, 'avgdiff': 1, 'expectedWait': 16.6055586875, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 19.5224719101, 'pct_remaining5m': 0.0, 'sum': 2.777225, 'tx_atabove': 635.0, 'hashpower_accepting': 31.25, 'hpa_coef2': -0.067, 'total_seen_30m': 7.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 8.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 12.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 4.03, 'avgdiff': 1, 'expectedWait': 16.0743526708, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 19.7331460674, 'pct_remaining5m': 0.0, 'sum': 2.774225, 'tx_atabove': 630.0, 'hashpower_accepting': 31.25, 'hpa_coef2': -0.067, 'total_seen_30m': 5.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 8.6, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 4.02, 'avgdiff': 1, 'expectedWait': 16.0262018751, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 19.8033707865, 'pct_remaining5m': 0.0, 'sum': 2.72905625, 'tx_atabove': 618.0, 'hashpower_accepting': 32.8125, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 8.8, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 3.84, 'avgdiff': 1, 'expectedWait': 15.3184234339, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 19.9638844302, 'pct_remaining5m': 0.0, 'sum': 2.6954, 'tx_atabove': 583.0, 'hashpower_accepting': 33.3333333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 8.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 6.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 3.72, 'avgdiff': 1, 'expectedWait': 14.8114421454, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 23.6155698234, 'pct_remaining5m': 0.0, 'sum': 2.3937875, 'tx_atabove': 460.0, 'hashpower_accepting': 42.7083333333, 'hpa_coef2': -0.067, 'total_seen_30m': 43.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 9.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 120.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.75, 'avgdiff': 1, 'expectedWait': 10.9549071782, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 24.0268860353, 'pct_remaining5m': 0.0, 'sum': 2.30313125, 'tx_atabove': 330.0, 'hashpower_accepting': 43.2291666667, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 9.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 23.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.51, 'avgdiff': 1, 'expectedWait': 10.0054630618, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 24.1472712681, 'pct_remaining5m': 0.0, 'sum': 2.287475, 'tx_atabove': 325.0, 'hashpower_accepting': 43.75, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 9.2, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.47, 'avgdiff': 1, 'expectedWait': 9.8500349165, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 24.2174959872, 'pct_remaining5m': 0.0, 'sum': 2.2609625, 'tx_atabove': 323.0, 'hashpower_accepting': 44.7916666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 9.3, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.41, 'avgdiff': 1, 'expectedWait': 9.5923173304, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 24.3880417335, 'pct_remaining5m': 0.0, 'sum': 2.22239375, 'tx_atabove': 322.0, 'hashpower_accepting': 46.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 9.4, 'pct_mined_5m': 100.0, 'total_seen_5m': 7.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.32, 'avgdiff': 1, 'expectedWait': 9.2293973144, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 24.5284911717, 'pct_remaining5m': 0.0, 'sum': 2.2091375, 'tx_atabove': 321.0, 'hashpower_accepting': 46.875, 'hpa_coef2': -0.067, 'total_seen_30m': 5.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 9.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.29, 'avgdiff': 1, 'expectedWait': 9.1078574773, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 24.7391653291, 'pct_remaining5m': 0.0, 'sum': 2.2073375, 'tx_atabove': 318.0, 'hashpower_accepting': 46.875, 'hpa_coef2': -0.067, 'total_seen_30m': 9.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 9.6, 'pct_mined_5m': 100.0, 'total_seen_5m': 8.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.28, 'avgdiff': 1, 'expectedWait': 9.0914780797, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 24.9699036918, 'pct_remaining5m': 0.0, 'sum': 2.182025, 'tx_atabove': 318.0, 'hashpower_accepting': 47.9166666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 9.7, 'pct_mined_5m': 88.0, 'total_seen_5m': 9.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.22, 'avgdiff': 1, 'expectedWait': 8.8642381788, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 2.0, 'hashpower_accepting2': 25.1203852327, 'pct_remaining5m': 0.0, 'sum': 2.16936875, 'tx_atabove': 318.0, 'hashpower_accepting': 48.4375, 'hpa_coef2': -0.067, 'total_seen_30m': 6.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 9.8, 'pct_mined_5m': 75.0, 'total_seen_5m': 4.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.2, 'avgdiff': 1, 'expectedWait': 8.7527571186, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 25.1705457464, 'pct_remaining5m': 0.0, 'sum': 2.1561125, 'tx_atabove': 317.0, 'hashpower_accepting': 48.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 9.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 2.17, 'avgdiff': 1, 'expectedWait': 8.637494048, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 36.3864365971, 'pct_remaining5m': 0.0, 'sum': 1.769825, 'tx_atabove': 306.0, 'hashpower_accepting': 64.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 353.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 10.0, 'pct_mined_5m': 99.0, 'total_seen_5m': 245.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.47, 'avgdiff': 1, 'expectedWait': 5.8698260519, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 36.536918138, 'pct_remaining5m': 0.0, 'sum': 1.733225, 'tx_atabove': 245.0, 'hashpower_accepting': 64.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 7.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 10.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.42, 'avgdiff': 1, 'expectedWait': 5.658874382, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 36.7576243981, 'pct_remaining5m': 0.0, 'sum': 1.733225, 'tx_atabove': 245.0, 'hashpower_accepting': 64.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 6.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 10.2, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.42, 'avgdiff': 1, 'expectedWait': 5.658874382, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 36.8378812199, 'pct_remaining5m': 0.0, 'sum': 1.732625, 'tx_atabove': 244.0, 'hashpower_accepting': 64.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 10.3, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.42, 'avgdiff': 1, 'expectedWait': 5.6554800758, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 36.8679775281, 'pct_remaining5m': 0.0, 'sum': 1.732025, 'tx_atabove': 243.0, 'hashpower_accepting': 64.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 10.4, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.42, 'avgdiff': 1, 'expectedWait': 5.6520878055, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 37.8109951846, 'pct_remaining5m': 0.0, 'sum': 1.69405625, 'tx_atabove': 243.0, 'hashpower_accepting': 66.1458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 12.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 10.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 53.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.37, 'avgdiff': 1, 'expectedWait': 5.4415081179, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 37.871187801, 'pct_remaining5m': 0.0, 'sum': 1.69285625, 'tx_atabove': 241.0, 'hashpower_accepting': 66.1458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 10.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.36, 'avgdiff': 1, 'expectedWait': 5.4349822245, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 38.1019261637, 'pct_remaining5m': 0.0, 'sum': 1.69285625, 'tx_atabove': 241.0, 'hashpower_accepting': 66.1458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 10.8, 'pct_mined_5m': 100.0, 'total_seen_5m': 6.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.36, 'avgdiff': 1, 'expectedWait': 5.4349822245, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 38.1821829856, 'pct_remaining5m': 0.0, 'sum': 1.68565625, 'tx_atabove': 229.0, 'hashpower_accepting': 66.1458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 10.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.35, 'avgdiff': 1, 'expectedWait': 5.3959908897, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 40.7002407705, 'pct_remaining5m': 0.0, 'sum': 1.520525, 'tx_atabove': 228.0, 'hashpower_accepting': 72.9166666667, 'hpa_coef2': -0.067, 'total_seen_30m': 84.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 11.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 84.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.15, 'avgdiff': 1, 'expectedWait': 4.5746262436, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 40.8206260032, 'pct_remaining5m': 0.0, 'sum': 1.507325, 'tx_atabove': 206.0, 'hashpower_accepting': 72.9166666667, 'hpa_coef2': -0.067, 'total_seen_30m': 5.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 11.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 4.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.13, 'avgdiff': 1, 'expectedWait': 4.5146379708, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 40.8908507223, 'pct_remaining5m': 0.0, 'sum': 1.507325, 'tx_atabove': 206.0, 'hashpower_accepting': 72.9166666667, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 11.2, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.13, 'avgdiff': 1, 'expectedWait': 4.5146379708, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 41.4024879615, 'pct_remaining5m': 0.0, 'sum': 1.49466875, 'tx_atabove': 206.0, 'hashpower_accepting': 73.4375, 'hpa_coef2': -0.067, 'total_seen_30m': 15.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 11.4, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.12, 'avgdiff': 1, 'expectedWait': 4.4578596422, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 41.4827447833, 'pct_remaining5m': 0.0, 'sum': 1.49466875, 'tx_atabove': 206.0, 'hashpower_accepting': 73.4375, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 11.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.12, 'avgdiff': 1, 'expectedWait': 4.4578596422, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 41.6131621188, 'pct_remaining5m': 0.0, 'sum': 1.49406875, 'tx_atabove': 205.0, 'hashpower_accepting': 73.4375, 'hpa_coef2': -0.067, 'total_seen_30m': 8.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 11.6, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.12, 'avgdiff': 1, 'expectedWait': 4.4551857287, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 41.6332263242, 'pct_remaining5m': 0.0, 'sum': 1.49406875, 'tx_atabove': 205.0, 'hashpower_accepting': 73.4375, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 11.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.12, 'avgdiff': 1, 'expectedWait': 4.4551857287, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 41.753611557, 'pct_remaining5m': 0.0, 'sum': 1.49406875, 'tx_atabove': 205.0, 'hashpower_accepting': 73.4375, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 11.8, 'pct_mined_5m': 100.0, 'total_seen_5m': 6.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.12, 'avgdiff': 1, 'expectedWait': 4.4551857287, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 41.7736757624, 'pct_remaining5m': null, 'sum': 1.49406875, 'tx_atabove': 205.0, 'hashpower_accepting': 73.4375, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 11.9, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.12, 'avgdiff': 1, 'expectedWait': 4.4551857287, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 44.7030497592, 'pct_remaining5m': 0.0, 'sum': 1.41813125, 'tx_atabove': 205.0, 'hashpower_accepting': 76.5625, 'hpa_coef2': -0.067, 'total_seen_30m': 96.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 39.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.04, 'avgdiff': 1, 'expectedWait': 4.1293964158, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 44.9036918138, 'pct_remaining5m': 0.0, 'sum': 1.399475, 'tx_atabove': 195.0, 'hashpower_accepting': 77.0833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 11.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 6.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.02, 'avgdiff': 1, 'expectedWait': 4.0530715456, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 45.0341091493, 'pct_remaining5m': null, 'sum': 1.38681875, 'tx_atabove': 195.0, 'hashpower_accepting': 77.6041666667, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.2, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 1.0, 'avgdiff': 1, 'expectedWait': 4.0020981056, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 45.1845906902, 'pct_remaining5m': 0.0, 'sum': 1.3735625, 'tx_atabove': 194.0, 'hashpower_accepting': 78.125, 'hpa_coef2': -0.067, 'total_seen_30m': 9.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.3, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.99, 'avgdiff': 1, 'expectedWait': 3.9493953846, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 45.1946227929, 'pct_remaining5m': null, 'sum': 1.3735625, 'tx_atabove': 194.0, 'hashpower_accepting': 78.125, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.4, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.99, 'avgdiff': 1, 'expectedWait': 3.9493953846, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 45.3752006421, 'pct_remaining5m': 0.0, 'sum': 1.36090625, 'tx_atabove': 194.0, 'hashpower_accepting': 78.6458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 10.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.98, 'avgdiff': 1, 'expectedWait': 3.8997258274, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 45.4955858748, 'pct_remaining5m': 0.0, 'sum': 1.36090625, 'tx_atabove': 194.0, 'hashpower_accepting': 78.6458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 4.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.6, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.98, 'avgdiff': 1, 'expectedWait': 3.8997258274, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 45.525682183, 'pct_remaining5m': null, 'sum': 1.36090625, 'tx_atabove': 194.0, 'hashpower_accepting': 78.6458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.7, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.98, 'avgdiff': 1, 'expectedWait': 3.8997258274, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 45.5858747994, 'pct_remaining5m': 0.0, 'sum': 1.36090625, 'tx_atabove': 194.0, 'hashpower_accepting': 78.6458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 4.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.8, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.98, 'avgdiff': 1, 'expectedWait': 3.8997258274, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 45.636035313, 'pct_remaining5m': 0.0, 'sum': 1.36090625, 'tx_atabove': 194.0, 'hashpower_accepting': 78.6458333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 12.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.98, 'avgdiff': 1, 'expectedWait': 3.8997258274, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 46.9903691814, 'pct_remaining5m': 0.0, 'sum': 1.31028125, 'tx_atabove': 194.0, 'hashpower_accepting': 80.7291666667, 'hpa_coef2': -0.067, 'total_seen_30m': 47.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 13.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 34.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.93, 'avgdiff': 1, 'expectedWait': 3.7072162202, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 47.5321027287, 'pct_remaining5m': 0.0, 'sum': 1.292825, 'tx_atabove': 186.0, 'hashpower_accepting': 81.25, 'hpa_coef2': -0.067, 'total_seen_30m': 9.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 13.2, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.91, 'avgdiff': 1, 'expectedWait': 3.6430636874, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 47.5621990369, 'pct_remaining5m': 0.0, 'sum': 1.292825, 'tx_atabove': 186.0, 'hashpower_accepting': 81.25, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 13.3, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.91, 'avgdiff': 1, 'expectedWait': 3.6430636874, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 47.632423756, 'pct_remaining5m': null, 'sum': 1.292825, 'tx_atabove': 186.0, 'hashpower_accepting': 81.25, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 13.4, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.91, 'avgdiff': 1, 'expectedWait': 3.6430636874, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.5, 'hashpower_accepting2': 48.1440609952, 'pct_remaining5m': 0.0, 'sum': 1.28016875, 'tx_atabove': 186.0, 'hashpower_accepting': 81.7708333333, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 13.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 21.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.9, 'avgdiff': 1, 'expectedWait': 3.5972467097, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 48.4550561798, 'pct_remaining5m': 0.0, 'sum': 1.2651125, 'tx_atabove': 182.0, 'hashpower_accepting': 82.2916666667, 'hpa_coef2': -0.067, 'total_seen_30m': 10.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 13.6, 'pct_mined_5m': 100.0, 'total_seen_5m': 10.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.89, 'avgdiff': 1, 'expectedWait': 3.5434913565, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 55.9590690209, 'pct_remaining5m': 0.0, 'sum': 1.2398, 'tx_atabove': 182.0, 'hashpower_accepting': 83.3333333333, 'hpa_coef2': -0.067, 'total_seen_30m': 253.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 13.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 212.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.87, 'avgdiff': 1, 'expectedWait': 3.4549224112, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 56.0593900482, 'pct_remaining5m': 0.0, 'sum': 1.226, 'tx_atabove': 159.0, 'hashpower_accepting': 83.3333333333, 'hpa_coef2': -0.067, 'total_seen_30m': 6.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 13.8, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.86, 'avgdiff': 1, 'expectedWait': 3.4075719515, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 56.1095505618, 'pct_remaining5m': 0.0, 'sum': 1.226, 'tx_atabove': 159.0, 'hashpower_accepting': 83.3333333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 13.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 4.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.86, 'avgdiff': 1, 'expectedWait': 3.4075719515, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.5, 'hashpower_accepting2': 59.6408507223, 'pct_remaining5m': 0.0, 'sum': 1.13740625, 'tx_atabove': 159.0, 'hashpower_accepting': 86.9791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 119.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 14.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 115.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.78, 'avgdiff': 1, 'expectedWait': 3.1186688184, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 59.7311396469, 'pct_remaining5m': 0.0, 'sum': 1.13440625, 'tx_atabove': 154.0, 'hashpower_accepting': 86.9791666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 14.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 5.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.78, 'avgdiff': 1, 'expectedWait': 3.1093268319, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 59.8214285714, 'pct_remaining5m': 0.0, 'sum': 1.13440625, 'tx_atabove': 154.0, 'hashpower_accepting': 86.9791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 4.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 14.2, 'pct_mined_5m': 100.0, 'total_seen_5m': 4.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.78, 'avgdiff': 1, 'expectedWait': 3.1093268319, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 60.1524879615, 'pct_remaining5m': 0.0, 'sum': 1.13380625, 'tx_atabove': 153.0, 'hashpower_accepting': 86.9791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 5.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 14.4, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.78, 'avgdiff': 1, 'expectedWait': 3.1074617954, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 60.1725521669, 'pct_remaining5m': 0.0, 'sum': 1.13320625, 'tx_atabove': 152.0, 'hashpower_accepting': 86.9791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 14.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.78, 'avgdiff': 1, 'expectedWait': 3.1055978775, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 60.2528089888, 'pct_remaining5m': 0.0, 'sum': 1.13320625, 'tx_atabove': 152.0, 'hashpower_accepting': 86.9791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 14.6, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.78, 'avgdiff': 1, 'expectedWait': 3.1055978775, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 60.6440609952, 'pct_remaining5m': 0.0, 'sum': 1.13320625, 'tx_atabove': 152.0, 'hashpower_accepting': 86.9791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 10.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 14.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 10.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.78, 'avgdiff': 1, 'expectedWait': 3.1055978775, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 60.6641252006, 'pct_remaining5m': 0.0, 'sum': 1.13320625, 'tx_atabove': 152.0, 'hashpower_accepting': 86.9791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 14.8, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.78, 'avgdiff': 1, 'expectedWait': 3.1055978775, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 60.6942215088, 'pct_remaining5m': null, 'sum': 1.13320625, 'tx_atabove': 152.0, 'hashpower_accepting': 86.9791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 14.9, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.78, 'avgdiff': 1, 'expectedWait': 3.1055978775, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 62.9113162119, 'pct_remaining5m': 0.0, 'sum': 1.0952375, 'tx_atabove': 152.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 65.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 15.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 48.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.75, 'avgdiff': 1, 'expectedWait': 2.9898926986, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 63.4129213483, 'pct_remaining5m': 0.0, 'sum': 1.0910375, 'tx_atabove': 145.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 11.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 15.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 8.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.75, 'avgdiff': 1, 'expectedWait': 2.9773614832, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 64.4161316212, 'pct_remaining5m': 0.0, 'sum': 1.0886375, 'tx_atabove': 141.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 15.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 4.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.75, 'avgdiff': 1, 'expectedWait': 2.9702243836, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 64.4863563403, 'pct_remaining5m': 0.0, 'sum': 1.0820375, 'tx_atabove': 130.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 5.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 15.6, 'pct_mined_5m': 50.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.74, 'avgdiff': 1, 'expectedWait': 2.9506854521, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 66.1817817014, 'pct_remaining5m': 0.0, 'sum': 1.0820375, 'tx_atabove': 130.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 24.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 15.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 17.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.74, 'avgdiff': 1, 'expectedWait': 2.9506854521, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 66.2620385233, 'pct_remaining5m': 0.0, 'sum': 1.0766375, 'tx_atabove': 121.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 15.8, 'pct_mined_5m': 83.0, 'total_seen_5m': 6.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.74, 'avgdiff': 1, 'expectedWait': 2.9347946943, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 67.2853130016, 'pct_remaining5m': 0.0, 'sum': 1.0766375, 'tx_atabove': 121.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 15.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 16.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 11.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.74, 'avgdiff': 1, 'expectedWait': 2.9347946943, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 67.2953451043, 'pct_remaining5m': null, 'sum': 1.0748375, 'tx_atabove': 118.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 16.1, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.74, 'avgdiff': 1, 'expectedWait': 2.9295168154, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 67.4458266453, 'pct_remaining5m': 0.0, 'sum': 1.0748375, 'tx_atabove': 118.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 16.2, 'pct_mined_5m': 100.0, 'total_seen_5m': 7.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.74, 'avgdiff': 1, 'expectedWait': 2.9295168154, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 67.455858748, 'pct_remaining5m': 0.0, 'sum': 1.0748375, 'tx_atabove': 118.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 16.3, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.74, 'avgdiff': 1, 'expectedWait': 2.9295168154, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 67.6565008026, 'pct_remaining5m': 0.0, 'sum': 1.0748375, 'tx_atabove': 118.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 5.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 16.4, 'pct_mined_5m': 100.0, 'total_seen_5m': 7.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.74, 'avgdiff': 1, 'expectedWait': 2.9295168154, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 67.7267255217, 'pct_remaining5m': 0.0, 'sum': 1.0742375, 'tx_atabove': 117.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 16.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.73, 'avgdiff': 1, 'expectedWait': 2.9277596325, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 68.6697431782, 'pct_remaining5m': 0.0, 'sum': 1.0742375, 'tx_atabove': 117.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 42.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 16.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 27.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.73, 'avgdiff': 1, 'expectedWait': 2.9277596325, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 68.8904494382, 'pct_remaining5m': 0.0, 'sum': 1.0718375, 'tx_atabove': 113.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 11.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 17.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.73, 'avgdiff': 1, 'expectedWait': 2.9207414346, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 69.0308988764, 'pct_remaining5m': 0.0, 'sum': 1.0712375, 'tx_atabove': 112.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 8.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 17.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.73, 'avgdiff': 1, 'expectedWait': 2.9189895153, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.0409309791, 'pct_remaining5m': null, 'sum': 1.0706375, 'tx_atabove': 111.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 17.3, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.73, 'avgdiff': 1, 'expectedWait': 2.9172386469, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.0710272873, 'pct_remaining5m': 0.0, 'sum': 1.0706375, 'tx_atabove': 111.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 17.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.73, 'avgdiff': 1, 'expectedWait': 2.9172386469, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.08105939, 'pct_remaining5m': 0.0, 'sum': 1.0706375, 'tx_atabove': 111.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 17.6, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.73, 'avgdiff': 1, 'expectedWait': 2.9172386469, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.1011235955, 'pct_remaining5m': null, 'sum': 1.0706375, 'tx_atabove': 111.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 17.8, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.73, 'avgdiff': 1, 'expectedWait': 2.9172386469, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.1111556982, 'pct_remaining5m': 0.0, 'sum': 1.0706375, 'tx_atabove': 111.0, 'hashpower_accepting': 88.5416666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 17.9, 'pct_mined_5m': 0.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.73, 'avgdiff': 1, 'expectedWait': 2.9172386469, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.7632423756, 'pct_remaining5m': 0.0, 'sum': 1.05798125, 'tx_atabove': 111.0, 'hashpower_accepting': 89.0625, 'hpa_coef2': -0.067, 'total_seen_30m': 16.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 18.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 16.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.72, 'avgdiff': 1, 'expectedWait': 2.8805500054, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.7732744783, 'pct_remaining5m': 0.0, 'sum': 1.05618125, 'tx_atabove': 108.0, 'hashpower_accepting': 89.0625, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 18.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.72, 'avgdiff': 1, 'expectedWait': 2.875369679, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.8134028892, 'pct_remaining5m': 0.0, 'sum': 1.05618125, 'tx_atabove': 108.0, 'hashpower_accepting': 89.0625, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 18.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.72, 'avgdiff': 1, 'expectedWait': 2.875369679, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.823434992, 'pct_remaining5m': 0.0, 'sum': 1.05618125, 'tx_atabove': 108.0, 'hashpower_accepting': 89.0625, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 18.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.72, 'avgdiff': 1, 'expectedWait': 2.875369679, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.8535313002, 'pct_remaining5m': null, 'sum': 1.05618125, 'tx_atabove': 108.0, 'hashpower_accepting': 89.0625, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 18.8, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.72, 'avgdiff': 1, 'expectedWait': 2.875369679, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 69.8735955056, 'pct_remaining5m': 0.0, 'sum': 1.05618125, 'tx_atabove': 108.0, 'hashpower_accepting': 89.0625, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 18.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.72, 'avgdiff': 1, 'expectedWait': 2.875369679, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 70.3150080257, 'pct_remaining5m': 0.0, 'sum': 1.043525, 'tx_atabove': 108.0, 'hashpower_accepting': 89.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 13.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 19.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 11.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.71, 'avgdiff': 1, 'expectedWait': 2.8392076024, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 70.3350722311, 'pct_remaining5m': 0.0, 'sum': 1.042325, 'tx_atabove': 106.0, 'hashpower_accepting': 89.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 19.3, 'pct_mined_5m': 66.0, 'total_seen_5m': 3.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.71, 'avgdiff': 1, 'expectedWait': 2.8358025967, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 70.3752006421, 'pct_remaining5m': 0.0, 'sum': 1.042325, 'tx_atabove': 106.0, 'hashpower_accepting': 89.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 19.4, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.71, 'avgdiff': 1, 'expectedWait': 2.8358025967, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 70.3852327448, 'pct_remaining5m': null, 'sum': 1.042325, 'tx_atabove': 106.0, 'hashpower_accepting': 89.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 19.5, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.71, 'avgdiff': 1, 'expectedWait': 2.8358025967, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 70.4052969502, 'pct_remaining5m': null, 'sum': 1.042325, 'tx_atabove': 106.0, 'hashpower_accepting': 89.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 19.8, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.71, 'avgdiff': 1, 'expectedWait': 2.8358025967, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 70.8266452648, 'pct_remaining5m': 0.0, 'sum': 1.042325, 'tx_atabove': 106.0, 'hashpower_accepting': 89.5833333333, 'hpa_coef2': -0.067, 'total_seen_30m': 18.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 19.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 10.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.71, 'avgdiff': 1, 'expectedWait': 2.8358025967, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 75.2708667737, 'pct_remaining5m': 0.0, 'sum': 0.90310625, 'tx_atabove': 106.0, 'hashpower_accepting': 95.3125, 'hpa_coef2': -0.067, 'total_seen_30m': 144.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 20.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 185.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.62, 'avgdiff': 1, 'expectedWait': 2.4672551317, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 75.3009630819, 'pct_remaining5m': null, 'sum': 0.89650625, 'tx_atabove': 95.0, 'hashpower_accepting': 95.3125, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 20.2, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.62, 'avgdiff': 1, 'expectedWait': 2.4510248666, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 75.3210272873, 'pct_remaining5m': 0.0, 'sum': 0.89650625, 'tx_atabove': 95.0, 'hashpower_accepting': 95.3125, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 20.3, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.62, 'avgdiff': 1, 'expectedWait': 2.4510248666, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 77.7387640449, 'pct_remaining5m': 0.0, 'sum': 0.88385, 'tx_atabove': 95.0, 'hashpower_accepting': 95.8333333333, 'hpa_coef2': -0.067, 'total_seen_30m': 24.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 20.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 9.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.61, 'avgdiff': 1, 'expectedWait': 2.420199561, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 77.7688603531, 'pct_remaining5m': 0.0, 'sum': 0.88325, 'tx_atabove': 94.0, 'hashpower_accepting': 95.8333333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 20.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.61, 'avgdiff': 1, 'expectedWait': 2.4187478768, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 77.9093097913, 'pct_remaining5m': null, 'sum': 0.87059375, 'tx_atabove': 94.0, 'hashpower_accepting': 96.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 7.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 20.9, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.6, 'avgdiff': 1, 'expectedWait': 2.3883285027, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 79.7752808989, 'pct_remaining5m': 0.0, 'sum': 0.87059375, 'tx_atabove': 94.0, 'hashpower_accepting': 96.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 39.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 21.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 36.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.6, 'avgdiff': 1, 'expectedWait': 2.3883285027, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 79.7953451043, 'pct_remaining5m': 0.0, 'sum': 0.86819375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 21.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.6, 'avgdiff': 1, 'expectedWait': 2.3826033871, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 79.8154093098, 'pct_remaining5m': null, 'sum': 0.86819375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 21.4, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.6, 'avgdiff': 1, 'expectedWait': 2.3826033871, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 79.845505618, 'pct_remaining5m': 0.0, 'sum': 0.86819375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 21.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.6, 'avgdiff': 1, 'expectedWait': 2.3826033871, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 80.3069823435, 'pct_remaining5m': 0.0, 'sum': 0.86819375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.3541666667, 'hpa_coef2': -0.067, 'total_seen_30m': 9.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 21.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.6, 'avgdiff': 1, 'expectedWait': 2.3826033871, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 80.8888443018, 'pct_remaining5m': 0.0, 'sum': 0.8555375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': 13.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 22.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 13.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.352638584, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 80.9089085072, 'pct_remaining5m': null, 'sum': 0.8555375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 22.1, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.352638584, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 80.91894061, 'pct_remaining5m': 0.0, 'sum': 0.8555375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 22.2, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.352638584, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 80.9289727127, 'pct_remaining5m': null, 'sum': 0.8555375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 22.3, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.352638584, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 80.9490369181, 'pct_remaining5m': 0.0, 'sum': 0.8555375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 22.5, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.352638584, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 80.9791332263, 'pct_remaining5m': 0.0, 'sum': 0.8555375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 22.8, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.352638584, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 80.9891653291, 'pct_remaining5m': null, 'sum': 0.8555375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 22.9, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.352638584, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 81.47070626, 'pct_remaining5m': 0.0, 'sum': 0.8555375, 'tx_atabove': 90.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': 15.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 23.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 19.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.352638584, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 81.4907704655, 'pct_remaining5m': 0.0, 'sum': 0.8531375, 'tx_atabove': 86.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 23.1, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.3469990216, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 81.5108346709, 'pct_remaining5m': 0.0, 'sum': 0.8525375, 'tx_atabove': 85.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 23.3, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.3455912446, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 81.6011235955, 'pct_remaining5m': 0.0, 'sum': 0.8525375, 'tx_atabove': 85.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 23.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.3455912446, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 81.621187801, 'pct_remaining5m': 0.0, 'sum': 0.8525375, 'tx_atabove': 85.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 23.9, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.3455912446, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 81.8719903692, 'pct_remaining5m': 0.0, 'sum': 0.8525375, 'tx_atabove': 85.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': 7.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 24.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 9.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.3455912446, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 81.8820224719, 'pct_remaining5m': 0.0, 'sum': 0.8501375, 'tx_atabove': 81.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 24.3, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.3399685755, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 81.9121187801, 'pct_remaining5m': 0.0, 'sum': 0.8501375, 'tx_atabove': 81.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 24.6, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.3399685755, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 81.9321829856, 'pct_remaining5m': 0.0, 'sum': 0.8501375, 'tx_atabove': 81.0, 'hashpower_accepting': 96.875, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 24.7, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.59, 'avgdiff': 1, 'expectedWait': 2.3399685755, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 83.3868378812, 'pct_remaining5m': 0.0, 'sum': 0.83688125, 'tx_atabove': 80.0, 'hashpower_accepting': 97.3958333333, 'hpa_coef2': -0.067, 'total_seen_30m': 24.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 25.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 32.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.58, 'avgdiff': 1, 'expectedWait': 2.3091540608, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 83.6276083467, 'pct_remaining5m': 0.0, 'sum': 0.82128125, 'tx_atabove': 54.0, 'hashpower_accepting': 97.3958333333, 'hpa_coef2': -0.067, 'total_seen_30m': 10.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 26.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 7.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.57, 'avgdiff': 1, 'expectedWait': 2.2734107799, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 83.7379614767, 'pct_remaining5m': 0.0, 'sum': 0.82008125, 'tx_atabove': 52.0, 'hashpower_accepting': 97.3958333333, 'hpa_coef2': -0.067, 'total_seen_30m': 5.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 27.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.57, 'avgdiff': 1, 'expectedWait': 2.2706843231, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 83.9887640449, 'pct_remaining5m': 0.0, 'sum': 0.81948125, 'tx_atabove': 51.0, 'hashpower_accepting': 97.3958333333, 'hpa_coef2': -0.067, 'total_seen_30m': 13.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 28.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 7.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.57, 'avgdiff': 1, 'expectedWait': 2.2693223212, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 84.0690208668, 'pct_remaining5m': 0.0, 'sum': 0.806825, 'tx_atabove': 51.0, 'hashpower_accepting': 97.9166666667, 'hpa_coef2': -0.067, 'total_seen_30m': 4.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 29.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.56, 'avgdiff': 1, 'expectedWait': 2.240782197, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 85.8447030498, 'pct_remaining5m': 0.0, 'sum': 0.7809125, 'tx_atabove': 50.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 55.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 30.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 49.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.55, 'avgdiff': 1, 'expectedWait': 2.1834637674, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 85.9751203852, 'pct_remaining5m': 0.0, 'sum': 0.7743125, 'tx_atabove': 39.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 6.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 31.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 5.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.169100358, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 86.1356340289, 'pct_remaining5m': 0.0, 'sum': 0.7743125, 'tx_atabove': 39.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 32.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 4.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.169100358, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 86.2760834671, 'pct_remaining5m': 0.0, 'sum': 0.7743125, 'tx_atabove': 39.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 33.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 6.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.169100358, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 86.4165329053, 'pct_remaining5m': 0.0, 'sum': 0.7737125, 'tx_atabove': 38.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 6.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 34.0, 'pct_mined_5m': 60.0, 'total_seen_5m': 5.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.1677992881, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.5, 'hashpower_accepting2': 86.7475922953, 'pct_remaining5m': 0.0, 'sum': 0.7737125, 'tx_atabove': 38.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 10.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 35.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 17.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.1677992881, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 86.7877207063, 'pct_remaining5m': 0.0, 'sum': 0.7725125, 'tx_atabove': 36.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 36.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.1651994891, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 86.8579454254, 'pct_remaining5m': 0.0, 'sum': 0.7725125, 'tx_atabove': 36.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 37.0, 'pct_mined_5m': 66.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.1651994891, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 86.908105939, 'pct_remaining5m': 0.0, 'sum': 0.7725125, 'tx_atabove': 36.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 38.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.1651994891, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 86.9783306581, 'pct_remaining5m': 0.0, 'sum': 0.7725125, 'tx_atabove': 36.0, 'hashpower_accepting': 98.9583333333, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 39.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.1651994891, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 87.4498394864, 'pct_remaining5m': 0.0, 'sum': 0.75985625, 'tx_atabove': 36.0, 'hashpower_accepting': 99.4791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 16.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 40.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 14.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.1379688654, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 89.6167736758, 'pct_remaining5m': 0.0, 'sum': 0.75805625, 'tx_atabove': 33.0, 'hashpower_accepting': 99.4791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 112.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 41.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 57.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.54, 'avgdiff': 1, 'expectedWait': 2.1341239829, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 89.6869983949, 'pct_remaining5m': 0.0, 'sum': 0.75445625, 'tx_atabove': 27.0, 'hashpower_accepting': 99.4791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 42.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.53, 'avgdiff': 1, 'expectedWait': 2.1264549491, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 89.7070626003, 'pct_remaining5m': null, 'sum': 0.75385625, 'tx_atabove': 26.0, 'hashpower_accepting': 99.4791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 43.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.53, 'avgdiff': 1, 'expectedWait': 2.1251794588, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 89.9478330658, 'pct_remaining5m': 0.0, 'sum': 0.75385625, 'tx_atabove': 26.0, 'hashpower_accepting': 99.4791666667, 'hpa_coef2': -0.067, 'total_seen_30m': 9.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 44.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 9.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.53, 'avgdiff': 1, 'expectedWait': 2.1251794588, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 90.088282504, 'pct_remaining5m': 0.0, 'sum': 0.7394, 'tx_atabove': 23.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 8.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 45.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 5.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.53, 'avgdiff': 1, 'expectedWait': 2.0946783304, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 90.4995987159, 'pct_remaining5m': 0.0, 'sum': 0.7394, 'tx_atabove': 23.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 17.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 46.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 19.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.53, 'avgdiff': 1, 'expectedWait': 2.0946783304, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 90.5999197432, 'pct_remaining5m': 0.0, 'sum': 0.7382, 'tx_atabove': 21.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 4.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 47.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.53, 'avgdiff': 1, 'expectedWait': 2.0921662239, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 90.6199839486, 'pct_remaining5m': null, 'sum': 0.7382, 'tx_atabove': 21.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 48.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.53, 'avgdiff': 1, 'expectedWait': 2.0921662239, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 90.6500802568, 'pct_remaining5m': null, 'sum': 0.7382, 'tx_atabove': 21.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 49.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.53, 'avgdiff': 1, 'expectedWait': 2.0921662239, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 94.1412520064, 'pct_remaining5m': 0.0, 'sum': 0.7382, 'tx_atabove': 21.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 62.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 50.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 71.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.53, 'avgdiff': 1, 'expectedWait': 2.0921662239, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 94.2114767255, 'pct_remaining5m': 0.0, 'sum': 0.7358, 'tx_atabove': 17.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 51.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0871510456, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 94.4422150883, 'pct_remaining5m': 0.0, 'sum': 0.7358, 'tx_atabove': 17.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 5.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 52.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0871510456, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 94.4723113965, 'pct_remaining5m': null, 'sum': 0.7358, 'tx_atabove': 17.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 54.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0871510456, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 94.4823434992, 'pct_remaining5m': null, 'sum': 0.7346, 'tx_atabove': 15.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 56.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0846479665, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 95.0140449438, 'pct_remaining5m': 0.0, 'sum': 0.7346, 'tx_atabove': 15.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 27.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 60.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 17.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0846479665, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 95.6059390048, 'pct_remaining5m': 0.0, 'sum': 0.7334, 'tx_atabove': 13.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 28.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 61.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 14.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0821478893, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 96.8699839486, 'pct_remaining5m': 0.0, 'sum': 0.7322, 'tx_atabove': 11.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 21.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 63.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 25.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0796508104, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 1.0, 'hashpower_accepting2': 98.3948635634, 'pct_remaining5m': 0.0, 'sum': 0.7298, 'tx_atabove': 7.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 83.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 64.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 38.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0746656331, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.4149277689, 'pct_remaining5m': 0.0, 'sum': 0.728, 'tx_atabove': 4.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 65.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0709345939, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.4951845907, 'pct_remaining5m': 0.0, 'sum': 0.7274, 'tx_atabove': 3.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 66.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0696924058, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.5052166934, 'pct_remaining5m': null, 'sum': 0.7274, 'tx_atabove': 3.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 69.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0696924058, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.5152487961, 'pct_remaining5m': null, 'sum': 0.7274, 'tx_atabove': 3.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 70.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0696924058, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.5553772071, 'pct_remaining5m': 0.0, 'sum': 0.7274, 'tx_atabove': 3.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 72.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0696924058, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.5754414125, 'pct_remaining5m': 0.0, 'sum': 0.7274, 'tx_atabove': 3.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 73.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0696924058, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.5854735152, 'pct_remaining5m': 0.0, 'sum': 0.7274, 'tx_atabove': 3.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 77.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0696924058, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.6055377207, 'pct_remaining5m': null, 'sum': 0.7274, 'tx_atabove': 3.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 79.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0696924058, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.8663723917, 'pct_remaining5m': 0.0, 'sum': 0.7274, 'tx_atabove': 3.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 14.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 80.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 6.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0696924058, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.8864365971, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 84.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.9466292135, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 88.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 98.9566613162, 'pct_remaining5m': null, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 90.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 99.0469502408, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 4.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 91.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 3.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 99.1272070626, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 3.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 96.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 99.1372391653, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 97.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 99.1472712681, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 99.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 99.7090690209, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 23.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 100.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 22.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 99.8394863563, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 7.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 101.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 99.9097110754, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 2.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 120.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 99.9297752809, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': null, 'int2': 6.9238, 'pct_remaining30m': null, 'gasprice': 134.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 2.0, 'pct_mined_30m': null, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': 0.0, 'hashpower_accepting2': 99.9498394864, 'pct_remaining5m': 0.0, 'sum': 0.7268, 'tx_atabove': 2.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 137.0, 'pct_mined_5m': 100.0, 'total_seen_5m': 1.0, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0684509628, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}, {'intercept': 4.8015, 'age': null, 'hashpower_accepting2': 99.9699036918, 'pct_remaining5m': null, 'sum': 0.7256, 'tx_atabove': 0.0, 'hashpower_accepting': 100.0, 'hpa_coef2': -0.067, 'total_seen_30m': 1.0, 'int2': 6.9238, 'pct_remaining30m': 0.0, 'gasprice': 180.0, 'pct_mined_5m': null, 'total_seen_5m': null, 'pct_mined_30m': 100.0, 'tx_atabove_coef': 0.0006, 'average': 600, 'safelow': 600, 'nomine': 550, 'expectedTime': 0.52, 'avgdiff': 1, 'expectedWait': 2.0659703104, 'avgdiff_coef': -1.6459, 'hpa_coef': -0.0243}]), + gasExpress: JSON.stringify({'safeLow': 2.0, 'standard': 3.0, 'fast': 10.0, 'fastest': 52.0, 'block_time': 15, 'blockNum': 6693030}), + metametrics: JSON.stringify({ 'mockMetaMetricsResponse': true }), +} diff --git a/test/e2e/beta/from-import-beta-ui.spec.js b/test/e2e/beta/from-import-beta-ui.spec.js index d2c3f8958..8aa0fb628 100644 --- a/test/e2e/beta/from-import-beta-ui.spec.js +++ b/test/e2e/beta/from-import-beta-ui.spec.js @@ -16,8 +16,8 @@ const { verboseReportOnFailure, findElement, findElements, - loadExtension, } = require('./helpers') +const fetchMockResponses = require('./fetch-mocks.js') describe('Using MetaMask with an existing account', function () { @@ -27,7 +27,6 @@ describe('Using MetaMask with an existing account', function () { const testSeedPhrase = 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent' const testAddress = '0xE18035BF8712672935FDB4e5e431b1a0183d2DFC' const testPrivateKey2 = '14abe6f4aab7f9f626fe981c864d0adeb5685f289ac9270c27b8fd790b4235d6' - const tinyDelayMs = 500 const regularDelayMs = 1000 const largeDelayMs = regularDelayMs * 2 @@ -35,13 +34,14 @@ describe('Using MetaMask with an existing account', function () { 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 driver.get(`chrome-extension://${extensionId}/popup.html`) await delay(regularDelayMs) + extensionUrl = `chrome-extension://${extensionId}/home.html` break } case 'firefox': { @@ -50,11 +50,34 @@ describe('Using MetaMask with an existing account', function () { await installWebExt(driver, extensionPath) await delay(regularDelayMs) extensionId = await getExtensionIdFirefox(driver) - await driver.get(`moz-extension://${extensionId}/popup.html`) - await delay(regularDelayMs) + 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); }' + ) }) afterEach(async function () { @@ -75,75 +98,28 @@ describe('Using MetaMask with an existing account', function () { await driver.quit() }) - describe('New UI setup', async function () { - it('switches to first tab', async function () { - await delay(tinyDelayMs) - const [firstTab] = await driver.getAllWindowHandles() - await driver.switchTo().window(firstTab) - await delay(regularDelayMs) + 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('selects the new UI option', async () => { - try { - const overlay = await findElement(driver, By.css('.full-flex-height')) - await driver.wait(until.stalenessOf(overlay)) - } catch (e) {} - - let button - try { - button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) - } catch (e) { - await loadExtension(driver, extensionId) - await delay(largeDelayMs) - button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) - } - await button.click() - await delay(regularDelayMs) - - // Close all other tabs - const [tab0, tab1, tab2] = await driver.getAllWindowHandles() - await driver.switchTo().window(tab0) - await delay(tinyDelayMs) - - let selectedUrl = await driver.getCurrentUrl() - await delay(tinyDelayMs) - if (tab0 && selectedUrl.match(/popup.html/)) { - await closeAllWindowHandlesExcept(driver, tab0) - } else if (tab1) { - await driver.switchTo().window(tab1) - selectedUrl = await driver.getCurrentUrl() - await delay(tinyDelayMs) - if (selectedUrl.match(/popup.html/)) { - await closeAllWindowHandlesExcept(driver, tab1) - } else if (tab2) { - await driver.switchTo().window(tab2) - selectedUrl = await driver.getCurrentUrl() - selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2) - } - } else { - throw new Error('popup.html not found') - } - await delay(regularDelayMs) - const [appTab] = await driver.getAllWindowHandles() - await driver.switchTo().window(appTab) - await delay(tinyDelayMs) - - await loadExtension(driver, extensionId) - await delay(regularDelayMs) + it('clicks the "Import Wallet" option', async () => { + const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`)) + customRpcButton.click() + await delay(largeDelayMs) + }) - const continueBtn = await findElement(driver, By.css('.welcome-screen__button')) - await continueBtn.click() - await delay(regularDelayMs) + 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) }) - }) - describe('First time flow starting from an existing seed phrase', () => { it('imports a seed phrase', async () => { - const [seedPhrase] = await findElements(driver, By.xpath(`//a[contains(text(), 'Import with seed phrase')]`)) - await seedPhrase.click() - await delay(regularDelayMs) - - const [seedTextArea] = await findElements(driver, By.css('textarea.import-account__secret-phrase')) + const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea')) await seedTextArea.sendKeys(testSeedPhrase) await delay(regularDelayMs) @@ -152,37 +128,18 @@ describe('Using MetaMask with an existing account', function () { 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 ToS', async () => { - // terms of use - const canClickThrough = await driver.findElement(By.css('.tou button')).isEnabled() - assert.equal(canClickThrough, false, 'disabled continue button') - const bottomOfTos = await findElement(driver, By.linkText('Attributions')) - await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos) - await delay(regularDelayMs) - const acceptTos = await findElement(driver, By.css('.tou button')) - await acceptTos.click() - await delay(regularDelayMs) - }) - - it('clicks through the privacy notice', async () => { - // privacy notice - const nextScreen = await findElement(driver, By.css('.tou button')) - await nextScreen.click() - await delay(regularDelayMs) - }) - - it('clicks through the phishing notice', async () => { - // phishing notice - const noticeElement = await driver.findElement(By.css('.markdown')) - await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement) - await delay(regularDelayMs) - const nextScreen = await findElement(driver, By.css('.tou button')) - await nextScreen.click() + 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) }) }) @@ -280,7 +237,7 @@ describe('Using MetaMask with an existing account', function () { }) describe('Send ETH from inside MetaMask', () => { - it('starts to send a transaction', async function () { + it('starts a send transaction', async function () { const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`)) await sendButton.click() await delay(regularDelayMs) @@ -291,7 +248,7 @@ describe('Using MetaMask with an existing account', function () { await inputAmount.sendKeys('1') // Set the gas limit - const configureGas = await findElement(driver, By.css('.send-v2__gas-fee-display button')) + const configureGas = await findElement(driver, By.css('.advanced-gas-options-btn')) await configureGas.click() await delay(regularDelayMs) @@ -373,36 +330,7 @@ describe('Using MetaMask with an existing account', function () { await connectButtons[0].click() await delay(regularDelayMs) const allWindows = await driver.getAllWindowHandles() - switch (process.env.SELENIUM_BROWSER) { - case 'chrome': - assert.equal(allWindows.length, 2) - break - default: - assert.equal(allWindows.length, 1) - } - }) - - it('should show the "Browser not supported" screen for non Chrome browsers', async () => { - if (process.env.SELENIUM_BROWSER !== 'chrome') { - const title = await findElements(driver, By.xpath(`//h3[contains(text(), 'Your Browser is not supported...')]`)) - assert.equal(title.length, 1) - - const downloadChromeButtons = await findElements(driver, By.xpath(`//button[contains(text(), 'Download Google Chrome')]`)) - assert.equal(downloadChromeButtons.length, 1) - - await downloadChromeButtons[0].click() - await delay(regularDelayMs) - - const [newUITab, downloadChromeTab] = await driver.getAllWindowHandles() - - await driver.switchTo().window(downloadChromeTab) - await delay(regularDelayMs) - const tabUrl = await driver.getCurrentUrl() - assert.equal(tabUrl, 'https://www.google.com/chrome/') - await driver.close() - await delay(regularDelayMs) - await driver.switchTo().window(newUITab) - } + assert.equal(allWindows.length, 2) }) }) }) diff --git a/test/e2e/beta/helpers.js b/test/e2e/beta/helpers.js index 08416857e..b6fc35e08 100644 --- a/test/e2e/beta/helpers.js +++ b/test/e2e/beta/helpers.js @@ -85,11 +85,22 @@ async function openNewPage (driver, url) { await delay(1000) } -async function waitUntilXWindowHandles (driver, x) { - const windowHandles = await driver.getAllWindowHandles() - if (windowHandles.length === x) return - await delay(1000) - return await waitUntilXWindowHandles(driver, x) +async function waitUntilXWindowHandles (driver, x, delayStep = 1000, timeout = 5000) { + let timeElapsed = 0 + async function _pollWindowHandles () { + const windowHandles = await driver.getAllWindowHandles() + if (windowHandles.length === x) { + return + } + await delay(delayStep) + timeElapsed += delayStep + if (timeElapsed > timeout) { + throw new Error('waitUntilXWindowHandles timed out polling window handles') + } else { + await _pollWindowHandles() + } + } + return await _pollWindowHandles() } async function switchToWindowWithTitle (driver, title, windowHandles) { @@ -109,6 +120,13 @@ async function switchToWindowWithTitle (driver, title, windowHandles) { } } +/** + * Closes all windows except those in the given list of exceptions + * @param {object} driver the WebDriver instance + * @param {string|Array<string>} exceptions the list of window handle exceptions + * @param {Array?} windowHandles the full list of window handles + * @returns {Promise<void>} + */ async function closeAllWindowHandlesExcept (driver, exceptions, windowHandles) { exceptions = typeof exceptions === 'string' ? [ exceptions ] : exceptions windowHandles = windowHandles || await driver.getAllWindowHandles() diff --git a/test/e2e/beta/metamask-beta-responsive-ui.spec.js b/test/e2e/beta/metamask-beta-responsive-ui.spec.js index b93563b25..6df1da051 100644 --- a/test/e2e/beta/metamask-beta-responsive-ui.spec.js +++ b/test/e2e/beta/metamask-beta-responsive-ui.spec.js @@ -18,6 +18,7 @@ const { loadExtension, verboseReportOnFailure, } = require('./helpers') +const fetchMockResponses = require('./fetch-mocks.js') describe('MetaMask', function () { let extensionId @@ -32,23 +33,50 @@ describe('MetaMask', function () { this.bail(true) before(async function () { + let extensionUrl switch (process.env.SELENIUM_BROWSER) { case 'chrome': { const extPath = path.resolve('dist/chrome') driver = buildChromeWebDriver(extPath, { responsive: true }) extensionId = await getExtensionIdChrome(driver) - await driver.get(`chrome-extension://${extensionId}/popup.html`) + await delay(largeDelayMs) + extensionUrl = `chrome-extension://${extensionId}/home.html` break } case 'firefox': { const extPath = path.resolve('dist/firefox') driver = buildFirefoxWebdriver({ responsive: true }) await installWebExt(driver, extPath) - await delay(700) + await delay(largeDelayMs) extensionId = await getExtensionIdFirefox(driver) - await driver.get(`moz-extension://${extensionId}/popup.html`) + 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); }' + ) }) afterEach(async function () { @@ -69,189 +97,111 @@ describe('MetaMask', function () { await driver.quit() }) - describe('New UI setup', async function () { - it('switches to first tab', async function () { - await delay(tinyDelayMs) - const [firstTab] = await driver.getAllWindowHandles() - await driver.switchTo().window(firstTab) - await delay(regularDelayMs) - }) - - it('selects the new UI option', async () => { - try { - const overlay = await findElement(driver, By.css('.full-flex-height')) - await driver.wait(until.stalenessOf(overlay)) - } catch (e) {} - - let button - try { - button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) - } catch (e) { - await loadExtension(driver, extensionId) + describe('Going through the first time flow', () => { + 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) - button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) - } - await button.click() - await delay(regularDelayMs) + }) - // Close all other tabs - const [tab0, tab1, tab2] = await driver.getAllWindowHandles() - await driver.switchTo().window(tab0) - await delay(tinyDelayMs) - - let selectedUrl = await driver.getCurrentUrl() - await delay(tinyDelayMs) - if (tab0 && selectedUrl.match(/popup.html/)) { - await closeAllWindowHandlesExcept(driver, tab0) - } else if (tab1) { - await driver.switchTo().window(tab1) - selectedUrl = await driver.getCurrentUrl() - await delay(tinyDelayMs) - if (selectedUrl.match(/popup.html/)) { - await closeAllWindowHandlesExcept(driver, tab1) - } else if (tab2) { - await driver.switchTo().window(tab2) - selectedUrl = await driver.getCurrentUrl() - selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2) - } - } else { - throw new Error('popup.html not found') - } - await delay(regularDelayMs) - const [appTab] = await driver.getAllWindowHandles() - await driver.switchTo().window(appTab) - await delay(tinyDelayMs) - - await loadExtension(driver, extensionId) - await delay(regularDelayMs) - - const continueBtn = await findElement(driver, By.css('.welcome-screen__button')) - await continueBtn.click() - await delay(regularDelayMs) - }) - }) - - describe('Going through the first time flow', () => { - it('accepts a secure password', async () => { - const passwordBox = await findElement(driver, By.css('.create-password #create-password')) - const passwordBoxConfirm = await findElement(driver, By.css('.create-password #confirm-password')) - const button = await findElement(driver, By.css('.create-password button')) - - await passwordBox.sendKeys('correct horse battery staple') - await passwordBoxConfirm.sendKeys('correct horse battery staple') - await button.click() - await delay(regularDelayMs) - }) + it('clicks the "Create New Wallet" option', async () => { + const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Create a Wallet')]`)) + customRpcButton.click() + await delay(largeDelayMs) + }) - it('clicks through the unique image screen', async () => { - const nextScreen = await findElement(driver, By.css('.unique-image button')) - await nextScreen.click() - await delay(regularDelayMs) - }) + it('clicks the "I agree" option on the metametrics opt-in screen', async () => { + const optOutButton = await findElement(driver, By.css('.btn-confirm')) + optOutButton.click() + await delay(largeDelayMs) + }) - it('clicks through the ToS', async () => { - // terms of use - const canClickThrough = await driver.findElement(By.css('.tou button')).isEnabled() - assert.equal(canClickThrough, false, 'disabled continue button') - const bottomOfTos = await findElement(driver, By.linkText('Attributions')) - await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos) - await delay(regularDelayMs) - const acceptTos = await findElement(driver, By.css('.tou button')) - driver.wait(until.elementIsEnabled(acceptTos)) - await acceptTos.click() - await delay(regularDelayMs) - }) + it('accepts a secure password', async () => { + const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password')) + const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password')) + const button = await findElement(driver, By.css('.first-time-flow__form button')) - it('clicks through the privacy notice', async () => { - // privacy notice - const nextScreen = await findElement(driver, By.css('.tou button')) - await nextScreen.click() - await delay(regularDelayMs) - }) + await passwordBox.sendKeys('correct horse battery staple') + await passwordBoxConfirm.sendKeys('correct horse battery staple') - it('clicks through the phishing notice', async () => { - // phishing notice - const noticeElement = await driver.findElement(By.css('.markdown')) - await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement) - await delay(regularDelayMs) - const nextScreen = await findElement(driver, By.css('.tou button')) - await nextScreen.click() - await delay(regularDelayMs) - }) + const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox')) + await tosCheckBox.click() - let seedPhrase + await button.click() + await delay(regularDelayMs) + }) - it('reveals the seed phrase', async () => { - const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button') - await driver.wait(until.elementLocated(byRevealButton, 10000)) - const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) - await revealSeedPhraseButton.click() - await delay(regularDelayMs) + let seedPhrase - seedPhrase = await driver.findElement(By.css('.backup-phrase__secret-words')).getText() - assert.equal(seedPhrase.split(' ').length, 12) - await delay(regularDelayMs) + it('reveals the seed phrase', async () => { + const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') + await driver.wait(until.elementLocated(byRevealButton, 10000)) + const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) + await revealSeedPhraseButton.click() + await delay(regularDelayMs) - const nextScreen = await findElement(driver, By.css('.backup-phrase button')) - await nextScreen.click() - await delay(regularDelayMs) - }) + seedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words')).getText() + assert.equal(seedPhrase.split(' ').length, 12) + await delay(regularDelayMs) - async function clickWordAndWait (word) { - const xpathClass = 'backup-phrase__confirm-seed-option backup-phrase__confirm-seed-option--unselected' - const xpath = `//button[@class='${xpathClass}' and contains(text(), '${word}')]` - const word0 = await findElement(driver, By.xpath(xpath), 10000) + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) + await nextScreen.click() + await delay(regularDelayMs) + }) - await word0.click() - await delay(tinyDelayMs) - } + async function clickWordAndWait (word) { + const xpath = `//div[contains(@class, 'confirm-seed-phrase__seed-word--shuffled') and not(contains(@class, 'confirm-seed-phrase__seed-word--selected')) and contains(text(), '${word}')]` + const word0 = await findElement(driver, By.xpath(xpath), 10000) - async function retypeSeedPhrase (words, wasReloaded, count = 0) { - try { - if (wasReloaded) { - const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button') - await driver.wait(until.elementLocated(byRevealButton, 10000)) - const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) - await revealSeedPhraseButton.click() - await delay(regularDelayMs) - - const nextScreen = await findElement(driver, By.css('.backup-phrase button')) - await nextScreen.click() - await delay(regularDelayMs) - } + await word0.click() + await delay(tinyDelayMs) + } - for (let i = 0; i < 12; i++) { - await clickWordAndWait(words[i]) - } - } catch (e) { - if (count > 2) { - throw e - } else { - await loadExtension(driver, extensionId) - await retypeSeedPhrase(words, true, count + 1) + async function retypeSeedPhrase (words, wasReloaded, count = 0) { + try { + if (wasReloaded) { + const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') + await driver.wait(until.elementLocated(byRevealButton, 10000)) + const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) + await revealSeedPhraseButton.click() + await delay(regularDelayMs) + + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) + await nextScreen.click() + await delay(regularDelayMs) + } + + for (let i = 0; i < 12; i++) { + await clickWordAndWait(words[i]) + } + } catch (e) { + if (count > 2) { + throw e + } else { + await loadExtension(driver, extensionId) + await retypeSeedPhrase(words, true, count + 1) + } } } - } - it('can retype the seed phrase', async () => { - const words = seedPhrase.split(' ') + it('can retype the seed phrase', async () => { + const words = seedPhrase.split(' ') - await retypeSeedPhrase(words) + await retypeSeedPhrase(words) - const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`)) - await confirm.click() - await delay(regularDelayMs) - }) + const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`)) + await confirm.click() + await delay(regularDelayMs) + }) - it('clicks through the deposit modal', async () => { - const byBuyModal = By.css('span .modal') - const buyModal = await driver.wait(until.elementLocated(byBuyModal)) - const closeModal = await findElement(driver, By.css('.page-container__header-close')) - await closeModal.click() - await driver.wait(until.stalenessOf(buyModal)) - 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('Show account information', () => { it('show account details dropdown menu', async () => { @@ -322,19 +272,24 @@ describe('MetaMask', function () { const inputValue = await inputAmount.getAttribute('value') assert.equal(inputValue, '1') + await delay(regularDelayMs) + }) + it('opens and closes the gas modal', async function () { // Set the gas limit - const configureGas = await findElement(driver, By.css('.send-v2__gas-fee-display button')) + const configureGas = await findElement(driver, By.css('.advanced-gas-options-btn')) await configureGas.click() await delay(regularDelayMs) const gasModal = await driver.findElement(By.css('span .modal')) - const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`)) + const save = await findElement(driver, By.css('.page-container__header-close-text')) await save.click() - await driver.wait(until.stalenessOf(gasModal)) + await driver.wait(until.stalenessOf(gasModal), 10000) await delay(regularDelayMs) + }) + it('clicks through to the confirm screen', async function () { // Continue to next screen const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`)) await nextScreen.click() diff --git a/test/e2e/beta/metamask-beta-ui.spec.js b/test/e2e/beta/metamask-beta-ui.spec.js index 3bfa1eaf2..2700d1656 100644 --- a/test/e2e/beta/metamask-beta-ui.spec.js +++ b/test/e2e/beta/metamask-beta-ui.spec.js @@ -22,6 +22,7 @@ const { verboseReportOnFailure, waitUntilXWindowHandles, } = require('./helpers') +const fetchMockResponses = require('./fetch-mocks.js') describe('MetaMask', function () { let extensionId @@ -37,23 +38,50 @@ describe('MetaMask', function () { this.bail(true) before(async function () { + let extensionUrl switch (process.env.SELENIUM_BROWSER) { case 'chrome': { const extPath = path.resolve('dist/chrome') driver = buildChromeWebDriver(extPath) extensionId = await getExtensionIdChrome(driver) - await driver.get(`chrome-extension://${extensionId}/popup.html`) + await delay(largeDelayMs) + extensionUrl = `chrome-extension://${extensionId}/home.html` break } case 'firefox': { const extPath = path.resolve('dist/firefox') driver = buildFirefoxWebdriver() await installWebExt(driver, extPath) - await delay(700) + await delay(largeDelayMs) extensionId = await getExtensionIdFirefox(driver) - await driver.get(`moz-extension://${extensionId}/popup.html`) + 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); }' + ) }) afterEach(async function () { @@ -74,137 +102,61 @@ describe('MetaMask', function () { await driver.quit() }) - describe('New UI setup', async function () { - it('switches to first tab', async function () { - await delay(tinyDelayMs) - const [firstTab] = await driver.getAllWindowHandles() - await driver.switchTo().window(firstTab) - await delay(regularDelayMs) + describe('Going through the first time flow', () => { + 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('selects the new UI option', async () => { - try { - const overlay = await findElement(driver, By.css('.full-flex-height')) - await driver.wait(until.stalenessOf(overlay)) - } catch (e) {} - - let button - try { - button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) - } catch (e) { - await loadExtension(driver, extensionId) - await delay(largeDelayMs) - button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) - } - await button.click() - await delay(regularDelayMs) - - // Close all other tabs - const [tab0, tab1, tab2] = await driver.getAllWindowHandles() - await driver.switchTo().window(tab0) - await delay(tinyDelayMs) - - let selectedUrl = await driver.getCurrentUrl() - await delay(tinyDelayMs) - if (tab0 && selectedUrl.match(/popup.html/)) { - await closeAllWindowHandlesExcept(driver, tab0) - } else if (tab1) { - await driver.switchTo().window(tab1) - selectedUrl = await driver.getCurrentUrl() - await delay(tinyDelayMs) - if (selectedUrl.match(/popup.html/)) { - await closeAllWindowHandlesExcept(driver, tab1) - } else if (tab2) { - await driver.switchTo().window(tab2) - selectedUrl = await driver.getCurrentUrl() - selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2) - } - } else { - throw new Error('popup.html not found') - } - await delay(regularDelayMs) - const [appTab] = await driver.getAllWindowHandles() - await driver.switchTo().window(appTab) - await delay(tinyDelayMs) - - await loadExtension(driver, extensionId) - await delay(regularDelayMs) + it('clicks the "Create New Wallet" option', async () => { + const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Create a Wallet')]`)) + customRpcButton.click() + await delay(largeDelayMs) + }) - const continueBtn = await findElement(driver, By.css('.welcome-screen__button')) - await continueBtn.click() - await delay(regularDelayMs) + 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) }) - }) - describe('Going through the first time flow', () => { it('accepts a secure password', async () => { - const passwordBox = await findElement(driver, By.css('.create-password #create-password')) - const passwordBoxConfirm = await findElement(driver, By.css('.create-password #confirm-password')) - const button = await findElement(driver, By.css('.create-password button')) + const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password')) + const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password')) + const button = await findElement(driver, By.css('.first-time-flow__form button')) await passwordBox.sendKeys('correct horse battery staple') await passwordBoxConfirm.sendKeys('correct horse battery staple') - await button.click() - await delay(regularDelayMs) - }) - - it('clicks through the unique image screen', async () => { - const nextScreen = await findElement(driver, By.css('.unique-image button')) - await nextScreen.click() - await delay(regularDelayMs) - }) - it('clicks through the ToS', async () => { - // terms of use - const canClickThrough = await driver.findElement(By.css('.tou button')).isEnabled() - assert.equal(canClickThrough, false, 'disabled continue button') - const bottomOfTos = await findElement(driver, By.linkText('Attributions')) - await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos) - await delay(regularDelayMs) - const acceptTos = await findElement(driver, By.css('.tou button')) - driver.wait(until.elementIsEnabled(acceptTos)) - await acceptTos.click() - await delay(regularDelayMs) - }) - - it('clicks through the privacy notice', async () => { - // privacy notice - const nextScreen = await findElement(driver, By.css('.tou button')) - await nextScreen.click() - await delay(regularDelayMs) - }) + const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox')) + await tosCheckBox.click() - it('clicks through the phishing notice', async () => { - // phishing notice - const noticeElement = await driver.findElement(By.css('.markdown')) - await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement) - await delay(regularDelayMs) - const nextScreen = await findElement(driver, By.css('.tou button')) - await nextScreen.click() + await button.click() await delay(regularDelayMs) }) let seedPhrase it('reveals the seed phrase', async () => { - const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button') + const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') await driver.wait(until.elementLocated(byRevealButton, 10000)) const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) await revealSeedPhraseButton.click() await delay(regularDelayMs) - seedPhrase = await driver.findElement(By.css('.backup-phrase__secret-words')).getText() + seedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words')).getText() assert.equal(seedPhrase.split(' ').length, 12) await delay(regularDelayMs) - const nextScreen = await findElement(driver, By.css('.backup-phrase button')) + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) await nextScreen.click() await delay(regularDelayMs) }) async function clickWordAndWait (word) { - const xpathClass = 'backup-phrase__confirm-seed-option backup-phrase__confirm-seed-option--unselected' - const xpath = `//button[@class='${xpathClass}' and contains(text(), '${word}')]` + const xpath = `//div[contains(@class, 'confirm-seed-phrase__seed-word--shuffled') and not(contains(@class, 'confirm-seed-phrase__seed-word--selected')) and contains(text(), '${word}')]` const word0 = await findElement(driver, By.xpath(xpath), 10000) await word0.click() @@ -214,13 +166,13 @@ describe('MetaMask', function () { async function retypeSeedPhrase (words, wasReloaded, count = 0) { try { if (wasReloaded) { - const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button') + const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button') await driver.wait(until.elementLocated(byRevealButton, 10000)) const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) await revealSeedPhraseButton.click() await delay(regularDelayMs) - const nextScreen = await findElement(driver, By.css('.backup-phrase button')) + const nextScreen = await findElement(driver, By.css('button.first-time-flow__button')) await nextScreen.click() await delay(regularDelayMs) } @@ -248,12 +200,10 @@ describe('MetaMask', function () { await delay(regularDelayMs) }) - it('clicks through the deposit modal', async () => { - const byBuyModal = By.css('span .modal') - const buyModal = await driver.wait(until.elementLocated(byBuyModal)) - const closeModal = await findElement(driver, By.css('.page-container__header-close')) - await closeModal.click() - await driver.wait(until.stalenessOf(buyModal)) + 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) }) }) @@ -283,7 +233,11 @@ describe('MetaMask', function () { await customRpcButton.click() await delay(regularDelayMs) - const privacyToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(10) .settings-page__content-item-col > div')) + const securityTab = await findElement(driver, By.xpath(`//div[contains(text(), 'Security & Privacy')]`)) + await securityTab.click() + await delay(regularDelayMs) + + const privacyToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(1) .settings-page__content-item-col > div')) await privacyToggle.click() await delay(largeDelayMs * 2) }) @@ -381,8 +335,88 @@ describe('MetaMask', function () { }) }) - describe('Send ETH from inside MetaMask', () => { - it('starts to send a transaction', async function () { + describe('Send ETH from inside MetaMask using default gas', () => { + it('starts a send transaction', async function () { + const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`)) + await sendButton.click() + await delay(regularDelayMs) + + const inputAddress = await findElement(driver, By.css('input[placeholder="Recipient Address"]')) + const inputAmount = await findElement(driver, By.css('.unit-input__input')) + await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970') + await inputAmount.sendKeys('1') + + const inputValue = await inputAmount.getAttribute('value') + assert.equal(inputValue, '1') + await delay(regularDelayMs) + + // Continue to next screen + const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`)) + await nextScreen.click() + await delay(regularDelayMs) + }) + + it('confirms the transaction', async function () { + const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`)) + await confirmButton.click() + await delay(largeDelayMs) + }) + + it('finds the transaction in the transactions list', async function () { + const transactions = await findElements(driver, By.css('.transaction-list-item')) + assert.equal(transactions.length, 1) + + if (process.env.SELENIUM_BROWSER !== 'firefox') { + const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) + await driver.wait(until.elementTextMatches(txValues, /-1\s*ETH/), 10000) + } + }) + }) + + describe('Send ETH from inside MetaMask using fast gas option', () => { + it('starts a send transaction', async function () { + const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`)) + await sendButton.click() + await delay(regularDelayMs) + + const inputAddress = await findElement(driver, By.css('input[placeholder="Recipient Address"]')) + const inputAmount = await findElement(driver, By.css('.unit-input__input')) + await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970') + await inputAmount.sendKeys('1') + + const inputValue = await inputAmount.getAttribute('value') + assert.equal(inputValue, '1') + + // Set the gas price + const fastGas = await findElement(driver, By.xpath(`//button/div/div[contains(text(), "Fast")]`)) + await fastGas.click() + await delay(regularDelayMs) + + // Continue to next screen + const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`)) + await nextScreen.click() + await delay(regularDelayMs) + }) + + it('confirms the transaction', async function () { + const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`)) + await confirmButton.click() + await delay(largeDelayMs) + }) + + it('finds the transaction in the transactions list', async function () { + const transactions = await findElements(driver, By.css('.transaction-list-item')) + assert.equal(transactions.length, 2) + + if (process.env.SELENIUM_BROWSER !== 'firefox') { + const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) + await driver.wait(until.elementTextMatches(txValues, /-1\s*ETH/), 10000) + } + }) + }) + + describe('Send ETH from inside MetaMask using advanced gas modal', () => { + it('starts a send transaction', async function () { const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`)) await sendButton.click() await delay(regularDelayMs) @@ -396,12 +430,11 @@ describe('MetaMask', function () { assert.equal(inputValue, '1') // Set the gas limit - const configureGas = await findElement(driver, By.css('.send-v2__gas-fee-display button')) + const configureGas = await findElement(driver, By.css('.advanced-gas-options-btn')) await configureGas.click() await delay(regularDelayMs) const gasModal = await driver.findElement(By.css('span .modal')) - const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`)) await save.click() await driver.wait(until.stalenessOf(gasModal)) @@ -421,7 +454,7 @@ describe('MetaMask', function () { it('finds the transaction in the transactions list', async function () { const transactions = await findElements(driver, By.css('.transaction-list-item')) - assert.equal(transactions.length, 1) + assert.equal(transactions.length, 3) if (process.env.SELENIUM_BROWSER !== 'firefox') { const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) @@ -430,12 +463,43 @@ describe('MetaMask', function () { }) }) - describe('Send ETH from dapp', () => { + describe('Send ETH from dapp using advanced gas controls', () => { let windowHandles let extension let popup let dapp + it('goes to the settings screen', async () => { + await driver.findElement(By.css('.account-menu__icon')).click() + await delay(regularDelayMs) + + const settingsButton = await findElement(driver, By.xpath(`//div[contains(text(), 'Settings')]`)) + settingsButton.click() + + // await findElement(driver, By.css('.tab-bar')) + + const advancedTab = await findElement(driver, By.xpath(`//div[contains(text(), 'Advanced')]`)) + await advancedTab.click() + await delay(regularDelayMs) + + const showConversionToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(7) .settings-page__content-item-col > div')) + await showConversionToggle.click() + + const advancedGasTitle = await findElement(driver, By.xpath(`//span[contains(text(), 'Advanced gas controls')]`)) + await driver.executeScript('arguments[0].scrollIntoView(true)', advancedGasTitle) + + const advancedGasToggle = await findElement(driver, By.css('.settings-page__content-row:nth-of-type(5) .settings-page__content-item-col > div')) + await advancedGasToggle.click() + windowHandles = await driver.getAllWindowHandles() + extension = windowHandles[0] + await closeAllWindowHandlesExcept(driver, [extension]) + + const metamaskHomeButton = await findElement(driver, By.css('.app-header__logo-container')) + await metamaskHomeButton.click() + + await delay(largeDelayMs) + }) + it('starts a send transaction inside the dapp', async () => { await openNewPage(driver, 'http://127.0.0.1:8080/') await delay(regularDelayMs) @@ -450,12 +514,12 @@ describe('MetaMask', function () { await delay(regularDelayMs) const approveButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`)) await approveButton.click() - }) - it('initiates a send from the dapp', async () => { await driver.switchTo().window(dapp) await delay(regularDelayMs) + }) + it('initiates a send from the dapp', async () => { const send3eth = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`), 10000) await send3eth.click() await delay(5000) @@ -466,6 +530,17 @@ describe('MetaMask', function () { await assertElementNotPresent(webdriver, driver, By.xpath(`//li[contains(text(), 'Data')]`)) + const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.advanced-gas-inputs__gas-edit-row__input')) + await gasPriceInput.clear() + await delay(tinyDelayMs) + + await gasPriceInput.sendKeys(Key.BACK_SPACE) + await gasPriceInput.sendKeys(Key.BACK_SPACE) + await gasPriceInput.sendKeys('10') + await delay(tinyDelayMs) + await gasLimitInput.sendKeys('5') + await delay(tinyDelayMs) + const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`), 10000) await confirmButton.click() await delay(regularDelayMs) @@ -475,12 +550,177 @@ describe('MetaMask', function () { await delay(regularDelayMs) }) + let txValues + it('finds the transaction in the transactions list', async function () { const transactions = await findElements(driver, By.css('.transaction-list-item')) - assert.equal(transactions.length, 2) + assert.equal(transactions.length, 4) - const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txValues, /-3\s*ETH/), 10000) + txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) + await driver.wait(until.elementTextMatches(txValues[0], /-3\s*ETH/), 10000) + }) + + it('the transaction has the expected gas price', async function () { + await delay(largeDelayMs) + let txGasPriceLabels + let txGasPrices + try { + await txValues[0].click() + txGasPrices = await findElements(driver, By.css('.transaction-breakdown__value')) + txGasPriceLabels = await findElements(driver, By.css('.transaction-breakdown-row__title')) + txGasPrices = await findElements(driver, By.css('.transaction-breakdown__value')) + await driver.wait(until.elementTextMatches(txGasPrices[3], /^10$/), 10000) + } catch (e) { + console.log(e.message) + txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) + await txValues[0].click() + txGasPriceLabels = await findElements(driver, By.css('.transaction-breakdown-row__title')) + txGasPrices = await findElements(driver, By.css('.transaction-breakdown__value')) + await driver.wait(until.elementTextMatches(txGasPrices[3], /^10$/), 10000) + } + assert(txGasPriceLabels[2]) + + await txValues[0].click() + }) + }) + + describe('Navigate transactions', () => { + it('adds multiple transactions', async () => { + await delay(regularDelayMs) + + await waitUntilXWindowHandles(driver, 2) + const windowHandles = await driver.getAllWindowHandles() + const extension = windowHandles[0] + const dapp = windowHandles[1] + + await driver.switchTo().window(dapp) + await delay(regularDelayMs) + + const send3eth = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`), 10000) + await send3eth.click() + await delay(regularDelayMs) + + const contractDeployment = await findElement(driver, By.xpath(`//button[contains(text(), 'Deploy Contract')]`), 10000) + await contractDeployment.click() + await delay(regularDelayMs) + + await send3eth.click() + await contractDeployment.click() + await delay(regularDelayMs) + + await driver.switchTo().window(extension) + await delay(regularDelayMs) + + const transactions = await findElements(driver, By.css('.transaction-list-item')) + await transactions[3].click() + await delay(regularDelayMs) + }) + + it('navigates the transactions', async () => { + let navigateTxButtons = await findElements(driver, By.css('.confirm-page-container-navigation__arrow'), 20000) + assert.equal(navigateTxButtons.length, 4, 'navigation button present') + + await navigateTxButtons[2].click() + let navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + let navigationText = await navigationElement.getText() + assert.equal(navigationText.includes('2'), true, 'changed transaction right') + + navigateTxButtons = await findElements(driver, By.css('.confirm-page-container-navigation__arrow')) + await navigateTxButtons[2].click() + navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + navigationText = await navigationElement.getText() + assert.equal(navigationText.includes('3'), true, 'changed transaction right') + + navigateTxButtons = await findElements(driver, By.css('.confirm-page-container-navigation__arrow')) + await navigateTxButtons[2].click() + navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + navigationText = await navigationElement.getText() + assert.equal(navigationText.includes('4'), true, 'changed transaction right') + + navigateTxButtons = await findElements(driver, By.css('.confirm-page-container-navigation__arrow')) + await navigateTxButtons[0].click() + navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + navigationText = await navigationElement.getText() + assert.equal(navigationText.includes('1'), true, 'navigate to first transaction') + + navigateTxButtons = await findElements(driver, By.css('.confirm-page-container-navigation__arrow')) + await navigateTxButtons[3].click() + navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + navigationText = await navigationElement.getText() + assert.equal(navigationText.split('4').length, 3, 'navigate to last transaction') + + navigateTxButtons = await findElements(driver, By.css('.confirm-page-container-navigation__arrow')) + await navigateTxButtons[1].click() + navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + navigationText = await navigationElement.getText() + assert.equal(navigationText.includes('3'), true, 'changed transaction left') + + navigateTxButtons = await findElements(driver, By.css('.confirm-page-container-navigation__arrow')) + await navigateTxButtons[1].click() + navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + navigationText = await navigationElement.getText() + assert.equal(navigationText.includes('2'), true, 'changed transaction left') + }) + + it('adds a transaction while confirm screen is in focus', async () => { + let navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + let navigationText = await navigationElement.getText() + assert.equal(navigationText.includes('2'), true, 'second transaction in focus') + + const windowHandles = await driver.getAllWindowHandles() + const extension = windowHandles[0] + const dapp = windowHandles[1] + + await driver.switchTo().window(dapp) + await delay(regularDelayMs) + + const send3eth = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`), 10000) + await send3eth.click() + await delay(regularDelayMs) + + await driver.switchTo().window(extension) + await delay(regularDelayMs) + + navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + navigationText = await navigationElement.getText() + assert.equal(navigationText.includes('3'), true, 'correct transaction in focus') + }) + + it('confirms a transaction', async () => { + const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`), 10000) + await confirmButton.click() + await delay(regularDelayMs) + + const navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + const navigationText = await navigationElement.getText() + assert.equal(navigationText.includes('4'), true, 'transaction confirmed') + }) + + it('rejects a transaction', async () => { + await delay(tinyDelayMs / 2) + const rejectButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Reject')]`), 10000) + await delay(tinyDelayMs / 2) + await rejectButton.click() + await delay(regularDelayMs) + + const navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation')) + await delay(tinyDelayMs / 2) + const navigationText = await navigationElement.getText() + await delay(tinyDelayMs / 2) + assert.equal(navigationText.includes('3'), true, 'transaction rejected') + }) + + it('rejects the rest of the transactions', async () => { + const rejectAllButton = await findElement(driver, By.xpath(`//a[contains(text(), 'Reject 3')]`), 10000) + await rejectAllButton.click() + await delay(regularDelayMs) + + const rejectButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Reject All')]`), 10000) + await rejectButton.click() + await delay(largeDelayMs * 2) + + const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item')) + assert.equal(confirmedTxes.length, 5, '5 transactions present') }) }) @@ -529,9 +769,9 @@ describe('MetaMask', function () { await confirmButton.click() await delay(largeDelayMs) - driver.wait(async () => { + await driver.wait(async () => { const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item')) - return confirmedTxes.length === 3 + return confirmedTxes.length === 6 }, 10000) const txAction = await findElements(driver, By.css('.transaction-list-item__action')) @@ -568,9 +808,12 @@ describe('MetaMask', function () { await delay(regularDelayMs) const gasModal = await findElement(driver, By.css('span .modal')) - await driver.wait(until.elementLocated(By.css('.customize-gas__title')), 10000) + await delay(regularDelayMs) + const modalTabs = await findElements(driver, By.css('.page-container__tab')) + await modalTabs[1].click() + await delay(regularDelayMs) - const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.customize-gas-input')) + const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.advanced-tab__gas-edit-row__input')) await gasPriceInput.clear() await gasPriceInput.sendKeys('10') await gasLimitInput.clear() @@ -586,9 +829,9 @@ describe('MetaMask', function () { await confirmButton.click() await delay(regularDelayMs) - driver.wait(async () => { + await driver.wait(async () => { const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item')) - return confirmedTxes.length === 4 + return confirmedTxes.length === 7 }, 10000) const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) @@ -618,9 +861,9 @@ describe('MetaMask', function () { await confirmButton.click() await delay(regularDelayMs) - driver.wait(async () => { + await driver.wait(async () => { const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item')) - return confirmedTxes.length === 5 + return confirmedTxes.length === 8 }, 10000) const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) @@ -634,9 +877,9 @@ describe('MetaMask', function () { const balance = await findElement(driver, By.css('.transaction-view-balance__primary-balance')) await delay(regularDelayMs) if (process.env.SELENIUM_BROWSER !== 'firefox') { - await driver.wait(until.elementTextMatches(balance, /^92.*\s*ETH.*$/), 10000) + await driver.wait(until.elementTextMatches(balance, /^87.*\s*ETH.*$/), 10000) const tokenAmount = await balance.getText() - assert.ok(/^92.*\s*ETH.*$/.test(tokenAmount)) + assert.ok(/^87.*\s*ETH.*$/.test(tokenAmount)) await delay(regularDelayMs) } }) @@ -661,6 +904,22 @@ describe('MetaMask', function () { await driver.switchTo().window(popup) await delay(regularDelayMs) + const configureGas = await driver.wait(until.elementLocated(By.css('.confirm-detail-row__header-text--edit')), 10000) + await configureGas.click() + await delay(regularDelayMs) + + const advancedTabButton = await driver.wait(until.elementLocated(By.xpath(`//li[contains(text(), 'Advanced')]`)), 10000) + await advancedTabButton.click() + await delay(tinyDelayMs) + + const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.advanced-tab__gas-edit-row__input')) + assert(gasPriceInput.getAttribute('value'), 20) + assert(gasLimitInput.getAttribute('value'), 4700000) + + const saveButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`)) + await saveButton.click() + await delay(regularDelayMs) + const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`)) await confirmButton.click() await delay(regularDelayMs) @@ -704,7 +963,7 @@ describe('MetaMask', function () { }) it('renders the balance for the new token', async () => { - const balance = await findElement(driver, By.css('.transaction-view-balance .transaction-view-balance__token-balance')) + const balance = await findElement(driver, By.css('.transaction-view-balance .transaction-view-balance__primary-balance')) await driver.wait(until.elementTextMatches(balance, /^100\s*TST\s*$/)) const tokenAmount = await balance.getText() assert.ok(/^100\s*TST\s*$/.test(tokenAmount)) @@ -725,15 +984,16 @@ describe('MetaMask', function () { await inputAmount.sendKeys('50') // Set the gas limit - const configureGas = await findElement(driver, By.css('.send-v2__gas-fee-display button')) + const configureGas = await findElement(driver, By.css('.advanced-gas-options-btn')) await configureGas.click() await delay(regularDelayMs) gasModal = await driver.findElement(By.css('span .modal')) + await delay(regularDelayMs) }) - it('opens customizes gas modal', async () => { - await driver.wait(until.elementLocated(By.css('.send-v2__customize-gas__title'))) + it('opens customize gas modal', async () => { + await driver.wait(until.elementLocated(By.css('.page-container__title'))) const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`)) await save.click() await delay(regularDelayMs) @@ -785,7 +1045,7 @@ describe('MetaMask', function () { await driver.wait(until.elementTextMatches(txValues[0], /-50\s*TST/), 10000) } - driver.wait(async () => { + await driver.wait(async () => { const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item')) return confirmedTxes.length === 1 }, 10000) @@ -801,7 +1061,6 @@ describe('MetaMask', function () { const windowHandles = await driver.getAllWindowHandles() const extension = windowHandles[0] const dapp = await switchToWindowWithTitle(driver, 'E2E Test Dapp', windowHandles) - await closeAllWindowHandlesExcept(driver, [extension, dapp]) await delay(regularDelayMs) await driver.switchTo().window(dapp) @@ -810,7 +1069,6 @@ describe('MetaMask', function () { const transferTokens = await findElement(driver, By.xpath(`//button[contains(text(), 'Transfer Tokens')]`)) await transferTokens.click() - await closeAllWindowHandlesExcept(driver, [extension, dapp]) await driver.switchTo().window(extension) await delay(largeDelayMs) @@ -829,31 +1087,36 @@ describe('MetaMask', function () { }) it('customizes gas', async () => { - await driver.wait(until.elementLocated(By.css('.customize-gas__title'))) + const modalTabs = await findElements(driver, By.css('.page-container__tab')) + await modalTabs[1].click() + await delay(regularDelayMs) - const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.customize-gas-input')) + const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.advanced-tab__gas-edit-row__input')) await gasPriceInput.clear() await delay(tinyDelayMs) + + await gasPriceInput.sendKeys(Key.BACK_SPACE) + await gasPriceInput.sendKeys(Key.BACK_SPACE) await gasPriceInput.sendKeys('10') await delay(tinyDelayMs) await gasLimitInput.clear() await delay(tinyDelayMs) await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'a')) + await gasLimitInput.sendKeys(Key.BACK_SPACE) + await gasLimitInput.sendKeys(Key.BACK_SPACE) + await gasLimitInput.sendKeys(Key.BACK_SPACE) + await gasLimitInput.sendKeys(Key.BACK_SPACE) + await gasLimitInput.sendKeys(Key.BACK_SPACE) await gasLimitInput.sendKeys('60000') await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'e')) - // Needed for different behaviour of input in different versions of firefox - const gasLimitInputValue = await gasLimitInput.getAttribute('value') - if (gasLimitInputValue === '600001') { - await gasLimitInput.sendKeys(Key.BACK_SPACE) - } - - const save = await findElement(driver, By.css('.customize-gas__save')) + const save = await findElement(driver, By.css('.page-container__footer-button')) await save.click() await driver.wait(until.stalenessOf(gasModal)) const gasFeeInputs = await findElements(driver, By.css('.confirm-detail-row__primary')) - assert.equal(await gasFeeInputs[0].getText(), '0.0006') + const renderedGasFee = await gasFeeInputs[0].getText() + assert.equal(renderedGasFee, '0.0006') }) it('submits the transaction', async function () { @@ -863,15 +1126,16 @@ describe('MetaMask', function () { }) it('finds the transaction in the transactions list', async function () { - driver.wait(async () => { + await driver.wait(async () => { const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item')) return confirmedTxes.length === 2 }, 10000) + await delay(regularDelayMs) const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) await driver.wait(until.elementTextMatches(txValues[0], /-7\s*TST/)) const txStatuses = await findElements(driver, By.css('.transaction-list-item__action')) - await driver.wait(until.elementTextMatches(txStatuses[0], /Sent\sToken/)) + await driver.wait(until.elementTextMatches(txStatuses[0], /Sent\sToken/), 10000) const walletBalance = await findElement(driver, By.css('.wallet-balance')) await walletBalance.click() @@ -883,8 +1147,8 @@ describe('MetaMask', function () { // test cancelled on firefox until https://github.com/mozilla/geckodriver/issues/906 is resolved, // or possibly until we use latest version of firefox in the tests if (process.env.SELENIUM_BROWSER !== 'firefox') { - const tokenBalanceAmount = await findElement(driver, By.css('.transaction-view-balance__token-balance')) - assert.equal(await tokenBalanceAmount.getText(), '43 TST') + const tokenBalanceAmount = await findElements(driver, By.css('.transaction-view-balance__primary-balance')) + await driver.wait(until.elementTextMatches(tokenBalanceAmount[0], /43\s*TST/), 10000) } }) }) @@ -908,7 +1172,7 @@ describe('MetaMask', function () { await driver.switchTo().window(extension) await delay(regularDelayMs) - driver.wait(async () => { + await driver.wait(async () => { const pendingTxes = await findElements(driver, By.css('.transaction-list__pending-transactions .transaction-list-item')) return pendingTxes.length === 1 }, 10000) @@ -952,9 +1216,11 @@ describe('MetaMask', function () { }) it('customizes gas', async () => { - await driver.wait(until.elementLocated(By.css('.customize-gas__title'))) + const modalTabs = await findElements(driver, By.css('.page-container__tab')) + await modalTabs[1].click() + await delay(regularDelayMs) - const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.customize-gas-input')) + const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.advanced-tab__gas-edit-row__input')) await gasPriceInput.clear() await delay(tinyDelayMs) await gasPriceInput.sendKeys('10') @@ -971,7 +1237,7 @@ describe('MetaMask', function () { await gasLimitInput.sendKeys(Key.BACK_SPACE) } - const save = await findElement(driver, By.css('.customize-gas__save')) + const save = await findElement(driver, By.css('.page-container__footer-button')) await save.click() await driver.wait(until.stalenessOf(gasModal)) @@ -986,7 +1252,7 @@ describe('MetaMask', function () { }) it('finds the transaction in the transactions list', async function () { - driver.wait(async () => { + await driver.wait(async () => { const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item')) return confirmedTxes.length === 3 }, 10000) @@ -1044,7 +1310,7 @@ describe('MetaMask', function () { }) it('renders the balance for the chosen token', async () => { - const balance = await findElement(driver, By.css('.transaction-view-balance__token-balance')) + const balance = await findElement(driver, By.css('.transaction-view-balance__primary-balance')) await driver.wait(until.elementTextMatches(balance, /0\s*BAT/)) await delay(regularDelayMs) }) diff --git a/test/e2e/beta/run-drizzle.sh b/test/e2e/beta/run-drizzle.sh index bfb7e6fdb..0799b5a65 100755 --- a/test/e2e/beta/run-drizzle.sh +++ b/test/e2e/beta/run-drizzle.sh @@ -4,17 +4,28 @@ set -e set -u set -o pipefail -export PATH="$PATH:./node_modules/.bin" - npm run ganache:start -- -b 2 >> /dev/null 2>&1 & +npm_run_ganache_start_pid=$! sleep 5 -cd test/e2e/beta/ -rm -rf drizzle-test -mkdir drizzle-test && cd drizzle-test -sudo npm install -g truffle -truffle unbox drizzle + +pushd "$(mktemp -d)" +npm install --no-package-lock truffle +truffle="$(npm bin)/truffle" +$truffle unbox drizzle echo "Deploying contracts for Drizzle test..." -truffle compile && truffle migrate +$truffle compile +$truffle migrate + BROWSER=none npm start >> /dev/null 2>&1 & -cd ../../../../ -mocha test/e2e/beta/drizzle.spec +npm_start_pid=$! + +popd +if ! mocha test/e2e/beta/drizzle.spec +then + test_status=1 +fi + +! kill -15 $npm_run_ganache_start_pid +! kill -15 $npm_start_pid +! wait $npm_run_ganache_start_pid $npm_start_pid +exit ${test_status:-} diff --git a/test/e2e/func.js b/test/e2e/func.js index 5301d78ae..dfad8466c 100644 --- a/test/e2e/func.js +++ b/test/e2e/func.js @@ -85,7 +85,7 @@ function buildFirefoxWebdriver (opts = {}) { async function getExtensionIdChrome (driver) { await driver.get('chrome://extensions') - const extensionId = await driver.executeScript('return document.querySelector("extensions-manager").shadowRoot.querySelector("extensions-view-manager extensions-item-list").shadowRoot.querySelector("extensions-item:nth-child(2)").getAttribute("id")') + const extensionId = await driver.executeScript('return document.querySelector("extensions-manager").shadowRoot.querySelector("extensions-item-list").shadowRoot.querySelector("extensions-item:nth-child(2)").getAttribute("id")') return extensionId } diff --git a/test/integration/lib/confirm-sig-requests.js b/test/integration/lib/confirm-sig-requests.js index 9c2ad7cf4..e4540c4f2 100644 --- a/test/integration/lib/confirm-sig-requests.js +++ b/test/integration/lib/confirm-sig-requests.js @@ -3,6 +3,7 @@ const { timeout, queryAsync, } = require('../../lib/util') +const fetchMockResponses = require('../../e2e/beta/fetch-mocks.js') QUnit.module('confirm sig requests') @@ -19,10 +20,17 @@ async function runConfirmSigRequestsTest (assert, done) { selectState.val('confirm sig requests') reactTriggerChange(selectState[0]) + global.fetch = (...args) => { + if (args[0].match(/chromeextensionmm/)) { + return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.metametrics)) }) + } + return window.fetch(...args) + } + const pendingRequestItem = $.find('.transaction-list-item .transaction-list-item__grid') - if (pendingRequestItem[0]) { - pendingRequestItem[0].click() + if (pendingRequestItem[2]) { + pendingRequestItem[2].click() } await timeout(1000) diff --git a/test/integration/lib/currency-localization.js b/test/integration/lib/currency-localization.js index f6b751ba2..cd10efa30 100644 --- a/test/integration/lib/currency-localization.js +++ b/test/integration/lib/currency-localization.js @@ -4,6 +4,7 @@ const { queryAsync, findAsync, } = require('../../lib/util') +const fetchMockResponses = require('../../e2e/beta/fetch-mocks.js') QUnit.module('currency localization') @@ -19,6 +20,14 @@ async function runCurrencyLocalizationTest (assert, done) { console.log('*** start runCurrencyLocalizationTest') const selectState = await queryAsync($, 'select') selectState.val('currency localization') + + global.fetch = (...args) => { + if (args[0].match(/chromeextensionmm/)) { + return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.metametrics)) }) + } + return window.fetch(...args) + } + await timeout(1000) reactTriggerChange(selectState[0]) await timeout(1000) diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js deleted file mode 100644 index 8cacd7f14..000000000 --- a/test/integration/lib/first-time.js +++ /dev/null @@ -1,117 +0,0 @@ -const reactTriggerChange = require('react-trigger-change') -const PASSWORD = 'password123' -const runMascaraFirstTimeTest = require('./mascara-first-time') -const { - timeout, - findAsync, -} = require('../../lib/util') - -QUnit.module('first time usage') - -QUnit.test('render init screen', (assert) => { - const done = assert.async() - runFirstTimeUsageTest(assert).then(done).catch((err) => { - assert.notOk(err, `Error was thrown: ${err.stack}`) - done() - }) -}) - -async function runFirstTimeUsageTest(assert, done) { - if (window.METAMASK_PLATFORM_TYPE === 'mascara') { - return runMascaraFirstTimeTest(assert, done) - } - - const selectState = $('select') - selectState.val('first time') - reactTriggerChange(selectState[0]) - - const app = $('#app-content') - - // Selects new ui - const tryNewUIButton = (await findAsync(app, 'button.negative'))[0] - tryNewUIButton.click() - await timeout() - - // recurse notices - while (true) { - const button = await findAsync(app, 'button') - if (button.html() === 'Accept') { - // still notices to accept - const termsPageRaw = await findAsync(app, '.markdown') - const termsPage = (await findAsync(app, '.markdown'))[0] - console.log('termsPageRaw', termsPageRaw) - termsPage.scrollTop = termsPage.scrollHeight - console.log('Clearing notice') - button.click() - } else { - // exit loop - console.log('No more notices...') - break - } - } - - // Scroll through terms - const title = (await findAsync(app, 'h1'))[0] - assert.equal(title.textContent, 'MetaMask', 'title screen') - - // enter password - const pwBox = (await findAsync(app, '#password-box'))[0] - const confBox = (await findAsync(app, '#password-box-confirm'))[0] - pwBox.value = PASSWORD - confBox.value = PASSWORD - - // create vault - const createButton = (await findAsync(app, 'button.primary'))[0] - createButton.click() - - await timeout() - const created = (await findAsync(app, 'h3'))[0] - assert.equal(created.textContent, 'Vault Created', 'Vault created screen') - - // Agree button - const button = (await findAsync(app, 'button'))[0] - assert.ok(button, 'button present') - button.click() - - const detail = (await findAsync(app, '.account-detail-section'))[0] - assert.ok(detail, 'Account detail section loaded.') - - const sandwich = (await findAsync(app, '.sandwich-expando'))[0] - sandwich.click() - - const menu = (await findAsync(app, '.menu-droppo'))[0] - const children = menu.children - const logout = children[2] - assert.ok(logout, 'Lock menu item found') - logout.click() - - const pwBox2 = (await findAsync(app, '#password-box'))[0] - pwBox2.value = PASSWORD - - const createButton2 = (await findAsync(app, 'button.primary'))[0] - createButton2.click() - - const detail2 = (await findAsync(app, '.account-detail-section'))[0] - assert.ok(detail2, 'Account detail section loaded again.') - - // open account settings dropdown - const qrButton = (await findAsync(app, '.fa.fa-ellipsis-h'))[0] - qrButton.click() - - // qr code item - const qrButton2 = (await findAsync(app, '.dropdown-menu-item'))[1] - qrButton2.click() - - const qrHeader = (await findAsync(app, '.qr-header'))[0] - const qrContainer = (await findAsync(app, '#qr-container'))[0] - assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.') - assert.ok(qrContainer, 'QR Container found') - - const networkMenu = (await findAsync(app, '.network-indicator'))[0] - networkMenu.click() - - const networkMenu2 = (await findAsync(app, '.network-indicator'))[0] - const children2 = networkMenu2.children - children2.length[3] - assert.ok(children2, 'All network options present') -} diff --git a/test/integration/lib/mascara-first-time.js b/test/integration/lib/mascara-first-time.js deleted file mode 100644 index 6756b83f9..000000000 --- a/test/integration/lib/mascara-first-time.js +++ /dev/null @@ -1,133 +0,0 @@ -const PASSWORD = 'password123' -const { - timeout, - findAsync, - queryAsync, -} = require('../../lib/util') - -async function runFirstTimeUsageTest (assert, done) { - await timeout(4000) - - const app = await queryAsync($, '#app-content') - - // Used to set values on TextField input component - const nativeInputValueSetter = Object.getOwnPropertyDescriptor( - window.HTMLInputElement.prototype, 'value' - ).set - - await skipNotices(app) - - const welcomeButton = (await findAsync(app, '.welcome-screen__button'))[0] - welcomeButton.click() - - // Scroll through terms - const title = (await findAsync(app, '.create-password__title')).text() - assert.equal(title, 'Create Password', 'create password screen') - - // enter password - const pwBox = (await findAsync(app, '#create-password'))[0] - const confBox = (await findAsync(app, '#confirm-password'))[0] - - nativeInputValueSetter.call(pwBox, PASSWORD) - pwBox.dispatchEvent(new Event('input', { bubbles: true})) - - nativeInputValueSetter.call(confBox, PASSWORD) - confBox.dispatchEvent(new Event('input', { bubbles: true})) - - // Create Password - const createButton = (await findAsync(app, 'button.first-time-flow__button'))[0] - createButton.click() - - const created = (await findAsync(app, '.unique-image__title'))[0] - assert.equal(created.textContent, 'Your unique account image', 'unique image screen') - - // Agree button - const button = (await findAsync(app, 'button'))[0] - assert.ok(button, 'button present') - button.click() - - await skipNotices(app) - - // secret backup phrase - const seedTitle = (await findAsync(app, '.backup-phrase__title'))[0] - assert.equal(seedTitle.textContent, 'Secret Backup Phrase', 'seed phrase screen') - ;(await findAsync(app, '.backup-phrase__reveal-button')).click() - const seedPhrase = (await findAsync(app, '.backup-phrase__secret-words')).text().split(' ') - ;(await findAsync(app, '.first-time-flow__button')).click() - - await timeout() - const selectPhrase = text => { - const option = $('.backup-phrase__confirm-seed-option') - .filter((i, d) => d.textContent === text)[0] - $(option).click() - } - - seedPhrase.forEach(sp => selectPhrase(sp)) - ;(await findAsync(app, '.first-time-flow__button')).click() - - // Deposit Ether Screen - const depositEthTitle = (await findAsync(app, '.page-container__title'))[0] - assert.equal(depositEthTitle.textContent, 'Deposit Ether', 'deposit ether screen') - ;(await findAsync(app, '.page-container__header-close')).click() - - const menu = (await findAsync(app, '.account-menu__icon'))[0] - menu.click() - - const lock = (await findAsync(app, '.account-menu__logout-button'))[0] - assert.ok(lock, 'Lock menu item found') - lock.click() - - await timeout(5000) - - const pwBox2 = (await findAsync(app, '#password'))[0] - pwBox2.focus() - await timeout(1000) - - nativeInputValueSetter.call(pwBox2, PASSWORD) - pwBox2.dispatchEvent(new Event('input', { bubbles: true})) - - const createButton2 = (await findAsync(app, 'button[type="submit"]'))[0] - createButton2.click() - - const detail2 = (await findAsync(app, '.wallet-view'))[0] - assert.ok(detail2, 'Account detail section loaded again.') - - // open account settings dropdown - const qrButton = (await findAsync(app, '.wallet-view__details-button'))[0] - qrButton.click() - - const qrHeader = (await findAsync(app, '.editable-label__value'))[0] - const qrContainer = (await findAsync(app, '.qr-wrapper'))[0] - assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.') - assert.ok(qrContainer, 'QR Container found') - - const networkMenu = (await findAsync(app, '.network-component'))[0] - networkMenu.click() - - const networkMenu2 = (await findAsync(app, '.network-indicator'))[0] - const children2 = networkMenu2.children - children2.length[3] - assert.ok(children2, 'All network options present') -} - -module.exports = runFirstTimeUsageTest - -async function skipNotices (app) { - while (true) { - const button = await findAsync(app, 'button') - if (button && button.html() === 'Accept') { - // still notices to accept - const termsPage = (await findAsync(app, '.markdown'))[0] - if (!termsPage) { - break - } - termsPage.scrollTop = termsPage.scrollHeight - await timeout() - button.click() - await timeout() - } else { - console.log('No more notices...') - break - } - } -} diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js index 271dd91cf..d7003f4cc 100644 --- a/test/integration/lib/send-new-ui.js +++ b/test/integration/lib/send-new-ui.js @@ -4,6 +4,7 @@ const { queryAsync, findAsync, } = require('../../lib/util') +const fetchMockResponses = require('../../e2e/beta/fetch-mocks.js') QUnit.module('new ui send flow') @@ -21,38 +22,22 @@ global.ethQuery = { global.ethereumProvider = {} -async function customizeGas (assert, price, limit, ethFee, usdFee) { - const sendGasOpenCustomizeModalButton = await queryAsync($, '.sliders-icon-container') - sendGasOpenCustomizeModalButton[0].click() - - const customizeGasModal = await queryAsync($, '.send-v2__customize-gas') - assert.ok(customizeGasModal[0], 'should render the customize gas modal') - - const customizeGasPriceInput = (await queryAsync($, '.send-v2__gas-modal-card')).first().find('input') - customizeGasPriceInput.val(price) - reactTriggerChange(customizeGasPriceInput[0]) - const customizeGasLimitInput = (await queryAsync($, '.send-v2__gas-modal-card')).last().find('input') - customizeGasLimitInput.val(limit) - reactTriggerChange(customizeGasLimitInput[0]) - - const customizeGasSaveButton = await queryAsync($, '.send-v2__customize-gas__save') - customizeGasSaveButton[0].click() - const sendGasField = await queryAsync($, '.send-v2__gas-fee-display') - - assert.equal( - (await findAsync(sendGasField, '.currency-display-component'))[0].textContent, - ethFee, - 'send gas field should show customized gas total' - ) - - assert.equal( - (await findAsync(sendGasField, '.currency-display__converted-value'))[0].textContent, - usdFee, - 'send gas field should show customized gas total converted to USD' - ) -} - async function runSendFlowTest (assert, done) { + const tempFetch = global.fetch + + global.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] === 'https://dev.blockscale.net/api/gasexpress.json') { + return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.gasExpress)) }) + } else if (args[0].match(/chromeextensionmm/)) { + return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.metametrics)) }) + } + return window.fetch(...args) + } + console.log('*** start runSendFlowTest') const selectState = await queryAsync($, 'select') selectState.val('send new ui') @@ -71,23 +56,14 @@ async function runSendFlowTest (assert, done) { const sendFromField = await queryAsync($, '.send-v2__form-field') assert.ok(sendFromField[0], 'send screen has a from field') - let sendFromFieldItemAddress = await queryAsync($, '.account-list-item__account-name') - assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 4', 'send from field shows correct account name') - - const sendFromFieldItem = await queryAsync($, '.account-list-item') - sendFromFieldItem[0].click() - - // this seems to fail if the firefox window is not in focus... - const sendFromDropdownList = await queryAsync($, '.send-v2__from-dropdown__list') - assert.equal(sendFromDropdownList.children().length, 4, 'send from dropdown shows all accounts') - sendFromDropdownList.children()[1].click() - - sendFromFieldItemAddress = await queryAsync($, '.account-list-item__account-name') - assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 2', 'send from field dropdown changes account name') + const sendFromFieldItemAddress = await queryAsync($, '.account-list-item__account-name') + assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 2', 'send from field shows correct account name') const sendToFieldInput = await queryAsync($, '.send-v2__to-autocomplete__input') sendToFieldInput[0].focus() + await timeout(1000) + const sendToDropdownList = await queryAsync($, '.send-v2__from-dropdown__list') assert.equal(sendToDropdownList.children().length, 5, 'send to dropdown shows all accounts and address book accounts') @@ -112,10 +88,6 @@ async function runSendFlowTest (assert, done) { errorMessage = $('.send-v2__error') assert.equal(errorMessage.length, 0, 'send should stop rendering amount error message after amount is corrected') - await customizeGas(assert, 0, 21000, '0ETH', '$0.00USD') - await customizeGas(assert, 1, 21000, '0.000021ETH', '$0.03USD') - await customizeGas(assert, 500, 60000, '0.03ETH', '$36.03USD') - const sendButton = await queryAsync($, 'button.btn-primary.btn--large.page-container__footer-button') assert.equal(sendButton[0].textContent, 'Next', 'next button rendered') sendButton[0].click() @@ -125,7 +97,7 @@ async function runSendFlowTest (assert, done) { reactTriggerChange(selectState[0]) const confirmFromName = (await queryAsync($, '.sender-to-recipient__name')).first() - assert.equal(confirmFromName[0].textContent, 'Send Account 4', 'confirm screen should show correct from name') + assert.equal(confirmFromName[0].textContent, 'Send Account 2', 'confirm screen should show correct from name') const confirmToName = (await queryAsync($, '.sender-to-recipient__name')).last() assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name') @@ -139,12 +111,6 @@ async function runSendFlowTest (assert, done) { const confirmScreenBackButton = await queryAsync($, '.confirm-page-container-header__back-button') confirmScreenBackButton[0].click() - const sendFromFieldItemInEdit = await queryAsync($, '.account-list-item') - sendFromFieldItemInEdit[0].click() - - const sendFromDropdownListInEdit = await queryAsync($, '.send-v2__from-dropdown__list') - sendFromDropdownListInEdit.children()[2].click() - const sendToFieldInputInEdit = await queryAsync($, '.send-v2__to-autocomplete__input') sendToFieldInputInEdit[0].focus() sendToFieldInputInEdit.val('0xd85a4b6a394794842887b8284293d69163007bbb') @@ -164,6 +130,8 @@ async function runSendFlowTest (assert, done) { const cancelButtonInEdit = await queryAsync($, '.btn-default.btn--large.page-container__footer-button') cancelButtonInEdit[0].click() + + global.fetch = tempFetch // sendButtonInEdit[0].click() // // TODO: Need a way to mock background so that we can test correct transition from editing to confirm diff --git a/test/integration/lib/tx-list-items.js b/test/integration/lib/tx-list-items.js index ed4f82074..c0056dd22 100644 --- a/test/integration/lib/tx-list-items.js +++ b/test/integration/lib/tx-list-items.js @@ -3,6 +3,7 @@ const { queryAsync, findAsync, } = require('../../lib/util') +const fetchMockResponses = require('../../e2e/beta/fetch-mocks.js') QUnit.module('tx list items') @@ -16,7 +17,7 @@ QUnit.test('renders list items successfully', (assert) => { global.ethQuery = global.ethQuery || {} global.ethQuery.getTransactionCount = (_, cb) => { - cb(null, '0x3') + cb(null, '0x4') } async function runTxListItemsTest (assert, done) { @@ -25,6 +26,13 @@ async function runTxListItemsTest (assert, done) { selectState.val('tx list items') reactTriggerChange(selectState[0]) + global.fetch = (...args) => { + if (args[0].match(/chromeextensionmm/)) { + return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.metametrics)) }) + } + return window.fetch(...args) + } + const metamaskLogo = await queryAsync($, '.app-header__logo-container') assert.ok(metamaskLogo[0], 'metamask logo present') metamaskLogo[0].click() @@ -32,33 +40,27 @@ async function runTxListItemsTest (assert, done) { const txListItems = await queryAsync($, '.transaction-list-item') assert.equal(txListItems.length, 8, 'all tx list items are rendered') - const retryTxGrid = await findAsync($(txListItems[2]), '.transaction-list-item__grid') - retryTxGrid[0].click() - const retryTxDetails = await findAsync($, '.transaction-list-item-details') - const headerButtons = await findAsync($(retryTxDetails[0]), '.transaction-list-item-details__header-button') - assert.equal(headerButtons[0].textContent, 'speed up') + const unapprovedMsg = txListItems[0] + const unapprovedMsgDescription = await findAsync($(unapprovedMsg), '.transaction-list-item__action') + assert.equal(unapprovedMsgDescription[0].textContent, 'Signature Request', 'unapprovedMsg has correct description') const approvedTx = txListItems[2] const approvedTxRenderedStatus = await findAsync($(approvedTx), '.transaction-list-item__status') assert.equal(approvedTxRenderedStatus[0].textContent, 'pending', 'approvedTx has correct label') - const unapprovedMsg = txListItems[0] - const unapprovedMsgDescription = await findAsync($(unapprovedMsg), '.transaction-list-item__action') - assert.equal(unapprovedMsgDescription[0].textContent, 'Signature Request', 'unapprovedMsg has correct description') - - const failedTx = txListItems[4] - const failedTxRenderedStatus = await findAsync($(failedTx), '.transaction-list-item__status') - assert.equal(failedTxRenderedStatus[0].textContent, 'Failed', 'failedTx has correct label') + const confirmedTokenTx1 = txListItems[4] + const confirmedTokenTx1Address = await findAsync($(confirmedTokenTx1), '.transaction-list-item__status') + assert.equal(confirmedTokenTx1Address[0].textContent, 'Confirmed', 'confirmedTokenTx has correct status') - const shapeShiftTx = txListItems[5] - const shapeShiftTxStatus = await findAsync($(shapeShiftTx), '.flex-column div:eq(1)') - assert.equal(shapeShiftTxStatus[0].textContent, 'No deposits received', 'shapeShiftTx has correct status') + const shapeShiftTx1 = txListItems[5] + const shapeShiftTx1Status = await findAsync($(shapeShiftTx1), '.flex-column div:eq(1)') + assert.equal(shapeShiftTx1Status[0].textContent, 'No deposits received', 'shapeShiftTx has correct status') - const confirmedTokenTx = txListItems[6] - const confirmedTokenTxAddress = await findAsync($(confirmedTokenTx), '.transaction-list-item__status') - assert.equal(confirmedTokenTxAddress[0].textContent, 'Confirmed', 'confirmedTokenTx has correct address') + const confirmedTokenTx2 = txListItems[6] + const confirmedTokenTx2Address = await findAsync($(confirmedTokenTx2), '.transaction-list-item__status') + assert.equal(confirmedTokenTx2Address[0].textContent, 'Confirmed', 'confirmedTokenTx has correct status') - const rejectedTx = txListItems[7] - const rejectedTxRenderedStatus = await findAsync($(rejectedTx), '.transaction-list-item__status') - assert.equal(rejectedTxRenderedStatus[0].textContent, 'Rejected', 'rejectedTx has correct label') + const shapeShiftTx2 = txListItems[7] + const shapeShiftTx2Address = await findAsync($(shapeShiftTx2), '.flex-column div:eq(1)') + assert.equal(shapeShiftTx2Address[0].textContent, 'No deposits received', 'shapeShiftTx has correct status') } diff --git a/test/lib/mock-tx-gen.js b/test/lib/mock-tx-gen.js index 106101500..e39551a7a 100644 --- a/test/lib/mock-tx-gen.js +++ b/test/lib/mock-tx-gen.js @@ -2,6 +2,7 @@ const extend = require('xtend') const BN = require('ethereumjs-util').BN const template = { 'status': 'submitted', + 'history': [{}], 'txParams': { 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', 'gas': '0x30d40', diff --git a/test/mascara.conf.js b/test/mascara.conf.js deleted file mode 100644 index faf3147bd..000000000 --- a/test/mascara.conf.js +++ /dev/null @@ -1,17 +0,0 @@ -const getBaseConfig = require('./base.conf.js') - -module.exports = function (config) { - const settings = getBaseConfig(config) - - // ui and tests - settings.files.push('dist/mascara/ui.js') - settings.files.push('dist/mascara/tests.js') - // service worker background - settings.files.push({ pattern: 'dist/mascara/background.js', watched: false, included: false, served: true }) - settings.proxies['/background.js'] = '/base/dist/mascara/background.js' - - // use this to keep the browser open for debugging - settings.browserNoActivityTimeout = 10000000 - - config.set(settings) -} diff --git a/test/setup.js b/test/setup.js index 8e7965a37..1a69f4866 100644 --- a/test/setup.js +++ b/test/setup.js @@ -3,3 +3,5 @@ require('babel-register')({ }) require('./helper') + +window.SVGPathElement = window.SVGPathElement || { prototype: {} } diff --git a/test/unit/actions/config_test.js b/test/unit/actions/config_test.js index 648f456fb..9127474a8 100644 --- a/test/unit/actions/config_test.js +++ b/test/unit/actions/config_test.js @@ -3,8 +3,8 @@ var assert = require('assert') var freeze = require('deep-freeze-strict') var path = require('path') -var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js')) -var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js')) +var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'store', 'actions.js')) +var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'ducks', 'index.js')) describe('config view actions', function () { var initialState = { diff --git a/test/unit/actions/set_account_label_test.js b/test/unit/actions/set_account_label_test.js index 53ea1d130..1601d6383 100644 --- a/test/unit/actions/set_account_label_test.js +++ b/test/unit/actions/set_account_label_test.js @@ -2,8 +2,8 @@ const assert = require('assert') const freeze = require('deep-freeze-strict') const path = require('path') -const actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js')) -const reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js')) +const actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'store', 'actions.js')) +const reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'ducks', 'index.js')) describe('SET_ACCOUNT_LABEL', function () { it('updates the state.metamask.identities[:i].name property of the state to the action.value.label', function () { diff --git a/test/unit/actions/set_selected_account_test.js b/test/unit/actions/set_selected_account_test.js index 28b47d09d..36d312d7b 100644 --- a/test/unit/actions/set_selected_account_test.js +++ b/test/unit/actions/set_selected_account_test.js @@ -3,8 +3,8 @@ var assert = require('assert') var freeze = require('deep-freeze-strict') var path = require('path') -var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js')) -var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js')) +var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'store', 'actions.js')) +var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'ducks', 'index.js')) describe('SET_SELECTED_ACCOUNT', function () { it('sets the state.appState.activeAddress property of the state to the action.value', function () { diff --git a/test/unit/actions/tx_test.js b/test/unit/actions/tx_test.js index 160cd4552..8c64d844f 100644 --- a/test/unit/actions/tx_test.js +++ b/test/unit/actions/tx_test.js @@ -4,7 +4,7 @@ var path = require('path') import configureMockStore from 'redux-mock-store' import thunk from 'redux-thunk' -const actions = require(path.join(__dirname, '../../../ui/app/actions.js')) +const actions = require(path.join(__dirname, '../../../ui/app/store/actions.js')) const middlewares = [thunk] const mockStore = configureMockStore(middlewares) diff --git a/test/unit/actions/view_info_test.js b/test/unit/actions/view_info_test.js index 69895d801..5785a368c 100644 --- a/test/unit/actions/view_info_test.js +++ b/test/unit/actions/view_info_test.js @@ -3,8 +3,8 @@ var assert = require('assert') var freeze = require('deep-freeze-strict') var path = require('path') -var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js')) -var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js')) +var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'store', 'actions.js')) +var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'ducks', 'index.js')) describe('SHOW_INFO_PAGE', function () { it('sets the state.appState.currentView.name property to info', function () { diff --git a/test/unit/actions/warning_test.js b/test/unit/actions/warning_test.js index 28b565499..e57374cda 100644 --- a/test/unit/actions/warning_test.js +++ b/test/unit/actions/warning_test.js @@ -3,8 +3,8 @@ var assert = require('assert') var freeze = require('deep-freeze-strict') var path = require('path') -var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js')) -var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js')) +var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'store', 'actions.js')) +var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'ducks', 'index.js')) describe('action DISPLAY_WARNING', function () { it('sets appState.warning to provided value', function () { diff --git a/test/unit/app/buy-eth-url.spec.js b/test/unit/app/buy-eth-url.spec.js index 36646fa68..6cf3e7d75 100644 --- a/test/unit/app/buy-eth-url.spec.js +++ b/test/unit/app/buy-eth-url.spec.js @@ -18,14 +18,9 @@ describe('', function () { } it('returns coinbase url with amount and address for network 1', function () { - const coinbaseUrl = getBuyEthUrl(mainnet) - const coinbase = coinbaseUrl.match(/(https:\/\/buy.coinbase.com)/) - const amount = coinbaseUrl.match(/(amount)\D\d/) - const address = coinbaseUrl.match(/(address)(.*)(?=&)/) - - assert.equal(coinbase[0], 'https://buy.coinbase.com') - assert.equal(amount[0], 'amount=5') - assert.equal(address[0], 'address=0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc') + const wyreUrl = getBuyEthUrl(mainnet) + + assert.equal(wyreUrl, 'https://dash.sendwyre.com/sign-up') }) diff --git a/test/unit/app/controllers/address-book-controller.js b/test/unit/app/controllers/address-book-controller.js deleted file mode 100644 index 1350e1a61..000000000 --- a/test/unit/app/controllers/address-book-controller.js +++ /dev/null @@ -1,54 +0,0 @@ -const assert = require('assert') -const AddressBookController = require('../../../../app/scripts/controllers/address-book') - -const stubPreferencesStore = { - getState: function () { - return { - identities: { - '0x0aaa': { - address: '0x0aaa', - name: 'owned', - }, - }, - } - }, -} - -describe('address-book-controller', function () { - var addressBookController - - beforeEach(function () { - addressBookController = new AddressBookController({ - preferencesStore: stubPreferencesStore, - }) - }) - - describe('addres book management', function () { - describe('#_getAddressBook', function () { - it('should be empty by default.', function () { - assert.equal(addressBookController._getAddressBook().length, 0) - }) - }) - describe('#setAddressBook', function () { - it('should properly set a new address.', function () { - addressBookController.setAddressBook('0x01234', 'test') - var addressBook = addressBookController._getAddressBook() - assert.equal(addressBook.length, 1, 'incorrect address book length.') - assert.equal(addressBook[0].address, '0x01234', 'incorrect addresss') - assert.equal(addressBook[0].name, 'test', 'incorrect nickname') - }) - - it('should reject duplicates.', function () { - addressBookController.setAddressBook('0x01234', 'test') - addressBookController.setAddressBook('0x01234', 'test') - var addressBook = addressBookController._getAddressBook() - assert.equal(addressBook.length, 1, 'incorrect address book length.') - }) - it('should not add any identities that are under user control', function () { - addressBookController.setAddressBook('0x0aaa', ' ') - var addressBook = addressBookController._getAddressBook() - assert.equal(addressBook.length, 0, 'incorrect address book length.') - }) - }) - }) -}) diff --git a/test/unit/app/controllers/cached-balances-test.js b/test/unit/app/controllers/cached-balances-test.js new file mode 100644 index 000000000..27aeabba2 --- /dev/null +++ b/test/unit/app/controllers/cached-balances-test.js @@ -0,0 +1,137 @@ +const assert = require('assert') +const sinon = require('sinon') +const CachedBalancesController = require('../../../../app/scripts/controllers/cached-balances') + +describe('CachedBalancesController', () => { + describe('updateCachedBalances', () => { + it('should update the cached balances', async () => { + const controller = new CachedBalancesController({ + getNetwork: () => Promise.resolve(17), + accountTracker: { + store: { + subscribe: () => {}, + }, + }, + initState: { + cachedBalances: 'mockCachedBalances', + }, + }) + + controller._generateBalancesToCache = sinon.stub().callsFake(() => Promise.resolve('mockNewCachedBalances')) + + await controller.updateCachedBalances({ accounts: 'mockAccounts' }) + + assert.equal(controller._generateBalancesToCache.callCount, 1) + assert.deepEqual(controller._generateBalancesToCache.args[0], ['mockAccounts', 17]) + assert.equal(controller.store.getState().cachedBalances, 'mockNewCachedBalances') + }) + }) + + describe('_generateBalancesToCache', () => { + it('should generate updated account balances where the current network was updated', () => { + const controller = new CachedBalancesController({ + accountTracker: { + store: { + subscribe: () => {}, + }, + }, + initState: { + cachedBalances: { + 17: { + a: '0x1', + b: '0x2', + c: '0x3', + }, + 16: { + a: '0xa', + b: '0xb', + c: '0xc', + }, + }, + }, + }) + + const result = controller._generateBalancesToCache({ + a: { balance: '0x4' }, + b: { balance: null }, + c: { balance: '0x5' }, + }, 17) + + assert.deepEqual(result, { + 17: { + a: '0x4', + b: '0x2', + c: '0x5', + }, + 16: { + a: '0xa', + b: '0xb', + c: '0xc', + }, + }) + }) + + it('should generate updated account balances where the a new network was selected', () => { + const controller = new CachedBalancesController({ + accountTracker: { + store: { + subscribe: () => {}, + }, + }, + initState: { + cachedBalances: { + 17: { + a: '0x1', + b: '0x2', + c: '0x3', + }, + }, + }, + }) + + const result = controller._generateBalancesToCache({ + a: { balance: '0x4' }, + b: { balance: null }, + c: { balance: '0x5' }, + }, 16) + + assert.deepEqual(result, { + 17: { + a: '0x1', + b: '0x2', + c: '0x3', + }, + 16: { + a: '0x4', + c: '0x5', + }, + }) + }) + }) + + describe('_registerUpdates', () => { + it('should subscribe to the account tracker with the updateCachedBalances method', async () => { + const subscribeSpy = sinon.spy() + const controller = new CachedBalancesController({ + getNetwork: () => Promise.resolve(17), + accountTracker: { + store: { + subscribe: subscribeSpy, + }, + }, + }) + subscribeSpy.resetHistory() + + const updateCachedBalancesSpy = sinon.spy() + controller.updateCachedBalances = updateCachedBalancesSpy + controller._registerUpdates({ accounts: 'mockAccounts' }) + + assert.equal(subscribeSpy.callCount, 1) + + subscribeSpy.args[0][0]() + + assert.equal(updateCachedBalancesSpy.callCount, 1) + }) + }) + +}) diff --git a/test/unit/app/controllers/metamask-controller-test.js b/test/unit/app/controllers/metamask-controller-test.js index 17be2c028..1ed6a95fb 100644 --- a/test/unit/app/controllers/metamask-controller-test.js +++ b/test/unit/app/controllers/metamask-controller-test.js @@ -58,6 +58,7 @@ describe('MetaMaskController', function () { }, }, initState: clone(firstTimeState), + platform: { showTransactionNotification: () => {} }, }) // disable diagnostics metamaskController.diagnostics = null diff --git a/test/unit/app/controllers/preferences-controller-test.js b/test/unit/app/controllers/preferences-controller-test.js index 473f22f8b..558597ae7 100644 --- a/test/unit/app/controllers/preferences-controller-test.js +++ b/test/unit/app/controllers/preferences-controller-test.js @@ -511,18 +511,30 @@ describe('preferences controller', function () { }) }) + describe('#updateRpc', function () { + it('should update the rpcDetails properly', () => { + preferencesController.store.updateState({frequentRpcListDetail: [{}, { rpcUrl: 'test' }, {}]}) + preferencesController.updateRpc({ rpcUrl: 'test', chainId: 1 }) + preferencesController.updateRpc({ rpcUrl: 'test/1', chainId: 1 }) + preferencesController.updateRpc({ rpcUrl: 'test/2', chainId: 1 }) + preferencesController.updateRpc({ rpcUrl: 'test/3', chainId: 1 }) + const list = preferencesController.getFrequentRpcListDetail() + assert.deepEqual(list[1], { rpcUrl: 'test', chainId: 1 }) + }) + }) + describe('on updateFrequentRpcList', function () { it('should add custom RPC url to state', function () { preferencesController.addToFrequentRpcList('rpc_url', 1) preferencesController.addToFrequentRpcList('http://localhost:8545', 1) - assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }] ) + assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }]) preferencesController.addToFrequentRpcList('rpc_url', 1) - assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }] ) + assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }]) }) it('should remove custom RPC url from state', function () { preferencesController.addToFrequentRpcList('rpc_url', 1) - assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }] ) + assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }]) preferencesController.removeFromFrequentRpcList('other_rpc_url') preferencesController.removeFromFrequentRpcList('http://localhost:8545') preferencesController.removeFromFrequentRpcList('rpc_url') diff --git a/test/unit/app/controllers/token-rates-controller.js b/test/unit/app/controllers/token-rates-controller.js index 28e583d8d..ccc279cbe 100644 --- a/test/unit/app/controllers/token-rates-controller.js +++ b/test/unit/app/controllers/token-rates-controller.js @@ -17,13 +17,4 @@ describe('TokenRatesController', () => { assert.strictEqual(stub.getCall(0).args[1], 1337) stub.restore() }) - - it('should fetch each token rate based on address', async () => { - const controller = new TokenRatesController() - controller.isActive = true - controller.fetchExchangeRate = address => address - controller.tokens = [{ address: 'foo' }, { address: 'bar' }] - await controller.updateExchangeRates() - assert.deepEqual(controller.store.getState().contractExchangeRates, { foo: 'foo', bar: 'bar' }) - }) }) diff --git a/test/unit/app/controllers/transactions/pending-tx-test.js b/test/unit/app/controllers/transactions/pending-tx-test.js index ba15f1953..2988bf61f 100644 --- a/test/unit/app/controllers/transactions/pending-tx-test.js +++ b/test/unit/app/controllers/transactions/pending-tx-test.js @@ -20,11 +20,13 @@ describe('PendingTransactionTracker', function () { nonce: '0x1', value: '0xfffff', }, + history: [{}], rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d', } txMetaNoHash = { id: 2, - status: 'signed', + history: [{}], + status: 'submitted', txParams: { from: '0x1678a085c290ebd122dc42cba69373b5953b831d'}, } @@ -212,6 +214,7 @@ describe('PendingTransactionTracker', function () { pendingTxTracker.publishTransaction = async (rawTx) => { assert.equal(rawTx, txMeta.rawTx, 'Should pass the rawTx') } + pendingTxTracker.approveTransaction = async () => {} sinon.spy(pendingTxTracker, 'publishTransaction') txMetaToTestExponentialBackoff = Object.assign({}, txMeta, { @@ -266,6 +269,18 @@ describe('PendingTransactionTracker', function () { assert.equal(pendingTxTracker.publishTransaction.callCount, 1, 'Should call publish transaction') }) + + it('should call opts.approveTransaction with the id if the tx is not signed', async () => { + const stubTx = { + id: 40, + } + const approveMock = sinon.stub(pendingTxTracker, 'approveTransaction') + + pendingTxTracker._resubmitTx(stubTx) + + assert.ok(approveMock.called) + approveMock.restore() + }) }) describe('#_checkIfNonceIsTaken', function () { diff --git a/test/unit/app/controllers/transactions/tx-controller-test.js b/test/unit/app/controllers/transactions/tx-controller-test.js index ea58aa560..9000cd364 100644 --- a/test/unit/app/controllers/transactions/tx-controller-test.js +++ b/test/unit/app/controllers/transactions/tx-controller-test.js @@ -5,11 +5,14 @@ const EthTx = require('ethereumjs-tx') const ObservableStore = require('obs-store') const sinon = require('sinon') const TransactionController = require('../../../../../app/scripts/controllers/transactions') +const { + TRANSACTION_TYPE_RETRY, +} = require('../../../../../app/scripts/controllers/transactions/enums') const { createTestProviderTools, getTestAccounts } = require('../../../../stub/provider') const noop = () => true const currentNetworkId = 42 - +const netStore = new ObservableStore(currentNetworkId) describe('Transaction Controller', function () { let txController, provider, providerResultStub, fromAccount @@ -28,7 +31,8 @@ describe('Transaction Controller', function () { blockTrackerStub.getLatestBlock = noop txController = new TransactionController({ provider, - networkStore: new ObservableStore(currentNetworkId), + getGasPrice: function () { return '0xee6b2800' }, + networkStore: netStore, txHistoryLimit: 10, blockTracker: blockTrackerStub, signTransaction: (ethTx) => new Promise((resolve) => { @@ -52,9 +56,9 @@ describe('Transaction Controller', function () { describe('#getUnapprovedTxCount', function () { it('should return the number of unapproved txs', function () { txController.txStateManager._saveTxList([ - { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] }, - { id: 2, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] }, - { id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] }, + { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [{}] }, + { id: 2, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [{}] }, + { id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [{}] }, ]) const unapprovedTxCount = txController.getUnapprovedTxCount() assert.equal(unapprovedTxCount, 3, 'should be 3') @@ -64,9 +68,9 @@ describe('Transaction Controller', function () { describe('#getPendingTxCount', function () { it('should return the number of pending txs', function () { txController.txStateManager._saveTxList([ - { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] }, - { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] }, - { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] }, + { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [{}] }, + { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [{}] }, + { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [{}] }, ]) const pendingTxCount = txController.getPendingTxCount() assert.equal(pendingTxCount, 3, 'should be 3') @@ -82,15 +86,15 @@ describe('Transaction Controller', function () { 'to': '0xc684832530fcbddae4b4230a47e991ddcec2831d', } txController.txStateManager._saveTxList([ - {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: [] }, + {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: [{}] }, ]) }) @@ -112,7 +116,7 @@ describe('Transaction Controller', function () { id: 1, metamaskNetworkId: currentNetworkId, txParams, - history: [], + history: [{}], } txController.txStateManager._saveTxList([txMeta]) stub = sinon.stub(txController, 'addUnapprovedTransaction').callsFake(() => { @@ -223,6 +227,15 @@ describe('Transaction Controller', function () { txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' }) .catch(done) }) + + it('should fail if netId is loading', function (done) { + txController.networkStore = new ObservableStore('loading') + txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' }) + .catch((err) => { + if (err.message === 'MetaMask is having trouble connecting to the network') done() + else done(err) + }) + }) }) describe('#addTxGasDefaults', function () { @@ -232,7 +245,7 @@ describe('Transaction Controller', function () { from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', to: '0xc684832530fcbddae4b4230a47e991ddcec2831d', }, - history: [], + history: [{}], } providerResultStub.eth_gasPrice = '4a817c800' providerResultStub.eth_getBlockByNumber = { gasLimit: '47b784' } @@ -313,6 +326,7 @@ describe('Transaction Controller', function () { assert.equal(params.gas, originalValue, 'gas unmodified') assert.equal(params.gasPrice, originalValue, 'gas price unmodified') assert.equal(result.hash, originalValue, `hash was set \n got: ${result.hash} \n expected: ${originalValue}`) + assert.equal(result.status, 'submitted', 'Should have reached the submitted status.') signStub.restore() pubStub.restore() done() @@ -390,6 +404,70 @@ describe('Transaction Controller', function () { }) + 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([ + { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [{}] }, + ]) + + 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, + }) + }) + }) + describe('#publishTransaction', function () { let hash, txMeta beforeEach(function () { @@ -414,18 +492,20 @@ describe('Transaction Controller', function () { }) describe('#retryTransaction', function () { - it('should create a new txMeta with the same txParams as the original one', function (done) { + it('should create a new txMeta with the same txParams as the original one but with a higher gasPrice', function (done) { const txParams = { + gasPrice: '0xee6b2800', nonce: '0x00', from: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4', to: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4', data: '0x0', } txController.txStateManager._saveTxList([ - { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [] }, + { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [{}] }, ]) txController.retryTransaction(1) .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') @@ -461,17 +541,19 @@ describe('Transaction Controller', function () { beforeEach(function () { txController.txStateManager._saveTxList([ { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, - { id: 2, status: 'rejected', metamaskNetworkId: currentNetworkId, txParams: {} }, - { id: 3, status: 'approved', metamaskNetworkId: currentNetworkId, txParams: {} }, - { id: 4, status: 'signed', metamaskNetworkId: currentNetworkId, txParams: {} }, - { id: 5, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} }, - { id: 6, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, - { id: 7, status: 'failed', metamaskNetworkId: currentNetworkId, txParams: {} }, + { 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: [{}] }, ]) }) - it('should show only submitted transactions as pending transasction', function () { - assert(txController.pendingTxTracker.getPendingTransactions().length, 1) - assert(txController.pendingTxTracker.getPendingTransactions()[0].status, 'submitted') + 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') }) }) }) diff --git a/test/unit/balance-formatter-test.js b/test/unit/balance-formatter-test.js index ab6daa19c..bd0eb5008 100644 --- a/test/unit/balance-formatter-test.js +++ b/test/unit/balance-formatter-test.js @@ -1,6 +1,6 @@ const assert = require('assert') const currencyFormatter = require('currency-formatter') -const infuraConversion = require('../../ui/app/infura-conversion.json') +const infuraConversion = require('../../ui/app/helpers/constants/infura-conversion.json') describe('currencyFormatting', function () { it('be able to format any infura currency', function (done) { diff --git a/test/unit/components/binary-renderer-test.js b/test/unit/components/binary-renderer-test.js deleted file mode 100644 index e428c26ad..000000000 --- a/test/unit/components/binary-renderer-test.js +++ /dev/null @@ -1,23 +0,0 @@ -var assert = require('assert') -var BinaryRenderer = require('../../../old-ui/app/components/binary-renderer') - -describe('BinaryRenderer', function () { - let binaryRenderer - const message = 'Hello, world!' - const buffer = Buffer.from(message, 'utf8') - const hex = buffer.toString('hex') - - beforeEach(function () { - binaryRenderer = new BinaryRenderer() - }) - - it('recovers message', function () { - const result = binaryRenderer.hexToText(hex) - assert.equal(result, message) - }) - - it('recovers message with hex prefix', function () { - const result = binaryRenderer.hexToText('0x' + hex) - assert.equal(result, message) - }) -}) diff --git a/test/unit/components/bn-as-decimal-input-test.js b/test/unit/components/bn-as-decimal-input-test.js deleted file mode 100644 index fab396548..000000000 --- a/test/unit/components/bn-as-decimal-input-test.js +++ /dev/null @@ -1,89 +0,0 @@ -var assert = require('assert') - -const additions = require('react-testutils-additions') -const h = require('react-hyperscript') -const ReactTestUtils = require('react-addons-test-utils') -const ethUtil = require('ethereumjs-util') -const BN = ethUtil.BN - -var BnInput = require('../../../old-ui/app/components/bn-as-decimal-input') - -describe('BnInput', function () { - it('can tolerate a gas decimal number at a high precision', function (done) { - const renderer = ReactTestUtils.createRenderer() - - let valueStr = '20' - while (valueStr.length < 20) { - valueStr += '0' - } - const value = new BN(valueStr, 10) - - const inputStr = '2.3' - - let targetStr = '23' - while (targetStr.length < 19) { - targetStr += '0' - } - const target = new BN(targetStr, 10) - - const precision = 18 // ether precision - const scale = 18 - - const props = { - value, - scale, - precision, - onChange: (newBn) => { - assert.equal(newBn.toString(), target.toString(), 'should tolerate increase') - done() - }, - } - - const inputComponent = h(BnInput, props) - const component = additions.renderIntoDocument(inputComponent) - renderer.render(inputComponent) - const input = additions.find(component, 'input.hex-input')[0] - ReactTestUtils.Simulate.change(input, { preventDefault () {}, target: { - value: inputStr, - checkValidity () { return true } }, - }) - }) - - it('can tolerate wei precision', function (done) { - const renderer = ReactTestUtils.createRenderer() - - const valueStr = '1000000000' - - const value = new BN(valueStr, 10) - const inputStr = '1.000000001' - - - const targetStr = '1000000001' - - const target = new BN(targetStr, 10) - - const precision = 9 // gwei precision - const scale = 9 - - const props = { - value, - scale, - precision, - onChange: (newBn) => { - assert.equal(newBn.toString(), target.toString(), 'should tolerate increase') - const reInput = BnInput.prototype.downsize(newBn.toString(), 9, 9) - assert.equal(reInput.toString(), inputStr, 'should tolerate increase') - done() - }, - } - - const inputComponent = h(BnInput, props) - const component = additions.renderIntoDocument(inputComponent) - renderer.render(inputComponent) - const input = additions.find(component, 'input.hex-input')[0] - ReactTestUtils.Simulate.change(input, { preventDefault () {}, target: { - value: inputStr, - checkValidity () { return true } }, - }) - }) -}) diff --git a/test/unit/development/sample-changelog.md b/test/unit/development/sample-changelog.md index fd980e375..69f6513e6 100644 --- a/test/unit/development/sample-changelog.md +++ b/test/unit/development/sample-changelog.md @@ -1,6 +1,6 @@ # Changelog -## Current Master +## Current Develop Branch ## 4.1.3 2018-2-28 diff --git a/test/unit/migrations/029-test.js b/test/unit/migrations/029-test.js new file mode 100644 index 000000000..a2876487b --- /dev/null +++ b/test/unit/migrations/029-test.js @@ -0,0 +1,38 @@ +const assert = require('assert') +const migration29 = require('../../../app/scripts/migrations/029') +const properTime = (new Date()).getTime() +const storage = { + 'meta': {}, + 'data': { + 'TransactionController': { + 'transactions': [ + { 'status': 'approved', id: 1, submittedTime: 0 }, + { 'status': 'approved', id: 2, submittedTime: properTime }, + { 'status': 'confirmed', id: 3, submittedTime: properTime }, + { 'status': 'submitted', id: 4, submittedTime: properTime }, + { 'status': 'submitted', id: 5, submittedTime: 0 }, + ], + }, + }, +} + +describe('storage is migrated successfully where transactions that are submitted have submittedTimes', () => { + it('should auto fail transactions more than 12 hours old', (done) => { + migration29.migrate(storage) + .then((migratedData) => { + const txs = migratedData.data.TransactionController.transactions + const [ txMeta1 ] = txs + assert.equal(migratedData.meta.version, 29) + + assert.equal(txMeta1.status, 'failed', 'old tx is auto failed') + assert(txMeta1.err.message.includes('too long'), 'error message assigned') + + txs.forEach((tx) => { + if (tx.id === 1) return + assert.notEqual(tx.status, 'failed', 'other tx is not auto failed') + }) + + done() + }).catch(done) + }) +}) diff --git a/test/unit/migrations/030-test.js b/test/unit/migrations/030-test.js new file mode 100644 index 000000000..ca410342f --- /dev/null +++ b/test/unit/migrations/030-test.js @@ -0,0 +1,37 @@ +const assert = require('assert') +const migrationTemplate = require('../../../app/scripts/migrations/030.js') +const storage = { + meta: {}, + data: { + NetworkController: { + network: 'fail', + provider: { + chainId: 'fail', + nickname: '', + rpcTarget: 'https://api.myetherwallet.com/eth', + ticker: 'ETH', + type: 'rinkeby', + }, + }, + PreferencesController: { + frequentRpcListDetail: [ + {chainId: 'fail', nickname: '', rpcUrl: 'http://127.0.0.1:8545', ticker: ''}, + {chainId: '1', nickname: '', rpcUrl: 'https://api.myetherwallet.com/eth', ticker: 'ETH'}, + ], + }, + }, +} + +describe('storage is migrated successfully', () => { + it('should work', (done) => { + migrationTemplate.migrate(storage) + .then((migratedData) => { + assert.equal(migratedData.meta.version, 30) + assert.equal(migratedData.data.PreferencesController.frequentRpcListDetail[0].chainId, undefined) + assert.equal(migratedData.data.PreferencesController.frequentRpcListDetail[1].chainId, '1') + assert.equal(migratedData.data.NetworkController.provider.chainId, undefined) + assert.equal(migratedData.data.NetworkController.network, undefined) + done() + }).catch(done) + }) +}) diff --git a/test/unit/migrations/031-test.js b/test/unit/migrations/031-test.js new file mode 100644 index 000000000..c85fd7af4 --- /dev/null +++ b/test/unit/migrations/031-test.js @@ -0,0 +1,56 @@ +const assert = require('assert') +const migration31 = require('../../../app/scripts/migrations/031') + + describe('migration #31', () => { + it('should set completedOnboarding to true if vault exists', done => { + const oldStorage = { + 'meta': {}, + 'data': { + 'PreferencesController': { + 'tokens': [{address: '0xa', symbol: 'A', decimals: 4}, {address: '0xb', symbol: 'B', decimals: 4}], + 'identities': { + '0x6d14': {}, + '0x3695': {}, + }, + }, + 'KeyringController': { + 'vault': { + 'data': 'test0', + 'iv': 'test1', + 'salt': 'test2', + }, + }, + }, + } + + migration31.migrate(oldStorage) + .then(newStorage => { + assert.equal(newStorage.data.PreferencesController.completedOnboarding, true) + done() + }) + .catch(done) + }) + + it('should set completedOnboarding to false if vault does not exist', done => { + const oldStorage = { + 'meta': {}, + 'data': { + 'PreferencesController': { + 'tokens': [{address: '0xa', symbol: 'A', decimals: 4}, {address: '0xb', symbol: 'B', decimals: 4}], + 'identities': { + '0x6d14': {}, + '0x3695': {}, + }, + }, + 'KeyringController': {}, + }, + } + + migration31.migrate(oldStorage) + .then(newStorage => { + assert.equal(newStorage.data.PreferencesController.completedOnboarding, false) + done() + }) + .catch(done) + }) +}) diff --git a/test/unit/nameForAccount_test.js b/test/unit/nameForAccount_test.js deleted file mode 100644 index 9bb02c6bc..000000000 --- a/test/unit/nameForAccount_test.js +++ /dev/null @@ -1,41 +0,0 @@ -var assert = require('assert') -var sinon = require('sinon') - -var path = require('path') -var contractNamer = require(path.join(__dirname, '..', '..', 'old-ui', 'lib', 'contract-namer.js')) - -describe('contractNamer', function () { - beforeEach(function () { - this.sinon = sinon.createSandbox() - }) - - afterEach(function () { - this.sinon.restore() - }) - - describe('naming a contract', function () { - it('should return nothing for an unknown random account', function () { - const input = '0x2386F26FC10000' - const output = contractNamer(input) - assert.deepEqual(output, null) - }) - - it('should accept identities as an optional second parameter', function () { - const input = '0x2386F26FC10000'.toLowerCase() - const expected = 'bar' - const identities = {} - identities[input] = { name: expected } - const output = contractNamer(input, identities) - assert.deepEqual(output, expected) - }) - - it('should check for identities case insensitively', function () { - const input = '0x2386F26FC10000'.toLowerCase() - const expected = 'bar' - const identities = {} - identities[input] = { name: expected } - const output = contractNamer(input.toUpperCase(), identities) - assert.deepEqual(output, expected) - }) - }) -}) diff --git a/test/unit/reducers/unlock_vault_test.js b/test/unit/reducers/unlock_vault_test.js index d66e8edbb..d66891a63 100644 --- a/test/unit/reducers/unlock_vault_test.js +++ b/test/unit/reducers/unlock_vault_test.js @@ -4,8 +4,8 @@ var assert = require('assert') var path = require('path') var sinon = require('sinon') -var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js')) -var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js')) +var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'store', 'actions.js')) +var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'ducks', 'index.js')) describe('#unlockMetamask(selectedAccount)', function () { beforeEach(function () { diff --git a/test/unit/responsive/components/dropdown-test.js b/test/unit/responsive/components/dropdown-test.js index f3f236d90..1fadbfb60 100644 --- a/test/unit/responsive/components/dropdown-test.js +++ b/test/unit/responsive/components/dropdown-test.js @@ -3,7 +3,7 @@ const assert = require('assert') const h = require('react-hyperscript') const sinon = require('sinon') const path = require('path') -const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdowns', 'index.js')).Dropdown +const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'app', 'dropdowns', 'index.js')).Dropdown const { createMockStore } = require('redux-test-utils') const { mountWithStore } = require('../../../lib/render-helpers') diff --git a/test/unit/ui/add-token.spec.js b/test/unit/ui/add-token.spec.js deleted file mode 100644 index f6b6155a0..000000000 --- a/test/unit/ui/add-token.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -const assert = require('assert') -const { createMockStore } = require('redux-test-utils') -const h = require('react-hyperscript') -const { shallowWithStore } = require('../../lib/render-helpers') -const AddTokenScreen = require('../../../old-ui/app/add-token') - -describe('Add Token Screen', function () { - let addTokenComponent, store, component - const mockState = { - metamask: { - identities: { - '0x7d3517b0d011698406d6e0aed8453f0be2697926': { - 'address': '0x7d3517b0d011698406d6e0aed8453f0be2697926', - 'name': 'Add Token Name', - }, - }, - }, - } - beforeEach(function () { - store = createMockStore(mockState) - component = shallowWithStore(h(AddTokenScreen), store) - addTokenComponent = component.dive() - }) - - describe('#ValidateInputs', function () { - - it('Default State', function () { - addTokenComponent.instance().validateInputs() - const state = addTokenComponent.state() - assert.equal(state.warning, 'Address is invalid.') - }) - - it('Address is a Metamask Identity', function () { - addTokenComponent.setState({ - address: '0x7d3517b0d011698406d6e0aed8453f0be2697926', - }) - addTokenComponent.instance().validateInputs() - const state = addTokenComponent.state() - assert.equal(state.warning, 'Personal address detected. Input the token contract address.') - }) - - }) -}) diff --git a/test/unit/ui/app/actions.spec.js b/test/unit/ui/app/actions.spec.js index df7d2ee8f..46e94bb32 100644 --- a/test/unit/ui/app/actions.spec.js +++ b/test/unit/ui/app/actions.spec.js @@ -16,7 +16,7 @@ const { createTestProviderTools } = require('../../../stub/provider') const provider = createTestProviderTools({ scaffold: {}}).provider const enLocale = require('../../../../app/_locales/en/messages.json') -const actions = require('../../../../ui/app/actions') +const actions = require('../../../../ui/app/store/actions') const MetaMaskController = require('../../../../app/scripts/metamask-controller') const firstTimeState = require('../../../unit/localhostState') @@ -198,7 +198,7 @@ describe('Actions', () => { createNewVaultAndRestoreSpy = sinon.spy(background, 'createNewVaultAndRestore') clearSeedWordCacheSpy = sinon.spy(background, 'clearSeedWordCache') return store.dispatch(actions.createNewVaultAndRestore()) - .then(() => { + .catch(() => { assert(clearSeedWordCacheSpy.calledOnce) assert(createNewVaultAndRestoreSpy.calledOnce) }) @@ -218,7 +218,7 @@ describe('Actions', () => { }) return store.dispatch(actions.createNewVaultAndRestore()) - .then(() => { + .catch(() => { assert.deepEqual(store.getActions(), expectedActions) }) }) @@ -240,7 +240,7 @@ describe('Actions', () => { }) return store.dispatch(actions.createNewVaultAndRestore()) - .then(() => { + .catch(() => { assert.deepEqual(store.getActions(), expectedActions) }) }) @@ -1079,8 +1079,10 @@ describe('Actions', () => { describe('#setProviderType', () => { let setProviderTypeSpy + let store beforeEach(() => { + store = mockStore({ metamask: { provider: {} } }) setProviderTypeSpy = sinon.stub(background, 'setProviderType') }) @@ -1089,13 +1091,11 @@ describe('Actions', () => { }) it('', () => { - const store = mockStore() store.dispatch(actions.setProviderType()) assert(setProviderTypeSpy.calledOnce) }) it('', () => { - const store = mockStore() const expectedActions = [ { type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' }, ] diff --git a/test/unit/ui/app/components/token-cell.spec.js b/test/unit/ui/app/components/token-cell.spec.js index 6145c6924..23e76c418 100644 --- a/test/unit/ui/app/components/token-cell.spec.js +++ b/test/unit/ui/app/components/token-cell.spec.js @@ -5,8 +5,8 @@ import { Provider } from 'react-redux' import configureMockStore from 'redux-mock-store' import { mount } from 'enzyme' -import TokenCell from '../../../../../ui/app/components/token-cell' -import Identicon from '../../../../../ui/app/components/identicon' +import TokenCell from '../../../../../ui/app/components/app/token-cell' +import Identicon from '../../../../../ui/app/components/ui/identicon' describe('Token Cell', () => { let wrapper diff --git a/test/unit/ui/app/reducers/app.spec.js b/test/unit/ui/app/reducers/app.spec.js index bee4963e5..6c77e0ef9 100644 --- a/test/unit/ui/app/reducers/app.spec.js +++ b/test/unit/ui/app/reducers/app.spec.js @@ -1,6 +1,6 @@ import assert from 'assert' -import reduceApp from '../../../../../ui/app/reducers/app' -import * as actions from '../../../../../ui/app/actions' +import reduceApp from '../../../../../ui/app/ducks/app/app' +import * as actions from '../../../../../ui/app/store/actions' describe('App State', () => { diff --git a/test/unit/ui/app/reducers/metamask.spec.js b/test/unit/ui/app/reducers/metamask.spec.js index e1a50eef2..388c67c76 100644 --- a/test/unit/ui/app/reducers/metamask.spec.js +++ b/test/unit/ui/app/reducers/metamask.spec.js @@ -1,11 +1,11 @@ import assert from 'assert' -import reduceMetamask from '../../../../../ui/app/reducers/metamask' -import * as actions from '../../../../../ui/app/actions' +import reduceMetamask from '../../../../../ui/app/ducks/metamask/metamask' +import * as actions from '../../../../../ui/app/store/actions' describe('MetaMask Reducers', () => { it('init state', () => { - const initState = reduceMetamask({metamask:{}}, {}) + const initState = reduceMetamask({metamask: {}}, {}) assert(initState) }) @@ -502,17 +502,15 @@ describe('MetaMask Reducers', () => { assert.equal(state.useBlockie, true) }) - it('updates feature flag', () => { + it('updates an arbitrary feature flag', () => { const state = reduceMetamask({}, { type: actions.UPDATE_FEATURE_FLAGS, value: { - betaUI: true, - skipAnnounceBetaUI: true, + foo: true, }, }) - assert.equal(state.featureFlags.betaUI, true) - assert.equal(state.featureFlags.skipAnnounceBetaUI, true) + assert.equal(state.featureFlags.foo, true) }) it('updates network endpoint type', () => { diff --git a/test/unit/ui/app/selectors.spec.js b/test/unit/ui/app/selectors.spec.js index 78c4267ee..b4aa8e272 100644 --- a/test/unit/ui/app/selectors.spec.js +++ b/test/unit/ui/app/selectors.spec.js @@ -1,5 +1,5 @@ const assert = require('assert') -const selectors = require('../../../../ui/app/selectors') +const selectors = require('../../../../ui/app/selectors/selectors') const mockState = require('../../../data/mock-state.json') const Eth = require('ethjs') @@ -19,6 +19,7 @@ describe('Selectors', function () { 'address': '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', }, }, + cachedBalances: {}, }, } }) @@ -148,7 +149,7 @@ describe('Selectors', function () { it('#getSelectedTokenToFiatRate', () => { const selectedTokenToFiatRate = selectors.getSelectedTokenToFiatRate(mockState) - assert.equal(selectedTokenToFiatRate, '0.21880988420033493') + assert.equal(selectedTokenToFiatRate, '0.21880988420033492152') }) describe('#getSelectedTokenContract', () => { diff --git a/test/unit/util_test.js b/test/unit/util_test.js index 39473854f..87f57b218 100644 --- a/test/unit/util_test.js +++ b/test/unit/util_test.js @@ -3,7 +3,7 @@ var sinon = require('sinon') const ethUtil = require('ethereumjs-util') var path = require('path') -var util = require(path.join(__dirname, '..', '..', 'ui', 'app', 'util.js')) +var util = require(path.join(__dirname, '..', '..', 'ui', 'app', 'helpers', 'utils', 'util.js')) describe('util', function () { var ethInWei = '1' |