diff --git a/common/badBrowserCheckA.js b/common/badBrowserCheckA.js new file mode 100644 index 00000000..ec2a1658 --- /dev/null +++ b/common/badBrowserCheckA.js @@ -0,0 +1,9 @@ + +// use dates as vars so they're not stripped out by minification +let letCheck = new Date(); +const constCheck = new Date(); +const arrowCheck = (() => new Date())(); + +if (letCheck && constCheck && arrowCheck) { + window.localStorage.setItem('goodBrowser', 'true'); +} diff --git a/common/badBrowserCheckB.js b/common/badBrowserCheckB.js new file mode 100644 index 00000000..51a82334 --- /dev/null +++ b/common/badBrowserCheckB.js @@ -0,0 +1,33 @@ +var badBrowser = false; + +try { + // Local storage + window.localStorage.setItem('test', 'test'); + window.localStorage.removeItem('test'); + + // Flexbox + var elTest = document.createElement('div'); + elTest.style.display = 'flex'; + if (elTest.style.display !== 'flex') { + badBrowser = true; + } + + // const and let check from badBrowserCheckA.js + if (window.localStorage.goodBrowser !== 'true') { + badBrowser = true; + } + window.localStorage.removeItem('goodBrowser'); + +} catch (err) { + badBrowser = true; +} + +if (badBrowser) { + var el = document.getElementsByClassName('BadBrowser')[0]; + el.className += ' is-open'; + // Dumb check for known mobile OS's. Not important to catch all, just + // displays more appropriate information. + if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) { + el.className += ' is-mobile'; + } +} diff --git a/common/components/AmountField.tsx b/common/components/AmountField.tsx index d104f183..2248b615 100644 --- a/common/components/AmountField.tsx +++ b/common/components/AmountField.tsx @@ -9,6 +9,7 @@ interface Props { hasUnitDropdown?: boolean; hasSendEverything?: boolean; showAllTokens?: boolean; + showInvalidWithoutValue?: boolean; customValidator?(rawAmount: string): boolean; } @@ -16,7 +17,8 @@ export const AmountField: React.SFC = ({ hasUnitDropdown, hasSendEverything, showAllTokens, - customValidator + customValidator, + showInvalidWithoutValue }) => ( ( @@ -30,6 +32,7 @@ export const AmountField: React.SFC = ({ value={raw} readOnly={!!readOnly} onChange={onChange} + showInvalidWithoutValue={showInvalidWithoutValue} /> {hasSendEverything && } {hasUnitDropdown && } diff --git a/common/components/BalanceSidebar/Promos.tsx b/common/components/BalanceSidebar/Promos.tsx index abf4e32e..863dd0ab 100644 --- a/common/components/BalanceSidebar/Promos.tsx +++ b/common/components/BalanceSidebar/Promos.tsx @@ -13,17 +13,7 @@ const CarouselAnimation = ({ children, ...props }: any) => ( ); // Don't change Coinbase index -const promos = [HardwareWallets, Coinbase, Shapeshift]; -const isEuroLocal = () => { - // getTimezoneOffset returns the difference in minutes between UTC and local time. - // the offset is positive if behind UTC (like UTC-4), and negative if above (like UTC+2) - const offset = new Date().getTimezoneOffset(); - // -240 to 0 covers UTC+4 to UTC+0, which is all of europe - return -240 <= offset && offset < 0; -}; -if (isEuroLocal()) { - promos.push(Simplex); -} +const promos = [HardwareWallets, Coinbase, Shapeshift, Simplex]; interface State { activePromo: number; @@ -35,7 +25,6 @@ interface StateProps { class PromosClass extends React.PureComponent { public timer: any = null; - public state = { activePromo: parseInt(String(Math.random() * promos.length), 10) }; diff --git a/common/components/ConfirmationModalTemplate/ConfirmationModalTemplate.tsx b/common/components/ConfirmationModalTemplate/ConfirmationModalTemplate.tsx index f0c87969..d07912cf 100644 --- a/common/components/ConfirmationModalTemplate/ConfirmationModalTemplate.tsx +++ b/common/components/ConfirmationModalTemplate/ConfirmationModalTemplate.tsx @@ -100,6 +100,7 @@ class ConfirmationModalTemplateClass extends React.Component { buttons={buttons} handleClose={onClose} disableButtons={transactionBroadcasting} + hideButtons={transactionBroadcasting} isOpen={isOpen} > {transactionBroadcasting ? ( diff --git a/common/components/NonceField.tsx b/common/components/NonceField.tsx index 32b889f5..c9831642 100644 --- a/common/components/NonceField.tsx +++ b/common/components/NonceField.tsx @@ -12,6 +12,7 @@ import './NonceField.scss'; interface OwnProps { alwaysDisplay: boolean; + showInvalidBeforeBlur?: boolean; } interface StateProps { @@ -27,7 +28,13 @@ type Props = OwnProps & DispatchProps & StateProps; class NonceField extends React.Component { public render() { - const { alwaysDisplay, requestNonce, noncePending, isOffline } = this.props; + const { + alwaysDisplay, + showInvalidBeforeBlur, + requestNonce, + noncePending, + isOffline + } = this.props; return ( { @@ -51,6 +58,7 @@ class NonceField extends React.Component { onChange={onChange} disabled={noncePending} showInvalidWithoutValue={true} + showInvalidBeforeBlur={showInvalidBeforeBlur} /> {noncePending ? (
diff --git a/common/components/TXMetaDataPanel/components/AdvancedGas.tsx b/common/components/TXMetaDataPanel/components/AdvancedGas.tsx index 92ac3359..98452d58 100644 --- a/common/components/TXMetaDataPanel/components/AdvancedGas.tsx +++ b/common/components/TXMetaDataPanel/components/AdvancedGas.tsx @@ -106,7 +106,7 @@ class AdvancedGas extends React.Component { )} {nonceField && (
- +
)}
diff --git a/common/components/ui/Input.tsx b/common/components/ui/Input.tsx index 34e31c0d..21dbaadc 100644 --- a/common/components/ui/Input.tsx +++ b/common/components/ui/Input.tsx @@ -49,7 +49,7 @@ class Input extends React.Component { } else if (!hasBlurred && !showInvalidBeforeBlur) { validClass = ''; } - if (!hasValue && showInvalidWithoutValue) { + if ((!isStateless || showInvalidBeforeBlur) && !hasValue && showInvalidWithoutValue) { validClass = 'invalid'; } diff --git a/common/components/ui/Modal/ModalBody.tsx b/common/components/ui/Modal/ModalBody.tsx index d82cba9f..8c74e41c 100644 --- a/common/components/ui/Modal/ModalBody.tsx +++ b/common/components/ui/Modal/ModalBody.tsx @@ -9,7 +9,8 @@ interface Props { modalStyle?: CSSProperties; hasButtons?: number; buttons?: IButton[]; - disableButtons?: any; + disableButtons?: boolean; + hideButtons?: boolean; handleClose(): void; } @@ -45,7 +46,7 @@ export default class ModalBody extends React.Component { }; public render() { - const { title, children, modalStyle, hasButtons, handleClose } = this.props; + const { title, children, modalStyle, hasButtons, hideButtons, handleClose } = this.props; return (
{
(this.modalContent = div as HTMLElement)}> {children} -
+
- {hasButtons &&
{this.renderButtons()}
} + {hasButtons && !hideButtons &&
{this.renderButtons()}
}
); } diff --git a/common/components/ui/Modal/index.tsx b/common/components/ui/Modal/index.tsx index 181d22f1..26c70547 100644 --- a/common/components/ui/Modal/index.tsx +++ b/common/components/ui/Modal/index.tsx @@ -15,6 +15,7 @@ interface Props { isOpen?: boolean; title?: React.ReactNode; disableButtons?: boolean; + hideButtons?: boolean; children: React.ReactNode; buttons?: IButton[]; maxWidth?: number; @@ -56,7 +57,16 @@ export default class Modal extends PureComponent { } public render() { - const { isOpen, title, children, buttons, handleClose, maxWidth } = this.props; + const { + isOpen, + title, + children, + buttons, + disableButtons, + hideButtons, + handleClose, + maxWidth + } = this.props; const hasButtons = buttons && buttons.length; const modalStyle: ModalStyle = {}; @@ -65,7 +75,16 @@ export default class Modal extends PureComponent { modalStyle.maxWidth = `${maxWidth}px`; } - const modalBodyProps = { title, children, modalStyle, hasButtons, buttons, handleClose }; + const modalBodyProps = { + title, + children, + modalStyle, + hasButtons, + buttons, + disableButtons, + hideButtons, + handleClose + }; const modal = ( diff --git a/common/config/contracts/rsk_testnet.json b/common/config/contracts/rsk_testnet.json new file mode 100644 index 00000000..cf8d3c9f --- /dev/null +++ b/common/config/contracts/rsk_testnet.json @@ -0,0 +1,8 @@ +[ + { + "name": "Bridge", + "address": "0x0000000000000000000000000000000001000006", + "abi": + "[{ \"name\": \"getFederationAddress\", \"type\": \"function\", \"constant\": true, \"inputs\": [], \"outputs\": [{ \"name\": \"\", \"type\": \"string\" }] }]" + } +] diff --git a/common/config/dpaths.ts b/common/config/dpaths.ts index 1449c582..5af627f2 100644 --- a/common/config/dpaths.ts +++ b/common/config/dpaths.ts @@ -83,6 +83,11 @@ export const RSK_TESTNET: DPath = { value: "m/44'/37310'/0'/0" }; +export const RSK_MAINNET: DPath = { + label: 'Mainnet (RSK)', + value: "m/44'/137'/0'/0" +}; + export const GO_DEFAULT: DPath = { label: 'Default (GO)', value: "m/44'/6060'/0'/0" @@ -114,6 +119,7 @@ export const DPaths: DPath[] = [ ETSC_DEFAULT, EGEM_DEFAULT, CLO_DEFAULT, + RSK_MAINNET, RSK_TESTNET, GO_DEFAULT, EOSC_DEFAULT, diff --git a/common/config/tokens/rsk.json b/common/config/tokens/rsk.json index fe51488c..0637a088 100644 --- a/common/config/tokens/rsk.json +++ b/common/config/tokens/rsk.json @@ -1 +1 @@ -[] +[] \ No newline at end of file diff --git a/common/config/tokens/rsk_testnet.json b/common/config/tokens/rsk_testnet.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/common/config/tokens/rsk_testnet.json @@ -0,0 +1 @@ +[] diff --git a/common/containers/Tabs/SendTransaction/components/Fields/Fields.tsx b/common/containers/Tabs/SendTransaction/components/Fields/Fields.tsx index 7cf85fa5..e55f26fc 100644 --- a/common/containers/Tabs/SendTransaction/components/Fields/Fields.tsx +++ b/common/containers/Tabs/SendTransaction/components/Fields/Fields.tsx @@ -54,7 +54,11 @@ class FieldsClass extends Component {
- +
{schedulingAvailable && (
diff --git a/common/containers/Tabs/SendTransaction/components/RequestPayment.tsx b/common/containers/Tabs/SendTransaction/components/RequestPayment.tsx index 3f7cfbbc..6a027e71 100644 --- a/common/containers/Tabs/SendTransaction/components/RequestPayment.tsx +++ b/common/containers/Tabs/SendTransaction/components/RequestPayment.tsx @@ -102,6 +102,7 @@ class RequestPayment extends React.Component { hasUnitDropdown={true} showAllTokens={true} customValidator={isValidAmount(decimal)} + showInvalidWithoutValue={true} />
diff --git a/common/features/config/networks/static/reducer.ts b/common/features/config/networks/static/reducer.ts index cab27ae8..b3a07312 100644 --- a/common/features/config/networks/static/reducer.ts +++ b/common/features/config/networks/static/reducer.ts @@ -21,6 +21,7 @@ import { ETSC_DEFAULT, EGEM_DEFAULT, CLO_DEFAULT, + RSK_MAINNET, RSK_TESTNET, GO_DEFAULT, EOSC_DEFAULT, @@ -205,7 +206,7 @@ export const STATIC_NETWORKS_INITIAL_STATE: StaticNetworksState = { isCustom: false, color: '#6d2eae', blockExplorer: makeExplorer({ - name: 'Etherchain Light', + name: 'POA Explorer', origin: 'https://poaexplorer.com', addressPath: 'address/search', blockPath: 'blocks/block' @@ -369,6 +370,32 @@ export const STATIC_NETWORKS_INITIAL_STATE: StaticNetworksState = { } }, + RSK: { + id: 'RSK', + name: 'RSK', + unit: 'SBTC', + chainId: 30, + color: '#58A052', + isCustom: false, + blockExplorer: makeExplorer({ + name: 'RSK Explorer', + origin: 'https://explorer.rsk.co' + }), + tokens: require('config/tokens/rsk.json'), + contracts: require('config/contracts/rsk.json'), + isTestnet: false, + dPathFormats: { + [SecureWalletName.TREZOR]: RSK_MAINNET, + [SecureWalletName.LEDGER_NANO_S]: RSK_MAINNET, + [InsecureWalletName.MNEMONIC_PHRASE]: RSK_MAINNET + }, + gasPriceSettings: { + min: 0.183, + max: 1.5, + initial: 0.183 + } + }, + RSK_TESTNET: { id: 'RSK_TESTNET', name: 'RSK', @@ -380,8 +407,8 @@ export const STATIC_NETWORKS_INITIAL_STATE: StaticNetworksState = { name: 'RSK Testnet Explorer', origin: 'https://explorer.testnet.rsk.co' }), - tokens: require('config/tokens/rsk.json'), - contracts: require('config/contracts/rsk.json'), + tokens: require('config/tokens/rsk_testnet.json'), + contracts: require('config/contracts/rsk_testnet.json'), isTestnet: true, dPathFormats: { [SecureWalletName.TREZOR]: RSK_TESTNET, diff --git a/common/index.html b/common/index.html index 0b00eab7..cac71d94 100644 --- a/common/index.html +++ b/common/index.html @@ -71,40 +71,6 @@
- diff --git a/common/index.tsx b/common/index.tsx index cd5212b0..41268c02 100644 --- a/common/index.tsx +++ b/common/index.tsx @@ -27,3 +27,12 @@ if (module.hot) { if (process.env.NODE_ENV === 'production') { consoleAdvertisement(); } + +const noOp = (event: DragEvent) => { + event.preventDefault(); + return false; +}; + +// disables drag-and-drop due to potential security issues by Cure53 recommendation +document.addEventListener('dragover', noOp, false); +document.addEventListener('drop', noOp, false); diff --git a/common/libs/nodes/configs.ts b/common/libs/nodes/configs.ts index a660f08c..18505415 100644 --- a/common/libs/nodes/configs.ts +++ b/common/libs/nodes/configs.ts @@ -108,9 +108,9 @@ export const NODE_CONFIGS: { [key in StaticNetworkIds]: RawNodeConfig[] } = { POA: [ { name: makeNodeName('POA', 'core'), - type: 'rpc', - service: 'poa.network', - url: 'https://core.poa.network' + type: 'infura', + service: 'poa.infura.io', + url: 'https://poa.infura.io' } ], @@ -168,6 +168,15 @@ export const NODE_CONFIGS: { [key in StaticNetworkIds]: RawNodeConfig[] } = { } ], + RSK: [ + { + name: makeNodeName('RSK', 'rsk_mainnet'), + type: 'rpc', + service: 'mycrypto.rsk.co', + url: 'https://mycrypto.rsk.co/' + } + ], + RSK_TESTNET: [ { name: makeNodeName('RSK_TESTNET', 'rsk_testnet'), diff --git a/common/libs/validators.ts b/common/libs/validators.ts index 358804f9..41981e4f 100644 --- a/common/libs/validators.ts +++ b/common/libs/validators.ts @@ -133,11 +133,20 @@ export const validPositiveNumber = (num: number) => validNumber(num) && num !== export const validDecimal = (input: string, decimal: number) => { const arr = input.split('.'); + + // Only a single decimal can exist. + if (arr.length > 2) { + return false; + } + const fractionPortion = arr[1]; + if (!fractionPortion || fractionPortion.length === 0) { return true; } + const decimalLength = fractionPortion.length; + return decimalLength <= decimal; }; diff --git a/common/translations/lang/en.json b/common/translations/lang/en.json index 0458db2f..5ff09533 100644 --- a/common/translations/lang/en.json +++ b/common/translations/lang/en.json @@ -643,7 +643,7 @@ "SHAPESHIFT_PROMO_2": "& Tokens", "COINBASE_PROMO_SMALL": "It’s now easier to get more ETH", "COINBASE_PROMO": "Buy ETH with USD", - "SIMPLEX_PROMO": "Buy ETH with EUR", + "SIMPLEX_PROMO": "Buy ETH with Credit Card", "TESTNET": "Testnet", "ENCLAVE_LEDGER_FAIL": "Failed to connect to Ledger", "ENCLAVE_LEDGER_IN_USE": "Your Ledger is currently in use with another application. Please wait, or close other wallet applications before trying again.", diff --git a/common/utils/consoleAdvertisement.ts b/common/utils/consoleAdvertisement.ts index dd78f6a7..66701f34 100644 --- a/common/utils/consoleAdvertisement.ts +++ b/common/utils/consoleAdvertisement.ts @@ -18,7 +18,7 @@ export default function consoleAdvertisement() { ┃ ,;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |__| | (_) | | | | | | |__| \\__ \\_| ┃ ┃ ;;;;;;;;;;;; ;;;;;;;;; ;;;;;;;;;;;; \\____/ \\___/|_|_| |_| \\____/|___(_) ┃ ┃ ;;;;;;;;;;;; ;;;;; ;;;;;;;;;;;, ┃ -┃ ;;;;;;;;;;;;: :;;;;;;;;;;;; https://github.com/MyCryptoHQ/MyCrypto ┃ +┃ ;;;;;;;;;;;;: :;;;;;;;;;;;; https://about.mycrypto.com/jobs ┃ ┃ ;;;;;;;;;;;;;: :;;;;;;;;;;;;; ┃ ┃ ;;;;;;;;;;;;;;: :;;;;;;;;;;;;;; ┃ ┃ ';;;;;;;;;;;;;;;;;;;;;;;;;;;;; ┃ diff --git a/shared/types/network.d.ts b/shared/types/network.d.ts index 5913b900..8a7a8554 100644 --- a/shared/types/network.d.ts +++ b/shared/types/network.d.ts @@ -13,6 +13,7 @@ type StaticNetworkIds = | 'ETSC' | 'EGEM' | 'CLO' + | 'RSK' | 'RSK_TESTNET' | 'GO' | 'EOSC' diff --git a/spec/libs/validators.spec.ts b/spec/libs/validators.spec.ts index 342f6ed9..8dc74a3f 100644 --- a/spec/libs/validators.spec.ts +++ b/spec/libs/validators.spec.ts @@ -16,6 +16,7 @@ configuredStore.getState(); const VALID_BTC_ADDRESS = '1MEWT2SGbqtz6mPCgFcnea8XmWV5Z4Wc6'; const VALID_ETH_ADDRESS = '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8'; const VALID_RSK_TESTNET_ADDRESS = '0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd'; +const VALID_RSK_MAINNET_ADDRESS = '0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD'; const VALID_ETH_PRIVATE_KEY = '3f4fd89ea4970cc77bfd2d07a95786575ea62e183857afe6301578e1a3c5c782'; const INVALID_ETH_PRIVATE_KEY = '3f4fd89ea4970cc77bfd2d07a95786575ea62e183857afe6301578e1a3c5ZZZZ'; const VALID_ETH_PRIVATE_BUFFER = Buffer.from(VALID_ETH_PRIVATE_KEY, 'hex'); @@ -50,6 +51,12 @@ describe('Validator', () => { it('should validate correct RSK address in RSK mainnet network as false', () => { expect(isValidAddress(VALID_RSK_TESTNET_ADDRESS, RSK_MAINNET_CHAIN_ID)).toBeFalsy(); }); + it('should validate correct RSK address in RSK mainnet network as true', () => { + expect(isValidAddress(VALID_RSK_MAINNET_ADDRESS, RSK_MAINNET_CHAIN_ID)).toBeTruthy(); + }); + it('should validate correct RSK mainnet address in RSK testnet network as false', () => { + expect(isValidAddress(VALID_RSK_MAINNET_ADDRESS, RSK_TESTNET_CHAIN_ID)).toBeFalsy(); + }); it('should validate an incorrect DPath as false', () => { expect(isValidPath('m/44/60/0/0')).toBeFalsy(); }); diff --git a/webpack_config/makeConfig.js b/webpack_config/makeConfig.js index 365c2b09..d9304990 100644 --- a/webpack_config/makeConfig.js +++ b/webpack_config/makeConfig.js @@ -32,6 +32,8 @@ module.exports = function(opts = {}) { // ====== Entry ======= // ==================== const entry = { + badBrowserCheckA: './common/badBrowserCheckA.js', + badBrowserCheckB: './common/badBrowserCheckB.js', client: './common/index.tsx' };