commit
f82c51c2c4
@ -0,0 +1,912 @@ |
|||||||
|
{ |
||||||
|
"accept": { |
||||||
|
"message": "Kabul et" |
||||||
|
}, |
||||||
|
"account": { |
||||||
|
"message": "Hesap" |
||||||
|
}, |
||||||
|
"accountDetails": { |
||||||
|
"message": "Hesap Detayları" |
||||||
|
}, |
||||||
|
"accountName": { |
||||||
|
"message": "Hesap İsmi" |
||||||
|
}, |
||||||
|
"address": { |
||||||
|
"message": "Adres" |
||||||
|
}, |
||||||
|
"addCustomToken": { |
||||||
|
"message": "Özel jeton ekle" |
||||||
|
}, |
||||||
|
"addToken": { |
||||||
|
"message": "Jeton ekle" |
||||||
|
}, |
||||||
|
"addTokens": { |
||||||
|
"message": "Jetonlar ekle" |
||||||
|
}, |
||||||
|
"amount": { |
||||||
|
"message": "Tutar" |
||||||
|
}, |
||||||
|
"amountPlusGas": { |
||||||
|
"message": "Tutar + Gas" |
||||||
|
}, |
||||||
|
"appDescription": { |
||||||
|
"message": "Ethereum Tarayıcı Uzantısı", |
||||||
|
"description": "Uygulama açıklaması" |
||||||
|
}, |
||||||
|
"appName": { |
||||||
|
"message": "MetaMask", |
||||||
|
"description": "Uygulama ismi" |
||||||
|
}, |
||||||
|
"approved": { |
||||||
|
"message": "Onaylandı" |
||||||
|
}, |
||||||
|
"attemptingConnect": { |
||||||
|
"message": "Blockchain'e bağlanmayı deniyor" |
||||||
|
}, |
||||||
|
"attributions": { |
||||||
|
"message": "Atıflar" |
||||||
|
}, |
||||||
|
"available": { |
||||||
|
"message": "Müsait" |
||||||
|
}, |
||||||
|
"back": { |
||||||
|
"message": "Geri" |
||||||
|
}, |
||||||
|
"balance": { |
||||||
|
"message": "Bakiye:" |
||||||
|
}, |
||||||
|
"balances": { |
||||||
|
"message": "Jeton bakiyesi" |
||||||
|
}, |
||||||
|
"balanceIsInsufficientGas": { |
||||||
|
"message": "Toplam gas için yetersiz bakiye" |
||||||
|
}, |
||||||
|
"beta": { |
||||||
|
"message": "BETA" |
||||||
|
}, |
||||||
|
"betweenMinAndMax": { |
||||||
|
"message": "$1'e eşit veya daha büyük olmalı ve $2'den küçük veya eşit olmalı", |
||||||
|
"description": "Onaltılık sayının ondalık sayı olarak girişi için yardımcı" |
||||||
|
}, |
||||||
|
"blockiesIdenticon": { |
||||||
|
"message": "Blockies Identicon kullan" |
||||||
|
}, |
||||||
|
"borrowDharma": { |
||||||
|
"message": "Dharma (Beta) ile ödünç al" |
||||||
|
}, |
||||||
|
"builtInCalifornia": { |
||||||
|
"message": "MetaMask California'da tasarlandı ve yaratıldı" |
||||||
|
}, |
||||||
|
"buy": { |
||||||
|
"message": "Satın al" |
||||||
|
}, |
||||||
|
"buyCoinbase": { |
||||||
|
"message": "Coinbase'de satın al" |
||||||
|
}, |
||||||
|
"buyCoinbaseExplainer": { |
||||||
|
"message": "Coinbase bitcoin, ethereum, and litecoin alıp satmanın dünyadaki en popüler yolu" |
||||||
|
}, |
||||||
|
"ok": { |
||||||
|
"message": "Tamam" |
||||||
|
}, |
||||||
|
"cancel": { |
||||||
|
"message": "Vazgeç" |
||||||
|
}, |
||||||
|
"classicInterface": { |
||||||
|
"message": "Klasik arayüzü kullan" |
||||||
|
}, |
||||||
|
"clickCopy": { |
||||||
|
"message": "Kopyalamak için tıkla" |
||||||
|
}, |
||||||
|
"confirm": { |
||||||
|
"message": "Onayla" |
||||||
|
}, |
||||||
|
"confirmed": { |
||||||
|
"message": "Onaylandı" |
||||||
|
}, |
||||||
|
"confirmContract": { |
||||||
|
"message": "Sözleşmeyi onayla" |
||||||
|
}, |
||||||
|
"confirmPassword": { |
||||||
|
"message": "Şifreyi onayla" |
||||||
|
}, |
||||||
|
"confirmTransaction": { |
||||||
|
"message": "İşlemi onayla" |
||||||
|
}, |
||||||
|
"continue": { |
||||||
|
"message": "Devam et" |
||||||
|
}, |
||||||
|
"continueToCoinbase": { |
||||||
|
"message": "Coinbase'e devam et" |
||||||
|
}, |
||||||
|
"contractDeployment": { |
||||||
|
"message": "Sözleşme kurulumu" |
||||||
|
}, |
||||||
|
"conversionProgress": { |
||||||
|
"message": "Çevirim devam ediyor" |
||||||
|
}, |
||||||
|
"copiedButton": { |
||||||
|
"message": "Kopyalandı" |
||||||
|
}, |
||||||
|
"copiedClipboard": { |
||||||
|
"message": "Panoya kopyalandı" |
||||||
|
}, |
||||||
|
"copiedExclamation": { |
||||||
|
"message": "Kopyalandı!" |
||||||
|
}, |
||||||
|
"copiedSafe": { |
||||||
|
"message": "Güvenli bir yere kopyaladım" |
||||||
|
}, |
||||||
|
"copy": { |
||||||
|
"message": "Kopyala" |
||||||
|
}, |
||||||
|
"copyToClipboard": { |
||||||
|
"message": "Panoya kopyala" |
||||||
|
}, |
||||||
|
"copyButton": { |
||||||
|
"message": " Kopyala " |
||||||
|
}, |
||||||
|
"copyPrivateKey": { |
||||||
|
"message": "Bu sizin özel anahtarınız (kopyalamak için tıklayın)" |
||||||
|
}, |
||||||
|
"create": { |
||||||
|
"message": "Yarat" |
||||||
|
}, |
||||||
|
"createAccount": { |
||||||
|
"message": "Hesap Oluştur" |
||||||
|
}, |
||||||
|
"createDen": { |
||||||
|
"message": "Yarat" |
||||||
|
}, |
||||||
|
"crypto": { |
||||||
|
"message": "Kripto", |
||||||
|
"description": "Kambiyo tipi (kripto para)" |
||||||
|
}, |
||||||
|
"currentConversion": { |
||||||
|
"message": "Geçerli çevirme" |
||||||
|
}, |
||||||
|
"currentNetwork": { |
||||||
|
"message": "Geçerli Ağ" |
||||||
|
}, |
||||||
|
"customGas": { |
||||||
|
"message": "Gas'i özelleştir" |
||||||
|
}, |
||||||
|
"customToken": { |
||||||
|
"message": "Özel Jeton" |
||||||
|
}, |
||||||
|
"customize": { |
||||||
|
"message": "Özelleştir" |
||||||
|
}, |
||||||
|
"customRPC": { |
||||||
|
"message": "Özel RPC" |
||||||
|
}, |
||||||
|
"decimalsMustZerotoTen": { |
||||||
|
"message": "Ondalıklar en azından 0 olmalı ve 36'dan büyük olmamalı." |
||||||
|
}, |
||||||
|
"decimal": { |
||||||
|
"message": "Ondalık hassasiyeti" |
||||||
|
}, |
||||||
|
"defaultNetwork": { |
||||||
|
"message": "Ether işlemleri için varsayılan ağ Main Net." |
||||||
|
}, |
||||||
|
"denExplainer": { |
||||||
|
"message": "DEN'iniz MetaMask içersinde parola-şifrelenmiş deponuzdur." |
||||||
|
}, |
||||||
|
"deposit": { |
||||||
|
"message": "Yatır" |
||||||
|
}, |
||||||
|
"depositBTC": { |
||||||
|
"message": "BTC'inizi aşağıdaki adrese yatırın:" |
||||||
|
}, |
||||||
|
"depositCoin": { |
||||||
|
"message": "$1'nızı aşağıdaki adrese yatırın", |
||||||
|
"description": "Kullanıcıya hangi jetonu seçtiyse onu yatırmasını shapeshift ile söyler." |
||||||
|
}, |
||||||
|
"depositEth": { |
||||||
|
"message": "Eth yatır" |
||||||
|
}, |
||||||
|
"depositEther": { |
||||||
|
"message": "Ether yatır" |
||||||
|
}, |
||||||
|
"depositFiat": { |
||||||
|
"message": "Para yatır" |
||||||
|
}, |
||||||
|
"depositFromAccount": { |
||||||
|
"message": "Başka bir hesaptan yatır" |
||||||
|
}, |
||||||
|
"depositShapeShift": { |
||||||
|
"message": "ShapeShift ile yatır" |
||||||
|
}, |
||||||
|
"depositShapeShiftExplainer": { |
||||||
|
"message": "Eğer başka kripto paralara sahipseniz, MetaMask cüzdanınıza direk olarak Ether yatırabilirsiniz. Hesaba gerek yoktur." |
||||||
|
}, |
||||||
|
"details": { |
||||||
|
"message": "Ayrıntılar" |
||||||
|
}, |
||||||
|
"directDeposit": { |
||||||
|
"message": "Direk Yatırma" |
||||||
|
}, |
||||||
|
"directDepositEther": { |
||||||
|
"message": "Direk Ether Yatırma" |
||||||
|
}, |
||||||
|
"directDepositEtherExplainer": { |
||||||
|
"message": "Eğer çoktan Etheriniz varsa, yeni hesabınıza Ether aktarmanın en kolay yolu direk yatırmadır." |
||||||
|
}, |
||||||
|
"done": { |
||||||
|
"message": "Bitti" |
||||||
|
}, |
||||||
|
"downloadStateLogs": { |
||||||
|
"message": "Durum kayıtlarını indir" |
||||||
|
}, |
||||||
|
"dropped": { |
||||||
|
"message": "Bırakıldı" |
||||||
|
}, |
||||||
|
"edit": { |
||||||
|
"message": "Düzenle" |
||||||
|
}, |
||||||
|
"editAccountName": { |
||||||
|
"message": "Hesap ismini düzenle" |
||||||
|
}, |
||||||
|
"emailUs": { |
||||||
|
"message": "Bize e-posta atın!" |
||||||
|
}, |
||||||
|
"encryptNewDen": { |
||||||
|
"message": "Yeni DEN'inizi şifreleyin" |
||||||
|
}, |
||||||
|
"enterPassword": { |
||||||
|
"message": "Parolanızı girin" |
||||||
|
}, |
||||||
|
"enterPasswordConfirm": { |
||||||
|
"message": "Onaylamak için parolanızı girin" |
||||||
|
}, |
||||||
|
"passwordNotLongEnough": { |
||||||
|
"message": "Parola yeterince uzun değil" |
||||||
|
}, |
||||||
|
"passwordsDontMatch": { |
||||||
|
"message": "Parolalar eşleşmiyor" |
||||||
|
}, |
||||||
|
"etherscanView": { |
||||||
|
"message": "Hesabı Etherscan üzerinde izle" |
||||||
|
}, |
||||||
|
"exchangeRate": { |
||||||
|
"message": "Döviz kuru" |
||||||
|
}, |
||||||
|
"exportPrivateKey": { |
||||||
|
"message": "Özel anahtarı ver" |
||||||
|
}, |
||||||
|
"exportPrivateKeyWarning": { |
||||||
|
"message": "Özel anahtarınızı vermek sizin sorumluluğunuzdadır." |
||||||
|
}, |
||||||
|
"failed": { |
||||||
|
"message": "Başarısız oldu" |
||||||
|
}, |
||||||
|
"fiat": { |
||||||
|
"message": "Para", |
||||||
|
"description": "Döviz türü" |
||||||
|
}, |
||||||
|
"fileImportFail": { |
||||||
|
"message": "Dosya alma çalışmıyor mu? Buraya tıklayın!", |
||||||
|
"description": "Kullanıcıların hesaplarını JSON dosyasından almalarına yardım eder" |
||||||
|
}, |
||||||
|
"followTwitter": { |
||||||
|
"message": "Bizi twitter'da takip edin" |
||||||
|
}, |
||||||
|
"from": { |
||||||
|
"message": "Kimden" |
||||||
|
}, |
||||||
|
"fromToSame": { |
||||||
|
"message": "Kimden ve kime adresi aynı olamaz" |
||||||
|
}, |
||||||
|
"fromShapeShift": { |
||||||
|
"message": "ShapeShift'den" |
||||||
|
}, |
||||||
|
"gas": { |
||||||
|
"message": "Gas", |
||||||
|
"description": "Gas maliyetinin kısa indikatörü" |
||||||
|
}, |
||||||
|
"gasFee": { |
||||||
|
"message": "Gas Ücreti" |
||||||
|
}, |
||||||
|
"gasLimit": { |
||||||
|
"message": "Gas Limiti" |
||||||
|
}, |
||||||
|
"gasLimitCalculation": { |
||||||
|
"message": "Önerilen gas limitini ağ başarı oranını baz alarak hesaplıyoruz." |
||||||
|
}, |
||||||
|
"gasLimitRequired": { |
||||||
|
"message": "Gas limiti gereklidir" |
||||||
|
}, |
||||||
|
"gasLimitTooLow": { |
||||||
|
"message": "Gas limiti en az 21000 olmalıdır" |
||||||
|
}, |
||||||
|
"generatingSeed": { |
||||||
|
"message": "Kaynak Oluşturuyor..." |
||||||
|
}, |
||||||
|
"gasPrice": { |
||||||
|
"message": "Gas Fiyatı (GWEI)" |
||||||
|
}, |
||||||
|
"gasPriceCalculation": { |
||||||
|
"message": "Önerilen gas fiyatını ağ başarı oranını baz alarak hesaplıyoruz." |
||||||
|
}, |
||||||
|
"gasPriceRequired": { |
||||||
|
"message": "Gas Fiyatı Gereklidir" |
||||||
|
}, |
||||||
|
"getEther": { |
||||||
|
"message": "Ether Al" |
||||||
|
}, |
||||||
|
"getEtherFromFaucet": { |
||||||
|
"message": "Musluktan $1 karşılığı Ether alın", |
||||||
|
"description": "Ether musluğunun ağ ismini gösterir" |
||||||
|
}, |
||||||
|
"greaterThanMin": { |
||||||
|
"message": "must be greater than or equal to $1.", |
||||||
|
"description": "helper for inputting hex as decimal input" |
||||||
|
}, |
||||||
|
"here": { |
||||||
|
"message": "burada", |
||||||
|
"description": "daha fazla bilgi için -buraya tıklayın- (troubleTokenBalances ile gidiyor)" |
||||||
|
}, |
||||||
|
"hereList": { |
||||||
|
"message": "İşte bir liste!!!!" |
||||||
|
}, |
||||||
|
"hide": { |
||||||
|
"message": "Gizle" |
||||||
|
}, |
||||||
|
"hideToken": { |
||||||
|
"message": "Jetonu gizle" |
||||||
|
}, |
||||||
|
"hideTokenPrompt": { |
||||||
|
"message": "Jetonu gizle?" |
||||||
|
}, |
||||||
|
"howToDeposit": { |
||||||
|
"message": "Ether'i nasıl yatırmak istersiniz?" |
||||||
|
}, |
||||||
|
"holdEther": { |
||||||
|
"message": "Ether ve jeton tutmanızı sağlar ve merkezi olmayan uygulamalar ve sizin aranızda köprü vazifesi görür." |
||||||
|
}, |
||||||
|
"import": { |
||||||
|
"message": "Al", |
||||||
|
"description": "Seçilen dosyadan hesap alma düğmesi. " |
||||||
|
}, |
||||||
|
"importAccount": { |
||||||
|
"message": "Hesap Al" |
||||||
|
}, |
||||||
|
"importAccountMsg": { |
||||||
|
"message":" Alınan hesaplar orjinal kaynakifadenizle yarattığınız MetaMask hesabınızla ilişkilendirilmez. Alınan hesaplar ile ilgili daha fazla bilgi edinin " |
||||||
|
}, |
||||||
|
"importAnAccount": { |
||||||
|
"message": "Hesap al" |
||||||
|
}, |
||||||
|
"importDen": { |
||||||
|
"message": "Varolan DEN al" |
||||||
|
}, |
||||||
|
"imported": { |
||||||
|
"message": "Alındı", |
||||||
|
"description": "Hesabın keyringe başarı ile alındığını gösteren durum" |
||||||
|
}, |
||||||
|
"infoHelp": { |
||||||
|
"message": "Bilgi ve yardım" |
||||||
|
}, |
||||||
|
"insufficientFunds": { |
||||||
|
"message": "Yetersiz kaynak." |
||||||
|
}, |
||||||
|
"insufficientTokens": { |
||||||
|
"message": "Yetersiz Jeton." |
||||||
|
}, |
||||||
|
"invalidAddress": { |
||||||
|
"message": "Geçersiz adres" |
||||||
|
}, |
||||||
|
"invalidAddressRecipient": { |
||||||
|
"message": "Alıcı adresi geçersiz" |
||||||
|
}, |
||||||
|
"invalidGasParams": { |
||||||
|
"message": "Geçersiz gas parametreleri" |
||||||
|
}, |
||||||
|
"invalidInput": { |
||||||
|
"message": "Geçersiz giriş." |
||||||
|
}, |
||||||
|
"invalidRequest": { |
||||||
|
"message": "Geçersiz istek" |
||||||
|
}, |
||||||
|
"invalidRPC": { |
||||||
|
"message": "Geçersiz RPC URI" |
||||||
|
}, |
||||||
|
"jsonFail": { |
||||||
|
"message": "Birşeyler yanlış gitti. JSON dosyanızın düzgün derlendiğinden emin olun." |
||||||
|
}, |
||||||
|
"jsonFile": { |
||||||
|
"message": "JSON Dosyası", |
||||||
|
"description": "Hesap alımı için düzenle" |
||||||
|
}, |
||||||
|
"keepTrackTokens": { |
||||||
|
"message": "MetaMask hesabınızla satın aldığınız jetonların kaydını tutun." |
||||||
|
}, |
||||||
|
"kovan": { |
||||||
|
"message": "Kovan Test Ağı" |
||||||
|
}, |
||||||
|
"knowledgeDataBase": { |
||||||
|
"message": "Bilgi veritabanımızı ziyaret edin" |
||||||
|
}, |
||||||
|
"max": { |
||||||
|
"message": "Maksimum" |
||||||
|
}, |
||||||
|
"learnMore": { |
||||||
|
"message": "Daha fazla bilgi." |
||||||
|
}, |
||||||
|
"lessThanMax": { |
||||||
|
"message": "$1'den az veya eşit olmalıdır.", |
||||||
|
"description": "Onaltılık sayıyı ondalık olarak girmek için yardımcı" |
||||||
|
}, |
||||||
|
"likeToAddTokens": { |
||||||
|
"message": "Bu jetonlara adres eklemek ister misiniz?" |
||||||
|
}, |
||||||
|
"links": { |
||||||
|
"message": "Bağlantılar" |
||||||
|
}, |
||||||
|
"limit": { |
||||||
|
"message": "Limit" |
||||||
|
}, |
||||||
|
"loading": { |
||||||
|
"message": "Yükleniyor..." |
||||||
|
}, |
||||||
|
"loadingTokens": { |
||||||
|
"message": "Jetonlar yükleniyor..." |
||||||
|
}, |
||||||
|
"localhost": { |
||||||
|
"message": "Localhost 8545" |
||||||
|
}, |
||||||
|
"login": { |
||||||
|
"message": "Giriş yap" |
||||||
|
}, |
||||||
|
"logout": { |
||||||
|
"message": "Çıkış" |
||||||
|
}, |
||||||
|
"loose": { |
||||||
|
"message": "Gevşek" |
||||||
|
}, |
||||||
|
"loweCaseWords": { |
||||||
|
"message": "kaynak kelimeleri sadece küçük harflerden oluşabilir." |
||||||
|
}, |
||||||
|
"mainnet": { |
||||||
|
"message": "Main Ethereum Ağı" |
||||||
|
}, |
||||||
|
"message": { |
||||||
|
"message": "Mesaj" |
||||||
|
}, |
||||||
|
"metamaskDescription": { |
||||||
|
"message": "MetaMask Ethereum için güvenli bir kimlik kasasıdır." |
||||||
|
}, |
||||||
|
"min": { |
||||||
|
"message": "Minimum" |
||||||
|
}, |
||||||
|
"myAccounts": { |
||||||
|
"message": "Hesaplarım" |
||||||
|
}, |
||||||
|
"mustSelectOne": { |
||||||
|
"message": "En az bir jeton seçilmeli" |
||||||
|
}, |
||||||
|
"needEtherInWallet": { |
||||||
|
"message": "MetaMask kullanarak merkezi olamayan uygulamalarla etkileşmek için cüzdanınızda Ether bulunmalıdır." |
||||||
|
}, |
||||||
|
"needImportFile": { |
||||||
|
"message": "Almak için bir dosya seçmelisiniz.", |
||||||
|
"description": "Kullanıcı bir hesap alır ve devam etmek içinbir dosya seçmelidir." |
||||||
|
}, |
||||||
|
"needImportPassword": { |
||||||
|
"message": "Seçilen dosya için bir parola girmelisiniz.", |
||||||
|
"description": "Hesap almak için parola ve dosya gerekiyor." |
||||||
|
}, |
||||||
|
"negativeETH": { |
||||||
|
"message": "Negatif ETH miktarları gönderilemez." |
||||||
|
}, |
||||||
|
"networks": { |
||||||
|
"message": "Ağlar" |
||||||
|
}, |
||||||
|
"newAccount": { |
||||||
|
"message": "Yeni Hesap" |
||||||
|
}, |
||||||
|
"newAccountNumberName": { |
||||||
|
"message": "Hesap $1", |
||||||
|
"description": "Hesap yaratma ekranındaki bir sonraki hesabın varsayılan ismi" |
||||||
|
}, |
||||||
|
"newContract": { |
||||||
|
"message": "Yeni Sözleşme" |
||||||
|
}, |
||||||
|
"newPassword": { |
||||||
|
"message": "Yeni Parola (min 8 karakter)" |
||||||
|
}, |
||||||
|
"newRecipient": { |
||||||
|
"message": "Yeni alıcı" |
||||||
|
}, |
||||||
|
"newRPC": { |
||||||
|
"message": "Yeni RPC URL" |
||||||
|
}, |
||||||
|
"next": { |
||||||
|
"message": "Sonraki" |
||||||
|
}, |
||||||
|
"noAddressForName": { |
||||||
|
"message": "Bu isim için bir adres tanımlanmamış." |
||||||
|
}, |
||||||
|
"noDeposits": { |
||||||
|
"message": "Yatırma alınmadı" |
||||||
|
}, |
||||||
|
"noTransactionHistory": { |
||||||
|
"message": "İşlem geçmişi yok." |
||||||
|
}, |
||||||
|
"noTransactions": { |
||||||
|
"message": "İşlem yok" |
||||||
|
}, |
||||||
|
"notStarted": { |
||||||
|
"message": "Başlamadı" |
||||||
|
}, |
||||||
|
"oldUI": { |
||||||
|
"message": "Eski UI" |
||||||
|
}, |
||||||
|
"oldUIMessage": { |
||||||
|
"message": "Eski UI'a döndünüz. Yeni UI'a üst sağ sekme menüsündeki seçenek ile dönebilirsiniz." |
||||||
|
}, |
||||||
|
"or": { |
||||||
|
"message": "veya", |
||||||
|
"description": "Yeni bir hesap yaratmak veya almak arasındaki seçim" |
||||||
|
}, |
||||||
|
"passwordCorrect": { |
||||||
|
"message": "Lütfen parolanın doğru olduğuna emin olun." |
||||||
|
}, |
||||||
|
"passwordMismatch": { |
||||||
|
"message": "parolalar eşleşmiyor", |
||||||
|
"description": "parola yaratma işleminde, iki yeni parola alanı eşleşmiyor." |
||||||
|
}, |
||||||
|
"passwordShort": { |
||||||
|
"message": "parola yeterince uzun değil", |
||||||
|
"description": "parola yaratma işleminde, parola güvenli olacak kadar uzun değil." |
||||||
|
}, |
||||||
|
"pastePrivateKey": { |
||||||
|
"message": "Özel anahtar dizinizi buraya yapıştırın:", |
||||||
|
"description": "Özel anahtardan hesap almak için" |
||||||
|
}, |
||||||
|
"pasteSeed": { |
||||||
|
"message": "Kaynak ifadenizi buraya yapıştırın!" |
||||||
|
}, |
||||||
|
"personalAddressDetected": { |
||||||
|
"message": "Kişisel adres tespit edilidi. Jeton sözleşme adresini girin." |
||||||
|
}, |
||||||
|
"pleaseReviewTransaction": { |
||||||
|
"message": "Lütfen işleminizi gözden geçirin." |
||||||
|
}, |
||||||
|
"popularTokens": { |
||||||
|
"message": "Popüler Jetonlar" |
||||||
|
}, |
||||||
|
"privacyMsg": { |
||||||
|
"message": "Gizlilik Şartları" |
||||||
|
}, |
||||||
|
"privateKey": { |
||||||
|
"message": "Özel Anahtar", |
||||||
|
"description": "Hesap alırken bu tip bir dosya seçin" |
||||||
|
}, |
||||||
|
"privateKeyWarning": { |
||||||
|
"message": "Uyarı: Bu anahtarı kimse ile paylaşmayın. Özel anahtarlarınıza sahip herkes hesaplarınızıdaki tüm varlığınızı çalabilir." |
||||||
|
}, |
||||||
|
"privateNetwork": { |
||||||
|
"message": "Özel Ağ" |
||||||
|
}, |
||||||
|
"qrCode": { |
||||||
|
"message": "QR Kodunu göster" |
||||||
|
}, |
||||||
|
"readdToken": { |
||||||
|
"message": "Gelecekte Bu jetonu hesap seçenekleri menüsünde “Jeton ekle”'ye giderek geri ekleyebilirsiniz." |
||||||
|
}, |
||||||
|
"readMore": { |
||||||
|
"message": "Burada daha fazla okuyun." |
||||||
|
}, |
||||||
|
"readMore2": { |
||||||
|
"message": "Daha fazla okuyun." |
||||||
|
}, |
||||||
|
"receive": { |
||||||
|
"message": "Al" |
||||||
|
}, |
||||||
|
"recipientAddress": { |
||||||
|
"message": "Alıcı adresi" |
||||||
|
}, |
||||||
|
"refundAddress": { |
||||||
|
"message": "İade adresiniz" |
||||||
|
}, |
||||||
|
"rejected": { |
||||||
|
"message": "Rededildi" |
||||||
|
}, |
||||||
|
"resetAccount": { |
||||||
|
"message": "Hesabı sıfıla" |
||||||
|
}, |
||||||
|
"restoreFromSeed": { |
||||||
|
"message": "Kaynak ifadeden geri getir. Restore from seed phrase" |
||||||
|
}, |
||||||
|
"restoreVault": { |
||||||
|
"message": "Kasayı geri getir" |
||||||
|
}, |
||||||
|
"required": { |
||||||
|
"message": "Gerekli" |
||||||
|
}, |
||||||
|
"retryWithMoreGas": { |
||||||
|
"message": "Daha yüksek bir gas fiyatı ile tekrar dene" |
||||||
|
}, |
||||||
|
"walletSeed": { |
||||||
|
"message": "Cüzdan Kaynağı" |
||||||
|
}, |
||||||
|
"revealSeedWords": { |
||||||
|
"message": "Kaynak kelimelerini göster" |
||||||
|
}, |
||||||
|
"revealSeedWordsWarning": { |
||||||
|
"message": "Açık bir yerde kaynak kelimeliriniz geri getirmeyin! Bu kelimeler tüm hesaplarınızı çalmak için kullanılabilir." |
||||||
|
}, |
||||||
|
"revert": { |
||||||
|
"message": "Geri döndür" |
||||||
|
}, |
||||||
|
"rinkeby": { |
||||||
|
"message": "Rinkeby Test Ağı" |
||||||
|
}, |
||||||
|
"ropsten": { |
||||||
|
"message": "Ropsten Test Ağı" |
||||||
|
}, |
||||||
|
"currentRpc": { |
||||||
|
"message": "Geçerli RPC" |
||||||
|
}, |
||||||
|
"connectingToMainnet": { |
||||||
|
"message": "Main Ethereum Ağına bağlanıyor" |
||||||
|
}, |
||||||
|
"connectingToRopsten": { |
||||||
|
"message": "Ropsten Test Ağına bağlanıyor" |
||||||
|
}, |
||||||
|
"connectingToKovan": { |
||||||
|
"message": "Kovan Test Ağına bağlanıyor" |
||||||
|
}, |
||||||
|
"connectingToRinkeby": { |
||||||
|
"message": "Rinkeby Test Ağına bağlanıyor" |
||||||
|
}, |
||||||
|
"connectingToUnknown": { |
||||||
|
"message": "Bilinmeyen Ağa bağlanıyor" |
||||||
|
}, |
||||||
|
"sampleAccountName": { |
||||||
|
"message": "E.g. Yeni hesabım", |
||||||
|
"description": "Kullanıcının hesabına okunabilir isim ekleme konseptini anlamasına yardımcı olmak." |
||||||
|
}, |
||||||
|
"save": { |
||||||
|
"message": "Kaydet" |
||||||
|
}, |
||||||
|
"reprice_title": { |
||||||
|
"message": "İşlemi Yeniden Fiyatlandır" |
||||||
|
}, |
||||||
|
"reprice_subtitle": { |
||||||
|
"message": "İşlemizi hızlandırmayı denemek için gas fiyatınızı yükseltin." |
||||||
|
}, |
||||||
|
"saveAsFile": { |
||||||
|
"message": "Dosya olarak kaydet", |
||||||
|
"description": "Hesap verme işlemi" |
||||||
|
}, |
||||||
|
"saveSeedAsFile": { |
||||||
|
"message": "Kaynak Kelimelerini Dosya olarak Kaydet" |
||||||
|
}, |
||||||
|
"search": { |
||||||
|
"message": "Ara" |
||||||
|
}, |
||||||
|
"secretPhrase": { |
||||||
|
"message": "Kasanızı geri getirmek için gizli 12 kelimelik ifadenizi giriniz." |
||||||
|
}, |
||||||
|
"newPassword8Chars": { |
||||||
|
"message": "Yeni Parola (minumum 8 karakter)" |
||||||
|
}, |
||||||
|
"seedPhraseReq": { |
||||||
|
"message": "Kaynak ifadeleri 12 kelimedir." |
||||||
|
}, |
||||||
|
"select": { |
||||||
|
"message": "Seç" |
||||||
|
}, |
||||||
|
"selectCurrency": { |
||||||
|
"message": "Döviz Seç" |
||||||
|
}, |
||||||
|
"selectService": { |
||||||
|
"message": "Servis Seç" |
||||||
|
}, |
||||||
|
"selectType": { |
||||||
|
"message": "Tip Seç" |
||||||
|
}, |
||||||
|
"send": { |
||||||
|
"message": "Gönder" |
||||||
|
}, |
||||||
|
"sendETH": { |
||||||
|
"message": "ETH Gönder" |
||||||
|
}, |
||||||
|
"sendTokens": { |
||||||
|
"message": "Jeton Gönder" |
||||||
|
}, |
||||||
|
"onlySendToEtherAddress": { |
||||||
|
"message": "Ethereum adresine sadece ETH gönder." |
||||||
|
}, |
||||||
|
"searchTokens": { |
||||||
|
"message": "Jeton ara" |
||||||
|
}, |
||||||
|
"sendTokensAnywhere": { |
||||||
|
"message": "Ethereum hesabı olan birine Jeton gönder" |
||||||
|
}, |
||||||
|
"settings": { |
||||||
|
"message": "Ayarlar" |
||||||
|
}, |
||||||
|
"info": { |
||||||
|
"message": "Bilgi" |
||||||
|
}, |
||||||
|
"shapeshiftBuy": { |
||||||
|
"message": "Shapeshift ile satın al" |
||||||
|
}, |
||||||
|
"showPrivateKeys": { |
||||||
|
"message": "Özel anahtarları göster" |
||||||
|
}, |
||||||
|
"showQRCode": { |
||||||
|
"message": "QR Kodu göster" |
||||||
|
}, |
||||||
|
"sign": { |
||||||
|
"message": "İmza" |
||||||
|
}, |
||||||
|
"signed": { |
||||||
|
"message": "İmzalandı" |
||||||
|
}, |
||||||
|
"signMessage": { |
||||||
|
"message": "Mesajı İmzala" |
||||||
|
}, |
||||||
|
"signNotice": { |
||||||
|
"message": "Bu mesajı imzalamanın tehlikeli \nyan etkileri olabilir. Tamamen güvendiğiniz sitelerden \ngelen mesajları hesabınızla imzalayınız.\n Bu tehlikeli metod gelecek versiyonlarda çıkarılacaktır. " |
||||||
|
}, |
||||||
|
"sigRequest": { |
||||||
|
"message": "İmza isteği" |
||||||
|
}, |
||||||
|
"sigRequested": { |
||||||
|
"message": "İmza isteniyor" |
||||||
|
}, |
||||||
|
"spaceBetween": { |
||||||
|
"message": "Kelimeler arası sadece bir boşluk olabilir." |
||||||
|
}, |
||||||
|
"status": { |
||||||
|
"message": "Durum" |
||||||
|
}, |
||||||
|
"stateLogs": { |
||||||
|
"message": "Durum Kayıtları" |
||||||
|
}, |
||||||
|
"stateLogsDescription": { |
||||||
|
"message": "Durum kayıtları açık hesap adresinizi ve gönderilen işlemleri içerir." |
||||||
|
}, |
||||||
|
"stateLogError": { |
||||||
|
"message": "Durum kayıtlarını alma hatası" |
||||||
|
}, |
||||||
|
"submit": { |
||||||
|
"message": "Gönder" |
||||||
|
}, |
||||||
|
"submitted": { |
||||||
|
"message": "Gönderildi" |
||||||
|
}, |
||||||
|
"supportCenter": { |
||||||
|
"message": "Destek merkezimizi ziyaret edin" |
||||||
|
}, |
||||||
|
"symbolBetweenZeroTen": { |
||||||
|
"message": "Sembol 0 ve 10 karakter aralığında olmalıdır." |
||||||
|
}, |
||||||
|
"takesTooLong": { |
||||||
|
"message": "Çok mu uzun sürüyor?" |
||||||
|
}, |
||||||
|
"terms": { |
||||||
|
"message": "Kullanım şartları" |
||||||
|
}, |
||||||
|
"testFaucet": { |
||||||
|
"message": "Test Musluğu" |
||||||
|
}, |
||||||
|
"to": { |
||||||
|
"message": "Kime: " |
||||||
|
}, |
||||||
|
"toETHviaShapeShift": { |
||||||
|
"message": "ShapeShift üstünden $1'dan ETH'e", |
||||||
|
"description": "system will fill in deposit type in start of message" |
||||||
|
}, |
||||||
|
"tokenAddress": { |
||||||
|
"message": "Jeton Adresi" |
||||||
|
}, |
||||||
|
"tokenAlreadyAdded": { |
||||||
|
"message": "Jeton çoktan eklenmiş." |
||||||
|
}, |
||||||
|
"tokenBalance": { |
||||||
|
"message": "Jeton bakiyeniz:" |
||||||
|
}, |
||||||
|
"tokenSelection": { |
||||||
|
"message": "Jeton arayın veya popüler jeton listemizden seçin." |
||||||
|
}, |
||||||
|
"tokenSymbol": { |
||||||
|
"message": "Jeton Sembolü" |
||||||
|
}, |
||||||
|
"tokenWarning1": { |
||||||
|
"message": "MetaMask hesabınızla aldığınız jetonların kaydını tutun. Başka bir hesapla jetonlar satın aldıysanız, o jetonlar burada gözükmeyecektir." |
||||||
|
}, |
||||||
|
"total": { |
||||||
|
"message": "Toplam" |
||||||
|
}, |
||||||
|
"transactions": { |
||||||
|
"message": "işlemler" |
||||||
|
}, |
||||||
|
"transactionError": { |
||||||
|
"message": "İşlem Hatası. Sözleşme kodundan kural dışı durum fırlatıldı." |
||||||
|
}, |
||||||
|
"transactionMemo": { |
||||||
|
"message": "İşlem notu (opsiyonel)" |
||||||
|
}, |
||||||
|
"transactionNumber": { |
||||||
|
"message": "İşlem numarası" |
||||||
|
}, |
||||||
|
"transfers": { |
||||||
|
"message": "Transferler" |
||||||
|
}, |
||||||
|
"troubleTokenBalances": { |
||||||
|
"message": "Jeton bakiyelerinizi yüklerken sorun yaşadık. Buradan izleyebilirsiniz ", |
||||||
|
"description": "Jeton bakiyelerini görmek için bir link (burası) ile takip ediliyor" |
||||||
|
}, |
||||||
|
"twelveWords": { |
||||||
|
"message": "MetaMask hesaplarınızı geri getirmenin tek yolu bu 12 kelimedir.\nBu kelimeleri güvenli ve gizli bir yerde saklayın." |
||||||
|
}, |
||||||
|
"typePassword": { |
||||||
|
"message": "Parolanızı girin" |
||||||
|
}, |
||||||
|
"uiWelcome": { |
||||||
|
"message": "Yeni UI (Beta)'ya hoşgeldiniz" |
||||||
|
}, |
||||||
|
"uiWelcomeMessage": { |
||||||
|
"message": "Şu anda yeni Metamask UI kullanmaktasınız. Gözatın, jeton gönderme gibi yeni özellikleri deneyin ve herhangi bir sorunlar karşılaşırsanız bize haber verin" |
||||||
|
}, |
||||||
|
"unapproved": { |
||||||
|
"message": "Onaylanmadı" |
||||||
|
}, |
||||||
|
"unavailable": { |
||||||
|
"message": "Mevcut değil" |
||||||
|
}, |
||||||
|
"unknown": { |
||||||
|
"message": "Bilinmeyen" |
||||||
|
}, |
||||||
|
"unknownNetwork": { |
||||||
|
"message": "Bilinmeyen özel ağ" |
||||||
|
}, |
||||||
|
"unknownNetworkId": { |
||||||
|
"message": "Bilinmeyen ağ IDsi" |
||||||
|
}, |
||||||
|
"uriErrorMsg": { |
||||||
|
"message": "URIler için HTTP/HTTPS öneki gerekmektedir." |
||||||
|
}, |
||||||
|
"usaOnly": { |
||||||
|
"message": "Sadece ABD", |
||||||
|
"description": "Bu dövizi sadece ABD ikamet edenler kullanabilir" |
||||||
|
}, |
||||||
|
"usedByClients": { |
||||||
|
"message": "Farklı istemciler tarafından kullanılmakta" |
||||||
|
}, |
||||||
|
"useOldUI": { |
||||||
|
"message": "Eski UI kullan" |
||||||
|
}, |
||||||
|
"validFileImport": { |
||||||
|
"message": "Almak için geçerli bir dosya seçmelisiniz" |
||||||
|
}, |
||||||
|
"vaultCreated": { |
||||||
|
"message": "Kasa Yaratıldı" |
||||||
|
}, |
||||||
|
"viewAccount": { |
||||||
|
"message": "Hesabı İncele" |
||||||
|
}, |
||||||
|
"visitWebSite": { |
||||||
|
"message": "Web sitemizi ziyaret edin" |
||||||
|
}, |
||||||
|
"warning": { |
||||||
|
"message": "Uyarı" |
||||||
|
}, |
||||||
|
"welcomeBeta": { |
||||||
|
"message": "MetaMask Beta'ya Hoşgeldiniz" |
||||||
|
}, |
||||||
|
"whatsThis": { |
||||||
|
"message": "Bu nedir?" |
||||||
|
}, |
||||||
|
"yourSigRequested": { |
||||||
|
"message": "İmzanız isteniyor" |
||||||
|
}, |
||||||
|
"youSign": { |
||||||
|
"message": "İmzalıyorsunuz" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
const clone = require('clone') |
||||||
|
|
||||||
|
module.exports = getObjStructure |
||||||
|
|
||||||
|
// This will create an object that represents the structure of the given object
|
||||||
|
// it replaces all values with the result of their type
|
||||||
|
|
||||||
|
// {
|
||||||
|
// "data": {
|
||||||
|
// "CurrencyController": {
|
||||||
|
// "conversionDate": "number",
|
||||||
|
// "conversionRate": "number",
|
||||||
|
// "currentCurrency": "string"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
function getObjStructure(obj) { |
||||||
|
const structure = clone(obj) |
||||||
|
return deepMap(structure, (value) => { |
||||||
|
return value === null ? 'null' : typeof value |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
function deepMap(target = {}, visit) { |
||||||
|
Object.entries(target).forEach(([key, value]) => { |
||||||
|
if (typeof value === 'object' && value !== null) { |
||||||
|
target[key] = deepMap(value, visit) |
||||||
|
} else { |
||||||
|
target[key] = visit(value) |
||||||
|
} |
||||||
|
}) |
||||||
|
return target |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
|
||||||
|
const version = 24 |
||||||
|
|
||||||
|
/* |
||||||
|
|
||||||
|
This migration ensures that the from address in txParams is to lower case for |
||||||
|
all unapproved transactions |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
const clone = require('clone') |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
version, |
||||||
|
|
||||||
|
migrate: async function (originalVersionedData) { |
||||||
|
const versionedData = clone(originalVersionedData) |
||||||
|
versionedData.meta.version = version |
||||||
|
const state = versionedData.data |
||||||
|
const newState = transformState(state) |
||||||
|
versionedData.data = newState |
||||||
|
return versionedData |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
function transformState (state) { |
||||||
|
const newState = state |
||||||
|
if (!newState.TransactionController) return newState |
||||||
|
const transactions = newState.TransactionController.transactions |
||||||
|
newState.TransactionController.transactions = transactions.map((txMeta, _, txList) => { |
||||||
|
if ( |
||||||
|
txMeta.status === 'unapproved' && |
||||||
|
txMeta.txParams && |
||||||
|
txMeta.txParams.from |
||||||
|
) { |
||||||
|
txMeta.txParams.from = txMeta.txParams.from.toLowerCase() |
||||||
|
} |
||||||
|
return txMeta |
||||||
|
}) |
||||||
|
return newState |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
// next version number
|
||||||
|
const version = 25 |
||||||
|
|
||||||
|
/* |
||||||
|
|
||||||
|
normalizes txParams on unconfirmed txs |
||||||
|
|
||||||
|
*/ |
||||||
|
const ethUtil = require('ethereumjs-util') |
||||||
|
const clone = require('clone') |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
version, |
||||||
|
|
||||||
|
migrate: async function (originalVersionedData) { |
||||||
|
const versionedData = clone(originalVersionedData) |
||||||
|
versionedData.meta.version = version |
||||||
|
const state = versionedData.data |
||||||
|
const newState = transformState(state) |
||||||
|
versionedData.data = newState |
||||||
|
return versionedData |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
function transformState (state) { |
||||||
|
const newState = state |
||||||
|
|
||||||
|
if (newState.TransactionController) { |
||||||
|
if (newState.TransactionController.transactions) { |
||||||
|
const transactions = newState.TransactionController.transactions |
||||||
|
newState.TransactionController.transactions = transactions.map((txMeta) => { |
||||||
|
if (txMeta.status !== 'unapproved') return txMeta |
||||||
|
txMeta.txParams = normalizeTxParams(txMeta.txParams) |
||||||
|
return txMeta |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return newState |
||||||
|
} |
||||||
|
|
||||||
|
function normalizeTxParams (txParams) { |
||||||
|
// functions that handle normalizing of that key in txParams
|
||||||
|
const whiteList = { |
||||||
|
from: from => ethUtil.addHexPrefix(from).toLowerCase(), |
||||||
|
to: to => ethUtil.addHexPrefix(txParams.to).toLowerCase(), |
||||||
|
nonce: nonce => ethUtil.addHexPrefix(nonce), |
||||||
|
value: value => ethUtil.addHexPrefix(value), |
||||||
|
data: data => ethUtil.addHexPrefix(data), |
||||||
|
gas: gas => ethUtil.addHexPrefix(gas), |
||||||
|
gasPrice: gasPrice => ethUtil.addHexPrefix(gasPrice), |
||||||
|
} |
||||||
|
|
||||||
|
// apply only keys in the whiteList
|
||||||
|
const normalizedTxParams = {} |
||||||
|
Object.keys(whiteList).forEach((key) => { |
||||||
|
if (txParams[key]) normalizedTxParams[key] = whiteList[key](txParams[key]) |
||||||
|
}) |
||||||
|
|
||||||
|
return normalizedTxParams |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
// next version number
|
||||||
|
const version = 0 |
||||||
|
|
||||||
|
/* |
||||||
|
|
||||||
|
description of migration and what it does |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
const clone = require('clone') |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
version, |
||||||
|
|
||||||
|
migrate: async function (originalVersionedData) { |
||||||
|
const versionedData = clone(originalVersionedData) |
||||||
|
versionedData.meta.version = version |
||||||
|
const state = versionedData.data |
||||||
|
const newState = transformState(state) |
||||||
|
versionedData.data = newState |
||||||
|
return versionedData |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
function transformState (state) { |
||||||
|
const newState = state |
||||||
|
// transform state here
|
||||||
|
return newState |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
#!/usr/bin/env node
|
||||||
|
const request = require('request-promise') |
||||||
|
const VERSION = require('../dist/chrome/manifest.json').version |
||||||
|
|
||||||
|
start().catch(console.error) |
||||||
|
|
||||||
|
async function start() { |
||||||
|
|
||||||
|
const GITHUB_COMMENT_TOKEN = process.env.GITHUB_COMMENT_TOKEN |
||||||
|
const CIRCLE_PULL_REQUEST = process.env.CIRCLE_PULL_REQUEST |
||||||
|
console.log('CIRCLE_PULL_REQUEST', CIRCLE_PULL_REQUEST) |
||||||
|
const CIRCLE_SHA1 = process.env.CIRCLE_SHA1 |
||||||
|
console.log('CIRCLE_SHA1', CIRCLE_SHA1) |
||||||
|
const CIRCLE_BUILD_NUM = process.env.CIRCLE_BUILD_NUM |
||||||
|
console.log('CIRCLE_BUILD_NUM', CIRCLE_BUILD_NUM) |
||||||
|
|
||||||
|
if (!CIRCLE_PULL_REQUEST) { |
||||||
|
console.warn(`No pull request detected for commit "${CIRCLE_SHA1}"`) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const CIRCLE_PR_NUMBER = CIRCLE_PULL_REQUEST.split('/').pop() |
||||||
|
const SHORT_SHA1 = CIRCLE_SHA1.slice(0,7) |
||||||
|
const BUILD_LINK_BASE = `https://${CIRCLE_BUILD_NUM}-42009758-gh.circle-artifacts.com/0` |
||||||
|
|
||||||
|
const MASCARA = `${BUILD_LINK_BASE}/builds/mascara/home.html` |
||||||
|
const CHROME = `${BUILD_LINK_BASE}/builds/metamask-chrome-${VERSION}.zip` |
||||||
|
const FIREFOX = `${BUILD_LINK_BASE}/builds/metamask-firefox-${VERSION}.zip` |
||||||
|
const EDGE = `${BUILD_LINK_BASE}/builds/metamask-edge-${VERSION}.zip` |
||||||
|
const OPERA = `${BUILD_LINK_BASE}/builds/metamask-opera-${VERSION}.zip` |
||||||
|
const WALKTHROUGH = `${BUILD_LINK_BASE}/test-artifacts/screens/walkthrough%20%28en%29.gif` |
||||||
|
|
||||||
|
const commentBody = ` |
||||||
|
<details> |
||||||
|
<summary> |
||||||
|
Builds ready [${SHORT_SHA1}]: |
||||||
|
<a href="${MASCARA}">mascara</a>, |
||||||
|
<a href="${CHROME}">chrome</a>, |
||||||
|
<a href="${FIREFOX}">firefox</a>, |
||||||
|
<a href="${EDGE}">edge</a>, |
||||||
|
<a href="${OPERA}">opera</a> |
||||||
|
</summary> |
||||||
|
<image src="${WALKTHROUGH}"> |
||||||
|
</details> |
||||||
|
` |
||||||
|
|
||||||
|
const JSON_PAYLOAD = JSON.stringify({ body: commentBody }) |
||||||
|
const POST_COMMENT_URI = `https://api.github.com/repos/metamask/metamask-extension/issues/${CIRCLE_PR_NUMBER}/comments` |
||||||
|
console.log(`Announcement:\n${commentBody}`) |
||||||
|
console.log(`Posting to: ${POST_COMMENT_URI}`) |
||||||
|
|
||||||
|
await request({ |
||||||
|
method: 'POST', |
||||||
|
uri: POST_COMMENT_URI, |
||||||
|
body: JSON_PAYLOAD, |
||||||
|
headers: { |
||||||
|
'User-Agent': 'metamaskbot', |
||||||
|
'Authorization': `token ${GITHUB_COMMENT_TOKEN}`, |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
#!/usr/bin/env node
|
||||||
|
const pify = require('pify') |
||||||
|
const exec = pify(require('child_process').exec, { multiArgs: true }) |
||||||
|
const VERSION = require('../dist/chrome/manifest.json').version |
||||||
|
|
||||||
|
start().catch(console.error) |
||||||
|
|
||||||
|
async function start(){ |
||||||
|
const authWorked = await checkIfAuthWorks() |
||||||
|
if (!authWorked) { |
||||||
|
console.log(`Sentry auth failed...`) |
||||||
|
} |
||||||
|
// check if version exists or not
|
||||||
|
const versionAlreadyExists = await checkIfVersionExists() |
||||||
|
// abort if versions exists
|
||||||
|
if (versionAlreadyExists) { |
||||||
|
console.log(`Version "${VERSION}" already exists on Sentry, aborting sourcemap upload.`) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// 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!') |
||||||
|
} |
||||||
|
|
||||||
|
async function checkIfAuthWorks() { |
||||||
|
const itWorked = await doesNotFail(async () => { |
||||||
|
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' list`) |
||||||
|
}) |
||||||
|
return itWorked |
||||||
|
} |
||||||
|
|
||||||
|
async function checkIfVersionExists() { |
||||||
|
const versionAlreadyExists = await doesNotFail(async () => { |
||||||
|
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' info ${VERSION}`) |
||||||
|
}) |
||||||
|
return versionAlreadyExists |
||||||
|
} |
||||||
|
|
||||||
|
async function doesNotFail(asyncFn) { |
||||||
|
try { |
||||||
|
await asyncFn() |
||||||
|
return true |
||||||
|
} catch (err) { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
# QA Guide |
||||||
|
|
||||||
|
Steps to mark a full pass of QA complete. |
||||||
|
* Browsers: Opera, Chrome, Firefox, Edge. |
||||||
|
* OS: Ubuntu, Mac OSX, Windows |
||||||
|
* Load older version of MetaMask and attempt to simulate updating the extension. |
||||||
|
* Open Developer Console in background and popup, inspect errors. |
||||||
|
* Watch the state logs |
||||||
|
* Transactions (unapproved txs -> rejected/submitted -> confirmed) |
||||||
|
* Nonces/LocalNonces |
||||||
|
* Vault integrity |
||||||
|
* create vault |
||||||
|
* Log out |
||||||
|
* Log in again |
||||||
|
* Log out |
||||||
|
* Restore from seed |
||||||
|
* Create a second account |
||||||
|
* Import a loose account (not related to HD Wallet) |
||||||
|
* Import old existing vault seed phrase (pref with test Ether) |
||||||
|
* Download State Logs, Priv key file, seed phrase file. |
||||||
|
* Send Ether |
||||||
|
* by address |
||||||
|
* by ens name |
||||||
|
* Web3 API Stability |
||||||
|
* Create a contract from a Ðapp (remix) |
||||||
|
* Load a Ðapp that reads using events/logs (ENS) |
||||||
|
* Connect to MEW/MyCypto |
||||||
|
* Send a transaction from any Ðapp |
||||||
|
- MEW |
||||||
|
- EtherDelta |
||||||
|
- Leeroy |
||||||
|
- Aragon |
||||||
|
- (https://tmashuang.github.io/demo-dapp) |
||||||
|
* Check account balances |
||||||
|
* Token Management |
||||||
|
* create a token with tokenfactory (http://tokenfactory.surge.sh/#/factory) |
||||||
|
* Add that token to the token view |
||||||
|
* Send that token to another metamask address. |
||||||
|
* confirm the token arrived. |
||||||
|
* Send a transaction and sign a message (https://danfinlay.github.io/js-eth-personal-sign-examples/) for each keyring type |
||||||
|
* hd keyring |
||||||
|
* imported keyring |
||||||
|
* Change network from mainnet → ropsten → rinkeby → localhost (ganache) |
||||||
|
* Ganache set blocktime to simulate retryTx in MetaMask |
||||||
|
* Copy public key to clipboard |
||||||
|
* Export private key |
||||||
|
|
||||||
|
* Explore changes in master, target features that have been changed and break. |
@ -0,0 +1,151 @@ |
|||||||
|
import React, { Component } from 'react' |
||||||
|
import PropTypes from 'prop-types' |
||||||
|
import { connect } from 'react-redux' |
||||||
|
import { withRouter } from 'react-router-dom' |
||||||
|
import classnames from 'classnames' |
||||||
|
import shuffle from 'lodash.shuffle' |
||||||
|
import { compose } from 'recompose' |
||||||
|
import Identicon from '../../../../ui/app/components/identicon' |
||||||
|
import { confirmSeedWords, showModal } from '../../../../ui/app/actions' |
||||||
|
import Breadcrumbs from './breadcrumbs' |
||||||
|
import LoadingScreen from './loading-screen' |
||||||
|
import { DEFAULT_ROUTE } from '../../../../ui/app/routes' |
||||||
|
|
||||||
|
class ConfirmSeedScreen extends Component { |
||||||
|
static propTypes = { |
||||||
|
isLoading: PropTypes.bool, |
||||||
|
address: PropTypes.string, |
||||||
|
seedWords: PropTypes.string, |
||||||
|
confirmSeedWords: PropTypes.func, |
||||||
|
history: PropTypes.object, |
||||||
|
openBuyEtherModal: PropTypes.func, |
||||||
|
}; |
||||||
|
|
||||||
|
static defaultProps = { |
||||||
|
seedWords: '', |
||||||
|
} |
||||||
|
|
||||||
|
constructor (props) { |
||||||
|
super(props) |
||||||
|
const { seedWords } = props |
||||||
|
this.state = { |
||||||
|
selectedSeeds: [], |
||||||
|
shuffledSeeds: seedWords && shuffle(seedWords.split(' ')) || [], |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
componentWillMount () { |
||||||
|
const { seedWords, history } = this.props |
||||||
|
|
||||||
|
if (!seedWords) { |
||||||
|
history.push(DEFAULT_ROUTE) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
handleClick () { |
||||||
|
const { confirmSeedWords, history, openBuyEtherModal } = this.props |
||||||
|
|
||||||
|
confirmSeedWords() |
||||||
|
.then(() => { |
||||||
|
history.push(DEFAULT_ROUTE) |
||||||
|
openBuyEtherModal() |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
const { seedWords } = this.props |
||||||
|
const { selectedSeeds, shuffledSeeds } = this.state |
||||||
|
const isValid = seedWords === selectedSeeds.map(([_, seed]) => seed).join(' ') |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="first-time-flow"> |
||||||
|
{ |
||||||
|
this.props.isLoading |
||||||
|
? <LoadingScreen loadingMessage="Creating your new account" /> |
||||||
|
: ( |
||||||
|
<div className="first-view-main-wrapper"> |
||||||
|
<div className="first-view-main"> |
||||||
|
<div className="backup-phrase"> |
||||||
|
<Identicon address={this.props.address} diameter={70} /> |
||||||
|
<div className="backup-phrase__content-wrapper"> |
||||||
|
<div> |
||||||
|
<div className="backup-phrase__title"> |
||||||
|
Confirm your Secret Backup Phrase |
||||||
|
</div> |
||||||
|
<div className="backup-phrase__body-text"> |
||||||
|
Please select each phrase in order to make sure it is correct. |
||||||
|
</div> |
||||||
|
<div className="backup-phrase__confirm-secret"> |
||||||
|
{selectedSeeds.map(([_, word], i) => ( |
||||||
|
<button |
||||||
|
key={i} |
||||||
|
className="backup-phrase__confirm-seed-option" |
||||||
|
> |
||||||
|
{word} |
||||||
|
</button> |
||||||
|
))} |
||||||
|
</div> |
||||||
|
<div className="backup-phrase__confirm-seed-options"> |
||||||
|
{shuffledSeeds.map((word, i) => { |
||||||
|
const isSelected = selectedSeeds |
||||||
|
.filter(([index, seed]) => seed === word && index === i) |
||||||
|
.length |
||||||
|
|
||||||
|
return ( |
||||||
|
<button |
||||||
|
key={i} |
||||||
|
className={classnames('backup-phrase__confirm-seed-option', { |
||||||
|
'backup-phrase__confirm-seed-option--selected': isSelected, |
||||||
|
})} |
||||||
|
onClick={() => { |
||||||
|
if (!isSelected) { |
||||||
|
this.setState({ |
||||||
|
selectedSeeds: [...selectedSeeds, [i, word]], |
||||||
|
}) |
||||||
|
} else { |
||||||
|
this.setState({ |
||||||
|
selectedSeeds: selectedSeeds |
||||||
|
.filter(([index, seed]) => !(seed === word && index === i)), |
||||||
|
}) |
||||||
|
} |
||||||
|
}} |
||||||
|
> |
||||||
|
{word} |
||||||
|
</button> |
||||||
|
) |
||||||
|
})} |
||||||
|
</div> |
||||||
|
<button |
||||||
|
className="first-time-flow__button" |
||||||
|
onClick={() => isValid && this.handleClick()} |
||||||
|
disabled={!isValid} |
||||||
|
> |
||||||
|
Confirm |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<Breadcrumbs total={3} currentIndex={1} /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default compose( |
||||||
|
withRouter, |
||||||
|
connect( |
||||||
|
({ metamask: { selectedAddress, seedWords }, appState: { isLoading } }) => ({ |
||||||
|
seedWords, |
||||||
|
isLoading, |
||||||
|
address: selectedAddress, |
||||||
|
}), |
||||||
|
dispatch => ({ |
||||||
|
confirmSeedWords: () => dispatch(confirmSeedWords()), |
||||||
|
openBuyEtherModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER'})), |
||||||
|
}) |
||||||
|
) |
||||||
|
)(ConfirmSeedScreen) |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,18 @@ |
|||||||
|
require('chromedriver') |
||||||
|
const webdriver = require('selenium-webdriver') |
||||||
|
|
||||||
|
exports.delay = function delay (time) { |
||||||
|
return new Promise(resolve => setTimeout(resolve, time)) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
exports.buildWebDriver = function buildWebDriver (extPath) { |
||||||
|
return new webdriver.Builder() |
||||||
|
.withCapabilities({ |
||||||
|
chromeOptions: { |
||||||
|
args: [`load-extension=${extPath}`], |
||||||
|
}, |
||||||
|
}) |
||||||
|
.forBrowser('chrome') |
||||||
|
.build() |
||||||
|
} |
@ -0,0 +1,230 @@ |
|||||||
|
const path = require('path') |
||||||
|
const fs = require('fs') |
||||||
|
const pify = require('pify') |
||||||
|
const mkdirp = require('mkdirp') |
||||||
|
const rimraf = require('rimraf') |
||||||
|
const webdriver = require('selenium-webdriver') |
||||||
|
const endOfStream = require('end-of-stream') |
||||||
|
const GIFEncoder = require('gifencoder') |
||||||
|
const pngFileStream = require('png-file-stream') |
||||||
|
const sizeOfPng = require('image-size/lib/types/png') |
||||||
|
const By = webdriver.By |
||||||
|
const { delay, buildWebDriver } = require('./func') |
||||||
|
const localesIndex = require('../../app/_locales/index.json') |
||||||
|
|
||||||
|
let driver |
||||||
|
|
||||||
|
captureAllScreens().catch((err) => { |
||||||
|
try { |
||||||
|
console.error(err) |
||||||
|
verboseReportOnFailure() |
||||||
|
driver.quit() |
||||||
|
} catch (err) { |
||||||
|
console.error(err) |
||||||
|
} |
||||||
|
process.exit(1) |
||||||
|
}) |
||||||
|
|
||||||
|
async function captureAllScreens() { |
||||||
|
let screenshotCount = 0 |
||||||
|
|
||||||
|
// common names
|
||||||
|
let button |
||||||
|
let tabs |
||||||
|
let element |
||||||
|
|
||||||
|
await cleanScreenShotDir() |
||||||
|
|
||||||
|
// setup selenium and install extension
|
||||||
|
const extPath = path.resolve('dist/chrome') |
||||||
|
driver = buildWebDriver(extPath) |
||||||
|
await driver.get('chrome://extensions-frame') |
||||||
|
const elems = await driver.findElements(By.css('.extension-list-item-wrapper')) |
||||||
|
const extensionId = await elems[1].getAttribute('id') |
||||||
|
await driver.get(`chrome-extension://${extensionId}/home.html`) |
||||||
|
await delay(500) |
||||||
|
tabs = await driver.getAllWindowHandles() |
||||||
|
await driver.switchTo().window(tabs[0]) |
||||||
|
await delay(1000) |
||||||
|
await setProviderType('localhost') |
||||||
|
await delay(300) |
||||||
|
|
||||||
|
// click try new ui
|
||||||
|
await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div.flex-row.flex-center.flex-grow > p')).click() |
||||||
|
await delay(300) |
||||||
|
|
||||||
|
// close metamask homepage and extra home.html
|
||||||
|
tabs = await driver.getAllWindowHandles() |
||||||
|
// metamask homepage is opened on prod, not dev
|
||||||
|
if (tabs.length > 2) { |
||||||
|
await driver.switchTo().window(tabs[2]) |
||||||
|
driver.close() |
||||||
|
} |
||||||
|
await driver.switchTo().window(tabs[1]) |
||||||
|
driver.close() |
||||||
|
await driver.switchTo().window(tabs[0]) |
||||||
|
await delay(300) |
||||||
|
await captureLanguageScreenShots('welcome-new-ui') |
||||||
|
|
||||||
|
// setup account
|
||||||
|
await delay(1000) |
||||||
|
await driver.findElement(By.css('body')).click() |
||||||
|
await delay(300) |
||||||
|
await captureLanguageScreenShots('welcome') |
||||||
|
|
||||||
|
await driver.findElement(By.css('button')).click() |
||||||
|
await captureLanguageScreenShots('create password') |
||||||
|
|
||||||
|
const passwordBox = await driver.findElement(By.css('input[type=password]:nth-of-type(1)')) |
||||||
|
const passwordBoxConfirm = await driver.findElement(By.css('input[type=password]:nth-of-type(2)')) |
||||||
|
passwordBox.sendKeys('123456789') |
||||||
|
passwordBoxConfirm.sendKeys('123456789') |
||||||
|
await delay(500) |
||||||
|
await captureLanguageScreenShots('choose-password-filled') |
||||||
|
|
||||||
|
await driver.findElement(By.css('button')).click() |
||||||
|
await delay(500) |
||||||
|
await captureLanguageScreenShots('unique account image') |
||||||
|
|
||||||
|
await driver.findElement(By.css('button')).click() |
||||||
|
await delay(500) |
||||||
|
await captureLanguageScreenShots('privacy note') |
||||||
|
|
||||||
|
await driver.findElement(By.css('button')).click() |
||||||
|
await delay(300) |
||||||
|
await captureLanguageScreenShots('terms') |
||||||
|
|
||||||
|
await delay(300) |
||||||
|
element = driver.findElement(By.linkText('Attributions')) |
||||||
|
await driver.executeScript('arguments[0].scrollIntoView(true)', element) |
||||||
|
await delay(300) |
||||||
|
await captureLanguageScreenShots('terms-scrolled') |
||||||
|
|
||||||
|
await driver.findElement(By.css('button')).click() |
||||||
|
await delay(300) |
||||||
|
await captureLanguageScreenShots('secret backup phrase') |
||||||
|
|
||||||
|
await driver.findElement(By.css('button')).click() |
||||||
|
await delay(300) |
||||||
|
await captureLanguageScreenShots('secret backup phrase') |
||||||
|
|
||||||
|
await driver.findElement(By.css('.backup-phrase__reveal-button')).click() |
||||||
|
await delay(300) |
||||||
|
await captureLanguageScreenShots('secret backup phrase - reveal') |
||||||
|
|
||||||
|
await driver.findElement(By.css('button')).click() |
||||||
|
await delay(300) |
||||||
|
await captureLanguageScreenShots('confirm secret backup phrase') |
||||||
|
|
||||||
|
// finish up
|
||||||
|
console.log('building gif...') |
||||||
|
await generateGif() |
||||||
|
await driver.quit() |
||||||
|
return |
||||||
|
|
||||||
|
//
|
||||||
|
// await button.click()
|
||||||
|
// await delay(700)
|
||||||
|
// this.seedPhase = await driver.findElement(By.css('.twelve-word-phrase')).getText()
|
||||||
|
// await captureScreenShot('seed phrase')
|
||||||
|
//
|
||||||
|
// const continueAfterSeedPhrase = await driver.findElement(By.css('button'))
|
||||||
|
// await continueAfterSeedPhrase.click()
|
||||||
|
// await delay(300)
|
||||||
|
// await captureScreenShot('main screen')
|
||||||
|
//
|
||||||
|
// await driver.findElement(By.css('.sandwich-expando')).click()
|
||||||
|
// await delay(500)
|
||||||
|
// await captureScreenShot('menu')
|
||||||
|
|
||||||
|
// await driver.findElement(By.css('#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)')).click()
|
||||||
|
// await captureScreenShot('main screen')
|
||||||
|
// it('should accept account password after lock', async () => {
|
||||||
|
// await delay(500)
|
||||||
|
// await driver.findElement(By.id('password-box')).sendKeys('123456789')
|
||||||
|
// await driver.findElement(By.css('button')).click()
|
||||||
|
// await delay(500)
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// it('should show QR code option', async () => {
|
||||||
|
// await delay(300)
|
||||||
|
// await driver.findElement(By.css('.fa-ellipsis-h')).click()
|
||||||
|
// await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div:nth-child(1) > flex-column > div.name-label > div > span > i > div > div > li:nth-child(3)')).click()
|
||||||
|
// await delay(300)
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// it('should show the account address', async () => {
|
||||||
|
// this.accountAddress = await driver.findElement(By.css('.ellip-address')).getText()
|
||||||
|
// await driver.findElement(By.css('.fa-arrow-left')).click()
|
||||||
|
// await delay(500)
|
||||||
|
// })
|
||||||
|
|
||||||
|
async function captureLanguageScreenShots(label) { |
||||||
|
const nonEnglishLocales = localesIndex.filter(localeMeta => localeMeta.code !== 'en') |
||||||
|
// take english shot
|
||||||
|
await captureScreenShot(`${label} (en)`) |
||||||
|
for (let localeMeta of nonEnglishLocales) { |
||||||
|
// set locale and take shot
|
||||||
|
await setLocale(localeMeta.code) |
||||||
|
await delay(300) |
||||||
|
await captureScreenShot(`${label} (${localeMeta.code})`) |
||||||
|
} |
||||||
|
// return locale to english
|
||||||
|
await setLocale('en') |
||||||
|
await delay(300) |
||||||
|
} |
||||||
|
|
||||||
|
async function setLocale(code) { |
||||||
|
await driver.executeScript('window.metamask.updateCurrentLocale(arguments[0])', code) |
||||||
|
} |
||||||
|
|
||||||
|
async function setProviderType(type) { |
||||||
|
await driver.executeScript('window.metamask.setProviderType(arguments[0])', type) |
||||||
|
} |
||||||
|
|
||||||
|
// cleanup
|
||||||
|
await driver.quit() |
||||||
|
|
||||||
|
async function cleanScreenShotDir() { |
||||||
|
await pify(rimraf)(`./test-artifacts/screens/`) |
||||||
|
} |
||||||
|
|
||||||
|
async function captureScreenShot(label) { |
||||||
|
const shotIndex = screenshotCount.toString().padStart(4, '0') |
||||||
|
screenshotCount++ |
||||||
|
const artifactDir = `./test-artifacts/screens/` |
||||||
|
await pify(mkdirp)(artifactDir) |
||||||
|
// capture screenshot
|
||||||
|
const screenshot = await driver.takeScreenshot() |
||||||
|
await pify(fs.writeFile)(`${artifactDir}/${shotIndex} - ${label}.png`, screenshot, { encoding: 'base64' }) |
||||||
|
} |
||||||
|
|
||||||
|
async function generateGif(){ |
||||||
|
// calculate screenshot size
|
||||||
|
const screenshot = await driver.takeScreenshot() |
||||||
|
const pngBuffer = Buffer.from(screenshot, 'base64') |
||||||
|
const size = sizeOfPng.calculate(pngBuffer) |
||||||
|
|
||||||
|
// read only the english pngs into gif
|
||||||
|
const encoder = new GIFEncoder(size.width, size.height) |
||||||
|
const stream = pngFileStream('./test-artifacts/screens/* (en).png') |
||||||
|
.pipe(encoder.createWriteStream({ repeat: 0, delay: 1000, quality: 10 })) |
||||||
|
.pipe(fs.createWriteStream('./test-artifacts/screens/walkthrough (en).gif')) |
||||||
|
|
||||||
|
// wait for end
|
||||||
|
await pify(endOfStream)(stream) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
async function verboseReportOnFailure(test) { |
||||||
|
const artifactDir = `./test-artifacts/${test.title}` |
||||||
|
const filepathBase = `${artifactDir}/test-failure` |
||||||
|
await pify(mkdirp)(artifactDir) |
||||||
|
// capture screenshot
|
||||||
|
const screenshot = await driver.takeScreenshot() |
||||||
|
await pify(fs.writeFile)(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' }) |
||||||
|
// capture dom source
|
||||||
|
const htmlSource = await driver.getPageSource() |
||||||
|
await pify(fs.writeFile)(`${filepathBase}-dom.html`, htmlSource) |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
const assert = require('assert') |
||||||
|
const migration24 = require('../../../app/scripts/migrations/024') |
||||||
|
const firstTimeState = { |
||||||
|
meta: {}, |
||||||
|
data: require('../../../app/scripts/first-time-state'), |
||||||
|
} |
||||||
|
const properTime = (new Date()).getTime() |
||||||
|
const storage = { |
||||||
|
"meta": {}, |
||||||
|
"data": { |
||||||
|
"TransactionController": { |
||||||
|
"transactions": [ |
||||||
|
] |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
const transactions = [] |
||||||
|
|
||||||
|
|
||||||
|
while (transactions.length <= 10) { |
||||||
|
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675' }, status: 'unapproved' }) |
||||||
|
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675' }, status: 'confirmed' }) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
storage.data.TransactionController.transactions = transactions |
||||||
|
|
||||||
|
describe('storage is migrated successfully and the txParams.from are lowercase', () => { |
||||||
|
it('should lowercase the from for unapproved txs', (done) => { |
||||||
|
migration24.migrate(storage) |
||||||
|
.then((migratedData) => { |
||||||
|
const migratedTransactions = migratedData.data.TransactionController.transactions |
||||||
|
migratedTransactions.forEach((tx) => { |
||||||
|
if (tx.status === 'unapproved') assert.equal(tx.txParams.from, '0x8acce2391c0d510a6c5e5d8f819a678f79b7e675') |
||||||
|
else assert.equal(tx.txParams.from, '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675') |
||||||
|
}) |
||||||
|
done() |
||||||
|
}).catch(done) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should migrate first time state', (done) => { |
||||||
|
migration24.migrate(firstTimeState) |
||||||
|
.then((migratedData) => { |
||||||
|
assert.equal(migratedData.meta.version, 24) |
||||||
|
done() |
||||||
|
}).catch(done) |
||||||
|
}) |
||||||
|
}) |
@ -0,0 +1,49 @@ |
|||||||
|
const assert = require('assert') |
||||||
|
const migration25 = require('../../../app/scripts/migrations/025') |
||||||
|
const firstTimeState = { |
||||||
|
meta: {}, |
||||||
|
data: require('../../../app/scripts/first-time-state'), |
||||||
|
} |
||||||
|
|
||||||
|
const storage = { |
||||||
|
"meta": {}, |
||||||
|
"data": { |
||||||
|
"TransactionController": { |
||||||
|
"transactions": [ |
||||||
|
] |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
const transactions = [] |
||||||
|
|
||||||
|
|
||||||
|
while (transactions.length <= 10) { |
||||||
|
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675', random: 'stuff', chainId: 2 }, status: 'unapproved' }) |
||||||
|
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675' }, status: 'confirmed' }) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
storage.data.TransactionController.transactions = transactions |
||||||
|
|
||||||
|
describe('storage is migrated successfully and the txParams.from are lowercase', () => { |
||||||
|
it('should lowercase the from for unapproved txs', (done) => { |
||||||
|
migration25.migrate(storage) |
||||||
|
.then((migratedData) => { |
||||||
|
const migratedTransactions = migratedData.data.TransactionController.transactions |
||||||
|
migratedTransactions.forEach((tx) => { |
||||||
|
if (tx.status === 'unapproved') assert(!tx.txParams.random) |
||||||
|
if (tx.status === 'unapproved') assert(!tx.txParams.chainId) |
||||||
|
}) |
||||||
|
done() |
||||||
|
}).catch(done) |
||||||
|
}) |
||||||
|
|
||||||
|
it('should migrate first time state', (done) => { |
||||||
|
migration25.migrate(firstTimeState) |
||||||
|
.then((migratedData) => { |
||||||
|
assert.equal(migratedData.meta.version, 25) |
||||||
|
done() |
||||||
|
}).catch(done) |
||||||
|
}) |
||||||
|
}) |
@ -0,0 +1,17 @@ |
|||||||
|
const assert = require('assert') |
||||||
|
const migrationTemplate = require('../../../app/scripts/migrations/template') |
||||||
|
const properTime = (new Date()).getTime() |
||||||
|
const storage = { |
||||||
|
meta: {}, |
||||||
|
data: {}, |
||||||
|
} |
||||||
|
|
||||||
|
describe('storage is migrated successfully', () => { |
||||||
|
it('should work', (done) => { |
||||||
|
migrationTemplate.migrate(storage) |
||||||
|
.then((migratedData) => { |
||||||
|
assert.equal(migratedData.meta.version, 0) |
||||||
|
done() |
||||||
|
}).catch(done) |
||||||
|
}) |
||||||
|
}) |
@ -0,0 +1,34 @@ |
|||||||
|
const { connect } = require('react-redux') |
||||||
|
const PropTypes = require('prop-types') |
||||||
|
const { Redirect } = require('react-router-dom') |
||||||
|
const h = require('react-hyperscript') |
||||||
|
const MetamaskRoute = require('./metamask-route') |
||||||
|
const { UNLOCK_ROUTE, INITIALIZE_ROUTE } = require('../../routes') |
||||||
|
|
||||||
|
const Authenticated = props => { |
||||||
|
const { isUnlocked, isInitialized } = props |
||||||
|
|
||||||
|
switch (true) { |
||||||
|
case isUnlocked && isInitialized: |
||||||
|
return h(MetamaskRoute, { ...props }) |
||||||
|
case !isInitialized: |
||||||
|
return h(Redirect, { to: { pathname: INITIALIZE_ROUTE } }) |
||||||
|
default: |
||||||
|
return h(Redirect, { to: { pathname: UNLOCK_ROUTE } }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Authenticated.propTypes = { |
||||||
|
isUnlocked: PropTypes.bool, |
||||||
|
isInitialized: PropTypes.bool, |
||||||
|
} |
||||||
|
|
||||||
|
const mapStateToProps = state => { |
||||||
|
const { metamask: { isUnlocked, isInitialized } } = state |
||||||
|
return { |
||||||
|
isUnlocked, |
||||||
|
isInitialized, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(Authenticated) |
@ -0,0 +1,81 @@ |
|||||||
|
const Component = require('react').Component |
||||||
|
const { Switch, Route, matchPath } = require('react-router-dom') |
||||||
|
const PropTypes = require('prop-types') |
||||||
|
const h = require('react-hyperscript') |
||||||
|
const connect = require('react-redux').connect |
||||||
|
const actions = require('../../../actions') |
||||||
|
const { getCurrentViewContext } = require('../../../selectors') |
||||||
|
const classnames = require('classnames') |
||||||
|
const NewAccountCreateForm = require('./new-account') |
||||||
|
const NewAccountImportForm = require('./import-account') |
||||||
|
const { NEW_ACCOUNT_ROUTE, IMPORT_ACCOUNT_ROUTE } = require('../../../routes') |
||||||
|
|
||||||
|
class CreateAccountPage extends Component { |
||||||
|
renderTabs () { |
||||||
|
const { history, location } = this.props |
||||||
|
|
||||||
|
return h('div.new-account__tabs', [ |
||||||
|
h('div.new-account__tabs__tab', { |
||||||
|
className: classnames('new-account__tabs__tab', { |
||||||
|
'new-account__tabs__selected': matchPath(location.pathname, { |
||||||
|
path: NEW_ACCOUNT_ROUTE, exact: true, |
||||||
|
}), |
||||||
|
}), |
||||||
|
onClick: () => history.push(NEW_ACCOUNT_ROUTE), |
||||||
|
}, 'Create'), |
||||||
|
|
||||||
|
h('div.new-account__tabs__tab', { |
||||||
|
className: classnames('new-account__tabs__tab', { |
||||||
|
'new-account__tabs__selected': matchPath(location.pathname, { |
||||||
|
path: IMPORT_ACCOUNT_ROUTE, exact: true, |
||||||
|
}), |
||||||
|
}), |
||||||
|
onClick: () => history.push(IMPORT_ACCOUNT_ROUTE), |
||||||
|
}, 'Import'), |
||||||
|
]) |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
return h('div.new-account', {}, [ |
||||||
|
h('div.new-account__header', [ |
||||||
|
h('div.new-account__title', 'New Account'), |
||||||
|
this.renderTabs(), |
||||||
|
]), |
||||||
|
h('div.new-account__form', [ |
||||||
|
h(Switch, [ |
||||||
|
h(Route, { |
||||||
|
exact: true, |
||||||
|
path: NEW_ACCOUNT_ROUTE, |
||||||
|
component: NewAccountCreateForm, |
||||||
|
}), |
||||||
|
h(Route, { |
||||||
|
exact: true, |
||||||
|
path: IMPORT_ACCOUNT_ROUTE, |
||||||
|
component: NewAccountImportForm, |
||||||
|
}), |
||||||
|
]), |
||||||
|
]), |
||||||
|
]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
CreateAccountPage.propTypes = { |
||||||
|
location: PropTypes.object, |
||||||
|
history: PropTypes.object, |
||||||
|
} |
||||||
|
|
||||||
|
const mapStateToProps = state => ({ |
||||||
|
displayedForm: getCurrentViewContext(state), |
||||||
|
}) |
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({ |
||||||
|
displayForm: form => dispatch(actions.setNewAccountForm(form)), |
||||||
|
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)), |
||||||
|
showExportPrivateKeyModal: () => { |
||||||
|
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' })) |
||||||
|
}, |
||||||
|
hideModal: () => dispatch(actions.hideModal()), |
||||||
|
saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)), |
||||||
|
}) |
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(CreateAccountPage) |
@ -0,0 +1,332 @@ |
|||||||
|
const { Component } = require('react') |
||||||
|
const PropTypes = require('prop-types') |
||||||
|
const connect = require('../../metamask-connect') |
||||||
|
const { Redirect, withRouter } = require('react-router-dom') |
||||||
|
const { compose } = require('recompose') |
||||||
|
const h = require('react-hyperscript') |
||||||
|
const actions = require('../../actions') |
||||||
|
|
||||||
|
// init
|
||||||
|
const NewKeyChainScreen = require('../../new-keychain') |
||||||
|
// mascara
|
||||||
|
const MascaraBuyEtherScreen = require('../../../../mascara/src/app/first-time/buy-ether-screen').default |
||||||
|
|
||||||
|
// accounts
|
||||||
|
const MainContainer = require('../../main-container') |
||||||
|
|
||||||
|
// other views
|
||||||
|
const BuyView = require('../../components/buy-button-subview') |
||||||
|
const QrView = require('../../components/qr-code') |
||||||
|
|
||||||
|
// Routes
|
||||||
|
const { |
||||||
|
REVEAL_SEED_ROUTE, |
||||||
|
RESTORE_VAULT_ROUTE, |
||||||
|
CONFIRM_TRANSACTION_ROUTE, |
||||||
|
NOTICE_ROUTE, |
||||||
|
} = require('../../routes') |
||||||
|
|
||||||
|
class Home extends Component { |
||||||
|
componentDidMount () { |
||||||
|
const { |
||||||
|
history, |
||||||
|
unapprovedTxs = {}, |
||||||
|
unapprovedMsgCount = 0, |
||||||
|
unapprovedPersonalMsgCount = 0, |
||||||
|
unapprovedTypedMessagesCount = 0, |
||||||
|
} = this.props |
||||||
|
|
||||||
|
// unapprovedTxs and unapproved messages
|
||||||
|
if (Object.keys(unapprovedTxs).length || |
||||||
|
unapprovedTypedMessagesCount + unapprovedMsgCount + unapprovedPersonalMsgCount > 0) { |
||||||
|
history.push(CONFIRM_TRANSACTION_ROUTE) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
log.debug('rendering primary') |
||||||
|
const { |
||||||
|
noActiveNotices, |
||||||
|
lostAccounts, |
||||||
|
forgottenPassword, |
||||||
|
currentView, |
||||||
|
activeAddress, |
||||||
|
seedWords, |
||||||
|
} = this.props |
||||||
|
|
||||||
|
// notices
|
||||||
|
if (!noActiveNotices || (lostAccounts && lostAccounts.length > 0)) { |
||||||
|
return h(Redirect, { |
||||||
|
to: { |
||||||
|
pathname: NOTICE_ROUTE, |
||||||
|
}, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// seed words
|
||||||
|
if (seedWords) { |
||||||
|
log.debug('rendering seed words') |
||||||
|
return h(Redirect, { |
||||||
|
to: { |
||||||
|
pathname: REVEAL_SEED_ROUTE, |
||||||
|
}, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
if (forgottenPassword) { |
||||||
|
log.debug('rendering restore vault screen') |
||||||
|
return h(Redirect, { |
||||||
|
to: { |
||||||
|
pathname: RESTORE_VAULT_ROUTE, |
||||||
|
}, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// if (!props.noActiveNotices) {
|
||||||
|
// log.debug('rendering notice screen for unread notices.')
|
||||||
|
// return h(NoticeScreen, {
|
||||||
|
// notice: props.lastUnreadNotice,
|
||||||
|
// key: 'NoticeScreen',
|
||||||
|
// onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
|
||||||
|
// })
|
||||||
|
// } else if (props.lostAccounts && props.lostAccounts.length > 0) {
|
||||||
|
// log.debug('rendering notice screen for lost accounts view.')
|
||||||
|
// return h(NoticeScreen, {
|
||||||
|
// notice: generateLostAccountsNotice(props.lostAccounts),
|
||||||
|
// key: 'LostAccountsNotice',
|
||||||
|
// onConfirm: () => props.dispatch(actions.markAccountsFound()),
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (props.seedWords) {
|
||||||
|
// log.debug('rendering seed words')
|
||||||
|
// return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
|
||||||
|
// }
|
||||||
|
|
||||||
|
// show initialize screen
|
||||||
|
// if (!isInitialized || forgottenPassword) {
|
||||||
|
// // show current view
|
||||||
|
// log.debug('rendering an initialize screen')
|
||||||
|
// // switch (props.currentView.name) {
|
||||||
|
|
||||||
|
// // case 'restoreVault':
|
||||||
|
// // log.debug('rendering restore vault screen')
|
||||||
|
// // return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
|
||||||
|
|
||||||
|
// // default:
|
||||||
|
// // log.debug('rendering menu screen')
|
||||||
|
// // return h(InitializeScreen, {key: 'menuScreenInit'})
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // show unlock screen
|
||||||
|
// if (!props.isUnlocked) {
|
||||||
|
// return h(MainContainer, {
|
||||||
|
// currentViewName: props.currentView.name,
|
||||||
|
// isUnlocked: props.isUnlocked,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// show current view
|
||||||
|
switch (currentView.name) { |
||||||
|
|
||||||
|
case 'accountDetail': |
||||||
|
log.debug('rendering main container') |
||||||
|
return h(MainContainer, {key: 'account-detail'}) |
||||||
|
|
||||||
|
// case 'sendTransaction':
|
||||||
|
// log.debug('rendering send tx screen')
|
||||||
|
|
||||||
|
// // Going to leave this here until we are ready to delete SendTransactionScreen v1
|
||||||
|
// // const SendComponentToRender = checkFeatureToggle('send-v2')
|
||||||
|
// // ? SendTransactionScreen2
|
||||||
|
// // : SendTransactionScreen
|
||||||
|
|
||||||
|
// return h(SendTransactionScreen2, {key: 'send-transaction'})
|
||||||
|
|
||||||
|
// case 'sendToken':
|
||||||
|
// log.debug('rendering send token screen')
|
||||||
|
|
||||||
|
// // Going to leave this here until we are ready to delete SendTransactionScreen v1
|
||||||
|
// // const SendTokenComponentToRender = checkFeatureToggle('send-v2')
|
||||||
|
// // ? SendTransactionScreen2
|
||||||
|
// // : SendTokenScreen
|
||||||
|
|
||||||
|
// return h(SendTransactionScreen2, {key: 'sendToken'})
|
||||||
|
|
||||||
|
case 'newKeychain': |
||||||
|
log.debug('rendering new keychain screen') |
||||||
|
return h(NewKeyChainScreen, {key: 'new-keychain'}) |
||||||
|
|
||||||
|
// case 'confTx':
|
||||||
|
// log.debug('rendering confirm tx screen')
|
||||||
|
// return h(Redirect, {
|
||||||
|
// to: {
|
||||||
|
// pathname: CONFIRM_TRANSACTION_ROUTE,
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// return h(ConfirmTxScreen, {key: 'confirm-tx'})
|
||||||
|
|
||||||
|
// case 'add-token':
|
||||||
|
// log.debug('rendering add-token screen from unlock screen.')
|
||||||
|
// return h(AddTokenScreen, {key: 'add-token'})
|
||||||
|
|
||||||
|
// case 'config':
|
||||||
|
// log.debug('rendering config screen')
|
||||||
|
// return h(Settings, {key: 'config'})
|
||||||
|
|
||||||
|
// case 'import-menu':
|
||||||
|
// log.debug('rendering import screen')
|
||||||
|
// return h(Import, {key: 'import-menu'})
|
||||||
|
|
||||||
|
// case 'reveal-seed-conf':
|
||||||
|
// log.debug('rendering reveal seed confirmation screen')
|
||||||
|
// return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
|
||||||
|
|
||||||
|
// case 'info':
|
||||||
|
// log.debug('rendering info screen')
|
||||||
|
// return h(Settings, {key: 'info', tab: 'info'})
|
||||||
|
|
||||||
|
case 'buyEth': |
||||||
|
log.debug('rendering buy ether screen') |
||||||
|
return h(BuyView, {key: 'buyEthView'}) |
||||||
|
|
||||||
|
case 'onboardingBuyEth': |
||||||
|
log.debug('rendering onboarding buy ether screen') |
||||||
|
return h(MascaraBuyEtherScreen, {key: 'buyEthView'}) |
||||||
|
|
||||||
|
case 'qr': |
||||||
|
log.debug('rendering show qr screen') |
||||||
|
return h('div', { |
||||||
|
style: { |
||||||
|
position: 'absolute', |
||||||
|
height: '100%', |
||||||
|
top: '0px', |
||||||
|
left: '0px', |
||||||
|
}, |
||||||
|
}, [ |
||||||
|
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', { |
||||||
|
onClick: () => this.props.dispatch(actions.backToAccountDetail(activeAddress)), |
||||||
|
style: { |
||||||
|
marginLeft: '10px', |
||||||
|
marginTop: '50px', |
||||||
|
}, |
||||||
|
}), |
||||||
|
h('div', { |
||||||
|
style: { |
||||||
|
position: 'absolute', |
||||||
|
left: '44px', |
||||||
|
width: '285px', |
||||||
|
}, |
||||||
|
}, [ |
||||||
|
h(QrView, {key: 'qr'}), |
||||||
|
]), |
||||||
|
]) |
||||||
|
|
||||||
|
default: |
||||||
|
log.debug('rendering default, account detail screen') |
||||||
|
return h(MainContainer, {key: 'account-detail'}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Home.propTypes = { |
||||||
|
currentCurrency: PropTypes.string, |
||||||
|
isLoading: PropTypes.bool, |
||||||
|
loadingMessage: PropTypes.string, |
||||||
|
network: PropTypes.string, |
||||||
|
provider: PropTypes.object, |
||||||
|
frequentRpcList: PropTypes.array, |
||||||
|
currentView: PropTypes.object, |
||||||
|
sidebarOpen: PropTypes.bool, |
||||||
|
isMascara: PropTypes.bool, |
||||||
|
isOnboarding: PropTypes.bool, |
||||||
|
isUnlocked: PropTypes.bool, |
||||||
|
networkDropdownOpen: PropTypes.bool, |
||||||
|
history: PropTypes.object, |
||||||
|
dispatch: PropTypes.func, |
||||||
|
selectedAddress: PropTypes.string, |
||||||
|
noActiveNotices: PropTypes.bool, |
||||||
|
lostAccounts: PropTypes.array, |
||||||
|
isInitialized: PropTypes.bool, |
||||||
|
forgottenPassword: PropTypes.bool, |
||||||
|
activeAddress: PropTypes.string, |
||||||
|
unapprovedTxs: PropTypes.object, |
||||||
|
seedWords: PropTypes.string, |
||||||
|
unapprovedMsgCount: PropTypes.number, |
||||||
|
unapprovedPersonalMsgCount: PropTypes.number, |
||||||
|
unapprovedTypedMessagesCount: PropTypes.number, |
||||||
|
welcomeScreenSeen: PropTypes.bool, |
||||||
|
isPopup: PropTypes.bool, |
||||||
|
isMouseUser: PropTypes.bool, |
||||||
|
t: PropTypes.func, |
||||||
|
} |
||||||
|
|
||||||
|
function mapStateToProps (state) { |
||||||
|
const { appState, metamask } = state |
||||||
|
const { |
||||||
|
networkDropdownOpen, |
||||||
|
sidebarOpen, |
||||||
|
isLoading, |
||||||
|
loadingMessage, |
||||||
|
} = appState |
||||||
|
|
||||||
|
const { |
||||||
|
accounts, |
||||||
|
address, |
||||||
|
isInitialized, |
||||||
|
noActiveNotices, |
||||||
|
seedWords, |
||||||
|
unapprovedTxs, |
||||||
|
lastUnreadNotice, |
||||||
|
lostAccounts, |
||||||
|
unapprovedMsgCount, |
||||||
|
unapprovedPersonalMsgCount, |
||||||
|
unapprovedTypedMessagesCount, |
||||||
|
} = metamask |
||||||
|
const selected = address || Object.keys(accounts)[0] |
||||||
|
|
||||||
|
return { |
||||||
|
// state from plugin
|
||||||
|
networkDropdownOpen, |
||||||
|
sidebarOpen, |
||||||
|
isLoading, |
||||||
|
loadingMessage, |
||||||
|
noActiveNotices, |
||||||
|
isInitialized, |
||||||
|
isUnlocked: state.metamask.isUnlocked, |
||||||
|
selectedAddress: state.metamask.selectedAddress, |
||||||
|
currentView: state.appState.currentView, |
||||||
|
activeAddress: state.appState.activeAddress, |
||||||
|
transForward: state.appState.transForward, |
||||||
|
isMascara: state.metamask.isMascara, |
||||||
|
isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized), |
||||||
|
isPopup: state.metamask.isPopup, |
||||||
|
seedWords: state.metamask.seedWords, |
||||||
|
unapprovedTxs, |
||||||
|
unapprovedMsgs: state.metamask.unapprovedMsgs, |
||||||
|
unapprovedMsgCount, |
||||||
|
unapprovedPersonalMsgCount, |
||||||
|
unapprovedTypedMessagesCount, |
||||||
|
menuOpen: state.appState.menuOpen, |
||||||
|
network: state.metamask.network, |
||||||
|
provider: state.metamask.provider, |
||||||
|
forgottenPassword: state.appState.forgottenPassword, |
||||||
|
lastUnreadNotice, |
||||||
|
lostAccounts, |
||||||
|
frequentRpcList: state.metamask.frequentRpcList || [], |
||||||
|
currentCurrency: state.metamask.currentCurrency, |
||||||
|
isMouseUser: state.appState.isMouseUser, |
||||||
|
isRevealingSeedWords: state.metamask.isRevealingSeedWords, |
||||||
|
Qr: state.appState.Qr, |
||||||
|
welcomeScreenSeen: state.metamask.welcomeScreenSeen, |
||||||
|
|
||||||
|
// state needed to get account dropdown temporarily rendering from app bar
|
||||||
|
selected, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = compose( |
||||||
|
withRouter, |
||||||
|
connect(mapStateToProps) |
||||||
|
)(Home) |
@ -0,0 +1,25 @@ |
|||||||
|
const { connect } = require('react-redux') |
||||||
|
const PropTypes = require('prop-types') |
||||||
|
const { Redirect } = require('react-router-dom') |
||||||
|
const h = require('react-hyperscript') |
||||||
|
const { INITIALIZE_ROUTE } = require('../../routes') |
||||||
|
const MetamaskRoute = require('./metamask-route') |
||||||
|
|
||||||
|
const Initialized = props => { |
||||||
|
return props.isInitialized |
||||||
|
? h(MetamaskRoute, { ...props }) |
||||||
|
: h(Redirect, { to: { pathname: INITIALIZE_ROUTE } }) |
||||||
|
} |
||||||
|
|
||||||
|
Initialized.propTypes = { |
||||||
|
isInitialized: PropTypes.bool, |
||||||
|
} |
||||||
|
|
||||||
|
const mapStateToProps = state => { |
||||||
|
const { metamask: { isInitialized } } = state |
||||||
|
return { |
||||||
|
isInitialized, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(Initialized) |
@ -0,0 +1,177 @@ |
|||||||
|
const { withRouter } = require('react-router-dom') |
||||||
|
const PropTypes = require('prop-types') |
||||||
|
const { compose } = require('recompose') |
||||||
|
const PersistentForm = require('../../../../lib/persistent-form') |
||||||
|
const connect = require('../../../metamask-connect') |
||||||
|
const h = require('react-hyperscript') |
||||||
|
const { createNewVaultAndRestore, unMarkPasswordForgotten } = require('../../../actions') |
||||||
|
const { DEFAULT_ROUTE } = require('../../../routes') |
||||||
|
|
||||||
|
class RestoreVaultPage extends PersistentForm { |
||||||
|
constructor (props) { |
||||||
|
super(props) |
||||||
|
|
||||||
|
this.state = { |
||||||
|
error: null, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
createOnEnter (event) { |
||||||
|
if (event.key === 'Enter') { |
||||||
|
this.createNewVaultAndRestore() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
cancel () { |
||||||
|
this.props.unMarkPasswordForgotten() |
||||||
|
.then(this.props.history.push(DEFAULT_ROUTE)) |
||||||
|
} |
||||||
|
|
||||||
|
createNewVaultAndRestore () { |
||||||
|
this.setState({ error: null }) |
||||||
|
|
||||||
|
// check password
|
||||||
|
var passwordBox = document.getElementById('password-box') |
||||||
|
var password = passwordBox.value |
||||||
|
var passwordConfirmBox = document.getElementById('password-box-confirm') |
||||||
|
var passwordConfirm = passwordConfirmBox.value |
||||||
|
|
||||||
|
if (password.length < 8) { |
||||||
|
this.setState({ error: 'Password not long enough' }) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (password !== passwordConfirm) { |
||||||
|
this.setState({ error: 'Passwords don\'t match' }) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// check seed
|
||||||
|
var seedBox = document.querySelector('textarea.twelve-word-phrase') |
||||||
|
var seed = seedBox.value.trim() |
||||||
|
if (seed.split(' ').length !== 12) { |
||||||
|
this.setState({ error: 'Seed phrases are 12 words long' }) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// submit
|
||||||
|
this.props.createNewVaultAndRestore(password, seed) |
||||||
|
.then(() => this.props.history.push(DEFAULT_ROUTE)) |
||||||
|
.catch(({ message }) => { |
||||||
|
this.setState({ error: message }) |
||||||
|
log.error(message) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
const { error } = this.state |
||||||
|
this.persistentFormParentId = 'restore-vault-form' |
||||||
|
|
||||||
|
return ( |
||||||
|
h('.initialize-screen.flex-column.flex-center.flex-grow', [ |
||||||
|
|
||||||
|
h('h3.flex-center.text-transform-uppercase', { |
||||||
|
style: { |
||||||
|
background: '#EBEBEB', |
||||||
|
color: '#AEAEAE', |
||||||
|
marginBottom: 24, |
||||||
|
width: '100%', |
||||||
|
fontSize: '20px', |
||||||
|
padding: 6, |
||||||
|
}, |
||||||
|
}, [ |
||||||
|
this.props.t('restoreVault'), |
||||||
|
]), |
||||||
|
|
||||||
|
// wallet seed entry
|
||||||
|
h('h3', 'Wallet Seed'), |
||||||
|
h('textarea.twelve-word-phrase.letter-spacey', { |
||||||
|
dataset: { |
||||||
|
persistentFormId: 'wallet-seed', |
||||||
|
}, |
||||||
|
placeholder: this.props.t('secretPhrase'), |
||||||
|
}), |
||||||
|
|
||||||
|
// password
|
||||||
|
h('input.large-input.letter-spacey', { |
||||||
|
type: 'password', |
||||||
|
id: 'password-box', |
||||||
|
placeholder: this.props.t('newPassword8Chars'), |
||||||
|
dataset: { |
||||||
|
persistentFormId: 'password', |
||||||
|
}, |
||||||
|
style: { |
||||||
|
width: 260, |
||||||
|
marginTop: 12, |
||||||
|
}, |
||||||
|
}), |
||||||
|
|
||||||
|
// confirm password
|
||||||
|
h('input.large-input.letter-spacey', { |
||||||
|
type: 'password', |
||||||
|
id: 'password-box-confirm', |
||||||
|
placeholder: this.props.t('confirmPassword'), |
||||||
|
onKeyPress: this.createOnEnter.bind(this), |
||||||
|
dataset: { |
||||||
|
persistentFormId: 'password-confirmation', |
||||||
|
}, |
||||||
|
style: { |
||||||
|
width: 260, |
||||||
|
marginTop: 16, |
||||||
|
}, |
||||||
|
}), |
||||||
|
|
||||||
|
error && ( |
||||||
|
h('span.error.in-progress-notification', error) |
||||||
|
), |
||||||
|
|
||||||
|
// submit
|
||||||
|
h('.flex-row.flex-space-between', { |
||||||
|
style: { |
||||||
|
marginTop: 30, |
||||||
|
width: '50%', |
||||||
|
}, |
||||||
|
}, [ |
||||||
|
|
||||||
|
// cancel
|
||||||
|
h('button.primary', { |
||||||
|
onClick: () => this.cancel(), |
||||||
|
}, this.props.t('cancel')), |
||||||
|
|
||||||
|
// submit
|
||||||
|
h('button.primary', { |
||||||
|
onClick: this.createNewVaultAndRestore.bind(this), |
||||||
|
}, this.props.t('ok')), |
||||||
|
|
||||||
|
]), |
||||||
|
]) |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RestoreVaultPage.propTypes = { |
||||||
|
history: PropTypes.object, |
||||||
|
} |
||||||
|
|
||||||
|
const mapStateToProps = state => { |
||||||
|
const { appState: { warning, forgottenPassword } } = state |
||||||
|
|
||||||
|
return { |
||||||
|
warning, |
||||||
|
forgottenPassword, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => { |
||||||
|
return { |
||||||
|
createNewVaultAndRestore: (password, seed) => { |
||||||
|
return dispatch(createNewVaultAndRestore(password, seed)) |
||||||
|
}, |
||||||
|
unMarkPasswordForgotten: () => dispatch(unMarkPasswordForgotten()), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = compose( |
||||||
|
withRouter, |
||||||
|
connect(mapStateToProps, mapDispatchToProps) |
||||||
|
)(RestoreVaultPage) |
@ -0,0 +1,195 @@ |
|||||||
|
const { Component } = require('react') |
||||||
|
const { connect } = require('react-redux') |
||||||
|
const PropTypes = require('prop-types') |
||||||
|
const h = require('react-hyperscript') |
||||||
|
const { exportAsFile } = require('../../../util') |
||||||
|
const { requestRevealSeed, confirmSeedWords } = require('../../../actions') |
||||||
|
const { DEFAULT_ROUTE } = require('../../../routes') |
||||||
|
|
||||||
|
class RevealSeedPage extends Component { |
||||||
|
componentDidMount () { |
||||||
|
const passwordBox = document.getElementById('password-box') |
||||||
|
if (passwordBox) { |
||||||
|
passwordBox.focus() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
checkConfirmation (event) { |
||||||
|
if (event.key === 'Enter') { |
||||||
|
event.preventDefault() |
||||||
|
this.revealSeedWords() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
revealSeedWords () { |
||||||
|
const password = document.getElementById('password-box').value |
||||||
|
this.props.requestRevealSeed(password) |
||||||
|
} |
||||||
|
|
||||||
|
renderSeed () { |
||||||
|
const { seedWords, confirmSeedWords, history } = this.props |
||||||
|
|
||||||
|
return ( |
||||||
|
h('.initialize-screen.flex-column.flex-center.flex-grow', [ |
||||||
|
|
||||||
|
h('h3.flex-center.text-transform-uppercase', { |
||||||
|
style: { |
||||||
|
background: '#EBEBEB', |
||||||
|
color: '#AEAEAE', |
||||||
|
marginTop: 36, |
||||||
|
marginBottom: 8, |
||||||
|
width: '100%', |
||||||
|
fontSize: '20px', |
||||||
|
padding: 6, |
||||||
|
}, |
||||||
|
}, [ |
||||||
|
'Vault Created', |
||||||
|
]), |
||||||
|
|
||||||
|
h('div', { |
||||||
|
style: { |
||||||
|
fontSize: '1em', |
||||||
|
marginTop: '10px', |
||||||
|
textAlign: 'center', |
||||||
|
}, |
||||||
|
}, [ |
||||||
|
h('span.error', 'These 12 words are the only way to restore your MetaMask accounts.\nSave them somewhere safe and secret.'), |
||||||
|
]), |
||||||
|
|
||||||
|
h('textarea.twelve-word-phrase', { |
||||||
|
readOnly: true, |
||||||
|
value: seedWords, |
||||||
|
}), |
||||||
|
|
||||||
|
h('button.primary', { |
||||||
|
onClick: () => confirmSeedWords().then(() => history.push(DEFAULT_ROUTE)), |
||||||
|
style: { |
||||||
|
margin: '24px', |
||||||
|
fontSize: '0.9em', |
||||||
|
marginBottom: '10px', |
||||||
|
}, |
||||||
|
}, 'I\'ve copied it somewhere safe'), |
||||||
|
|
||||||
|
h('button.primary', { |
||||||
|
onClick: () => exportAsFile(`MetaMask Seed Words`, seedWords), |
||||||
|
style: { |
||||||
|
margin: '10px', |
||||||
|
fontSize: '0.9em', |
||||||
|
}, |
||||||
|
}, 'Save Seed Words As File'), |
||||||
|
]) |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
renderConfirmation () { |
||||||
|
const { history, warning, inProgress } = this.props |
||||||
|
|
||||||
|
return ( |
||||||
|
h('.initialize-screen.flex-column.flex-center.flex-grow', { |
||||||
|
style: { maxWidth: '420px' }, |
||||||
|
}, [ |
||||||
|
|
||||||
|
h('h3.flex-center.text-transform-uppercase', { |
||||||
|
style: { |
||||||
|
background: '#EBEBEB', |
||||||
|
color: '#AEAEAE', |
||||||
|
marginBottom: 24, |
||||||
|
width: '100%', |
||||||
|
fontSize: '20px', |
||||||
|
padding: 6, |
||||||
|
}, |
||||||
|
}, [ |
||||||
|
'Reveal Seed Words', |
||||||
|
]), |
||||||
|
|
||||||
|
h('.div', { |
||||||
|
style: { |
||||||
|
display: 'flex', |
||||||
|
flexDirection: 'column', |
||||||
|
padding: '20px', |
||||||
|
justifyContent: 'center', |
||||||
|
}, |
||||||
|
}, [ |
||||||
|
|
||||||
|
h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'), |
||||||
|
|
||||||
|
// confirmation
|
||||||
|
h('input.large-input.letter-spacey', { |
||||||
|
type: 'password', |
||||||
|
id: 'password-box', |
||||||
|
placeholder: 'Enter your password to confirm', |
||||||
|
onKeyPress: this.checkConfirmation.bind(this), |
||||||
|
style: { |
||||||
|
width: 260, |
||||||
|
marginTop: '12px', |
||||||
|
}, |
||||||
|
}), |
||||||
|
|
||||||
|
h('.flex-row.flex-start', { |
||||||
|
style: { |
||||||
|
marginTop: 30, |
||||||
|
width: '50%', |
||||||
|
}, |
||||||
|
}, [ |
||||||
|
// cancel
|
||||||
|
h('button.primary', { |
||||||
|
onClick: () => history.push(DEFAULT_ROUTE), |
||||||
|
}, 'CANCEL'), |
||||||
|
|
||||||
|
// submit
|
||||||
|
h('button.primary', { |
||||||
|
style: { marginLeft: '10px' }, |
||||||
|
onClick: this.revealSeedWords.bind(this), |
||||||
|
}, 'OK'), |
||||||
|
|
||||||
|
]), |
||||||
|
|
||||||
|
warning && ( |
||||||
|
h('span.error', { |
||||||
|
style: { |
||||||
|
margin: '20px', |
||||||
|
}, |
||||||
|
}, warning.split('-')) |
||||||
|
), |
||||||
|
|
||||||
|
inProgress && ( |
||||||
|
h('span.in-progress-notification', 'Generating Seed...') |
||||||
|
), |
||||||
|
]), |
||||||
|
]) |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
return this.props.seedWords |
||||||
|
? this.renderSeed() |
||||||
|
: this.renderConfirmation() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RevealSeedPage.propTypes = { |
||||||
|
requestRevealSeed: PropTypes.func, |
||||||
|
confirmSeedWords: PropTypes.func, |
||||||
|
seedWords: PropTypes.string, |
||||||
|
inProgress: PropTypes.bool, |
||||||
|
history: PropTypes.object, |
||||||
|
warning: PropTypes.string, |
||||||
|
} |
||||||
|
|
||||||
|
const mapStateToProps = state => { |
||||||
|
const { appState: { warning }, metamask: { seedWords } } = state |
||||||
|
|
||||||
|
return { |
||||||
|
warning, |
||||||
|
seedWords, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => { |
||||||
|
return { |
||||||
|
requestRevealSeed: password => dispatch(requestRevealSeed(password)), |
||||||
|
confirmSeedWords: () => dispatch(confirmSeedWords()), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(RevealSeedPage) |
@ -0,0 +1,28 @@ |
|||||||
|
const { connect } = require('react-redux') |
||||||
|
const PropTypes = require('prop-types') |
||||||
|
const { Route } = require('react-router-dom') |
||||||
|
const h = require('react-hyperscript') |
||||||
|
|
||||||
|
const MetamaskRoute = ({ component, mascaraComponent, isMascara, ...props }) => { |
||||||
|
return ( |
||||||
|
h(Route, { |
||||||
|
...props, |
||||||
|
component: isMascara && mascaraComponent ? mascaraComponent : component, |
||||||
|
}) |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
MetamaskRoute.propTypes = { |
||||||
|
component: PropTypes.func, |
||||||
|
mascaraComponent: PropTypes.func, |
||||||
|
isMascara: PropTypes.bool, |
||||||
|
} |
||||||
|
|
||||||
|
const mapStateToProps = state => { |
||||||
|
const { metamask: { isMascara } } = state |
||||||
|
return { |
||||||
|
isMascara, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(MetamaskRoute) |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue