Compare commits
4 Commits
73b3d6e26d
...
c88cfc22b8
Author | SHA1 | Date |
---|---|---|
Andrei O | c88cfc22b8 | |
Andrei O | 97d37021f0 | |
Andrei O | 7a187cd0cf | |
Andrei O | 498629d073 |
|
@ -1,5 +1,13 @@
|
|||
# Changelog
|
||||
|
||||
## Manifest Version 1.3.3
|
||||
|
||||
- improved eth_call and eth_blockNumber to be more compatible with older websites
|
||||
- better error internal handling
|
||||
- modify the receipt returned to resamble more the one from Metamask
|
||||
- change some notes in about
|
||||
- refactored account name edit to be more user friendly
|
||||
|
||||
## Manifest Version 1.3.2
|
||||
|
||||
- added button to open non kyc exchange, no referral is used to maximize privacy
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "clear-wallet",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3",
|
||||
"private": true,
|
||||
"description": "Clear Wallet (CLW) is a wallet that helps you manage your Ethereum assets and interact with Ethereum dApps and contracts with the main focus on absolute privacy.",
|
||||
"scripts": {
|
||||
|
|
|
@ -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 |
|
@ -165,7 +165,7 @@ class MetaMaskAPI {
|
|||
} else if (typeof arg1 === 'object') {
|
||||
return sendMessage(arg1 as RequestArguments)
|
||||
} else {
|
||||
console.info('ERROR: Clear Wallet: faulty request')
|
||||
console.error('ERROR: Clear Wallet: faulty request')
|
||||
}
|
||||
}else if( typeof arg1 === 'string' ) {
|
||||
return sendMessage({
|
||||
|
@ -323,7 +323,7 @@ const listner = function(event: any) {
|
|||
try {
|
||||
if(event?.data?.data?.error){
|
||||
promResolvers.get(event.data.resId)?.reject(event.data.data);
|
||||
console.info('Error: ', event?.data?.data)
|
||||
// console.info('Error: ', event?.data?.data)
|
||||
}else {
|
||||
promResolvers.get(event.data.resId)?.resolve(event.data.data);
|
||||
}
|
||||
|
@ -369,14 +369,14 @@ const proxy1 = new Proxy(new MetaMaskAPI(), {
|
|||
// Intercept method calls and log them
|
||||
if (typeof target[prop] === 'function') {
|
||||
return function (...args: any[]) {
|
||||
console.log(`Calling ${prop} with arguments:`, args);
|
||||
// console.log(`Calling ${prop} with arguments:`, args);
|
||||
// eslint-disable-next-line prefer-spread
|
||||
const result = target[prop].apply(target, args);
|
||||
console.log(`${prop} returned:`, result);
|
||||
// console.log(`${prop} returned:`, result);
|
||||
return result;
|
||||
};
|
||||
} else {
|
||||
console.log(`Reading ${prop}`);
|
||||
// console.log(`Reading ${prop}`);
|
||||
return target[prop];
|
||||
}
|
||||
},
|
||||
|
@ -397,7 +397,7 @@ Object.defineProperty(win, 'web3', {
|
|||
sendMessage({
|
||||
method: 'wallet_ready'
|
||||
}, true)
|
||||
console.log('Clear wallet injected', (window as any).ethereum, win)
|
||||
// console.log('Clear wallet injected', (window as any).ethereum, win)
|
||||
}
|
||||
|
||||
injectWallet(this);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"name": "__MSG_appName__",
|
||||
"description": "__MSG_appDesc__",
|
||||
"default_locale": "en",
|
||||
"version": "1.3.2",
|
||||
"version_name": "1.3.2",
|
||||
"version": "1.3.3",
|
||||
"version_name": "1.3.3",
|
||||
"icons": {
|
||||
"16": "assets/extension-icon/wallet_16.png",
|
||||
"32": "assets/extension-icon/wallet_32.png",
|
||||
|
|
|
@ -167,24 +167,36 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
|
|||
// ETH API
|
||||
switch (message.method) {
|
||||
case 'eth_call': {
|
||||
sendResponse(await evmCall(message?.params?.[0]))
|
||||
try {
|
||||
sendResponse(await evmCall(message?.params ?? []))
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_call', e)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'eth_getBlockByNumber': {
|
||||
try {
|
||||
const params = message?.params?.[0] as any
|
||||
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)
|
||||
} catch {
|
||||
const newBlock = {...block}
|
||||
newBlock.gasLimit = numToHexStr(block.gasLimit)
|
||||
newBlock.gasUsed = numToHexStr(block.gasUsed)
|
||||
newBlock.baseFeePerGas = numToHexStr(block.baseFeePerGas)
|
||||
newBlock._difficulty = numToHexStr(block.difficulty)
|
||||
newBlock.difficulty = block._difficulty
|
||||
sendResponse(newBlock)
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.log('eth_getBlockByNumber', e)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -195,49 +207,52 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
|
|||
}else {
|
||||
sendResponse(numToHexStr(Number(await getTxCount(message?.params?.[0] as string))))
|
||||
}
|
||||
} catch {
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
|
||||
console.error('eth_getTransactionCount', e)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'eth_getTransactionByHash': {
|
||||
try {
|
||||
sendResponse(await getTxByHash(message?.params?.[0] as string))
|
||||
} catch {
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_getTransactionByHash', e)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'eth_getTransactionReceipt':{
|
||||
try {
|
||||
sendResponse(await getTxReceipt(message?.params?.[0] as string))
|
||||
} catch {
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_getTransactionReceipt', e)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'eth_gasPrice': {
|
||||
try {
|
||||
sendResponse(numToHexStr(BigInt(Math.trunc(await getGasPrice() * 1e9))))
|
||||
} catch {
|
||||
} catch(e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_gasPrice', e)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -246,36 +261,39 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
|
|||
const balance = await getBalance()
|
||||
const balanceHex = numToHexStr(balance ?? 0n)
|
||||
sendResponse(balanceHex)
|
||||
} catch {
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_getBalance', e)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'eth_getCode': {
|
||||
try {
|
||||
sendResponse(await getCode(message?.params?.[0] as string))
|
||||
} catch {
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_getCode', e)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'eth_blockNumber': {
|
||||
try {
|
||||
sendResponse(await getBlockNumber())
|
||||
} catch {
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_blockNumber', e)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -311,12 +329,14 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
|
|||
code: rpcError.USER_REJECTED,
|
||||
message: 'Gas estimate failed'
|
||||
})
|
||||
}
|
||||
} else {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_estimateGas', err)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -324,12 +344,13 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
|
|||
case 'eth_accounts': {
|
||||
try {
|
||||
sendResponse(await getSelectedAddress())
|
||||
} catch {
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_accounts', e)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -338,12 +359,13 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
|
|||
const network = await getSelectedNetwork()
|
||||
const chainId = network?.chainId ?? 0
|
||||
sendResponse(`0x${chainId.toString(16)}`)
|
||||
} catch {
|
||||
} catch (e) {
|
||||
sendResponse({
|
||||
error: true,
|
||||
code: rpcError.USER_REJECTED,
|
||||
message: 'No network or user selected'
|
||||
})
|
||||
console.error('eth_chainId', e)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
|
@ -1,5 +1,30 @@
|
|||
import { getSelectedAccount, getSelectedNetwork } from '@/utils/platform';
|
||||
import { ethers} from "ethers"
|
||||
import { getSelectedAccount, getSelectedNetwork, numToHexStr } from '@/utils/platform';
|
||||
import { ethers } from "ethers"
|
||||
|
||||
const convertReceipt = (receipt: ethers.TransactionReceipt | null) => {
|
||||
if(!receipt) return null
|
||||
const newReceipt = {...receipt} as any
|
||||
newReceipt.transactionHash = newReceipt.hash
|
||||
newReceipt.blockNumber = numToHexStr(newReceipt.blockNumber)
|
||||
newReceipt.index = numToHexStr(newReceipt.index)
|
||||
newReceipt.transactionIndex = newReceipt.index
|
||||
newReceipt.cumulativeGasUsed = numToHexStr(newReceipt.cumulativeGasUsed)
|
||||
newReceipt.gasUsed = numToHexStr(newReceipt.gasUsed)
|
||||
newReceipt.gasPrice = numToHexStr(newReceipt.gasPrice)
|
||||
newReceipt.type = "0x2"
|
||||
newReceipt.status = numToHexStr(newReceipt.status)
|
||||
newReceipt.logs = receipt?.logs?.map((log: any) => {
|
||||
return {
|
||||
...log,
|
||||
blockNumber: numToHexStr(log.blockNumber),
|
||||
logIndex: numToHexStr(log.index),
|
||||
transactionIndex: numToHexStr(log.transactionIndex),
|
||||
removed: false
|
||||
}
|
||||
})
|
||||
return newReceipt
|
||||
}
|
||||
|
||||
|
||||
export const signMsg = async (msg: string) => {
|
||||
const account = await getSelectedAccount()
|
||||
|
@ -55,10 +80,15 @@ export const estimateGas = async ({to = '', from = '', data = '', value = '0x0'
|
|||
return await provider.estimateGas({to, from, data, value})
|
||||
}
|
||||
|
||||
export const evmCall = async ({to = '', from = '', data = '', value = '0x0' }: {to: string, from: string, data: string, value: string}) => {
|
||||
export const evmCall = async (params: any[]) => {
|
||||
const param1 = (params[0] ?? {to:'', from: '', data:'', value: '0x0', blockTag: 'latest' }) as {to: string, from: string, data: string, value: string, blockTag: string}
|
||||
const param2 = (params[1] ?? 'latest') as string
|
||||
param1.blockTag = param2
|
||||
|
||||
|
||||
const network = await getSelectedNetwork()
|
||||
const provider = new ethers.JsonRpcProvider(network.rpc)
|
||||
return await provider.call({to, from, data, value})
|
||||
return await provider.call(param1)
|
||||
}
|
||||
|
||||
export const getTxByHash = async (hash: string) => {
|
||||
|
@ -68,9 +98,16 @@ export const getTxByHash = async (hash: string) => {
|
|||
}
|
||||
|
||||
export const getTxReceipt = async (hash: string) => {
|
||||
try {
|
||||
const network = await getSelectedNetwork()
|
||||
const provider = new ethers.JsonRpcProvider(network.rpc)
|
||||
return await provider.getTransactionReceipt(hash)
|
||||
const receipt = await provider.getTransactionReceipt(hash)
|
||||
|
||||
return convertReceipt(receipt)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const getCode = async (addr: string) => {
|
||||
|
|
|
@ -42,9 +42,16 @@
|
|||
</template>
|
||||
<ion-item>
|
||||
<ion-button @click="onCancel">Cancel</ion-button>
|
||||
<ion-button @click="onAddAccount">{{
|
||||
isEdit ? "Edit Account" : "Add Account"
|
||||
}}</ion-button>
|
||||
<ion-button
|
||||
@click="
|
||||
() => {
|
||||
isEdit ? onEditAccount() : onAddAccount();
|
||||
}
|
||||
"
|
||||
expand="full"
|
||||
color="primary"
|
||||
>{{ isEdit ? "Edit Account" : "Add Account" }}</ion-button
|
||||
>
|
||||
</ion-item>
|
||||
<ion-alert
|
||||
:is-open="alertOpen"
|
||||
|
@ -120,6 +127,7 @@ import {
|
|||
smallRandomString,
|
||||
paste,
|
||||
getSettings,
|
||||
replaceAccounts,
|
||||
} from "@/utils/platform";
|
||||
import router from "@/router";
|
||||
import { useRoute } from "vue-router";
|
||||
|
@ -192,8 +200,48 @@ export default defineComponent({
|
|||
}
|
||||
});
|
||||
|
||||
const deleteAccount = async (address: string, accounts: Account[]) => {
|
||||
const findIndex = accounts.findIndex((a) => a.address === address);
|
||||
const pArr: Array<Promise<void>> = [];
|
||||
if (findIndex !== -1) {
|
||||
accounts.splice(findIndex, 1);
|
||||
pArr.push(replaceAccounts([...accounts]));
|
||||
}
|
||||
await Promise.all(pArr);
|
||||
};
|
||||
|
||||
const onEditAccount = async () => {
|
||||
if (name.value.length < 1) {
|
||||
alertMsg.value = "Name cannot be empty.";
|
||||
alertOpen.value = true;
|
||||
return;
|
||||
}
|
||||
const accounts = (await accountsProm) as Account[];
|
||||
const account = accounts.find((acc) => acc.address === paramAddress);
|
||||
if (!account) {
|
||||
alertMsg.value = "Account not found.";
|
||||
alertOpen.value = true;
|
||||
return;
|
||||
}
|
||||
const savedAcc = {
|
||||
address: account.address,
|
||||
name: name.value,
|
||||
pk: account.pk,
|
||||
encPk: account.encPk,
|
||||
};
|
||||
await deleteAccount(account.address, accounts);
|
||||
|
||||
await saveAccount(savedAcc);
|
||||
router.push("/tabs/accounts");
|
||||
};
|
||||
|
||||
const onAddAccount = async () => {
|
||||
let p1 = Promise.resolve();
|
||||
if (name.value.length < 1) {
|
||||
alertMsg.value = "Name cannot be empty.";
|
||||
alertOpen.value = true;
|
||||
return;
|
||||
}
|
||||
if (pk.value.length === 64) {
|
||||
pk.value = `0x${pk.value.trim()}`;
|
||||
}
|
||||
|
@ -320,6 +368,7 @@ export default defineComponent({
|
|||
mnemonic,
|
||||
mnemonicIndex,
|
||||
extractMnemonic,
|
||||
onEditAccount,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
<img
|
||||
:alt="token?.name"
|
||||
:src="token?.image"
|
||||
@error="token.image = getUrl('assets/randomGrad.svg')"
|
||||
@error="token.image = getUrl('assets/chain-icons/eth.webp')"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label
|
||||
|
|
|
@ -120,13 +120,12 @@
|
|||
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.
|
||||
Unlike most wallets, this wallet has no ads, no analytics, no trackers, no
|
||||
bloatware, no telemetry, no data collection, no sponsored content, no
|
||||
sponsored Dapps, no sponsored tokens, no sponsored NFTs, no sponsored
|
||||
anything. It is a clean wallet with no revenue model, made by a single
|
||||
developer, if you want to support this project financially you can donate at
|
||||
andrei0x309.eth.
|
||||
</p>
|
||||
<p>
|
||||
Github Repo:
|
||||
|
@ -134,6 +133,10 @@
|
|||
>LINK</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
Docs Website:
|
||||
<a href="#" @click="openTab('https://clear-wallet.flashsoft.eu')">LINK</a>
|
||||
</p>
|
||||
<br />
|
||||
<p style="margin-bottom: 0.2rem">Places you can check me out:</p>
|
||||
<p>
|
||||
|
@ -148,10 +151,6 @@
|
|||
Blog Flashsoft
|
||||
<a href="#" @click="openTab('https://blog.flashsoft.eu')">LINK</a>
|
||||
</p>
|
||||
<p>
|
||||
Crypto-Leftists Discord
|
||||
<a href="#" @click="openTab('https://discord.gg/gzA4bTCdhb')">LINK</a>
|
||||
</p>
|
||||
</div>
|
||||
</ion-accordion>
|
||||
<ion-accordion value="4">
|
||||
|
|
Loading…
Reference in New Issue