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 # 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 ## Manifest Version 1.3.2
- added button to open non kyc exchange, no referral is used to maximize privacy - added button to open non kyc exchange, no referral is used to maximize privacy

View File

@ -1,6 +1,6 @@
{ {
"name": "clear-wallet", "name": "clear-wallet",
"version": "1.3.2", "version": "1.3.3",
"private": true, "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.", "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": { "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') { } else if (typeof arg1 === 'object') {
return sendMessage(arg1 as RequestArguments) return sendMessage(arg1 as RequestArguments)
} else { } else {
console.info('ERROR: Clear Wallet: faulty request') console.error('ERROR: Clear Wallet: faulty request')
} }
}else if( typeof arg1 === 'string' ) { }else if( typeof arg1 === 'string' ) {
return sendMessage({ return sendMessage({
@ -323,7 +323,7 @@ const listner = function(event: any) {
try { try {
if(event?.data?.data?.error){ if(event?.data?.data?.error){
promResolvers.get(event.data.resId)?.reject(event.data.data); promResolvers.get(event.data.resId)?.reject(event.data.data);
console.info('Error: ', event?.data?.data) // console.info('Error: ', event?.data?.data)
}else { }else {
promResolvers.get(event.data.resId)?.resolve(event.data.data); 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 // Intercept method calls and log them
if (typeof target[prop] === 'function') { if (typeof target[prop] === 'function') {
return function (...args: any[]) { return function (...args: any[]) {
console.log(`Calling ${prop} with arguments:`, args); // console.log(`Calling ${prop} with arguments:`, args);
// eslint-disable-next-line prefer-spread // eslint-disable-next-line prefer-spread
const result = target[prop].apply(target, args); const result = target[prop].apply(target, args);
console.log(`${prop} returned:`, result); // console.log(`${prop} returned:`, result);
return result; return result;
}; };
} else { } else {
console.log(`Reading ${prop}`); // console.log(`Reading ${prop}`);
return target[prop]; return target[prop];
} }
}, },
@ -397,7 +397,7 @@ Object.defineProperty(win, 'web3', {
sendMessage({ sendMessage({
method: 'wallet_ready' method: 'wallet_ready'
}, true) }, true)
console.log('Clear wallet injected', (window as any).ethereum, win) // console.log('Clear wallet injected', (window as any).ethereum, win)
} }
injectWallet(this); injectWallet(this);

View File

@ -3,8 +3,8 @@
"name": "__MSG_appName__", "name": "__MSG_appName__",
"description": "__MSG_appDesc__", "description": "__MSG_appDesc__",
"default_locale": "en", "default_locale": "en",
"version": "1.3.2", "version": "1.3.3",
"version_name": "1.3.2", "version_name": "1.3.3",
"icons": { "icons": {
"16": "assets/extension-icon/wallet_16.png", "16": "assets/extension-icon/wallet_16.png",
"32": "assets/extension-icon/wallet_32.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 // ETH API
switch (message.method) { switch (message.method) {
case 'eth_call': { 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 break
} }
case 'eth_getBlockByNumber': { case 'eth_getBlockByNumber': {
try { try {
const params = message?.params?.[0] as any const params = message?.params?.[0] as any
const block = await getBlockByNumber(params) as any const block = await getBlockByNumber(params) as any
block.gasLimit = block.gasLimit.toHexString() const newBlock = {...block}
block.gasUsed = block.gasUsed.toHexString() newBlock.gasLimit = numToHexStr(block.gasLimit)
block.baseFeePerGas = block.baseFeePerGas.toHexString() newBlock.gasUsed = numToHexStr(block.gasUsed)
block._difficulty = block._difficulty.toHexString() newBlock.baseFeePerGas = numToHexStr(block.baseFeePerGas)
sendResponse(block) newBlock._difficulty = numToHexStr(block.difficulty)
} catch { newBlock.difficulty = block._difficulty
sendResponse(newBlock)
} catch (e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.log('eth_getBlockByNumber', e)
} }
break; break;
} }
@ -195,49 +207,52 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
}else { }else {
sendResponse(numToHexStr(Number(await getTxCount(message?.params?.[0] as string)))) sendResponse(numToHexStr(Number(await getTxCount(message?.params?.[0] as string))))
} }
} catch { } catch (e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_getTransactionCount', e)
} }
break break
} }
case 'eth_getTransactionByHash': { case 'eth_getTransactionByHash': {
try { try {
sendResponse(await getTxByHash(message?.params?.[0] as string)) sendResponse(await getTxByHash(message?.params?.[0] as string))
} catch { } catch (e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_getTransactionByHash', e)
} }
break break
} }
case 'eth_getTransactionReceipt':{ case 'eth_getTransactionReceipt':{
try { try {
sendResponse(await getTxReceipt(message?.params?.[0] as string)) sendResponse(await getTxReceipt(message?.params?.[0] as string))
} catch { } catch (e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_getTransactionReceipt', e)
} }
break break
} }
case 'eth_gasPrice': { case 'eth_gasPrice': {
try { try {
sendResponse(numToHexStr(BigInt(Math.trunc(await getGasPrice() * 1e9)))) sendResponse(numToHexStr(BigInt(Math.trunc(await getGasPrice() * 1e9))))
} catch { } catch(e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_gasPrice', e)
} }
break; break;
} }
@ -246,36 +261,39 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
const balance = await getBalance() const balance = await getBalance()
const balanceHex = numToHexStr(balance ?? 0n) const balanceHex = numToHexStr(balance ?? 0n)
sendResponse(balanceHex) sendResponse(balanceHex)
} catch { } catch (e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_getBalance', e)
} }
break break
} }
case 'eth_getCode': { case 'eth_getCode': {
try { try {
sendResponse(await getCode(message?.params?.[0] as string)) sendResponse(await getCode(message?.params?.[0] as string))
} catch { } catch (e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_getCode', e)
} }
break break
} }
case 'eth_blockNumber': { case 'eth_blockNumber': {
try { try {
sendResponse(await getBlockNumber()) sendResponse(await getBlockNumber())
} catch { } catch (e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_blockNumber', e)
} }
break break
} }
@ -311,12 +329,14 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'Gas estimate failed' message: 'Gas estimate failed'
}) })
} } else {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_estimateGas', err)
}
} }
break break
} }
@ -324,12 +344,13 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
case 'eth_accounts': { case 'eth_accounts': {
try { try {
sendResponse(await getSelectedAddress()) sendResponse(await getSelectedAddress())
} catch { } catch (e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_accounts', e)
} }
break break
} }
@ -338,12 +359,13 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
const network = await getSelectedNetwork() const network = await getSelectedNetwork()
const chainId = network?.chainId ?? 0 const chainId = network?.chainId ?? 0
sendResponse(`0x${chainId.toString(16)}`) sendResponse(`0x${chainId.toString(16)}`)
} catch { } catch (e) {
sendResponse({ sendResponse({
error: true, error: true,
code: rpcError.USER_REJECTED, code: rpcError.USER_REJECTED,
message: 'No network or user selected' message: 'No network or user selected'
}) })
console.error('eth_chainId', e)
} }
break break
} }

View File

@ -1,5 +1,30 @@
import { getSelectedAccount, getSelectedNetwork } from '@/utils/platform'; import { getSelectedAccount, getSelectedNetwork, numToHexStr } from '@/utils/platform';
import { ethers} from "ethers" 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) => { export const signMsg = async (msg: string) => {
const account = await getSelectedAccount() 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}) 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 network = await getSelectedNetwork()
const provider = new ethers.JsonRpcProvider(network.rpc) 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) => { export const getTxByHash = async (hash: string) => {
@ -68,9 +98,16 @@ export const getTxByHash = async (hash: string) => {
} }
export const getTxReceipt = async (hash: string) => { export const getTxReceipt = async (hash: string) => {
try {
const network = await getSelectedNetwork() const network = await getSelectedNetwork()
const provider = new ethers.JsonRpcProvider(network.rpc) 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) => { export const getCode = async (addr: string) => {

View File

@ -42,9 +42,16 @@
</template> </template>
<ion-item> <ion-item>
<ion-button @click="onCancel">Cancel</ion-button> <ion-button @click="onCancel">Cancel</ion-button>
<ion-button @click="onAddAccount">{{ <ion-button
isEdit ? "Edit Account" : "Add Account" @click="
}}</ion-button> () => {
isEdit ? onEditAccount() : onAddAccount();
}
"
expand="full"
color="primary"
>{{ isEdit ? "Edit Account" : "Add Account" }}</ion-button
>
</ion-item> </ion-item>
<ion-alert <ion-alert
:is-open="alertOpen" :is-open="alertOpen"
@ -120,6 +127,7 @@ import {
smallRandomString, smallRandomString,
paste, paste,
getSettings, getSettings,
replaceAccounts,
} from "@/utils/platform"; } from "@/utils/platform";
import router from "@/router"; import router from "@/router";
import { useRoute } from "vue-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 () => { const onAddAccount = async () => {
let p1 = Promise.resolve(); let p1 = Promise.resolve();
if (name.value.length < 1) {
alertMsg.value = "Name cannot be empty.";
alertOpen.value = true;
return;
}
if (pk.value.length === 64) { if (pk.value.length === 64) {
pk.value = `0x${pk.value.trim()}`; pk.value = `0x${pk.value.trim()}`;
} }
@ -320,6 +368,7 @@ export default defineComponent({
mnemonic, mnemonic,
mnemonicIndex, mnemonicIndex,
extractMnemonic, extractMnemonic,
onEditAccount,
}; };
}, },
}); });

View File

@ -52,7 +52,7 @@
<img <img
:alt="token?.name" :alt="token?.name"
:src="token?.image" :src="token?.image"
@error="token.image = getUrl('assets/randomGrad.svg')" @error="token.image = getUrl('assets/chain-icons/eth.webp')"
/> />
</ion-avatar> </ion-avatar>
<ion-label <ion-label

View File

@ -120,13 +120,12 @@
and Ethers. and Ethers.
</p> </p>
<p> <p>
It emulates Metamask Wallet and can be used as a drop-in replacement, right Unlike most wallets, this wallet has no ads, no analytics, no trackers, no
now if you have both extensions, CLW will overwrite Metamask. bloatware, no telemetry, no data collection, no sponsored content, no
</p> sponsored Dapps, no sponsored tokens, no sponsored NFTs, no sponsored
<p> anything. It is a clean wallet with no revenue model, made by a single
Main philosophy of the wallet is: no trackers, full control, export/import developer, if you want to support this project financially you can donate at
JSONs with accounts, fast generate new accounts, and wipe everything with andrei0x309.eth.
one click.
</p> </p>
<p> <p>
Github Repo: Github Repo:
@ -134,6 +133,10 @@
>LINK</a >LINK</a
> >
</p> </p>
<p>
Docs Website:
<a href="#" @click="openTab('https://clear-wallet.flashsoft.eu')">LINK</a>
</p>
<br /> <br />
<p style="margin-bottom: 0.2rem">Places you can check me out:</p> <p style="margin-bottom: 0.2rem">Places you can check me out:</p>
<p> <p>
@ -148,10 +151,6 @@
Blog Flashsoft Blog Flashsoft
<a href="#" @click="openTab('https://blog.flashsoft.eu')">LINK</a> <a href="#" @click="openTab('https://blog.flashsoft.eu')">LINK</a>
</p> </p>
<p>
Crypto-Leftists Discord
<a href="#" @click="openTab('https://discord.gg/gzA4bTCdhb')">LINK</a>
</p>
</div> </div>
</ion-accordion> </ion-accordion>
<ion-accordion value="4"> <ion-accordion value="4">