mirror of
https://github.com/andrei0x309/clear-wallet.git
synced 2024-11-18 23:41:10 +00:00
dev: 1.0.3
This commit is contained in:
parent
1a4b531ae2
commit
6c18b2841d
@ -41,6 +41,12 @@ switch (route?.query?.route ?? "") {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "wallet-error": {
|
||||||
|
router.push({
|
||||||
|
path: `/wallet-error"/${rid}/${param}`
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
router.push({ path: "/", })
|
router.push({ path: "/", })
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { getSelectedNetwork, numToHexStr } from "@/utils/platform";
|
||||||
|
|
||||||
const allowedMethods = {
|
const allowedMethods = {
|
||||||
'eth_accounts': true,
|
'eth_accounts': true,
|
||||||
@ -19,18 +20,33 @@ window.addEventListener("message", (event) => {
|
|||||||
if (event.source != window)
|
if (event.source != window)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event.data.type && (event.data.type == "CLWALLET_CONTENT")) {
|
if (event.data.type && (event.data.type === "CLWALLET_CONTENT")) {
|
||||||
event.data.data.resId = event.data.resId
|
event.data.data.resId = event.data.resId
|
||||||
if((event?.data?.data?.method ?? 'x') in allowedMethods) {
|
if((event?.data?.data?.method ?? 'x') in allowedMethods) {
|
||||||
chrome.runtime.sendMessage(event.data.data, (res) => {
|
chrome.runtime.sendMessage(event.data.data, (res) => {
|
||||||
const data = { type: "CLWALLET_PAGE", data: res, resId: event.data.resId };
|
const data = { type: "CLWALLET_PAGE", data: res, resId: event.data.resId, website: window?.location?.href ?? '' };
|
||||||
console.log('data back', data)
|
console.log('data back', data)
|
||||||
window.postMessage(data, "*");
|
window.postMessage(data, "*");
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
const data = { type: "CLWALLET_PAGE", data: { error: true, message: 'Unknown method requested'}, resId: event.data.resId };
|
const data = { type: "CLWALLET_PAGE", data: { error: true, message: 'Unknown method requested'}, resId: event.data.resId };
|
||||||
window.postMessage(data, "*");
|
window.postMessage(data, "*");
|
||||||
}
|
}
|
||||||
|
} else if (event.data.type && (event.data.type === "CLWALLET_PING")) {
|
||||||
|
getSelectedNetwork().then(network => {
|
||||||
|
const data = { type: "CLWALLET_PAGE_LISTENER", data: {
|
||||||
|
listener: 'connected',
|
||||||
|
data: {
|
||||||
|
chainId: numToHexStr(network.chainId ?? 0)
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
window.postMessage(data, "*");
|
||||||
|
})
|
||||||
|
} else if (event.data.type && (event.data.type === "CLWALLET_EXT_LISTNER")) {
|
||||||
|
const data = { type: "CLWALLET_PAGE_LISTENER", data: event.data.data, };
|
||||||
|
console.log('data listner', data)
|
||||||
|
window.postMessage(data, "*");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -37,11 +37,14 @@ const listner = function(event: any) {
|
|||||||
|
|
||||||
window.addEventListener("message",listner)
|
window.addEventListener("message",listner)
|
||||||
|
|
||||||
const sendMessage = (args: RequestArguments) => {
|
const sendMessage = (args: RequestArguments, ping = false) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const resId = crypto.randomUUID()
|
const resId = crypto.randomUUID()
|
||||||
promResolvers[resId] = { resolve, reject }
|
promResolvers[resId] = { resolve, reject }
|
||||||
const data = { type: "CLWALLET_CONTENT", data: args, resId};
|
const data = { type: "CLWALLET_CONTENT", data: args, resId};
|
||||||
|
if (ping) {
|
||||||
|
data.type = 'CLWALLET_PING'
|
||||||
|
}
|
||||||
console.log('data in', data)
|
console.log('data in', data)
|
||||||
window.postMessage(data, "*");
|
window.postMessage(data, "*");
|
||||||
})
|
})
|
||||||
@ -200,17 +203,22 @@ const injectWallet = (win: any) => {
|
|||||||
console.log('Clear wallet injected', (window as any).ethereum, win)
|
console.log('Clear wallet injected', (window as any).ethereum, win)
|
||||||
}
|
}
|
||||||
injectWallet(this)
|
injectWallet(this)
|
||||||
|
sendMessage({
|
||||||
|
method: 'wallet_ready'
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
// setTimeout(() => {
|
console.log('Metamask clone test');
|
||||||
// console.log('Metamask clone test');
|
// (<any>window).ethereum.request({method: 'eth_requestAccounts', params: Array(0)}).then((res: any) => { console.log(res, '111111111')});
|
||||||
// // (<any>window).ethereum.request({method: 'eth_requestAccounts', params: Array(0)}).then((res: any) => { console.log(res, '111111111')});
|
// (<any>window).ethereum.request({method: 'eth_accounts', params: Array(0)}).then((res: any) => { console.log(res, '111111111')});
|
||||||
// // (<any>window).ethereum.request({method: 'eth_accounts', params: Array(0)}).then((res: any) => { console.log(res, '111111111')});
|
// (<any>window).ethereum.request({method: 'eth_chainId', params: Array(0)}).then((res: any) => { console.log(res, '111111111')});
|
||||||
// // (<any>window).ethereum.request({method: 'eth_chainId', params: Array(0)}).then((res: any) => { console.log(res, '111111111')});
|
// (<any>window).ethereum.request({method: 'wallet_requestPermissions', params: [{eth_accounts: {}}]}).then((res: any) => { console.log(res, '111111111')});
|
||||||
// // (<any>window).ethereum.request({method: 'wallet_requestPermissions', params: [{eth_accounts: {}}]}).then((res: any) => { console.log(res, '111111111')});
|
// (<any>window).ethereum.request({method: 'net_version', params: []}).then((res: any) => { console.log(res, '111111111')});
|
||||||
// // (<any>window).ethereum.request({method: 'net_version', params: []}).then((res: any) => { console.log(res, '111111111')});
|
// (<any>window).ethereum.request({method: 'wallet_switchEthereumChain', params: [{chainId: "0x99"}]}).then((res: any) => { console.log(res, '111111111')});
|
||||||
// // (<any>window).ethereum.request({method: 'wallet_switchEthereumChain', params: [{chainId: "0x99"}]}).then((res: any) => { console.log(res, '111111111')});
|
(<any>window).ethereum.on('connect', ((a: any, b: any) => console.log('connect', a, b)));
|
||||||
// }, 3500)
|
(<any>window).ethereum.on('accountsChanged', ((a: any, b: any) => console.log('accountsChanged', a, b)));
|
||||||
|
(<any>window).ethereum.on('chainChanged', ((a: any, b: any) => console.log('chainChanged', a, typeof a)));
|
||||||
|
}, 3500)
|
||||||
|
|
||||||
// console.log( (window as any).ethereum.request({method: 'eth_chainId'}))
|
// console.log( (window as any).ethereum.request({method: 'eth_chainId'}))
|
7
src/extension/listners.ts
Normal file
7
src/extension/listners.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { listnerType } from '@/extension/types'
|
||||||
|
|
||||||
|
export const triggerListner = ( type: listnerType, listnerData: any ) => {
|
||||||
|
const data = { type: "CLWALLET_EXT_LISTNER", data: { listner: type, data: listnerData } }
|
||||||
|
window.postMessage(data, "*")
|
||||||
|
console.log('trigger', data)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { getAccounts, getSelectedAccount, getSelectedNetwork, smallRandomString, getSettings, clearPk, openTab, getUrl } from '@/utils/platform';
|
import { getAccounts, getSelectedAccount, getSelectedNetwork, smallRandomString, getSettings, clearPk, openTab, getUrl, addToHistory } from '@/utils/platform';
|
||||||
import { userApprove, userReject, rIdWin, rIdData } from '@/extension/userRequest'
|
import { userApprove, userReject, rIdWin, rIdData } from '@/extension/userRequest'
|
||||||
import { signMsg, getBalance, getBlockNumber, estimateGas, sendTransaction, getGasPrice, getBlockByNumber } from '@/utils/wallet'
|
import { signMsg, getBalance, getBlockNumber, estimateGas, sendTransaction, getGasPrice, getBlockByNumber } from '@/utils/wallet'
|
||||||
import type { RequestArguments } from '@/extension/types'
|
import type { RequestArguments } from '@/extension/types'
|
||||||
@ -164,7 +164,22 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
const [account, network] = await Promise.all([getSelectedAccount(), getSelectedNetwork()])
|
const [account, network] = await Promise.all([getSelectedAccount(), getSelectedNetwork()])
|
||||||
if(!account || !network) {
|
if(!account || !('address' in account)) {
|
||||||
|
await chrome.windows.create({
|
||||||
|
height: 450,
|
||||||
|
width: 400,
|
||||||
|
url: chrome.runtime.getURL(`index.html?route=sign-tx¶m=${encodeURIComponent('No account is selected you need to have an account selected before trying to make a transaction')}&rid=${String(message?.resId ?? '')}`),
|
||||||
|
type: 'popup'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(!network || !('chainId' in network)) {
|
||||||
|
await chrome.windows.create({
|
||||||
|
height: 450,
|
||||||
|
width: 400,
|
||||||
|
url: chrome.runtime.getURL(`index.html?route=sign-tx¶m=${encodeURIComponent('No network is selected you need to have a network selected before trying to make a transaction')}&rid=${String(message?.resId ?? '')}`),
|
||||||
|
type: 'popup'
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
params.from = account.address
|
params.from = account.address
|
||||||
@ -197,6 +212,13 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
|
|||||||
sendResponse(tx)
|
sendResponse(tx)
|
||||||
const buttons = {} as any
|
const buttons = {} as any
|
||||||
const network = await getSelectedNetwork()
|
const network = await getSelectedNetwork()
|
||||||
|
addToHistory({
|
||||||
|
date: Date.now(),
|
||||||
|
txHash: tx.hash,
|
||||||
|
chainId: network.chainId,
|
||||||
|
...(network.explorer ? {txUrl: `${network.explorer}/tx/${tx.hash}`.replace('//', '/') } : {}),
|
||||||
|
webiste: (message?.website)
|
||||||
|
})
|
||||||
const notificationId = crypto.randomUUID()
|
const notificationId = crypto.randomUUID()
|
||||||
if(network?.explorer) {
|
if(network?.explorer) {
|
||||||
notificationUrl = `${network.explorer}/tx/${tx.hash}`.replace('//', '/')
|
notificationUrl = `${network.explorer}/tx/${tx.hash}`.replace('//', '/')
|
||||||
@ -246,6 +268,18 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
|
|||||||
case ('personal_sign' || 'eth_sign'): {
|
case ('personal_sign' || 'eth_sign'): {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
const account = await getSelectedAccount()
|
||||||
|
|
||||||
|
if(!account || !('address' in account)) {
|
||||||
|
await chrome.windows.create({
|
||||||
|
height: 450,
|
||||||
|
width: 400,
|
||||||
|
url: chrome.runtime.getURL(`index.html?route=sign-tx¶m=${encodeURIComponent('No account is selected you need to have an account selected before trying sign a message')}&rid=${String(message?.resId ?? '')}`),
|
||||||
|
type: 'popup'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
chrome.windows.create({
|
chrome.windows.create({
|
||||||
height: 450,
|
height: 450,
|
||||||
|
@ -27,6 +27,7 @@ export interface RequestArguments {
|
|||||||
method: string;
|
method: string;
|
||||||
params?: any[];
|
params?: any[];
|
||||||
resId?: string
|
resId?: string
|
||||||
|
website?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProviderRpcError extends Error {
|
export interface ProviderRpcError extends Error {
|
||||||
@ -51,4 +52,14 @@ export interface Settings {
|
|||||||
theme: 'system' | 'light' | 'dark'
|
theme: 'system' | 'light' | 'dark'
|
||||||
lastLock: number
|
lastLock: number
|
||||||
lockOutBlocked: boolean
|
lockOutBlocked: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type listnerType = 'accountsChanged' | 'connect' | 'disconnect' | 'chainChanged'
|
||||||
|
|
||||||
|
export interface HistoryItem {
|
||||||
|
date: number
|
||||||
|
txUrl?: string
|
||||||
|
chainId?: number
|
||||||
|
webiste?: string
|
||||||
|
txHash: string
|
||||||
|
}
|
||||||
|
@ -23,6 +23,10 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
path: '/contract-error/:rid/:param/:contract',
|
path: '/contract-error/:rid/:param/:contract',
|
||||||
component: () => import('@/views/ContractError.vue'),
|
component: () => import('@/views/ContractError.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/wallet-error/:rid/:error',
|
||||||
|
component: () => import('@/views/WalletError.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/tabs/',
|
path: '/tabs/',
|
||||||
component: AppTabs,
|
component: AppTabs,
|
||||||
@ -59,6 +63,10 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
path: 'add-account',
|
path: 'add-account',
|
||||||
component: () => import('@/views/AddAccount.vue'),
|
component: () => import('@/views/AddAccount.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'add-account/edit/:address',
|
||||||
|
component: () => import('@/views/AddAccount.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'add-network',
|
path: 'add-network',
|
||||||
component: () => import('@/views/AddNetwork.vue'),
|
component: () => import('@/views/AddNetwork.vue'),
|
||||||
|
11
src/utils/misc.ts
Normal file
11
src/utils/misc.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const exportFile = (fileName: string, content: string, type = 'json') => {
|
||||||
|
const link = document.createElement('a')
|
||||||
|
const blob = new Blob([content], { type: `text/${type};charset=utf-8;` })
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
link.setAttribute('href', url)
|
||||||
|
link.setAttribute('download', fileName)
|
||||||
|
link.style.visibility = 'hidden'
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import type { Network, Account, Prices, Settings, Networks } from '@/extension/types'
|
import type { Network, Account, Prices, Settings, Networks, HistoryItem } from '@/extension/types'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
@ -79,6 +79,21 @@ export const getPrices = async (): Promise<Prices> => {
|
|||||||
return (await storageGet('prices'))?.prices ?? {} as unknown as Prices
|
return (await storageGet('prices'))?.prices ?? {} as unknown as Prices
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getHistory = async (): Promise<HistoryItem[]> => {
|
||||||
|
return (await storageGet('history'))?.history ?? [] as unknown as Prices
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addToHistory = async (historyItem: HistoryItem): Promise<void> => {
|
||||||
|
const history = await getHistory()
|
||||||
|
if (history.length >= 100) {
|
||||||
|
history.pop()
|
||||||
|
history.unshift(historyItem)
|
||||||
|
} else {
|
||||||
|
history.unshift(historyItem)
|
||||||
|
}
|
||||||
|
await storageSave('history', history)
|
||||||
|
}
|
||||||
|
|
||||||
export const getSettings = async (): Promise<Settings> => {
|
export const getSettings = async (): Promise<Settings> => {
|
||||||
return (await storageGet('settings'))?.settings ?? defaultSettings as unknown as Settings
|
return (await storageGet('settings'))?.settings ?? defaultSettings as unknown as Settings
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ export const estimateGas = async ({to = '', from = '', data = '', value = '0x0'
|
|||||||
return await provider.estimateGas({to, from, data, value})
|
return await provider.estimateGas({to, from, data, value})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const sendTransaction = async ({ data= '', gas='0x0', to='', from='', value='0x0', gasPrice='0x0'}:
|
export const sendTransaction = async ({ data= '', gas='0x0', to='', from='', value='0x0', gasPrice='0x0'}:
|
||||||
{to: string, from: string, data: string, value: string, gas: string, gasPrice: string},
|
{to: string, from: string, data: string, value: string, gas: string, gasPrice: string},
|
||||||
gasEstimate: Promise<BigNumber> | null = null, pGasPrice : Promise<BigNumber> | null) => {
|
gasEstimate: Promise<BigNumber> | null = null, pGasPrice : Promise<BigNumber> | null) => {
|
||||||
|
@ -48,31 +48,30 @@ async function getKey(passwordBytes: Uint8Array) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const encrypt = async (password: string, data:string) => {
|
export const getCryptoParams = async(password: string): Promise<{ key: CryptoKey, iv: any }> => {
|
||||||
|
const enc = new TextEncoder()
|
||||||
|
const encKey = enc.encode(password)
|
||||||
|
return { key: await getKey(encKey), iv:await getIv() }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const encrypt = async (data: string, cryptoParams: { key: CryptoKey, iv: any }) => {
|
||||||
const enc = new TextEncoder()
|
const enc = new TextEncoder()
|
||||||
const encData = enc.encode(data)
|
const encData = enc.encode(data)
|
||||||
const encKey = enc.encode(password)
|
|
||||||
const key = await getKey(encKey)
|
|
||||||
const iv = await getIv()
|
|
||||||
const encResult = await crypto.subtle.encrypt(
|
const encResult = await crypto.subtle.encrypt(
|
||||||
{
|
{
|
||||||
name: "AES-GCM",
|
name: "AES-GCM",
|
||||||
iv,
|
iv: cryptoParams.iv,
|
||||||
},
|
},
|
||||||
key,
|
cryptoParams.key,
|
||||||
encData,
|
encData,
|
||||||
)
|
)
|
||||||
return JSON.stringify(new Uint8Array(encResult))
|
return JSON.stringify(new Uint8Array(encResult))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decrypt = async (encryptedData: string, password: string) => {
|
export const decrypt = async (encryptedData: string, cryptoParams: { key: CryptoKey, iv: any }) => {
|
||||||
const enc = new TextEncoder()
|
const encryptedUint = new Uint8Array(Object.values(JSON.parse(encryptedData)));
|
||||||
const encKey = enc.encode(password)
|
|
||||||
const key = await getKey(encKey)
|
|
||||||
const iv = await getIv()
|
|
||||||
const encryptedUint= new Uint8Array(Object.values(JSON.parse(encryptedData)));
|
|
||||||
const contentBytes = new Uint8Array(
|
const contentBytes = new Uint8Array(
|
||||||
await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, encryptedUint)
|
await crypto.subtle.decrypt({ name: "AES-GCM", iv:cryptoParams.iv }, cryptoParams.key, encryptedUint)
|
||||||
);
|
);
|
||||||
return new TextDecoder().decode(contentBytes)
|
return new TextDecoder().decode(contentBytes)
|
||||||
}
|
}
|
@ -20,6 +20,10 @@
|
|||||||
message="Copied to clipboard"
|
message="Copied to clipboard"
|
||||||
:duration="1500"
|
:duration="1500"
|
||||||
></ion-toast>
|
></ion-toast>
|
||||||
|
<ion-item v-if="loading || accounts.length < 1">
|
||||||
|
<ion-label>No EVM accounts found</ion-label>
|
||||||
|
<ion-button @click="goToAddAccount">Add Account</ion-button>
|
||||||
|
</ion-item>
|
||||||
<ion-list v-for="account of accounts" :key="account.address">
|
<ion-list v-for="account of accounts" :key="account.address">
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
@ -32,7 +36,7 @@
|
|||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-chip>View Pk</ion-chip>
|
<ion-chip>View Pk</ion-chip>
|
||||||
<ion-chip @click="deleteAccount(account.address)">Delete</ion-chip>
|
<ion-chip @click="deleteAccount(account.address)">Delete</ion-chip>
|
||||||
<ion-chip @click="editName(account.address)">Edit Name</ion-chip>
|
<ion-chip @click="editAccount(account.address)">Edit Name</ion-chip>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
@ -60,6 +64,7 @@ import {
|
|||||||
} from "@ionic/vue";
|
} from "@ionic/vue";
|
||||||
|
|
||||||
import { addCircleOutline, copyOutline } from "ionicons/icons";
|
import { addCircleOutline, copyOutline } from "ionicons/icons";
|
||||||
|
import router from "@/router";
|
||||||
import type { Account } from '@/extension/types'
|
import type { Account } from '@/extension/types'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -102,10 +107,15 @@ export default defineComponent({
|
|||||||
await replaceAccounts([...accounts.value])
|
await replaceAccounts([...accounts.value])
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
const editName = async (name: string) => {
|
|
||||||
// do nothing
|
const editAccount = (address: string) => {
|
||||||
|
router.push(`add-account/edit/${address}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const goToAddAccount = () => {
|
||||||
|
router.push("/tabs/add-account");
|
||||||
|
};
|
||||||
|
|
||||||
onIonViewWillEnter(() => {
|
onIonViewWillEnter(() => {
|
||||||
loadData()
|
loadData()
|
||||||
})
|
})
|
||||||
@ -118,7 +128,9 @@ export default defineComponent({
|
|||||||
copyAddress,
|
copyAddress,
|
||||||
getToastRef,
|
getToastRef,
|
||||||
deleteAccount,
|
deleteAccount,
|
||||||
editName
|
editAccount,
|
||||||
|
loading,
|
||||||
|
goToAddAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
<ion-page>
|
<ion-page>
|
||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar>
|
<ion-toolbar>
|
||||||
<ion-title>Add Account</ion-title>
|
<ion-title v-if="!isEdit" >Add Account</ion-title>
|
||||||
|
<ion-title v-else >Edit Account</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
@ -15,11 +16,12 @@
|
|||||||
<ion-label>Get Random Name</ion-label>
|
<ion-label>Get Random Name</ion-label>
|
||||||
<ion-button @click="getRandomName" >Generate</ion-button>
|
<ion-button @click="getRandomName" >Generate</ion-button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item v-if="!isEdit">
|
||||||
<ion-icon style="margin-right: 0.5rem;" @click="paste('pasteRpc')" :icon="clipboardOutline" button /><ion-label>PK</ion-label>
|
<ion-icon style="margin-right: 0.5rem;" @click="paste('pastePk')" :icon="clipboardOutline" button/>
|
||||||
|
<ion-label button>PK</ion-label>
|
||||||
<ion-input id="pastePk" v-model="pk"></ion-input>
|
<ion-input id="pastePk" v-model="pk"></ion-input>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item v-if="!isEdit">
|
||||||
<ion-label>Get Random PK</ion-label>
|
<ion-label>Get Random PK</ion-label>
|
||||||
<ion-button @click="generateRandomPk" >Generate</ion-button>
|
<ion-button @click="generateRandomPk" >Generate</ion-button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
@ -40,10 +42,12 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from "vue";
|
import { defineComponent, ref } from "vue";
|
||||||
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonInput, IonButton, IonAlert, IonIcon } from "@ionic/vue";
|
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonInput, IonButton, IonAlert, IonIcon, onIonViewWillEnter } from "@ionic/vue";
|
||||||
import { ethers } from "ethers"
|
import { ethers } from "ethers"
|
||||||
import { saveSelectedAccount, getAccounts, saveAccount, getRandomPk, smallRandomString, paste } from "@/utils/platform";
|
import { saveSelectedAccount, getAccounts, saveAccount, getRandomPk, smallRandomString, paste } from "@/utils/platform";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import type { Account } from '@/extension/types'
|
||||||
|
|
||||||
import { clipboardOutline } from "ionicons/icons";
|
import { clipboardOutline } from "ionicons/icons";
|
||||||
|
|
||||||
@ -54,12 +58,27 @@ export default defineComponent({
|
|||||||
const pk = ref('')
|
const pk = ref('')
|
||||||
const alertOpen = ref(false)
|
const alertOpen = ref(false)
|
||||||
const alertMsg = ref('')
|
const alertMsg = ref('')
|
||||||
|
const route = useRoute()
|
||||||
|
const isEdit = route.path.includes('/edit')
|
||||||
|
const paramAddress = route.params.address ?? ""
|
||||||
|
let accountsProm: Promise<Account[] | undefined>
|
||||||
|
|
||||||
const resetFields = () => {
|
const resetFields = () => {
|
||||||
name.value = ''
|
name.value = ''
|
||||||
pk.value = ''
|
pk.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onIonViewWillEnter(async () => {
|
||||||
|
if(isEdit && paramAddress) {
|
||||||
|
accountsProm = getAccounts()
|
||||||
|
const accounts = await accountsProm as Account[]
|
||||||
|
const acc = accounts.find(account => account.address === paramAddress)
|
||||||
|
if(acc) {
|
||||||
|
name.value = acc.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const onAddAccount = async () => {
|
const onAddAccount = async () => {
|
||||||
let p1 = Promise.resolve()
|
let p1 = Promise.resolve()
|
||||||
if(pk.value.length === 64){
|
if(pk.value.length === 64){
|
||||||
@ -73,7 +92,10 @@ export default defineComponent({
|
|||||||
|
|
||||||
|
|
||||||
const wallet = new ethers.Wallet(pk.value)
|
const wallet = new ethers.Wallet(pk.value)
|
||||||
const accounts = await getAccounts()
|
if(!accountsProm) {
|
||||||
|
accountsProm = getAccounts()
|
||||||
|
}
|
||||||
|
const accounts = await accountsProm as Account[]
|
||||||
if((accounts.length ?? 0) < 1 ){
|
if((accounts.length ?? 0) < 1 ){
|
||||||
p1 = saveSelectedAccount({
|
p1 = saveSelectedAccount({
|
||||||
address: wallet.address,
|
address: wallet.address,
|
||||||
@ -94,7 +116,11 @@ export default defineComponent({
|
|||||||
encPk: ''
|
encPk: ''
|
||||||
})
|
})
|
||||||
await Promise.all([p1, p2])
|
await Promise.all([p1, p2])
|
||||||
router.push('/')
|
if(isEdit) {
|
||||||
|
router.push('accounts')
|
||||||
|
}else {
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
resetFields()
|
resetFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +133,11 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
router.push('/')
|
if(isEdit) {
|
||||||
|
router.push('accounts')
|
||||||
|
}else {
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -120,7 +150,8 @@ export default defineComponent({
|
|||||||
generateRandomPk,
|
generateRandomPk,
|
||||||
getRandomName,
|
getRandomName,
|
||||||
clipboardOutline,
|
clipboardOutline,
|
||||||
paste
|
paste,
|
||||||
|
isEdit
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,11 @@ export default defineComponent({
|
|||||||
networks[chainId.value] = network
|
networks[chainId.value] = network
|
||||||
const p2 = replaceNetworks(networks)
|
const p2 = replaceNetworks(networks)
|
||||||
await Promise.all([p1, p2])
|
await Promise.all([p1, p2])
|
||||||
router.push('/')
|
if(isEdit) {
|
||||||
|
router.push('networks')
|
||||||
|
}else {
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
resetFields()
|
resetFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +202,11 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
|
if(isEdit) {
|
||||||
|
router.push('networks')
|
||||||
|
}else {
|
||||||
router.push('/')
|
router.push('/')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fillTemplate = (network: typeof mainNets[1] ) =>{
|
const fillTemplate = (network: typeof mainNets[1] ) =>{
|
||||||
|
@ -5,16 +5,48 @@
|
|||||||
<ion-title>Assets</ion-title>
|
<ion-title>Assets</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
<ion-content class="ion-padding">
|
||||||
<ion-content class="ion-padding">Schedule Tab</ion-content>
|
1
|
||||||
|
</ion-content>
|
||||||
|
<ion-content class="ion-padding">
|
||||||
|
2
|
||||||
|
</ion-content>
|
||||||
|
<ion-content class="ion-padding">
|
||||||
|
3
|
||||||
|
</ion-content>
|
||||||
</ion-page>
|
</ion-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent, Ref, ref } from "vue";
|
||||||
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from "@ionic/vue";
|
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, onIonViewWillEnter } from "@ionic/vue";
|
||||||
|
import { getSelectedAccount } from "@/utils/platform"
|
||||||
|
import type { Account } from "@/extension/types"
|
||||||
|
|
||||||
|
const yupAssetsApi = 'https://api.yup.io/profile'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar },
|
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar },
|
||||||
|
setup: () => {
|
||||||
|
const selectedAccount = ref({}) as Ref<Account>
|
||||||
|
const assets = ref({})
|
||||||
|
const loading = ref(true)
|
||||||
|
const isError = ref(false)
|
||||||
|
const noAssets = ref(false)
|
||||||
|
|
||||||
|
onIonViewWillEnter(async () => {
|
||||||
|
selectedAccount.value = await getSelectedAccount()
|
||||||
|
const req = await fetch(`${yupAssetsApi}/${selectedAccount.value.address}`)
|
||||||
|
if(req.ok) {
|
||||||
|
assets.value = (await req.json()) ?? {}
|
||||||
|
if(!('poaps' in assets.value) && !('tokens' in assets.value) && !('nfts' in assets.value)) {
|
||||||
|
noAssets.value = true
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
isError.value = true
|
||||||
|
}
|
||||||
|
loading .value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,15 +6,29 @@
|
|||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content class="ion-padding">Schedule Tab</ion-content>
|
<ion-content class="ion-padding">Not implemented</ion-content>
|
||||||
</ion-page>
|
</ion-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent, Ref, ref } from "vue";
|
||||||
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from "@ionic/vue";
|
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, onIonViewWillEnter } from "@ionic/vue";
|
||||||
|
import { getHistory } from '@/utils/platform'
|
||||||
|
import type { HistoryItem } from '@/extension/types'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar },
|
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar },
|
||||||
|
setup: () => {
|
||||||
|
const history = ref([]) as Ref<HistoryItem[]>;
|
||||||
|
const loading = ref(true)
|
||||||
|
onIonViewWillEnter(async () => {
|
||||||
|
history.value = await getHistory()
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
history,
|
||||||
|
loading
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<ion-label>Selected Account: {{ selectedAccount?.name }}</ion-label>
|
<ion-label>Selected Account: {{ selectedAccount?.name }}</ion-label>
|
||||||
<ion-button @click="accountsModal = true">Select</ion-button>
|
<ion-button @click="accountsModal = true">Select</ion-button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item button @click="copyAddress(selectedAccount.address, getToastRef())">
|
<ion-item button @click="copyAddress(selectedAccount?.address, getToastRef())">
|
||||||
<p style="font-size: 0.7rem">{{ selectedAccount?.address }}</p>
|
<p style="font-size: 0.7rem">{{ selectedAccount?.address }}</p>
|
||||||
<ion-icon style="margin-left: 0.5rem" :icon="copyOutline"></ion-icon>
|
<ion-icon style="margin-left: 0.5rem" :icon="copyOutline"></ion-icon>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
@ -67,7 +67,7 @@
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content class="ion-padding">
|
<ion-content class="ion-padding">
|
||||||
<ion-list style="margin-bottom: 4rem">
|
<ion-list style="margin-bottom: 4rem">
|
||||||
<ion-radio-group :value="selectedAccount.address">
|
<ion-radio-group :value="selectedAccount?.address ?? ''">
|
||||||
<ion-list-header>
|
<ion-list-header>
|
||||||
<ion-label>Accounts</ion-label>
|
<ion-label>Accounts</ion-label>
|
||||||
</ion-list-header>
|
</ion-list-header>
|
||||||
@ -168,10 +168,12 @@ import {
|
|||||||
replaceNetworks,
|
replaceNetworks,
|
||||||
getUrl,
|
getUrl,
|
||||||
saveSelectedNetwork,
|
saveSelectedNetwork,
|
||||||
|
numToHexStr
|
||||||
} from "@/utils/platform";
|
} from "@/utils/platform";
|
||||||
import type { Network, Account, Networks } from "@/extension/types";
|
import type { Network, Account, Networks } from "@/extension/types";
|
||||||
import { mainNets } from "@/utils/networks";
|
import { mainNets } from "@/utils/networks";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
|
import { triggerListner } from '@/extension/listners'
|
||||||
|
|
||||||
import { copyOutline } from "ionicons/icons";
|
import { copyOutline } from "ionicons/icons";
|
||||||
|
|
||||||
@ -251,8 +253,9 @@ export default defineComponent({
|
|||||||
// console.log(({ [address]: accounts.value[address], ...accounts.value}))
|
// console.log(({ [address]: accounts.value[address], ...accounts.value}))
|
||||||
accounts.value.splice(findIndex, 1);
|
accounts.value.splice(findIndex, 1);
|
||||||
accounts.value.splice(0,0,selectedAccount.value)
|
accounts.value.splice(0,0,selectedAccount.value)
|
||||||
await replaceAccounts([...accounts.value])
|
const newAccounts = [...accounts.value]
|
||||||
|
await replaceAccounts(newAccounts)
|
||||||
|
triggerListner('accountsChanged', newAccounts.map(a => a.address))
|
||||||
}
|
}
|
||||||
accountsModal.value = false;
|
accountsModal.value = false;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@ -266,6 +269,7 @@ export default defineComponent({
|
|||||||
Object.assign({ [chainId]: networks.value[chainId] }, networks.value)
|
Object.assign({ [chainId]: networks.value[chainId] }, networks.value)
|
||||||
);
|
);
|
||||||
selectedNetwork.value = networks.value[chainId];
|
selectedNetwork.value = networks.value[chainId];
|
||||||
|
triggerListner('chainChanged', numToHexStr(chainId))
|
||||||
}
|
}
|
||||||
networksModal.value = false;
|
networksModal.value = false;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content class="ion-padding">
|
<ion-content class="ion-padding">
|
||||||
|
<ion-item v-if="loading || Object.keys(networks).length < 1">
|
||||||
|
<ion-label>No EVM Networks found</ion-label>
|
||||||
|
<ion-button @click="goToAddNetwork">Add Network</ion-button>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
<ion-list v-for="network of networks" :key="network.chainId">
|
<ion-list v-for="network of networks" :key="network.chainId">
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-avatar v-if="(mainNets as any)[network.chainId]?.icon" style="margin-right: 1rem; width: 1.8rem; height:1.8rem;">
|
<ion-avatar v-if="(mainNets as any)[network.chainId]?.icon" style="margin-right: 1rem; width: 1.8rem; height:1.8rem;">
|
||||||
@ -101,6 +106,12 @@ export default defineComponent({
|
|||||||
router.push(`add-network/edit/${chainId}`)
|
router.push(`add-network/edit/${chainId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const goToAddNetwork = () => {
|
||||||
|
router.push("/tabs/add-network");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
onIonViewWillEnter(() => {
|
onIonViewWillEnter(() => {
|
||||||
loadData()
|
loadData()
|
||||||
})
|
})
|
||||||
@ -115,7 +126,9 @@ export default defineComponent({
|
|||||||
getUrl,
|
getUrl,
|
||||||
mainNets,
|
mainNets,
|
||||||
deleteNetwork,
|
deleteNetwork,
|
||||||
editNetwork
|
editNetwork,
|
||||||
|
loading,
|
||||||
|
goToAddNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
</ion-item>
|
</ion-item>
|
||||||
<div class="ion-padding" slot="content">
|
<div class="ion-padding" slot="content">
|
||||||
<ion-list>
|
<ion-list>
|
||||||
<ion-list>
|
<ion-item v-if="noAccounts">You need at least one account to touch this settings</ion-item>
|
||||||
|
<ion-list :disabled="noAccounts">
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label>Enable Storage Encryption</ion-label>
|
<ion-label>Enable Storage Encryption</ion-label>
|
||||||
<ion-toggle :key="updateKey" @ion-change="changeEncryption" slot="end" :checked="settings.s.enableStorageEnctyption"></ion-toggle>
|
<ion-toggle :key="updateKey" @ion-change="changeEncryption" slot="end" :checked="settings.s.enableStorageEnctyption"></ion-toggle>
|
||||||
@ -24,16 +25,16 @@
|
|||||||
</ion-list>
|
</ion-list>
|
||||||
<ion-item :disabled="!settings.s.enableStorageEnctyption">
|
<ion-item :disabled="!settings.s.enableStorageEnctyption">
|
||||||
<ion-label>Enable Auto Lock</ion-label>
|
<ion-label>Enable Auto Lock</ion-label>
|
||||||
<ion-toggle :key="updateKey" slot="end" :checked="settings.s.lockOutEnabled"></ion-toggle>
|
<ion-toggle :key="updateKey" @ion-change="changeAutoLock" slot="end" :checked="settings.s.lockOutEnabled"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-list>
|
<ion-list>
|
||||||
<ion-item :disabled="!settings.s.enableStorageEnctyption || settings.s.lockOutEnabled">
|
<ion-item :disabled="!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled">
|
||||||
<ion-label>Auto-lock Period: (2-120) minutes</ion-label>
|
<ion-label>Auto-lock Period: (2-120) minutes</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item :disabled="!settings.s.enableStorageEnctyption || settings.s.lockOutEnabled">
|
<ion-item :disabled="!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled">
|
||||||
<ion-input :key="updateKey" v-model="settings.s.lockOutPeriod" type="number"></ion-input>
|
<ion-input :key="updateKey" v-model="settings.s.lockOutPeriod" type="number"></ion-input>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item :disabled="!settings.s.enableStorageEnctyption || settings.s.lockOutEnabled">
|
<ion-item :disabled="!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled">
|
||||||
<ion-button @click="setTime">Set Auto-lock</ion-button>
|
<ion-button @click="setTime">Set Auto-lock</ion-button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
@ -136,9 +137,10 @@
|
|||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar>
|
<ion-toolbar>
|
||||||
<ion-buttons slot="start">
|
<ion-buttons slot="start">
|
||||||
<ion-button @click="mpModal=false">Close</ion-button>
|
<ion-button @click="modalGetPassword?.reject ? (() => { modalGetPassword.reject(); modalGetPassword = null })() : mpModal=false">Close</ion-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
<ion-title>Create Encryption Password</ion-title>
|
<ion-title v-if="!settings.s.enableStorageEnctyption">Create Encryption Password</ion-title>
|
||||||
|
<ion-title v-else>Enter Encryption Password</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content class="ion-padding">
|
<ion-content class="ion-padding">
|
||||||
@ -166,13 +168,13 @@
|
|||||||
</ion-list>
|
</ion-list>
|
||||||
</div>
|
</div>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-button @click="confirmModal">Confirm</ion-button>
|
<ion-button @click="modalGetPassword?.resolve ? (() => { modalGetPassword.resolve(); modalGetPassword = null })() : confirmModal()">Confirm</ion-button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
</ion-modal>
|
</ion-modal>
|
||||||
<ion-alert
|
<ion-alert
|
||||||
:is-open="alertOpen"
|
:is-open="alertOpen"
|
||||||
header="Error"
|
:header="alertHeader"
|
||||||
:message="alertMsg"
|
:message="alertMsg"
|
||||||
:buttons="['OK']"
|
:buttons="['OK']"
|
||||||
@didDismiss="alertOpen=false"
|
@didDismiss="alertOpen=false"
|
||||||
@ -184,8 +186,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, reactive, Ref } from "vue";
|
import { defineComponent, ref, reactive, Ref } from "vue";
|
||||||
import { storageWipe, getSettings, setSettings, getAccounts, saveSelectedAccount, replaceAccounts } from "@/utils/platform";
|
import { storageWipe, getSettings, setSettings, getAccounts, saveSelectedAccount, replaceAccounts } from "@/utils/platform";
|
||||||
import { decrypt, encrypt } from "@/utils/webCrypto"
|
import { decrypt, encrypt, getCryptoParams } from "@/utils/webCrypto"
|
||||||
// import { Account } from '@/extension/type'
|
import { Account } from '@/extension/types'
|
||||||
|
import { exportFile } from '@/utils/misc'
|
||||||
import type { Settings } from "@/extension/types"
|
import type { Settings } from "@/extension/types"
|
||||||
import {
|
import {
|
||||||
IonContent,
|
IonContent,
|
||||||
@ -244,7 +247,11 @@ export default defineComponent({
|
|||||||
const alertMsg = ref('');
|
const alertMsg = ref('');
|
||||||
const toastState = ref(false);
|
const toastState = ref(false);
|
||||||
const toastMsg = ref('');
|
const toastMsg = ref('');
|
||||||
|
const alertHeader = ref('Error')
|
||||||
const importFile = ref(null) as unknown as Ref<HTMLInputElement>
|
const importFile = ref(null) as unknown as Ref<HTMLInputElement>
|
||||||
|
type ModalPromisePassword = null | { resolve: ((p?: unknown) => void), reject: ((p?: unknown) => void)}
|
||||||
|
const modalGetPassword = ref(null) as Ref<ModalPromisePassword>
|
||||||
|
const noAccounts = ref(true)
|
||||||
|
|
||||||
const wipeStorage = async () => {
|
const wipeStorage = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@ -266,6 +273,12 @@ export default defineComponent({
|
|||||||
updateKey.value++
|
updateKey.value++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeAutoLock = async () => {
|
||||||
|
settings.s.lockOutEnabled = !settings.s.lockOutEnabled
|
||||||
|
updateKey.value++
|
||||||
|
await saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
const changeEncryption = async () => {
|
const changeEncryption = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
mpModal.value = true
|
mpModal.value = true
|
||||||
@ -276,6 +289,7 @@ export default defineComponent({
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
if(mpPass.value.length < 3) {
|
if(mpPass.value.length < 3) {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
alertHeader.value = 'Error'
|
||||||
alertMsg.value = 'Password is too short. More than 3 characters are required.';
|
alertMsg.value = 'Password is too short. More than 3 characters are required.';
|
||||||
alertOpen.value = true
|
alertOpen.value = true
|
||||||
setEncryptToggle(settings.s.enableStorageEnctyption)
|
setEncryptToggle(settings.s.enableStorageEnctyption)
|
||||||
@ -285,14 +299,16 @@ export default defineComponent({
|
|||||||
if (!settings.s.enableStorageEnctyption) {
|
if (!settings.s.enableStorageEnctyption) {
|
||||||
if (mpPass.value !== mpConfirm.value) {
|
if (mpPass.value !== mpConfirm.value) {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
alertHeader.value = 'Error'
|
||||||
alertMsg.value = 'Password and confirm password do not match';
|
alertMsg.value = 'Password and confirm password do not match';
|
||||||
alertOpen.value = true
|
alertOpen.value = true
|
||||||
setEncryptToggle(settings.s.enableStorageEnctyption)
|
setEncryptToggle(settings.s.enableStorageEnctyption)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let accounts = await getAccounts()
|
let accounts = await getAccounts()
|
||||||
|
const cryptoParams = await getCryptoParams(mpPass.value)
|
||||||
const accProm = accounts.map(async a => {
|
const accProm = accounts.map(async a => {
|
||||||
a.encPk = await encrypt(mpPass.value, a.pk)
|
a.encPk = await encrypt(a.pk, cryptoParams)
|
||||||
a.pk = ''
|
a.pk = ''
|
||||||
return a
|
return a
|
||||||
})
|
})
|
||||||
@ -307,12 +323,14 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
let accounts = await getAccounts()
|
let accounts = await getAccounts()
|
||||||
|
const cryptoParams = await getCryptoParams(mpPass.value)
|
||||||
const accProm = accounts.map(async a => {
|
const accProm = accounts.map(async a => {
|
||||||
if(a.encPk) {
|
if(a.encPk) {
|
||||||
a.pk = await decrypt(a.encPk, mpPass.value)
|
a.pk = await decrypt(a.encPk, cryptoParams)
|
||||||
}
|
}
|
||||||
return a
|
return a
|
||||||
})
|
})
|
||||||
|
accProm.forEach( a => a.catch(e => console.log(e)) )
|
||||||
accounts = await Promise.all(accProm)
|
accounts = await Promise.all(accProm)
|
||||||
await replaceAccounts(accounts)
|
await replaceAccounts(accounts)
|
||||||
await saveSelectedAccount(accounts[0])
|
await saveSelectedAccount(accounts[0])
|
||||||
@ -323,8 +341,10 @@ export default defineComponent({
|
|||||||
mpPass.value = ''
|
mpPass.value = ''
|
||||||
mpConfirm.value = ''
|
mpConfirm.value = ''
|
||||||
mpModal.value = false
|
mpModal.value = false
|
||||||
} catch {
|
} catch(error) {
|
||||||
|
console.log(error)
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
alertHeader.value = 'Error'
|
||||||
alertMsg.value = 'Decryption failed, password is not correct.';
|
alertMsg.value = 'Decryption failed, password is not correct.';
|
||||||
alertOpen.value = true
|
alertOpen.value = true
|
||||||
setEncryptToggle(settings.s.enableStorageEnctyption)
|
setEncryptToggle(settings.s.enableStorageEnctyption)
|
||||||
@ -348,13 +368,13 @@ export default defineComponent({
|
|||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
const json = JSON.parse(event?.target?.result as string)
|
const json = JSON.parse(event?.target?.result as string)
|
||||||
if(!json.length){
|
if(!json.length){
|
||||||
return resolve({ error: 'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key" },{...}]' })
|
return resolve({ error: 'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key", "address": "0x..." },{...}]' })
|
||||||
}
|
}
|
||||||
const test = json.some((e:any) => ( !('pk' in e ) || !('name' in e) || !(e.pk.length !== 66 && e.pk.length !== 64)))
|
const test = json.some((e:any) => ( !('pk' in e ) || !('name' in e) || !('address' in e) || !(e.pk.length === 66 || e.pk.length === 64)))
|
||||||
if(test) {
|
if(test) {
|
||||||
return resolve({ error: 'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key" },{...}], Also PK must be valid' })
|
return resolve({ error: 'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key", "address": "0x..." },{...}], Also PK must be valid (66 || 64 length) !' })
|
||||||
}
|
}
|
||||||
return resolve({ error: false })
|
return resolve({ error: false, json })
|
||||||
}
|
}
|
||||||
reader.readAsText(importFile.value?.files?.[0] as File);
|
reader.readAsText(importFile.value?.files?.[0] as File);
|
||||||
|
|
||||||
@ -366,6 +386,42 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getPassword = () => {
|
||||||
|
return new Promise( (resolve, reject) => {
|
||||||
|
modalGetPassword.value = { resolve, reject }
|
||||||
|
mpModal.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const promptForPassword = async (accounts: Account[]) => {
|
||||||
|
let isCorectPass = false
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
await getPassword()
|
||||||
|
modalGetPassword.value = null
|
||||||
|
} catch {
|
||||||
|
alertHeader.value = 'Error'
|
||||||
|
alertMsg.value = "Password is required!"
|
||||||
|
alertOpen.value = true
|
||||||
|
mpModal.value = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const cryptoParams = await getCryptoParams(mpPass.value)
|
||||||
|
if(accounts?.[0]?.encPk) {
|
||||||
|
await decrypt(accounts[0].encPk, cryptoParams)
|
||||||
|
}
|
||||||
|
isCorectPass = true
|
||||||
|
} catch {
|
||||||
|
isCorectPass = false
|
||||||
|
alertHeader.value = 'Error'
|
||||||
|
alertMsg.value = "Password is wrong!"
|
||||||
|
alertOpen.value = true
|
||||||
|
}
|
||||||
|
} while (!isCorectPass);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const importAcc = async () => {
|
const importAcc = async () => {
|
||||||
const validation = await validateFile() as { error: any }
|
const validation = await validateFile() as { error: any }
|
||||||
@ -374,20 +430,71 @@ export default defineComponent({
|
|||||||
alertOpen.value = true
|
alertOpen.value = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const accounts = await getAccounts()
|
||||||
|
const newAccounts = (validation as unknown as { json: Account[] }).json
|
||||||
|
if(settings.s.enableStorageEnctyption) {
|
||||||
|
const hasPass = await promptForPassword(accounts)
|
||||||
|
if(hasPass) {
|
||||||
|
const cryptoParams = await getCryptoParams(mpPass.value)
|
||||||
|
const accProm = newAccounts.map(async a => {
|
||||||
|
if(a.pk.length === 64) {
|
||||||
|
a.pk = `0x${a.pk}`
|
||||||
|
}
|
||||||
|
a.encPk = await encrypt(a.pk, cryptoParams)
|
||||||
|
return a
|
||||||
|
})
|
||||||
|
const encNewAccounts = await Promise.all(accProm)
|
||||||
|
await replaceAccounts([...accounts, ...encNewAccounts])
|
||||||
|
alertHeader.value = 'Success'
|
||||||
|
alertMsg.value = "Successfully imported new accounts."
|
||||||
|
alertOpen.value = true
|
||||||
|
noAccounts.value = false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
await replaceAccounts([...accounts, ...newAccounts.map( a => { a.encPk = ''; return a })])
|
||||||
|
alertHeader.value = 'Success'
|
||||||
|
alertMsg.value = "Successfully imported new accounts."
|
||||||
|
alertOpen.value = true
|
||||||
|
noAccounts.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportAcc = async () => {
|
const exportAcc = async () => {
|
||||||
//
|
const accounts = await getAccounts()
|
||||||
|
if(!accounts.length) {
|
||||||
|
alertMsg.value = "You need at least one account to export."
|
||||||
|
alertOpen.value = true
|
||||||
|
}
|
||||||
|
if(settings.s.enableStorageEnctyption) {
|
||||||
|
const hasPass = await promptForPassword(accounts)
|
||||||
|
if(hasPass) {
|
||||||
|
const cryptoParams = await getCryptoParams(mpPass.value)
|
||||||
|
const accProm = accounts.map(async a => {
|
||||||
|
a.pk = await decrypt(a.encPk, cryptoParams)
|
||||||
|
return a
|
||||||
|
})
|
||||||
|
const encNewAccounts = await Promise.all(accProm)
|
||||||
|
exportFile('wallet_export.json', JSON.stringify(encNewAccounts, null, 2))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
exportFile('wallet_export.json', JSON.stringify(accounts, null, 2))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onIonViewWillEnter( () => {
|
onIonViewWillEnter(async () => {
|
||||||
getSettings().then((storeSettings) =>
|
await Promise.all([getSettings().then((storeSettings) =>
|
||||||
{
|
{
|
||||||
settings.s = storeSettings
|
settings.s = storeSettings
|
||||||
loading.value = false
|
}),
|
||||||
})
|
getAccounts().then((accounts) => {
|
||||||
|
if(accounts.length) {
|
||||||
|
noAccounts.value = false
|
||||||
|
}
|
||||||
|
})])
|
||||||
|
loading.value = false
|
||||||
})
|
})
|
||||||
|
|
||||||
const setTime = async () => {
|
const setTime = async () => {
|
||||||
@ -428,7 +535,11 @@ export default defineComponent({
|
|||||||
toastMsg,
|
toastMsg,
|
||||||
importAcc,
|
importAcc,
|
||||||
exportAcc,
|
exportAcc,
|
||||||
importFile
|
importFile,
|
||||||
|
modalGetPassword,
|
||||||
|
noAccounts,
|
||||||
|
alertHeader,
|
||||||
|
changeAutoLock
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -69,7 +69,7 @@ import {
|
|||||||
replaceAccounts,
|
replaceAccounts,
|
||||||
saveSelectedAccount
|
saveSelectedAccount
|
||||||
} from "@/utils/platform";
|
} from "@/utils/platform";
|
||||||
import { decrypt } from "@/utils/webCrypto"
|
import { decrypt, getCryptoParams } from "@/utils/webCrypto"
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@ -107,8 +107,9 @@ export default defineComponent({
|
|||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
let accounts = await getAccounts()
|
let accounts = await getAccounts()
|
||||||
|
const cryptoParams = await getCryptoParams(mpPass.value)
|
||||||
const accProm = accounts.map(async a => {
|
const accProm = accounts.map(async a => {
|
||||||
a.pk = await decrypt(a.encPk, mpPass.value)
|
a.pk = await decrypt(a.encPk, cryptoParams)
|
||||||
return a
|
return a
|
||||||
})
|
})
|
||||||
accounts = await Promise.all(accProm)
|
accounts = await Promise.all(accProm)
|
||||||
|
93
src/views/WalletError.vue
Normal file
93
src/views/WalletError.vue
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<ion-page>
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Contract Error</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content class="ion-padding">
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>Transaction was aboreted before being sent</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>Error:</ion-label>
|
||||||
|
<ion-textarea
|
||||||
|
style="overflow-y: scroll"
|
||||||
|
:rows="10"
|
||||||
|
:cols="20"
|
||||||
|
:value="error"
|
||||||
|
readonly
|
||||||
|
></ion-textarea>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item>
|
||||||
|
<ion-button @click="onCancel">Exit</ion-button>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-loading
|
||||||
|
:is-open="loading"
|
||||||
|
cssClass="my-custom-class"
|
||||||
|
message="Please wait..."
|
||||||
|
:duration="4000"
|
||||||
|
@didDismiss="loading = false"
|
||||||
|
>
|
||||||
|
</ion-loading>
|
||||||
|
</ion-content>
|
||||||
|
</ion-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref } from "vue";
|
||||||
|
import {
|
||||||
|
IonContent,
|
||||||
|
IonHeader,
|
||||||
|
IonPage,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar,
|
||||||
|
IonItem,
|
||||||
|
IonLabel,
|
||||||
|
IonButton,
|
||||||
|
IonTextarea,
|
||||||
|
onIonViewWillEnter,
|
||||||
|
IonLoading,
|
||||||
|
} from "@ionic/vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
IonContent,
|
||||||
|
IonHeader,
|
||||||
|
IonPage,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar,
|
||||||
|
IonItem,
|
||||||
|
IonLabel,
|
||||||
|
IonButton,
|
||||||
|
IonTextarea,
|
||||||
|
IonLoading,
|
||||||
|
},
|
||||||
|
setup: () => {
|
||||||
|
const route = useRoute();
|
||||||
|
const error = decodeURIComponent((route.params?.param as string) ?? "");
|
||||||
|
const loading = ref(true);
|
||||||
|
const contract = (route.params?.contract as string) ?? "";
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
window.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
onIonViewWillEnter(async () => {
|
||||||
|
(window as any)?.resizeTo?.(700, 600);
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
onCancel,
|
||||||
|
contract,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user