mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Merge pull request #6484 from MetaMask/develop
Update master branch with develop (v6.4.0)
This commit is contained in:
commit
87d5be9081
26
CHANGELOG.md
26
CHANGELOG.md
@ -2,6 +2,32 @@
|
||||
|
||||
## Current Develop Branch
|
||||
|
||||
## 6.4.0 Wed Apr 17 2019
|
||||
|
||||
- [#6445](https://github.com/MetaMask/metamask-extension/pull/6445): * Move send to pages/
|
||||
- [#6470](https://github.com/MetaMask/metamask-extension/pull/6470): update publishing.md with dev diagram
|
||||
- [#6403](https://github.com/MetaMask/metamask-extension/pull/6403): Update to eth-method-registry@1.2.0
|
||||
- [#6468](https://github.com/MetaMask/metamask-extension/pull/6468): Fix switcher height when Custom RPC is selected or loading
|
||||
- [#6459](https://github.com/MetaMask/metamask-extension/pull/6459): feature: add Goerli support
|
||||
- [#6444](https://github.com/MetaMask/metamask-extension/pull/6444): Fixes #6321 & #6421 - Add Localhost 8545 for network dropdown names
|
||||
- [#6454](https://github.com/MetaMask/metamask-extension/pull/6454): Bump eth-contract-metadata
|
||||
- [#6448](https://github.com/MetaMask/metamask-extension/pull/6448): Remove unneeded array cloning in getSendToAccounts selector
|
||||
- [#6056](https://github.com/MetaMask/metamask-extension/pull/6056): repeated getSelectedAddress() func send.selectors.js removed
|
||||
- [#6422](https://github.com/MetaMask/metamask-extension/pull/6422): Added Chrome limited site access solution doc
|
||||
- [#6424](https://github.com/MetaMask/metamask-extension/pull/6424): feature: switch token pricing to CoinGecko API
|
||||
- [#6428](https://github.com/MetaMask/metamask-extension/pull/6428): Don't inject web3 on sharefile.com
|
||||
- [#6417](https://github.com/MetaMask/metamask-extension/pull/6417): Metrics updates
|
||||
- [#6420](https://github.com/MetaMask/metamask-extension/pull/6420): Fix links to MetamaskInpageProvider in porting_to_new_environment.md
|
||||
- [#6362](https://github.com/MetaMask/metamask-extension/pull/6362): Remove broken image walkthrough from metamaskbot comment
|
||||
- [#6401](https://github.com/MetaMask/metamask-extension/pull/6401): metamask-controller - use improved provider-as-middleware utility
|
||||
- [#6406](https://github.com/MetaMask/metamask-extension/pull/6406): remove user actions controller
|
||||
- [#6399](https://github.com/MetaMask/metamask-extension/pull/6399): doc - publishing - typo fix
|
||||
- [#6396](https://github.com/MetaMask/metamask-extension/pull/6396): pin eth-contract-metadata to last commit hash
|
||||
- [#6397](https://github.com/MetaMask/metamask-extension/pull/6397): Change coinbase to wyre
|
||||
- [#6395](https://github.com/MetaMask/metamask-extension/pull/6395): bump ledger and trezor keyring
|
||||
- [#6389](https://github.com/MetaMask/metamask-extension/pull/6389): Fix display of gas chart on Ethereum networks
|
||||
- [#6382](https://github.com/MetaMask/metamask-extension/pull/6382): Remove NoticeController
|
||||
|
||||
## 6.3.2 Mon Apr 8 2019
|
||||
|
||||
- [#6389](https://github.com/MetaMask/metamask-extension/pull/6389): Fix display of gas chart on ethereum networks
|
||||
|
@ -139,6 +139,9 @@
|
||||
"approved": {
|
||||
"message": "Approved"
|
||||
},
|
||||
"asset": {
|
||||
"message": "Asset"
|
||||
},
|
||||
"attemptingConnect": {
|
||||
"message": "Attempting to connect to blockchain."
|
||||
},
|
||||
@ -308,6 +311,12 @@
|
||||
"connectingToRinkeby": {
|
||||
"message": "Connecting to Rinkeby Test Network"
|
||||
},
|
||||
"connectingToLocalhost": {
|
||||
"message": "Connecting to Localhost 8545"
|
||||
},
|
||||
"connectingToGoerli": {
|
||||
"message": "Connecting to Goerli Test Network"
|
||||
},
|
||||
"connectingToUnknown": {
|
||||
"message": "Connecting to Unknown Network"
|
||||
},
|
||||
@ -1226,6 +1235,9 @@
|
||||
"ropsten": {
|
||||
"message": "Ropsten Test Network"
|
||||
},
|
||||
"goerli": {
|
||||
"message": "Goerli Test Network"
|
||||
},
|
||||
"rpc": {
|
||||
"message": "Custom RPC"
|
||||
},
|
||||
@ -1342,6 +1354,9 @@
|
||||
"selectAnAccountHelp": {
|
||||
"message": "Select the account to view in MetaMask"
|
||||
},
|
||||
"selectAnAsset": {
|
||||
"message": "Select an Asset"
|
||||
},
|
||||
"selectAHigherGasFee": {
|
||||
"message": "Select a higher gas fee to accelerate the processing of your transaction.*"
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_appName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "6.3.2",
|
||||
"version": "6.4.0",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "__MSG_appDescription__",
|
||||
|
@ -275,6 +275,7 @@ function blacklistedDomainCheck () {
|
||||
'harbourair.com',
|
||||
'ani.gamer.com.tw',
|
||||
'blueskybooking.com',
|
||||
'sharefile.com',
|
||||
]
|
||||
const currentUrl = window.location.href
|
||||
let currentRegex
|
||||
|
@ -49,6 +49,10 @@ function createNetworkAndChainIdMiddleware ({ network }) {
|
||||
netId = '42'
|
||||
chainId = '0x2a'
|
||||
break
|
||||
case 'goerli':
|
||||
netId = '5'
|
||||
chainId = '0x05'
|
||||
break
|
||||
default:
|
||||
throw new Error(`createInfuraClient - unknown network "${network}"`)
|
||||
}
|
||||
|
@ -3,16 +3,19 @@ const RINKEBY = 'rinkeby'
|
||||
const KOVAN = 'kovan'
|
||||
const MAINNET = 'mainnet'
|
||||
const LOCALHOST = 'localhost'
|
||||
const GOERLI = 'goerli'
|
||||
|
||||
const MAINNET_CODE = 1
|
||||
const ROPSTEN_CODE = 3
|
||||
const RINKEYBY_CODE = 4
|
||||
const KOVAN_CODE = 42
|
||||
const GOERLI_CODE = 5
|
||||
|
||||
const ROPSTEN_DISPLAY_NAME = 'Ropsten'
|
||||
const RINKEBY_DISPLAY_NAME = 'Rinkeby'
|
||||
const KOVAN_DISPLAY_NAME = 'Kovan'
|
||||
const MAINNET_DISPLAY_NAME = 'Main Ethereum Network'
|
||||
const GOERLI_DISPLAY_NAME = 'Goerli'
|
||||
|
||||
module.exports = {
|
||||
ROPSTEN,
|
||||
@ -20,12 +23,15 @@ module.exports = {
|
||||
KOVAN,
|
||||
MAINNET,
|
||||
LOCALHOST,
|
||||
GOERLI,
|
||||
MAINNET_CODE,
|
||||
ROPSTEN_CODE,
|
||||
RINKEYBY_CODE,
|
||||
KOVAN_CODE,
|
||||
GOERLI_CODE,
|
||||
ROPSTEN_DISPLAY_NAME,
|
||||
RINKEBY_DISPLAY_NAME,
|
||||
KOVAN_DISPLAY_NAME,
|
||||
MAINNET_DISPLAY_NAME,
|
||||
GOERLI_DISPLAY_NAME,
|
||||
}
|
||||
|
@ -20,8 +20,9 @@ const {
|
||||
KOVAN,
|
||||
MAINNET,
|
||||
LOCALHOST,
|
||||
GOERLI,
|
||||
} = require('./enums')
|
||||
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
|
||||
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, GOERLI]
|
||||
|
||||
const env = process.env.METAMASK_ENV
|
||||
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
|
||||
@ -140,10 +141,10 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
this.providerConfig = providerConfig
|
||||
}
|
||||
|
||||
async setProviderType (type) {
|
||||
async setProviderType (type, rpcTarget = '', ticker = 'ETH', nickname = '') {
|
||||
assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
|
||||
assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`)
|
||||
const providerConfig = { type }
|
||||
const providerConfig = { type, rpcTarget, ticker, nickname }
|
||||
this.providerConfig = providerConfig
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,16 @@ const {
|
||||
RINKEBY,
|
||||
KOVAN,
|
||||
MAINNET,
|
||||
GOERLI,
|
||||
ROPSTEN_CODE,
|
||||
RINKEYBY_CODE,
|
||||
KOVAN_CODE,
|
||||
GOERLI_CODE,
|
||||
ROPSTEN_DISPLAY_NAME,
|
||||
RINKEBY_DISPLAY_NAME,
|
||||
KOVAN_DISPLAY_NAME,
|
||||
MAINNET_DISPLAY_NAME,
|
||||
GOERLI_DISPLAY_NAME,
|
||||
} = require('./enums')
|
||||
|
||||
const networkToNameMap = {
|
||||
@ -17,9 +20,11 @@ const networkToNameMap = {
|
||||
[RINKEBY]: RINKEBY_DISPLAY_NAME,
|
||||
[KOVAN]: KOVAN_DISPLAY_NAME,
|
||||
[MAINNET]: MAINNET_DISPLAY_NAME,
|
||||
[GOERLI]: GOERLI_DISPLAY_NAME,
|
||||
[ROPSTEN_CODE]: ROPSTEN_DISPLAY_NAME,
|
||||
[RINKEYBY_CODE]: RINKEBY_DISPLAY_NAME,
|
||||
[KOVAN_CODE]: KOVAN_DISPLAY_NAME,
|
||||
[GOERLI_CODE]: GOERLI_DISPLAY_NAME,
|
||||
}
|
||||
|
||||
const getNetworkDisplayName = key => networkToNameMap[key]
|
||||
|
@ -8,8 +8,9 @@ const {
|
||||
RINKEBY,
|
||||
KOVAN,
|
||||
MAINNET,
|
||||
GOERLI,
|
||||
} = require('./network/enums')
|
||||
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
|
||||
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, GOERLI]
|
||||
|
||||
|
||||
class RecentBlocksController {
|
||||
|
@ -28,16 +28,16 @@ class TokenRatesController {
|
||||
async updateExchangeRates () {
|
||||
if (!this.isActive) { return }
|
||||
const contractExchangeRates = {}
|
||||
const nativeCurrency = this.currency ? this.currency.getState().nativeCurrency.toUpperCase() : 'ETH'
|
||||
const pairs = this._tokens.map(token => `pairs[]=${token.address}/${nativeCurrency}`)
|
||||
const query = pairs.join('&')
|
||||
const nativeCurrency = this.currency ? this.currency.getState().nativeCurrency.toLowerCase() : 'eth'
|
||||
const pairs = this._tokens.map(token => token.address).join(',')
|
||||
const query = `contract_addresses=${pairs}&vs_currencies=${nativeCurrency}`
|
||||
if (this._tokens.length > 0) {
|
||||
try {
|
||||
const response = await fetch(`https://exchanges.balanc3.net/pie?${query}&autoConversion=false`)
|
||||
const { prices = [] } = await response.json()
|
||||
prices.forEach(({ pair, price }) => {
|
||||
const address = pair.split('/')[0]
|
||||
contractExchangeRates[normalizeAddress(address)] = typeof price === 'number' ? price : 0
|
||||
const response = await fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`)
|
||||
const prices = await response.json()
|
||||
this._tokens.forEach(token => {
|
||||
const price = prices[token.address.toLowerCase()]
|
||||
contractExchangeRates[normalizeAddress(token.address)] = price ? price[nativeCurrency] : 0
|
||||
})
|
||||
} catch (error) {
|
||||
log.warn(`MetaMask - TokenRatesController exchange rate fetch failed.`, error)
|
||||
|
@ -218,6 +218,12 @@ inpageProvider.publicConfigStore.subscribe(function (state) {
|
||||
web3.eth.defaultAccount = state.selectedAddress
|
||||
})
|
||||
|
||||
inpageProvider.publicConfigStore.subscribe(function (state) {
|
||||
if (state.onboardingcomplete) {
|
||||
window.postMessage('onboardingcomplete', '*')
|
||||
}
|
||||
})
|
||||
|
||||
// need to make sure we aren't affected by overlapping namespaces
|
||||
// and that we dont affect the app with our namespace
|
||||
// mostly a fix for web3's BigNumber if AMD's "define" is defined...
|
||||
|
@ -28,6 +28,8 @@ function getBuyEthUrl ({ network, amount, address, service }) {
|
||||
return 'https://www.rinkeby.io/'
|
||||
case 'kovan-faucet':
|
||||
return 'https://github.com/kovan-testnet/faucet'
|
||||
case 'goerli-faucet':
|
||||
return 'https://goerli-faucet.slock.it/'
|
||||
}
|
||||
throw new Error(`Unknown cryptocurrency exchange or faucet: "${service}"`)
|
||||
}
|
||||
@ -42,6 +44,8 @@ function getDefaultServiceForNetwork (network) {
|
||||
return 'rinkeby-faucet'
|
||||
case '42':
|
||||
return 'kovan-faucet'
|
||||
case '5':
|
||||
return 'goerli-faucet'
|
||||
}
|
||||
throw new Error(`No default cryptocurrency exchange or faucet for networkId: "${network}"`)
|
||||
}
|
||||
|
@ -320,6 +320,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
const result = {
|
||||
selectedAddress: memState.isUnlocked ? memState.selectedAddress : undefined,
|
||||
networkVersion: memState.network,
|
||||
onboardingcomplete: memState.completedOnboarding,
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -230,7 +230,8 @@
|
||||
"kovan": "ok",
|
||||
"mainnet": "ok",
|
||||
"rinkeby": "ok",
|
||||
"ropsten": "ok"
|
||||
"ropsten": "ok",
|
||||
"goerli": "ok"
|
||||
},
|
||||
"lostAccounts": []
|
||||
},
|
||||
|
@ -704,7 +704,8 @@
|
||||
"mainnet": "ok",
|
||||
"ropsten": "ok",
|
||||
"kovan": "ok",
|
||||
"rinkeby": "ok"
|
||||
"rinkeby": "ok",
|
||||
"goerli": "ok"
|
||||
},
|
||||
"shapeShiftTxList": [],
|
||||
"lostAccounts": []
|
||||
|
5
docs/limited_site_access.md
Normal file
5
docs/limited_site_access.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Google Chrome/Brave Limited Site Access for Extensions
|
||||
|
||||
Problem: MetaMask doesn't work with limited site access enabled under Chrome's extensions.
|
||||
|
||||
Solution: In addition to the site you wish to whitelist, you must add 'api.infura.io' as another domain, so the MetaMask extension is authorized to make RPC calls to Infura.
|
@ -10,7 +10,7 @@ The `metamask-background` describes the file at `app/scripts/background.js`, whi
|
||||
|
||||
When a new site is visited, the WebExtension creates a new `ContentScript` in that page's context, which can be seen at `app/scripts/contentscript.js`. This script represents a per-page setup process, which creates the per-page `web3` api, connects it to the background script via the Port API (wrapped in a [stream abstraction](https://github.com/substack/stream-handbook)), and injected into the DOM before anything loads.
|
||||
|
||||
The most confusing part about porting MetaMask to a new platform is the way we provide the Web3 API over a series of streams between contexts. Once you understand how we create the [InpageProvider](../app/scripts/lib/inpage-provider.js) in the [inpage.js script](../app/scripts/inpage.js), you will be able to understand how the [port-stream](../app/scripts/lib/port-stream.js) is just a thin wrapper around the [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage), and a similar stream API can be wrapped around any communication channel to communicate with the `MetaMaskController` via its `setupUntrustedCommunication(stream, domain)` method.
|
||||
The most confusing part about porting MetaMask to a new platform is the way we provide the Web3 API over a series of streams between contexts. Once you understand how we create the [MetamaskInpageProvider](https://github.com/MetaMask/metamask-inpage-provider/blob/master/index.js) in the [inpage.js script](../app/scripts/inpage.js), you will be able to understand how the [port-stream](../app/scripts/lib/port-stream.js) is just a thin wrapper around the [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage), and a similar stream API can be wrapped around any communication channel to communicate with the `MetaMaskController` via its `setupUntrustedCommunication(stream, domain)` method.
|
||||
|
||||
### The MetaMask Controller
|
||||
|
||||
@ -89,7 +89,7 @@ MetaMask has two kinds of [duplex stream APIs](https://github.com/substack/strea
|
||||
|
||||
If you are making a MetaMask-powered browser for a new platform, one of the trickiest tasks will be injecting the Web3 API into websites that are visited. On WebExtensions, we actually have to pipe data through a total of three JS contexts just to let sites talk to our background process (site -> contentscript -> background).
|
||||
|
||||
To see how we do that, you can refer to the [inpage script](https://github.com/MetaMask/metamask-extension/blob/master/app/scripts/inpage.js) that we inject into every website. There you can see it creates a multiplex stream to the background, and uses it to initialize what we call the [inpage-provider](https://github.com/MetaMask/metamask-extension/blob/master/app/scripts/lib/inpage-provider.js), which you can see stubs a few methods out, but mostly just passes calls to `sendAsync` through the stream it's passed! That's really all the magic that's needed to create a web3-like API in a remote context, once you have a stream to MetaMask available.
|
||||
To see how we do that, you can refer to the [inpage script](https://github.com/MetaMask/metamask-extension/blob/master/app/scripts/inpage.js) that we inject into every website. There you can see it creates a multiplex stream to the background, and uses it to initialize what we call the [MetamaskInpageProvider](https://github.com/MetaMask/metamask-inpage-provider/blob/master/index.js), which you can see stubs a few methods out, but mostly just passes calls to `sendAsync` through the stream it's passed! That's really all the magic that's needed to create a web3-like API in a remote context, once you have a stream to MetaMask available.
|
||||
|
||||
In `inpage.js` you can see we create a `PortStream`, that's just a class we use to wrap WebExtension ports as streams, so we can reuse our favorite stream abstraction over the more irregular API surface of the WebExtension. In a new platform, you will probably need to construct this stream differently. The key is that you need to construct a stream that talks from the site context to the background. Once you have that set up, it works like magic!
|
||||
|
||||
|
@ -2,6 +2,13 @@
|
||||
|
||||
When publishing a new version of MetaMask, we follow this procedure:
|
||||
|
||||
## Overview
|
||||
|
||||
The below diagram outlines our process for design, development, and release. Building MetaMask is a community affair, and many steps of the process invite participation from external contributors as indicated. All QA, code review, and release of new versions is done by members of the core MetaMask team.
|
||||
|
||||
<img width="664" alt="mm-dev-process" src="https://user-images.githubusercontent.com/1016190/56308059-36906000-60fb-11e9-8e61-6655bca0c54f.png">
|
||||
|
||||
|
||||
## Preparation
|
||||
|
||||
We try to ensure certain criteria are met before deploying:
|
||||
|
103
package-lock.json
generated
103
package-lock.json
generated
@ -9759,8 +9759,8 @@
|
||||
}
|
||||
},
|
||||
"eth-contract-metadata": {
|
||||
"version": "github:MetaMask/eth-contract-metadata#92e7d1442c7585bfd24e50a0fda78df11dedadfe",
|
||||
"from": "github:MetaMask/eth-contract-metadata#92e7d1442c7585bfd24e50a0fda78df11dedadfe"
|
||||
"version": "github:MetaMask/eth-contract-metadata#41a14e8004bdd37eaba5af5f2bb1fc4f4ff7063f",
|
||||
"from": "github:MetaMask/eth-contract-metadata#41a14e8004bdd37eaba5af5f2bb1fc4f4ff7063f"
|
||||
},
|
||||
"eth-ens-namehash": {
|
||||
"version": "2.0.8",
|
||||
@ -9806,6 +9806,31 @@
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9814,8 +9839,7 @@
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
"bn.js": "^4.11.8"
|
||||
}
|
||||
},
|
||||
"ethereumjs-util": {
|
||||
@ -9896,6 +9920,31 @@
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9904,8 +9953,7 @@
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
"bn.js": "^4.11.8"
|
||||
}
|
||||
},
|
||||
"ethereumjs-util": {
|
||||
@ -10076,6 +10124,33 @@
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10084,14 +10159,14 @@
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
"bn.js": "^4.11.8"
|
||||
}
|
||||
},
|
||||
"ethereumjs-util": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
|
||||
"integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
@ -10192,8 +10267,7 @@
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
"bn.js": "^4.11.8"
|
||||
}
|
||||
},
|
||||
"ethereumjs-util": {
|
||||
@ -10324,9 +10398,9 @@
|
||||
}
|
||||
},
|
||||
"eth-method-registry": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eth-method-registry/-/eth-method-registry-1.0.0.tgz",
|
||||
"integrity": "sha1-8Ij3Wdad6f3BK3EEm83GiKMoOLY=",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eth-method-registry/-/eth-method-registry-1.2.0.tgz",
|
||||
"integrity": "sha512-m+nphH4kOxz5KTvQ+BeIKVggxAul1sp4Ev09lfxRXIEHM1t/6NQEtaErL5ddTDFXXFVtTiW8uC9edTVUTnBZNg==",
|
||||
"requires": {
|
||||
"ethjs": "^0.3.0"
|
||||
},
|
||||
@ -10502,8 +10576,7 @@
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
"bn.js": "^4.11.8"
|
||||
}
|
||||
},
|
||||
"ethereumjs-util": {
|
||||
|
@ -17,6 +17,8 @@
|
||||
"test:integration:build": "gulp build:scss",
|
||||
"test:e2e:drizzle:beta": "SELENIUM_BROWSER=chrome test/e2e/beta/run-drizzle.sh",
|
||||
"test:e2e:chrome": "SELENIUM_BROWSER=chrome test/e2e/beta/run-all.sh",
|
||||
"test:web3:chrome": "SELENIUM_BROWSER=chrome test/e2e/beta/run-web3.sh",
|
||||
"test:web3:firefox": "SELENIUM_BROWSER=firefox test/e2e/beta/run-web3.sh",
|
||||
"test:e2e:firefox": "SELENIUM_BROWSER=firefox test/e2e/beta/run-all.sh",
|
||||
"test:screens": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:screens:run'",
|
||||
"test:screens:run": "node test/screens/new-ui.js",
|
||||
@ -82,14 +84,14 @@
|
||||
"ensnare": "^1.0.0",
|
||||
"eth-bin-to-ops": "^1.0.1",
|
||||
"eth-block-tracker": "^4.1.0",
|
||||
"eth-contract-metadata": "github:MetaMask/eth-contract-metadata#92e7d1442c7585bfd24e50a0fda78df11dedadfe",
|
||||
"eth-contract-metadata": "github:MetaMask/eth-contract-metadata#41a14e8004bdd37eaba5af5f2bb1fc4f4ff7063f",
|
||||
"eth-ens-namehash": "^2.0.8",
|
||||
"eth-hd-keyring": "^1.2.2",
|
||||
"eth-json-rpc-filters": "^3.0.1",
|
||||
"eth-json-rpc-infura": "^3.0.0",
|
||||
"eth-keyring-controller": "^3.3.1",
|
||||
"eth-ledger-bridge-keyring": "^0.2.0",
|
||||
"eth-method-registry": "^1.0.0",
|
||||
"eth-method-registry": "^1.2.0",
|
||||
"eth-phishing-detect": "^1.1.4",
|
||||
"eth-query": "^2.1.2",
|
||||
"eth-sig-util": "^2.0.2",
|
||||
|
@ -54,7 +54,8 @@
|
||||
"mainnet": "degraded",
|
||||
"ropsten": "ok",
|
||||
"kovan": "ok",
|
||||
"rinkeby": "ok"
|
||||
"rinkeby": "ok",
|
||||
"goerli": "ok"
|
||||
}
|
||||
},
|
||||
"BlacklistController": {
|
||||
|
@ -123,7 +123,7 @@ describe('MetaMask', function () {
|
||||
})
|
||||
|
||||
it('clicks the "I agree" option on the metametrics opt-in screen', async () => {
|
||||
const optOutButton = await findElement(driver, By.css('.btn-confirm'))
|
||||
const optOutButton = await findElement(driver, By.css('.btn-primary'))
|
||||
optOutButton.click()
|
||||
await delay(largeDelayMs)
|
||||
})
|
||||
|
9
test/e2e/beta/run-web3.sh
Executable file
9
test/e2e/beta/run-web3.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
export PATH="$PATH:./node_modules/.bin"
|
||||
|
||||
shell-parallel -s 'static-server test/web3 --port 8080' -x 'sleep 5 && mocha test/e2e/beta/web3.spec'
|
365
test/e2e/beta/web3.spec.js
Normal file
365
test/e2e/beta/web3.spec.js
Normal file
@ -0,0 +1,365 @@
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
const webdriver = require('selenium-webdriver')
|
||||
const { By } = webdriver
|
||||
const {
|
||||
delay,
|
||||
buildChromeWebDriver,
|
||||
buildFirefoxWebdriver,
|
||||
installWebExt,
|
||||
getExtensionIdChrome,
|
||||
getExtensionIdFirefox,
|
||||
} = require('../func')
|
||||
const {
|
||||
checkBrowserForConsoleErrors,
|
||||
closeAllWindowHandlesExcept,
|
||||
findElement,
|
||||
findElements,
|
||||
openNewPage,
|
||||
switchToWindowWithTitle,
|
||||
verboseReportOnFailure,
|
||||
waitUntilXWindowHandles,
|
||||
} = require('./helpers')
|
||||
const fetchMockResponses = require('./fetch-mocks.js')
|
||||
|
||||
|
||||
describe('Using MetaMask with an existing account', function () {
|
||||
let extensionId
|
||||
let driver
|
||||
|
||||
const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress'
|
||||
const regularDelayMs = 1000
|
||||
const largeDelayMs = regularDelayMs * 2
|
||||
|
||||
const button = async (x) => {
|
||||
const buttoncheck = x
|
||||
await buttoncheck.click()
|
||||
await delay(largeDelayMs)
|
||||
const [results] = await findElements(driver, By.css('#results'))
|
||||
const resulttext = await results.getText()
|
||||
var parsedData = JSON.parse(resulttext)
|
||||
|
||||
return (parsedData)
|
||||
|
||||
}
|
||||
|
||||
this.timeout(0)
|
||||
this.bail(true)
|
||||
|
||||
before(async function () {
|
||||
let extensionUrl
|
||||
switch (process.env.SELENIUM_BROWSER) {
|
||||
case 'chrome': {
|
||||
const extensionPath = path.resolve('dist/chrome')
|
||||
driver = buildChromeWebDriver(extensionPath)
|
||||
extensionId = await getExtensionIdChrome(driver)
|
||||
await delay(regularDelayMs)
|
||||
extensionUrl = `chrome-extension://${extensionId}/home.html`
|
||||
break
|
||||
}
|
||||
case 'firefox': {
|
||||
const extensionPath = path.resolve('dist/firefox')
|
||||
driver = buildFirefoxWebdriver()
|
||||
await installWebExt(driver, extensionPath)
|
||||
await delay(regularDelayMs)
|
||||
extensionId = await getExtensionIdFirefox(driver)
|
||||
extensionUrl = `moz-extension://${extensionId}/home.html`
|
||||
break
|
||||
}
|
||||
}
|
||||
// Depending on the state of the application built into the above directory (extPath) and the value of
|
||||
// METAMASK_DEBUG we will see different post-install behaviour and possibly some extra windows. Here we
|
||||
// are closing any extraneous windows to reset us to a single window before continuing.
|
||||
const [tab1] = await driver.getAllWindowHandles()
|
||||
await closeAllWindowHandlesExcept(driver, [tab1])
|
||||
await driver.switchTo().window(tab1)
|
||||
await driver.get(extensionUrl)
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
await driver.executeScript(
|
||||
'window.origFetch = window.fetch.bind(window);' +
|
||||
'window.fetch = ' +
|
||||
'(...args) => { ' +
|
||||
'if (args[0] === "https://ethgasstation.info/json/ethgasAPI.json") { return ' +
|
||||
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' +
|
||||
'(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' +
|
||||
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } else if ' +
|
||||
'(args[0].match(/chromeextensionmm/)) { return ' +
|
||||
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.metametrics + '\')) }); } else if ' +
|
||||
'(args[0] === "https://dev.blockscale.net/api/gasexpress.json") { return ' +
|
||||
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.gasExpress + '\')) }); } ' +
|
||||
'return window.origFetch(...args); };' +
|
||||
'function cancelInfuraRequest(requestDetails) {' +
|
||||
'console.log("Canceling: " + requestDetails.url);' +
|
||||
'return {' +
|
||||
'cancel: true' +
|
||||
'};' +
|
||||
' }' +
|
||||
'window.chrome && window.chrome.webRequest && window.chrome.webRequest.onBeforeRequest.addListener(' +
|
||||
'cancelInfuraRequest,' +
|
||||
'{urls: ["https://*.infura.io/*"]},' +
|
||||
'["blocking"]' +
|
||||
');'
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
if (process.env.SELENIUM_BROWSER === 'chrome') {
|
||||
const errors = await checkBrowserForConsoleErrors(driver)
|
||||
if (errors.length) {
|
||||
const errorReports = errors.map(err => err.message)
|
||||
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
|
||||
console.error(new Error(errorMessage))
|
||||
}
|
||||
}
|
||||
if (this.currentTest.state === 'failed') {
|
||||
await verboseReportOnFailure(driver, this.currentTest)
|
||||
}
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await driver.quit()
|
||||
})
|
||||
|
||||
describe('First time flow starting from an existing seed phrase', () => {
|
||||
it('clicks the continue button on the welcome screen', async () => {
|
||||
await findElement(driver, By.css('.welcome-page__header'))
|
||||
const welcomeScreenBtn = await findElement(driver, By.css('.first-time-flow__button'))
|
||||
welcomeScreenBtn.click()
|
||||
await delay(largeDelayMs)
|
||||
})
|
||||
|
||||
it('clicks the "Import Wallet" option', async () => {
|
||||
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`))
|
||||
customRpcButton.click()
|
||||
await delay(largeDelayMs)
|
||||
})
|
||||
|
||||
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
|
||||
const optOutButton = await findElement(driver, By.css('.btn-default'))
|
||||
optOutButton.click()
|
||||
await delay(largeDelayMs)
|
||||
})
|
||||
|
||||
it('imports a seed phrase', async () => {
|
||||
const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea'))
|
||||
await seedTextArea.sendKeys(testSeedPhrase)
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const [password] = await findElements(driver, By.id('password'))
|
||||
await password.sendKeys('correct horse battery staple')
|
||||
const [confirmPassword] = await findElements(driver, By.id('confirm-password'))
|
||||
confirmPassword.sendKeys('correct horse battery staple')
|
||||
|
||||
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
|
||||
await tosCheckBox.click()
|
||||
|
||||
const [importButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
|
||||
await importButton.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('clicks through the success screen', async () => {
|
||||
await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
|
||||
const doneButton = await findElement(driver, By.css('button.first-time-flow__button'))
|
||||
await doneButton.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('opens dapp', () => {
|
||||
|
||||
it('switches to mainnet', async () => {
|
||||
const networkDropdown = await findElement(driver, By.css('.network-name'))
|
||||
await networkDropdown.click()
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const [mainnet] = await findElements(driver, By.xpath(`//span[contains(text(), 'Main Ethereum Network')]`))
|
||||
await mainnet.click()
|
||||
await delay(largeDelayMs * 2)
|
||||
})
|
||||
|
||||
it('', async () => {
|
||||
await openNewPage(driver, 'http://127.0.0.1:8080/')
|
||||
await delay(regularDelayMs)
|
||||
|
||||
await waitUntilXWindowHandles(driver, 3)
|
||||
const windowHandles = await driver.getAllWindowHandles()
|
||||
|
||||
const extension = windowHandles[0]
|
||||
const popup = await switchToWindowWithTitle(driver, 'MetaMask Notification', windowHandles)
|
||||
const dapp = windowHandles.find(handle => handle !== extension && handle !== popup)
|
||||
|
||||
await delay(regularDelayMs)
|
||||
const approveButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`))
|
||||
await approveButton.click()
|
||||
|
||||
await driver.switchTo().window(dapp)
|
||||
await delay(regularDelayMs)
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe('testing web3 methods', async () => {
|
||||
|
||||
|
||||
it('testing hexa methods', async () => {
|
||||
|
||||
|
||||
var List = await driver.findElements(By.className('hexaNumberMethods'))
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
var parsedData = await button(List[i])
|
||||
console.log(parsedData)
|
||||
var result = parseInt(parsedData.result, 16)
|
||||
|
||||
assert.equal((typeof result === 'number'), true)
|
||||
await delay(regularDelayMs)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('testing booleanMethods', async () => {
|
||||
|
||||
var List = await driver.findElements(By.className('booleanMethods'))
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
var parsedData = await button(List[i])
|
||||
console.log(parsedData)
|
||||
var result = parsedData.result
|
||||
|
||||
assert.equal(result, false)
|
||||
await delay(regularDelayMs)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
it('testing transactionMethods', async () => {
|
||||
|
||||
var List = await driver.findElements(By.className('transactionMethods'))
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
var parsedData = await button(List[i])
|
||||
|
||||
console.log(parsedData.result.blockHash)
|
||||
|
||||
var result = []
|
||||
result.push(parseInt(parsedData.result.blockHash, 16))
|
||||
result.push(parseInt(parsedData.result.blockNumber, 16))
|
||||
result.push(parseInt(parsedData.result.gas, 16))
|
||||
result.push(parseInt(parsedData.result.gasPrice, 16))
|
||||
result.push(parseInt(parsedData.result.hash, 16))
|
||||
result.push(parseInt(parsedData.result.input, 16))
|
||||
result.push(parseInt(parsedData.result.nonce, 16))
|
||||
result.push(parseInt(parsedData.result.r, 16))
|
||||
result.push(parseInt(parsedData.result.s, 16))
|
||||
result.push(parseInt(parsedData.result.v, 16))
|
||||
result.push(parseInt(parsedData.result.to, 16))
|
||||
result.push(parseInt(parsedData.result.value, 16))
|
||||
|
||||
|
||||
result.forEach((value) => {
|
||||
assert.equal((typeof value === 'number'), true)
|
||||
})
|
||||
|
||||
|
||||
} catch (err) {
|
||||
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
it('testing blockMethods', async () => {
|
||||
|
||||
var List = await driver.findElements(By.className('blockMethods'))
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
var parsedData = await button(List[i])
|
||||
console.log(JSON.stringify(parsedData) + i)
|
||||
|
||||
console.log(parsedData.result.parentHash)
|
||||
|
||||
var result = parseInt(parsedData.result.parentHash, 16)
|
||||
|
||||
assert.equal((typeof result === 'number'), true)
|
||||
await delay(regularDelayMs)
|
||||
} catch (err) {
|
||||
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('testing methods', async () => {
|
||||
|
||||
var List = await driver.findElements(By.className('methods'))
|
||||
var parsedData
|
||||
var result
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
if (i === 2) {
|
||||
|
||||
parsedData = await button(List[i])
|
||||
console.log(parsedData.result.blockHash)
|
||||
|
||||
result = parseInt(parsedData.result.blockHash, 16)
|
||||
|
||||
assert.equal((typeof result === 'number' || (result === 0)), true)
|
||||
await delay(regularDelayMs)
|
||||
} else {
|
||||
parsedData = await button(List[i])
|
||||
console.log(parsedData.result)
|
||||
|
||||
result = parseInt(parsedData.result, 16)
|
||||
|
||||
assert.equal((typeof result === 'number' || (result === 0)), true)
|
||||
await delay(regularDelayMs)
|
||||
}
|
||||
|
||||
|
||||
} catch (err) {
|
||||
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
@ -44,7 +44,7 @@ async function runConfirmSigRequestsTest (assert, done) {
|
||||
let confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
|
||||
assert.equal(confirmSigRowValue[0].textContent, '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0')
|
||||
|
||||
let confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large')
|
||||
let confirmSigSignButton = await queryAsync($, 'button.btn-secondary.btn--large')
|
||||
confirmSigSignButton[0].click()
|
||||
await timeout(1000)
|
||||
confirmSigHeadline = await queryAsync($, '.request-signature__headline')
|
||||
@ -53,7 +53,7 @@ async function runConfirmSigRequestsTest (assert, done) {
|
||||
confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
|
||||
assert.ok(confirmSigRowValue[0].textContent.match(/^#\sTerms\sof\sUse/))
|
||||
|
||||
confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large')
|
||||
confirmSigSignButton = await queryAsync($, 'button.btn-secondary.btn--large')
|
||||
confirmSigSignButton[0].click()
|
||||
await timeout(1000)
|
||||
confirmSigHeadline = await queryAsync($, '.request-signature__headline')
|
||||
@ -63,7 +63,7 @@ async function runConfirmSigRequestsTest (assert, done) {
|
||||
assert.equal(confirmSigRowValue[0].textContent, 'Hi, Alice!')
|
||||
assert.equal(confirmSigRowValue[1].textContent, '1337')
|
||||
|
||||
confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large')
|
||||
confirmSigSignButton = await queryAsync($, 'button.btn-secondary.btn--large')
|
||||
confirmSigSignButton[0].click()
|
||||
|
||||
await timeout(2000)
|
||||
|
@ -43,16 +43,13 @@ async function runSendFlowTest (assert, done) {
|
||||
selectState.val('send new ui')
|
||||
reactTriggerChange(selectState[0])
|
||||
|
||||
const sendScreenButton = await queryAsync($, 'button.btn-primary.transaction-view-balance__button')
|
||||
const sendScreenButton = await queryAsync($, 'button.btn-secondary.transaction-view-balance__button')
|
||||
assert.ok(sendScreenButton[1], 'send screen button present')
|
||||
sendScreenButton[1].click()
|
||||
|
||||
const sendTitle = await queryAsync($, '.page-container__title')
|
||||
assert.equal(sendTitle[0].textContent, 'Send ETH', 'Send screen title is correct')
|
||||
|
||||
const sendCopy = await queryAsync($, '.page-container__subtitle')
|
||||
assert.equal(sendCopy[0].textContent, 'Only send ETH to an Ethereum address.', 'Send screen has copy')
|
||||
|
||||
const sendFromField = await queryAsync($, '.send-v2__form-field')
|
||||
assert.ok(sendFromField[0], 'send screen has a from field')
|
||||
|
||||
@ -72,7 +69,7 @@ async function runSendFlowTest (assert, done) {
|
||||
const sendToAccountAddress = sendToFieldInput.val()
|
||||
assert.equal(sendToAccountAddress, '0x2f8D4a878cFA04A6E60D46362f5644DeAb66572D', 'send to dropdown selects the correct address')
|
||||
|
||||
const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(2)')
|
||||
const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(3)')
|
||||
sendAmountField.find('.unit-input')[0].click()
|
||||
|
||||
const sendAmountFieldInput = await findAsync(sendAmountField, '.unit-input__input')
|
||||
@ -88,7 +85,7 @@ 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')
|
||||
|
||||
const sendButton = await queryAsync($, 'button.btn-primary.btn--large.page-container__footer-button')
|
||||
const sendButton = await queryAsync($, 'button.btn-secondary.btn--large.page-container__footer-button')
|
||||
assert.equal(sendButton[0].textContent, 'Next', 'next button rendered')
|
||||
sendButton[0].click()
|
||||
await timeout()
|
||||
@ -115,14 +112,14 @@ async function runSendFlowTest (assert, done) {
|
||||
sendToFieldInputInEdit[0].focus()
|
||||
sendToFieldInputInEdit.val('0xd85a4b6a394794842887b8284293d69163007bbb')
|
||||
|
||||
const sendAmountFieldInEdit = await queryAsync($, '.send-v2__form-row:eq(2)')
|
||||
const sendAmountFieldInEdit = await queryAsync($, '.send-v2__form-row:eq(3)')
|
||||
sendAmountFieldInEdit.find('.unit-input')[0].click()
|
||||
|
||||
const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('.unit-input__input')
|
||||
sendAmountFieldInputInEdit.val('1.0')
|
||||
reactTriggerChange(sendAmountFieldInputInEdit[0])
|
||||
|
||||
const sendButtonInEdit = await queryAsync($, '.btn-primary.btn--large.page-container__footer-button')
|
||||
const sendButtonInEdit = await queryAsync($, '.btn-secondary.btn--large.page-container__footer-button')
|
||||
assert.equal(sendButtonInEdit[0].textContent, 'Next', 'next button in edit rendered')
|
||||
|
||||
selectState.val('send new ui')
|
||||
|
@ -4,7 +4,7 @@ const InfuraController = require('../../../../app/scripts/controllers/infura')
|
||||
|
||||
describe('infura-controller', function () {
|
||||
let infuraController, sandbox, networkStatus
|
||||
const response = {'mainnet': 'degraded', 'ropsten': 'ok', 'kovan': 'ok', 'rinkeby': 'down'}
|
||||
const response = {'mainnet': 'degraded', 'ropsten': 'ok', 'kovan': 'ok', 'rinkeby': 'down', 'goerli': 'ok'}
|
||||
|
||||
before(async function () {
|
||||
infuraController = new InfuraController()
|
||||
@ -58,5 +58,15 @@ describe('infura-controller', function () {
|
||||
assert.equal(networkStatus.rinkeby, 'down')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Goerli', function () {
|
||||
it('should have Goerli', function () {
|
||||
assert.equal(Object.keys(networkStatus)[4], 'goerli')
|
||||
})
|
||||
|
||||
it('should have a value for Goerli status', function () {
|
||||
assert.equal(networkStatus.goerli, 'ok')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -92,6 +92,9 @@ describe('Network utils', () => {
|
||||
}, {
|
||||
input: 'mainnet',
|
||||
expected: 'Main Ethereum Network',
|
||||
}, {
|
||||
input: 'goerli',
|
||||
expected: 'Goerli',
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -4,6 +4,7 @@ const {
|
||||
ROPSTEN_CODE,
|
||||
RINKEYBY_CODE,
|
||||
KOVAN_CODE,
|
||||
GOERLI_CODE,
|
||||
} = require('../../../../../app/scripts/controllers/network/enums')
|
||||
|
||||
const KeyringController = require('eth-keyring-controller')
|
||||
@ -27,14 +28,14 @@ describe('Recipient Blacklist Checker', function () {
|
||||
describe('#checkAccount', function () {
|
||||
it('does not fail on test networks', function () {
|
||||
let callCount = 0
|
||||
const networks = [ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE]
|
||||
const networks = [ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE, GOERLI_CODE]
|
||||
for (const networkId in networks) {
|
||||
publicAccounts.forEach((account) => {
|
||||
recipientBlackListChecker.checkAccount(networkId, account)
|
||||
callCount++
|
||||
})
|
||||
}
|
||||
assert.equal(callCount, 30)
|
||||
assert.equal(callCount, 40)
|
||||
})
|
||||
|
||||
it('fails on mainnet', function () {
|
||||
|
@ -23,4 +23,8 @@ describe('Etherscan Network Prefix', () => {
|
||||
assert.equal(etherscanNetworkPrefix(42), 'kovan.')
|
||||
})
|
||||
|
||||
it('returs goerli as prefix for networkId of 5', () => {
|
||||
assert.equal(etherscanNetworkPrefix(5), 'goerli.')
|
||||
})
|
||||
|
||||
})
|
||||
|
105
test/web3/index.html
Normal file
105
test/web3/index.html
Normal file
@ -0,0 +1,105 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Web3 Test Dapp</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;">hexaNumberMethods</div>
|
||||
<div style="display: flex;">
|
||||
<button id="eth_blockNumber" class="hexaNumberMethods">eth_blockNumber</button>
|
||||
|
||||
<button id="eth_gasPrice" class="hexaNumberMethods">eth_gasPrice</button>
|
||||
<button id="eth_newBlockFilter" class="hexaNumberMethods">eth_newBlockFilter</button>
|
||||
<button id="eth_newPendingTransactionFilter" class="hexaNumberMethods">
|
||||
eth_newPendingTransactionFilter
|
||||
</button>
|
||||
<button id="eth_getUncleCountByBlockHash" class="hexaNumberMethods">
|
||||
eth_getUncleCountByBlockHash
|
||||
</button>
|
||||
<button id="eth_getBlockTransactionCountByHash" class="hexaNumberMethods">
|
||||
getBlockTransactionCountByHash
|
||||
</button>
|
||||
</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_getTransactionCount" class="hexaNumberMethods">eth_getTransactionCount</button>
|
||||
<button id="eth_getBalance" class="hexaNumberMethods">eth_getBalance</button>
|
||||
<button id="eth_estimateGas" class="hexaNumberMethods">eth_estimateGas</button>
|
||||
</div>
|
||||
<div style="display: flex ;">
|
||||
|
||||
<button id="eth_getUncleCountByBlockNumber" class="hexaNumberMethods">
|
||||
eth_getUncleCountByBlockNumber
|
||||
</button>
|
||||
<button id='eth_getBlockTransactionCountByNumber' class="hexaNumberMethods">
|
||||
eth_getBlockTransactionCountByNumber
|
||||
</button>
|
||||
<button id="eth_protocolVersion" class="hexaNumberMethods">eth_protocolVersion</button>
|
||||
|
||||
<button id="eth_getCode" class="hexaNumberMethods">eth_getCode</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;">booleanMethods</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_uninstallFilter" class = 'booleanMethods'>eth_uninstallFilter</button>
|
||||
<button id="eth_mining" class = 'booleanMethods'>eth_mining</button>
|
||||
<button id="eth_syncing" class = 'booleanMethods'>eth_syncing</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;" >transactionMethods</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_getTransactionByHash" class='transactionMethods'>eth_getTransactionByHash</button>
|
||||
<button id="eth_getTransactionByBlockHashAndIndex" class = 'transactionMethods'>
|
||||
eth_getTransactionByBlockHashAndIndex
|
||||
</button>
|
||||
<button id="eth_getTransactionByBlockNumberAndIndex" class="transactionMethods">
|
||||
eth_getTransactionByBlockNumberAndIndex
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;">blockMethods</div>
|
||||
|
||||
<div style="display: flex ;">
|
||||
|
||||
|
||||
<button id="eth_getUncleByBlockHashAndIndex" class="blockMethods">
|
||||
eth_getUncleByBlockHashAndIndex
|
||||
</button>
|
||||
<button id="eth_getBlockByHash" class="blockMethods">eth_getBlockByHash</button>
|
||||
</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_getBlockByNumber" class="blockMethods">eth_getBlockByNumber</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;">Methods</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_call" class = 'methods'>eth_call</button>
|
||||
<button id="eth_getStorageAt" class="methods">eth_getStorageAt</button>
|
||||
<button id="eth_getTransactionReceipt" class="methods">
|
||||
eth_getTransactionReceipt
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div id='results'></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<script src="schema.js"></script>
|
||||
<script src="web3.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
209
test/web3/schema.js
Normal file
209
test/web3/schema.js
Normal file
@ -0,0 +1,209 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
|
||||
var params = {
|
||||
// diffrent params used in the methods
|
||||
param: [],
|
||||
blockHashParams: '0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35',
|
||||
filterParams: ['0xfe704947a3cd3ca12541458a4321c869'],
|
||||
transactionHashParams: [
|
||||
'0xbb3a336e3f823ec18197f1e13ee875700f08f03e2cab75f0d0b118dabb44cba0',
|
||||
],
|
||||
blockHashAndIndexParams: [
|
||||
'0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35',
|
||||
'0x0',
|
||||
],
|
||||
uncleByBlockNumberAndIndexParams: ['0x29c', '0x0'],
|
||||
blockParameterParams: '0x5bad55',
|
||||
data: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675',
|
||||
addressParams: '0xc94770007dda54cF92009BFF0dE90c06F603a09f',
|
||||
getStorageAtParams: [
|
||||
'0x295a70b2de5e3953354a6a8344e616ed314d7251',
|
||||
'0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9',
|
||||
'0x65a8db',
|
||||
],
|
||||
getCodeParams: ['0x06012c8cf97bead5deae237070f9587f8e7a266d', '0x65a8db'],
|
||||
estimateTransaction: {
|
||||
from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155',
|
||||
to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567',
|
||||
gas: '0x76c0',
|
||||
gasPrice: '0x9184e72a000',
|
||||
value: '0x9184e72a',
|
||||
data: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675',
|
||||
},
|
||||
filterGetLogs: [{'blockHash': '0x7c5a35e9cb3e8ae0e221ab470abae9d446c3a5626ce6689fc777dcffcab52c70', 'topics': ['0x241ea03ca20251805084d27d4440371c34a0b85ff108f6bb5611248f73818b80']}],
|
||||
block: {
|
||||
__required: [],
|
||||
number: 'Q',
|
||||
hash: 'D32',
|
||||
parentHash: 'D32',
|
||||
nonce: 'D',
|
||||
sha3Uncles: 'D',
|
||||
logsBloom: 'D',
|
||||
transactionsRoot: 'D',
|
||||
stateRoot: 'D',
|
||||
receiptsRoot: 'D',
|
||||
miner: 'D',
|
||||
difficulty: 'Q',
|
||||
totalDifficulty: 'Q',
|
||||
extraData: 'D',
|
||||
size: 'Q',
|
||||
gasLimit: 'Q',
|
||||
gasUsed: 'Q',
|
||||
timestamp: 'Q',
|
||||
transactions: ['DATA|Transaction'],
|
||||
uncles: ['D'],
|
||||
},
|
||||
transaction: {
|
||||
__required: [],
|
||||
hash: 'D32',
|
||||
nonce: 'Q',
|
||||
blockHash: 'D32',
|
||||
blockNumber: 'Q',
|
||||
transactionIndex: 'Q',
|
||||
from: 'D20',
|
||||
to: 'D20',
|
||||
value: 'Q',
|
||||
gasPrice: 'Q',
|
||||
gas: 'Q',
|
||||
input: 'D',
|
||||
},
|
||||
receipt: {
|
||||
__required: [],
|
||||
transactionHash: 'D32',
|
||||
transactionIndex: 'Q',
|
||||
blockHash: 'D32',
|
||||
blockNumber: 'Q',
|
||||
cumulativeGasUsed: 'Q',
|
||||
gasUsed: 'Q',
|
||||
contractAddress: 'D20',
|
||||
logs: ['FilterChange'],
|
||||
},
|
||||
|
||||
filterChange: {
|
||||
__required: [],
|
||||
removed: 'B',
|
||||
logIndex: 'Q',
|
||||
transactionIndex: 'Q',
|
||||
transactionHash: 'D32',
|
||||
blockHash: 'D32',
|
||||
blockNumber: 'Q',
|
||||
address: 'D20',
|
||||
data: 'Array|DATA',
|
||||
topics: ['D'],
|
||||
},
|
||||
}
|
||||
|
||||
var methods = {
|
||||
hexaNumberMethods: {
|
||||
// these are the methods which have output in the form of hexa decimal numbers
|
||||
eth_blockNumber: ['eth_blockNumber', params.param, 'Q'],
|
||||
eth_gasPrice: ['eth_gasPrice', params.param, 'Q'],
|
||||
eth_newBlockFilter: ['eth_newBlockFilter', params.param, 'Q'],
|
||||
eth_newPendingTransactionFilter: [
|
||||
'eth_newPendingTransactionFilter',
|
||||
params.param,
|
||||
'Q',
|
||||
],
|
||||
eth_getUncleCountByBlockHash: [
|
||||
'eth_getUncleCountByBlockHash',
|
||||
[params.blockHashParams],
|
||||
'Q',
|
||||
1,
|
||||
],
|
||||
eth_getBlockTransactionCountByHash: [
|
||||
'eth_getBlockTransactionCountByHash',
|
||||
[params.blockHashParams],
|
||||
'Q',
|
||||
1,
|
||||
],
|
||||
eth_getTransactionCount: [
|
||||
'eth_getTransactionCount',
|
||||
[params.addressParams, params.blockParameterParams],
|
||||
'Q',
|
||||
1,
|
||||
2,
|
||||
],
|
||||
eth_getBalance: ['eth_getBalance', [params.addressParams, 'latest'], 'Q', 1, 2],
|
||||
eth_estimateGas: ['eth_estimateGas', [params.estimateTransaction], 'Q', 1],
|
||||
eth_getUncleCountByBlockNumber: [
|
||||
'eth_getUncleCountByBlockNumber',
|
||||
[params.blockParameterParams],
|
||||
'Q',
|
||||
1,
|
||||
],
|
||||
eth_getBlockTransactionCountByNumber: [
|
||||
'eth_getBlockTransactionCountByNumber',
|
||||
['latest'],
|
||||
'Q',
|
||||
1,
|
||||
],
|
||||
eth_protocolVersion: ['eth_protocolVersion', params.param, 'S'],
|
||||
eth_getCode: ['eth_getCode', params.getCodeParams, 'D', 1, 2],
|
||||
},
|
||||
booleanMethods: {
|
||||
// these are the methods which have output in the form of boolean
|
||||
eth_uninstallFilter: ['eth_uninstallFilter', params.filterParams, 'B', 1],
|
||||
eth_mining: ['eth_mining', params.param, 'B'],
|
||||
eth_syncing: ['eth_syncing', params.param, 'B|EthSyncing'],
|
||||
},
|
||||
transactionMethods: {
|
||||
// these are the methods which have output in the form of transaction object
|
||||
eth_getTransactionByHash: [
|
||||
'eth_getTransactionByHash',
|
||||
params.transactionHashParams,
|
||||
params.transaction,
|
||||
1,
|
||||
],
|
||||
eth_getTransactionByBlockHashAndIndex: [
|
||||
'eth_getTransactionByBlockHashAndIndex',
|
||||
params.blockHashAndIndexParams,
|
||||
params.transaction,
|
||||
2,
|
||||
],
|
||||
eth_getTransactionByBlockNumberAndIndex: [
|
||||
'eth_getTransactionByBlockNumberAndIndex',
|
||||
[params.blockParameterParams, '0x0'],
|
||||
params.transaction,
|
||||
2,
|
||||
],
|
||||
|
||||
},
|
||||
blockMethods: {
|
||||
// these are the methods which have output in the form of a block
|
||||
|
||||
eth_getUncleByBlockNumberAndIndex: [
|
||||
'eth_getUncleByBlockNumberAndIndex',
|
||||
params.uncleByBlockNumberAndIndexParams,
|
||||
params.block,
|
||||
2,
|
||||
],
|
||||
eth_getBlockByHash: [
|
||||
'eth_getBlockByHash',
|
||||
[params.params, false],
|
||||
params.block,
|
||||
2,
|
||||
],
|
||||
eth_getBlockByNumber: [
|
||||
'eth_getBlockByNumber',
|
||||
[params.blockParameterParams, false],
|
||||
params.block,
|
||||
2,
|
||||
],
|
||||
},
|
||||
|
||||
methods: {
|
||||
// these are the methods which have output in the form of bytes data
|
||||
|
||||
eth_call: ['eth_call', [params.estimateTransaction, 'latest'], 'D', 1, 2],
|
||||
eth_getStorageAt: ['eth_getStorageAt', params.getStorageAtParams, 'D', 2, 2],
|
||||
eth_getTransactionReceipt: [
|
||||
'eth_getTransactionReceipt',
|
||||
params.transactionHashParams,
|
||||
params.receipt,
|
||||
1,
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
34
test/web3/web3.js
Normal file
34
test/web3/web3.js
Normal file
@ -0,0 +1,34 @@
|
||||
/* eslint no-undef: 0 */
|
||||
|
||||
var json = methods
|
||||
|
||||
web3.currentProvider.enable().then(() => {
|
||||
|
||||
Object.keys(json).forEach(methodGroupKey => {
|
||||
|
||||
console.log(methodGroupKey)
|
||||
const methodGroup = json[methodGroupKey]
|
||||
console.log(methodGroup)
|
||||
Object.keys(methodGroup).forEach(methodKey => {
|
||||
|
||||
const methodButton = document.getElementById(methodKey)
|
||||
methodButton.addEventListener('click', function (event) {
|
||||
|
||||
window.ethereum.sendAsync({
|
||||
method: methodKey,
|
||||
params: methodGroup[methodKey][1],
|
||||
}, function (err, result) {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
console.log(methodKey)
|
||||
} else {
|
||||
document.getElementById('results').innerHTML = JSON.stringify(result)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
@ -17,10 +17,7 @@
|
||||
}
|
||||
|
||||
&__button {
|
||||
font-size: 0.75rem;
|
||||
@extend %small-link;
|
||||
margin: 1rem;
|
||||
text-transform: uppercase;
|
||||
color: $curious-blue;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
const Component = require('react').Component
|
||||
const PropTypes = require('prop-types')
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../store/actions')
|
||||
|
||||
CoinbaseForm.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps)(CoinbaseForm)
|
||||
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
warning: state.appState.warning,
|
||||
}
|
||||
}
|
||||
|
||||
inherits(CoinbaseForm, Component)
|
||||
|
||||
function CoinbaseForm () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
CoinbaseForm.prototype.render = function () {
|
||||
var props = this.props
|
||||
|
||||
return h('.flex-column', {
|
||||
style: {
|
||||
marginTop: '35px',
|
||||
padding: '25px',
|
||||
width: '100%',
|
||||
},
|
||||
}, [
|
||||
h('.flex-row', {
|
||||
style: {
|
||||
justifyContent: 'space-around',
|
||||
margin: '33px',
|
||||
marginTop: '0px',
|
||||
},
|
||||
}, [
|
||||
h('button.btn-green', {
|
||||
onClick: this.toCoinbase.bind(this),
|
||||
}, this.context.t('continueToCoinbase')),
|
||||
|
||||
h('button.btn-red', {
|
||||
onClick: () => props.dispatch(actions.goHome()),
|
||||
}, this.context.t('cancel')),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
CoinbaseForm.prototype.toCoinbase = function () {
|
||||
const props = this.props
|
||||
const address = props.buyView.buyAddress
|
||||
props.dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
|
||||
}
|
||||
|
||||
CoinbaseForm.prototype.renderLoading = function () {
|
||||
return h('img', {
|
||||
style: {
|
||||
width: '27px',
|
||||
marginRight: '-27px',
|
||||
},
|
||||
src: 'images/loading.svg',
|
||||
})
|
||||
}
|
@ -18,11 +18,11 @@ const {
|
||||
MIN_GAS_PRICE_DEC,
|
||||
MIN_GAS_LIMIT_DEC,
|
||||
MIN_GAS_PRICE_GWEI,
|
||||
} = require('../send/send.constants')
|
||||
} = require('../../../pages/send/send.constants')
|
||||
|
||||
const {
|
||||
isBalanceSufficient,
|
||||
} = require('../send/send.utils')
|
||||
} = require('../../../pages/send/send.utils')
|
||||
|
||||
const {
|
||||
conversionUtil,
|
||||
@ -47,7 +47,7 @@ const {
|
||||
const {
|
||||
getGasPrice,
|
||||
getGasLimit,
|
||||
} = require('../send/send.selectors')
|
||||
} = require('../../../pages/send/send.selectors')
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const selectedToken = getSelectedToken(state)
|
||||
@ -382,7 +382,7 @@ CustomizeGasModal.prototype.render = function () {
|
||||
onClick: this.props.hideModal,
|
||||
}, [this.context.t('cancel')]),
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
className: 'send-v2__customize-gas__save',
|
||||
onClick: () => !error && this.save(newGasPrice, gasLimit, gasTotal),
|
||||
disabled: error,
|
||||
|
@ -204,6 +204,28 @@ NetworkDropdown.prototype.render = function () {
|
||||
]
|
||||
),
|
||||
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
key: 'goerli',
|
||||
closeMenu: () => this.props.hideNetworkDropdown(),
|
||||
onClick: () => this.handleClick('goerli'),
|
||||
style: dropdownMenuItemStyle,
|
||||
},
|
||||
[
|
||||
providerType === 'goerli' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
|
||||
h(NetworkDropdownIcon, {
|
||||
backgroundColor: '#3099f2', // $dodger-blue
|
||||
isSelected: providerType === 'goerli',
|
||||
}),
|
||||
h('span.network-name-item', {
|
||||
style: {
|
||||
color: providerType === 'goerli' ? '#ffffff' : '#9b9b9b',
|
||||
},
|
||||
}, this.context.t('goerli')),
|
||||
]
|
||||
),
|
||||
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
@ -285,6 +307,10 @@ NetworkDropdown.prototype.getNetworkName = function () {
|
||||
name = this.context.t('kovan')
|
||||
} else if (providerName === 'rinkeby') {
|
||||
name = this.context.t('rinkeby')
|
||||
} else if (providerName === 'localhost') {
|
||||
name = this.context.t('localhost')
|
||||
} else if (providerName === 'goerli') {
|
||||
name = this.context.t('goerli')
|
||||
} else {
|
||||
name = provider.nickname || this.context.t('unknownNetwork')
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ describe('Network Dropdown', () => {
|
||||
})
|
||||
|
||||
it('renders 7 DropDownMenuItems ', () => {
|
||||
assert.equal(wrapper.find(DropdownMenuItem).length, 7)
|
||||
assert.equal(wrapper.find(DropdownMenuItem).length, 8)
|
||||
})
|
||||
|
||||
it('checks background color for first NetworkDropdownIcon', () => {
|
||||
@ -82,16 +82,20 @@ describe('Network Dropdown', () => {
|
||||
})
|
||||
|
||||
it('checks background color for fifth NetworkDropdownIcon', () => {
|
||||
assert.equal(wrapper.find(NetworkDropdownIcon).at(4).prop('innerBorder'), '1px solid #9b9b9b')
|
||||
})
|
||||
|
||||
it('checks dropdown for frequestRPCList from state ', () => {
|
||||
assert.equal(wrapper.find(DropdownMenuItem).at(5).text(), '✓http://localhost:7545')
|
||||
assert.equal(wrapper.find(NetworkDropdownIcon).at(4).prop('backgroundColor'), '#3099f2') // Goerli Blue
|
||||
})
|
||||
|
||||
it('checks background color for sixth NetworkDropdownIcon', () => {
|
||||
assert.equal(wrapper.find(NetworkDropdownIcon).at(5).prop('innerBorder'), '1px solid #9b9b9b')
|
||||
})
|
||||
|
||||
it('checks dropdown for frequestRPCList from state ', () => {
|
||||
assert.equal(wrapper.find(DropdownMenuItem).at(6).text(), '✓http://localhost:7545')
|
||||
})
|
||||
|
||||
it('checks background color for seventh NetworkDropdownIcon', () => {
|
||||
assert.equal(wrapper.find(NetworkDropdownIcon).at(6).prop('innerBorder'), '1px solid #9b9b9b')
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
@ -10,7 +10,7 @@ const networkMap = require('ethjs-ens/lib/network-map.json')
|
||||
const ensRE = /.+\..+$/
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
||||
const connect = require('react-redux').connect
|
||||
const ToAutoComplete = require('./send/to-autocomplete').default
|
||||
const ToAutoComplete = require('../../pages/send/to-autocomplete').default
|
||||
const log = require('loglevel')
|
||||
const { isValidENSAddress } = require('../../helpers/utils/util')
|
||||
|
||||
|
@ -63,7 +63,7 @@ import {
|
||||
import {
|
||||
calcGasTotal,
|
||||
isBalanceSufficient,
|
||||
} from '../../send/send.utils'
|
||||
} from '../../../../pages/send/send.utils'
|
||||
import { addHexPrefix } from 'ethereumjs-util'
|
||||
import { getAdjacentGasPrices, extrapolateY } from '../gas-price-chart/gas-price-chart.utils'
|
||||
|
||||
|
@ -65,6 +65,7 @@
|
||||
.gas-price-button-group--small {
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
height: 54px;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
max-width: 260px;
|
||||
@ -80,10 +81,14 @@
|
||||
|
||||
&__label {
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
&__primary-currency {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
font-size: 10px;
|
||||
@ -92,6 +97,8 @@
|
||||
|
||||
&__secondary-currency {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
font-size: 10px;
|
||||
@ -99,19 +106,13 @@
|
||||
}
|
||||
|
||||
&__loading-container {
|
||||
height: 78px;
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
.button-group__button, .button-group__button--active {
|
||||
height: 78px;
|
||||
background: white;
|
||||
color: $scorpion;
|
||||
padding-top: 9px;
|
||||
padding-left: 8.5px;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
padding-left: 4px;
|
||||
}
|
||||
padding: 0 4px;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
|
@ -45,6 +45,10 @@ export default class LoadingNetworkScreen extends PureComponent {
|
||||
name = this.context.t('connectingToKovan')
|
||||
} else if (providerName === 'rinkeby') {
|
||||
name = this.context.t('connectingToRinkeby')
|
||||
} else if (providerName === 'localhost') {
|
||||
name = this.context.t('connectingToLocalhost')
|
||||
} else if (providerName === 'goerli') {
|
||||
name = this.context.t('connectingToGoerli')
|
||||
} else {
|
||||
name = this.context.t('connectingTo', [providerId])
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export default class Modal extends PureComponent {
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
submitType: 'primary',
|
||||
submitType: 'secondary',
|
||||
cancelType: 'default',
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ describe('Modal Component', () => {
|
||||
assert.equal(wrapper.find('.modal-container').length, 1)
|
||||
const buttons = wrapper.find(Button)
|
||||
assert.equal(buttons.length, 1)
|
||||
assert.equal(buttons.at(0).props().type, 'primary')
|
||||
assert.equal(buttons.at(0).props().type, 'secondary')
|
||||
})
|
||||
|
||||
it('should render a modal with a cancel and a submit button', () => {
|
||||
@ -38,7 +38,7 @@ describe('Modal Component', () => {
|
||||
cancelButton.simulate('click')
|
||||
assert.equal(handleCancel.callCount, 1)
|
||||
|
||||
assert.equal(submitButton.props().type, 'primary')
|
||||
assert.equal(submitButton.props().type, 'secondary')
|
||||
assert.equal(submitButton.props().children, 'Submit')
|
||||
assert.equal(handleSubmit.callCount, 0)
|
||||
submitButton.simulate('click')
|
||||
|
@ -84,7 +84,7 @@ AccountDetailsModal.prototype.render = function () {
|
||||
h('div.account-modal-divider'),
|
||||
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
className: 'account-modal__button',
|
||||
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
|
||||
}, this.context.t('etherscanView')),
|
||||
@ -92,7 +92,7 @@ AccountDetailsModal.prototype.render = function () {
|
||||
// Holding on redesign for Export Private Key functionality
|
||||
|
||||
exportPrivateKeyFeatureEnabled ? h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
className: 'account-modal__button',
|
||||
onClick: () => showExportPrivateKeyModal(),
|
||||
}, this.context.t('exportPrivateKey')) : null,
|
||||
|
@ -2,7 +2,7 @@ import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import GasModalCard from '../../customize-gas-modal/gas-modal-card'
|
||||
import { MIN_GAS_PRICE_GWEI } from '../../send/send.constants'
|
||||
import { MIN_GAS_PRICE_GWEI } from '../../../../pages/send/send.constants'
|
||||
import Button from '../../../ui/button'
|
||||
|
||||
import {
|
||||
@ -128,7 +128,7 @@ export default class CustomizeGas extends Component {
|
||||
{ t('cancel') }
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
type="secondary"
|
||||
className="customize-gas__save"
|
||||
onClick={() => {
|
||||
metricsEvent({
|
||||
|
@ -119,7 +119,7 @@ DepositEtherModal.prototype.renderRow = function ({
|
||||
|
||||
!hideButton && h('div.deposit-ether-modal__buy-row__button', [
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
className: 'deposit-ether-modal__deposit-button',
|
||||
large: true,
|
||||
onClick: onButtonClick,
|
||||
@ -133,7 +133,7 @@ DepositEtherModal.prototype.render = function () {
|
||||
const { network, toWyre, toCoinSwitch, address, toFaucet } = this.props
|
||||
const { buyingWithShapeshift } = this.state
|
||||
|
||||
const isTestNetwork = ['3', '4', '42'].find(n => n === network)
|
||||
const isTestNetwork = ['3', '4', '5', '42'].find(n => n === network)
|
||||
const networkName = getNetworkDisplayName(network)
|
||||
|
||||
return h('div.page-container.page-container--full-width.page-container--full-height', {}, [
|
||||
|
@ -66,7 +66,7 @@ EditAccountNameModal.prototype.render = function () {
|
||||
value: this.state.inputText,
|
||||
}, []),
|
||||
|
||||
h('button.btn-clear.edit-account-name-modal-save-button.allcaps', {
|
||||
h('button.button.btn-secondary.edit-account-name-modal-save-button.allcaps', {
|
||||
onClick: () => {
|
||||
if (this.state.inputText.length !== 0) {
|
||||
setAccountLabel(identity.address, this.state.inputText)
|
||||
|
@ -110,14 +110,14 @@ ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password,
|
||||
(privateKey
|
||||
? (
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
large: true,
|
||||
className: 'export-private-key__button',
|
||||
onClick: () => hideModal(),
|
||||
}, this.context.t('done'))
|
||||
) : (
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
large: true,
|
||||
className: 'export-private-key__button',
|
||||
onClick: () => this.exportAccountAndGetPrivateKey(this.state.password, address),
|
||||
|
@ -67,12 +67,12 @@ HideTokenConfirmationModal.prototype.render = function () {
|
||||
]),
|
||||
|
||||
h('div.hide-token-confirmation__buttons', {}, [
|
||||
h('button.btn-cancel.hide-token-confirmation__button.allcaps', {
|
||||
h('button.btn-default.hide-token-confirmation__button.btn--large', {
|
||||
onClick: () => hideModal(),
|
||||
}, [
|
||||
this.context.t('cancel'),
|
||||
]),
|
||||
h('button.btn-clear.hide-token-confirmation__button.allcaps', {
|
||||
h('button.btn-secondary.hide-token-confirmation__button.btn--large', {
|
||||
onClick: () => hideToken(address),
|
||||
}, [
|
||||
this.context.t('hide'),
|
||||
|
@ -37,11 +37,11 @@ class NotificationModal extends Component {
|
||||
|
||||
showButtons && h('div.notification-modal__buttons', [
|
||||
|
||||
showCancelButton && h('div.btn-cancel.notification-modal__buttons__btn', {
|
||||
showCancelButton && h('div.btn-default.notification-modal__buttons__btn', {
|
||||
onClick: hideModal,
|
||||
}, 'Cancel'),
|
||||
|
||||
showConfirmButton && h('div.btn-clear.notification-modal__buttons__btn', {
|
||||
showConfirmButton && h('div.button.btn-secondary.notification-modal__buttons__btn', {
|
||||
onClick: () => {
|
||||
onConfirm()
|
||||
hideModal()
|
||||
|
@ -26,6 +26,10 @@
|
||||
&--rinkeby {
|
||||
background-color: lighten($tulip-tree, 35%);
|
||||
}
|
||||
|
||||
&--goerli {
|
||||
background-color: lighten($dodger-blue, 35%);
|
||||
}
|
||||
}
|
||||
|
||||
&__name {
|
||||
@ -53,5 +57,9 @@
|
||||
&--rinkeby {
|
||||
background-color: $tulip-tree;
|
||||
}
|
||||
|
||||
&--goerli {
|
||||
background-color: $dodger-blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,14 @@ import {
|
||||
ROPSTEN_CODE,
|
||||
RINKEYBY_CODE,
|
||||
KOVAN_CODE,
|
||||
GOERLI_CODE,
|
||||
} from '../../../../../app/scripts/controllers/network/enums'
|
||||
|
||||
const networkToClassHash = {
|
||||
[MAINNET_CODE]: 'mainnet',
|
||||
[ROPSTEN_CODE]: 'ropsten',
|
||||
[RINKEYBY_CODE]: 'rinkeby',
|
||||
[GOERLI_CODE]: 'goerli',
|
||||
[KOVAN_CODE]: 'kovan',
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,9 @@ Network.prototype.render = function () {
|
||||
} else if (providerName === 'rinkeby') {
|
||||
hoverText = context.t('rinkeby')
|
||||
iconName = 'rinkeby-test-network'
|
||||
} else if (providerName === 'goerli') {
|
||||
hoverText = context.t('goerli')
|
||||
iconName = 'goerli-test-network'
|
||||
} else {
|
||||
hoverText = providerId
|
||||
iconName = 'private-network'
|
||||
@ -63,6 +66,7 @@ Network.prototype.render = function () {
|
||||
'ropsten-test-network': providerName === 'ropsten' || parseInt(networkNumber) === 3,
|
||||
'kovan-test-network': providerName === 'kovan',
|
||||
'rinkeby-test-network': providerName === 'rinkeby',
|
||||
'goerli-test-network': providerName === 'goerli',
|
||||
}),
|
||||
title: hoverText,
|
||||
onClick: (event) => {
|
||||
@ -113,33 +117,34 @@ Network.prototype.render = function () {
|
||||
h('.network-name', context.t('rinkeby')),
|
||||
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||
])
|
||||
case 'goerli-test-network':
|
||||
return h('.network-indicator', [
|
||||
h(NetworkDropdownIcon, {
|
||||
backgroundColor: '#3099f2', // $dodger-blue
|
||||
nonSelectBackgroundColor: '#ecb23e',
|
||||
loading: networkNumber === 'loading',
|
||||
}),
|
||||
h('.network-name', context.t('goerli')),
|
||||
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||
])
|
||||
default:
|
||||
return h('.network-indicator', [
|
||||
networkNumber === 'loading'
|
||||
? h('span.pointer.network-indicator', {
|
||||
style: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
? h('span.pointer.network-loading-spinner', {
|
||||
onClick: (event) => this.props.onClick(event),
|
||||
}, [
|
||||
h('img', {
|
||||
title: context.t('attemptingConnect'),
|
||||
style: {
|
||||
width: '27px',
|
||||
},
|
||||
src: 'images/loading.svg',
|
||||
}),
|
||||
])
|
||||
: h('i.fa.fa-question-circle.fa-lg', {
|
||||
style: {
|
||||
margin: '10px',
|
||||
color: 'rgb(125, 128, 130)',
|
||||
},
|
||||
}),
|
||||
|
||||
h('.network-name', providerNick || context.t('privateNetwork')),
|
||||
h('.network-name', providerName === 'localhost' ? context.t('localhost') : providerNick || context.t('privateNetwork')),
|
||||
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
|
||||
])
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ ShapeshiftForm.prototype.render = function () {
|
||||
]),
|
||||
|
||||
!depositAddress && h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
large: true,
|
||||
className: `${btnClass} shapeshift-form__shapeshift-buy-btn`,
|
||||
disabled: !token,
|
||||
|
@ -311,7 +311,7 @@ SignatureRequest.prototype.renderFooter = function () {
|
||||
},
|
||||
}, this.context.t('cancel')),
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
large: true,
|
||||
className: 'request-signature__footer__sign-button',
|
||||
onClick: event => {
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
setCustomGasLimit,
|
||||
} from '../../../ducks/gas/gas.duck'
|
||||
import { getIsMainnet, preferencesSelector, getSelectedAddress, conversionRateSelector } from '../../../selectors/selectors'
|
||||
import { isBalanceSufficient } from '../send/send.utils'
|
||||
import { isBalanceSufficient } from '../../../pages/send/send.utils'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { metamask: { knownMethodData, accounts } } = state
|
||||
|
@ -87,7 +87,7 @@ export default class TransactionViewBalance extends PureComponent {
|
||||
{
|
||||
!selectedToken && (
|
||||
<Button
|
||||
type="primary"
|
||||
type="secondary"
|
||||
className="transaction-view-balance__button"
|
||||
onClick={() => {
|
||||
metricsEvent({
|
||||
@ -105,14 +105,14 @@ export default class TransactionViewBalance extends PureComponent {
|
||||
)
|
||||
}
|
||||
<Button
|
||||
type="primary"
|
||||
type="secondary"
|
||||
className="transaction-view-balance__button"
|
||||
onClick={() => {
|
||||
metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Navigation',
|
||||
action: 'Home',
|
||||
name: 'Clicked Send',
|
||||
name: selectedToken ? 'Clicked Send: Token' : 'Clicked Send: Eth',
|
||||
},
|
||||
})
|
||||
history.push(SEND_ROUTE)
|
||||
|
@ -190,7 +190,7 @@ WalletView.prototype.render = function () {
|
||||
identities[selectedAddress].name,
|
||||
]),
|
||||
|
||||
h('button.btn-clear.wallet-view__details-button.allcaps', this.context.t('details')),
|
||||
h('button.btn-secondary.wallet-view__details-button', this.context.t('details')),
|
||||
]),
|
||||
]),
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import AccountListItem from '../../app/send/account-list-item/account-list-item.component'
|
||||
import AccountListItem from '../../../pages/send/account-list-item/account-list-item.component'
|
||||
|
||||
export default class AccountDropdownMini extends PureComponent {
|
||||
static propTypes = {
|
||||
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import AccountDropdownMini from '../account-dropdown-mini.component'
|
||||
import AccountListItem from '../../../app/send/account-list-item/account-list-item.component'
|
||||
import AccountListItem from '../../../../pages/send/account-list-item/account-list-item.component'
|
||||
|
||||
describe('AccountDropdownMini', () => {
|
||||
it('should render an account with an icon', () => {
|
||||
|
@ -5,7 +5,7 @@ import classnames from 'classnames'
|
||||
const CLASSNAME_DEFAULT = 'btn-default'
|
||||
const CLASSNAME_PRIMARY = 'btn-primary'
|
||||
const CLASSNAME_SECONDARY = 'btn-secondary'
|
||||
const CLASSNAME_CONFIRM = 'btn-confirm'
|
||||
const CLASSNAME_CONFIRM = 'btn-primary'
|
||||
const CLASSNAME_RAISED = 'btn-raised'
|
||||
const CLASSNAME_LARGE = 'btn--large'
|
||||
const CLASSNAME_FIRST_TIME = 'btn--first-time'
|
||||
@ -14,6 +14,11 @@ const typeHash = {
|
||||
default: CLASSNAME_DEFAULT,
|
||||
primary: CLASSNAME_PRIMARY,
|
||||
secondary: CLASSNAME_SECONDARY,
|
||||
warning: 'btn-warning',
|
||||
danger: 'btn-danger',
|
||||
'danger-primary': 'btn-danger-primary',
|
||||
link: 'btn-link',
|
||||
// TODO: Legacy button type to be deprecated
|
||||
confirm: CLASSNAME_CONFIRM,
|
||||
raised: CLASSNAME_RAISED,
|
||||
'first-time': CLASSNAME_FIRST_TIME,
|
||||
@ -38,7 +43,7 @@ export default class Button extends Component {
|
||||
<button
|
||||
className={classnames(
|
||||
'button',
|
||||
typeHash[type],
|
||||
typeHash[type] || CLASSNAME_DEFAULT,
|
||||
large && CLASSNAME_LARGE,
|
||||
className
|
||||
)}
|
||||
|
@ -2,57 +2,70 @@ import React from 'react'
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import { action } from '@storybook/addon-actions'
|
||||
import Button from '.'
|
||||
import { text } from '@storybook/addon-knobs/react'
|
||||
import { text, boolean } from '@storybook/addon-knobs/react'
|
||||
|
||||
// ', 'secondary', 'default', 'warning', 'danger', 'danger-primary', 'link'], 'primary')}
|
||||
storiesOf('Button', module)
|
||||
.add('primary', () =>
|
||||
.add('Button - Primary', () =>
|
||||
<Button
|
||||
onClick={action('clicked')}
|
||||
type="primary"
|
||||
disabled={boolean('disabled', false)}
|
||||
>
|
||||
{text('text', 'Click me')}
|
||||
</Button>
|
||||
)
|
||||
.add('secondary', () =>
|
||||
.add('Button - Secondary', () =>
|
||||
<Button
|
||||
onClick={action('clicked')}
|
||||
type="secondary"
|
||||
disabled={boolean('disabled', false)}
|
||||
>
|
||||
{text('text', 'Click me')}
|
||||
</Button>
|
||||
)
|
||||
.add('default', () => (
|
||||
.add('Button - Default', () =>
|
||||
<Button
|
||||
onClick={action('clicked')}
|
||||
type="default"
|
||||
disabled={boolean('disabled', false)}
|
||||
>
|
||||
{text('text', 'Click me')}
|
||||
</Button>
|
||||
))
|
||||
.add('large primary', () => (
|
||||
)
|
||||
.add('Button - Warning', () =>
|
||||
<Button
|
||||
onClick={action('clicked')}
|
||||
type="primary"
|
||||
large
|
||||
type="warning"
|
||||
disabled={boolean('disabled', false)}
|
||||
>
|
||||
{text('text', 'Click me')}
|
||||
</Button>
|
||||
))
|
||||
.add('large secondary', () => (
|
||||
)
|
||||
.add('Button - Danger', () =>
|
||||
<Button
|
||||
onClick={action('clicked')}
|
||||
type="secondary"
|
||||
large
|
||||
type="danger"
|
||||
disabled={boolean('disabled', false)}
|
||||
>
|
||||
{text('text', 'Click me')}
|
||||
</Button>
|
||||
))
|
||||
.add('large default', () => (
|
||||
)
|
||||
.add('Button - Danger Primary', () =>
|
||||
<Button
|
||||
onClick={action('clicked')}
|
||||
type="default"
|
||||
large
|
||||
type="danger-primary"
|
||||
disabled={boolean('disabled', false)}
|
||||
>
|
||||
{text('text', 'Click me')}
|
||||
</Button>
|
||||
))
|
||||
)
|
||||
.add('Button - Link', () =>
|
||||
<Button
|
||||
onClick={action('clicked')}
|
||||
type="link"
|
||||
disabled={boolean('disabled', false)}
|
||||
>
|
||||
{text('text', 'Click me')}
|
||||
</Button>
|
||||
)
|
||||
|
244
ui/app/components/ui/button/buttons.scss
Normal file
244
ui/app/components/ui/button/buttons.scss
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
Buttons
|
||||
*/
|
||||
|
||||
$hover-secondary: #B0D7F2;
|
||||
$hover-default: #B3B3B3;
|
||||
$hover-confirm: #0372C3;
|
||||
$hover-red: #FEB6BF;
|
||||
$hover-red-primary: #C72837;
|
||||
$hover-orange: #FFD3B5;
|
||||
|
||||
%button {
|
||||
@include h6;
|
||||
|
||||
font-weight: 500;
|
||||
font-family: Roboto, Arial;
|
||||
line-height: 1.25rem;
|
||||
padding: .75rem 1rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
transition: border-color .3s ease, background-color .3s ease;
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
cursor: auto;
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
%link {
|
||||
@include h4;
|
||||
|
||||
color: $Blue-500;
|
||||
line-height: 1.25rem;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
|
||||
&:hover {
|
||||
color: $Blue-400;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: $Blue-600;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
cursor: auto;
|
||||
opacity: 1;
|
||||
pointer-events: none;
|
||||
color: $hover-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
%small-link {
|
||||
@extend %link;
|
||||
@include h6;
|
||||
}
|
||||
|
||||
.button {
|
||||
@extend %button;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
color: $Blue-500;
|
||||
border: 2px solid $hover-secondary;
|
||||
|
||||
&:hover {
|
||||
border-color: $Blue-500;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $Blue-000;
|
||||
border-color: $Blue-500;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
opacity: 1;
|
||||
color: $hover-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
color: $Orange-500;
|
||||
border: 2px solid $hover-orange;
|
||||
|
||||
&:hover {
|
||||
border-color: $Orange-500;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $Orange-000;
|
||||
border-color: $Orange-500;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
opacity: 1;
|
||||
color: $hover-orange;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
color: $Red-500;
|
||||
border: 2px solid $hover-red;
|
||||
|
||||
&:hover {
|
||||
border-color: $Red-500;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $Red-000;
|
||||
border-color: $Red-500;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
opacity: 1;
|
||||
color: $hover-red;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-danger-primary {
|
||||
color: $white;
|
||||
border: 2px solid $Red-500;
|
||||
background-color: $Red-500;
|
||||
|
||||
&:hover {
|
||||
border-color: $hover-red-primary;
|
||||
background-color: $hover-red-primary;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $Red-600;
|
||||
border-color: $Red-600;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
opacity: 1;
|
||||
border-color: $hover-red;
|
||||
background-color: $hover-red;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
color: $Grey-500;
|
||||
border: 2px solid $hover-default;
|
||||
|
||||
&:hover {
|
||||
border-color: $Grey-500;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #FBFBFC;
|
||||
border-color: $Grey-500;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
opacity: 1;
|
||||
color: $hover-default;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: $white;
|
||||
border: 2px solid $Blue-500;
|
||||
background-color: $Blue-500;
|
||||
|
||||
&:hover {
|
||||
border-color: $hover-confirm;
|
||||
background-color: $hover-confirm;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $Blue-600;
|
||||
border-color: $Blue-600;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
border-color: $hover-secondary;
|
||||
background-color: $hover-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
@extend %link;
|
||||
}
|
||||
|
||||
.btn--large {
|
||||
min-height: 54px;
|
||||
}
|
||||
|
||||
/**
|
||||
All Buttons styles are deviations from design guide
|
||||
*/
|
||||
|
||||
.btn-raised {
|
||||
color: $curious-blue;
|
||||
background-color: $white;
|
||||
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 6px;
|
||||
height: initial;
|
||||
min-height: initial;
|
||||
width: initial;
|
||||
min-width: initial;
|
||||
}
|
||||
|
||||
.btn--first-time {
|
||||
height: 54px;
|
||||
width: 198px;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .14);
|
||||
color: $white;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
transition: 200ms ease-in-out;
|
||||
background-color: rgba(247, 134, 28, .9);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button[disabled],
|
||||
input[type="submit"][disabled] {
|
||||
cursor: not-allowed;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
button.primary {
|
||||
padding: 8px 12px;
|
||||
background: #f7861c;
|
||||
box-shadow: 0 3px 6px rgba(247, 134, 28, .36);
|
||||
color: $white;
|
||||
font-size: 1.1em;
|
||||
font-family: Roboto;
|
||||
text-transform: uppercase;
|
||||
}
|
@ -55,11 +55,6 @@
|
||||
border-top: 1px solid $geyser;
|
||||
flex: 0 0 auto;
|
||||
|
||||
.btn-default,
|
||||
.btn-confirm {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
@ -86,9 +81,6 @@
|
||||
}
|
||||
|
||||
&__footer-button {
|
||||
height: 55px;
|
||||
font-size: 1rem;
|
||||
text-transform: uppercase;
|
||||
margin-right: 16px;
|
||||
|
||||
&:last-of-type {
|
||||
|
@ -45,7 +45,7 @@ export default class PageContainerFooter extends Component {
|
||||
</Button>}
|
||||
|
||||
<Button
|
||||
type={submitButtonType || 'primary'}
|
||||
type={submitButtonType || 'secondary'}
|
||||
large
|
||||
className="page-container__footer-button"
|
||||
disabled={disabled}
|
||||
|
@ -7,7 +7,7 @@
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
color: #4d4d4d;
|
||||
font-size: 1rem;
|
||||
font-size: 16px;
|
||||
padding: 8px 10px;
|
||||
position: relative;
|
||||
|
||||
@ -29,6 +29,8 @@
|
||||
|
||||
&__inputs {
|
||||
flex: 1 0 auto;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
@ -38,15 +40,20 @@
|
||||
border: none;
|
||||
outline: 0 !important;
|
||||
max-width: 22ch;
|
||||
height: 16px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
&__input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
&__suffix {
|
||||
margin-left: 3px;
|
||||
font-size: 1rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
|
||||
&--error {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import { removeLeadingZeroes } from '../../app/send/send.utils'
|
||||
import { removeLeadingZeroes } from '../../../pages/send/send.utils'
|
||||
|
||||
/**
|
||||
* Component that attaches a suffix or unit of measurement trailing user input, ex. 'ETH'. Also
|
||||
|
@ -1,230 +0,0 @@
|
||||
/*
|
||||
Buttons
|
||||
*/
|
||||
|
||||
.button {
|
||||
min-height: 44px;
|
||||
background: $white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
transition: border-color .3s ease;
|
||||
padding: 0 16px;
|
||||
min-width: 140px;
|
||||
width: 100%;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
font-family: Roboto;
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
cursor: auto;
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: $curious-blue;
|
||||
border: 2px solid $spindle;
|
||||
|
||||
&:active {
|
||||
background: $zumthor;
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
color: $monzo;
|
||||
border: 2px solid lighten($monzo, 40%);
|
||||
|
||||
&:active {
|
||||
background: lighten($monzo, 55%);
|
||||
border-color: $monzo;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $monzo;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
color: $scorpion;
|
||||
border: 2px solid $dusty-gray;
|
||||
|
||||
&:active {
|
||||
background: $gallery;
|
||||
border-color: $dusty-gray;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $scorpion;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
color: $white;
|
||||
border: 2px solid $curious-blue;
|
||||
background-color: $curious-blue;
|
||||
}
|
||||
|
||||
.btn-raised {
|
||||
color: $curious-blue;
|
||||
background-color: $white;
|
||||
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 6px;
|
||||
height: initial;
|
||||
min-height: initial;
|
||||
width: initial;
|
||||
min-width: initial;
|
||||
}
|
||||
|
||||
.btn--first-time {
|
||||
height: 54px;
|
||||
width: 198px;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .14);
|
||||
color: $white;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
transition: 200ms ease-in-out;
|
||||
background-color: rgba(247, 134, 28, .9);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn--large {
|
||||
min-height: 54px;
|
||||
}
|
||||
|
||||
.btn-green {
|
||||
background-color: #02c9b1; // TODO: reusable color in colors.css
|
||||
}
|
||||
|
||||
.btn-clear {
|
||||
background: $white;
|
||||
text-align: center;
|
||||
padding: .8rem 1rem;
|
||||
color: $curious-blue;
|
||||
border: 2px solid $spindle;
|
||||
border-radius: 4px;
|
||||
font-size: .85rem;
|
||||
font-weight: 400;
|
||||
transition: border-color .3s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
cursor: auto;
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
background: $white;
|
||||
text-align: center;
|
||||
padding: .9rem 1rem;
|
||||
color: $scorpion;
|
||||
border: 2px solid $dusty-gray;
|
||||
border-radius: 4px;
|
||||
font-size: .85rem;
|
||||
font-weight: 400;
|
||||
transition: border-color .3s ease;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
border-color: $scorpion;
|
||||
}
|
||||
}
|
||||
|
||||
// No longer used in flat design, remove when modal buttons done
|
||||
// div.wallet-btn {
|
||||
// border: 1px solid rgb(91, 93, 103);
|
||||
// border-radius: 2px;
|
||||
// height: 30px;
|
||||
// width: 75px;
|
||||
// font-size: 0.8em;
|
||||
// text-align: center;
|
||||
// line-height: 25px;
|
||||
// }
|
||||
|
||||
// .btn-red {
|
||||
// background: rgba(254, 35, 17, 1);
|
||||
// box-shadow: 0px 3px 6px rgba(254, 35, 17, 0.36);
|
||||
// }
|
||||
|
||||
button[disabled],
|
||||
input[type="submit"][disabled] {
|
||||
cursor: not-allowed;
|
||||
opacity: .5;
|
||||
// background: rgba(197, 197, 197, 1);
|
||||
// box-shadow: 0 3px 6px rgba(197, 197, 197, .36);
|
||||
}
|
||||
|
||||
// button.spaced {
|
||||
// margin: 2px;
|
||||
// }
|
||||
|
||||
// button:not([disabled]):hover, input[type="submit"]:not([disabled]):hover {
|
||||
// transform: scale(1.1);
|
||||
// }
|
||||
// button:not([disabled]):active, input[type="submit"]:not([disabled]):active {
|
||||
// transform: scale(0.95);
|
||||
// }
|
||||
|
||||
button.primary {
|
||||
padding: 8px 12px;
|
||||
background: #f7861c;
|
||||
box-shadow: 0 3px 6px rgba(247, 134, 28, .36);
|
||||
color: $white;
|
||||
font-size: 1.1em;
|
||||
font-family: Roboto;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.btn-light {
|
||||
padding: 8px 12px;
|
||||
// background: #FFFFFF; // $bg-white
|
||||
box-shadow: 0 3px 6px rgba(247, 134, 28, .36);
|
||||
color: #585d67; // TODO: make reusable light button color
|
||||
font-size: 1.1em;
|
||||
font-family: Roboto;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #979797; // #TODO: make reusable light border color
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
// TODO: cleanup: not used anywhere
|
||||
button.btn-thin {
|
||||
border: 1px solid;
|
||||
border-color: #4d4d4d;
|
||||
color: #4d4d4d;
|
||||
background: rgb(255, 174, 41);
|
||||
border-radius: 4px;
|
||||
min-width: 200px;
|
||||
margin: 12px 0;
|
||||
padding: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.btn-tertiary {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px;
|
||||
background-color: transparent;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
padding: 16px 42px;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
@import './buttons.scss';
|
||||
@import '../../../components/ui/button/buttons';
|
||||
|
||||
@import './footer.scss';
|
||||
|
||||
|
@ -538,6 +538,8 @@
|
||||
}
|
||||
|
||||
&__button {
|
||||
@include paragraph;
|
||||
@extend %button;
|
||||
width: 141px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
@ -29,6 +29,10 @@
|
||||
&.rinkeby-test-network .menu-icon-circle div {
|
||||
background-color: rgba(235, 179, 63, .7) !important;
|
||||
}
|
||||
|
||||
&.goerli-test-network .menu-icon-circle div {
|
||||
background-color: rgba(48, 153, 242, .7) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-item {
|
||||
@ -48,6 +52,11 @@
|
||||
font-size: 12px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.fa-question-circle {
|
||||
margin: 0 4px 0 6px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.network-name {
|
||||
@ -165,5 +174,22 @@
|
||||
}
|
||||
|
||||
.network-caret {
|
||||
margin: 0 8px 2px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.network-loading-spinner {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-left: 5px;
|
||||
|
||||
img {
|
||||
height: 26px;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: -6px;
|
||||
}
|
||||
}
|
||||
|
@ -549,7 +549,7 @@
|
||||
}
|
||||
|
||||
&__form-row {
|
||||
margin: 14.5px 18px 0px;
|
||||
margin: 8px 18px 0px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
@ -592,8 +592,8 @@
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
&__from-dropdown {
|
||||
height: 73px;
|
||||
&__from-dropdown,
|
||||
&__asset-dropdown {
|
||||
width: 100%;
|
||||
border: 1px solid $alto;
|
||||
border-radius: 4px;
|
||||
@ -628,6 +628,112 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__from-dropdown {
|
||||
height: 73px;
|
||||
}
|
||||
|
||||
&__asset-dropdown {
|
||||
height: 54px;
|
||||
border: none;
|
||||
|
||||
&__asset {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($alto, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&__asset-icon {
|
||||
.identicon {
|
||||
border: 1px solid $alto;
|
||||
}
|
||||
}
|
||||
|
||||
&__asset-data {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&__symbol {
|
||||
font-size: 16px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&__name {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
font-size: 12px;
|
||||
|
||||
&__label {
|
||||
margin-right: .25rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__close-area {
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
&__list {
|
||||
z-index: 2050;
|
||||
position: absolute;
|
||||
height: 220px;
|
||||
width: 100%;
|
||||
border: 1px solid $geyser;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
box-shadow: 0 3px 6px 0 rgba(0 ,0 ,0 ,.11);
|
||||
top: 65px;
|
||||
left: 0;
|
||||
box-sizing: content-box;
|
||||
overflow-y: scroll;
|
||||
margin-top: 0;
|
||||
padding: 4px 0;
|
||||
|
||||
.send-v2__asset-dropdown__asset {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__input-wrapper {
|
||||
border: 1px solid $alto;
|
||||
border-radius: 4px;
|
||||
height: 100%;
|
||||
|
||||
&--opened {
|
||||
position: relative;
|
||||
z-index: 2050;
|
||||
}
|
||||
|
||||
.send-v2__asset-dropdown__asset {
|
||||
height: 100%;
|
||||
&:hover {
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__input {
|
||||
z-index: 1025;
|
||||
position: relative;
|
||||
height: 54px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
color: $tundora;
|
||||
padding: 10px;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
&__to-autocomplete {
|
||||
position: relative;
|
||||
|
||||
|
@ -18,6 +18,7 @@ body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 16px;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
overflow-y: overlay;
|
||||
|
@ -403,3 +403,40 @@
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@mixin fontScale($weight: 400, $size: 1rem) {
|
||||
font-weight: $weight;
|
||||
font-size: $size;
|
||||
}
|
||||
|
||||
@mixin h1($weight: 400, $size: 2.5rem){
|
||||
@include fontScale($weight, $size);
|
||||
}
|
||||
|
||||
@mixin h2($weight: 400, $size: 2rem){
|
||||
@include fontScale($weight, $size);
|
||||
}
|
||||
|
||||
@mixin h3($weight: 400, $size: 1.5rem){
|
||||
@include fontScale($weight, $size);
|
||||
}
|
||||
|
||||
@mixin h4($weight: 400, $size: 1.125rem){
|
||||
@include fontScale($weight, $size);
|
||||
}
|
||||
|
||||
@mixin h5($weight: 400, $size: 1rem){
|
||||
@include fontScale($weight, $size);
|
||||
}
|
||||
|
||||
@mixin h6($weight: 400, $size: .875rem){
|
||||
@include fontScale($weight, $size);
|
||||
}
|
||||
|
||||
@mixin h7($weight: 400, $size: .75rem){
|
||||
@include fontScale($weight, $size);
|
||||
}
|
||||
|
||||
@mixin paragraph($weight: 400, $size: 1rem){
|
||||
@include fontScale($weight, $size);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ $dusty-gray: #9b9b9b;
|
||||
$alto: #dedede;
|
||||
$alabaster: #fafafa;
|
||||
$silver-chalice: #aeaeae;
|
||||
$curious-blue: #2f9ae0;
|
||||
$curious-blue: #037DD6;
|
||||
$concrete: #f3f3f3;
|
||||
$tundora: #4d4d4d;
|
||||
$nile-blue: #1b344d;
|
||||
@ -93,3 +93,19 @@ $break-large: 576px;
|
||||
|
||||
$primary-font-type: Roboto;
|
||||
|
||||
$Blue-000: #eaf6ff;
|
||||
$Blue-400: #1098fc;
|
||||
$Blue-500: #037DD6;
|
||||
$Blue-600: #0260a4;
|
||||
|
||||
$Grey-000: #f2f3f4;
|
||||
$Grey-500: #6A737D;
|
||||
|
||||
$Red-000: #fcf2f3;
|
||||
$Red-500: #D73A49;
|
||||
$Red-600: #b92534;
|
||||
|
||||
$Orange-000: #fef5ef;
|
||||
$Orange-500: #F66A0A;
|
||||
|
||||
|
||||
|
@ -154,9 +154,26 @@ function reduceMetamask (state, action) {
|
||||
return newState
|
||||
|
||||
case actions.SET_SELECTED_TOKEN:
|
||||
return extend(metamaskState, {
|
||||
newState = extend(metamaskState, {
|
||||
selectedTokenAddress: action.value,
|
||||
})
|
||||
const newSend = extend(metamaskState.send)
|
||||
|
||||
if (metamaskState.send.editingTransactionId && !action.value) {
|
||||
delete newSend.token
|
||||
const unapprovedTx = newState.unapprovedTxs[newSend.editingTransactionId] || {}
|
||||
const txParams = unapprovedTx.txParams || {}
|
||||
newState.unapprovedTxs = extend(newState.unapprovedTxs, {
|
||||
[newSend.editingTransactionId]: extend(unapprovedTx, {
|
||||
txParams: extend(txParams, { data: '' }),
|
||||
}),
|
||||
})
|
||||
newSend.tokenBalance = null
|
||||
newSend.balance = '0'
|
||||
}
|
||||
|
||||
newState.send = newSend
|
||||
return newState
|
||||
|
||||
case actions.SET_ACCOUNT_LABEL:
|
||||
const account = action.value.account
|
||||
|
@ -10,4 +10,5 @@ export const NETWORK_TYPES = {
|
||||
MAINNET: 'mainnet',
|
||||
RINKEBY: 'rinkeby',
|
||||
ROPSTEN: 'ropsten',
|
||||
GOERLI: 'goerli',
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ const METAMETRICS_CUSTOM_ERROR_FIELD = 'errorField'
|
||||
const METAMETRICS_CUSTOM_ERROR_MESSAGE = 'errorMessage'
|
||||
const METAMETRICS_CUSTOM_RPC_NETWORK_ID = 'networkId'
|
||||
const METAMETRICS_CUSTOM_RPC_CHAIN_ID = 'chainId'
|
||||
const METAMETRICS_CUSTOM_GAS_CHANGED = 'gasChanged'
|
||||
|
||||
const METAMETRICS_CUSTOM_NETWORK = 'network'
|
||||
const METAMETRICS_CUSTOM_ENVIRONMENT_TYPE = 'environmentType'
|
||||
@ -43,6 +44,7 @@ const customVariableNameIdMap = {
|
||||
[METAMETRICS_CUSTOM_RPC_CHAIN_ID]: 2,
|
||||
[METAMETRICS_CUSTOM_ERROR_FIELD]: 1,
|
||||
[METAMETRICS_CUSTOM_ERROR_MESSAGE]: 2,
|
||||
[METAMETRICS_CUSTOM_GAS_CHANGED]: 1,
|
||||
}
|
||||
|
||||
const customDimensionsNameIdMap = {
|
||||
|
@ -30,6 +30,21 @@ export function getTokenData (data = '') {
|
||||
return abiDecoder.decodeMethod(data)
|
||||
}
|
||||
|
||||
async function getMethodFrom4Byte (fourBytePrefix) {
|
||||
const fourByteResponse = (await fetch(`https://www.4byte.directory/api/v1/signatures/?hex_signature=${fourBytePrefix}`, {
|
||||
referrerPolicy: 'no-referrer-when-downgrade',
|
||||
body: null,
|
||||
method: 'GET',
|
||||
mode: 'cors',
|
||||
})).json()
|
||||
|
||||
if (fourByteResponse.count === 1) {
|
||||
return fourByteResponse.results[0].text_signature
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const registry = new MethodRegistry({ provider: global.ethereumProvider })
|
||||
|
||||
/**
|
||||
@ -43,7 +58,16 @@ const registry = new MethodRegistry({ provider: global.ethereumProvider })
|
||||
const fourBytePrefix = prefixedData.slice(0, 10)
|
||||
|
||||
try {
|
||||
const sig = await registry.lookup(fourBytePrefix)
|
||||
const fourByteSig = getMethodFrom4Byte(fourBytePrefix).catch((e) => {
|
||||
log.error(e)
|
||||
return null
|
||||
})
|
||||
|
||||
let sig = await registry.lookup(fourBytePrefix)
|
||||
|
||||
if (!sig) {
|
||||
sig = await fourByteSig
|
||||
}
|
||||
|
||||
if (!sig) {
|
||||
return {}
|
||||
@ -57,8 +81,8 @@ const registry = new MethodRegistry({ provider: global.ethereumProvider })
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(error)
|
||||
const contractData = getTokenData(data)
|
||||
const { name } = contractData || {}
|
||||
const tokenData = getTokenData(data)
|
||||
const { name } = tokenData || {}
|
||||
return { name }
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
}
|
||||
|
||||
&__link {
|
||||
color: $curious-blue;
|
||||
@extend %link;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ export default class ConfirmAddSuggestedToken extends Component {
|
||||
{ this.context.t('cancel') }
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
type="secondary"
|
||||
large
|
||||
className="page-container__footer-button"
|
||||
onClick={() => {
|
||||
|
@ -18,7 +18,7 @@ const mapStateToProps = ({ metamask }) => {
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
addToken: ({address, symbol, decimals, image}) => dispatch(addToken(address, symbol, decimals, image)),
|
||||
addToken: ({address, symbol, decimals, image}) => dispatch(addToken(address, symbol, Number(decimals), image)),
|
||||
removeSuggestedTokens: () => dispatch(removeSuggestedTokens()),
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ export default class ConfirmAddToken extends Component {
|
||||
{ this.context.t('back') }
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
type="secondary"
|
||||
large
|
||||
className="page-container__footer-button"
|
||||
onClick={() => {
|
||||
|
@ -56,7 +56,7 @@ export default class ConfirmDeployContract extends Component {
|
||||
render () {
|
||||
return (
|
||||
<ConfirmTransactionBase
|
||||
action={this.context.t('contractDeployment')}
|
||||
actionKey={'contractDeployment'}
|
||||
dataComponent={this.renderData()}
|
||||
/>
|
||||
)
|
||||
|
@ -30,7 +30,7 @@ export default class ConfirmSendEther extends Component {
|
||||
|
||||
return (
|
||||
<ConfirmTransactionBase
|
||||
action={this.context.t('confirm')}
|
||||
actionKey={'confirm'}
|
||||
hideData={hideData}
|
||||
onEdit={confirmTransactionData => this.handleEdit(confirmTransactionData)}
|
||||
/>
|
||||
|
@ -4,7 +4,7 @@ import PropTypes from 'prop-types'
|
||||
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../app/scripts/lib/enums'
|
||||
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
|
||||
import ConfirmPageContainer, { ConfirmDetailRow } from '../../components/app/confirm-page-container'
|
||||
import { isBalanceSufficient } from '../../components/app/send/send.utils'
|
||||
import { isBalanceSufficient } from '../send/send.utils'
|
||||
import { DEFAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE } from '../../helpers/constants/routes'
|
||||
import {
|
||||
INSUFFICIENT_FUNDS_ERROR_KEY,
|
||||
@ -64,7 +64,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
updateGasAndCalculate: PropTypes.func,
|
||||
customGas: PropTypes.object,
|
||||
// Component props
|
||||
action: PropTypes.string,
|
||||
actionKey: PropTypes.string,
|
||||
contentComponent: PropTypes.node,
|
||||
dataComponent: PropTypes.node,
|
||||
detailsComponent: PropTypes.node,
|
||||
@ -159,7 +159,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
}
|
||||
|
||||
handleEditGas () {
|
||||
const { onEditGas, showCustomizeGasModal, action, txData: { origin }, methodData = {} } = this.props
|
||||
const { onEditGas, showCustomizeGasModal, actionKey, txData: { origin }, methodData = {} } = this.props
|
||||
|
||||
this.context.metricsEvent({
|
||||
eventOpts: {
|
||||
@ -169,7 +169,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
},
|
||||
customVariables: {
|
||||
recipientKnown: null,
|
||||
functionType: action || getMethodName(methodData.name) || this.context.t('contractInteraction'),
|
||||
functionType: actionKey || getMethodName(methodData.name) || 'contractInteraction',
|
||||
origin,
|
||||
},
|
||||
})
|
||||
@ -292,7 +292,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
}
|
||||
|
||||
handleEdit () {
|
||||
const { txData, tokenData, tokenProps, onEdit, action, txData: { origin }, methodData = {} } = this.props
|
||||
const { txData, tokenData, tokenProps, onEdit, actionKey, txData: { origin }, methodData = {} } = this.props
|
||||
|
||||
this.context.metricsEvent({
|
||||
eventOpts: {
|
||||
@ -302,7 +302,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
},
|
||||
customVariables: {
|
||||
recipientKnown: null,
|
||||
functionType: action || getMethodName(methodData.name) || this.context.t('contractInteraction'),
|
||||
functionType: actionKey || getMethodName(methodData.name) || 'contractInteraction',
|
||||
origin,
|
||||
},
|
||||
})
|
||||
@ -331,7 +331,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
|
||||
handleCancel () {
|
||||
const { metricsEvent } = this.context
|
||||
const { onCancel, txData, cancelTransaction, history, clearConfirmTransaction, action, txData: { origin }, methodData = {} } = this.props
|
||||
const { onCancel, txData, cancelTransaction, history, clearConfirmTransaction, actionKey, txData: { origin }, methodData = {} } = this.props
|
||||
|
||||
if (onCancel) {
|
||||
metricsEvent({
|
||||
@ -342,7 +342,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
},
|
||||
customVariables: {
|
||||
recipientKnown: null,
|
||||
functionType: action || getMethodName(methodData.name) || this.context.t('contractInteraction'),
|
||||
functionType: actionKey || getMethodName(methodData.name) || 'contractInteraction',
|
||||
origin,
|
||||
},
|
||||
})
|
||||
@ -358,7 +358,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
|
||||
handleSubmit () {
|
||||
const { metricsEvent } = this.context
|
||||
const { txData: { origin }, sendTransaction, clearConfirmTransaction, txData, history, onSubmit, action, metaMetricsSendCount = 0, setMetaMetricsSendCount, methodData = {} } = this.props
|
||||
const { txData: { origin }, sendTransaction, clearConfirmTransaction, txData, history, onSubmit, actionKey, metaMetricsSendCount = 0, setMetaMetricsSendCount, methodData = {} } = this.props
|
||||
const { submitting } = this.state
|
||||
|
||||
if (submitting) {
|
||||
@ -377,7 +377,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
},
|
||||
customVariables: {
|
||||
recipientKnown: null,
|
||||
functionType: action || getMethodName(methodData.name) || this.context.t('contractInteraction'),
|
||||
functionType: actionKey || getMethodName(methodData.name) || 'contractInteraction',
|
||||
origin,
|
||||
},
|
||||
})
|
||||
@ -517,7 +517,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
valid: propsValid = true,
|
||||
errorMessage,
|
||||
errorKey: propsErrorKey,
|
||||
action,
|
||||
actionKey,
|
||||
title,
|
||||
subtitle,
|
||||
hideSubtitle,
|
||||
@ -543,7 +543,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
toName={toName}
|
||||
toAddress={toAddress}
|
||||
showEdit={onEdit && !isTxReprice}
|
||||
action={action || getMethodName(name) || this.context.t('contractInteraction')}
|
||||
action={actionKey && this.context.t(actionKey) || getMethodName(name) || this.context.t('contractInteraction')}
|
||||
title={title}
|
||||
titleComponent={this.renderTitleComponent()}
|
||||
subtitle={subtitle}
|
||||
|
@ -14,9 +14,9 @@ import {
|
||||
GAS_LIMIT_TOO_LOW_ERROR_KEY,
|
||||
} from '../../helpers/constants/error-keys'
|
||||
import { getHexGasTotal } from '../../helpers/utils/confirm-tx.util'
|
||||
import { isBalanceSufficient, calcGasTotal } from '../../components/app/send/send.utils'
|
||||
import { isBalanceSufficient, calcGasTotal } from '../send/send.utils'
|
||||
import { conversionGreaterThan } from '../../helpers/utils/conversion-util'
|
||||
import { MIN_GAS_LIMIT_DEC } from '../../components/app/send/send.constants'
|
||||
import { MIN_GAS_LIMIT_DEC } from '../send/send.constants'
|
||||
import { checksumAddress, addressSlicer, valuesFor } from '../../helpers/utils/util'
|
||||
import {getMetaMaskAccounts, getAdvancedInlineGasShown, preferencesSelector, getIsMainnet} from '../../selectors/selectors'
|
||||
|
||||
|
@ -152,7 +152,7 @@ class AccountList extends Component {
|
||||
}, [this.context.t('cancel')]),
|
||||
|
||||
h(Button, {
|
||||
type: 'confirm',
|
||||
type: 'primary',
|
||||
large: true,
|
||||
className: 'new-account-connect-form__button unlock',
|
||||
disabled,
|
||||
|
@ -46,7 +46,7 @@ class ConnectScreen extends Component {
|
||||
this.renderConnectToTrezorButton(),
|
||||
]),
|
||||
h(Button, {
|
||||
type: 'confirm',
|
||||
type: 'primary',
|
||||
large: true,
|
||||
className: 'hw-connect__connect-btn',
|
||||
onClick: this.connect,
|
||||
|
@ -61,7 +61,7 @@ class JsonImportSubview extends Component {
|
||||
}, [this.context.t('cancel')]),
|
||||
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
large: true,
|
||||
className: 'new-account-create-form__button',
|
||||
onClick: () => this.createNewKeychain(),
|
||||
|
@ -75,7 +75,7 @@ PrivateKeyImportView.prototype.render = function () {
|
||||
}, [this.context.t('cancel')]),
|
||||
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
large: true,
|
||||
className: 'new-account-create-form__button',
|
||||
onClick: () => this.createNewKeychain(),
|
||||
|
@ -47,7 +47,7 @@ class NewAccountCreateForm extends Component {
|
||||
}, [this.context.t('cancel')]),
|
||||
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
type: 'secondary',
|
||||
large: true,
|
||||
className: 'new-account-create-form__button',
|
||||
onClick: () => {
|
||||
|
@ -36,6 +36,20 @@ export default class ImportWithSeedPhrase extends PureComponent {
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
window.onbeforeunload = () => this.context.metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Onboarding',
|
||||
action: 'Import Seed Phrase',
|
||||
name: 'Close window on import screen',
|
||||
},
|
||||
customVariables: {
|
||||
errorLabel: 'Seed Phrase Error',
|
||||
errorMessage: this.state.seedPhraseError,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
handleSeedPhraseChange (seedPhrase) {
|
||||
let seedPhraseError = ''
|
||||
|
||||
@ -172,6 +186,10 @@ export default class ImportWithSeedPhrase extends PureComponent {
|
||||
action: 'Import Seed Phrase',
|
||||
name: 'Go Back from Onboarding Import',
|
||||
},
|
||||
customVariables: {
|
||||
errorLabel: 'Seed Phrase Error',
|
||||
errorMessage: seedPhraseError,
|
||||
},
|
||||
})
|
||||
this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE)
|
||||
}}
|
||||
@ -243,7 +261,7 @@ export default class ImportWithSeedPhrase extends PureComponent {
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
type="confirm"
|
||||
type="primary"
|
||||
className="first-time-flow__button"
|
||||
disabled={!this.isValid() || !termsChecked}
|
||||
onClick={this.handleImport}
|
||||
|
@ -211,7 +211,7 @@ export default class NewAccount extends PureComponent {
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
type="confirm"
|
||||
type="primary"
|
||||
className="first-time-flow__button"
|
||||
disabled={!this.isValid() || !termsChecked}
|
||||
onClick={this.handleCreate}
|
||||
|
@ -34,7 +34,7 @@ export default class UniqueImageScreen extends PureComponent {
|
||||
{ t('protectYourKeysMessage2') }
|
||||
</div>
|
||||
<Button
|
||||
type="confirm"
|
||||
type="primary"
|
||||
className="first-time-flow__button"
|
||||
onClick={() => {
|
||||
this.context.metricsEvent({
|
||||
|
@ -71,7 +71,7 @@ export default class EndOfFlowScreen extends PureComponent {
|
||||
</a>.
|
||||
</div>
|
||||
<Button
|
||||
type="confirm"
|
||||
type="primary"
|
||||
className="first-time-flow__button"
|
||||
onClick={async () => {
|
||||
await completeOnboarding()
|
||||
|
@ -149,7 +149,7 @@ export default class MetaMetricsOptIn extends Component {
|
||||
})
|
||||
}}
|
||||
submitText={'I agree'}
|
||||
submitButtonType={'confirm'}
|
||||
submitButtonType={'primary'}
|
||||
disabled={false}
|
||||
/>
|
||||
<div className="metametrics-opt-in__bottom-text">
|
||||
|
@ -142,7 +142,7 @@ export default class ConfirmSeedPhrase extends PureComponent {
|
||||
}
|
||||
</div>
|
||||
<Button
|
||||
type="confirm"
|
||||
type="primary"
|
||||
className="first-time-flow__button"
|
||||
onClick={this.handleSubmit}
|
||||
disabled={!this.isValid()}
|
||||
|
@ -130,7 +130,7 @@ export default class RevealSeedPhrase extends PureComponent {
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="confirm"
|
||||
type="primary"
|
||||
className="first-time-flow__button"
|
||||
onClick={this.handleNext}
|
||||
disabled={!isShowingSeedPhrase}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user