mirror of
https://github.com/andrei0x309/clear-wallet.git
synced 2024-12-19 00:00:45 +00:00
dev: 1.0.4
This commit is contained in:
parent
6c18b2841d
commit
b376839770
4
.gitignore
vendored
4
.gitignore
vendored
@ -30,4 +30,6 @@ npm-debug.log*
|
||||
/plugins
|
||||
/www
|
||||
/src/extension/inject.js
|
||||
readme.md
|
||||
README.md
|
||||
PRIVACY_POLICY.md
|
||||
LICENSE
|
10
public/_locale/en/messages.json
Normal file
10
public/_locale/en/messages.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"appName": {
|
||||
"message": "Clear EVM Wallet (CLW)",
|
||||
"description": "This is an open-source EVM wallet based on ethers, ionic, vue, that implements the metamask API."
|
||||
},
|
||||
"appDesc": {
|
||||
"message": "Clear EVM Wallet (CLW)",
|
||||
"description": "This is an open-source EVM wallet based on ethers, ionic, vue, that implements the metamask API."
|
||||
}
|
||||
}
|
6
public/assets/randomGrad.svg
Normal file
6
public/assets/randomGrad.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" viewBox="0 0 700 700" width="700" height="700" opacity="0.91"><defs><linearGradient gradientTransform="rotate(201, 0.5, 0.5)" x1="50%" y1="0%" x2="50%" y2="100%" id="ffflux-gradient"><stop stop-color="hsl(272, 100%, 27%)" stop-opacity="1" offset="0%"></stop><stop stop-color="hsl(296, 66%, 61%)" stop-opacity="1" offset="100%"></stop></linearGradient><filter id="ffflux-filter" x="-20%" y="-20%" width="140%" height="140%" filterUnits="objectBoundingBox" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.006 0.004" numOctaves="2" seed="83" stitchTiles="stitch" x="0%" y="0%" width="100%" height="100%" result="turbulence"></feTurbulence>
|
||||
<feGaussianBlur stdDeviation="24 37" x="0%" y="0%" width="100%" height="100%" in="turbulence" edgeMode="duplicate" result="blur"></feGaussianBlur>
|
||||
<feBlend mode="screen" x="0%" y="0%" width="100%" height="100%" in="SourceGraphic" in2="blur" result="blend"></feBlend>
|
||||
|
||||
</filter></defs><rect width="700" height="700" fill="url(#ffflux-gradient)" filter="url(#ffflux-filter)"></rect></svg>
|
After Width: | Height: | Size: 1.2 KiB |
15
src/App.vue
15
src/App.vue
@ -6,8 +6,9 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { IonApp, IonRouterOutlet } from "@ionic/vue";
|
||||
import { defineComponent } from "vue";
|
||||
import { useRoute, useRouter} from "vue-router";
|
||||
import { defineComponent, onBeforeMount } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { getSettings } from '@/utils/platform'
|
||||
|
||||
export default defineComponent({
|
||||
name: "App",
|
||||
@ -21,6 +22,14 @@ const router = useRouter()
|
||||
const { param, rid } = route.query;
|
||||
console.log(route?.query,'zzzzzzzzzzzzzzz')
|
||||
|
||||
onBeforeMount( () => {
|
||||
getSettings().then((settings) => {
|
||||
if(settings.theme !== 'system') {
|
||||
document.body.classList.remove(settings.theme === 'dark' ? 'light': 'dark')
|
||||
document.body.classList.add(settings.theme)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
switch (route?.query?.route ?? "") {
|
||||
case "sign-msg": {
|
||||
@ -43,7 +52,7 @@ switch (route?.query?.route ?? "") {
|
||||
}
|
||||
case "wallet-error": {
|
||||
router.push({
|
||||
path: `/wallet-error"/${rid}/${param}`
|
||||
path: `/wallet-error/${rid}/${param}`
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { getSelectedNetwork, numToHexStr } from "@/utils/platform";
|
||||
import type { RequestArguments } from '@/extension/types'
|
||||
|
||||
const allowedMethods = {
|
||||
'eth_accounts': true,
|
||||
@ -22,10 +23,11 @@ window.addEventListener("message", (event) => {
|
||||
|
||||
if (event.data.type && (event.data.type === "CLWALLET_CONTENT")) {
|
||||
event.data.data.resId = event.data.resId
|
||||
event.data.data.type = "CLWALLET_CONTENT_MSG"
|
||||
if((event?.data?.data?.method ?? 'x') in allowedMethods) {
|
||||
chrome.runtime.sendMessage(event.data.data, (res) => {
|
||||
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, "*");
|
||||
})
|
||||
}
|
||||
@ -36,18 +38,24 @@ window.addEventListener("message", (event) => {
|
||||
} else if (event.data.type && (event.data.type === "CLWALLET_PING")) {
|
||||
getSelectedNetwork().then(network => {
|
||||
const data = { type: "CLWALLET_PAGE_LISTENER", data: {
|
||||
listener: 'connected',
|
||||
listner: 'connect',
|
||||
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)
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
chrome.runtime.onMessage.addListener((message: RequestArguments , sender, sendResponse) => {
|
||||
if(message.type === "CLWALLET_EXT_LISTNER") {
|
||||
const data = { type: "CLWALLET_PAGE_LISTENER", data: message.data };
|
||||
// console.log('data listner', data)
|
||||
window.postMessage(data, "*");
|
||||
}
|
||||
return true
|
||||
});
|
||||
|
||||
(function() {
|
||||
|
@ -18,7 +18,7 @@ const listner = function(event: any) {
|
||||
if (event.data.type && (event.data.type === "CLWALLET_PAGE")) {
|
||||
if(event?.data?.data?.error){
|
||||
promResolvers[event.data.resId].reject(event.data.data);
|
||||
console.log('rejected')
|
||||
// console.log('rejected')
|
||||
}else {
|
||||
promResolvers[event.data.resId].resolve(event.data.data);
|
||||
}
|
||||
@ -45,7 +45,7 @@ return new Promise((resolve, reject) => {
|
||||
if (ping) {
|
||||
data.type = 'CLWALLET_PING'
|
||||
}
|
||||
console.log('data in', data)
|
||||
// console.log('data in', data)
|
||||
window.postMessage(data, "*");
|
||||
})
|
||||
}
|
||||
@ -149,6 +149,9 @@ const eth = new Proxy({
|
||||
break
|
||||
case 'connect':
|
||||
listners.connect.add(callback)
|
||||
sendMessage({
|
||||
method: 'wallet_ready'
|
||||
}, true)
|
||||
break;
|
||||
case 'disconnect':
|
||||
listners.disconnect.add(callback)
|
||||
@ -161,6 +164,24 @@ const eth = new Proxy({
|
||||
return
|
||||
}
|
||||
},
|
||||
removeListener: (eventName: string, callback: () => void) => {
|
||||
switch (eventName) {
|
||||
case 'accountsChanged':
|
||||
listners.accountsChanged.delete(callback)
|
||||
break
|
||||
case 'connect':
|
||||
listners.connect.delete(callback)
|
||||
break;
|
||||
case 'disconnect':
|
||||
listners.disconnect.delete(callback)
|
||||
break;
|
||||
case 'chainChanged':
|
||||
listners.chainChanged.delete(callback)
|
||||
break;
|
||||
default:
|
||||
return
|
||||
}
|
||||
},
|
||||
// Simulate Metamask
|
||||
_warnOfDeprecation: () => null,
|
||||
_state: {},
|
||||
@ -200,25 +221,21 @@ const injectWallet = (win: any) => {
|
||||
return
|
||||
}
|
||||
});
|
||||
console.log('Clear wallet injected', (window as any).ethereum, win)
|
||||
// console.log('Clear wallet injected', (window as any).ethereum, win)
|
||||
}
|
||||
injectWallet(this)
|
||||
sendMessage({
|
||||
method: 'wallet_ready'
|
||||
}, true)
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
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_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: '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: '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)));
|
||||
(<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)
|
||||
// setTimeout(() => {
|
||||
// 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_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: '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: '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)));
|
||||
// (<any>window).ethereum.on('accountsChanged', ((a: any, b: any) => console.log('accountsChanged', a, b)));
|
||||
// (<any>window).ethereum.on('chainChanged', ((a: any) => console.log('chainChanged', a, typeof a)));
|
||||
// }, 3500)
|
||||
|
||||
// console.log( (window as any).ethereum.request({method: 'eth_chainId'}))
|
@ -2,6 +2,11 @@ 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)
|
||||
chrome.tabs.query({}, (tabs) => tabs.forEach( tab =>
|
||||
{
|
||||
if (tab?.id) {
|
||||
chrome.tabs.sendMessage(tab.id, data)
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
@ -80,7 +80,9 @@ if (!chrome.notifications.onButtonClicked.hasListener(viewTxListner)){
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendResponse) => {
|
||||
console.log(message);
|
||||
if(message?.type !== "CLWALLET_CONTENT_MSG") {
|
||||
return true
|
||||
}
|
||||
(async () => {
|
||||
if (!('method' in message)) {
|
||||
sendResponse({
|
||||
@ -147,7 +149,6 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
|
||||
}
|
||||
case 'eth_chainId': {
|
||||
const network = await getSelectedNetwork()
|
||||
console.log(network, 'network')
|
||||
const chainId = network?.chainId ?? 0
|
||||
sendResponse(`0x${chainId.toString(16)}`)
|
||||
break
|
||||
@ -168,7 +169,7 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
|
||||
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 ?? '')}`),
|
||||
url: chrome.runtime.getURL(`index.html?route=wallet-error¶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
|
||||
@ -177,7 +178,7 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
|
||||
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 ?? '')}`),
|
||||
url: chrome.runtime.getURL(`index.html?route=wallet-error¶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
|
||||
@ -242,12 +243,17 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
|
||||
} as any)
|
||||
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'TX Failed'
|
||||
})
|
||||
chrome.windows.create({
|
||||
height: 450,
|
||||
width: 400,
|
||||
url: chrome.runtime.getURL(`index.html?route=wallet-error¶m=${encodeURIComponent(String(err))}&rid=${String(message?.resId ?? '')}`),
|
||||
type: 'popup'
|
||||
})
|
||||
chrome.notifications.create({
|
||||
message: 'Transaction Failed',
|
||||
title: 'Error',
|
||||
@ -274,7 +280,7 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
|
||||
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 ?? '')}`),
|
||||
url: chrome.runtime.getURL(`index.html?route=wallet-error¶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
|
||||
@ -360,7 +366,8 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
|
||||
}
|
||||
try {
|
||||
chrome.windows.remove(sender.tab?.windowId ?? 0)
|
||||
}catch{
|
||||
}catch (e) {
|
||||
console.log(e)
|
||||
// ignore
|
||||
}
|
||||
break
|
||||
|
@ -24,16 +24,18 @@ export interface Networks {
|
||||
}
|
||||
|
||||
export interface RequestArguments {
|
||||
method: string;
|
||||
params?: any[];
|
||||
method: string
|
||||
type: string
|
||||
params?: any[]
|
||||
resId?: string
|
||||
website?: string
|
||||
data?: any
|
||||
}
|
||||
|
||||
export interface ProviderRpcError extends Error {
|
||||
message: string;
|
||||
code: number;
|
||||
data?: unknown;
|
||||
message: string
|
||||
code: number
|
||||
data?: unknown
|
||||
}
|
||||
|
||||
export interface Price {
|
||||
|
@ -1,32 +1,32 @@
|
||||
export const userReject = {} as Record<string, (() => any) | undefined>
|
||||
export const userApprove = {} as Record<string, ((a: unknown) => any) | undefined>
|
||||
export const userReject = {} as Record<string, (() => any) | undefined>
|
||||
export const userApprove = {} as Record<string, ((a: unknown) => any) | undefined>
|
||||
export const rIdWin = {} as Record<string, string | undefined>
|
||||
export const rIdData = {} as Record<string, any | undefined>
|
||||
|
||||
export const approve = (rId: string) => {
|
||||
chrome.runtime.sendMessage({ method: 'wallet_approve', resId: rId })
|
||||
chrome.runtime.sendMessage({ method: 'wallet_approve', resId: rId, type: 'CLWALLET_CONTENT_MSG' })
|
||||
}
|
||||
|
||||
export const walletSendData = (rId: string, data: any) => {
|
||||
return new Promise((resolve) => {
|
||||
chrome.runtime.sendMessage({ method: 'wallet_send_data', resId: rId, data}, (r) => {
|
||||
resolve(r)
|
||||
})
|
||||
chrome.runtime.sendMessage({ method: 'wallet_send_data', resId: rId, data, type: 'CLWALLET_CONTENT_MSG' }, (r) => {
|
||||
resolve(r)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const walletGetData = (rId: string) => {
|
||||
return new Promise((resolve) => {
|
||||
chrome.runtime.sendMessage({ method: 'wallet_get_data', resId: rId }, (r) => {
|
||||
resolve(r)
|
||||
})
|
||||
chrome.runtime.sendMessage({ method: 'wallet_get_data', resId: rId, type: 'CLWALLET_CONTENT_MSG' }, (r) => {
|
||||
resolve(r)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const walletPing = () => {
|
||||
return new Promise((resolve) => {
|
||||
chrome.runtime.sendMessage({ method: 'wallet_ping' }, (r) => {
|
||||
chrome.runtime.sendMessage({ method: 'wallet_ping', type: 'CLWALLET_CONTENT_MSG' }, (r) => {
|
||||
resolve(r)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
component: () => import('@/views/ContractError.vue'),
|
||||
},
|
||||
{
|
||||
path: '/wallet-error/:rid/:error',
|
||||
path: '/wallet-error/:rid/:param',
|
||||
component: () => import('@/views/WalletError.vue'),
|
||||
},
|
||||
{
|
||||
|
@ -2,7 +2,167 @@
|
||||
http://ionicframework.com/docs/theming/ */
|
||||
|
||||
/** Ionic CSS Variables **/
|
||||
:root {
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
/*
|
||||
* Dark Colors
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
body:not(.light) {
|
||||
--ion-color-primary: #6a64ff;
|
||||
--ion-color-primary-rgb: 106, 100, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #5d58e0;
|
||||
--ion-color-primary-tint: #7974ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80,200,255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255,255,255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106,100,255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255,255,255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47,223,117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0,0,0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255,213,52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0,0,0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255,73,97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255,255,255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244,245,248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0,0,0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152,154,162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0,0,0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34,36,40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255,255,255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body:not(.light) {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0,0,0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
.ios ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body:not(.light) {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18,18,18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
}
|
||||
|
||||
:root, body.light {
|
||||
/** primary **/
|
||||
--ion-color-primary: #5260ff;
|
||||
--ion-color-primary-rgb: 82, 96, 255;
|
||||
@ -76,161 +236,155 @@ http://ionicframework.com/docs/theming/ */
|
||||
--ion-color-light-tint: #f5f6f9;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/*
|
||||
* Dark Colors
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
body {
|
||||
--ion-color-primary: #6a64ff;
|
||||
--ion-color-primary-rgb: 106, 100, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #5d58e0;
|
||||
--ion-color-primary-tint: #7974ff;
|
||||
body.dark {
|
||||
--ion-color-primary: #6a64ff;
|
||||
--ion-color-primary-rgb: 106, 100, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #5d58e0;
|
||||
--ion-color-primary-tint: #7974ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80,200,255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255,255,255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80,200,255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255,255,255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106,100,255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255,255,255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106,100,255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255,255,255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47,223,117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0,0,0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47,223,117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0,0,0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255,213,52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0,0,0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255,213,52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0,0,0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255,73,97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255,255,255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255,73,97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255,255,255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244,245,248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0,0,0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244,245,248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0,0,0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152,154,162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0,0,0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152,154,162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0,0,0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34,36,40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255,255,255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34,36,40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255,255,255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0,0,0;
|
||||
.ios body.dark {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0,0,0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
.ios ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
}
|
||||
.ios ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18,18,18;
|
||||
.md body.dark {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18,18,18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255,255,255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
@ -94,6 +94,10 @@ export const addToHistory = async (historyItem: HistoryItem): Promise<void> => {
|
||||
await storageSave('history', history)
|
||||
}
|
||||
|
||||
export const wipeHistory = async (): Promise<void> => {
|
||||
await storageSave('history', [])
|
||||
}
|
||||
|
||||
export const getSettings = async (): Promise<Settings> => {
|
||||
return (await storageGet('settings'))?.settings ?? defaultSettings as unknown as Settings
|
||||
}
|
||||
@ -140,6 +144,9 @@ export const smallRandomString = () => {
|
||||
export const clearPk = async (): Promise<void> => {
|
||||
let accounts = await getAccounts()
|
||||
const accProm = accounts.map(async a => {
|
||||
if(a.encPk) {
|
||||
a.pk = ''
|
||||
}
|
||||
return a
|
||||
})
|
||||
accounts = await Promise.all(accProm)
|
||||
|
@ -10,8 +10,8 @@ export const signMsg = async (msg: string) => {
|
||||
export const getBalance = async () =>{
|
||||
const account = await getSelectedAccount()
|
||||
const network = await getSelectedNetwork()
|
||||
const wallet = new ethers.Wallet(account.pk, new ethers.providers.JsonRpcProvider(network.rpc))
|
||||
return await wallet.getBalance()
|
||||
const provider = new ethers.providers.JsonRpcProvider(network.rpc)
|
||||
return await provider.getBalance(account.address)
|
||||
}
|
||||
|
||||
export const getGasPrice = async () => {
|
||||
|
@ -34,18 +34,42 @@
|
||||
<p style="font-size:0.7rem">{{ account.address }}</p><ion-icon :icon="copyOutline"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-chip>View Pk</ion-chip>
|
||||
<ion-chip @click="viewPk(account.address)">View Pk</ion-chip>
|
||||
<ion-chip @click="deleteAccount(account.address)">Delete</ion-chip>
|
||||
<ion-chip @click="editAccount(account.address)">Edit Name</ion-chip>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
|
||||
<ion-modal
|
||||
:is-open="pkModal"
|
||||
@didDismiss="shownPk=''"
|
||||
>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-button @click="pkModal=false">Close</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-title>View PK</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-item @click="copyAddress(shownPk, getToastRef())" button>
|
||||
<ion-icon style="margin-right: 0.5rem;" :icon="copyOutline" />
|
||||
<ion-label button>PK</ion-label>
|
||||
<ion-input id="pastePk" v-model="shownPk" readonly></ion-input>
|
||||
</ion-item>
|
||||
</ion-content>
|
||||
</ion-modal>
|
||||
|
||||
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, Ref } from "vue";
|
||||
import { getAccounts, copyAddress, replaceAccounts } from "@/utils/platform"
|
||||
import { getAccounts, copyAddress, replaceAccounts, getSettings, clearPk } from "@/utils/platform"
|
||||
import {
|
||||
IonContent,
|
||||
IonHeader,
|
||||
@ -60,12 +84,17 @@ import {
|
||||
IonButtons,
|
||||
IonButton,
|
||||
onIonViewWillEnter,
|
||||
IonToast
|
||||
IonToast,
|
||||
modalController,
|
||||
IonInput,
|
||||
IonModal
|
||||
} from "@ionic/vue";
|
||||
|
||||
import { addCircleOutline, copyOutline } from "ionicons/icons";
|
||||
import router from "@/router";
|
||||
import type { Account } from '@/extension/types'
|
||||
import UnlockModal from '@/views/UnlockModal.vue'
|
||||
|
||||
import type { Account, Settings } from '@/extension/types'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -81,19 +110,26 @@ export default defineComponent({
|
||||
IonChip,
|
||||
IonButtons,
|
||||
IonButton,
|
||||
IonToast
|
||||
IonToast,
|
||||
IonInput,
|
||||
IonModal
|
||||
},
|
||||
setup () {
|
||||
const accounts = ref({}) as Ref<Account[]>
|
||||
const accounts = ref([]) as Ref<Account[]>
|
||||
const loading = ref(true)
|
||||
const toastState = ref(false)
|
||||
const shownPk = ref('')
|
||||
const pkModal = ref(false)
|
||||
const settings = ref({}) as Ref<Settings>
|
||||
|
||||
const getToastRef = () => toastState
|
||||
|
||||
const loadData = () => {
|
||||
const pAccounts = getAccounts()
|
||||
Promise.all([pAccounts]).then(( res ) => {
|
||||
const pGetSettings = getSettings()
|
||||
Promise.all([pAccounts, pGetSettings]).then(( res ) => {
|
||||
accounts.value = res[0]
|
||||
settings.value = res[1]
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
@ -120,6 +156,45 @@ export default defineComponent({
|
||||
loadData()
|
||||
})
|
||||
|
||||
const openModal = async () => {
|
||||
const modal = await modalController.create({
|
||||
component: UnlockModal,
|
||||
componentProps: {
|
||||
unlockType: 'viewPk'
|
||||
}
|
||||
|
||||
});
|
||||
modal.present();
|
||||
const { role } = await modal.onWillDismiss();
|
||||
if(role === 'confirm') return true
|
||||
return false
|
||||
}
|
||||
|
||||
const viewPk = async (addr: string) => {
|
||||
let pk = ''
|
||||
const account = accounts.value.find(a => a.address === addr)
|
||||
if(settings.value.enableStorageEnctyption) {
|
||||
if(account?.encPk) {
|
||||
const modalR = await openModal()
|
||||
if(modalR){
|
||||
const account = (await getAccounts()).find(a => a.address === addr)
|
||||
pk = account?.pk ?? ''
|
||||
}
|
||||
}else {
|
||||
pk = account?.pk ?? ''
|
||||
}
|
||||
}else {
|
||||
pk = account?.pk ?? ''
|
||||
}
|
||||
if(pk) {
|
||||
shownPk.value = pk
|
||||
if(settings.value.encryptAfterEveryTx) {
|
||||
clearPk()
|
||||
}
|
||||
pkModal.value = true
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
accounts,
|
||||
addCircleOutline,
|
||||
@ -130,7 +205,10 @@ export default defineComponent({
|
||||
deleteAccount,
|
||||
editAccount,
|
||||
loading,
|
||||
goToAddAccount
|
||||
goToAddAccount,
|
||||
viewPk,
|
||||
pkModal,
|
||||
shownPk
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-button @click="onCancel">Cancel</ion-button>
|
||||
<ion-button @click="onAddAccount">Add Account</ion-button>
|
||||
<ion-button @click="onAddAccount">{{ isEdit ? 'Edit Account' : 'Add Account' }}</ion-button>
|
||||
</ion-item>
|
||||
<ion-alert
|
||||
:is-open="alertOpen"
|
||||
@ -42,12 +42,14 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonInput, IonButton, IonAlert, IonIcon, onIonViewWillEnter } from "@ionic/vue";
|
||||
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonInput, IonButton, IonAlert, IonIcon, onIonViewWillEnter, modalController } from "@ionic/vue";
|
||||
import { ethers } from "ethers"
|
||||
import { saveSelectedAccount, getAccounts, saveAccount, getRandomPk, smallRandomString, paste } from "@/utils/platform";
|
||||
import { saveSelectedAccount, getAccounts, saveAccount, getRandomPk, smallRandomString, paste, getSettings } from "@/utils/platform";
|
||||
import router from "@/router";
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { Account } from '@/extension/types'
|
||||
import type { Account, Settings } from '@/extension/types'
|
||||
import UnlockModal from '@/views/UnlockModal.vue'
|
||||
import { encrypt, getCryptoParams } from '@/utils/webCrypto'
|
||||
|
||||
import { clipboardOutline } from "ionicons/icons";
|
||||
|
||||
@ -62,15 +64,31 @@ export default defineComponent({
|
||||
const isEdit = route.path.includes('/edit')
|
||||
const paramAddress = route.params.address ?? ""
|
||||
let accountsProm: Promise<Account[] | undefined>
|
||||
let settingsProm: Promise<Settings | undefined>
|
||||
|
||||
const resetFields = () => {
|
||||
name.value = ''
|
||||
pk.value = ''
|
||||
}
|
||||
|
||||
const openModal = async () => {
|
||||
const modal = await modalController.create({
|
||||
component: UnlockModal,
|
||||
componentProps: {
|
||||
unlockType: 'addAccount'
|
||||
}
|
||||
|
||||
});
|
||||
modal.present();
|
||||
const { role, data } = await modal.onWillDismiss();
|
||||
if(role === 'confirm') return data
|
||||
return false
|
||||
}
|
||||
|
||||
onIonViewWillEnter(async () => {
|
||||
if(isEdit && paramAddress) {
|
||||
accountsProm = getAccounts()
|
||||
settingsProm = getSettings()
|
||||
const accounts = await accountsProm as Account[]
|
||||
const acc = accounts.find(account => account.address === paramAddress)
|
||||
if(acc) {
|
||||
@ -95,7 +113,40 @@ export default defineComponent({
|
||||
if(!accountsProm) {
|
||||
accountsProm = getAccounts()
|
||||
}
|
||||
if(!settingsProm) {
|
||||
settingsProm = getSettings()
|
||||
}
|
||||
const accounts = await accountsProm as Account[]
|
||||
const settings = await settingsProm as Settings
|
||||
if( settings.enableStorageEnctyption) {
|
||||
const pass = await openModal()
|
||||
if(!pass){
|
||||
alertMsg.value = "Cannot add account with encryption password."
|
||||
alertOpen.value = true
|
||||
return
|
||||
}
|
||||
const cryptoParams = await getCryptoParams(pass)
|
||||
if((accounts.length ?? 0) < 1 ){
|
||||
p1 = saveSelectedAccount({
|
||||
address: wallet.address,
|
||||
name: name.value,
|
||||
pk: pk.value,
|
||||
encPk: await encrypt(pk.value, cryptoParams)
|
||||
})
|
||||
} else {
|
||||
if(accounts.find(account => account.address === wallet.address)){
|
||||
alertMsg.value = "Account already exists."
|
||||
return alertOpen.value = true
|
||||
}
|
||||
}
|
||||
const p2 = saveAccount({
|
||||
address: wallet.address,
|
||||
name: name.value,
|
||||
pk: pk.value,
|
||||
encPk: await encrypt(pk.value, cryptoParams)
|
||||
})
|
||||
await Promise.all([p1, p2])
|
||||
}else {
|
||||
if((accounts.length ?? 0) < 1 ){
|
||||
p1 = saveSelectedAccount({
|
||||
address: wallet.address,
|
||||
@ -116,10 +167,11 @@ export default defineComponent({
|
||||
encPk: ''
|
||||
})
|
||||
await Promise.all([p1, p2])
|
||||
}
|
||||
if(isEdit) {
|
||||
router.push('accounts')
|
||||
router.push('/tabs/accounts')
|
||||
}else {
|
||||
router.push('/')
|
||||
router.push('/tabs/home')
|
||||
}
|
||||
resetFields()
|
||||
}
|
||||
@ -134,9 +186,9 @@ export default defineComponent({
|
||||
|
||||
const onCancel = () => {
|
||||
if(isEdit) {
|
||||
router.push('accounts')
|
||||
router.push('/tabs/accounts')
|
||||
}else {
|
||||
router.push('/')
|
||||
router.push('/tabs/home')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-button @click="templateModal=true" expand="block">Add from popular chain list</ion-button>
|
||||
<ion-button v-if="!isEdit" @click="templateModal=true" expand="block">Add from popular chain list</ion-button>
|
||||
<ion-item>
|
||||
<ion-label>Name(*)</ion-label>
|
||||
<ion-input v-model="name" placeholder="ex: Polygon"></ion-input>
|
||||
@ -190,9 +190,9 @@ export default defineComponent({
|
||||
const p2 = replaceNetworks(networks)
|
||||
await Promise.all([p1, p2])
|
||||
if(isEdit) {
|
||||
router.push('networks')
|
||||
router.push('/tabs/networks')
|
||||
}else {
|
||||
router.push('/')
|
||||
router.push('/tabs/home')
|
||||
}
|
||||
resetFields()
|
||||
}
|
||||
@ -203,9 +203,9 @@ export default defineComponent({
|
||||
|
||||
const onCancel = () => {
|
||||
if(isEdit) {
|
||||
router.push('networks')
|
||||
router.push('/tabs/networks')
|
||||
}else {
|
||||
router.push('/')
|
||||
router.push('/tabs/home')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,33 +6,226 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
1
|
||||
</ion-content>
|
||||
<ion-content class="ion-padding">
|
||||
2
|
||||
</ion-content>
|
||||
<ion-content class="ion-padding">
|
||||
3
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
|
||||
<ion-loading
|
||||
:is-open="loading"
|
||||
cssClass="my-custom-class"
|
||||
message="Please wait..."
|
||||
:duration="4000"
|
||||
@didDismiss="loading = false"
|
||||
>
|
||||
</ion-loading>
|
||||
<ion-toast
|
||||
:is-open="toastState"
|
||||
@didDismiss="toastState = false"
|
||||
message="Copied to clipboard"
|
||||
:duration="1500"
|
||||
></ion-toast>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Assests for Account: {{ selectedAccount?.name }}</ion-label>
|
||||
</ion-item>
|
||||
<ion-item button @click="copyAddress(selectedAccount?.address, getToastRef())">
|
||||
<p style="font-size: 0.7rem">{{ selectedAccount?.address }}</p>
|
||||
<ion-icon style="margin-left: 0.5rem" :icon="copyOutline"></ion-icon>
|
||||
</ion-item>
|
||||
<template v-if="isError">
|
||||
Assets info could not be retrieved because of an http error, API down or conectivity issues.
|
||||
</template>
|
||||
<template v-else-if="noAssets">
|
||||
No assets found for this wallet address.
|
||||
</template>
|
||||
<template v-else>
|
||||
<ion-item v-if="assets.yupScore">
|
||||
<span style="font-size: 0.9rem">YUP Score:</span> <span style="font-size: 1.1rem; margin-left: 0.5rem">{{ assets.yupScore.toFixed(2) }}</span>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<p style="font-size: 0.7rem">YUP score is a score of your wallet based on assets and transactions. </p>
|
||||
</ion-item>
|
||||
<template v-if="assets.tokens">
|
||||
<template v-if="ethTokens.length">
|
||||
<ion-item>Ethereum Tokens</ion-item>
|
||||
<ion-list>
|
||||
<ion-item v-for="token of ethTokens" :key="token.address">
|
||||
<ion-avatar
|
||||
v-if="token?.image"
|
||||
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
|
||||
>
|
||||
<img
|
||||
:alt="token?.name"
|
||||
:src="token?.image"
|
||||
@error="token.image = getUrl('assets/randomGrad.svg')"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label>
|
||||
</ion-item>
|
||||
<ion-item v-if="hasMore.ethTokens">
|
||||
<ion-button @click="loadMore('ethTokens')">Load More</ion-button>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</template>
|
||||
|
||||
<template v-if="polyTokens.length">
|
||||
<ion-item>Polygon Tokens</ion-item>
|
||||
<ion-list>
|
||||
<ion-item v-for="token of polyTokens" :key="token.address">
|
||||
<ion-avatar
|
||||
v-if="token?.image"
|
||||
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
|
||||
>
|
||||
<img
|
||||
:alt="token?.name"
|
||||
:src="token?.image"
|
||||
@error="token.image = getUrl('assets/randomGrad.svg')"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label>
|
||||
</ion-item>
|
||||
<ion-item v-if="hasMore.polyTokens">
|
||||
<ion-button @click="loadMore('polyTokens')">Load More</ion-button>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
<template v-if="assets.nfts">
|
||||
|
||||
<template v-if="ethNfts.length">
|
||||
<ion-item>Ethereum NFTs</ion-item>
|
||||
<ion-list>
|
||||
<ion-item v-for="nft of ethNfts" :key="nft.address">
|
||||
<ion-avatar
|
||||
v-if="nft?.imageURI"
|
||||
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
|
||||
>
|
||||
<img
|
||||
:alt="nft?.collectionName"
|
||||
:src="nft?.imageURI"
|
||||
@error="nft.imageURI = getUrl('assets/randomGrad.svg')"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label><b>{{ nft?.collectionName }}</b></ion-label>
|
||||
</ion-item>
|
||||
<ion-item v-if="hasMore.ethNfts">
|
||||
<ion-button @click="loadMore('ethNfts')">Load More</ion-button>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</template>
|
||||
|
||||
<template v-if="polyNfts.length">
|
||||
<ion-item>Polygon NFTs</ion-item>
|
||||
<ion-list>
|
||||
<ion-item v-for="nft of polyNfts" :key="nft.address">
|
||||
<ion-avatar
|
||||
v-if="nft?.imageURI"
|
||||
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
|
||||
>
|
||||
<img
|
||||
:alt="nft?.collectionName"
|
||||
:src="nft?.imageURI"
|
||||
@error="nft.imageURI = getUrl('assets/randomGrad.svg')"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label><b>{{ nft?.collectionName }}</b></ion-label>
|
||||
</ion-item>
|
||||
<ion-item v-if="hasMore.polyNfts">
|
||||
<ion-button @click="loadMore('polyNfts')">Load More</ion-button>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
<template v-if="assets.poaps">
|
||||
<template v-if="poaps.length">
|
||||
<ion-item>POAPs</ion-item>
|
||||
<ion-list>
|
||||
<ion-item v-for="nft of poaps" :key="nft.eventId">
|
||||
<ion-avatar
|
||||
v-if="nft?.image"
|
||||
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
|
||||
>
|
||||
<img
|
||||
:alt="nft?.title"
|
||||
:src="nft?.image"
|
||||
@error="nft.image = getUrl('assets/randomGrad.svg')"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label><b>{{ nft?.title }}</b></ion-label>
|
||||
</ion-item>
|
||||
<ion-item v-if="hasMore.poaps">
|
||||
<ion-button @click="loadMore('poaps')">Load More</ion-button>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, Ref, ref } from "vue";
|
||||
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, onIonViewWillEnter } from "@ionic/vue";
|
||||
import { getSelectedAccount } from "@/utils/platform"
|
||||
import { defineComponent, Ref, ref, reactive } from "vue";
|
||||
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, onIonViewWillEnter, IonItem, IonLabel, IonAvatar, IonList, IonButton, IonToast, IonLoading, IonIcon } from "@ionic/vue";
|
||||
import { getSelectedAccount, copyAddress, getUrl } from "@/utils/platform"
|
||||
import type { Account } from "@/extension/types"
|
||||
|
||||
import { copyOutline } from "ionicons/icons";
|
||||
|
||||
|
||||
const yupAssetsApi = 'https://api.yup.io/profile'
|
||||
|
||||
interface IProfileToken {
|
||||
address: string
|
||||
balance: number
|
||||
image: string
|
||||
name: string
|
||||
symbol: string
|
||||
}
|
||||
|
||||
interface IProfileNFT {
|
||||
address: string
|
||||
collectionImageURI: string
|
||||
collectionName: string
|
||||
imageURI: string
|
||||
link: string
|
||||
tokenId: number
|
||||
}
|
||||
|
||||
interface IProfilePOAP {
|
||||
description: string
|
||||
eventId: string
|
||||
image: string
|
||||
link: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar },
|
||||
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonAvatar, IonList, IonButton, IonToast, IonLoading, IonIcon },
|
||||
setup: () => {
|
||||
const selectedAccount = ref({}) as Ref<Account>
|
||||
const assets = ref({})
|
||||
const assets = ref({}) as Ref< {
|
||||
yupScore?: number
|
||||
tokens?: any[]
|
||||
nfts?: any[]
|
||||
poaps?: any[]
|
||||
}>
|
||||
const loading = ref(true)
|
||||
const isError = ref(false)
|
||||
const noAssets = ref(false)
|
||||
const toastState = ref(false);
|
||||
const ethTokens = ref([]) as Ref< IProfileToken[]>
|
||||
const polyTokens = ref([]) as Ref< IProfileToken[]>
|
||||
const ethNfts = ref([]) as Ref< IProfileNFT[]>
|
||||
const polyNfts = ref([]) as Ref< IProfileNFT[]>
|
||||
const poaps = ref([]) as Ref< IProfilePOAP[]>
|
||||
const hasMore = reactive({
|
||||
poaps: true,
|
||||
ethTokens: true,
|
||||
polyTokens: true,
|
||||
ethNfts: true,
|
||||
polyNfts: true,
|
||||
})
|
||||
const getToastRef = () => toastState;
|
||||
|
||||
onIonViewWillEnter(async () => {
|
||||
selectedAccount.value = await getSelectedAccount()
|
||||
@ -42,11 +235,100 @@ export default defineComponent({
|
||||
if(!('poaps' in assets.value) && !('tokens' in assets.value) && !('nfts' in assets.value)) {
|
||||
noAssets.value = true
|
||||
}
|
||||
if ('poaps' in assets.value) {
|
||||
poaps.value = assets.value?.poaps?.slice(0, 10) ?? []
|
||||
if(poaps.value.length >= (assets.value?.poaps?.length ?? 0)) {
|
||||
hasMore.poaps = false
|
||||
}
|
||||
}
|
||||
if ('nfts' in assets.value) {
|
||||
ethNfts.value = assets.value?.nfts?.filter(n => n.chain === 'ethereum').slice(0, 10) ?? []
|
||||
if(ethNfts.value.length >= (assets.value?.nfts?.filter(n => n.chain === 'ethereum').length ?? 0)) {
|
||||
hasMore.ethNfts = false
|
||||
}
|
||||
polyNfts.value = assets.value?.nfts?.filter(n => n.chain === 'polygon').slice(0, 10) ?? []
|
||||
if(polyNfts.value.length >= (assets.value?.nfts?.filter(n => n.chain === 'polygon').length ?? 0)) {
|
||||
hasMore.polyNfts = false
|
||||
}
|
||||
}
|
||||
if ('tokens' in assets.value) {
|
||||
ethTokens.value = assets.value?.tokens?.filter(n => n.chain === 'ethereum').slice(0, 10) ?? []
|
||||
if(ethTokens.value.length >= (assets.value?.tokens?.filter(n => n.chain === 'ethereum').length ?? 0)) {
|
||||
hasMore.ethTokens = false
|
||||
}
|
||||
polyTokens.value = assets.value?.tokens?.filter(n => n.chain === 'polygon').slice(0, 10) ?? []
|
||||
if(polyTokens.value.length >= (assets.value?.tokens?.filter(n => n.chain === 'polygon').length ?? 0)) {
|
||||
hasMore.polyTokens = false
|
||||
}
|
||||
}
|
||||
}else {
|
||||
isError.value = true
|
||||
}
|
||||
loading .value = false
|
||||
})
|
||||
|
||||
const loadMore = (type: string) => {
|
||||
switch(type) {
|
||||
case 'ethTokens': {
|
||||
ethTokens.value = assets.value?.tokens?.filter(n => n.chain === 'ethereum').slice(0, ethTokens.value.length + 10) ?? []
|
||||
if(ethTokens.value.length >= (assets.value?.tokens?.filter(n => n.chain === 'ethereum').length ?? 0)) {
|
||||
hasMore.ethTokens = false
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'polyTokens': {
|
||||
polyTokens.value = assets.value?.tokens?.filter(n => n.chain === 'polygon').slice(0, polyTokens.value.length + 10) ?? []
|
||||
if(polyTokens.value.length >= (assets.value?.tokens?.filter(n => n.chain === 'polygon').length ?? 0)) {
|
||||
hasMore.polyTokens = false
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'ethNfts': {
|
||||
ethNfts.value = assets.value?.nfts?.filter(n => n.chain === 'ethereum').slice(0, ethNfts.value.length + 10) ?? []
|
||||
if(ethNfts.value.length >= (assets.value?.nfts?.filter(n => n.chain === 'ethereum').length ?? 0)) {
|
||||
hasMore.ethNfts = false
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'polyNfts': {
|
||||
polyNfts.value = assets.value?.nfts?.filter(n => n.chain === 'polygon').slice(0, polyNfts.value.length + 10) ?? []
|
||||
if(polyNfts.value.length >= (assets.value?.nfts?.filter(n => n.chain === 'polygon').length ?? 0)) {
|
||||
hasMore.polyNfts = false
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'poaps': {
|
||||
poaps.value = assets.value?.poaps?.slice(0, poaps.value.length + 10) ?? []
|
||||
if(poaps.value.length >= (assets.value?.poaps?.length ?? 0)) {
|
||||
hasMore.poaps = false
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
selectedAccount,
|
||||
loading,
|
||||
isError,
|
||||
noAssets,
|
||||
assets,
|
||||
getToastRef,
|
||||
copyAddress,
|
||||
copyOutline,
|
||||
ethTokens,
|
||||
polyTokens,
|
||||
ethNfts,
|
||||
poaps,
|
||||
hasMore,
|
||||
polyNfts,
|
||||
loadMore,
|
||||
toastState,
|
||||
getUrl
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
@ -5,29 +5,87 @@
|
||||
<ion-title>History</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-item v-if="history.length === 0">
|
||||
You don't have any transaction history
|
||||
</ion-item>
|
||||
<ion-item v-else>
|
||||
<ion-list>
|
||||
<ion-item style="margin-bottom: 0.3rem;margin-top: 0.3rem" v-for="item of history" :key="item.txHash">
|
||||
<ion-list>
|
||||
<ion-item><b style="margin-right: 0.5rem">Date:</b> {{ new Date(item.date).toDateString() }}</ion-item>
|
||||
<ion-item button @click="copyAddress(item.txHash, getToastRef())">
|
||||
<p style="font-size: 0.7rem"><b style="margin-right: 0.5rem"><ion-icon style="margin-right: 0.3rem; display: inline-block;" :icon="copyOutline"></ion-icon>TxHash:</b>{{ item.txHash }}</p>
|
||||
</ion-item>
|
||||
<ion-item v-if="item.chainId"><b style="margin-right: 0.5rem">ChainId:</b> {{ item.chainId }}</ion-item>
|
||||
<ion-item v-if="item.webiste"><b style="margin-right: 0.5rem">Website:</b> {{ item.webiste }}</ion-item>
|
||||
<ion-item v-if="item.txUrl"><b style="margin-right: 0.5rem">ViewTx:</b> <a :href="item.txUrl">LINK</a></ion-item>
|
||||
</ion-list>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-button style="margin-left: 0.5rem" color="warning" @click="onWipeHistory">WIPE HISTORY</ion-button>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-item>
|
||||
|
||||
<ion-content class="ion-padding">Not implemented</ion-content>
|
||||
<ion-loading
|
||||
:is-open="loading"
|
||||
cssClass="my-custom-class"
|
||||
message="Please wait..."
|
||||
:duration="4000"
|
||||
@didDismiss="loading = false"
|
||||
>
|
||||
</ion-loading>
|
||||
<ion-toast
|
||||
:is-open="toastState"
|
||||
@didDismiss="toastState = false"
|
||||
message="Copied to clipboard"
|
||||
:duration="1500"
|
||||
></ion-toast>
|
||||
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, Ref, ref } from "vue";
|
||||
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, onIonViewWillEnter } from "@ionic/vue";
|
||||
import { getHistory } from '@/utils/platform'
|
||||
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, onIonViewWillEnter, IonItem, IonList, IonToast, IonLoading, IonButton, IonIcon } from "@ionic/vue";
|
||||
import { getHistory, copyAddress, wipeHistory } from '@/utils/platform'
|
||||
import type { HistoryItem } from '@/extension/types'
|
||||
|
||||
import { copyOutline } from "ionicons/icons";
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar },
|
||||
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonList, IonToast, IonLoading, IonButton, IonIcon },
|
||||
setup: () => {
|
||||
const history = ref([]) as Ref<HistoryItem[]>;
|
||||
const loading = ref(true)
|
||||
onIonViewWillEnter(async () => {
|
||||
const loading = ref(false)
|
||||
const toastState = ref(false);
|
||||
const getToastRef = () => toastState;
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true
|
||||
history.value = await getHistory()
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const onWipeHistory = async () => {
|
||||
await wipeHistory()
|
||||
loadData();
|
||||
}
|
||||
|
||||
onIonViewWillEnter(async () => {
|
||||
loadData()
|
||||
})
|
||||
return {
|
||||
history,
|
||||
loading
|
||||
loading,
|
||||
copyAddress,
|
||||
getToastRef,
|
||||
toastState,
|
||||
copyOutline,
|
||||
onWipeHistory
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-accordion-group v-if="!loading">
|
||||
<ion-accordion-group :value="defaultAccordionOpen" v-if="!loading">
|
||||
<ion-accordion value="1">
|
||||
<ion-item slot="header" color="light">
|
||||
<ion-label>Security</ion-label>
|
||||
@ -28,7 +28,7 @@
|
||||
<ion-toggle :key="updateKey" @ion-change="changeAutoLock" slot="end" :checked="settings.s.lockOutEnabled"></ion-toggle>
|
||||
</ion-item>
|
||||
<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-item>
|
||||
<ion-item :disabled="!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled">
|
||||
@ -41,7 +41,7 @@
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-label>Permanent Lock</ion-label>
|
||||
<ion-toggle :key="updateKey" slot="end" :disabled="!settings.s.enableStorageEnctyption" :checked="settings.s.encryptAfterEveryTx"></ion-toggle>
|
||||
<ion-toggle @ion-change="changePermaLock" :key="updateKey" slot="end" :disabled="!settings.s.enableStorageEnctyption" :checked="settings.s.encryptAfterEveryTx"></ion-toggle>
|
||||
</ion-item>
|
||||
<ion-item>Will require decrypt pass before any sign or transaction</ion-item>
|
||||
</ion-list>
|
||||
@ -54,11 +54,12 @@
|
||||
</ion-item>
|
||||
<div class="ion-padding" slot="content">
|
||||
<ion-list>
|
||||
<ion-radio-group :value="settings.s.theme">
|
||||
<ion-radio-group :value="radioTheme">
|
||||
<ion-item>
|
||||
<ion-radio
|
||||
slot="start"
|
||||
value="system"
|
||||
@click="changeTheme('system')"
|
||||
/>
|
||||
<ion-label>System Default</ion-label>
|
||||
</ion-item>
|
||||
@ -66,6 +67,7 @@
|
||||
<ion-radio
|
||||
slot="start"
|
||||
value="dark"
|
||||
@click="changeTheme('dark')"
|
||||
/>
|
||||
<ion-label>Dark</ion-label>
|
||||
</ion-item>
|
||||
@ -73,6 +75,7 @@
|
||||
<ion-radio
|
||||
slot="start"
|
||||
value="light"
|
||||
@click="changeTheme('light')"
|
||||
/>
|
||||
<ion-label>Light</ion-label>
|
||||
</ion-item>
|
||||
@ -85,7 +88,16 @@
|
||||
<ion-label>About</ion-label>
|
||||
</ion-item>
|
||||
<div class="ion-padding" slot="content">
|
||||
About text
|
||||
<p>Clear EVM Wallet (CLW) is a fully open-source wallet built with Vue, Ionic, and Ethers.</p>
|
||||
<p>It emulates Metamask Wallet and can be used as a drop-in replacement, right now if you have both extensions, CLW will overwrite Metamask.</p>
|
||||
<p>Main philosophy of the wallet is: no trackers, full control, export/import JSONs with accounts, fast generate new accounts, and wipe everything with one click.</p>
|
||||
<p>Github Repo: <a href="#" @click="openTab('https://github.com/andrei0x309/clear-wallet')">LINK</a></p>
|
||||
<br/>
|
||||
<p style="margin-bottom: 0.2rem">Some Web3 Projects I personally appreciate:</p>
|
||||
<p>YUP - web3 social platform <a href="#" @click="openTab('https://app.yup.io')">LINK</a></p>
|
||||
<p>Crypto-Leftists: web3 left-wing crypto community <a href="#" @click="openTab('https://discord.gg/gzA4bTCdhb')">LINK</a></p>
|
||||
<p>Idena: web3 fully private identity provider blockchain <a href="#" @click="openTab('https://www.idena.io/')">LINK</a></p>
|
||||
<p>Mirror: web3 publishing platform <a href="#" @click="openTab('https://mirror.xyz')">LINK</a></p>
|
||||
</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="4">
|
||||
@ -146,7 +158,7 @@
|
||||
<ion-content class="ion-padding">
|
||||
<ion-list v-if="settings.s.enableStorageEnctyption">
|
||||
<ion-item>
|
||||
<ion-label>Old Passord</ion-label>
|
||||
<ion-label>Old Password</ion-label>
|
||||
</ion-item> <ion-item>
|
||||
<ion-input v-model="mpPass" type="password"></ion-input>
|
||||
</ion-item>
|
||||
@ -185,7 +197,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, reactive, Ref } from "vue";
|
||||
import { storageWipe, getSettings, setSettings, getAccounts, saveSelectedAccount, replaceAccounts } from "@/utils/platform";
|
||||
import { storageWipe, getSettings, setSettings, getAccounts, saveSelectedAccount, replaceAccounts, openTab } from "@/utils/platform";
|
||||
import { decrypt, encrypt, getCryptoParams } from "@/utils/webCrypto"
|
||||
import { Account } from '@/extension/types'
|
||||
import { exportFile } from '@/utils/misc'
|
||||
@ -252,6 +264,8 @@ export default defineComponent({
|
||||
type ModalPromisePassword = null | { resolve: ((p?: unknown) => void), reject: ((p?: unknown) => void)}
|
||||
const modalGetPassword = ref(null) as Ref<ModalPromisePassword>
|
||||
const noAccounts = ref(true)
|
||||
const defaultAccordionOpen = ref("0")
|
||||
const radioTheme = ref('system') as Ref<'system' | 'light' | 'dark'>
|
||||
|
||||
const wipeStorage = async () => {
|
||||
loading.value = true;
|
||||
@ -271,12 +285,30 @@ export default defineComponent({
|
||||
const setEncryptToggle = (state: boolean) => {
|
||||
settings.s.enableStorageEnctyption = state
|
||||
updateKey.value++
|
||||
defaultAccordionOpen.value = "1"
|
||||
}
|
||||
|
||||
const changeAutoLock = async () => {
|
||||
settings.s.lockOutEnabled = !settings.s.lockOutEnabled
|
||||
updateKey.value++
|
||||
await saveSettings()
|
||||
defaultAccordionOpen.value = "1"
|
||||
}
|
||||
|
||||
const changePermaLock = async () => {
|
||||
settings.s.lockOutEnabled = !settings.s.encryptAfterEveryTx
|
||||
updateKey.value++
|
||||
await saveSettings()
|
||||
defaultAccordionOpen.value = "1"
|
||||
}
|
||||
|
||||
const changeTheme = async (theme: 'system' | 'light' | 'dark') => {
|
||||
document.body.classList.remove(radioTheme.value)
|
||||
document.body.classList.add(theme)
|
||||
radioTheme.value = theme
|
||||
settings.s.theme = theme
|
||||
await saveSettings()
|
||||
defaultAccordionOpen.value = "2"
|
||||
}
|
||||
|
||||
const changeEncryption = async () => {
|
||||
@ -352,7 +384,6 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
// settings.s.enableStorageEnctyption = true;
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@ -488,6 +519,7 @@ export default defineComponent({
|
||||
await Promise.all([getSettings().then((storeSettings) =>
|
||||
{
|
||||
settings.s = storeSettings
|
||||
radioTheme.value = settings.s.theme
|
||||
}),
|
||||
getAccounts().then((accounts) => {
|
||||
if(accounts.length) {
|
||||
@ -539,7 +571,12 @@ export default defineComponent({
|
||||
modalGetPassword,
|
||||
noAccounts,
|
||||
alertHeader,
|
||||
changeAutoLock
|
||||
changeAutoLock,
|
||||
defaultAccordionOpen,
|
||||
changeTheme,
|
||||
openTab,
|
||||
radioTheme,
|
||||
changePermaLock
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -57,7 +57,7 @@ import {
|
||||
import { hexTostr } from "@/utils/platform";
|
||||
import { approve, walletPing } from "@/extension/userRequest";
|
||||
import { useRoute } from "vue-router";
|
||||
import { getSelectedAccount, unBlockLockout, blockLockout } from "@/utils/platform";
|
||||
import { getSelectedAccount, unBlockLockout, blockLockout, clearPk, getSettings } from "@/utils/platform";
|
||||
import UnlockModal from "@/views/UnlockModal.vue";
|
||||
|
||||
export default defineComponent({
|
||||
@ -83,6 +83,7 @@ export default defineComponent({
|
||||
const alertMsg = ref("");
|
||||
const timerReject = ref(140);
|
||||
let interval: any;
|
||||
let pSettings = getSettings()
|
||||
|
||||
const onCancel = () => {
|
||||
window.close();
|
||||
@ -129,6 +130,13 @@ export default defineComponent({
|
||||
const modalResult = await openModal();
|
||||
if (modalResult) {
|
||||
unBlockLockout();
|
||||
if(!pSettings) {
|
||||
pSettings = getSettings()
|
||||
}
|
||||
const settings = await pSettings
|
||||
if(settings.encryptAfterEveryTx) {
|
||||
clearPk()
|
||||
}
|
||||
approve(rid);
|
||||
} else {
|
||||
onCancel();
|
||||
|
@ -157,7 +157,7 @@ import {
|
||||
import { ethers } from "ethers";
|
||||
import { approve, walletPing, walletSendData } from "@/extension/userRequest";
|
||||
import { useRoute } from "vue-router";
|
||||
import { getSelectedNetwork, getUrl, getPrices, numToHexStr, blockLockout, unBlockLockout, getSelectedAccount, strToHex} from '@/utils/platform'
|
||||
import { getSelectedNetwork, getUrl, getPrices, numToHexStr, blockLockout, unBlockLockout, getSelectedAccount, strToHex, getSettings, clearPk } from '@/utils/platform'
|
||||
import { getBalance, getGasPrice, estimateGas } from '@/utils/wallet'
|
||||
import type { Network } from '@/extension/types'
|
||||
import { mainNets } from "@/utils/networks";
|
||||
@ -209,6 +209,7 @@ export default defineComponent({
|
||||
const gasPriceModal = ref(false)
|
||||
const inGasPrice = ref(0)
|
||||
const inGasLimit = ref(0)
|
||||
let pSettings = getSettings()
|
||||
|
||||
let interval = 0
|
||||
const bars = ref(0)
|
||||
@ -227,7 +228,7 @@ export default defineComponent({
|
||||
const modal = await modalController.create({
|
||||
component: UnlockModal,
|
||||
componentProps: {
|
||||
unlockType: 'message'
|
||||
unlockType: 'transaction'
|
||||
}
|
||||
|
||||
});
|
||||
@ -240,10 +241,18 @@ export default defineComponent({
|
||||
const onSign = async () => {
|
||||
loading.value = true;
|
||||
const selectedAccount = await getSelectedAccount()
|
||||
loading.value = false
|
||||
if ((selectedAccount.pk ?? '').length !== 66) {
|
||||
const modalResult = await openModal()
|
||||
if(modalResult) {
|
||||
unBlockLockout()
|
||||
if(!pSettings) {
|
||||
pSettings = getSettings()
|
||||
}
|
||||
const settings = await pSettings
|
||||
if(settings.encryptAfterEveryTx) {
|
||||
clearPk()
|
||||
}
|
||||
approve(rid)
|
||||
}else {
|
||||
onCancel()
|
||||
@ -287,9 +296,9 @@ export default defineComponent({
|
||||
const pBalance = getBalance()
|
||||
const pGetPrices = getPrices()
|
||||
selectedNetwork.value = await getSelectedNetwork()
|
||||
userBalance.value = Number(ethers.utils.formatEther((await pBalance).toString()))
|
||||
userBalance.value = Number(ethers.utils.formatEther((await pBalance).toString() ?? '0x0'))
|
||||
|
||||
gasPrice.value = parseInt(ethers.utils.formatUnits((await pGasPrice).toString(), "gwei"), 10)
|
||||
gasPrice.value = parseInt(ethers.utils.formatUnits((await pGasPrice).toString() ?? '0x0', "gwei"), 10)
|
||||
try {
|
||||
gasLimit.value = parseInt((await pEstimateGas).toString(), 10)
|
||||
} catch (err) {
|
||||
|
@ -194,10 +194,10 @@ export default defineComponent({
|
||||
const onSwitchTemplates = async () => {
|
||||
loading.value = true;
|
||||
selectedNetwork.value = templateNetworks[Number(networkId.value)]
|
||||
saveNetwork(templateNetworks[Number(networkId.value)])
|
||||
saveSelectedNetwork(templateNetworks[Number(networkId.value)])
|
||||
await saveNetwork(templateNetworks[Number(networkId.value)])
|
||||
await saveSelectedNetwork(templateNetworks[Number(networkId.value)])
|
||||
approve(rid);
|
||||
loading.value = true;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const onSwitchNotExisting = async () => {
|
||||
|
@ -8,20 +8,29 @@
|
||||
<ion-title>Unlock to Proceed</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<ion-list>
|
||||
<ion-list v-if="unlockType === 'message'">
|
||||
<ion-item>To continue signing the message, unlock wallet.</ion-item>
|
||||
<ion-item>Closing will reject sigining the message</ion-item>
|
||||
<ion-item>Closing will reject sigining the message.</ion-item>
|
||||
</ion-list>
|
||||
<ion-list v-else-if="unlockType === 'viewPk'">
|
||||
<ion-item>To view the PK, unlock wallet.</ion-item>
|
||||
<ion-item>Closing will not show the PK.</ion-item>
|
||||
</ion-list>
|
||||
<ion-list v-else-if="unlockType === 'addAccount'">
|
||||
<ion-item>Storage Encrypted, Unlock to add account.</ion-item>
|
||||
<ion-item>Closing will not add the account.</ion-item>
|
||||
</ion-list>
|
||||
<ion-list v-else>
|
||||
<ion-item>To continue sending the transaction, unlock wallet.</ion-item>
|
||||
<ion-item>Closing will reject sending the tranzaction.</ion-item>
|
||||
<ion-item>Closing will reject sending the transaction.</ion-item>
|
||||
</ion-list>
|
||||
<ion-item>
|
||||
<ion-label>Unlock Password</ion-label>
|
||||
</ion-item> <ion-item>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-input v-model="mpPass" type="password"></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
@ -100,7 +109,7 @@ export default defineComponent({
|
||||
const alertMsg = ref('');
|
||||
|
||||
const close = () => {
|
||||
return modalController.dismiss(null, 'cancel');
|
||||
return modalController?.dismiss(null, 'cancel');
|
||||
}
|
||||
|
||||
const unlock = async () => {
|
||||
@ -109,14 +118,16 @@ export default defineComponent({
|
||||
let accounts = await getAccounts()
|
||||
const cryptoParams = await getCryptoParams(mpPass.value)
|
||||
const accProm = accounts.map(async a => {
|
||||
a.pk = await decrypt(a.encPk, cryptoParams)
|
||||
if(a.encPk) {
|
||||
a.pk = await decrypt(a.encPk, cryptoParams)
|
||||
}
|
||||
return a
|
||||
})
|
||||
accounts = await Promise.all(accProm)
|
||||
await replaceAccounts(accounts)
|
||||
await saveSelectedAccount(accounts[0])
|
||||
loading.value = false
|
||||
return modalController.dismiss(null, 'confirm');
|
||||
return modalController?.dismiss(mpPass.value, 'confirm');
|
||||
} catch {
|
||||
loading.value = false
|
||||
alertMsg.value = 'Decryption failed, password is not correct, try again.';
|
||||
|
Loading…
Reference in New Issue
Block a user