Compare commits

...

4 Commits

Author SHA1 Message Date
Andrei O c88cfc22b8
'clear-wallet@v1.3.3' 2024-02-11 23:08:24 +02:00
Andrei O 97d37021f0
chore: other changes added in changelog 2024-02-11 21:21:16 +02:00
Andrei O 7a187cd0cf
chore: cleanup 2024-02-08 21:59:03 +02:00
Andrei O 498629d073
chore: changes for a new release 2024-02-08 20:08:06 +02:00
10 changed files with 168 additions and 47 deletions

View File

@ -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

View File

@ -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": {

View 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

View File

@ -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);

View File

@ -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",

View File

@ -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
}

View File

@ -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) => {

View File

@ -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,
};
},
});

View File

@ -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

View File

@ -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">