aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/_locales/en/messages.json26
-rw-r--r--app/_locales/ko/messages.json514
-rw-r--r--app/scripts/controllers/transactions/index.js4
-rw-r--r--development/sentry-publish.js38
-rw-r--r--mascara/src/app/first-time/index.css2
-rw-r--r--package-lock.json233
-rw-r--r--package.json1
-rw-r--r--test/data/2-state.json70
-rw-r--r--test/unit/app/controllers/transactions/tx-controller-test.js30
-rw-r--r--test/unit/ui/app/actions.spec.js1468
-rw-r--r--ui/app/actions.js12
11 files changed, 1969 insertions, 429 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 6b76140f2..d8467e9eb 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -14,6 +14,9 @@
"accountName": {
"message": "Account Name"
},
+ "accountOptions": {
+ "message": "Account Options"
+ },
"accountSelectionRequired": {
"message": "You need to select an account!"
},
@@ -726,9 +729,6 @@
"openInTab": {
"message": "Open in tab"
},
- "accountOptions": {
- "message": "Account Options"
- },
"or": {
"message": "or",
"description": "choice between creating or importing a new account"
@@ -745,22 +745,22 @@
"parameters": {
"message": "Parameters"
},
- "passwordNotLongEnough": {
- "message": "Password not long enough"
- },
- "passwordsDontMatch": {
- "message": "Passwords Don't Match"
- },
"password": {
"message": "Password"
},
"passwordCorrect": {
"message": "Please make sure your password is correct."
},
+ "passwordsDontMatch": {
+ "message": "Passwords Don't Match"
+ },
"passwordMismatch": {
"message": "passwords don't match",
"description": "in password creation process, the two new password fields did not match"
},
+ "passwordNotLongEnough": {
+ "message": "Password not long enough"
+ },
"passwordShort": {
"message": "password not long enough",
"description": "in password creation process, the password is not long enough to be secure"
@@ -863,9 +863,6 @@
"retryWithMoreGas": {
"message": "Retry with a higher gas price here"
},
- "walletSeed": {
- "message": "Wallet Seed"
- },
"restore": {
"message": "Restore"
},
@@ -1225,7 +1222,7 @@
"message": "Ooops! Something went wrong...."
},
"unknownCameraError": {
- "message": "There was an error while trying to access you camera. Please try again..."
+ "message": "There was an error while trying to access your camera. Please try again..."
},
"unlock": {
"message": "Unlock"
@@ -1264,6 +1261,9 @@
"visitWebSite": {
"message": "Visit our web site"
},
+ "walletSeed": {
+ "message": "Wallet Seed"
+ },
"warning": {
"message": "Warning"
},
diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json
index 5bc539e70..55549bb87 100644
--- a/app/_locales/ko/messages.json
+++ b/app/_locales/ko/messages.json
@@ -2,6 +2,9 @@
"accept": {
"message": "수락"
},
+ "accessingYourCamera": {
+ "message": "카메라에 접근 중..."
+ },
"account": {
"message": "계정"
},
@@ -11,6 +14,9 @@
"accountName": {
"message": "계정 이름"
},
+ "accountSelectionRequired": {
+ "message": "계정을 선택하셔야 합니다!"
+ },
"address": {
"message": "주소"
},
@@ -40,6 +46,9 @@
"message": "메타마스크",
"description": "애플리케이션 이름"
},
+ "approve": {
+ "message": "수락"
+ },
"approved": {
"message": "수락"
},
@@ -75,7 +84,10 @@
"message": "Blockies 아이덴티콘 사용"
},
"borrowDharma": {
- "message": "Dharma에서 대여하기(Beta)"
+ "message": "Dharma에서 대출받기 (Beta)"
+ },
+ "browserNotSupported": {
+ "message": "브라우저를 지원하지 않습니다..."
},
"builtInCalifornia": {
"message": "메타마스크는 캘리포니아에서 디자인되고 만들어졌습니다."
@@ -89,6 +101,9 @@
"buyCoinbaseExplainer": {
"message": "코인베이스는 비트코인, 이더리움, 라이트코인을 거래할 수 있는 유명한 거래소입니다."
},
+ "bytes": {
+ "message": "바이트"
+ },
"ok": {
"message": "확인"
},
@@ -104,6 +119,9 @@
"close": {
"message": "닫기"
},
+ "chromeRequiredForHardwareWallets": {
+ "message": "하드웨어 지갑을 연결하기 위해서는 구글 크롬에서 메타마스크를 사용하셔야 합니다."
+ },
"confirm": {
"message": "승인"
},
@@ -119,6 +137,36 @@
"confirmTransaction": {
"message": "트랜잭션 승인"
},
+ "connectHardwareWallet": {
+ "message": "하드웨어 지갑 연결"
+ },
+ "connect": {
+ "message": "연결"
+ },
+ "connecting": {
+ "message": "연결 중..."
+ },
+ "connectingToMainnet": {
+ "message": "이더리움 메인넷 접속 중"
+ },
+ "connectingToRopsten": {
+ "message": "Ropsten 테스트넷 접속 중"
+ },
+ "connectingToKovan": {
+ "message": "Kovan 테스트넷 접속 중"
+ },
+ "connectingToRinkeby": {
+ "message": "Rinkeby 테스트넷 접속 중"
+ },
+ "connectingToUnknown": {
+ "message": "알 수 없는 네트워크 접속 중"
+ },
+ "connectToLedger": {
+ "message": "Ledger 연결"
+ },
+ "connectToTrezor": {
+ "message": "Trezor 연결"
+ },
"continue": {
"message": "계속"
},
@@ -129,7 +177,7 @@
"message": "컨트랙트 배포"
},
"conversionProgress": {
- "message": "변환 진행중"
+ "message": "변환 진행 중"
},
"copiedButton": {
"message": "복사됨"
@@ -146,6 +194,9 @@
"copy": {
"message": "복사"
},
+ "copyAddress": {
+ "message": "클립보드로 주소 복사"
+ },
"copyToClipboard": {
"message": "클립보드로 복사"
},
@@ -174,6 +225,9 @@
"currentNetwork": {
"message": "현재 네트워크"
},
+ "currentRpc": {
+ "message": "현재 RPC"
+ },
"customGas": {
"message": "가스 설정"
},
@@ -181,7 +235,7 @@
"message": "사용자 정의 토큰"
},
"customize": {
- "message": "맞춤화 하기"
+ "message": "맞춤화하기"
},
"customRPC": {
"message": "사용자 정의 RPC"
@@ -224,7 +278,7 @@
"message": "ShapeShift를 통해 입금하기"
},
"depositShapeShiftExplainer": {
- "message": "다른 암호화폐를 가지고 있으면, 계정을 생성할 필요없이 메타마스크 지갑에 이더리움을 바로 거래하거나 입금할 수 있습니다."
+ "message": "다른 암호화폐를 가지고 있으면, 계정을 생성할 필요 없이 메타마스크 지갑에 이더리움을 바로 거래하거나 입금할 수 있습니다."
},
"details": {
"message": "세부사항"
@@ -241,9 +295,15 @@
"done": {
"message": "완료"
},
+ "downloadGoogleChrome": {
+ "message": "구글 크롬 다운로드"
+ },
"downloadStateLogs": {
"message": "상태 로그 다운로드"
},
+ "dontHaveAHardwareWallet": {
+ "message": "하드웨어 지갑이 없나요?"
+ },
"dropped": {
"message": "중단됨"
},
@@ -254,7 +314,7 @@
"message": "계정 이름 수정"
},
"editingTransaction": {
- "message": "트랜젝션을 변경합니다"
+ "message": "트랜잭션을 변경합니다"
},
"emailUs": {
"message": "저자에게 메일 보내기!"
@@ -262,6 +322,9 @@
"encryptNewDen": {
"message": "새로운 DEN을 암호화"
},
+ "ensNameNotFound": {
+ "message": "ENS 이름을 찾을 수 없습니다"
+ },
"enterPassword": {
"message": "비밀번호를 입력해주세요"
},
@@ -271,12 +334,6 @@
"enterPasswordContinue": {
"message": "계속하기 위해 비밀번호 입력"
},
- "passwordNotLongEnough": {
- "message": "비밀번호가 충분히 길지 않습니다"
- },
- "passwordsDontMatch": {
- "message": "비밀번호가 맞지 않습니다"
- },
"etherscanView": {
"message": "이더스캔에서 계정보기"
},
@@ -303,14 +360,20 @@
"followTwitter": {
"message": "트위터에서 팔로우하세요"
},
+ "forgetDevice": {
+ "message": "장치 연결 해제"
+ },
"from": {
"message": "보내는 이"
},
"fromToSame": {
- "message": "보내고 받는 주소는 동일할 수 없습니다"
+ "message": "보내고 받는 주소는 같을 수 없습니다"
},
"fromShapeShift": {
- "message": "ShapeShift로 부터"
+ "message": "ShapeShift로부터"
+ },
+ "functionType": {
+ "message": "함수 유형"
},
"gas": {
"message": "가스",
@@ -329,10 +392,10 @@
"message": "가스 한도가 필요합니다."
},
"gasLimitTooLow": {
- "message": "가스 한도는 최소 21000 이상이여야 합니다."
+ "message": "가스 한도는 최소 21000 이상이어야 합니다."
},
"generatingSeed": {
- "message": "시드 생성중..."
+ "message": "시드 생성 중..."
},
"gasPrice": {
"message": "가스 가격 (GWEI)"
@@ -344,7 +407,7 @@
"message": "가스 가격이 필요합니다."
},
"generatingTransaction": {
- "message": "트랜잭션 생성중"
+ "message": "트랜잭션 생성 중"
},
"getEther": {
"message": "이더 얻기"
@@ -353,10 +416,28 @@
"message": "파우셋에서 $1에 달하는 이더를 얻으세요.",
"description": "이더 파우셋에 대한 네트워크 이름을 표시합니다"
},
+ "getHelp": {
+ "message": "도움말"
+ },
"greaterThanMin": {
"message": "$1 이상이어야 합니다.",
"description": "10진수 입력으로 hex값 입력을 도와줍니다"
},
+ "hardware": {
+ "message": "하드웨어"
+ },
+ "hardwareWalletConnected": {
+ "message": "하드웨어 지갑이 연결됨"
+ },
+ "hardwareWallets": {
+ "message": "하드웨어 지갑 연결"
+ },
+ "hardwareWalletsMsg": {
+ "message": "메타마스크에서 사용할 하드웨어 지갑을 선택해주세요"
+ },
+ "havingTroubleConnecting": {
+ "message": "연결에 문제가 있나요?"
+ },
"here": {
"message": "여기",
"description": "as in -click here- for more information (goes with troubleTokenBalances)"
@@ -364,6 +445,9 @@
"hereList": {
"message": "리스트가 있습니다!!!!"
},
+ "hexData": {
+ "message": "Hex 데이터"
+ },
"hide": {
"message": "숨기기"
},
@@ -397,7 +481,7 @@
},
"imported": {
"message": "가져온 계정",
- "description": "이 상태는 해당 계정이 keyring으로 완전히 적재된 상태임을 표시합니다"
+ "description": "이 상태는 해당 계정이 keyring으로 완전히 로드된 상태임을 표시합니다"
},
"importUsingSeed": {
"message": "계정 시드 구문으로 가져오기"
@@ -406,7 +490,7 @@
"message": "정보 및 도움말"
},
"initialTransactionConfirmed": {
- "message": "초기 트랜잭션이 네트워크를 통해 확정되었습니다. 확인을 누르고 이전으로 돌아갑니다."
+ "message": "초기 트랜잭션이 네트워크를 통해 확정되었습니다. 확인을 누르면 이전으로 돌아갑니다."
},
"insufficientFunds": {
"message": "충분하지 않은 자금."
@@ -432,6 +516,9 @@
"invalidRPC": {
"message": "올바르지 않은 RPC URI"
},
+ "invalidSeedPhrase": {
+ "message": "잘못된 시드 구문"
+ },
"jsonFail": {
"message": "이상이 있습니다. JSON 파일이 올바른 파일인지 확인해주세요."
},
@@ -454,6 +541,9 @@
"learnMore": {
"message": "더 배우기."
},
+ "ledgerAccountRestriction": {
+ "message": "새 계정을 추가하려면 최소 마지막 계정을 사용해야 합니다."
+ },
"lessThanMax": {
"message": "$1 이하여야합니다.",
"description": "10진수 입력으로 hex값 입력을 도와줍니다"
@@ -491,6 +581,9 @@
"mainnet": {
"message": "이더리움 메인넷"
},
+ "menu": {
+ "message": "메뉴"
+ },
"message": {
"message": "메시지"
},
@@ -527,14 +620,14 @@
"message": "네트워크"
},
"nevermind": {
- "message": "상관안함"
+ "message": "상관 안 함"
},
"newAccount": {
"message": "새 계정"
},
"newAccountNumberName": {
"message": "새 계정 $1",
- "description": "계정 생성시 볼 수 있는 새 계정의 기본 이름"
+ "description": "계정 생성 시 볼 수 있는 새 계정의 기본 이름"
},
"newContract": {
"message": "새 컨트랙트"
@@ -557,24 +650,55 @@
"noDeposits": {
"message": "입금 내역이 없습니다."
},
+ "noConversionRateAvailable": {
+ "message": "변환 비율을 찾을 수 없습니다"
+ },
"noTransactionHistory": {
"message": "트랜잭션 기록이 없습니다."
},
"noTransactions": {
"message": "트랜잭션이 없습니다"
},
+ "notFound": {
+ "message": "찾을 수 없음"
+ },
"notStarted": {
- "message": "시작 안됨"
+ "message": "시작 안 됨"
+ },
+ "noWebcamFound": {
+ "message": "컴퓨터의 웹캠을 찾을 수 없습니다. 다시 시도해보세요."
+ },
+ "noWebcamFoundTitle": {
+ "message": "웹캠이 없습니다"
},
"oldUI": {
"message": "구버전 UI"
},
"oldUIMessage": {
- "message": "구버전 UI로 변경하셨습니다. 우 상단 드롭다운 메뉴에서 새 UI로 변경하실 수 있습니다."
+ "message": "구버전 UI로 변경하셨습니다. 오른쪽 위 드롭다운 메뉴에서 새 UI로 변경하실 수 있습니다."
+ },
+ "onlySendToEtherAddress": {
+ "message": "이더리움 주소로 ETH만 송금하세요."
+ },
+ "onlySendTokensToAccountAddress": {
+ "message": "이더리움 계정 주소로 $1만 보내기.",
+ "description": "토큰 심볼 보이기"
+ },
+ "openInTab": {
+ "message": "탭으로 열기"
},
"or": {
"message": "또는",
- "description": "새 계정을 만들거나 가져오기중에 선택하기"
+ "description": "새 계정을 만들거나 가져오기 중에 선택하기"
+ },
+ "orderOneHere": {
+ "message": "Trezor 혹은 Ledger를 구입하고 자금을 콜드 스토리지에 저장합니다"
+ },
+ "origin": {
+ "message": "Origin"
+ },
+ "parameters": {
+ "message": "매개변수"
},
"password": {
"message": "비밀번호"
@@ -582,9 +706,15 @@
"passwordCorrect": {
"message": "비밀번호가 맞는지 확인해주세요."
},
+ "passwordsDontMatch": {
+ "message": "비밀번호가 맞지 않습니다"
+ },
"passwordMismatch": {
"message": "비밀번호가 일치하지 않습니다.",
- "description": "비밀번호를 생성하는 과정에서 두개의 새 비밀번호 입력란이 일치하지 않습니다"
+ "description": "비밀번호를 생성하는 과정에서 두 개의 새 비밀번호 입력란이 일치하지 않습니다"
+ },
+ "passwordNotLongEnough": {
+ "message": "비밀번호가 충분히 길지 않습니다"
},
"passwordShort": {
"message": "비밀번호가 짧습니다.",
@@ -595,7 +725,7 @@
"description": "개인키로부터 계정을 가져오는 것입니다"
},
"pasteSeed": {
- "message": "시드 구문을 이곳에 붙여넣어주세요!"
+ "message": "시드 구문을 이곳에 붙여넣어 주세요!"
},
"personalAddressDetected": {
"message": "개인 주소가 탐지됨. 토큰 컨트랙트 주소를 입력하세요."
@@ -606,6 +736,9 @@
"popularTokens": {
"message": "인기있는 토큰"
},
+ "prev": {
+ "message": "이전"
+ },
"privacyMsg": {
"message": "개인정보 보호 정책"
},
@@ -614,7 +747,7 @@
"description": "이 형식의 파일을 선택하여 계정을 가져올 수 있습니다"
},
"privateKeyWarning": {
- "message": " 절대 이 키를 노출하지 마십시오. 개인키가 노출되면 누구나 당신의 계정에서 자산을 빼갈 수 있습니다."
+ "message": "절대 이 키를 노출하지 마십시오. 개인키가 노출되면 누구나 당신의 계정에서 자산을 빼갈 수 있습니다."
},
"privateNetwork": {
"message": "프라이빗 네트워크"
@@ -658,14 +791,17 @@
"restoreVault": {
"message": "저장소 복구"
},
+ "restoreAccountWithSeed": {
+ "message": "시드 구문으로 계정 복구하기"
+ },
"required": {
"message": "필요함"
},
"retryWithMoreGas": {
"message": "더 높은 가스 가격으로 다시 시도해주세요"
},
- "walletSeed": {
- "message": "지갑 시드값"
+ "restore": {
+ "message": "복구"
},
"revealSeedWords": {
"message": "시드 단어 보이기"
@@ -677,14 +813,26 @@
"message": "브라우저를 바꾸거나 컴퓨터를 옮기는 경우 이 시드 구문이 필요하며 이를 통해 계정에 접근할 수 있습니다. 시드 구문을 안전한 곳에 보관하세요."
},
"revealSeedWordsWarningTitle": {
- "message": "이 구문을 다른사람과 절대로 공유하지 마세요!"
+ "message": "이 구문을 다른 사람과 절대로 공유하지 마세요!"
},
"revealSeedWordsWarning": {
- "message": "이 단어모음은 당신의 모든 계정을 훔치는데 사용할 수 있습니다."
+ "message": "이 단어 모음은 당신의 모든 계정을 훔치는데 사용할 수 있습니다."
},
"revert": {
"message": "되돌림"
},
+ "remove": {
+ "message": "제거"
+ },
+ "removeAccount": {
+ "message": "계정 제거"
+ },
+ "removeAccountDescription": {
+ "message": "이 계정은 지갑에서 삭제될 것입니다. 지우기 전에 이 계정에 대한 개인 키 혹은 시드 구문을 가지고 있는지 확인하세요. 계정 드롭다운 메뉴를 통해서 계정을 가져오거나 생성할 수 있습니다."
+ },
+ "readyToConnect": {
+ "message": "접속 준비되었나요?"
+ },
"rinkeby": {
"message": "Rinkeby 테스트넷"
},
@@ -694,24 +842,6 @@
"rpc": {
"message": "사용자 정의 RPC"
},
- "currentRpc": {
- "message": "현재 RPC"
- },
- "connectingToMainnet": {
- "message": "이더리움 메인넷 접속중"
- },
- "connectingToRopsten": {
- "message": "Ropsten 테스트넷 접속중"
- },
- "connectingToKovan": {
- "message": "Kovan 테스트넷 접속중"
- },
- "connectingToRinkeby": {
- "message": "Rinkeby 테스트넷 접속중"
- },
- "connectingToUnknown": {
- "message": "알려지지 않은 네트워크 접속중"
- },
"sampleAccountName": {
"message": "예) 나의 새 계정",
"description": "각 계정에 대해서 구별하기 쉬운 이름을 지정하여 사용자가 쉽게 이해할 수 있게 합니다"
@@ -723,7 +853,7 @@
"message": "트랜잭션 속도 향상하기"
},
"speedUpSubtitle": {
- "message": "트랜잭션 가스 가격을 늘려서 해당 트랙잭션에 덮어쓰기 하여 속도를 빠르게 합니다"
+ "message": "트랜잭션 가스 가격을 올려서 해당 트랜잭션에 덮어쓰고 속도를 빠르게 합니다"
},
"saveAsCsvFile": {
"message": "CSV 파일로 저장"
@@ -735,6 +865,12 @@
"saveSeedAsFile": {
"message": "시드 단어를 파일로 저장하기"
},
+ "scanInstructions": {
+ "message": "QR 코드를 카메라 앞에 가져다 놓아주세요"
+ },
+ "scanQrCode": {
+ "message": "QR 코드 스캔"
+ },
"search": {
"message": "검색"
},
@@ -771,18 +907,29 @@
"sendTokens": {
"message": "토큰 전송"
},
- "onlySendToEtherAddress": {
- "message": "이더리움 주소로 ETH만 송금하세요."
- },
- "onlySendTokensToAccountAddress": {
- "message": "이더리움계정 주소로 $1 만 보내기.",
- "description": "토큰 심볼 보이기"
+ "separateEachWord": {
+ "message": "각 단어는 공백 한칸으로 분리합니다"
},
"searchTokens": {
"message": "토큰 검색"
},
+ "selectAnAddress": {
+ "message": "주소 선택"
+ },
+ "selectAnAccount": {
+ "message": "계정 선택"
+ },
+ "selectAnAccountHelp": {
+ "message": "메타마스크에서 보기 위한 계정 선택"
+ },
+ "selectHdPath": {
+ "message": "HD 경로 지정"
+ },
+ "selectPathHelp": {
+ "message": "하단에서 Ledger 지갑 계정을 찾지 못하겠으면 \"Legacy (MEW / MyCrypto)\" 경로로 바꿔보세요"
+ },
"sendTokensAnywhere": {
- "message": "이더 계정로 토큰 전송"
+ "message": "이더 계정으로 토큰 전송"
},
"settings": {
"message": "설정"
@@ -832,6 +979,24 @@
"stateLogError": {
"message": "상태 로그 받기 실패."
},
+ "step1HardwareWallet": {
+ "message": "1. 하드웨어 지갑 연결"
+ },
+ "step1HardwareWalletMsg": {
+ "message": "하드웨어 지갑을 컴퓨터에 연결해주세요."
+ },
+ "step2HardwareWallet": {
+ "message": "2. 계정 선택"
+ },
+ "step2HardwareWalletMsg": {
+ "message": "보고 싶은 계정을 선택합니다. 한 번에 하나의 계정만 선택할 수 있습니다."
+ },
+ "step3HardwareWallet": {
+ "message": "3. dApps을 사용하거나 다른 것을 합니다!"
+ },
+ "step3HardwareWalletMsg": {
+ "message": "다른 이더리움 계정을 사용하듯 하드웨어 계정을 사용합니다. dApps을 로그인하거나, 이더를 보내거나, ERC20 토큰 혹은 대체 가능하지 않은 토큰 (예를 들어 CryptoKitties)을 사거나 저장하거나 합니다."
+ },
"submit": {
"message": "제출"
},
@@ -879,7 +1044,7 @@
"message": "토큰 기호"
},
"tokenWarning1": {
- "message": "메타마스크 계좌를 통해 구입한 토큰을 추적합니다. 다른 계정으로 토큰을 구입한 경우 이곳에 나타나지 않습니다."
+ "message": "메타마스크 계정을 통해 구입한 토큰을 추적합니다. 다른 계정으로 토큰을 구입한 경우 이곳에 나타나지 않습니다."
},
"total": {
"message": "합계"
@@ -896,13 +1061,22 @@
"transactionNumber": {
"message": "트랜잭션 번호"
},
+ "transfer": {
+ "message": "전송"
+ },
"transfers": {
"message": "전송"
},
+ "trezorHardwareWallet": {
+ "message": "TREZOR 하드웨어 지갑"
+ },
"troubleTokenBalances": {
"message": "토큰 잔액을 가져오는 데에 문제가 생겼습니다. 링크에서 상세내용을 볼 수 있습니다.",
"description": "토큰 잔액을 보려면 (here) 링크를 따라가세요"
},
+ "tryAgain": {
+ "message": "다시 시도하세요"
+ },
"twelveWords": {
"message": "12개의 단어는 메타마스크 계정을 복구하기 위한 유일한 방법입니다.\n안전한 장소에 보관하시기 바랍니다."
},
@@ -916,19 +1090,34 @@
"message": "새로운 메타마스크 UI를 사용하고 계십니다. 토큰 전송과 같은 새 기능들을 사용해보시면서 문제가 있다면 알려주세요."
},
"unapproved": {
- "message": "허가안됨"
+ "message": "허가 안 됨"
},
"unavailable": {
- "message": "유효하지 않음"
+ "message": "이용할 수 없음"
},
"unknown": {
- "message": "알려지지 않음"
+ "message": "알 수 없음"
+ },
+ "unknownFunction": {
+ "message": "알 수 없는 함수"
},
"unknownNetwork": {
- "message": "알려지지 않은 프라이빗 네트워크"
+ "message": "알 수 없는 프라이빗 네트워크"
},
"unknownNetworkId": {
- "message": "알려지지 않은 네트워크 ID"
+ "message": "알 수 없는 네트워크 ID"
+ },
+ "unknownQrCode": {
+ "message": "오류: QR 코드를 확인할 수 없습니다"
+ },
+ "unknownCameraErrorTitle": {
+ "message": "이런! 뭔가 잘못되었습니다...."
+ },
+ "unknownCameraError": {
+ "message": "카메라에 접근하는 중 오류가 발생했습니다. 다시 시도해 주세요..."
+ },
+ "unlock": {
+ "message": "잠금 해제"
},
"unlockMessage": {
"message": "우리가 기다리던 분권형 웹입니다"
@@ -938,7 +1127,7 @@
},
"usaOnly": {
"message": "USA 거주자 한정",
- "description": "해당 거래소는 USA거주자에 한해서만 사용가능합니다"
+ "description": "해당 거래소는 USA 거주자에 한해서만 사용 가능합니다"
},
"usedByClients": {
"message": "다양한 클라이언트에서 사용되고 있습니다"
@@ -956,11 +1145,14 @@
"message": "계정 보기"
},
"viewOnEtherscan": {
- "message": "이터스캔에서 보기"
+ "message": "이더스캔에서 보기"
},
"visitWebSite": {
"message": "웹사이트 방문"
},
+ "walletSeed": {
+ "message": "지갑 시드값"
+ },
"warning": {
"message": "경고"
},
@@ -974,7 +1166,7 @@
"message": "이것은 무엇인가요?"
},
"yourSigRequested": {
- "message": "서명이 요청되고 있습니다."
+ "message": "서명을 요청 중입니다."
},
"youSign": {
"message": "서명 중입니다"
@@ -982,198 +1174,6 @@
"yourPrivateSeedPhrase": {
"message": "개인 시드 구문"
},
- "accessingYourCamera": {
- "message": "카메라 접근중..."
- },
- "accountSelectionRequired": {
- "message": "계정을 선택하셔야 합니다!"
- },
- "approve": {
- "message": "수락"
- },
- "browserNotSupported": {
- "message": "브라우저가 지원하지 않습니다..."
- },
- "bytes": {
- "message": "바이트"
- },
- "chromeRequiredForHardwareWallets": {
- "message": "하드웨어 지갑을 연결하기 위해서는 구글 크롬에서 메타마스크를 사용하셔야 합니다."
- },
- "connectHardwareWallet": {
- "message": "하드웨어 지갑 연결"
- },
- "connect": {
- "message": "연결"
- },
- "connecting": {
- "message": "연결중..."
- },
- "connectToLedger": {
- "message": "Ledger 연결"
- },
- "connectToTrezor": {
- "message": "Trezor 연결"
- },
- "copyAddress": {
- "message": "클립보드로 주소 복사"
- },
- "downloadGoogleChrome": {
- "message": "구글 크롬 다운로드"
- },
- "dontHaveAHardwareWallet": {
- "message": "하드웨어 지갑이 없나요?"
- },
- "ensNameNotFound": {
- "message": "ENS 이름을 찾을 수 없습니다"
- },
- "parameters": {
- "message": "매개변수"
- },
- "forgetDevice": {
- "message": "장치 연결 해제"
- },
- "functionType": {
- "message": "함수 유형"
- },
- "getHelp": {
- "message": "도움말"
- },
- "hardware": {
- "message": "하드웨어"
- },
- "hardwareWalletConnected": {
- "message": "하드웨어 지갑이 연결됨"
- },
- "hardwareWallets": {
- "message": "하드웨어 지갑 연결"
- },
- "hardwareWalletsMsg": {
- "message": "메타마스크에서 사용할 하드웨어 지갑을 선택해주세요"
- },
- "havingTroubleConnecting": {
- "message": "연결에 문제가 있나요?"
- },
- "hexData": {
- "message": "Hex 데이터"
- },
- "invalidSeedPhrase": {
- "message": "잘못된 시드 구문"
- },
- "ledgerAccountRestriction": {
- "message": "새 계정을 추가하려면 최소 마지막 계정을 사용해야 합니다."
- },
- "menu": {
- "message": "메뉴"
- },
- "noConversionRateAvailable": {
- "message": "변환 비율을 찾을 수 없습니다"
- },
- "notFound": {
- "message": "찾을 수 없음"
- },
- "noWebcamFoundTitle": {
- "message": "웹캠이 없습니다"
- },
- "noWebcamFound": {
- "message": "컴퓨터의 웹캠을 찾을 수 없습니다. 다시 시도해보세요."
- },
- "openInTab": {
- "message": "탭으로 열기"
- },
- "origin": {
- "message": "Origin"
- },
- "prev": {
- "message": "이전"
- },
- "restoreAccountWithSeed": {
- "message": "시드 구문으로 계정 복구하기"
- },
- "restore": {
- "message": "복구"
- },
- "remove": {
- "message": "제거"
- },
- "removeAccount": {
- "message": "계정 제거"
- },
- "removeAccountDescription": {
- "message": "이 계정한 지갑에서 삭제될 것입니다. 지우기 전에 이 계정에 대한 개인 키 혹은 시드 구문을 가지고 있는지 확인하세요. 계정 드랍다운 메뉴를 통해서 계정을 가져오거나 생성할 수 있습니다."
- },
- "readyToConnect": {
- "message": "접속 준비되었나요?"
- },
- "separateEachWord": {
- "message": "각 단어는 공백 한칸으로 분리합니다"
- },
- "orderOneHere": {
- "message": "Trezor 혹은 Ledger를 구입하고 자금을 콜드 스토리지에 저장합니다"
- },
- "selectAnAddress": {
- "message": "주소 선택"
- },
- "selectAnAccount": {
- "message": "계정 선택"
- },
- "selectAnAccountHelp": {
- "message": "메타마스크에서 보기위한 계정 선택"
- },
- "selectHdPath": {
- "message": "HD 경로 지정"
- },
- "selectPathHelp": {
- "message": "하단에서 Ledger지갑 계정을 찾지 못하겠으면 \"Legacy (MEW / MyCrypto)\" 경로로 바꿔보세요"
- },
- "step1HardwareWallet": {
- "message": "1. 하드웨어 지갑 연결"
- },
- "step1HardwareWalletMsg": {
- "message": "하드웨어 지갑을 컴퓨터에 연결해주세요."
- },
- "step2HardwareWallet": {
- "message": "2. 계정 선택"
- },
- "step2HardwareWalletMsg": {
- "message": "보고 싶은 계정을 선택합니다. 한번에 하나의 계정만 선택할 수 있습니다."
- },
- "step3HardwareWallet": {
- "message": "3. dApps을 사용하거나 다른 것을 합니다!"
- },
- "step3HardwareWalletMsg": {
- "message": "다른 이더리움 계정을 사용하듯 하드웨어 계정을 사용합니다. dApps을 로그인하거나, 이더를 보내거나, ERC20토큰 혹은 대체가능하지 않은 토큰 (예를 들어 CryptoKitties)을 사거나 저장하거나 합니다."
- },
- "scanInstructions": {
- "message": "QR 코드를 카메라 앞에 가져다 놓아주세요"
- },
- "scanQrCode": {
- "message": "QR 코드 스캔"
- },
- "transfer": {
- "message": "전송"
- },
- "trezorHardwareWallet": {
- "message": "TREZOR 하드웨어 지갑"
- },
- "tryAgain": {
- "message": "다시 시도하세요"
- },
- "unknownFunction": {
- "message": "알 수 없는 함수"
- },
- "unknownQrCode": {
- "message": "오류: QR 코드를 확인할 수 없습니다"
- },
- "unknownCameraErrorTitle": {
- "message": "이런! 뭔가 잘못되었습니다...."
- },
- "unknownCameraError": {
- "message": "카메라를 접근하는 중 오류가 발생했습니다. 다시 시도해 주세요..."
- },
- "unlock": {
- "message": "잠금 해제"
- },
"youNeedToAllowCameraAccess": {
"message": "이 기능을 사용하려면 카메라 접근을 허용해야 합니다."
}
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index ebd49f882..a57c85f50 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -166,6 +166,10 @@ class TransactionController extends EventEmitter {
async addUnapprovedTransaction (txParams) {
// validate
const normalizedTxParams = txUtils.normalizeTxParams(txParams)
+ // Assert the from address is the selected address
+ if (normalizedTxParams.from !== this.getSelectedAddress()) {
+ throw new Error(`Transaction from address isn't valid for this account`)
+ }
txUtils.validateTxParams(normalizedTxParams)
// construct txMeta
let txMeta = this.txStateManager.generateTxMeta({
diff --git a/development/sentry-publish.js b/development/sentry-publish.js
index 7a6d55115..e14f3f176 100644
--- a/development/sentry-publish.js
+++ b/development/sentry-publish.js
@@ -14,21 +14,27 @@ async function start () {
const versionAlreadyExists = await checkIfVersionExists()
// abort if versions exists
if (versionAlreadyExists) {
- console.log(`Version "${VERSION}" already exists on Sentry, aborting sourcemap upload.`)
- return
+ console.log(`Version "${VERSION}" already exists on Sentry, skipping version creation`)
+ } else {
+ // create sentry release
+ console.log(`creating Sentry release for "${VERSION}"...`)
+ await exec(`sentry-cli releases --org 'metamask' --project 'metamask' new ${VERSION}`)
+ console.log(`removing any existing files from Sentry release "${VERSION}"...`)
+ await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} delete --all`)
}
- // create sentry release
- console.log(`creating Sentry release for "${VERSION}"...`)
- await exec(`sentry-cli releases --org 'metamask' --project 'metamask' new ${VERSION}`)
- console.log(`removing any existing files from Sentry release "${VERSION}"...`)
- await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} delete --all`)
- // upload sentry source and sourcemaps
- console.log(`uploading source files Sentry release "${VERSION}"...`)
- await exec(`for FILEPATH in ./dist/chrome/*.js; do [ -e $FILEPATH ] || continue; export FILE=\`basename $FILEPATH\` && echo uploading $FILE && sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload $FILEPATH metamask/$FILE; done;`)
- console.log(`uploading sourcemaps Sentry release "${VERSION}"...`)
- await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload-sourcemaps ./dist/sourcemaps/ --url-prefix 'sourcemaps'`)
- console.log('all done!')
+ // check if version has artifacts or not
+ const versionHasArtifacts = versionAlreadyExists && await checkIfVersionHasArtifacts()
+ if (!versionHasArtifacts) {
+ // upload sentry source and sourcemaps
+ console.log(`uploading source files Sentry release "${VERSION}"...`)
+ await exec(`for FILEPATH in ./dist/chrome/*.js; do [ -e $FILEPATH ] || continue; export FILE=\`basename $FILEPATH\` && echo uploading $FILE && sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload $FILEPATH metamask/$FILE; done;`)
+ console.log(`uploading sourcemaps Sentry release "${VERSION}"...`)
+ await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload-sourcemaps ./dist/sourcemaps/ --url-prefix 'sourcemaps'`)
+ console.log('all done!')
+ } else {
+ console.log(`Version "${VERSION}" already has artifacts on Sentry, skipping sourcemap upload`)
+ }
}
async function checkIfAuthWorks () {
@@ -45,6 +51,12 @@ async function checkIfVersionExists () {
return versionAlreadyExists
}
+async function checkIfVersionHasArtifacts () {
+ const artifacts = await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} list`)
+ // When there's no artifacts, we get a response from the shell like this ['', '']
+ return artifacts[0] && artifacts[0].length > 0
+}
+
async function doesNotFail (asyncFn) {
try {
await asyncFn()
diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css
index a575fe97e..f3df240e7 100644
--- a/mascara/src/app/first-time/index.css
+++ b/mascara/src/app/first-time/index.css
@@ -17,7 +17,7 @@
font-family: Roboto;
}
-@media screen and (min-height: 576px) {
+@media screen and (min-height: 601px) {
.first-time-flow {
height: 100vh;
}
diff --git a/package-lock.json b/package-lock.json
index a617d2de4..9bc5c068a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1740,7 +1740,7 @@
"dependencies": {
"bignumber.js": {
"version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2",
- "from": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
+ "from": "bignumber.js@git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
},
"chai": {
"version": "3.5.0",
@@ -10954,7 +10954,7 @@
},
"ethereumjs-util": {
"version": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
- "from": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
+ "from": "ethereumjs-util@github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
"requires": {
"bn.js": "^4.8.0",
"create-hash": "^1.1.2",
@@ -11902,6 +11902,25 @@
"pend": "~1.2.0"
}
},
+ "fetch-mock": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-6.5.2.tgz",
+ "integrity": "sha512-EIvbpCLBTYyDLu4HJiqD7wC8psDwTUaPaWXNKZbhNO/peUYKiNp5PkZGKRJtnTxaPQu71ivqafvjpM7aL+MofQ==",
+ "dev": true,
+ "requires": {
+ "babel-polyfill": "^6.26.0",
+ "glob-to-regexp": "^0.4.0",
+ "path-to-regexp": "^2.2.1"
+ },
+ "dependencies": {
+ "path-to-regexp": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz",
+ "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==",
+ "dev": true
+ }
+ }
+ },
"fetch-ponyfill": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz",
@@ -12735,25 +12754,21 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
- "resolved": false,
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "bundled": true,
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
- "resolved": false,
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ "bundled": true
},
"aproba": {
"version": "1.2.0",
- "resolved": false,
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+ "bundled": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
- "resolved": false,
- "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
+ "bundled": true,
"optional": true,
"requires": {
"delegates": "^1.0.0",
@@ -12762,13 +12777,11 @@
},
"balanced-match": {
"version": "1.0.0",
- "resolved": false,
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ "bundled": true
},
"brace-expansion": {
"version": "1.1.11",
- "resolved": false,
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "bundled": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -12776,35 +12789,29 @@
},
"chownr": {
"version": "1.0.1",
- "resolved": false,
- "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
+ "bundled": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
- "resolved": false,
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+ "bundled": true
},
"concat-map": {
"version": "0.0.1",
- "resolved": false,
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ "bundled": true
},
"console-control-strings": {
"version": "1.1.0",
- "resolved": false,
- "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+ "bundled": true
},
"core-util-is": {
"version": "1.0.2",
- "resolved": false,
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "bundled": true,
"optional": true
},
"debug": {
"version": "2.6.9",
- "resolved": false,
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "bundled": true,
"optional": true,
"requires": {
"ms": "2.0.0"
@@ -12812,26 +12819,22 @@
},
"deep-extend": {
"version": "0.5.1",
- "resolved": false,
- "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==",
+ "bundled": true,
"optional": true
},
"delegates": {
"version": "1.0.0",
- "resolved": false,
- "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+ "bundled": true,
"optional": true
},
"detect-libc": {
"version": "1.0.3",
- "resolved": false,
- "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
+ "bundled": true,
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
- "resolved": false,
- "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
+ "bundled": true,
"optional": true,
"requires": {
"minipass": "^2.2.1"
@@ -12839,14 +12842,12 @@
},
"fs.realpath": {
"version": "1.0.0",
- "resolved": false,
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "bundled": true,
"optional": true
},
"gauge": {
"version": "2.7.4",
- "resolved": false,
- "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+ "bundled": true,
"optional": true,
"requires": {
"aproba": "^1.0.3",
@@ -12861,8 +12862,7 @@
},
"glob": {
"version": "7.1.2",
- "resolved": false,
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "bundled": true,
"optional": true,
"requires": {
"fs.realpath": "^1.0.0",
@@ -12875,14 +12875,12 @@
},
"has-unicode": {
"version": "2.0.1",
- "resolved": false,
- "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+ "bundled": true,
"optional": true
},
"iconv-lite": {
"version": "0.4.21",
- "resolved": false,
- "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
+ "bundled": true,
"optional": true,
"requires": {
"safer-buffer": "^2.1.0"
@@ -12890,8 +12888,7 @@
},
"ignore-walk": {
"version": "3.0.1",
- "resolved": false,
- "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
+ "bundled": true,
"optional": true,
"requires": {
"minimatch": "^3.0.4"
@@ -12899,8 +12896,7 @@
},
"inflight": {
"version": "1.0.6",
- "resolved": false,
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "bundled": true,
"optional": true,
"requires": {
"once": "^1.3.0",
@@ -12909,46 +12905,39 @@
},
"inherits": {
"version": "2.0.3",
- "resolved": false,
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ "bundled": true
},
"ini": {
"version": "1.3.5",
- "resolved": false,
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "bundled": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
- "resolved": false,
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "bundled": true,
"requires": {
"number-is-nan": "^1.0.0"
}
},
"isarray": {
"version": "1.0.0",
- "resolved": false,
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "bundled": true,
"optional": true
},
"minimatch": {
"version": "3.0.4",
- "resolved": false,
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "bundled": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
- "resolved": false,
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ "bundled": true
},
"minipass": {
"version": "2.2.4",
- "resolved": false,
- "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
+ "bundled": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -12956,8 +12945,7 @@
},
"minizlib": {
"version": "1.1.0",
- "resolved": false,
- "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
+ "bundled": true,
"optional": true,
"requires": {
"minipass": "^2.2.1"
@@ -12965,28 +12953,25 @@
},
"mkdirp": {
"version": "0.5.1",
- "resolved": false,
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "bundled": true,
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.0.0",
- "resolved": false,
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "bundled": true,
"optional": true
},
"nan": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
- "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz",
+ "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==",
"optional": true
},
"needle": {
"version": "2.2.0",
- "resolved": false,
- "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==",
+ "bundled": true,
"optional": true,
"requires": {
"debug": "^2.1.2",
@@ -12996,8 +12981,7 @@
},
"node-pre-gyp": {
"version": "0.10.0",
- "resolved": false,
- "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==",
+ "bundled": true,
"optional": true,
"requires": {
"detect-libc": "^1.0.2",
@@ -13014,8 +12998,7 @@
},
"nopt": {
"version": "4.0.1",
- "resolved": false,
- "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
+ "bundled": true,
"optional": true,
"requires": {
"abbrev": "1",
@@ -13024,14 +13007,12 @@
},
"npm-bundled": {
"version": "1.0.3",
- "resolved": false,
- "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==",
+ "bundled": true,
"optional": true
},
"npm-packlist": {
"version": "1.1.10",
- "resolved": false,
- "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==",
+ "bundled": true,
"optional": true,
"requires": {
"ignore-walk": "^3.0.1",
@@ -13040,8 +13021,7 @@
},
"npmlog": {
"version": "4.1.2",
- "resolved": false,
- "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+ "bundled": true,
"optional": true,
"requires": {
"are-we-there-yet": "~1.1.2",
@@ -13052,39 +13032,33 @@
},
"number-is-nan": {
"version": "1.0.1",
- "resolved": false,
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ "bundled": true
},
"object-assign": {
"version": "4.1.1",
- "resolved": false,
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "bundled": true,
"optional": true
},
"once": {
"version": "1.4.0",
- "resolved": false,
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "bundled": true,
"requires": {
"wrappy": "1"
}
},
"os-homedir": {
"version": "1.0.2",
- "resolved": false,
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+ "bundled": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
- "resolved": false,
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "bundled": true,
"optional": true
},
"osenv": {
"version": "0.1.5",
- "resolved": false,
- "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "bundled": true,
"optional": true,
"requires": {
"os-homedir": "^1.0.0",
@@ -13093,20 +13067,17 @@
},
"path-is-absolute": {
"version": "1.0.1",
- "resolved": false,
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "bundled": true,
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
- "resolved": false,
- "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+ "bundled": true,
"optional": true
},
"rc": {
"version": "1.2.7",
- "resolved": false,
- "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==",
+ "bundled": true,
"optional": true,
"requires": {
"deep-extend": "^0.5.1",
@@ -13117,16 +13088,14 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
- "resolved": false,
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "bundled": true,
"optional": true
}
}
},
"readable-stream": {
"version": "2.3.6",
- "resolved": false,
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "bundled": true,
"optional": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -13140,8 +13109,7 @@
},
"rimraf": {
"version": "2.6.2",
- "resolved": false,
- "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+ "bundled": true,
"optional": true,
"requires": {
"glob": "^7.0.5"
@@ -13149,43 +13117,36 @@
},
"safe-buffer": {
"version": "5.1.1",
- "resolved": false,
- "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+ "bundled": true
},
"safer-buffer": {
"version": "2.1.2",
- "resolved": false,
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "bundled": true,
"optional": true
},
"sax": {
"version": "1.2.4",
- "resolved": false,
- "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "bundled": true,
"optional": true
},
"semver": {
"version": "5.5.0",
- "resolved": false,
- "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+ "bundled": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
- "resolved": false,
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "bundled": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
- "resolved": false,
- "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "bundled": true,
"optional": true
},
"string-width": {
"version": "1.0.2",
- "resolved": false,
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "bundled": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -13194,8 +13155,7 @@
},
"string_decoder": {
"version": "1.1.1",
- "resolved": false,
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "bundled": true,
"optional": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -13203,22 +13163,19 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": false,
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "bundled": true,
"requires": {
"ansi-regex": "^2.0.0"
}
},
"strip-json-comments": {
"version": "2.0.1",
- "resolved": false,
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "bundled": true,
"optional": true
},
"tar": {
"version": "4.4.1",
- "resolved": false,
- "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==",
+ "bundled": true,
"optional": true,
"requires": {
"chownr": "^1.0.1",
@@ -13232,14 +13189,12 @@
},
"util-deprecate": {
"version": "1.0.2",
- "resolved": false,
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "bundled": true,
"optional": true
},
"wide-align": {
"version": "1.1.2",
- "resolved": false,
- "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
+ "bundled": true,
"optional": true,
"requires": {
"string-width": "^1.0.2"
@@ -13247,13 +13202,11 @@
},
"wrappy": {
"version": "1.0.2",
- "resolved": false,
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ "bundled": true
},
"yallist": {
"version": "3.0.2",
- "resolved": false,
- "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
+ "bundled": true
}
}
},
@@ -14302,6 +14255,12 @@
}
}
},
+ "glob-to-regexp": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.0.tgz",
+ "integrity": "sha512-fyPCII4vn9Gvjq2U/oDAfP433aiE64cyP/CJjRJcpVGjqqNdioUYn9+r0cSzT1XPwmGAHuTT7iv+rQT8u/YHKQ==",
+ "dev": true
+ },
"glob-watcher": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-4.0.0.tgz",
diff --git a/package.json b/package.json
index adbf46123..3a906a271 100644
--- a/package.json
+++ b/package.json
@@ -263,6 +263,7 @@
"eslint-plugin-react": "^7.4.0",
"eth-json-rpc-middleware": "^3.1.1",
"eth-keyring-controller": "^3.3.1",
+ "fetch-mock": "^6.5.2",
"file-loader": "^1.1.11",
"fs-extra": "^6.0.1",
"fs-promise": "^2.0.3",
diff --git a/test/data/2-state.json b/test/data/2-state.json
new file mode 100644
index 000000000..d41a403ff
--- /dev/null
+++ b/test/data/2-state.json
@@ -0,0 +1,70 @@
+{ "isInitialized": true,
+ "provider": { "type": "rpc", "rpcTarget": "http://localhost:8545" },
+ "network": "loading",
+ "accounts": {
+ "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
+ "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "balance": "0x0"
+ },
+ "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
+ "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b",
+ "balance": "0x0"
+ }
+ },
+ "currentBlockGasLimit": "",
+ "unapprovedTxs": {},
+ "selectedAddressTxList": [],
+ "computedBalances": {},
+ "unapprovedMsgs": {},
+ "unapprovedMsgCount": 0,
+ "unapprovedPersonalMsgs": {},
+ "unapprovedPersonalMsgCount": 0,
+ "unapprovedTypedMessages": {},
+ "unapprovedTypedMessagesCount": 0,
+ "isUnlocked": true,
+ "keyringTypes": [ "Simple Key Pair", "HD Key Tree" ],
+ "keyrings":[
+ { "type": "HD Key Tree",
+ "accounts": [
+ "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ ]
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": [
+ "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
+ ]
+ }
+ ],
+ "frequentRpcList": [],
+ "currentAccountTab": "history",
+ "tokens": [],
+ "useBlockie": false,
+ "featureFlags": {},
+ "currentLocale": null,
+ "identities": {
+ "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
+ "name": "Account 1",
+ "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ },
+ "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
+ "name": "Account 2",
+ "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
+ }
+ },
+
+ "lostIdentities": {},
+ "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "recentBlocks": [],
+ "addressBook": [],
+ "currentCurrency": "usd",
+ "conversionRate": 288.45,
+ "conversionDate": 1506444677,
+ "nextUnreadNotice": null,
+ "noActiveNotices": true,
+ "shapeShiftTxList": [],
+ "infuraNetworkStatus": {},
+ "lostAccounts": [],
+ "seedWords": "debris dizzy just program just float decrease vacant alarm reduce speak stadium",
+ "forgottenPassword": null
+} \ No newline at end of file
diff --git a/test/unit/app/controllers/transactions/tx-controller-test.js b/test/unit/app/controllers/transactions/tx-controller-test.js
index 5ac813b49..ea58aa560 100644
--- a/test/unit/app/controllers/transactions/tx-controller-test.js
+++ b/test/unit/app/controllers/transactions/tx-controller-test.js
@@ -158,9 +158,19 @@ describe('Transaction Controller', function () {
})
describe('#addUnapprovedTransaction', function () {
+ const selectedAddress = '0x1678a085c290ebd122dc42cba69373b5953b831d'
+
+ let getSelectedAddress
+ beforeEach(function () {
+ getSelectedAddress = sinon.stub(txController, 'getSelectedAddress').returns(selectedAddress)
+ })
+
+ afterEach(function () {
+ getSelectedAddress.restore()
+ })
it('should add an unapproved transaction and return a valid txMeta', function (done) {
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
+ txController.addUnapprovedTransaction({ from: selectedAddress })
.then((txMeta) => {
assert(('id' in txMeta), 'should have a id')
assert(('time' in txMeta), 'should have a time stamp')
@@ -180,25 +190,37 @@ describe('Transaction Controller', function () {
assert(txMetaFromEmit, 'txMeta is falsey')
done()
})
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
+ txController.addUnapprovedTransaction({ from: selectedAddress })
.catch(done)
})
it('should fail if recipient is public', function (done) {
txController.networkStore = new ObservableStore(1)
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
+ txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch((err) => {
if (err.message === 'Recipient is a public account') done()
else done(err)
})
})
+ it('should fail if the from address isn\'t the selected address', function (done) {
+ txController.addUnapprovedTransaction({from: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2'})
+ .then(function () {
+ assert.fail('transaction should not have been added')
+ done()
+ })
+ .catch(function () {
+ assert.ok('pass')
+ done()
+ })
+ })
+
it('should not fail if recipient is public but not on mainnet', function (done) {
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
assert(txMetaFromEmit, 'txMeta is falsey')
done()
})
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
+ txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch(done)
})
})
diff --git a/test/unit/ui/app/actions.spec.js b/test/unit/ui/app/actions.spec.js
new file mode 100644
index 000000000..748a58b32
--- /dev/null
+++ b/test/unit/ui/app/actions.spec.js
@@ -0,0 +1,1468 @@
+// Used to inspect long objects
+// util.inspect({JSON}, false, null))
+// const util = require('util')
+const assert = require('assert')
+const sinon = require('sinon')
+const clone = require('clone')
+const nock = require('nock')
+const fetchMock = require('fetch-mock')
+const configureStore = require('redux-mock-store').default
+const thunk = require('redux-thunk').default
+const EthQuery = require('eth-query')
+const Eth = require('ethjs')
+const KeyringController = require('eth-keyring-controller')
+
+const { createTestProviderTools } = require('../../../stub/provider')
+const provider = createTestProviderTools({ scaffold: {}}).provider
+
+const enLocale = require('../../../../app/_locales/en/messages.json')
+const actions = require('../../../../ui/app/actions')
+const MetaMaskController = require('../../../../app/scripts/metamask-controller')
+
+const firstTimeState = require('../../../unit/localhostState')
+const devState = require('../../../data/2-state.json')
+
+const middleware = [thunk]
+const mockStore = configureStore(middleware)
+
+describe('Actions', () => {
+
+ const noop = () => {}
+
+ let background, metamaskController
+
+ const TEST_SEED = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
+ const password = 'a-fake-password'
+ const importPrivkey = '4cfd3e90fc78b0f86bf7524722150bb8da9c60cd532564d7ff43f5716514f553'
+
+ beforeEach(async () => {
+
+
+ metamaskController = new MetaMaskController({
+ provider,
+ keyringController: new KeyringController({}),
+ showUnapprovedTx: noop,
+ showUnconfirmedMessage: noop,
+ encryptor: {
+ encrypt: function (password, object) {
+ this.object = object
+ return Promise.resolve('mock-encrypted')
+ },
+ decrypt: function () {
+ return Promise.resolve(this.object)
+ },
+ },
+ initState: clone(firstTimeState),
+ })
+
+ await metamaskController.createNewVaultAndRestore(password, TEST_SEED)
+
+ await metamaskController.importAccountWithStrategy('Private Key', [ importPrivkey ])
+
+ background = metamaskController.getApi()
+
+ actions._setBackgroundConnection(background)
+
+ global.ethQuery = new EthQuery(provider)
+ })
+
+ describe('#tryUnlockMetamask', () => {
+
+ let submitPasswordSpy, verifySeedPhraseSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ verifySeedPhraseSpy.restore()
+ })
+
+ it('', async () => {
+
+ const store = mockStore({})
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ verifySeedPhraseSpy = sinon.spy(background, 'verifySeedPhrase')
+
+ return store.dispatch(actions.tryUnlockMetamask())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(verifySeedPhraseSpy.calledOnce)
+ })
+ })
+
+ it('errors on submitPassword will fail', () => {
+
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'UNLOCK_IN_PROGRESS' },
+ { type: 'UNLOCK_FAILED', value: 'error in submitPassword' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error in submitPassword'))
+ })
+
+ return store.dispatch(actions.tryUnlockMetamask('test'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('displays warning error and unlock failed when verifySeed fails', () => {
+ const store = mockStore({})
+ const displayWarningError = [ { type: 'DISPLAY_WARNING', value: 'error' } ]
+ const unlockFailedError = [ { type: 'UNLOCK_FAILED', value: 'error' } ]
+
+ verifySeedPhraseSpy = sinon.stub(background, 'verifySeedPhrase')
+ verifySeedPhraseSpy.callsFake(callback => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.tryUnlockMetamask('test'))
+ .catch(() => {
+ const actions = store.getActions()
+ const warning = actions.filter(action => action.type === 'DISPLAY_WARNING')
+ const unlockFailed = actions.filter(action => action.type === 'UNLOCK_FAILED')
+ assert.deepEqual(warning, displayWarningError)
+ assert.deepEqual(unlockFailed, unlockFailedError)
+ })
+ })
+ })
+
+ describe('#confirmSeedWords', () => {
+
+ let clearSeedWordCacheSpy
+
+ afterEach(() => {
+ clearSeedWordCacheSpy.restore()
+ })
+
+ it('shows account page after clearing seed word cache', () => {
+
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_ACCOUNTS_PAGE' },
+ ]
+
+ clearSeedWordCacheSpy = sinon.spy(background, 'clearSeedWordCache')
+
+ return store.dispatch(actions.confirmSeedWords())
+ .then(() => {
+ assert.equal(clearSeedWordCacheSpy.callCount, 1)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('errors in callback will display warning', () => {
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ clearSeedWordCacheSpy = sinon.stub(background, 'clearSeedWordCache')
+
+ clearSeedWordCacheSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.confirmSeedWords())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#createNewVaultAndRestore', () => {
+
+ let createNewVaultAndRestoreSpy, clearSeedWordCacheSpy
+
+ afterEach(() => {
+ createNewVaultAndRestoreSpy.restore()
+ })
+
+ it('clears seed words and restores new vault', () => {
+
+ const store = mockStore({})
+
+ createNewVaultAndRestoreSpy = sinon.spy(background, 'createNewVaultAndRestore')
+ clearSeedWordCacheSpy = sinon.spy(background, 'clearSeedWordCache')
+ return store.dispatch(actions.createNewVaultAndRestore())
+ .then(() => {
+ assert(clearSeedWordCacheSpy.calledOnce)
+ assert(createNewVaultAndRestoreSpy.calledOnce)
+ })
+ })
+
+ it('errors when callback in clearSeedWordCache throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ clearSeedWordCacheSpy = sinon.stub(background, 'clearSeedWordCache')
+ clearSeedWordCacheSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndRestore())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('errors when callback in createNewVaultAndRestore throws', () => {
+
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ createNewVaultAndRestoreSpy = sinon.stub(background, 'createNewVaultAndRestore')
+
+ createNewVaultAndRestoreSpy.callsFake((password, seed, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndRestore())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#createNewVaultAndKeychain', () => {
+
+ let createNewVaultAndKeychainSpy, placeSeedWordsSpy
+
+ afterEach(() => {
+ createNewVaultAndKeychainSpy.restore()
+ placeSeedWordsSpy.restore()
+ })
+
+ it('calls createNewVaultAndKeychain and placeSeedWords in background', () => {
+
+ const store = mockStore()
+
+ createNewVaultAndKeychainSpy = sinon.spy(background, 'createNewVaultAndKeychain')
+ placeSeedWordsSpy = sinon.spy(background, 'placeSeedWords')
+
+ return store.dispatch(actions.createNewVaultAndKeychain())
+ .then(() => {
+ assert(createNewVaultAndKeychainSpy.calledOnce)
+ assert(placeSeedWordsSpy.calledOnce)
+ })
+ })
+
+ it('displays error and value when callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ createNewVaultAndKeychainSpy = sinon.stub(background, 'createNewVaultAndKeychain')
+ createNewVaultAndKeychainSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndKeychain())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+
+ it('errors when placeSeedWords throws', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ placeSeedWordsSpy = sinon.stub(background, 'placeSeedWords')
+ placeSeedWordsSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndKeychain())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#requestRevealSeed', () => {
+
+ let submitPasswordSpy, placeSeedWordsSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ })
+
+ it('calls submitPassword and placeSeedWords from background', () => {
+
+ const store = mockStore()
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ placeSeedWordsSpy = sinon.spy(background, 'placeSeedWords')
+
+ return store.dispatch(actions.requestRevealSeed())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(placeSeedWordsSpy.calledOnce)
+ })
+ })
+
+ it('displays warning error with value when callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.requestRevealSeed())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#requestRevealSeedWords', () => {
+ let submitPasswordSpy
+
+ it('calls submitPassword in background', () => {
+ const store = mockStore()
+
+ submitPasswordSpy = sinon.spy(background, 'verifySeedPhrase')
+
+ return store.dispatch(actions.requestRevealSeedWords())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ })
+ })
+
+ it('displays warning error message then callback in background errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ submitPasswordSpy = sinon.stub(background, 'verifySeedPhrase')
+ submitPasswordSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.requestRevealSeedWords())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+ })
+
+ describe('#requestRevealSeed', () => {
+
+ let submitPasswordSpy, placeSeedWordsSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ placeSeedWordsSpy.restore()
+ })
+
+ it('calls submitPassword and placeSeedWords in background', () => {
+
+ const store = mockStore()
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ placeSeedWordsSpy = sinon.spy(background, 'placeSeedWords')
+
+ return store.dispatch(actions.requestRevealSeed())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(placeSeedWordsSpy.calledOnce)
+ })
+ })
+
+ it('displays warning error message when submitPassword in background errors', () => {
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ return store.dispatch(actions.requestRevealSeed())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('errors when placeSeedWords throw', () => {
+ placeSeedWordsSpy = sinon.stub(background, 'placeSeedWords')
+ placeSeedWordsSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ return store.dispatch(actions.requestRevealSeed())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#removeAccount', () => {
+ let removeAccountSpy
+
+ afterEach(() => {
+ removeAccountSpy.restore()
+ })
+
+ it('calls removeAccount in background and expect actions to show account', () => {
+ const store = mockStore(devState)
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_ACCOUNTS_PAGE' },
+ ]
+
+ removeAccountSpy = sinon.spy(background, 'removeAccount')
+
+ return store.dispatch(actions.removeAccount('0xe18035bf8712672935fdb4e5e431b1a0183d2dfc'))
+ .then(() => {
+ assert(removeAccountSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('displays warning error message when removeAccount callback errors', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ removeAccountSpy = sinon.stub(background, 'removeAccount')
+ removeAccountSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.removeAccount('0xe18035bf8712672935fdb4e5e431b1a0183d2dfc'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+ })
+
+ describe('#addNewKeyring', () => {
+ let addNewKeyringSpy
+
+ beforeEach(() => {
+ addNewKeyringSpy = sinon.stub(background, 'addNewKeyring')
+ })
+
+ afterEach(() => {
+ addNewKeyringSpy.restore()
+ })
+
+ it('', () => {
+ const privateKey = 'c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3'
+
+ const store = mockStore()
+ store.dispatch(actions.addNewKeyring('Simple Key Pair', [ privateKey ]))
+ assert(addNewKeyringSpy.calledOnce)
+ })
+
+ it('errors then addNewKeyring in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ addNewKeyringSpy.callsFake((type, opts, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.addNewKeyring())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+
+ describe('#resetAccount', () => {
+
+ let resetAccountSpy
+
+ afterEach(() => {
+ resetAccountSpy.restore()
+ })
+
+ it('', () => {
+
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_ACCOUNTS_PAGE' },
+ ]
+
+ resetAccountSpy = sinon.spy(background, 'resetAccount')
+
+ return store.dispatch(actions.resetAccount())
+ .then(() => {
+ assert(resetAccountSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ resetAccountSpy = sinon.stub(background, 'resetAccount')
+ resetAccountSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.resetAccount())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#importNewAccount', () => {
+
+ let importAccountWithStrategySpy
+
+ afterEach(() => {
+ importAccountWithStrategySpy.restore()
+ })
+
+ it('calls importAccountWithStrategies in background', () => {
+ const store = mockStore()
+
+ importAccountWithStrategySpy = sinon.spy(background, 'importAccountWithStrategy')
+
+ const importPrivkey = 'c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3'
+
+ return store.dispatch(actions.importNewAccount('Private Key', [ importPrivkey ]))
+ .then(() => {
+ assert(importAccountWithStrategySpy.calledOnce)
+ })
+ })
+
+ it('displays warning error message when importAccount in background callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: 'This may take a while, please be patient.' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ importAccountWithStrategySpy = sinon.stub(background, 'importAccountWithStrategy')
+ importAccountWithStrategySpy.callsFake((strategy, args, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.importNewAccount())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#addNewAccount', () => {
+
+ let addNewAccountSpy
+
+ afterEach(() => {
+ addNewAccountSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore({ metamask: devState })
+
+ addNewAccountSpy = sinon.spy(background, 'addNewAccount')
+
+ return store.dispatch(actions.addNewAccount())
+ .then(() => {
+ assert(addNewAccountSpy.calledOnce)
+ })
+ })
+ })
+
+ describe('#setCurrentCurrency', () => {
+
+ let setCurrentCurrencySpy
+
+ beforeEach(() => {
+ setCurrentCurrencySpy = sinon.stub(background, 'setCurrentCurrency')
+ })
+
+ afterEach(() => {
+ setCurrentCurrencySpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.setCurrentCurrency('jpy'))
+ assert(setCurrentCurrencySpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ setCurrentCurrencySpy.callsFake((currencyCode, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setCurrentCurrency())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#signMsg', () => {
+
+ let signMessageSpy, metamaskMsgs, msgId, messages
+
+ const msgParams = {
+ from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+ data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0',
+ }
+
+ beforeEach(() => {
+ metamaskController.newUnsignedMessage(msgParams, noop)
+ metamaskMsgs = metamaskController.messageManager.getUnapprovedMsgs()
+ messages = metamaskController.messageManager.messages
+ msgId = Object.keys(metamaskMsgs)[0]
+ messages[0].msgParams.metamaskId = parseInt(msgId)
+ })
+
+ afterEach(() => {
+ signMessageSpy.restore()
+ })
+
+ it('calls signMsg in background', () => {
+ const store = mockStore()
+
+ signMessageSpy = sinon.spy(background, 'signMessage')
+
+ return store.dispatch(actions.signMsg(msgParams))
+ .then(() => {
+ assert(signMessageSpy.calledOnce)
+ })
+
+ })
+
+ it('errors when signMessage in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'UPDATE_METAMASK_STATE', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ signMessageSpy = sinon.stub(background, 'signMessage')
+ signMessageSpy.callsFake((msgData, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.signMsg())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ })
+
+ describe('#signPersonalMsg', () => {
+
+ let signPersonalMessageSpy, metamaskMsgs, msgId, personalMessages
+
+ const msgParams = {
+ from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+ data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0',
+ }
+
+ beforeEach(() => {
+ metamaskController.newUnsignedPersonalMessage(msgParams, noop)
+ metamaskMsgs = metamaskController.personalMessageManager.getUnapprovedMsgs()
+ personalMessages = metamaskController.personalMessageManager.messages
+ msgId = Object.keys(metamaskMsgs)[0]
+ personalMessages[0].msgParams.metamaskId = parseInt(msgId)
+ })
+
+ afterEach(() => {
+ signPersonalMessageSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ signPersonalMessageSpy = sinon.spy(background, 'signPersonalMessage')
+
+ return store.dispatch(actions.signPersonalMsg(msgParams))
+ .then(() => {
+ assert(signPersonalMessageSpy.calledOnce)
+ })
+
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'UPDATE_METAMASK_STATE', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ signPersonalMessageSpy = sinon.stub(background, 'signPersonalMessage')
+ signPersonalMessageSpy.callsFake((msgData, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.signPersonalMsg(msgParams))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ })
+
+ describe('#signTx', () => {
+
+ let sendTransactionSpy
+
+ beforeEach(() => {
+ global.ethQuery = new EthQuery(provider)
+ sendTransactionSpy = sinon.stub(global.ethQuery, 'sendTransaction')
+ })
+
+ afterEach(() => {
+ sendTransactionSpy.restore()
+ })
+
+ it('calls sendTransaction in global ethQuery', () => {
+ const store = mockStore()
+ store.dispatch(actions.signTx())
+ assert(sendTransactionSpy.calledOnce)
+ })
+
+ it('errors in when sendTransaction throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'SHOW_CONF_TX_PAGE', transForward: true, id: undefined },
+ ]
+ sendTransactionSpy.callsFake((txData, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.signTx())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#signTokenTx', () => {
+
+ let tokenSpy
+
+ beforeEach(() => {
+ global.eth = new Eth(provider)
+ tokenSpy = sinon.spy(global.eth, 'contract')
+ })
+
+ afterEach(() => {
+ tokenSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.signTokenTx())
+ assert(tokenSpy.calledOnce)
+ })
+ })
+
+ describe('#lockMetamask', () => {
+ let backgroundSetLockedSpy
+
+ afterEach(() => {
+ backgroundSetLockedSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ backgroundSetLockedSpy = sinon.spy(background, 'setLocked')
+
+ return store.dispatch(actions.lockMetamask())
+ .then(() => {
+ assert(backgroundSetLockedSpy.calledOnce)
+ })
+ })
+
+ it('returns display warning error with value when setLocked in background callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'LOCK_METAMASK' },
+ ]
+ backgroundSetLockedSpy = sinon.stub(background, 'setLocked')
+ backgroundSetLockedSpy.callsFake(callback => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.lockMetamask())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setSelectedAddress', () => {
+ let setSelectedAddressSpy
+
+ beforeEach(() => {
+ setSelectedAddressSpy = sinon.stub(background, 'setSelectedAddress')
+ })
+
+ afterEach(() => {
+ setSelectedAddressSpy.restore()
+ })
+
+ it('calls setSelectedAddress in background', () => {
+ const store = mockStore({ metamask: devState })
+
+ store.dispatch(actions.setSelectedAddress('0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ assert(setSelectedAddressSpy.calledOnce)
+ })
+
+ it('errors when setSelectedAddress throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ setSelectedAddressSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setSelectedAddress())
+ assert.deepEqual(store.getActions(), expectedActions)
+
+ })
+ })
+
+ describe('#showAccountDetail', () => {
+ let setSelectedAddressSpy
+
+ beforeEach(() => {
+ setSelectedAddressSpy = sinon.stub(background, 'setSelectedAddress')
+ })
+
+ afterEach(() => {
+ setSelectedAddressSpy.restore()
+ })
+
+ it('#showAccountDetail', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.showAccountDetail())
+ assert(setSelectedAddressSpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ setSelectedAddressSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+
+ store.dispatch(actions.showAccountDetail())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#addToken', () => {
+ let addTokenSpy
+
+ beforeEach(() => {
+ addTokenSpy = sinon.stub(background, 'addToken')
+ })
+
+ afterEach(() => {
+ addTokenSpy.restore()
+ })
+
+ it('calls addToken in background', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.addToken())
+ .then(() => {
+ assert(addTokenSpy.calledOnce)
+ })
+ })
+
+ it('errors when addToken in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'UPDATE_TOKENS', newTokens: undefined },
+ ]
+
+ addTokenSpy.callsFake((address, symbol, decimals, image, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.addToken())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#removeToken', () => {
+
+ let removeTokenSpy
+
+ beforeEach(() => {
+ removeTokenSpy = sinon.stub(background, 'removeToken')
+ })
+
+ afterEach(() => {
+ removeTokenSpy.restore()
+ })
+
+ it('calls removeToken in background', () => {
+ const store = mockStore()
+ store.dispatch(actions.removeToken())
+ .then(() => {
+ assert(removeTokenSpy.calledOnce)
+ })
+ })
+
+ it('errors when removeToken in background fails', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'UPDATE_TOKENS', newTokens: undefined },
+ ]
+
+ removeTokenSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.removeToken())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#markNoticeRead', () => {
+ let markNoticeReadSpy
+ const notice = {
+ id: 0,
+ read: false,
+ date: 'test date',
+ title: 'test title',
+ body: 'test body',
+ }
+
+ beforeEach(() => {
+ markNoticeReadSpy = sinon.stub(background, 'markNoticeRead')
+ })
+
+ afterEach(() => {
+ markNoticeReadSpy.restore()
+ })
+
+ it('calls markNoticeRead in background', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.markNoticeRead(notice))
+ .then(() => {
+ assert(markNoticeReadSpy.calledOnce)
+ })
+
+ })
+
+ it('errors when markNoticeRead in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ markNoticeReadSpy.callsFake((notice, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.markNoticeRead())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setProviderType', () => {
+ let setProviderTypeSpy
+
+ beforeEach(() => {
+ setProviderTypeSpy = sinon.stub(background, 'setProviderType')
+ })
+
+ afterEach(() => {
+ setProviderTypeSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.setProviderType())
+ assert(setProviderTypeSpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
+ ]
+
+ setProviderTypeSpy.callsFake((type, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setProviderType())
+ assert(setProviderTypeSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#setRpcTarget', () => {
+ let setRpcTargetSpy
+
+ beforeEach(() => {
+ setRpcTargetSpy = sinon.stub(background, 'setCustomRpc')
+ })
+
+ afterEach(() => {
+ setRpcTargetSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.setRpcTarget('http://localhost:8545'))
+ assert(setRpcTargetSpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
+ ]
+
+ setRpcTargetSpy.callsFake((newRpc, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setRpcTarget())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#addToAddressBook', () => {
+ let addToAddressBookSpy
+
+ beforeEach(() => {
+ addToAddressBookSpy = sinon.stub(background, 'setAddressBook')
+ })
+
+ afterEach(() => {
+ addToAddressBookSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.addToAddressBook('test'))
+ assert(addToAddressBookSpy.calledOnce)
+ })
+ })
+
+ describe('#exportAccount', () => {
+ let submitPasswordSpy, exportAccountSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ exportAccountSpy.restore()
+ })
+
+ it('returns expected actions for successful action', () => {
+ const store = mockStore(devState)
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_PRIVATE_KEY', value: '7ec73b91bb20f209a7ff2d32f542c3420b4fccf14abcc7840d2eff0ebcb18505' },
+ ]
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ exportAccountSpy = sinon.spy(background, 'exportAccount')
+
+ return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ .then((result) => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(exportAccountSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('returns action errors when first func callback errors', () => {
+ const store = mockStore(devState)
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'Incorrect Password.' },
+ ]
+
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('returns action errors when second func callback errors', () => {
+ const store = mockStore(devState)
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'Had a problem exporting the account.' },
+ ]
+
+ exportAccountSpy = sinon.stub(background, 'exportAccount')
+ exportAccountSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setAccountLabel', () => {
+ let setAccountLabelSpy
+
+ beforeEach(() => {
+ setAccountLabelSpy = sinon.stub(background, 'setAccountLabel')
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.setAccountLabel('0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', 'test'))
+ assert(setAccountLabelSpy.calledOnce)
+ })
+ })
+
+ describe('#pairUpdate', () => {
+ beforeEach(() => {
+
+ nock('https://shapeshift.io')
+ .defaultReplyHeaders({ 'access-control-allow-origin': '*' })
+ .get('/marketinfo/btc_eth')
+ .reply(200, {pair: 'BTC_ETH', rate: 25.68289016, minerFee: 0.00176, limit: 0.67748474, minimum: 0.00013569, maxLimit: 0.67758573})
+
+ nock('https://shapeshift.io')
+ .defaultReplyHeaders({ 'access-control-allow-origin': '*' })
+ .get('/coins')
+ .reply(200)
+ })
+
+ afterEach(() => {
+ nock.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ // issue with dispatch action in callback not showing
+ const expectedActions = [
+ { type: 'SHOW_SUB_LOADING_INDICATION' },
+ { type: 'HIDE_WARNING' },
+ ]
+
+ store.dispatch(actions.pairUpdate('btc'))
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#setFeatureFlag', () => {
+ let setFeatureFlagSpy
+
+ beforeEach(() => {
+ setFeatureFlagSpy = sinon.stub(background, 'setFeatureFlag')
+ })
+
+ afterEach(() => {
+ setFeatureFlagSpy.restore()
+ })
+
+ it('calls setFeatureFlag in the background', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.setFeatureFlag())
+ assert(setFeatureFlagSpy.calledOnce)
+ })
+
+ it('errors when setFeatureFlag in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ setFeatureFlagSpy.callsFake((feature, activated, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setFeatureFlag())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#updateNetworkNonce', () => {
+ let getTransactionCountSpy
+
+ afterEach(() => {
+ getTransactionCountSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ getTransactionCountSpy = sinon.spy(global.ethQuery, 'getTransactionCount')
+
+ store.dispatch(actions.updateNetworkNonce())
+ .then(() => {
+ assert(getTransactionCountSpy.calledOnce)
+ })
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ getTransactionCountSpy = sinon.stub(global.ethQuery, 'getTransactionCount')
+ getTransactionCountSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.updateNetworkNonce())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setUseBlockie', () => {
+ let setUseBlockieSpy
+
+ beforeEach(() => {
+ setUseBlockieSpy = sinon.stub(background, 'setUseBlockie')
+ })
+
+ afterEach(() => {
+ setUseBlockieSpy.restore()
+ })
+
+ it('calls setUseBlockie in background', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.setUseBlockie())
+ assert(setUseBlockieSpy.calledOnce)
+ })
+
+ it('errors when setUseBlockie in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'SET_USE_BLOCKIE', value: undefined },
+ ]
+
+ setUseBlockieSpy.callsFake((val, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setUseBlockie())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#updateCurrentLocale', () => {
+ let setCurrentLocaleSpy
+
+ beforeEach(() => {
+ fetchMock.get('*', enLocale)
+ })
+
+ afterEach(() => {
+ setCurrentLocaleSpy.restore()
+ fetchMock.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ setCurrentLocaleSpy = sinon.spy(background, 'setCurrentLocale')
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SET_CURRENT_LOCALE', value: 'en' },
+ { type: 'SET_LOCALE_MESSAGES', value: enLocale },
+ ]
+
+ return store.dispatch(actions.updateCurrentLocale('en'))
+ .then(() => {
+ assert(setCurrentLocaleSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ setCurrentLocaleSpy = sinon.stub(background, 'setCurrentLocale')
+ setCurrentLocaleSpy.callsFake((key, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.updateCurrentLocale('en'))
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#markPasswordForgotten', () => {
+ let markPasswordForgottenSpy
+
+ beforeEach(() => {
+ markPasswordForgottenSpy = sinon.stub(background, 'markPasswordForgotten')
+ })
+
+ afterEach(() => {
+ markPasswordForgottenSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.markPasswordForgotten())
+ assert(markPasswordForgottenSpy.calledOnce)
+ })
+ })
+
+ describe('#unMarkPasswordForgotten', () => {
+ let unMarkPasswordForgottenSpy
+
+ beforeEach(() => {
+ unMarkPasswordForgottenSpy = sinon.stub(background, 'unMarkPasswordForgotten')
+ })
+
+ afterEach(() => {
+ unMarkPasswordForgottenSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.unMarkPasswordForgotten())
+ assert(unMarkPasswordForgottenSpy.calledOnce)
+ })
+ })
+
+
+})
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 8f6586139..eea581d33 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -1762,7 +1762,7 @@ function markNoticeRead (notice) {
background.markNoticeRead(notice, (err, notice) => {
dispatch(actions.hideLoadingIndication())
if (err) {
- dispatch(actions.displayWarning(err))
+ dispatch(actions.displayWarning(err.message))
return reject(err)
}
@@ -1852,7 +1852,7 @@ function setProviderType (type) {
background.setProviderType(type, (err, result) => {
if (err) {
log.error(err)
- return dispatch(self.displayWarning('Had a problem changing networks!'))
+ return dispatch(actions.displayWarning('Had a problem changing networks!'))
}
dispatch(actions.updateProviderType(type))
dispatch(actions.setSelectedToken())
@@ -1874,7 +1874,7 @@ function setRpcTarget (newRpc) {
background.setCustomRpc(newRpc, (err, result) => {
if (err) {
log.error(err)
- return dispatch(self.displayWarning('Had a problem changing networks!'))
+ return dispatch(actions.displayWarning('Had a problem changing networks!'))
}
dispatch(actions.setSelectedToken())
})
@@ -2309,6 +2309,10 @@ function updateNetworkNonce (address) {
return (dispatch) => {
return new Promise((resolve, reject) => {
global.ethQuery.getTransactionCount(address, (err, data) => {
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
dispatch(setNetworkNonce(data))
resolve(data)
})
@@ -2396,7 +2400,7 @@ function setUseBlockie (val) {
function updateCurrentLocale (key) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- fetchLocale(key)
+ return fetchLocale(key)
.then((localeMessages) => {
log.debug(`background.setCurrentLocale`)
background.setCurrentLocale(key, (err) => {