dev: 1.0.2

This commit is contained in:
Andrei O 2022-10-10 03:52:23 +03:00
parent 9932a1a522
commit 610fdc6afb
No known key found for this signature in database
GPG Key ID: B961E5B68389457E
17 changed files with 437 additions and 355 deletions

1
.gitignore vendored
View File

@ -29,3 +29,4 @@ npm-debug.log*
/platforms
/plugins
/www
/src/extension/inject.js

View File

@ -4,7 +4,14 @@ const allowedMethods = {
'eth_requestAccounts' : true,
'eth_chainId': true,
'personal_sign' : true,
'wallet_requestPermissions': true
'wallet_requestPermissions': true,
'eth_gasPrice': true,
'eth_getBlockByNumber': true,
'eth_blockNumber': true,
'eth_estimateGas': true,
'eth_sign': true,
'net_version': true,
'eth_sendTransaction': true,
}
window.addEventListener("message", (event) => {
@ -13,19 +20,23 @@ window.addEventListener("message", (event) => {
if (event.data.type && (event.data.type == "CLWALLET_CONTENT")) {
event.data.data.resId = event.data.resId
console.log('data in', event?.data)
if(event?.data?.data?.method ?? 'x' in allowedMethods)
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 };
console.log('data back', data)
window.postMessage(data, "*");
})
} else {
const data = { type: "CLWALLET_PAGE", data: { error: true, message: 'Unknown method requested'}, resId: event.data.resId };
window.postMessage(data, "*");
}
}
});
(function() {
chrome.runtime.connect({
name: 'content'
})
const script = document.createElement('script')
script.src = chrome.runtime.getURL('src/extension/inject.js')
document.documentElement.appendChild(script)

View File

@ -1,227 +0,0 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var _this = this;
var listners = {
accountsChanged: new Set(),
connect: new Set(),
disconnect: new Set,
chainChanged: new Set()
};
var promResolvers = {};
var listner = function (event) {
var _a, _b;
if (event.source != window)
return;
if (event.data.type && (event.data.type == "CLWALLET_PAGE")) {
if ((_b = (_a = event === null || event === void 0 ? void 0 : event.data) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) {
promResolvers[event.data.resId].reject(event.data.data);
}
else {
promResolvers[event.data.resId].resolve(event.data.data);
}
promResolvers[event.data.resId] = undefined;
}
};
window.addEventListener("message", listner);
var sendMessage = function (args) {
return new Promise(function (resolve, reject) {
var resId = crypto.randomUUID();
promResolvers[resId] = { resolve: resolve, reject: reject };
var data = { type: "CLWALLET_CONTENT", data: args, resId: resId };
window.postMessage(data, "*");
});
};
// chainId
// :
// "0x89"
// enable
// :
// ƒ ()
// isMetaMask
// :
// true
// networkVersion
// :
// "137"
// request
// :
// ƒ ()
// selectedAddress
// :
// null
// send
// :
// ƒ ()
// sendAsync
// :
// ƒ ()
// _events
// :
// {connect: ƒ}
// _eventsCount
// :
// 1
// _handleAccountsChanged
// :
// ƒ ()
// _handleChainChanged
// :
// ƒ ()
// _handleConnect
// :
// ƒ ()
// _handleDisconnect
// :
// ƒ ()
// _handleStreamDisconnect
// :
// ƒ ()
// _handleUnlockStateChanged
// :
// ƒ ()
// _jsonRpcConnection
// :
// {events: s, stream: d, middleware: ƒ}
// _log
// :
// u {name: undefined, levels: {…}, methodFactory: ƒ, getLevel: ƒ, setLevel: ƒ, …}
// _maxListeners
// :
// 100
// _metamask
// :
// Proxy {isUnlocked: ƒ, requestBatch: ƒ}
// _rpcEngine
// :
// o {_events: {…}, _eventsCount: 0, _maxListeners: undefined, _middleware: Array(3)}
// _rpcRequest
// :
// ƒ ()
// _sendSync
// :
// ƒ ()
// _sentWarnings
// :
// {enable: false, experimentalMethods: false, send: false, events: {…}}
// _state
// :
// {accounts: Array(0), isConnected: true, isUnlocked: true, initialized: true, isPermanentlyDisconnected: false}
// _warnOfDeprecation
// :
// ƒ (
var eth = new Proxy({
isConnected: function () {
return true;
},
// for maximum compatibility since is cloning the same API
isMetaMask: true,
enable: function () {
return sendMessage({ method: 'eth_requestAccounts', params: Array(0) });
},
request: function (args) {
return sendMessage(args);
},
on: function (eventName, callback) {
switch (eventName) {
case 'accountsChanged':
listners.accountsChanged.add(callback);
break;
case 'connect':
listners.connect.add(callback);
break;
case 'disconnect':
listners.disconnect.add(callback);
break;
case 'chainChanged':
listners.chainChanged.add(callback);
break;
default:
return;
}
},
// Simulate Metamask
_warnOfDeprecation: function () { return null; },
_state: {},
_sentWarnings: function () { return null; },
_rpcRequest: function () { return null; },
_handleAccountsChanged: function () { return null; },
chainId: "0x89",
networkVersion: "137",
selectedAddress: null,
send: function () { return null; },
sendAsync: function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, null];
}); }); },
_events: {},
_eventsCount: 0,
_handleChainChanged: function () { return null; },
_handleConnect: function () { return null; },
_handleDisconnect: function () { return null; },
_handleStreamDisconnect: function () { return null; },
_handleUnlockStateChanged: function () { return null; },
_jsonRpcConnection: {},
_log: {},
_maxListeners: 100,
_metamask: new Proxy({}, {}),
_rpcEngine: {}
}, {
set: function () { return false; },
deleteProperty: function () { return false; }
});
var injectWallet = function (win) {
Object.defineProperty(win, 'ethereum', {
get: function () {
return eth;
},
set: function () {
return;
}
});
window.tttest = 'test';
// Object.defineProperty(window, 'ethereum', 444)
console.log('Clear wallet injected', window.ethereum, win);
};
injectWallet(this);
// 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')});
// }, 3500)
// console.log( (window as any).ethereum.request({method: 'eth_chainId'}))

View File

@ -4,10 +4,10 @@ interface RequestArguments {
}
const listners = {
accountsChanged: new Set<() => void>(),
connect: new Set<() => void>(),
disconnect: new Set<() => void>,
chainChanged: new Set<() => void>(),
accountsChanged: new Set<(p?: any) => void>(),
connect: new Set<(p?: any) => void>(),
disconnect: new Set<(p?: any) => void>,
chainChanged: new Set<(p?: any) => void>(),
}
const promResolvers = {} as any
@ -15,14 +15,23 @@ const promResolvers = {} as any
const listner = function(event: any) {
if (event.source != window)
return;
if (event.data.type && (event.data.type == "CLWALLET_PAGE")) {
if (event.data.type && (event.data.type === "CLWALLET_PAGE")) {
if(event?.data?.data?.error){
promResolvers[event.data.resId].reject(event.data.data)
promResolvers[event.data.resId].reject(event.data.data);
console.log('rejected')
}else {
promResolvers[event.data.resId].resolve(event.data.data);
}
promResolvers[event.data.resId] = undefined;
} else if( event.data.type && (event.data.type === "CLWALLET_PAGE_LISTENER")) {
if((event?.data?.data?.listner ?? 'x') in listners ) {
try {
const listnerName = event?.data?.data?.listner as ('accountsChanged' | 'connect' | 'disconnect' | 'chainChanged')
listners[listnerName].forEach(listner => listner(event?.data?.data?.data));
} catch {
// ignore
}
}
}
}
@ -187,8 +196,6 @@ const injectWallet = (win: any) => {
return
}
});
(window as any).tttest = 'test'
// Object.defineProperty(window, 'ethereum', 444)
console.log('Clear wallet injected', (window as any).ethereum, win)
}
injectWallet(this)
@ -202,7 +209,7 @@ injectWallet(this)
// // (<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')});
// }, 3500)
// console.log( (window as any).ethereum.request({method: 'eth_chainId'}))

View File

@ -23,6 +23,7 @@
"scripting",
"webNavigation",
"tabs",
"notifications",
"storage",
"alarms",
"unlimitedStorage",

View File

@ -1,11 +1,13 @@
import { getAccounts, getSelectedAccount, getSelectedNetwork, smallRandomString, storageSave} from '@/utils/platform';
import { getAccounts, getSelectedAccount, getSelectedNetwork, smallRandomString, getSettings, clearPk, openTab } from '@/utils/platform';
import { userApprove, userReject, rIdWin, rIdData } from '@/extension/userRequest'
import { signMsg, getBalance, getBlockNumber, estimateGas, sendTransaction, getGasPrice } from '@/utils/wallet'
import { signMsg, getBalance, getBlockNumber, estimateGas, sendTransaction, getGasPrice, getBlockByNumber } from '@/utils/wallet'
import type { RequestArguments } from '@/extension/types'
import type { Account } from '@/extension/types'
import { rpcError } from '@/extension/rpcConstants'
import { updatePrices } from '@/utils/gecko'
let notificationUrl: string
chrome.runtime.onInstalled.addListener(() => {
console.log('Service worker installed');
chrome.runtime.connect(null as unknown as string, {
@ -13,9 +15,9 @@ chrome.runtime.onInstalled.addListener(() => {
})
})
chrome.runtime.onConnect.addListener(port => port.onDisconnect.addListener((a) =>
chrome.runtime.onConnect.addListener(port => port.onDisconnect.addListener(() =>
{
console.log('Service worker connected', storageSave('test-d', a));
console.log('Service worker connected');
}))
@ -40,7 +42,7 @@ chrome.alarms.onAlarm.addListener((alarm) => {
}
})
chrome.windows.onRemoved.addListener((winId) => {
chrome.windows.onRemoved.addListener(async (winId) => {
if (winId in (userReject ?? {})){
userReject[winId]?.()
}
@ -48,12 +50,28 @@ chrome.windows.onRemoved.addListener((winId) => {
userApprove[winId] = undefined
rIdWin[winId] = undefined
rIdData[winId] = undefined
chrome.windows.getAll().then((wins) => {
const wins = await chrome.windows.getAll()
if(wins.length === 0) {
storageSave('test-p', 'browser-closed')
const s = await getSettings()
if(s.enableStorageEnctyption) {
await clearPk()
}
}
})
})
const viewTxListner = async (id: string) => {
try {
const url = new URL(notificationUrl)
openTab(url.href)
chrome.notifications.clear(id)
} catch {
// ignore
}
}
if (!chrome.notifications.onButtonClicked.hasListener(viewTxListner)){
chrome.notifications.onButtonClicked.addListener(viewTxListner)
}
chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendResponse) => {
console.log(message);
@ -69,6 +87,15 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
case 'eth_call': {
break
}
case 'eth_getBlockByNumber': {
const params = message?.params?.[0] as any
sendResponse(await getBlockByNumber(params))
break;
}
case 'eth_gasPrice': {
sendResponse(await getGasPrice())
break;
}
case 'eth_getBalance': {
sendResponse(await getBalance())
break
@ -153,11 +180,45 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
})
})
sendResponse(
await sendTransaction({...params, ...(rIdData?.[String(gWin?.id ?? 0)] ?? {}) }, pEstimateGas, pGasPrice)
)
try {
const tx = await sendTransaction({...params, ...(rIdData?.[String(gWin?.id ?? 0)] ?? {}) }, pEstimateGas, pGasPrice)
sendResponse(tx)
const buttons = {} as any
const network = await getSelectedNetwork()
const notificationId = crypto.randomUUID()
if(network?.explorer) {
notificationUrl = `${network.explorer}/tx/${tx.hash}`.replace('//', '/')
buttons.buttons = [{
title: 'View Transaction',
}]
setTimeout(() => {
try {
chrome.notifications.clear(notificationId)
} catch {
// ignore
}
}, 6e4)
}
chrome.notifications.create({
notificationId,
message: 'Transaction Confirmed',
title: 'Success',
...(buttons)
} as any)
} catch {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'TX Failed'
})
chrome.notifications.create({
message: 'Transaction Failed',
title: 'Error',
} as any)
}
} catch(err) {
console.error(err)
// console.error(err)
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
@ -223,7 +284,7 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
chrome.windows.create({
height: 450,
width: 400,
url: chrome.runtime.getURL(`index.html?route=switch-network&param=${String(message?.params?.[0] ?? '' )}&rid=${String(message?.resId ?? '')}`),
url: chrome.runtime.getURL(`index.html?route=switch-network&param=${String(message?.params?.[0]?.chainId ?? '' )}&rid=${String(message?.resId ?? '')}`),
type: 'popup'
}).then((win) => {
userReject[String(win.id)] = reject
@ -232,9 +293,7 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
})
})
sendResponse(
await signMsg(String(message?.params?.[0]) ?? '' )
)
sendResponse(null)
} catch {
sendResponse({
error: true,

View File

@ -25,7 +25,7 @@ export interface Networks {
export interface RequestArguments {
method: string;
params?: unknown[];
params?: any[];
resId?: string
}
@ -49,5 +49,5 @@ export interface Settings {
lockOutPeriod: number
lockOutEnabled: boolean
theme: 'system' | 'light' | 'dark'
MP: string
lastLock: number
}

View File

@ -59,6 +59,10 @@ const routes: Array<RouteRecordRaw> = [
path: 'add-network',
component: () => import('@/views/AddNetwork.vue'),
},
{
path: 'add-network/edit/:chainId',
component: () => import('@/views/AddNetwork.vue'),
},
],
},
]

View File

@ -59,8 +59,8 @@ export const mainNets: {[key: number]: Network} = {
}
export const testNets = {
1: {
name: 'TESTNET Ethereum oerli',
5: {
name: 'TESTNET Ethereum Goerli',
rpc: 'https://rpc.ankr.com/eth_goerli',
chainId: 5,
explorer: 'https://goerli.etherscan.io',
@ -69,8 +69,8 @@ export const testNets = {
4: {
name: 'TESTNET Ethereum Rinkeby',
rpc: 'https://rpc.ankr.com/eth_rinkeby',
chainId: 5,
explorer: 'https://goerli.etherscan.io',
chainId: 4,
explorer: 'https://rinkeby.etherscan.io',
icon: 'eth.webp'
},
80001: {

View File

@ -75,7 +75,7 @@ export const setPrices = async (prices: Prices): Promise<void> => {
await storageSave('prices', prices )
}
export const getPrices = async (): Promise<void> => {
export const getPrices = async (): Promise<Prices> => {
return (await storageGet('prices'))?.prices ?? {} as unknown as Prices
}
@ -108,6 +108,15 @@ export const smallRandomString = () => {
return (Math.random() + 1).toString(36).substring(7);
}
export const clearPk = async (): Promise<void> => {
let accounts = await getAccounts()
const accProm = accounts.map(async a => {
return a
})
accounts = await Promise.all(accProm)
await replaceAccounts(accounts)
}
export const hexTostr = (hexStr: string) =>
{
if(hexStr.substring(0,2) === '0x') {
@ -124,6 +133,8 @@ export const hexTostr = (hexStr: string) =>
return hexStr
}
export const numToHexStr = (num: number) => `0x${num.toString(16)}`
export const copyAddress = async (address: string, toastRef: Ref<boolean>) => {
await navigator.clipboard.writeText(address)
toastRef.value = true

View File

@ -26,6 +26,12 @@ export const getBlockNumber = async () => {
return await provider.getBlockNumber()
}
export const getBlockByNumber = async (blockNum: number) => {
const network = await getSelectedNetwork()
const provider = new ethers.providers.JsonRpcProvider(network.rpc)
return await provider.getBlock(blockNum)
}
export const estimateGas = async ({to = '', from = '', data = '', value = '0x0' }: {to: string, from: string, data: string, value: string}) => {
const network = await getSelectedNetwork()
const provider = new ethers.providers.JsonRpcProvider(network.rpc)

View File

@ -2,27 +2,37 @@
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Add Network</ion-title>
<ion-title>{{ isEdit ? 'Edit Network': 'Add Network' }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button @click="templateModal=true" expand="block">Add from popular chain list</ion-button>
<ion-item>
<ion-label>Name</ion-label>
<ion-input v-model="name"></ion-input>
<ion-label>Name(*)</ion-label>
<ion-input v-model="name" placeholder="ex: Polygon"></ion-input>
</ion-item>
<ion-item>
<ion-label>ChainId</ion-label>
<ion-input v-model="chainId" type="number"></ion-input>
<ion-label>ChainId(*)</ion-label>
<ion-input v-model="chainId" placeholder="137" type="number"></ion-input>
</ion-item>
<ion-item button>
<ion-icon :icon="clipboardOutline" @click="paste('pasteRpc')" />
<ion-label>RPC URL</ion-label>
<ion-input id="pasteRpc" v-model="rpc" ></ion-input>
<ion-label>RPC URL(*)</ion-label>
<ion-input id="pasteRpc" placeholder="https://polygon-mainnet.g.alchemy.com/..." v-model="rpc" ></ion-input>
</ion-item>
<ion-item button>
<ion-icon :icon="clipboardOutline" @click="paste('pasteRpc')" />
<ion-label>Native Token Symbol(?)</ion-label>
<ion-input id="pasteRpc" placeholder="MATIC" v-model="symbol" ></ion-input>
</ion-item>
<ion-item button>
<ion-icon :icon="clipboardOutline" @click="paste('pasteExplorer')" />
<ion-label>Explorer(?)</ion-label>
<ion-input id="pasteExplorer" placeholder="https://polygon-scan.com" v-model="explorer" ></ion-input>
</ion-item>
<ion-item>
<ion-button @click="onCancel">Cancel</ion-button>
<ion-button @click="onAddNetwork">Add Network</ion-button>
<ion-button @click="onAddNetwork">{{ isEdit ? 'Edit Network': 'Add Network' }}</ion-button>
</ion-item>
<ion-alert
:is-open="alertOpen"
@ -85,13 +95,14 @@
<script lang="ts">
import { defineComponent, ref } from "vue";
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonInput, IonButton, IonIcon,
IonModal, IonList, IonSegment, IonSegmentButton, IonListHeader, IonButtons, IonAvatar, modalController, IonAlert
IonModal, IonList, IonSegment, IonSegmentButton, IonListHeader, IonButtons, IonAvatar, modalController, IonAlert, onIonViewWillEnter
} from "@ionic/vue";
import { getNetworks, saveSelectedNetwork, saveNetwork, getUrl, paste } from "@/utils/platform";
import { getNetworks, saveSelectedNetwork, getUrl, paste, replaceNetworks } from "@/utils/platform";
import router from "@/router";
import { mainNets, testNets } from "@/utils/networks"
import { useRoute } from 'vue-router'
import { clipboardOutline } from "ionicons/icons";
import type { Networks, Network } from '@/extension/types'
export default defineComponent({
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonInput, IonButton, IonIcon,
@ -100,10 +111,33 @@ export default defineComponent({
const name = ref('')
const chainId = ref(0)
const rpc = ref('')
const symbol = ref('')
const explorer = ref('')
const templateModal = ref(false)
const currentSegment = ref('mainnets')
const alertOpen = ref(false)
const alertMsg = ref('')
const route = useRoute()
const isEdit = route.path.includes('/edit')
const paramChainId = route.params.chainId ?? ""
let networksProm: Promise<Networks | undefined>
const fillNetworkInputs = (network: Network) => {
name.value = network.name
chainId.value = network.chainId
rpc.value = network.rpc
symbol.value = network.symbol ?? ''
explorer.value = network.explorer ?? ''
}
onIonViewWillEnter(async () => {
if(isEdit && paramChainId) {
networksProm = getNetworks()
const networks = await networksProm as Networks
fillNetworkInputs(networks[Number(paramChainId)])
}
})
const resetFields = () => {
name.value = ''
@ -133,25 +167,27 @@ export default defineComponent({
}
}
let p1 = Promise.resolve()
const networks = await getNetworks()
if( (Object.keys(networks).length ?? 0) < 1 ){
p1 = saveSelectedNetwork({
if(!networksProm) {
networksProm = getNetworks()
}
const networks = await networksProm as Networks
const network = {
name: name.value,
chainId: chainId.value,
rpc: rpc.value
})
rpc: rpc.value,
...( symbol.value ? {symbol: symbol.value}:{}),
...( explorer.value ? {explorer: explorer.value}:{})
}
if( (Object.keys(networks).length ?? 0) < 1 ){
p1 = saveSelectedNetwork(network)
} else {
if(chainId.value in networks){
if((chainId.value in networks) && !isEdit){
alertMsg.value = "Network already exists."
return alertOpen.value = true
}
}
const p2 = saveNetwork({
name: name.value,
chainId: chainId.value,
rpc: rpc.value
})
networks[chainId.value] = network
const p2 = replaceNetworks(networks)
await Promise.all([p1, p2])
router.push('/')
resetFields()
@ -166,9 +202,7 @@ export default defineComponent({
}
const fillTemplate = (network: typeof mainNets[1] ) =>{
name.value = network.name
chainId.value = network.chainId
rpc.value = network.rpc
fillNetworkInputs(network)
modalController.dismiss(null, 'cancel')
}
@ -188,7 +222,10 @@ export default defineComponent({
getUrl,
fillTemplate,
alertOpen,
alertMsg
alertMsg,
symbol,
explorer,
isEdit
}
}

View File

@ -27,8 +27,8 @@
</ion-label>
</ion-item>
<ion-item>
<ion-chip>Edit</ion-chip>
<ion-chip>Delete</ion-chip>
<router-link :to="`/add-network/edit/${network.chainId}`" ><ion-chip>Edit</ion-chip></router-link>
<ion-chip @click="deleteNetwork">Delete</ion-chip>
</ion-item>
</ion-list>
</ion-content>
@ -37,7 +37,7 @@
<script lang="ts">
import { defineComponent, ref, Ref } from "vue";
import { getNetworks, copyAddress, getUrl } from "@/utils/platform"
import { getNetworks, copyAddress, getUrl, replaceNetworks } from "@/utils/platform"
import {
IonContent,
IonHeader,
@ -75,7 +75,7 @@ export default defineComponent({
IonAvatar
},
setup () {
const networks = ref([]) as Ref<Networks>
const networks = ref({}) as Ref<Networks>
const loading = ref(true)
const toastState = ref(false)
@ -89,6 +89,13 @@ export default defineComponent({
})
}
const deleteNetwork = async (chainId: number) => {
loading.value = true
delete networks.value[chainId]
await replaceNetworks(networks.value)
loading.value = false
}
onIonViewWillEnter(() => {
loadData()
})
@ -101,7 +108,8 @@ export default defineComponent({
copyAddress,
getToastRef,
getUrl,
mainNets
mainNets,
deleteNetwork
}
}

View File

@ -88,6 +88,21 @@
</div>
</ion-accordion>
<ion-accordion value="4">
<ion-item slot="header" color="light">
<ion-label> Import / Export Accounts</ion-label>
</ion-item>
<div class="ion-padding" slot="content">
<ion-item>
<ion-label>Import Additional Accounts</ion-label>
<ion-button color="danger" @click="importAcc">Import</ion-button>
</ion-item>
<ion-item>
<ion-label>Export All Accounts</ion-label>
<ion-button color="danger" @click="exportAcc">Export</ion-button>
</ion-item>
</div>
</ion-accordion>
<ion-accordion value="5">
<ion-item slot="header" color="light">
<ion-label>Danger</ion-label>
</ion-item>
@ -240,7 +255,6 @@ export default defineComponent({
const saveSettings = async () => {
loading.value = true
settings.s.lockOutPeriod = settings.s.lockOutPeriod * 6e4
await setSettings(settings.s)
loading.value = false
}
@ -278,11 +292,9 @@ export default defineComponent({
const accProm = accounts.map(async a => {
a.encPk = await encrypt(mpPass.value, a.pk)
a.pk = ''
console.log(a)
return a
})
accounts = await Promise.all(accProm)
console.log(accounts)
await replaceAccounts(accounts)
await saveSelectedAccount(accounts[0])
setEncryptToggle(true)
@ -322,12 +334,19 @@ export default defineComponent({
loading.value = false
}
const importAcc = async () => {
//
}
const exportAcc = async () => {
//
}
onIonViewWillEnter( () => {
getSettings().then((storeSettings) =>
{
settings.s = storeSettings
settings.s.lockOutPeriod = (settings.s.lockOutPeriod / 6e4)
loading.value = false
})
@ -368,7 +387,9 @@ export default defineComponent({
modalDismiss,
setTime,
toastState,
toastMsg
toastMsg,
importAcc,
exportAcc
};
},
});

View File

@ -17,6 +17,7 @@
<ion-button @click="onCancel">Cancel</ion-button>
<ion-button @click="onSign">Sign</ion-button>
</ion-item>
<ion-item>Auto-reject Timer: {{ timerReject }}</ion-item>
<ion-alert
:is-open="alertOpen"
header="Error"
@ -49,13 +50,14 @@ import {
IonAlert,
IonText,
IonLoading,
modalController
modalController,
onIonViewWillEnter
} from "@ionic/vue";
// import { ethers } from "ethers";
import {
hexTostr,
} from "@/utils/platform";
import { approve } from "@/extension/userRequest";
import { approve, walletPing } from "@/extension/userRequest";
import { useRoute } from 'vue-router';
import { getSelectedAccount } from '@/utils/platform'
import UnlockModal from '@/views/UnlockModal.vue'
@ -81,11 +83,33 @@ export default defineComponent({
const signMsg = ref(hexTostr(route?.params?.param as string ?? ''));
const alertOpen = ref(false);
const alertMsg = ref("");
const timerReject = ref(140);
let interval: any
const onCancel = () => {
window.close()
window.close();
if(interval) {
try {
clearInterval(interval)
} catch {
// ignore
}
}
};
onIonViewWillEnter(async () => {
interval = setInterval(async () => {
if(timerReject.value <= 0) {
onCancel()
return;
}
timerReject.value -= 1
walletPing()
}, 1000) as any
});
const openModal = async () => {
const modal = await modalController.create({
component: UnlockModal,
@ -123,7 +147,8 @@ export default defineComponent({
alertOpen,
alertMsg,
onSign,
loading
loading,
timerReject
};
},
});

View File

@ -24,25 +24,25 @@
<ion-label>Transaction to Sign &amp; Send</ion-label>
</ion-item>
<ion-item>
Last Balance: {{ userBalance }}
Last Balance: {{ userBalance }} <span style="opacity:0.7" v-if="dollarPrice > 0">${{ dollarPrice*userBalance }}</span>
</ion-item>
<ion-item>
Contract: {{ contract }}
</ion-item>
<ion-item>
Tx Total Cost: {{ totalCost }}
Tx Total Cost: {{ totalCost }} <span style="opacity:0.7" v-if="dollarPrice > 0">${{ dollarPrice*totalCost }}</span>
</ion-item>
<ion-item>
Gas Fee: {{ gasFee }}
Gas Fee: {{ gasFee }} <span style="opacity:0.7" v-if="dollarPrice > 0">${{ dollarPrice*gasFee }}</span>
</ion-item>
<ion-item>
Tx value: {{ txValue }}
</ion-item>
<ion-item>
Gas Limit: {{ gasLimit }} <ion-button @click="setGasLimit">Set manually</ion-button>
Gas Limit: {{ gasLimit }} <ion-button style="margin-left: 1rem" @click="gasLimitModal=true">Set manually</ion-button>
</ion-item>
<ion-item>
Gas Price: {{ gasPrice}} <ion-button @click="setGasPrice">Set manually</ion-button>
Gas Price: {{ gasPrice }} <ion-button style="margin-left: 1rem" @click="gasPriceModal=true">Set manually</ion-button>
</ion-item>
<ion-item>
<ion-label>Raw TX:</ion-label>
@ -75,6 +75,59 @@
@didDismiss="loading = false"
>
</ion-loading>
<ion-modal
:is-open="gasLimitModal"
>
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="gasLimitModal=false">Close</ion-button>
</ion-buttons>
<ion-title>Set Gas Limit</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-list>
<ion-item>
<ion-label>Limit in units</ion-label>
</ion-item>
<ion-item>
<ion-input v-model="inGasLimit" type="number"></ion-input>
</ion-item>
<ion-item>
<ion-button @click="setGasLimit">Set Price</ion-button>
</ion-item>
</ion-list>
</ion-content>
</ion-modal>
<ion-modal
:is-open="gasPriceModal"
>
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="gasPriceModal=false">Close</ion-button>
</ion-buttons>
<ion-title>Set Gas Price</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-list>
<ion-item>
<ion-label>Price in gwei</ion-label>
</ion-item>
<ion-item>
<ion-input v-model="inGasPrice" type="number"></ion-input>
</ion-item>
<ion-item>
<ion-button @click="setGasPrice">Set Price</ion-button>
</ion-item>
</ion-list>
</ion-content>
</ion-modal>
</ion-content>
</ion-page>
@ -95,12 +148,15 @@ import {
IonTextarea,
onIonViewWillEnter,
IonList,
IonLoading
IonLoading,
IonModal,
IonButtons,
IonInput
} from "@ionic/vue";
import { ethers } from "ethers";
import { approve, walletPing, walletSendData } from "@/extension/userRequest";
import { useRoute } from "vue-router";
import { getSelectedNetwork, getUrl } from '@/utils/platform'
import { getSelectedNetwork, getUrl, getPrices, numToHexStr } from '@/utils/platform'
import { getBalance, getGasPrice, estimateGas } from '@/utils/wallet'
import type { Network } from '@/extension/types'
import { mainNets } from "@/utils/networks";
@ -118,7 +174,10 @@ export default defineComponent({
IonAlert,
IonTextarea,
IonList,
IonLoading
IonLoading,
IonModal,
IonButtons,
IonInput
},
setup: () => {
const route = useRoute();
@ -142,6 +201,12 @@ export default defineComponent({
const insuficientBalance = ref(false)
const gasPriceReFetch = ref(true)
const selectedNetwork = (ref(null) as unknown) as Ref<Network>;
const dollarPrice = ref(0)
const gasLimitModal = ref(false)
const gasPriceModal = ref(false)
const inGasPrice = ref(0)
const inGasLimit = ref(0)
let interval = 0
const bars = ref(0)
@ -170,6 +235,12 @@ export default defineComponent({
}
};
const newGasData = () => {
gasFee.value = Number(ethers.utils.formatUnits(String(gasLimit.value * gasPrice.value), "gwei"))
txValue.value = Number(ethers.utils.formatEther(params?.value ?? '0x0'))
totalCost.value = gasFee.value + txValue.value
}
onIonViewWillEnter(async () => {
console.log(params.value);
(window as any)?.resizeTo?.(600, 800)
@ -181,19 +252,26 @@ export default defineComponent({
})
const pGasPrice = getGasPrice()
const pBalance = getBalance()
const pGetPrices = getPrices()
selectedNetwork.value = await getSelectedNetwork()
userBalance.value = Number(ethers.utils.formatEther((await pBalance).toString()))
console.log(userBalance.value)
gasPrice.value = parseInt(ethers.utils.formatUnits((await pGasPrice).toString(), "gwei"), 10)
console.log(gasPrice.value)
gasLimit.value = parseInt((await pEstimateGas).toString(), 10)
gasFee.value = Number(ethers.utils.formatUnits(String(gasLimit.value * gasPrice.value), "gwei"))
txValue.value = Number(ethers.utils.formatEther(params?.value ?? '0x0'))
totalCost.value = gasFee.value + txValue.value
console.log( 'test', ethers.utils.formatUnits((await pGasPrice).toString(), "gwei"), ethers.utils.formatUnits(ethers.utils.parseUnits(gasPrice.value.toString(), "gwei"), "gwei") )
newGasData()
if(userBalance.value < totalCost.value){
insuficientBalance.value = true
}
const prices = await pGetPrices
console.log('dd', prices, selectedNetwork.value?.priceId)
if ( (selectedNetwork.value?.priceId ?? 'x') in prices ){
dollarPrice.value = prices[(selectedNetwork.value?.priceId ?? 'x')]?.usd ?? 0
}
loading.value=false
interval = setInterval(async () => {
@ -207,8 +285,7 @@ export default defineComponent({
timerFee.value = 20
loading.value=true
gasPrice.value = parseInt(ethers.utils.formatUnits((await getGasPrice()).toString(), "gwei"), 10)
gasFee.value = Number(ethers.utils.formatUnits(String(gasLimit.value * gasPrice.value), "gwei"))
txValue.value = Number(ethers.utils.formatEther(params?.value ?? '0x0'))
newGasData()
loading.value=false
}
}
@ -219,12 +296,24 @@ export default defineComponent({
}, 1000) as any
})
const setGasLimit = () => {
// TODO
gasLimit.value = inGasLimit.value
walletSendData(rid, {
gas: numToHexStr(gasLimit.value)
})
newGasData()
gasLimitModal.value = false
}
const setGasPrice = () => {
// TODO
gasPrice.value = inGasPrice.value
gasPriceReFetch.value = false
walletSendData(rid, {
gasPrice: ethers.utils.parseUnits(gasPrice.value.toString(), "gwei")
})
newGasData()
gasPriceModal.value = false
}
@ -252,7 +341,12 @@ export default defineComponent({
mainNets,
getUrl,
setGasLimit,
setGasPrice
setGasPrice,
dollarPrice,
gasLimitModal,
gasPriceModal,
inGasPrice,
inGasLimit
};
},
});

View File

@ -35,18 +35,18 @@
<ion-item>To</ion-item>
<ion-item>
<ion-list>
<ion-item>Network Name: {{ (templateNetworks as any)[selectedNetwork?.chainId]?.name }}</ion-item>
<ion-item>Network Name: {{ (templateNetworks as any)[networkId]?.name }}</ion-item>
<ion-item>
<ion-avatar
v-if="(templateNetworks as any)[selectedNetwork?.chainId]?.icon"
v-if="(templateNetworks as any)[networkId]?.icon"
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
>
<img
:alt="selectedNetwork?.name"
:src="getUrl('assets/chain-icons/' + (templateNetworks as any)[selectedNetwork?.chainId]?.icon)"
:src="getUrl('assets/chain-icons/' + (templateNetworks as any)[networkId]?.icon)"
/>
</ion-avatar>
<ion-label>Network ID: {{ (templateNetworks as any)[selectedNetwork?.chainId]?.chainId }}</ion-label>
<ion-label>Network ID: {{ (templateNetworks as any)[networkId]?.chainId }}</ion-label>
</ion-item>
</ion-list>
</ion-item>
@ -59,15 +59,16 @@
</ion-item>
<ion-item v-else>
<ion-list>
<ion-item>Request to change to unknown network ID: {{ }}</ion-item>
<ion-item>Do you want to go to {{ }}</ion-item>
<ion-item>Request to change to unknown network ID: {{ networkId }}</ion-item>
<ion-item>Do you want to go to {{ addChainUrl }}</ion-item>
<ion-item>To add it manually.</ion-item>
<ion-item>
<ion-button @click="onCancel">No</ion-button>
<ion-button @click="onSwitchTemplates">Yes</ion-button>
<ion-button @click="onSwitchNotExisting">Yes</ion-button>
</ion-item>
</ion-list>
</ion-item>
<ion-item>Auto-reject Timer: {{ timerReject }}</ion-item>
</ion-list>
<ion-alert
@ -108,11 +109,11 @@ import {
IonList,
} from "@ionic/vue";
// import { ethers } from "ethers";
import { hexTostr } from "@/utils/platform";
import { useRoute } from "vue-router";
import { getSelectedNetwork, getNetworks, getUrl } from "@/utils/platform";
import type { Network } from "@/extension/types";
import { getSelectedNetwork, getNetworks, getUrl, saveSelectedNetwork, saveNetwork, openTab} from "@/utils/platform";
import type { Network, Networks } from "@/extension/types";
import { mainNets, testNets } from "@/utils/networks";
import { approve, walletPing } from '@/extension/userRequest'
export default defineComponent({
components: {
@ -133,55 +134,76 @@ export default defineComponent({
const route = useRoute();
const loading = ref(true);
const rid = (route?.params?.rid as string) ?? "";
const networkId = ref(hexTostr((route?.params?.param as string) ?? ""));
const networkId = ref(String(Number(route?.params?.param as string ?? "")));
const alertOpen = ref(false);
const selectedNetwork = (ref(null) as unknown) as Ref<Network>;
const alertMsg = ref("");
const networkCase = ref("");
let pnetworks;
let pnetworks: Promise<Networks>;
const templateNetworks = Object.assign({}, mainNets, testNets) ?? {};
const addChainUrl = `${chainListPage}${networkId.value}`
const timerReject = ref(140);
let interval: any
const onCancel = () => {
window.close();
if(interval) {
try {
clearInterval(interval)
} catch {
// ignore
}
}
};
onIonViewWillEnter(async () => {
(window as any)?.resizeTo?.(600, 600)
pnetworks = getNetworks();
selectedNetwork.value = await getSelectedNetwork();
const chainId = parseInt(networkId.value, 16);
console.log(networkId.value)
const existingNetworks = await pnetworks;
if ((chainId ?? "0") in existingNetworks ?? {}) {
if ((networkId.value ?? "0") in existingNetworks ?? {}) {
networkCase.value = "exists";
} else if ((chainId ?? "0") in templateNetworks) {
} else if ((networkId.value ?? "0") in templateNetworks) {
networkCase.value = "inTemplates";
} else {
networkCase.value = "doesNotExist";
}
loading.value = false;
interval = setInterval(async () => {
if(timerReject.value <= 0) {
onCancel()
return;
}
timerReject.value -= 1
walletPing()
}, 1000) as any
});
const onSwitchExists = async () => {
loading.value = true;
// const selectedAccount = await getSelectedAccount();
// if ((selectedAccount.pk ?? "").length !== 66) {
// const modalResult = await openModal();
// if (modalResult) {
// approve(rid);
// } else {
// onCancel();
// }
// } else {
// approve(rid);
// }
// loading.value = false;
const existingNetworks = await pnetworks;
selectedNetwork.value = existingNetworks[Number(networkId.value)]
await saveSelectedNetwork(selectedNetwork.value)
approve(rid);
loading.value = false;
};
const onSwitchTemplates = async () => {
loading.value = true;
selectedNetwork.value = templateNetworks[Number(networkId.value)]
saveNetwork(templateNetworks[Number(networkId.value)])
saveSelectedNetwork(templateNetworks[Number(networkId.value)])
approve(rid);
loading.value = true;
};
const onSwitchNotExisting = async () => {
loading.value = true;
openTab(addChainUrl)
onCancel()
};
return {
@ -196,7 +218,9 @@ export default defineComponent({
templateNetworks,
getUrl,
onSwitchTemplates,
onSwitchNotExisting
onSwitchNotExisting,
addChainUrl,
timerReject
};
},
});