diff --git a/README.md b/README.md index 4bd4b2e..a0710b2 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,10 @@ Auroracoin Wallet Extension 2.0 - Aurora-node API Version Auroracoin wallet in the browser. Send and receive instantly on any web page. -Version 1.3 - Updated for Multi-Algo -Version 2.0 - Updated to use Insight exclusively for blockchain access and tx send +Version 1.3 - Updated for Multi-Algo +Version 2.0 - Updated to use Insight exclusively for blockchain access and tx send +Version 2.0.1 - Removed incompatible API, exchange rates currently unavailable +Version 2.0.2 - Fixed API calls, exchange rates and currencies re-enabled Security -------- diff --git a/currency-manager.js b/currency-manager.js new file mode 100644 index 0000000..d332a14 --- /dev/null +++ b/currency-manager.js @@ -0,0 +1,105 @@ +/** + * currency-manager.js + * Copyright (c) 2014 Andrew Toth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. + * + * Currency manager handles the exchange rate of the currency + * and the proper formatting of the currency value + */ + +(function (window) { + var currencyInstance; + var currencyManager = function () {}; + currencyManager.prototype = { + updateExchangeRate: function () { + //upadate the value of AuroraCoin to bitcoin + var tickresult + util.getJSON('https://bittrex.com/api/v1.1/public/getticker?market=btc-aur').then(function (response){ + tickresult = response ; + AURExchangeRate = tickresult.result.Last; + //console.log(AURExchangeRate); + }); + return preferences.getCurrency().then(function (currency) { + return util.getJSON('https://apiv2.bitcoinaverage.com/indices/global/ticker/BTC' + currency); + }).then(function (response) { + return preferences.setExchangeRate(response.last * AURExchangeRate ); + }); + }, + + getSymbol: function () { + return preferences.getCurrency().then(function (currency) { + switch (currency) { + case 'AUD': + case 'CAD': + case 'NZD': + case 'SGD': + case 'USD': + return(['$', 'before']); + case 'BRL': + return(['R$', 'before']); + case 'CHF': + return([' Fr.', 'after']); + case 'CNY': + case 'JPY': + return(['¥', 'before']); + case 'EUR': + return(['€', 'before']); + case 'GBP': + return(['£', 'before']); + case 'ILS': + return(['₪', 'before']); + case 'NOK': + case 'SEK': + case 'ISK': + return([' ISK', 'after']); + case 'PLN': + return(['zł', 'after']); + case 'RUB': + return([' RUB', 'after']); + case 'ZAR': + return([' R', 'after']); + default: + return(['$', 'before']); + } + }); + }, + + getAvailableCurrencies: function () { + return ['AUD', 'BRL', 'CAD', 'CHF', 'CNY', 'ISK', 'EUR', 'GBP', 'ILS', 'JPY', 'NOK', 'NZD', 'PLN', 'RUB', 'SEK', 'SGD', 'USD', 'ZAR']; + }, + + formatAmount: function (value) { + return Promise.all([preferences.getExchangeRate(), this.getSymbol()]).then(function (values) { + var rate = values[0], + symbol = values[1][0], + beforeOrAfter = values[1][1], + SATOSHIS = 100000000, + text = (value / SATOSHIS * rate).formatMoney(2); + if (beforeOrAfter === 'before') { + text = symbol + text; + } else { + text += symbol; + } + return text; + }); + } + }; + + Number.prototype.formatMoney = function(c, d, t){ + var n = this, + c = isNaN(c = Math.abs(c)) ? 2 : c, + d = d == undefined ? "." : d, + t = t == undefined ? "," : t, + s = n < 0 ? "-" : "", + i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", + j = (j = i.length) > 3 ? j % 3 : 0; + return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); + }; + + var ret = new currencyManager(); + ret.updateExchangeRate(); + window.currencyManager = ret; + +})(window); diff --git a/index.html b/index.html new file mode 100644 index 0000000..d401560 --- /dev/null +++ b/index.html @@ -0,0 +1,327 @@ + + + + Auroracoin Wallet Extension + + + + + + + + + + + + + + + + + + +
+
+ +
+ + +
+ + + +
+ + +
+ + AUR +
+ +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..187f784 --- /dev/null +++ b/index.js @@ -0,0 +1,514 @@ +/** + * index.js + * Copyright (c) 2014 Andrew Toth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. + * + * Controls index.html, the main wallet Chrome popover/Firefox panel + */ + +$(document).ready(function () { + // Setup the wallet, page values and callbacks + var val = '', + address = '', + SATOSHIS = 100000000, + FEE = SATOSHIS * .0001, + BTCUnits = 'AUR', + BTCMultiplier = SATOSHIS; + function setupWallet() { + wallet.restoreAddress().then(setQRCodes, + function () { + return wallet.generateAddress(); + }).then(setQRCodes, + function () { + alert('Failed to generate wallet. Refresh and try again.'); + }); + + function setQRCodes() { + $('#qrcode').html(createQRCodeCanvas(wallet.getAddress())); + $('#textAddress').text(wallet.getAddress()); + } + } + wallet.setBalanceListener(function (balance) { + setBalance(balance); + }); + setupWallet(); + + $('#amount').on('keyup change', function () { + val = Math.floor(Number($(this).val() * BTCMultiplier)); + if (val > 0) { + currencyManager.formatAmount(val).then(function (formattedMoney) { + var text = 'Amount: ' + formattedMoney; + $('#amountLabel').text(text); + }); + } else { + $('#amountLabel').text('Amount:'); + } + }); + + + function setBTCUnits(units) { + BTCUnits = units; + if (units === 'µAUR') { + BTCMultiplier = SATOSHIS / 1000000; + } else if (units === 'mAUR') { + BTCMultiplier = SATOSHIS / 1000; + } else { + BTCMultiplier = SATOSHIS; + } + + setBalance(wallet.getBalance()); + $('#sendUnit').html(BTCUnits); + $('#amount').attr('placeholder', '(Plus ' + FEE / BTCMultiplier + ' ' + BTCUnits + ' fee)').attr('step', 100000 / BTCMultiplier).val(null); + $('#amountLabel').text('Amount:'); + } + preferences.getBTCUnits().then(setBTCUnits); + + function setBalance(balance) { + if (Number(balance) < 0 || isNaN(balance)) { + balance = 0; + } + $('#balance').text(balance / BTCMultiplier + ' ' + BTCUnits); + } + + $('#successAlertClose').click(function () { + $('#successAlert').fadeOut(); + if (typeof chrome === 'undefined') { + addon.port.emit('resize', 278); + } + }); + + $('#unkownErrorAlertClose').click(function () { + $('#unknownErrorAlert').fadeOut(); + }); + + if (typeof chrome === 'undefined') { + addon.port.on('show', setupWallet); + } + + /* + * Send BTC + */ + $('#sendButton').click(function () { + val = Math.floor(Number($('#amount').val() * BTCMultiplier)); + address = $('#sendAddress').val(); + var balance = wallet.getBalance(); + var validAmount = true; + if (val <= 0) { + validAmount = false; + } else if (val + FEE > balance) { + validAmount = false; + } + if (validAmount) { + $('#amountAlert').slideUp(); + } else { + $('#amountAlert').slideDown(); + } + + var regex = /^[Aa][1-9A-HJ-NP-Za-km-z]{26,33}$/; + var validAddress = true; + if (!regex.test(String(address))) { + validAddress = false; + } else { + try { + new bitcoin.Address.fromBase58Check(address); + } catch (e) { + validAddress = false; + } + } + + if (validAddress) { + $('#addressAlert').slideUp(); + } else { + $('#addressAlert').slideDown(); + } + + if (validAddress && validAmount) { + if (wallet.isEncrypted()) { + currencyManager.formatAmount(val).then(function (formattedMoney) { + var text = 'Are you sure you want to send
' + val / BTCMultiplier + ' ' + BTCUnits + ' (' + formattedMoney + ')
to ' + address + ' ?'; + $('#sendConfirmationText').html(text); + $('#sendConfirmationPassword').val(null); + $('#sendConfirmationPasswordIncorrect').hide(); + $('#sendConfirmationModal').modal().show(); + }); + } else { + confirmSend(); + } + } + }); + + $('#confirmSendButton').click(function () { + confirmSend(); + }); + + function confirmSend() { + $('#cover').show(); + var password = $('#sendConfirmationPassword').val(); + wallet.send(address, val, FEE, password).then(function () { + $('#amount').val(null); + $('#sendAddress').val(null); + $('#amountLabel').text('Amount:'); + var text = 'Sent ' + val / BTCMultiplier + ' ' + BTCUnits + ' to ' + address + '.'; + $('#successAlertLabel').text(text); + $('#successAlert').slideDown(); + $('#sendConfirmationModal').modal('hide'); + $('#cover').fadeOut('slow'); + preferences.getLastBalance().then(function (result) { + $('#balance').text(result / BTCMultiplier + ' ' + BTCUnits); + }); + }, function (e) { + if (wallet.isEncrypted()) { + $('#sendConfirmationPasswordIncorrect').text(e.message).slideDown(); + } else { + $('#unknownErrorAlertLabel').text(e.message); + $('#unknownErrorAlert').slideDown(); + } + $('#cover').hide(); + }); + } + + /* + * Settings Menu + */ + + /* + * Set Password + */ + $('#setPassword').click(function () { + $('#passwordMismatch').hide(); + $('#setPasswordIncorrect').hide(); + $('#setPasswordBlank').hide(); + if (wallet.isEncrypted()) { + $('#removePasswordDiv').show(); + $('#setPasswordPassword').show().val(null); + } else { + $('#removePasswordDiv').hide(); + $('#setPasswordPassword').hide().val(null); + } + $('#newPassword').show().val(null); + $('#confirmNewPassword').show().val(null); + $('#removePassword').attr('checked', false); + $('#setPasswordModal').modal().show(); + }); + + $('#removePassword').click(function () { + if (this.checked) { + $('#newPassword').val(null).slideUp(); + $('#confirmNewPassword').val(null).slideUp(); + } else { + $('#newPassword').slideDown(); + $('#confirmNewPassword').slideDown(); + } + }); + + $('#confirmSetPassword').click(function () { + var password = $('#setPasswordPassword').val(), + newPassword = $('#newPassword').val(), + confirmNewPassword = $('#confirmNewPassword').val(); + var validInput = true; + //already encrypted, or want to remove password and no password + if ((wallet.isEncrypted() && !password) || (!$('#removePassword').is(':checked') && (!newPassword || !confirmNewPassword))) { + validInput = false; + $('#setPasswordBlank').slideDown(); + } else { + $('#setPasswordBlank').slideUp(); + } + + if (validInput && newPassword !== confirmNewPassword) { + validInput = false; + $('#passwordMismatch').slideDown(); + } else { + $('#passwordMismatch').slideUp(); + } + + if (validInput && wallet.isEncrypted() && !wallet.validatePassword(password)) { + validInput = false; + $('#setPasswordIncorrect').slideDown(); + } else { + $('#setPasswordIncorrect').slideUp(); + } + if (validInput) { + wallet.updatePassword(String(password), String(newPassword)).then(function () { + $('#successAlertLabel').text('New password set.'); + $('#successAlert').show(); + $('#setPasswordModal').modal('hide'); + }); + } + + }); + + /* + * Currency selection + */ + $('#setCurrency').click(function () { + preferences.getCurrency().then(function (currency) { + var currencies = currencyManager.getAvailableCurrencies(); + var tableBody = ''; + for (var i = 0; i < currencies.length/3; i++) { + tableBody += ''; + for (var j = i; j <= i+12; j+=6) { + tableBody += '
'; + } + tableBody += ''; + } + $('#tableBody').html(tableBody); + $('#setCurrencyModal').modal().show(); + $('.radio').click(function () { + var currency = $.trim($(this).text()); + $('input:radio[name=' + currency + ']').attr('checked', 'checked'); + preferences.setCurrency(currency).then(function () { + $('#amountLabel').text('Amount:'); + $('#successAlertLabel').text('Currency set to ' + currency + '.'); + $('#successAlert').show(); + $('#setCurrencyModal').modal('hide'); + }); + }); + }); + }); + + /* + * Units selection + */ + $('#setUnits').click(function () { + preferences.getBTCUnits().then(function (units) { + var availableUnits = ['AUR', 'mAUR', 'µAUR']; + var tableBody = ''; + for (var i = 0; i < availableUnits.length; i++) { + tableBody += '
'; + } + tableBody += ''; + $('#tableBody').html(tableBody); + $('#setCurrencyModal').modal().show(); + $('.radio').click(function () { + var units = $.trim($(this).text()); + $('input:radio[name=' + units + ']').attr('checked', 'checked'); + setBTCUnits(units); + preferences.setBTCUnits(units).then(function () { + $('#successAlertLabel').text('Units set to ' + units + '.'); + $('#successAlert').show(); + $('#setCurrencyModal').modal('hide'); + }); + }); + }); + }); + + /* + * Show Private Key + */ + $('#showPrivateKey').click(function () { + $('#showPrivateKeyPasswordIncorrect').hide(); + if (wallet.isEncrypted()) { + $('#showPrivateKeyPassword').val(null).show(); + } else { + $('#showPrivateKeyPassword').hide(); + } + $('#privateKey').hide(); + $('#showPrivateKeyModal').modal().show(); + }); + + $('#showPrivateKeyConfirm').click(function () { + var password = $('#showPrivateKeyPassword').val(); + if (wallet.isEncrypted() && !wallet.validatePassword(password)) { + $('#showPrivateKeyPasswordIncorrect').slideDown(); + } else { + $('#showPrivateKeyPasswordIncorrect').slideUp(); + var privateKey = wallet.getDecryptedPrivateKey(password); + $('#privateKeyQRCode').html(createQRCodeCanvas(privateKey)); + $('#privateKeyText').text(privateKey); + $('#privateKey').slideDown(function () { + $('#main').height($('#showPrivateKeyModal').find('.modal-dialog').height()); + }); + } + }); + + /* + * Import Private Key + */ + $('#importPrivateKey').click(function () { + $('#importPrivateKeyPasswordIncorrect').hide(); + $('#importPrivateKeyBadPrivateKey').hide(); + if (wallet.isEncrypted()) { + $('#importPrivateKeyPassword').val(null).show(); + } else { + $('#importPrivateKeyPassword').hide(); + } + $('#importPrivateKeyPrivateKey').val(null); + $('#importPrivateKeyModal').modal().show(); + }); + + $('#importPrivateKeyConfirm').click(function () { + var privateKey = $('#importPrivateKeyPrivateKey').val(); + try { + new bitcoin.ECKey.fromWIF(privateKey); + } catch (e) { + $('#importPrivateKeyBadPrivateKey').slideDown(); + return; + } + wallet.importAddress($('#importPrivateKeyPassword').val(), privateKey).then(function () { + setupWallet(); + $('#successAlertLabel').text('Private key imported successfully.'); + $('#successAlert').show(); + $('#importPrivateKeyModal').modal('hide'); + }, function (e) { + if (e.message === 'Incorrect password') { + $('#importPrivateKeyBadPrivateKey').slideUp(); + $('#importPrivateKeyPasswordIncorrect').slideDown(); + } else { + $('#importPrivateKeyPasswordIncorrect').slideUp(); + $('#importPrivateKeyBadPrivateKey').slideDown(); + } + }); + }); + + /* + * Generate New Wallet + */ + $('#generateNewWallet').click(function () { + $('#generateNewWalletPasswordIncorrect').hide(); + if (wallet.isEncrypted()) { + $('#generateNewWalletPassword').show().val(null); + } else { + $('#generateNewWalletPassword').hide(); + } + $('#generateNewWalletModal').modal().show(); + }); + + $('#generateNewWalletConfirm').click(function () { + wallet.generateAddress($('#generateNewWalletPassword').val()).then(function () { + setupWallet(); + $('#successAlertLabel').text('New wallet generated.'); + $('#successAlert').show(); + $('#generateNewWalletModal').modal('hide'); + }, function () { + $('#generateNewWalletPasswordIncorrect').slideDown(); + }); + }); + + /* + * About + */ + + if (typeof chrome !== 'undefined') { + $('#version').text(chrome.runtime.getManifest().version); + } else { + addon.port.on('version', function (version) { + $('#version').text(version); + }); + } + + $('#aboutModal').on('click', 'a', function () { + if (typeof chrome !== 'undefined') { + chrome.tabs.create({url: $(this).attr('href')}); + } else { + addon.port.emit('openTab', $(this).attr('href')); + } + return false; + }); + /* + * Tutorials + */ + $('#tutorialsModal').on('click', 'a', function () { + if (typeof chrome !== 'undefined') { + chrome.tabs.create({url: $(this).attr('href')}); + } else { + addon.port.emit('openTab', $(this).attr('href')); + } + return false; + }); + + $('#tutorialsModal').on('click', 'a', function () { + if (typeof chrome !== 'undefined') { + chrome.tabs.create({url: $(this).attr('href')}); + } else { + addon.port.emit('openTab', $(this).attr('href')); + } + return false; + }); + + function openTutorial() { + if (typeof chrome !== 'undefined') { + chrome.tabs.create({url : "http://auroracoin101.is/auroracoin-browser-wallet-tutorial/"}); + } else { + addon.port.emit('openTab', $(this).attr('href')); + } + return false; + + } + + /* + * Resizing + */ + + $('.modal').on('shown.bs.modal', function() { + var $main = $('#main'); + var height = $main.height(); + var modalHeight = $(this).find('.modal-dialog').height(); + if (modalHeight > height) { + $main.height(modalHeight); + if (typeof chrome === 'undefined') { + addon.port.emit('resize', modalHeight+2); + } + } + }).on('hidden.bs.modal', function () { + $('#main').height('auto'); + if (typeof chrome === 'undefined') { + if ($('#successAlert').is(':visible')) { + var height = 350; + } else { + var height = 278; + } + addon.port.emit('resize', height); + } + }); + + function createQRCodeCanvas(text) { + var sizeMultiplier = 4; + var typeNumber; + var lengthCalculation = text.length * 8 + 12; + if (lengthCalculation < 72) { typeNumber = 1; } + else if (lengthCalculation < 128) { typeNumber = 2; } + else if (lengthCalculation < 208) { typeNumber = 3; } + else if (lengthCalculation < 288) { typeNumber = 4; } + else if (lengthCalculation < 368) { typeNumber = 5; } + else if (lengthCalculation < 480) { typeNumber = 6; } + else if (lengthCalculation < 528) { typeNumber = 7; } + else if (lengthCalculation < 688) { typeNumber = 8; } + else if (lengthCalculation < 800) { typeNumber = 9; } + else if (lengthCalculation < 976) { typeNumber = 10; } + var qrcode = new QRCode(typeNumber, QRCode.ErrorCorrectLevel.H); + qrcode.addData(text); + qrcode.make(); + var width = qrcode.getModuleCount() * sizeMultiplier; + var height = qrcode.getModuleCount() * sizeMultiplier; + // create canvas element + var canvas = document.createElement('canvas'); + var scale = 10.0; + canvas.width = width * scale; + canvas.height = height * scale; + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + var ctx = canvas.getContext('2d'); + ctx.scale(scale, scale); + // compute tileW/tileH based on width/height + var tileW = width / qrcode.getModuleCount(); + var tileH = height / qrcode.getModuleCount(); + // draw in the canvas + for (var row = 0; row < qrcode.getModuleCount(); row++) { + for (var col = 0; col < qrcode.getModuleCount(); col++) { + ctx.fillStyle = qrcode.isDark(row, col) ? "#000000" : "#ffffff"; + ctx.fillRect(col * tileW, row * tileH, tileW, tileH); + } + } + return canvas; + } +}); diff --git a/manifest.json b/manifest.json index 2524968..7964ebc 100644 --- a/manifest.json +++ b/manifest.json @@ -1,10 +1,10 @@ { "manifest_version": 2, - "name": "Auroracoin 2.0 Chrome Extension", + "name": "Auroracoin Browser Wallet", "author": "Andrew Toth, modifed by Joseph Lee and SkateFish", "description": "Auroracoin wallet in the browser. Send and receive instantly on any web page.", - "version": "2.0", + "version": "2.0.2", "icons": { "16": "data/auroracoin16.png", diff --git a/preferences.js b/preferences.js new file mode 100644 index 0000000..41429c0 --- /dev/null +++ b/preferences.js @@ -0,0 +1,125 @@ +/** + * preferences.js + * Copyright (c) 2014 Andrew Toth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. + * + * Preferences handles storing and retrieving saved values + */ + +(function (window) { + + var ADDRESS = "wallet.address", + PRIVATE_KEY = "wallet.private_key", + IS_ENCRYPTED = "wallet.is_encrypted", + LAST_BALANCE = "wallet.last_balance", + EXCHANGE_RATE = 'wallet.exchange_rate', + BTC_UNITS = 'wallet.btc_units', + CURRENCY = 'wallet.currency', + SITE = 'http://insight.auroracoin.is', + preferences = function() {}; + + function sync() { + return new Promise(function (resolve) { + // Different APIs for Chrome and Firefox + if (typeof chrome !== 'undefined') { + var object = {}; + object[ADDRESS] = ''; + object[PRIVATE_KEY] = ''; + object[IS_ENCRYPTED] = false; + object[LAST_BALANCE] = 0; + object[EXCHANGE_RATE] = 0; + object[BTC_UNITS] = 'AUR'; + object[CURRENCY] = 'ISK'; + chrome.storage.sync.get(object, resolve); + } else { + util.message('get').then(function (message) { + if (typeof message[PRIVATE_KEY] === 'undefined') { + message[ADDRESS] = ''; + message[PRIVATE_KEY] = ''; + message[IS_ENCRYPTED] = false; + message[LAST_BALANCE] = 0; + message[EXCHANGE_RATE] = 0; + message[BTC_UNITS] = 'AUR'; + message[CURRENCY] = 'ISK'; + return util.message('save', message); + } else { + return message; + } + }).then(function (message) { + resolve(message); + }); + } + }); + } + + function get(pref) { + return function () { + return sync().then(function (values) { + return values[pref]; + }); + }; + }; + + function set(key, value) { + return new Promise(function (resolve) { + var object = {}; + object[key] = value; + // Different APIs for Chrome and Firefox + if (typeof chrome !== 'undefined') { + chrome.storage.sync.set(object, resolve); + } else { + util.message('save', object).then(resolve); + } + }); + }; + + preferences.prototype = { + + getAddress: get(ADDRESS), + setAddress: function (address) { + return set(ADDRESS, address); + }, + + getPrivateKey: get(PRIVATE_KEY), + setPrivateKey: function (privateKey) { + return set(PRIVATE_KEY, privateKey); + }, + + getIsEncrypted: get(IS_ENCRYPTED), + setIsEncrypted: function (isEncrypted) { + return set(IS_ENCRYPTED, isEncrypted); + }, + + getLastBalance: get(LAST_BALANCE), + setLastBalance: function (lastBalance) { + return set(LAST_BALANCE, lastBalance); + }, + + getExchangeRate: get(EXCHANGE_RATE), + setExchangeRate: function (exchangeRate) { + return set(EXCHANGE_RATE, exchangeRate); + }, + + getBTCUnits: get(BTC_UNITS), + setBTCUnits: function (btcUnits) { + return set(BTC_UNITS, btcUnits); + }, + + getCurrency: get(CURRENCY), + setCurrency: function (currency) { + return set(CURRENCY, currency).then(function () { + currencyManager.updateExchangeRate(); + }); + }, + + getSite: function () { + return SITE; + } + + }; + + window.preferences = new preferences(); + +})(window);