const themeCheckbox = document.getElementById('theme-checkbox')
themeCheckbox.addEventListener('change', selectTheme)
selectTheme()
- const forms = document.querySelectorAll('.form')
- for (const form of forms) {
- const cancelButtons = form.querySelectorAll('button.cancel')
+ const dialogs = document.querySelectorAll('dialog, form, .form')
+ for (const dialog of dialogs) {
+ const cancelButtons = dialog.querySelectorAll('button.cancel')
for (const cancelButton of cancelButtons) {
cancelButton.addEventListener('click', (event) => {
- cancelForm(form.id)
+ cancelDialog(dialog.id)
})
}
}
const exportMnemonicButton = document.getElementById('export-mnemonic-button')
exportMnemonicButton.addEventListener('click', async (event) => {
if (event.detail === 1) {
- requestPassword(exportMnemonic)
+ unlockThen(exportMnemonic)
}
})
const exportSeedButton = document.getElementById('export-seed-button')
exportSeedButton.addEventListener('click', async (event) => {
if (event.detail === 1) {
- requestPassword(exportSeed)
+ unlockThen(exportSeed)
}
})
setTimeout(() => {
}
case '_new': {
try {
- document.querySelector(location.hash).classList.add('hide')
+ document.querySelectorAll('.page').forEach(n => n.classList.add('hide'))
document.getElementById('wallet-new').classList.remove('hide')
} catch (err) {
walletSelect.value = ''
}
case '_import': {
try {
- document.querySelector(location.hash).classList.add('hide')
+ document.querySelectorAll('.page').forEach(n => n.classList.add('hide'))
document.getElementById('wallet-import').classList.remove('hide')
} catch (err) {
walletSelect.value = ''
option?.remove()
notify.error(err.msg)
} finally {
- await cancelForm('wallet-new')
+ await cancelDialog('wallet-new')
+ return wallet
+ }
+ }
+
+ async function importWallet () {
+ const walletSelect = document.getElementById('wallet')
+ const form = document.getElementById('wallet-import')
+ const name = document.getElementById('wallet-import-name-input').value
+ if (name.substring(0, 1) === '_') {
+ notify.error(`Wallet name cannot start with an underscore`)
+ return
+ }
+ const algorithm = document.getElementById('wallet-import-algorithm-select').value
+ const walletClass = getWalletClass(algorithm)
+ const type = document.getElementById(`wallet-import-type-select`).value
+ const secret = document.getElementById(`wallet-import-secret-input`).value
+ const salt = document.getElementById('wallet-import-salt-input')?.value
+ const password = document.getElementById('wallet-import-password-input')?.value
+ if (!password) {
+ notify.error('Password is required')
+ return
+ }
+ let wallet, option
+ try {
+ if (type === 'mnemonic') {
+ wallet = await walletClass.fromMnemonic(password, secret, salt)
+ } else if (type === 'seed') {
+ wallet = await walletClass.fromSeed(password, secret)
+ } else {
+ notify.error(`Error importing unknown wallet type`)
+ return
+ }
+ const walletData = {
+ id: wallet.id,
+ name: name || wallet.id,
+ algorithm
+ }
+ await addToStorage(`wallets`, JSON.stringify(walletData))
+
+ const select = document.getElementById('wallet')
+ option = new Option(walletData.name, walletData.id, false, true)
+ select.add(option)
+ await selectWallet()
+ } catch (err) {
+ option?.remove()
+ console.error(err)
+ notify.error(err.message)
+ } finally {
+ await cancelDialog('wallet-import')
return wallet
}
}
+ async function unlockThen (callback) {
+ const getPassword = async (event) => {
+ if (event.detail === 1) {
+ try {
+ const passwordInput = document.getElementById('unlock-modal-password-input')
+ const password = passwordInput.value
+ passwordInput.value = ''
+ document.getElementById('unlock-modal').close()
+ unlockModalOkButton.removeEventListener('click', getPassword)
+ const wallet = await loadWallet()
+ await wallet.unlock(password)
+ await callback(wallet)
+ await wallet.lock(password)
+ } catch (err) {
+ notify.error(err)
+ }
+ }
+ }
+ const unlockModalOkButton = document.getElementById('unlock-modal-ok-button')
+ unlockModalOkButton.addEventListener('click', getPassword)
+ const unlockModal = document.getElementById('unlock-modal')
+ unlockModal.showModal()
+ }
+
+ async function changePassword (newPassword) {
+ const getPassword = async (event) => {
+ if (event.detail === 1) {
+ const oldPassword = document.getElementById('unlock-modal-password-input').value
+ await cancelDialog('unlock-modal')
+ unlockModalOkButton.removeEventListener('click', getPassword)
+ const wallet = await loadWallet()
+ await wallet.unlock(oldPassword)
+ await wallet.lock(newPassword)
+ }
+ }
+ const unlockModalOkButton = document.getElementById('unlock-modal-ok-button')
+ unlockModalOkButton.addEventListener('click', getPassword)
+ const unlockModal = document.getElementById('unlock-modal')
+ unlockModal.showModal()
+ }
+
function updateWalletForm (id) {
const form = document.getElementById(id)
if (!form) {
const algorithm = document.getElementById(`${id}-algorithm-select`)
const typeContainer = document.getElementById(`${id}-type`)
const type = document.getElementById(`${id}-type-select`)
- const importValueContainer = document.getElementById(`${id}-value`)
- const importValue = document.getElementById(`${id}-value-input`)
- const importValueLabel = document.getElementById(`${id}-value-label`)
+ const importSecretContainer = document.getElementById(`${id}-secret`)
+ const importSecret = document.getElementById(`${id}-secret-input`)
+ const importSecretLabel = document.getElementById(`${id}-secret-label`)
const saltContainer = document.getElementById(`${id}-salt`)
const salt = document.getElementById(`${id}-salt-input`)
- if (algorithm.value === 'bip44' && type?.value === 'mnemonic') {
+ if (algorithm.value === 'bip44' && type?.value !== 'seed') {
saltContainer.classList.remove('hide')
} else {
saltContainer.classList.add('hide')
}
if (type?.value != null && type?.value !== '') {
- importValueContainer.classList.remove('hide')
+ importSecretContainer.classList.remove('hide')
}
- if (importValueLabel) {
- importValueLabel.textContent = type.value
+ if (importSecretLabel) {
+ importSecretLabel.textContent = type.value
}
}
default: {
try {
const account = await loadAccount()
- createQrCode(account.address)
+ await loadBalance(account)
+ await createQrCode(account.address)
} catch (err) {
accountSelect.value = ''
console.error(err)
return account
}
- async function createAccount () {
- let account, option, wallet
- const accountSelect = document.getElementById('account')
+ async function loadBalance (a) {
+ const balanceContainer = document.getElementById('account-balance')
+ const account = new libnemo.Account(a.address)
try {
- wallet = await loadWallet()
+ await account.refresh('https://node.somenano.com/proxy')
} catch (err) {
console.error(err)
- notify.error(`Error creating account`)
- return
}
+ balanceContainer.textContent = account.balance ?? 0
+ }
+
+ async function createAccount () {
+ await unlockThen(async (wallet) => {
+ const indexInput = document.getElementById('account-new-index-input')
+ const index = indexInput?.value
+ const accounts = await wallet.accounts(+index)
+ await storeAccount(wallet.id, accounts[0])
+ })
+ }
+
+ async function storeAccount (walletId, account) {
+ let option
+ const accountSelect = document.getElementById('account')
const form = document.getElementById('account-new')
- const name = document.getElementById('account-new-name-input')?.value
+ const nameInput = document.getElementById('account-new-name-input')
+ const name = nameInput?.value
if (name?.substring(0, 1) === '_') {
notify.error(`Account name cannot start with an underscore`)
return
}
- const index = document.getElementById('account-new-index-input')?.value
- const password = document.getElementById('account-new-password-input')?.value
try {
- await wallet.unlock(password)
- const accounts = await wallet.accounts(+index)
- await wallet.lock(password)
- const account = accounts[0]
const accountData = {
- name: `${index}${name ? ': ' : ''}${name}`,
- walletId: wallet.id,
+ name: `${account.index}${name ? ': ' : ''}${name}`,
+ walletId: walletId,
index: await account.index,
address: await account.address
}
console.error(err)
notify.error(`Error creating account`)
} finally {
- await cancelForm('account-new')
+ await cancelDialog('account-new')
return account
}
}
- async function cancelForm (id) {
- await resetFormInputs()
- document.getElementById(id).classList.add('hide')
- document.querySelector(location.hash).classList.remove('hide')
+ async function cancelDialog (id) {
+ const form = document.getElementById(id)
+ await resetInputs()
+ try {
+ form.close()
+ } catch (err) {
+ form.classList.add('hide')
+ document.querySelector(location.hash).classList.remove('hide')
+ }
}
- async function resetFormInputs () {
- const fields = document.querySelectorAll('main form input, main form select')
+ async function resetInputs () {
+ const fields = document.querySelectorAll('main input, main select')
for (const field of fields) {
field.value = ''
}
qrCode.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4AQMAAADW3v7MAAAABlBMVEUAAAD///+l2Z/dAAAC0klEQVR42u3cPQ6CMBiAYYWFjStwE6+mN+Mq3oCVxKQO4k8N6ABNWvK8G1+HPuuXNBzDIYuOHBwcHBwcHBwcHBwcHMU6rt3cyanf/raxmZu2AwcHBwcHBwcHBwcHR3GOdoiPLudUjvoWj6bLOTg4ODg4ODg4ODg4ynO8d62Q2BGqj52Rg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4Oj986Rg4ODg4ODg4ODg4NjlWMm///g4ODg4ODg4ODg4OBYcuQQBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHB8e+Hdcu/g4cHBwcHBwcHBwcHBz5Otrhaag4ODg4ODg4ODg4ODjKdnzvWo9O/faOsTnUNw4ODg4ODg4ODg4Ojj04XrvW1OWcxPF3n+Pg4ODg4ODg4ODg4CjIMTbPWUjjiOLg4ODg4ODg4ODg4OBYFwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHB8feHVEcHBwcHBwcHBwcHBwcvx0zcXBwcHBwcHBwcHBwcCw5coiDg4ODg4ODg4ODg4ODY013wW3C00//4ZIAAAAASUVORK5CYII='
}
- async function requestPassword (callback) {
- const dialog = document.createElement('dialog')
- dialog.classList.add('flex-y', 'center', 'mid')
- document.body.appendChild(dialog)
-
- const label = document.createElement('label')
- label.textContent = 'Unlock wallet to continue'
- label.id = 'unlock-modal-password-label'
- label.for = 'unlock-modal-password-input'
- dialog.appendChild(label)
-
- const input = document.createElement('input')
- input.type = 'password'
- input.placeholder = 'Password'
- input.id = 'unlock-modal-password-input'
- dialog.appendChild(input)
-
- const buttons = document.createElement('div')
- buttons.classList.add('flex-x', 'center')
- dialog.appendChild(buttons)
-
- const cancel = document.createElement('button')
- cancel.textContent = 'Cancel'
- cancel.addEventListener('click', (event) => {
- input.value = ''
- dialog.close()
- dialog.remove()
- })
- buttons.appendChild(cancel)
-
- const ok = document.createElement('button')
- ok.textContent = 'OK'
- ok.addEventListener('click', (event) => {
- if (event.detail === 1) {
- const password = input.value
- cancel.click()
- callback(password)
- }
- })
- buttons.appendChild(ok)
-
- dialog.showModal()
- }
-
- async function exportMnemonic (password) {
+ async function exportMnemonic (wallet) {
try {
- const wallet = await loadWallet()
- await wallet.unlock(password)
- const mnemonic = wallet.mnemonic
- await wallet.lock(password)
- notify.info(mnemonic)
+ notify.info(wallet.mnemonic)
} catch (err) {
console.error(err)
notify.error(`Error exporting mnemonic`)
}
}
- async function exportSeed (password) {
+ async function exportSeed (wallet) {
try {
- const wallet = await loadWallet()
- await wallet.unlock(password)
- const seed = wallet.seed
- await wallet.lock(password)
- notify.info(seed)
+ notify.info(wallet.seed)
} catch (err) {
console.error(err)
notify.error(`Error exporting seed`)
.page {
height: 100%;
}
- .page.form {
- z-index: 10;
- }
.hide {
display: none;
}
<body onhashchange="switchPage(event)" style="visibility:hidden;">
<div id="notifications"></div>
<header>
- <form>
-
- <!-- Wallet Select -->
-
- <div id="wallets" class="grid">
- <label for="wallet" class="col-3">Wallet</label>
- <select id="wallet" class="col-9" onchange="selectWallet()">
- <option disabled selected value>Choose...</option>
- <hr />
- <option value="_new">New</option>
- <option value="_import">Import</option>
- <hr />
- <option disabled value="_ledger">Ledger</option>
- <hr />
- </select>
- </div>
- <!-- Account Select -->
+ <!-- Wallet Select -->
- <div id="accounts" class="grid">
- <label for="account" class="col-3">Account</label>
- <select id="account" class="col-9" disabled onchange="selectAccount()">
- <option disabled selected value>Choose...</option>
- <hr />
- <option value="_new">New</option>
- <hr />
- </select>
- </div>
+ <div id="wallets" class="grid">
+ <label for="wallet" class="col-3">Wallet</label>
+ <select id="wallet" class="col-9" onchange="selectWallet()">
+ <option disabled selected value>Choose...</option>
+ <hr />
+ <option value="_new">New</option>
+ <option value="_import">Import</option>
+ <hr />
+ <option disabled value="_ledger">Ledger</option>
+ <hr />
+ </select>
+ </div>
+
+ <!-- Account Select -->
+
+ <div id="accounts" class="grid">
+ <label for="account" class="col-3">Account</label>
+ <select id="account" class="col-9" disabled onchange="selectAccount()">
+ <option disabled selected value>Choose...</option>
+ <hr />
+ <option value="_new">New</option>
+ <hr />
+ </select>
+ </div>
+
+ <!-- Account Balance -->
+
+ <div id="account-balance">
+ X0
+ </div>
- </form>
</header>
<!-- Main Content Pages -->
<option value="seed">Import seed</option>
</select>
</div>
- <div id="wallet-import-value" class="flex-x left mid hide">
- <label id="wallet-import-value-label" for="wallet-import-value-input"
+ <div id="wallet-import-secret" class="flex-x left mid hide">
+ <label id="wallet-import-secret-label" for="wallet-import-secret-input"
style="text-transform:capitalize"></label>
- <input id="wallet-import-value-input" name="wallet-import-value" type="password" autocomplete="off"
+ <input id="wallet-import-secret-input" name="wallet-import-secret" type="password" autocomplete="off"
placeholder="" required />
</div>
<div id="wallet-import-salt" class="flex-x left mid hide">
<input id="account-new-index-input" name="account-new-index" type="number" autocomplete="off" min="0"
max="2147483647" required value="0" />
</div>
- <div id="account-new-password" class="flex-x left mid">
- <label id="account-new-password-label" for="account-new-password-input">Unlock</label>
- <input id="account-new-password-input" name="account-new-password" autocomplete="off" type="password"
- placeholder="Password" required />
- </div>
<div class="grid">
<div class="col-3"></div>
<button id="account-new-cancel" class="cancel col-3" type="reset">Cancel</button>
- <button id="account-new-ok" class="col-3" onclick="createAccount().then(a => { console.log(a) })">OK</button>
+ <button id="account-new-ok" class="col-3" onclick="createAccount()">OK</button>
</div>
</div>
</div>
+ <!-- Unlock Form -->
+
+ <dialog id="unlock-modal" class="">
+ <div class="flex-y center mid">
+ <label id="unlock-modal-password-label" for="unlock-modal-password-input">Unlock wallet to continue</label>
+ <input id="unlock-modal-password-input" name="unlock-modal-password" autocomplete="off" type="password"
+ placeholder="Password" required />
+ <div class="grid">
+ <div class="col-3"></div>
+ <button id="unlock-modal-cancel-button" class="cancel col-3" type="reset">Cancel</button>
+ <button id="unlock-modal-ok-button" class="col-3">OK</button>
+ </div>
+ </div>
+ </dialog>
+
<!-- Exchange Page -->
<div id="exchange" class="page hide">