dev: 1.0.2

This commit is contained in:
Andrei O 2022-10-11 02:01:14 +03:00
parent 610fdc6afb
commit 1a4b531ae2
No known key found for this signature in database
GPG Key ID: B961E5B68389457E
16 changed files with 332 additions and 108 deletions

1
.gitignore vendored
View File

@ -30,3 +30,4 @@ npm-debug.log*
/plugins
/www
/src/extension/inject.js
readme.md

View File

@ -12,6 +12,7 @@ const allowedMethods = {
'eth_sign': true,
'net_version': true,
'eth_sendTransaction': true,
'wallet_switchEthereumChain': true
}
window.addEventListener("message", (event) => {

View File

@ -42,6 +42,7 @@ return new Promise((resolve, reject) => {
const resId = crypto.randomUUID()
promResolvers[resId] = { resolve, reject }
const data = { type: "CLWALLET_CONTENT", data: args, resId};
console.log('data in', data)
window.postMessage(data, "*");
})
}

View File

@ -1,4 +1,4 @@
import { getAccounts, getSelectedAccount, getSelectedNetwork, smallRandomString, getSettings, clearPk, openTab } from '@/utils/platform';
import { getAccounts, getSelectedAccount, getSelectedNetwork, smallRandomString, getSettings, clearPk, openTab, getUrl } from '@/utils/platform';
import { userApprove, userReject, rIdWin, rIdData } from '@/extension/userRequest'
import { signMsg, getBalance, getBlockNumber, estimateGas, sendTransaction, getGasPrice, getBlockByNumber } from '@/utils/wallet'
import type { RequestArguments } from '@/extension/types'
@ -40,6 +40,12 @@ chrome.alarms.onAlarm.addListener((alarm) => {
console.log('Prices updated')
})
}
getSettings().then((settings) => {
if( ((settings.lastLock + settings.lockOutPeriod * 6e4) < Date.now()) && settings.lockOutEnabled && !settings.lockOutBlocked ) {
settings.lastLock = Date.now()
clearPk()
}
})
})
chrome.windows.onRemoved.addListener(async (winId) => {
@ -89,11 +95,16 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
}
case 'eth_getBlockByNumber': {
const params = message?.params?.[0] as any
sendResponse(await getBlockByNumber(params))
const block = await getBlockByNumber(params) as any
block.gasLimit = block.gasLimit.toHexString()
block.gasUsed = block.gasUsed.toHexString()
block.baseFeePerGas = block.baseFeePerGas.toHexString()
block._difficulty = block._difficulty.toHexString()
sendResponse(block)
break;
}
case 'eth_gasPrice': {
sendResponse(await getGasPrice())
sendResponse((await getGasPrice()).toHexString())
break;
}
case 'eth_getBalance': {
@ -156,6 +167,7 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
if(!account || !network) {
return
}
params.from = account.address
const serializeParams = encodeURIComponent(JSON.stringify(params)) ?? ''
const pEstimateGas = estimateGas({
to: params?.to ?? '',
@ -199,14 +211,16 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
}
}, 6e4)
}
chrome.notifications.create({
notificationId,
chrome.notifications.create(notificationId,{
message: 'Transaction Confirmed',
title: 'Success',
iconUrl: getUrl('assets/extension-icon/wallet_128.png'),
type: 'basic',
...(buttons)
} as any)
} catch {
} catch (err) {
console.log(err)
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
@ -215,10 +229,12 @@ chrome.runtime.onMessage.addListener((message: RequestArguments, sender, sendRes
chrome.notifications.create({
message: 'Transaction Failed',
title: 'Error',
iconUrl: getUrl('assets/extension-icon/wallet_128.png'),
type: 'basic'
} as any)
}
} catch(err) {
// console.error(err)
// console.log(err)
sendResponse({
error: true,
code: rpcError.USER_REJECTED,

View File

@ -50,4 +50,5 @@ export interface Settings {
lockOutEnabled: boolean
theme: 'system' | 'light' | 'dark'
lastLock: number
lockOutBlocked: boolean
}

View File

@ -19,6 +19,10 @@ const routes: Array<RouteRecordRaw> = [
path: '/switch-network/:rid/:param',
component: () => import('@/views/SwitchNetwork.vue'),
},
{
path: '/contract-error/:rid/:param/:contract',
component: () => import('@/views/ContractError.vue'),
},
{
path: '/tabs/',
component: AppTabs,

View File

@ -6,16 +6,16 @@ export const mainNets: {[key: number]: Network} = {
name: 'Ethereum Main',
rpc: 'https://eth-mainnet.public.blastapi.io',
chainId: 1,
explorer: '',
explorer: 'https://etherscan.io',
icon: 'eth.webp',
symbol: 'ETH',
priceId: 'ethereum'
priceId: 'ethereum',
},
137: {
name: 'Polygon Mainnet',
rpc: 'https://polygon-rpc.com',
chainId: 137,
explorer: '',
explorer: 'https://polygonscan.com',
icon:'polygon.webp',
symbol: 'MATIC',
priceId: 'matic-network'
@ -24,7 +24,7 @@ export const mainNets: {[key: number]: Network} = {
name: 'Gnosis',
rpc: 'https://rpc.gnosischain.com/',
chainId: 100,
explorer: '',
explorer: 'https://gnosisscan.io',
icon:'xdai.webp',
symbol: 'xDAI',
priceId: 'xdai'
@ -33,7 +33,7 @@ export const mainNets: {[key: number]: Network} = {
name: 'Optimism',
rpc: 'https://mainnet.optimism.io',
chainId: 10,
explorer: '',
explorer: 'https://optimistic.etherscan.io',
icon: 'optimism.webp',
symbol: 'ETH',
priceId: 'ethereum'
@ -42,7 +42,7 @@ export const mainNets: {[key: number]: Network} = {
name: 'BSC Main',
rpc: 'https://bsc-dataseed2.binance.org',
chainId: 56,
explorer: '',
explorer: 'https://bscscan.com',
icon: 'binance.webp',
symbol: 'BNB',
priceId: 'binancecoin'
@ -51,7 +51,7 @@ export const mainNets: {[key: number]: Network} = {
name: 'Arbitrum One',
rpc: 'https://rpc.ankr.com/arbitrum',
chainId: 42161,
explorer: '',
explorer: 'https://explorer.offchainlabs.com',
icon: 'arbitrum.webp',
symbol: 'ETH',
priceId: 'ethereum'
@ -77,7 +77,7 @@ export const testNets = {
name: 'TESTNET Polygon',
rpc: 'https://rpc.ankr.com/polygon_mumbai',
chainId: 80001,
explorer: '',
explorer: 'https://mumbai.polygonscan.com/',
icon:'polygon.webp'
},
100100: {
@ -91,21 +91,21 @@ export const testNets = {
name: 'TESTNET Optimism Goreli',
rpc: 'https://goerli.optimism.io/',
chainId: 420,
explorer: '',
explorer: 'https://goerli.etherscan.io/',
icon: 'optimism.webp'
},
97: {
name: 'TESTNET BSC Main',
name: 'TESTNET BSC',
rpc: 'https://bsctestapi.terminet.io/rpc',
chainId: 97,
explorer: '',
explorer: 'https://testnet.bscscan.com/',
icon: 'binance.webp'
},
421613: {
name: 'TESTNET Arbitrum One',
rpc: 'https://goerli-rollup.arbitrum.io/rpc/',
chainId: 421613,
explorer: '',
explorer: 'https://testnet.arbiscan.io/',
icon: 'arbitrum.webp'
},
}

View File

@ -5,10 +5,10 @@ const defaultSettings = {
enableStorageEnctyption: false,
encryptAfterEveryTx: false,
lockOutEnabled: false,
lockOutPeriod: 12e4,
lockOutPeriod: 2,
lockOutBlocked: false,
theme: 'system',
MP: ''
lastLock: Date.now()
}
export const storageSave = async (key: string, value: any): Promise<void> =>{
@ -87,6 +87,20 @@ export const setSettings = async (settings: Settings): Promise<void> => {
await storageSave('settings', settings )
}
export const blockLockout = async (): Promise<Settings> => {
const settings = await getSettings()
settings.lockOutBlocked = true
await setSettings(settings)
return settings
}
export const unBlockLockout = async (): Promise<Settings> => {
const settings = await getSettings()
settings.lockOutBlocked = false
await setSettings(settings)
return settings
}
export const getBalanceCache = async (): Promise<string> => {
return (await storageGet('balance'))?.balance ?? '0x0'
}
@ -127,12 +141,13 @@ export const hexTostr = (hexStr: string) =>
}
return chunks.reduce(
(pv, cv) => `${pv}${String.fromCharCode(parseInt(cv, 16))}`,
''
).substring(0, 66)
'')
}
return hexStr
}
export const strToHex = (str: string) => `0x${str.split('').map( s => s.charCodeAt(0).toString(16) ).join('')}`
export const numToHexStr = (num: number) => `0x${num.toString(16)}`
export const copyAddress = async (address: string, toastRef: Ref<boolean>) => {

View File

@ -51,19 +51,9 @@ async function getKey(passwordBytes: Uint8Array) {
export const encrypt = async (password: string, data:string) => {
const enc = new TextEncoder()
const encData = enc.encode(data)
console.log(encData)
const encKey = enc.encode(password)
console.log(encKey)
const key = await getKey(encKey)
console.log(key)
const iv = await getIv()
console.log(iv)
console.log({
name: "AES-GCM",
iv,
},
key,
encData)
const encResult = await crypto.subtle.encrypt(
{
name: "AES-GCM",
@ -72,7 +62,6 @@ export const encrypt = async (password: string, data:string) => {
key,
encData,
)
console.log(JSON.stringify(new Uint8Array(encResult)))
return JSON.stringify(new Uint8Array(encResult))
}
@ -82,7 +71,6 @@ export const decrypt = async (encryptedData: string, password: string) => {
const key = await getKey(encKey)
const iv = await getIv()
const encryptedUint= new Uint8Array(Object.values(JSON.parse(encryptedData)));
console.log(encryptedUint)
const contentBytes = new Uint8Array(
await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, encryptedUint)
);

View File

@ -28,7 +28,7 @@
<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-input id="pasteExplorer" placeholder="https://polygonscan.com" v-model="explorer" ></ion-input>
</ion-item>
<ion-item>
<ion-button @click="onCancel">Cancel</ion-button>

113
src/views/ContractError.vue Normal file
View File

@ -0,0 +1,113 @@
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Contract Error</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-item
><ion-label>Network Name: {{ selectedNetwork?.name }}</ion-label></ion-item
>
<ion-item>
<ion-avatar
v-if="(mainNets as any)[selectedNetwork?.chainId]?.icon"
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
>
<img
:alt="selectedNetwork?.name"
:src="getUrl('assets/chain-icons/' + (mainNets as any)[selectedNetwork?.chainId]?.icon)"
/>
</ion-avatar>
<ion-label>Network ID: {{ selectedNetwork?.chainId }}</ion-label>
</ion-item>
<ion-item>
<ion-label>Transaction was aboreted before being sent</ion-label>
</ion-item>
<ion-item>
<ion-label>Gas Estimation Error due to Contract Error</ion-label>
</ion-item>
<ion-item> Contract: {{ contract }} </ion-item>
<ion-item>
<ion-label>Error From Contract:</ion-label>
<ion-textarea style="overflow-y: scroll;" :rows="10" :cols="20" :value="error" readonly></ion-textarea>
</ion-item>
<ion-item>
<ion-button @click="onCancel">Exit</ion-button>
</ion-item>
<ion-loading
:is-open="loading"
cssClass="my-custom-class"
message="Please wait..."
:duration="4000"
@didDismiss="loading = false"
>
</ion-loading>
</ion-content>
</ion-page>
</template>
<script lang="ts">
import { defineComponent, ref, Ref } from "vue";
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonItem,
IonLabel,
IonButton,
IonTextarea,
onIonViewWillEnter,
IonLoading,
} from "@ionic/vue";
import { useRoute } from "vue-router";
import { getSelectedNetwork, getUrl, hexTostr, } from "@/utils/platform";
import type { Network } from "@/extension/types";
import { mainNets } from "@/utils/networks";
export default defineComponent({
components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonItem,
IonLabel,
IonButton,
IonTextarea,
IonLoading,
},
setup: () => {
const route = useRoute();
const error = hexTostr((route.params?.param as string) ?? "");
const loading = ref(true);
const contract = (route.params?.contract as string) ?? ""
const selectedNetwork = (ref(null) as unknown) as Ref<Network>;
const onCancel = () => {
window.close();
};
onIonViewWillEnter(async () => {
(window as any)?.resizeTo?.(700, 600)
selectedNetwork.value = await getSelectedNetwork()
loading.value = false
});
return {
onCancel,
contract,
loading,
selectedNetwork,
mainNets,
getUrl,
error
};
},
});
</script>

View File

@ -16,7 +16,7 @@
<ion-content class="ion-padding">
<ion-list v-for="network of networks" :key="network.chainId">
<ion-item>
<ion-avatar v-if="(mainNets as any)[network.chainId].icon" style="margin-right: 1rem; width: 1.8rem; height:1.8rem;">
<ion-avatar v-if="(mainNets as any)[network.chainId]?.icon" style="margin-right: 1rem; width: 1.8rem; height:1.8rem;">
<img :alt="network.name" :src="getUrl('assets/chain-icons/' + (mainNets as any)[network.chainId].icon)" />
</ion-avatar>
<ion-label>
@ -27,8 +27,8 @@
</ion-label>
</ion-item>
<ion-item>
<router-link :to="`/add-network/edit/${network.chainId}`" ><ion-chip>Edit</ion-chip></router-link>
<ion-chip @click="deleteNetwork">Delete</ion-chip>
<ion-chip @click="editNetwork(network.chainId)" button>Edit</ion-chip>
<ion-chip @click="deleteNetwork(network.chainId)" button>Delete</ion-chip>
</ion-item>
</ion-list>
</ion-content>
@ -56,6 +56,7 @@ import {
} from "@ionic/vue";
import { mainNets } from "@/utils/networks"
import { addCircleOutline, copyOutline } from "ionicons/icons";
import router from '@/router/index'
import type { Networks } from '@/extension/types'
export default defineComponent({
@ -96,6 +97,10 @@ export default defineComponent({
loading.value = false
}
const editNetwork = (chainId: number) => {
router.push(`add-network/edit/${chainId}`)
}
onIonViewWillEnter(() => {
loadData()
})
@ -109,7 +114,8 @@ export default defineComponent({
getToastRef,
getUrl,
mainNets,
deleteNetwork
deleteNetwork,
editNetwork
}
}

View File

@ -94,11 +94,12 @@
<div class="ion-padding" slot="content">
<ion-item>
<ion-label>Import Additional Accounts</ion-label>
<ion-button color="danger" @click="importAcc">Import</ion-button>
<input ref="importFile" type="file" accept=".json" />
<ion-button color="warning" @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-button color="warning" @click="exportAcc">Export</ion-button>
</ion-item>
</div>
</ion-accordion>
@ -181,7 +182,7 @@
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from "vue";
import { defineComponent, ref, reactive, Ref } from "vue";
import { storageWipe, getSettings, setSettings, getAccounts, saveSelectedAccount, replaceAccounts } from "@/utils/platform";
import { decrypt, encrypt } from "@/utils/webCrypto"
// import { Account } from '@/extension/type'
@ -243,6 +244,7 @@ export default defineComponent({
const alertMsg = ref('');
const toastState = ref(false);
const toastMsg = ref('');
const importFile = ref(null) as unknown as Ref<HTMLInputElement>
const wipeStorage = async () => {
loading.value = true;
@ -334,8 +336,44 @@ export default defineComponent({
loading.value = false
}
const validateFile = () => {
return new Promise((resolve) => {
try {
if (!importFile.value?.value?.length) {
return resolve({
error: 'Import json file is missing'
})
}
const reader = new FileReader();
reader.onload = (event) => {
const json = JSON.parse(event?.target?.result as string)
if(!json.length){
return resolve({ error: 'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key" },{...}]' })
}
const test = json.some((e:any) => ( !('pk' in e ) || !('name' in e) || !(e.pk.length !== 66 && e.pk.length !== 64)))
if(test) {
return resolve({ error: 'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key" },{...}], Also PK must be valid' })
}
return resolve({ error: false })
}
reader.readAsText(importFile.value?.files?.[0] as File);
}catch {
return resolve(
{
error: 'Parsing JSON file'
})
}
})
}
const importAcc = async () => {
//
const validation = await validateFile() as { error: any }
if (validation.error) {
alertMsg.value = validation.error
alertOpen.value = true
return
}
}
const exportAcc = async () => {
@ -389,7 +427,8 @@ export default defineComponent({
toastState,
toastMsg,
importAcc,
exportAcc
exportAcc,
importFile
};
},
});

View File

@ -51,16 +51,14 @@ import {
IonText,
IonLoading,
modalController,
onIonViewWillEnter
onIonViewWillEnter,
} from "@ionic/vue";
// import { ethers } from "ethers";
import {
hexTostr,
} from "@/utils/platform";
import { hexTostr } from "@/utils/platform";
import { approve, walletPing } from "@/extension/userRequest";
import { useRoute } from 'vue-router';
import { getSelectedAccount } from '@/utils/platform'
import UnlockModal from '@/views/UnlockModal.vue'
import { useRoute } from "vue-router";
import { getSelectedAccount, unBlockLockout, blockLockout } from "@/utils/platform";
import UnlockModal from "@/views/UnlockModal.vue";
export default defineComponent({
components: {
@ -74,23 +72,24 @@ export default defineComponent({
IonButton,
IonAlert,
IonText,
IonLoading
IonLoading,
},
setup: () => {
const route = useRoute()
const loading = ref(false)
const rid = route?.params?.rid as string ?? '';
const signMsg = ref(hexTostr(route?.params?.param as string ?? ''));
const route = useRoute();
const loading = ref(false);
const rid = (route?.params?.rid as string) ?? "";
const signMsg = ref(hexTostr((route?.params?.param as string) ?? ""));
const alertOpen = ref(false);
const alertMsg = ref("");
const timerReject = ref(140);
let interval: any
let interval: any;
const onCancel = () => {
window.close();
if (interval) {
try {
clearInterval(interval)
unBlockLockout();
clearInterval(interval);
} catch {
// ignore
}
@ -98,48 +97,48 @@ export default defineComponent({
};
onIonViewWillEnter(async () => {
blockLockout();
interval = setInterval(async () => {
if (timerReject.value <= 0) {
onCancel()
onCancel();
return;
}
timerReject.value -= 1
walletPing()
}, 1000) as any
timerReject.value -= 1;
walletPing();
}, 1000) as any;
});
const openModal = async () => {
const modal = await modalController.create({
component: UnlockModal,
componentProps: {
unlockType: 'message'
}
unlockType: "message",
},
});
modal.present();
const { role } = await modal.onWillDismiss();
if(role === 'confirm') return true
return false
}
if (role === "confirm") return true;
return false;
};
const onSign = async () => {
loading.value = true;
const selectedAccount = await getSelectedAccount()
if ((selectedAccount.pk ?? '').length !== 66) {
const modalResult = await openModal()
const selectedAccount = await getSelectedAccount();
if ((selectedAccount.pk ?? "").length !== 66) {
const modalResult = await openModal();
if (modalResult) {
approve(rid)
unBlockLockout();
approve(rid);
} else {
onCancel()
onCancel();
}
} else {
approve(rid)
}
loading.value = false
unBlockLockout();
approve(rid);
}
loading.value = false;
};
return {
signMsg,
@ -148,7 +147,7 @@ export default defineComponent({
alertMsg,
onSign,
loading,
timerReject
timerReject,
};
},
});

View File

@ -46,7 +46,7 @@
</ion-item>
<ion-item>
<ion-label>Raw TX:</ion-label>
<ion-textarea :rows="10" :cols="20" :value="signTxData" readonly></ion-textarea>
<ion-textarea style="overflow-y: scroll;" :rows="10" :cols="20" :value="signTxData" readonly></ion-textarea>
</ion-item>
<ion-item>
<ion-button @click="onCancel">Cancel</ion-button>
@ -151,15 +151,18 @@ import {
IonLoading,
IonModal,
IonButtons,
IonInput
IonInput,
modalController
} from "@ionic/vue";
import { ethers } from "ethers";
import { approve, walletPing, walletSendData } from "@/extension/userRequest";
import { useRoute } from "vue-router";
import { getSelectedNetwork, getUrl, getPrices, numToHexStr } from '@/utils/platform'
import { getSelectedNetwork, getUrl, getPrices, numToHexStr, blockLockout, unBlockLockout, getSelectedAccount, strToHex} from '@/utils/platform'
import { getBalance, getGasPrice, estimateGas } from '@/utils/wallet'
import type { Network } from '@/extension/types'
import { mainNets } from "@/utils/networks";
import UnlockModal from '@/views/UnlockModal.vue'
import router from "@/router";
export default defineComponent({
components: {
@ -220,14 +223,43 @@ export default defineComponent({
signTxData.value = JSON.stringify( params, null, 2)
}
const onSign = () => {
approve(rid);
};
const openModal = async () => {
const modal = await modalController.create({
component: UnlockModal,
componentProps: {
unlockType: 'message'
}
});
modal.present();
const { role } = await modal.onWillDismiss();
if(role === 'confirm') return true
return false
}
const onSign = async () => {
loading.value = true;
const selectedAccount = await getSelectedAccount()
if ((selectedAccount.pk ?? '').length !== 66) {
const modalResult = await openModal()
if(modalResult) {
unBlockLockout()
approve(rid)
}else {
onCancel()
}
}else {
unBlockLockout()
approve(rid)
}
loading.value = false
}
const onCancel = () => {
window.close();
if(interval) {
try {
unBlockLockout()
clearInterval(interval)
} catch {
// ignore
@ -250,6 +282,7 @@ export default defineComponent({
data: params?.data ?? '',
value: params?.value ?? '0x0'
})
blockLockout()
const pGasPrice = getGasPrice()
const pBalance = getBalance()
const pGetPrices = getPrices()
@ -257,19 +290,26 @@ export default defineComponent({
userBalance.value = Number(ethers.utils.formatEther((await pBalance).toString()))
gasPrice.value = parseInt(ethers.utils.formatUnits((await pGasPrice).toString(), "gwei"), 10)
try {
gasLimit.value = parseInt((await pEstimateGas).toString(), 10)
} catch (err) {
const errorToHex = strToHex(String(err))
router.push(`/contract-error/${rid}/${errorToHex}/${contract}`)
loading.value = false
return
}
inGasPrice.value = gasPrice.value
inGasLimit.value = gasLimit.value
console.log( 'test', ethers.utils.formatUnits((await pGasPrice).toString(), "gwei"), ethers.utils.formatUnits(ethers.utils.parseUnits(gasPrice.value.toString(), "gwei"), "gwei") )
// 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

View File

@ -27,7 +27,7 @@ export default defineConfig({
rollupOptions: {
plugins: [nodePolyfills()]
},
sourcemap: false,
sourcemap: true,
chunkSizeWarningLimit: 1000,
commonjsOptions: {
transformMixedEsModules: true