chore: changes for 1.4.0

This commit is contained in:
Andrei O 2024-07-26 00:01:24 +03:00
parent 7eab21188b
commit a4ad84e058
No known key found for this signature in database
GPG Key ID: B961E5B68389457E
21 changed files with 944 additions and 925 deletions

View File

@ -1,4 +1,3 @@
# Not supported due to compatibility issues with Bun Http2 stdlib
name: Bun Main Workflow
on:

View File

@ -1,4 +1,3 @@
# Not supported due to compatibility issues with Bun Http2 stdlib
name: Bun Main Workflow
on:

View File

@ -1,83 +1,95 @@
# Changelog
## Manifest Version 1.4.0
- added bun workflow to announce changes & new versions
- nicer display of type sign messages
- added reinjecting extension in case of context invalidation
- added button to community ERC20 Bridge
- changed the assets page to use another provider for fetching assets
- changed the display of assets to be more focused on tokens
- added Github link icon to the header of the first wallet page
- improved compatibility with non-EIP1159 networks
- minimal changes to switch network displays
## Manifest Version 1.3.9
- add an additional throttle on 'eth_chainId' to prevent websites from spamming the wallet with requests
- change inject throttle to only affect UI requests
- updated some core dependencies
- optimized performance for json rpc calls
- disabled assets fetch until new provider is found before yup.io was used
- optimized performance for JSON RPC calls
- disabled assets fetch until a new provider is found before yup.io was used
- simplified wallet switching
- added sonarCloud badge to README.md
## Manifest Version 1.3.8
- improved sign message display to better accomodate SIWE & other messages
- improved sign message display to better accommodate SIWE & other messages
## Manifest Version 1.3.7
- improved add Network pages
- upgraded and optimized some dependencies including vite
- optimized vite config
- upgraded and optimized some dependencies including Vite
- optimized site config
- added condition to not reinject wallet if already injected for websites that reload injected scripts
- optimized throttle fuffilment of requests in case of too many requests
- removed uneeded mobile native code
- optimized throttle fulfillment of requests in case of too many requests
- removed unneeded mobile native code
## Manifest Version 1.3.6
- better display of blockchain explorer button
- updated ethers dependency to latest 6.11.1
- better handling of type sigining
- better display of the blockchain explorer button
- updated ethers dependency to the latest 6.11.1
- better handling of type signing
- changed the password input for unlock to not lose focus
- activated focus on password input for unlock on view enter
- disabled integration of fire wallet(in cause user has it installed) with type signing due to incompatibility
- disabled integration of fire wallet(in case user has it installed) with type signing due to incompatibility
- other misc improvements
- added a check when sending native token to check if internet / RPC or Blockchain and show a message to the user
- customize testNets icons to show a small dev icon on the top right corner
- updated testNets templates to include newer networks
- show icons for testNets too in most places
- added a check when sending native tokens to check if internet / RPC or Blockchain and show a message to the user
- customize test-Nets icons to show a small dev icon on the top right corner
- updated test-Nets templates to include newer networks
- show icons for test-Nets too in most places
## Manifest Version 1.3.5
- added copy button to chainId for easier development
- added settings to be able to transfrom address to lower case when copying
- added a check in get recepit to return null if hash is missing
- added version display to wallet first page
- added copy button to ChainId for easier development
- added settings to be able to transform address to lowercase when copying
- added a check in get receipt to return null if the hash is missing
- added version display to the wallet on the first page
## Manifest Version 1.3.4
- bump fake Metamask version signature to 11.0.0
- improved compatibility with older deprecated websites
- improved mimicking of Metamask API
- made the wallet compatible with fire extension on sending transaction( by mimicking new Metamask API)
- made the wallet compatible with fire extension on sending transactions ( by mimicking the new Metamask API)
## 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
- modify the receipt returned to resemble the one from Metamask
- change some notes in about
- refactored account name edit to be more user friendly
- 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
- added button to navigate to non-KYC exchange, no referral is used to maximize privacy
## Manifest Version 1.3.1
- refactored the wallet to use etheres V6
- refactored the wallet to use ethers V6
- implemented EIP6963Provider
- updated all dependencies
- added ability to send native tokens
- added ability to manage ABIs
- added ability to perfrom arbitrary read calls to contracts
- added ability to perfrom arbitrary write calls to contracts
- added ability to perform arbitrary read calls to contracts
- added ability to perform arbitrary write calls to contracts
- added ability to save read or write calls for later use
- added sandbox to be able to evaluate JS code in order to pass complex parameters to read or write calls
- added base Network to templates class
- added Icon for base network
- added icon for base network
- added ability to add contacts and load them in Read contract and Write and Send token pages
- added ability to paste current selected address to both webpages and insde wallet itself
- added the ability to paste the current selected address to both web pages and inside the wallet itself
## Manifest Version 1.2.8
@ -91,15 +103,15 @@
## Manifest Version 1.2.6
- upgrade ionic to v7 and update dependencies
## Manifest Version 1.2.5
- improve post build script
- improve post-build script
## Manifest Version 1.2.4
- updated showing assets page to use new api
- removed yup score from assets page
- updated showing assets page to use the new API
- removed YUP score from the assets page
- change the info modal in settings
## Manifest Version 1.2.3
@ -114,15 +126,15 @@
## Manifest Version 1.2.1
- added support fro eth_getTransactionCount method
- added support from eth_getTransactionCount method
## Manifest Version 1.1.9
- added proxy in intial stub for send, request, sendAsync for better compatibility
- added proxy in initial stub for send, request, sendAsync for better compatibility
## Manifest Version 1.1.8
- added support to extract private key from seed when adding account
- added support to extract the private key from the seed when adding an account
## Manifest Version 1.1.7
@ -137,18 +149,18 @@
## Manifest Version 1.1.5
- Added multiple new multiple implementations of MetamaskAPI including request to add a network by a website
- Injecting in sync mode stub wallet to increese compatibility with websites that expect a walled defined at the lowest point of page load
- Modifing CSP requests to allow sync injecting of stub
- Added multiple new implementations of MetamaskAPI including a request to add a network by a website
- Injecting in sync mode stub wallet to increase compatibility with websites that expect a walled defined at the lowest point of page load
- Modifying CSP requests to allow sync injecting of stub
- Added Web3 Shim for compatibility with older websites
- Tested new websites and TX's
- Refactoring the 10 maximum conqurent messages limit
- Added support for most of listners and improve emiting them
- Added a post buil script
- Switch the content script to load initialy without a wrapper module
- Tested new websites and TXs
- Refactoring the 10 maximum concurrent messages limit
- Added support for most of the listeners and improved emitting them
- Added a post-build script
- Switch the content script to load initially without a wrapper module
## Manifest Version: 1.1.4
## Manifest Version 1.1.4
- Added max 10 allowed concurrent messages to the wallet to prevent abusive websites from sending too many messages.
- Added explorer-button to main wallet page for easier viewing of the selected address on the blockchain explorer.
- Show the price converted in dollars also besides the native token price on transaction view for networks: 1(Ethereum), 137(Polygon), 100(Gnosis), 10(Optimism), 56(BSC), 42161(Arbitrum One)
- Show the price converted in dollars besides the native token price on transaction view for networks: 1(Ethereum), 137(Polygon), 100(Gnosis), 10(Optimism), 56(BSC), 42161(Arbitrum One)

View File

@ -59,19 +59,30 @@ const main = async () => {
if(action === 'update') {
const VERSION = GithubEvent.inputs.version;
const message = `Github ClearWallet new version ${VERSION} has been released!\n
ChromeStore: https://bit.ly/clw-evm \n
Github: https://github.com/andrei0x309/clear-wallet
`;
const message = `Clear Wallet - New version ${VERSION} released! \n
ChangeLog: https://bit.ly/clw-cl \n
ChromeStore: https://bit.ly/clw-evm \n
`
if(ENABLED) {
await yupAPI.sendPost({
content: message,
platforms: ['twitter', 'threads', 'bsky', 'lens']
})
await fchubUtils.createFarcasterPost({
const fcPost = await fchubUtils.createFarcasterPost({
content: message,
})
const fcPostHash = Buffer.from(fcPost).toString('hex');
if(fcPostHash) {
await new Promise((resolve) => setTimeout(resolve, 3000));
const launchCasterMessage = `@launch`
await fchubUtils.createFarcasterPost({ content: launchCasterMessage, replyTo: {
hash: fcPostHash,
fid: String(USER_FID)
} })
}
} else {
console.log('No action required')
}

View File

@ -0,0 +1,22 @@
<template>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-label="down"
class="Delta__StyledDownArrow-sc-bcba1827-1 fstbcV"
>
<path
d="M10.6979 16.2453L6.31787 9.75247C5.58184 8.66118 6.2058 7 7.35185 7L16.6482 7C17.7942 7 18.4182 8.66243 17.6821 9.75247L13.3021 16.2453C12.623 17.2516 11.377 17.2516 10.6979 16.2453Z"
fill="currentColor"
></path>
</svg>
</template>
<style scoped>
.fstbcV {
color: rgb(255, 95, 82);
}
</style>

View File

@ -0,0 +1,22 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
aria-label="up"
class="Delta__StyledUpArrow-sc-bcba1827-0 hrYaKr"
>
<path
d="M13.3021 7.7547L17.6821 14.2475C18.4182 15.3388 17.7942 17 16.6482 17L7.3518 17C6.2058 17 5.5818 15.3376 6.3179 14.2475L10.6979 7.7547C11.377 6.7484 12.623 6.7484 13.3021 7.7547Z"
fill="currentColor"
></path>
</svg>
</template>
<style scoped>
.hrYaKr {
color: rgb(64, 182, 107);
}
</style>

View File

@ -0,0 +1,22 @@
<template>
<svg enable-background="new 0 0 32 32" viewBox="0 0 32 32">
<g>
<polygon
fill="none"
points="12,3 12,8 31,8 31,14 1,14 "
stroke="currentColor"
stroke-linejoin="round"
stroke-miterlimit="10"
stroke-width="2"
></polygon>
<polygon
fill="none"
points="20,29 20,24 1,24 1,18 31,18 "
stroke="currentColor"
stroke-linejoin="round"
stroke-miterlimit="10"
stroke-width="2"
></polygon>
</g>
</svg>
</template>

View File

@ -0,0 +1,16 @@
<template>
<svg
fill="none"
height="25"
viewBox="0 0 25 25"
width="25"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M15.7481 24.9471C24.0901 24.7061 24.9111 22.9501 24.9111 12.9811C24.9111 1.98108 23.9111 0.981079 12.9111 0.981079C1.91113 0.981079 0.911133 1.98108 0.911133 12.9811C0.911133 22.9761 1.73713 24.7151 10.1391 24.9491C10.2121 24.8581 10.2391 24.7441 10.2391 24.6281C10.2391 24.3781 10.2291 21.8121 10.2241 20.9291C7.18713 21.5681 6.54613 19.5101 6.54613 19.5101C6.04913 18.2881 5.33313 17.9621 5.33313 17.9621C4.34213 17.3061 5.40813 17.3191 5.40813 17.3191C6.50413 17.3941 7.08113 18.4101 7.08113 18.4101C8.05513 20.0271 9.63713 19.5601 10.2591 19.2891C10.3581 18.6061 10.6401 18.1391 10.9521 17.8751C8.52713 17.6081 5.97813 16.7001 5.97813 12.6451C5.97813 11.4901 6.40413 10.5461 7.10213 9.80608C6.98913 9.53808 6.61513 8.46208 7.20913 7.00608C7.20913 7.00608 8.12613 6.72108 10.2121 8.09008C11.0831 7.85508 12.0171 7.73808 12.9461 7.73408C13.8731 7.73808 14.8071 7.85508 15.6801 8.09008C17.7651 6.72108 18.6801 7.00608 18.6801 7.00608C19.2761 8.46208 18.9011 9.53808 18.7881 9.80608C19.4881 10.5461 19.9111 11.4901 19.9111 12.6451C19.9111 16.7101 17.3581 17.6051 14.9251 17.8661C15.3171 18.1931 15.6661 18.8391 15.6661 19.8261C15.6661 20.7721 15.6601 22.4451 15.6561 23.5541C15.6541 24.1031 15.6531 24.5131 15.6531 24.6281C15.6531 24.7371 15.6821 24.8521 15.7481 24.9471V24.9471Z"
fill="currentColor"
fill-rule="evenodd"
/>
</svg>
</template>

View File

@ -46,40 +46,49 @@ window.addEventListener("message", (event) => {
event.data.data.data.website = document?.location?.href ?? ''
if ((event?.data?.data?.method ?? 'x') in allowedMethods) {
event.data.data.data.method = event?.data?.data?.method ?? ''
chrome?.runtime?.sendMessage(event.data.data.data, (res) => {
if (chrome.runtime.lastError) {
console.warn("LOC1: Error sending message:", chrome.runtime.lastError);
}
const id = Number(event.data.resId.replace(/[A-Za-z]/g, '').slice(0, 10))
const data = {
target: 'metamask-inpage',
type: "CLWALLET_PAGE",
resId: event.data.resId,
data: { name: 'metamask-provider', data: {
jsonrpc: '2.0',
try {
chrome?.runtime?.sendMessage(event.data.data.data, (res) => {
if (chrome.runtime.lastError) {
console.warn("LOC1: Error sending message:", chrome.runtime.lastError);
}
const id = Number(event.data.resId.replace(/[A-Za-z]/g, '').slice(0, 10))
const data = {
target: 'metamask-inpage',
type: "CLWALLET_PAGE",
resId: event.data.resId,
data: {
name: 'metamask-provider', data: {
jsonrpc: '2.0',
id,
result: res,
},
id,
result: res,
},
id,
method: event?.data?.data?.data?.method ?? '',
params: event?.data?.data?.data?.params ?? [],
},
}
if(event?.data?.data?.data?.method !== 'eth_chainId') {
console.info('data out', data)
}
method: event?.data?.data?.data?.method ?? '',
params: event?.data?.data?.data?.params ?? [],
},
}
if (event?.data?.data?.data?.method !== 'eth_chainId') {
// console.info('data out', data)
}
window.postMessage(data, "*");
})
window.postMessage(data, "*");
})
} catch (e) {
if ((e as Error)?.message === 'Extension context invalidated') {
console.info('Error: Extension context invalidated. Ignoring.');
}
}
}
else {
const data = {
type: "CLWALLET_PAGE",
const data = {
type: "CLWALLET_PAGE",
data: {
data: {
result: { error: true, message: 'ClearWallet: Unknown method requested ' + (event?.data?.data?.data?.method ?? '') }
} }
, resId: event.data.resId };
data: {
result: { error: true, message: 'ClearWallet: Unknown method requested ' + (event?.data?.data?.data?.method ?? '') }
}
}
, resId: event.data.resId
};
window.postMessage(data, "*");
}
} else if (event?.data?.type === "CLWALLET_PING") {
@ -87,12 +96,18 @@ window.addEventListener("message", (event) => {
event.data.data.data.type = "CLWALLET_CONTENT_MSG"
event.data.data.data.method = "wallet_connect"
event.data.data.data.params = Array(0)
chrome.runtime.sendMessage(event.data.data.data, async (res) => {
if (chrome.runtime.lastError) {
console.warn("LOC2: Error sending message:", chrome.runtime.lastError);
try {
chrome.runtime.sendMessage(event.data.data.data, async (res) => {
if (chrome.runtime.lastError) {
console.warn("LOC2: Error sending message:", chrome.runtime.lastError);
}
window.postMessage(res, "*");
})
} catch (e) {
if ((e as Error)?.message === 'Extension context invalidated') {
console.info('Error: Extension context invalidated. Ignoring.');
}
window.postMessage(res, "*");
})
}
}
});

View File

@ -56,6 +56,7 @@ const listners = {
}
const promResolvers = new Map()
const UIpromResolvers = new Map()
const getListnersCount = (): number => {
let count = 0
@ -75,14 +76,20 @@ const sendMessage = (args: RequestArguments, ping = false, from = 'request'): Pr
return new Promise((resolve, reject) => {
const p = [ "eth_signTypedData", "eth_signTypedData_v3", "eth_signTypedData_v4"]
const throttledMethods = [...p, 'eth_sign', 'personal_sign', 'eth_sendTransaction']
if(promResolvers.size > MAX_PROMISES && throttledMethods.includes(args.method)) {
const isThrottled = throttledMethods.includes(args.method)
if(UIpromResolvers.size > MAX_PROMISES && isThrottled) {
reject({code: -32000, message: 'ClearWallet: Too many requests', error: true })
return
}
const resId = [...`${Math.random().toString(16) + Date.now().toString(16)}`].slice(2).join('')
promResolvers.set(resId, { resolve, reject })
if(isThrottled) {
UIpromResolvers.set(resId, { resolve, reject })
}
promResolvers.set(resId, { resolve, reject })
const method = args.method
if (p.includes(args.method)) {
@ -101,7 +108,7 @@ const sendMessage = (args: RequestArguments, ping = false, from = 'request'): Pr
data.type = 'CLWALLET_PING'
}
if(method!== 'eth_chainId') {
console.info('data in', data)
// console.info('data in', data)
}
window.postMessage(data, "*");
@ -389,6 +396,10 @@ const listner = function(event: any) {
if(promResolvers.has(resId)) {
promResolvers.delete(resId)
}
if(UIpromResolvers.has(resId)) {
UIpromResolvers.get(resId).resolve(result)
UIpromResolvers.delete(resId)
}
}
window.addEventListener("message",listner)

View File

@ -3,8 +3,8 @@
"name": "__MSG_appName__",
"description": "__MSG_appDesc__",
"default_locale": "en",
"version": "1.3.9",
"version_name": "1.3.9",
"version": "1.4.0",
"version_name": "1.4.0",
"icons": {
"16": "assets/extension-icon/wallet_16.png",
"32": "assets/extension-icon/wallet_32.png",

View File

@ -1,37 +1,37 @@
import {
import {
CLW_CONTEXT_MENU_ID,
getSelectedAccount,
getSelectedNetwork,
smallRandomString,
getSettings,
clearPk,
openTab,
getUrl,
addToHistory,
getNetworks,
strToHex,
getSelectedAccount,
getSelectedNetwork,
smallRandomString,
getSettings,
clearPk,
openTab,
getUrl,
addToHistory,
getNetworks,
strToHex,
numToHexStr,
enableRightClickVote,
} from '@/utils/platform';
import {
userApprove,
userReject,
rIdWin,
import {
userApprove,
userReject,
rIdWin,
rIdData,
} from '@/extension/userRequest'
import {
signMsg,
getBalance,
getBlockNumber,
estimateGas,
sendTransaction,
getGasPrice,
getBlockByNumber,
evmCall,
getTxByHash,
getTxReceipt,
signTypedData,
getCode,
import {
signMsg,
getBalance,
getBlockNumber,
estimateGas,
sendTransaction,
getGasPrice,
getBlockByNumber,
evmCall,
getTxByHash,
getTxReceipt,
signTypedData,
getCode,
getTxCount,
getSelectedAddress
} from '@/utils/wallet'
@ -44,35 +44,65 @@ import { allTemplateNets } from '@/utils/networks'
let notificationUrl: string
const chainIdThrottle: {[key: string]: number} = {}
const chainIdThrottle: { [key: string]: number } = {}
const reInjectContentScripts = async () => {
const cts = chrome.runtime.getManifest().content_scripts ?? []
for (const cs of cts) {
const tabs = await chrome.tabs.query({ url: cs.matches })
for (const tab of tabs) {
if (!tab?.id || !cs.js || !tab.url) {
continue;
}
if (tab.url.match(/(chrome|chrome-extension):\/\//gi)) {
continue;
}
const isWorldMain = (cs as any)?.world === 'MAIN'
chrome.scripting.executeScript({
files: cs.js,
target: { tabId: tab.id, allFrames: cs.all_frames },
injectImmediately: cs.run_at === 'document_start',
world: isWorldMain ? 'MAIN' : 'ISOLATED'
}).catch((err) => {
console.warn('Error injecting content script', err)
})
}
}
}
chrome.runtime.onInstalled.addListener(() => {
enableRightClickVote()
reInjectContentScripts();
console.info('Service worker installed');
if (chrome.runtime.lastError) {
console.warn("Whoops.. " + chrome.runtime.lastError.message);
}
})
chrome.runtime.onStartup.addListener(() => {
console.info('Service worker startup');
enableRightClickVote();
if(chrome.runtime.lastError) {
if (chrome.runtime.lastError) {
console.warn("Whoops.. " + chrome.runtime.lastError.message);
}
})
chrome.runtime.onSuspend.addListener(() => {
console.info('Service worker suspend');
if(chrome.runtime.lastError) {
if (chrome.runtime.lastError) {
console.warn("Whoops.. " + chrome.runtime.lastError.message);
}
})
async function pasteAddress() {
const currentAddress = (await (window as any).ethereum?.request({
method: 'eth_accounts',
params: []
}))
if(currentAddress.length > 0) {
document.execCommand("insertText", false, currentAddress[0]);
async function pasteAddress () {
const currentAddress = (await (window as any).ethereum?.request({
method: 'eth_accounts',
params: []
}))
if (currentAddress.length > 0) {
document.execCommand("insertText", false, currentAddress[0]);
}
}
@ -88,9 +118,9 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => {
func: pasteAddress
});
} catch {
// igonre
// igonre
}
} else if(isOwnExtension) {
} else if (isOwnExtension) {
chrome.runtime.sendMessage({ method: 'paste', type: 'CLWALLET_PAGE_MSG' }, (r) => {
if (chrome.runtime.lastError) {
console.warn("LOC3: Error sending message:", chrome.runtime.lastError);
@ -105,23 +135,23 @@ chrome.alarms.create('updatePrices', {
})
chrome.alarms.onAlarm.addListener((alarm) => {
if(alarm.name === 'updatePrices') {
updatePrices().then(() => {
console.info('Prices updated')
}).catch((err) => {
console.warn('Prices update failed', err)
})
}
getSettings().then((settings) => {
if( ((settings.lastLock + settings.lockOutPeriod * 6e4) < Date.now()) && settings.lockOutEnabled && !settings.lockOutBlocked ) {
settings.lastLock = Date.now()
clearPk()
if (alarm.name === 'updatePrices') {
updatePrices().then(() => {
console.info('Prices updated')
}).catch((err) => {
console.warn('Prices update failed', err)
})
}
})
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) => {
if (winId in (userReject ?? {})){
if (winId in (userReject ?? {})) {
userReject[winId]?.()
}
userReject[winId] = undefined
@ -129,9 +159,9 @@ chrome.windows.onRemoved.addListener(async (winId) => {
rIdWin[winId] = undefined
rIdData[winId] = undefined
const wins = await chrome.windows.getAll()
if(wins.length === 0) {
if (wins.length === 0) {
const s = await getSettings()
if(s.enableStorageEnctyption) {
if (s.enableStorageEnctyption) {
await clearPk()
}
}
@ -147,7 +177,7 @@ const viewTxListner = async (id: string) => {
}
}
if (!chrome.notifications.onButtonClicked.hasListener(viewTxListner)){
if (!chrome.notifications.onButtonClicked.hasListener(viewTxListner)) {
chrome.notifications.onButtonClicked.addListener(viewTxListner)
}
@ -159,33 +189,30 @@ const chainIdThrottleFn = async (website: string) => {
} catch {
urlKey = 'invalid'
}
if(chainIdThrottle[urlKey] === undefined) {
if (chainIdThrottle[urlKey] === undefined) {
chainIdThrottle[urlKey] = 0
}
chainIdThrottle[urlKey] += 1
if( chainIdThrottle[urlKey] > 3) {
if (chainIdThrottle[urlKey] > 6) {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(null)
}, 450)
}, 250)
})
// console.log('throttling', chainIdThrottle)
}
return urlKey
}
const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: any) => any) => {
const mainListner = (message: RequestArguments, sender: any, sendResponse: (a: any) => any) => {
if (chrome.runtime.lastError) {
console.info("Error receiving message:", chrome.runtime.lastError);
}
if(message?.type !== "CLWALLET_CONTENT_MSG") {
if (message?.type !== "CLWALLET_CONTENT_MSG") {
return true
}
(async () => {
// console.info('Message:', message)
(async () => {
if (!(message?.method)) {
sendResponse({
code: 500,
@ -196,7 +223,7 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
switch (message.method) {
case 'eth_call': {
try {
sendResponse(await evmCall(message?.params ?? []))
sendResponse(await evmCall(message?.params ?? []))
} catch (e) {
sendResponse({
error: true,
@ -209,15 +236,15 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
}
case 'eth_getBlockByNumber': {
try {
const params = message?.params?.[0] as any
const block = await getBlockByNumber(params) as any
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)
const params = message?.params?.[0] as any
const block = await getBlockByNumber(params) as any
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,
@ -230,11 +257,11 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
}
case 'eth_getTransactionCount': {
try {
if(message?.params?.[1]) {
sendResponse(numToHexStr(Number(await getTxCount(message?.params?.[0] as string, message?.params?.[1] as string))))
}else {
sendResponse(numToHexStr(Number(await getTxCount(message?.params?.[0] as string))))
}
if (message?.params?.[1]) {
sendResponse(numToHexStr(Number(await getTxCount(message?.params?.[0] as string, message?.params?.[1] as string))))
} else {
sendResponse(numToHexStr(Number(await getTxCount(message?.params?.[0] as string))))
}
} catch (e) {
sendResponse({
error: true,
@ -247,7 +274,7 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
}
case 'eth_getTransactionByHash': {
try {
sendResponse(await getTxByHash(message?.params?.[0] as string))
sendResponse(await getTxByHash(message?.params?.[0] as string))
} catch (e) {
sendResponse({
error: true,
@ -258,9 +285,9 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
}
break
}
case 'eth_getTransactionReceipt':{
case 'eth_getTransactionReceipt': {
try {
sendResponse(await getTxReceipt(message?.params?.[0] as string))
sendResponse(await getTxReceipt(message?.params?.[0] as string))
} catch (e) {
sendResponse({
error: true,
@ -273,8 +300,8 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
}
case 'eth_gasPrice': {
try {
sendResponse(numToHexStr(BigInt(Math.trunc(await getGasPrice() * 1e9))))
} catch(e) {
sendResponse(numToHexStr(BigInt(Math.trunc((await getGasPrice()).price * 1e9))))
} catch (e) {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
@ -286,9 +313,9 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
}
case 'eth_getBalance': {
try {
const balance = await getBalance()
const balanceHex = numToHexStr(balance ?? 0n)
sendResponse(balanceHex)
const balance = await getBalance()
const balanceHex = numToHexStr(balance ?? 0n)
sendResponse(balanceHex)
} catch (e) {
sendResponse({
error: true,
@ -301,7 +328,7 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
}
case 'eth_getCode': {
try {
sendResponse(await getCode(message?.params?.[0] as string))
sendResponse(await getCode(message?.params?.[0] as string))
} catch (e) {
sendResponse({
error: true,
@ -314,7 +341,7 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
}
case 'eth_blockNumber': {
try {
sendResponse(numToHexStr(await getBlockNumber()))
sendResponse(numToHexStr(await getBlockNumber()))
} catch (e) {
sendResponse({
error: true,
@ -323,89 +350,89 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
})
console.warn('Error: eth_blockNumber', e)
}
break
break
}
case 'eth_estimateGas': {
try {
const params = message?.params?.[0] as any
if(!params) {
sendResponse({
error: true,
code: rpcError.INVALID_PARAM,
message: 'Invalid param for gas estimate'
try {
const params = message?.params?.[0] as any
if (!params) {
sendResponse({
error: true,
code: rpcError.INVALID_PARAM,
message: 'Invalid param for gas estimate'
})
break
}
const gas = await estimateGas({
to: params?.to ?? '',
from: params?.from ?? '',
data: params?.data ?? '',
value: params?.value ?? '0x0'
})
break
const gasHex = numToHexStr(gas ?? 0n)
sendResponse(gasHex)
} catch (err) {
if (String(err).includes('UNPREDICTABLE_GAS_LIMIT')) {
chrome.notifications.create({
message: 'Gas estimate failed likely due to to many decimals substract 0.00001 form the value you have inpputed and try again.',
title: 'Error',
iconUrl: getUrl('assets/extension-icon/wallet_128.png'),
type: 'basic'
} as any)
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'Gas estimate failed'
})
} else {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'No network or user selected'
})
console.warn('Error: eth_estimateGas', err)
}
}
const gas = await estimateGas({
to: params?.to ?? '',
from: params?.from ?? '',
data: params?.data ?? '',
value: params?.value ?? '0x0'
})
const gasHex = numToHexStr(gas ?? 0n)
sendResponse(gasHex)
} catch(err) {
if(String(err).includes('UNPREDICTABLE_GAS_LIMIT')) {
chrome.notifications.create({
message: 'Gas estimate failed likely due to to many decimals substract 0.00001 form the value you have inpputed and try again.',
title: 'Error',
iconUrl: getUrl('assets/extension-icon/wallet_128.png'),
type: 'basic'
} as any)
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'Gas estimate failed'
})
} else {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'No network or user selected'
})
console.warn('Error: eth_estimateGas', err)
}
}
break
break
}
case 'eth_requestAccounts':
case 'eth_accounts': {
try {
sendResponse(await getSelectedAddress())
} catch (e) {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'No network or user selected'
})
console.warn('Error: eth_accounts', e)
}
break
try {
sendResponse(await getSelectedAddress())
} catch (e) {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'No network or user selected'
})
console.warn('Error: eth_accounts', e)
}
break
}
case 'eth_chainId':
case 'net_version':
{
try {
const isNetVersion = message.method === 'net_version'
const urlKey = await chainIdThrottleFn(message?.website ?? '')
const network = await getSelectedNetwork()
const chainId = network?.chainId ?? 1
sendResponse(isNetVersion ? chainId.toString() : `0x${chainId.toString(16)}`)
chainIdThrottle[urlKey] -= 1
} catch (e) {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'No network or user selected'
})
console.warn('Error: eth_chainId', e)
}
break
}
case 'net_version':
{
try {
const isNetVersion = message.method === 'net_version'
const urlKey = await chainIdThrottleFn(message?.website ?? '')
const network = await getSelectedNetwork()
const chainId = network?.chainId ?? 1
sendResponse(isNetVersion ? chainId.toString() : `0x${chainId.toString(16)}`)
chainIdThrottle[urlKey] -= 1
} catch (e) {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'No network or user selected'
})
console.warn('Error: eth_chainId', e)
}
break
}
case 'eth_sendTransaction': {
try {
const params = message?.params?.[0] as any
if(!params) {
if (!params) {
sendResponse({
error: true,
code: rpcError.INVALID_PARAM,
@ -414,7 +441,7 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
break
}
const [account, network] = await Promise.all([getSelectedAccount(), getSelectedNetwork()])
if(!account || !('address' in account)) {
if (!account || !('address' in account)) {
await chrome.windows.create({
height: 450,
width: 400,
@ -423,7 +450,7 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
})
return
}
if(!network || !('chainId' in network)) {
if (!network || !('chainId' in network)) {
await chrome.windows.create({
height: 450,
width: 400,
@ -436,62 +463,63 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
const serializeParams = strToHex(JSON.stringify(params)) ?? ''
let gWin: any
await new Promise((resolve, reject) => {
chrome.windows.create({
height: 450,
width: 400,
url: chrome.runtime.getURL(`index.html?route=sign-tx&param=${serializeParams}&rid=${String(message?.resId ?? '')}`),
type: 'popup'
}).then((win) => {
gWin = win
userReject[String(win.id)] = reject
userApprove[String(win.id)] = resolve
rIdWin[String(win.id)] = String(message.resId)
rIdData[String(win.id)] = {}
})
chrome.windows.create({
height: 450,
width: 400,
url: chrome.runtime.getURL(`index.html?route=sign-tx&param=${serializeParams}&rid=${String(message?.resId ?? '')}`),
type: 'popup'
}).then((win) => {
gWin = win
userReject[String(win.id)] = reject
userApprove[String(win.id)] = resolve
rIdWin[String(win.id)] = String(message.resId)
rIdData[String(win.id)] = {}
})
})
try {
// console.log('waiting for user to approve or reject')
// console.log(rIdData?.[String(gWin?.id ?? 0)])
const tx = await sendTransaction({...params, ...(rIdData?.[String(gWin?.id ?? 0)] ?? {}) } )
sendResponse(tx.hash)
const buttons = {} as any
const network = await getSelectedNetwork()
addToHistory({
date: Date.now(),
txHash: tx.hash,
chainId: network.chainId,
...(network.explorer ? {txUrl: `${network.explorer}/tx/${tx.hash}`.replace('//', '/') } : {}),
webiste: (message?.website)
})
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',
iconUrl: getUrl('assets/extension-icon/wallet_128.png'),
type: 'basic',
...(buttons)
} as any)
console.log('waiting for user to approve or reject')
console.log(rIdData?.[String(gWin?.id ?? 0)])
const tx = await sendTransaction({ ...params, ...(rIdData?.[String(gWin?.id ?? 0)] ?? {}) })
sendResponse(tx.hash)
const buttons = {} as any
const network = await getSelectedNetwork()
addToHistory({
date: Date.now(),
txHash: tx.hash,
chainId: network.chainId,
...(network.explorer ? { txUrl: `${network.explorer}/tx/${tx.hash}`.replace('//', '/') } : {}),
webiste: (message?.website)
})
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',
iconUrl: getUrl('assets/extension-icon/wallet_128.png'),
type: 'basic',
...(buttons)
} as any)
const settings = await getSettings()
if(settings.encryptAfterEveryTx) {
clearPk()
}
const settings = await getSettings()
if (settings.encryptAfterEveryTx) {
clearPk()
}
} catch (err) {
console.info('Error: eth_sendTransaction', err)
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
@ -510,73 +538,73 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
type: 'basic'
} as any)
}
} catch(err) {
console.warn('Error: eth_sendTransaction', err)
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'User Rejected Signature'
})
}
} catch (err) {
console.warn('Error: eth_sendTransaction', err)
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'User Rejected Signature'
})
}
break
}
case 'signTypedData':
case 'eth_signTypedData':
case 'signTypedData_v1':
case 'eth_signTypedData_v1':
case 'signTypedData_v3':
case 'signTypedData_v3':
case 'eth_signTypedData_v3':
case 'signTypedData_v4':
case 'eth_signTypedData_v4':
case 'personal_sign':
case 'eth_sign': {
try {
const account = await getSelectedAccount()
if(!account || !('address' in account)) {
await chrome.windows.create({
height: 450,
width: 400,
url: chrome.runtime.getURL(`index.html?route=wallet-error&param=${strToHex('No account is selected you need to have an account selected before trying sign a message')}&rid=${String(message?.resId ?? '')}`),
type: 'popup'
const account = await getSelectedAccount()
if (!account || !('address' in account)) {
await chrome.windows.create({
height: 450,
width: 400,
url: chrome.runtime.getURL(`index.html?route=wallet-error&param=${strToHex('No account is selected you need to have an account selected before trying sign a message')}&rid=${String(message?.resId ?? '')}`),
type: 'popup'
})
return
}
const isTypedSigned = [
'signTypedData',
'eth_signTypedData',
'signTypedData_v1',
'eth_signTypedData_v1',
'signTypedData_v3',
'eth_signTypedData_v3',
'signTypedData_v4',
'eth_signTypedData_v4'].includes(message?.method);
const signMsgData = isTypedSigned ? String(message?.params?.[1] ?? '') : String(message?.params?.[0] ?? '');
await new Promise((resolve, reject) => {
chrome.windows.create({
height: 510,
width: 480,
url: chrome.runtime.getURL(`index.html?route=sign-msg&param=${strToHex(signMsgData)}&rid=${String(message?.resId ?? '')}`),
type: 'popup'
}).then((win) => {
userReject[String(win.id)] = reject
userApprove[String(win.id)] = resolve
rIdWin[String(win.id)] = String(message.resId)
})
})
return
}
const isTypedSigned = [
'signTypedData',
'eth_signTypedData',
'signTypedData_v1',
'eth_signTypedData_v1',
'signTypedData_v3',
'eth_signTypedData_v3',
'signTypedData_v4',
'eth_signTypedData_v4'].includes(message?.method);
const signMsgData = isTypedSigned ? String(message?.params?.[1] ?? '' ) : String(message?.params?.[0] ?? '' );
await new Promise((resolve, reject) => {
chrome.windows.create({
height: 510,
width: 480,
url: chrome.runtime.getURL(`index.html?route=sign-msg&param=${strToHex(signMsgData)}&rid=${String(message?.resId ?? '')}`),
type: 'popup'
}).then((win) => {
userReject[String(win.id)] = reject
userApprove[String(win.id)] = resolve
rIdWin[String(win.id)] = String(message.resId)
})
})
sendResponse(
isTypedSigned ?
await signTypedData(signMsgData):
await signMsg(signMsgData)
)
const settings = await getSettings()
if(settings.encryptAfterEveryTx) {
clearPk()
}
sendResponse(
isTypedSigned ?
await signTypedData(signMsgData) :
await signMsg(signMsgData)
)
const settings = await getSettings()
if (settings.encryptAfterEveryTx) {
clearPk()
}
} catch (e) {
console.warn('Error: signTypedData', e)
sendResponse({
@ -609,9 +637,9 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
sendResponse([{
id: smallRandomString(21),
parentCapability: 'eth_accounts',
invoker: message?.website?.split('/').slice(0,3).join('/') ?? '',
invoker: message?.website?.split('/').slice(0, 3).join('/') ?? '',
caveats: [{
type:'restrictReturnedAccounts',
type: 'restrictReturnedAccounts',
value: address
}],
date: Date.now(),
@ -633,127 +661,130 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
case 'wallet_switchEthereumChain': {
try {
const currentChainId = `0x${((await getSelectedNetwork())?.chainId ?? 0).toString(16)}`
if(currentChainId === String(message?.params?.[0]?.chainId ?? '' )) {
if (currentChainId === String(message?.params?.[0]?.chainId ?? '')) {
sendResponse(null)
}else {
await new Promise((resolve, reject) => {
chrome.windows.create({
height: 450,
width: 400,
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
userApprove[String(win.id)] = resolve
rIdWin[String(win.id)] = String(message.resId)
})
})
sendResponse(null)
}
} catch {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'User Rejected chain switch'
} else {
await new Promise((resolve, reject) => {
chrome.windows.create({
height: 450,
width: 400,
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
userApprove[String(win.id)] = resolve
rIdWin[String(win.id)] = String(message.resId)
})
})
sendResponse(null)
}
} catch {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'User Rejected chain switch'
})
}
break
}
case 'wallet_addEthereumChain': {
const userNetworks = await getNetworks()
const networks = {...allTemplateNets, ...userNetworks}
const networks = { ...allTemplateNets, ...userNetworks }
const chainId = Number(message?.params?.[0]?.chainId ?? '0')
if(!chainId) {
if (!chainId) {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'Invalid Network'
})
}
if( chainId in networks ) {
mainListner({...message, method:'wallet_switchEthereumChain', params: [{
chainId: `0x${(chainId).toString(16)}`
}] }, sender, sendResponse)
if (chainId in networks) {
mainListner({
...message, method: 'wallet_switchEthereumChain', params: [{
chainId: `0x${(chainId).toString(16)}`
}]
}, sender, sendResponse)
} else {
if ( !message?.params?.[0]?.chainId ||
!message?.params?.[0]?.chainName ||
!message?.params?.[0]?.rpcUrls ||
!message?.params?.[0]?.blockExplorerUrls ||
!message?.params?.[0]?.nativeCurrency?.symbol
){
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'Invalid Network params chainId, chainName, rpcUrls, blockExplorerUrls, and nativeCurrency are required'
})
}else {
try {
await new Promise((resolve, reject) => {
if (!message?.params?.[0]?.chainId ||
!message?.params?.[0]?.chainName ||
!message?.params?.[0]?.rpcUrls ||
!message?.params?.[0]?.blockExplorerUrls ||
!message?.params?.[0]?.nativeCurrency?.symbol
) {
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'Invalid Network params chainId, chainName, rpcUrls, blockExplorerUrls, and nativeCurrency are required'
})
} else {
try {
await new Promise((resolve, reject) => {
chrome.windows.create({
height: 450,
width: 400,
url: chrome.runtime.getURL(`index.html?route=request-network&param=${strToHex(JSON.stringify({...{website: message?.website ?? ''}, ...(message?.params?.[0] ?? {})}) ?? '')}&rid=${String(message?.resId ?? '')}`),
url: chrome.runtime.getURL(`index.html?route=request-network&param=${strToHex(JSON.stringify({ ...{ website: message?.website ?? '' }, ...(message?.params?.[0] ?? {}) }) ?? '')}&rid=${String(message?.resId ?? '')}`),
type: 'popup'
}).then((win) => {
userReject[String(win.id)] = reject
userApprove[String(win.id)] = resolve
rIdWin[String(win.id)] = String(message.resId)
})
})
sendResponse(null)
} catch (err) {
console.error('err')
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'User Rejected adding network'
})
}
})
sendResponse(null)
} catch (err) {
console.error('err')
sendResponse({
error: true,
code: rpcError.USER_REJECTED,
message: 'User Rejected adding network'
})
}
}
}
break
}
// internal messeges
case 'wallet_connect': {
const pNetwork = getSelectedNetwork()
const pAccount = getSelectedAccount()
const pNetwork = getSelectedNetwork()
const pAccount = getSelectedAccount()
const [network, account] = await Promise.all([pNetwork, pAccount])
const address = account?.address ? [account?.address] : []
const chainId = `0x${(network?.chainId ?? 0).toString(16)}`
const data = {
const data = {
type: "CLWALLET_PAGE_LISTENER", data: {
listner: 'connect',
data: {
chainId
},
address
}};
listner: 'connect',
data: {
chainId
},
address
}
};
sendResponse(data)
break
}
case 'wallet_approve': {
if(String(sender.tab?.windowId) in rIdWin){
if (String(sender.tab?.windowId) in rIdWin) {
userApprove[String(sender.tab?.windowId)]?.(true)
}
try {
chrome.windows.remove(sender.tab?.windowId ?? 0)
}catch (e) {
} catch (e) {
console.info(e)
// ignore
}
break
}
case 'wallet_send_data': {
if(String(sender.tab?.windowId) in rIdData){
if (String(sender.tab?.windowId) in rIdData) {
const intData = rIdData[String(sender?.tab?.windowId ?? '')] ?? {}
rIdData[String(sender?.tab?.windowId ?? '')] = {...intData, ...(message?.data ?? {})}
rIdData[String(sender?.tab?.windowId ?? '')] = { ...intData, ...(message?.data ?? {}) }
sendResponse(true)
}
break
}
case 'wallet_get_data': {
if(String(sender.tab?.windowId) in rIdData){
sendResponse( rIdData[String(sender?.tab?.windowId ?? '')] ?? {})
if (String(sender.tab?.windowId) in rIdData) {
sendResponse(rIdData[String(sender?.tab?.windowId ?? '')] ?? {})
}
break
}
@ -765,13 +796,13 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an
sendResponse({
error: true,
code: rpcError.INVALID_PARAM,
message: 'ClearWallet: Invalid request method ' + (message?.method ?? '')
message: 'ClearWallet: Invalid request method ' + (message?.method ?? '')
})
break
}
}
}
}
)();
return true;

View File

@ -81,3 +81,64 @@ export interface ContractAction {
export interface ContractActions {
[key: string] : ContractAction
}
export interface UniSwapPortfolioResponse {
data: {
portfolios: {
id: string;
tokenBalances: {
id: string;
quantity: number;
denominatedValue: {
value: number;
}
token: {
id: string;
address: string;
chain: string;
symbol: string;
name: string;
decimals: number;
standard: string;
project: {
id: string;
name: string;
logo: null;
safetyLevel: string;
logoUrl: null;
isSpam: boolean;
__typename: string;
};
__typename: string;
};
tokenProjectMarket: {
id: string;
pricePercentChange: null;
tokenProject: {
id: string;
logoUrl: null;
isSpam: boolean;
__typename: string;
};
__typename: string;
};
__typename: string;
}[];
__typename: string;
tokensTotalDenominatedValue: {
id: string;
value: number;
}
tokensTotalDenominatedValueChange: {
absolute: {
id: string;
value: number;
}
percentage: {
id: string;
value: number;
}
}
}[];
};
}

View File

@ -40,7 +40,7 @@ export const mainNets: {[key: number]: Network} = {
},
56: {
name: 'BSC Main',
rpc: 'https://bsc-dataseed2.binance.org',
rpc: 'https://bsc-dataseed1.binance.org',
chainId: 56,
explorer: 'https://bscscan.com',
icon: 'binance.webp',

View File

@ -72,7 +72,10 @@ export const getGasPrice = async () => {
const { provider } = await getCurrentProvider()
const feed = await provider.getFeeData()
const gasPrice = feed.maxFeePerGas ?? feed.gasPrice ?? 0n
return Number(gasPrice) / 1e9
return {
price: Number(gasPrice) / 1e9,
feed
}
}
export const getBlockNumber = async () => {
@ -152,8 +155,8 @@ export const getRandomPk = () => {
return ethers.Wallet.createRandom().privateKey
}
export const sendTransaction = async ({ data= '', gas='0x0', to='', from='', value='', gasPrice='0x0'}:
{to: string, from: string, data: string, value: string, gas: string, gasPrice: string}) => {
export const sendTransaction = async ({ data= '', gas='0x0', to='', from='', value='', gasPrice='0x0', supportsEIP1559=true}:
{to: string, from: string, data: string, value: string, gas: string, gasPrice: string, supportsEIP1559: boolean}) => {
const account = await getSelectedAccount()
const { provider } = await getCurrentProvider()
const wallet = new ethers.Wallet(account.pk, provider)
@ -163,7 +166,7 @@ export const sendTransaction = async ({ data= '', gas='0x0', to='', from='', val
if(gas === '0x0' || gasPrice === '0x0') {
throw new Error('No gas estimate available')
}
return await wallet.sendTransaction({
return supportsEIP1559 ? await wallet.sendTransaction({
to,
from,
data: data ? data : null,
@ -171,15 +174,24 @@ export const sendTransaction = async ({ data= '', gas='0x0', to='', from='', val
gasLimit: gasInt,
gasPrice: null,
maxFeePerGas: gasPriceInt,
}) :
await wallet.sendTransaction({
to,
from,
data: data ? data : null,
value: value ? value : null,
gasLimit: gasInt,
gasPrice: gasPriceInt
})
}
export const formatBalance = (balance: string) => {
Intl.NumberFormat('en-US', {
notation: 'compact',
maximumFractionDigits: 6
}).format(Number(ethers.parseEther(balance)))
}
export const formatNumber = (num: number, digits = 0) => {
return Intl.NumberFormat('en-US', {
notation: 'compact',
maximumFractionDigits: digits
}).format(num)
}
export const getSelectedAddress = async () => {
// give only the selected address for better privacy

View File

@ -24,147 +24,96 @@
></ion-toast>
<ion-item>
<ion-label>Assests for Account: {{ selectedAccount?.name }}</ion-label>
<ion-label style="text-align: center"
>Assets for: {{ selectedAccount?.name }}</ion-label
>
</ion-item>
<ion-item button @click="copyText(selectedAccount?.address, getToastRef())">
<p style="font-size: 0.7rem">{{ selectedAccount?.address }}</p>
<ion-icon style="margin-left: 0.5rem" :icon="copyOutline"></ion-icon>
</ion-item>
<ion-item v-if="assetsValue?.value">
<ion-list>
<ion-item
><b>Total Value:&nbsp;</b>
<span style="color: #ffcc00; font-size: 0.9rem">
{{ formatNumber(assetsValue?.value, 2) }} $</span
></ion-item
>
<ion-item
><b>24H Change:&nbsp;</b>
<arrow-up v-if="assetsChange.percentage.value > 0" />
<arrow-down v-else />
<span
:style="`font-size: 0.9rem;color: ${
assetsChange.percentage.value > 0 ? '#33cc33' : '#ff5050'
}`"
>
{{ formatNumber(assetsChange.percentage.value, 2) }}%</span
></ion-item
>
</ion-list>
</ion-item>
<ion-item>
<div style="display: flex; flex-direction: column; margin: auto">
<button
alt="ERC20 Bridge"
@click="openTab('https://erc20-bridge.pages.dev/')"
class="bridge-button"
>
<bridge-icon />
Community ERC20 Bridge
</button>
</div>
</ion-item>
<template v-if="isError">
Assets info could not be retrieved because of an http error, API down or
conectivity issues.
</template>
<template v-else-if="noAssets">
<!-- <p class="padding: 1rem;">
No know assets found for this wallet address.
</p> -->
<p class="padding: 1rem;">
Assets view temporarily disabled until finding better provider. As old
provider(yup.io) is no longer available.
</p>
<p class="padding: 1rem;">No know assets found for this wallet address.</p>
</template>
<template v-else>
<template v-if="ethTokens.length || polyTokens.length">
<template v-if="ethTokens.length">
<ion-item>Ethereum Tokens</ion-item>
<ion-list>
<ion-item v-for="token of ethTokens" :key="token.address">
<ion-avatar
v-if="token?.image"
style="margin-right: 1rem; width: 1.6rem; height: 1.6rem"
>
<img
:alt="token?.name"
:src="token?.image"
@error="token.image = getUrl('assets/chain-icons/eth.webp')"
/>
</ion-avatar>
<ion-label
><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label
>
</ion-item>
<ion-item v-if="hasMore.ethTokens">
<ion-button @click="loadMore('ethTokens')">Load More</ion-button>
</ion-item>
</ion-list>
</template>
<template v-if="polyTokens.length">
<ion-item>Polygon Tokens</ion-item>
<ion-list>
<ion-item v-for="token of polyTokens" :key="token.address">
<ion-avatar
v-if="token?.image"
style="margin-right: 1rem; width: 1.6rem; height: 1.6rem"
>
<img
:alt="token?.name"
:src="token?.image"
@error="token.image = getUrl('assets/randomGrad.svg')"
/>
</ion-avatar>
<ion-label
><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label
>
</ion-item>
<ion-item v-if="hasMore.polyTokens">
<ion-button @click="loadMore('polyTokens')">Load More</ion-button>
</ion-item>
</ion-list>
</template>
</template>
<template v-if="ethNfts.length || polyNfts.length">
<template v-if="ethNfts.length">
<ion-item>Ethereum NFTs</ion-item>
<ion-list>
<ion-item v-for="nft of ethNfts" :key="nft.address">
<ion-avatar
v-if="nft?.imageURI"
style="margin-right: 1rem; width: 1.6rem; height: 1.6rem"
>
<img
:alt="nft?.collectionName"
:src="nft?.imageURI"
@error="nft.imageURI = getUrl('assets/randomGrad.svg')"
/>
</ion-avatar>
<ion-label
><b>{{ nft?.collectionName }}</b></ion-label
>
</ion-item>
<ion-item v-if="hasMore.ethNfts">
<ion-button @click="loadMore('ethNfts')">Load More</ion-button>
</ion-item>
</ion-list>
</template>
<template v-if="polyNfts.length">
<ion-item>Polygon NFTs</ion-item>
<ion-list>
<ion-item v-for="nft of polyNfts" :key="nft.address">
<ion-avatar
v-if="nft?.imageURI"
style="margin-right: 1rem; width: 1.6rem; height: 1.6rem"
>
<img
:alt="nft?.collectionName"
:src="nft?.imageURI"
@error="nft.imageURI = getUrl('assets/randomGrad.svg')"
/>
</ion-avatar>
<ion-label
><b>{{ nft?.collectionName }}</b></ion-label
>
</ion-item>
<ion-item v-if="hasMore.polyNfts">
<ion-button @click="loadMore('polyNfts')">Load More</ion-button>
</ion-item>
</ion-list>
</template>
</template>
<template v-if="poaps.length">
<ion-item>POAPs</ion-item>
<ion-list>
<ion-item>
<ion-label style="text-align: center">Tokens</ion-label>
</ion-item>
<ion-list>
<ion-item v-for="nft of poaps" :key="nft.eventId">
<ion-avatar
v-if="nft?.image"
style="margin-right: 1rem; width: 1.6rem; height: 1.6rem"
>
<ion-item v-for="token of shownTokens" :key="token.token.address">
<ion-avatar style="margin-right: 1rem; width: 1.6rem; height: 1.6rem">
<img
:alt="nft?.title"
:src="nft?.image"
@error="nft.image = getUrl('assets/randomGrad.svg')"
v-if="token?.token?.project?.logoUrl"
:alt="token?.token?.name"
:src="token?.token?.project?.logoUrl"
/>
<img
v-else
:alt="token?.token?.name"
:src="getUrl('assets/randomGrad.svg')"
/>
</ion-avatar>
<ion-label
><b>{{ nft?.title }}</b></ion-label
<ion-label class="flex-col flex">
<div class="flex">
<b>{{ token?.token?.symbol }}:</b>
{{ formatNumber(token?.quantity, 4) }}
</div>
<div class="flex">
<span style="font-size: 0.8rem; opacity: 0.7">{{
token?.token?.chain
}}</span>
</div> </ion-label
><span style="font-size: 0.8rem; opacity: 0.7"
>{{ formatNumber(token?.denominatedValue?.value, 2) }} $</span
>
</ion-item>
<ion-item v-if="hasMore.poaps">
<ion-button @click="loadMore('poaps')">Load More</ion-button>
<ion-item v-if="alltokens.length > shownTokens.length">
<ion-button
@click="shownTokens = alltokens.slice(0, shownTokens.length + 10)"
>Load More</ion-button
>
</ion-item>
</ion-list>
</template>
</ion-list>
</template>
</ion-content>
</ion-page>
@ -188,35 +137,13 @@ import {
IonLoading,
IonIcon,
} from "@ionic/vue";
import { getSelectedAccount, copyText, getUrl } from "@/utils/platform";
import type { Account } from "@/extension/types";
import { getSelectedAccount, copyText, getUrl, openTab } from "@/utils/platform";
import type { Account, UniSwapPortfolioResponse } from "@/extension/types";
import { formatNumber } from "@/utils/wallet";
import ArrowDown from "@/components/icons/ArrowDown.vue";
import ArrowUp from "@/components/icons/ArrowUp.vue";
import { copyOutline } from "ionicons/icons";
interface IProfileToken {
address: string;
balance: number;
image: string;
name: string;
symbol: string;
}
interface IProfileNFT {
address: string;
collectionImageURI: string;
collectionName: string;
imageURI: string;
link: string;
tokenId: number;
}
interface IProfilePOAP {
description: string;
eventId: string;
image: string;
link: string;
title: string;
}
import BridgeIcon from "@/components/icons/Bridge.vue";
export default defineComponent({
components: {
@ -233,6 +160,9 @@ export default defineComponent({
IonToast,
IonLoading,
IonIcon,
ArrowDown,
ArrowUp,
BridgeIcon,
},
setup: () => {
const selectedAccount = ref({}) as Ref<Account>;
@ -240,328 +170,103 @@ export default defineComponent({
const isError = ref(false);
const noAssets = ref(false);
const toastState = ref(false);
const ethTokens = ref([]) as Ref<IProfileToken[]>;
const polyTokens = ref([]) as Ref<IProfileToken[]>;
const ethNfts = ref([]) as Ref<IProfileNFT[]>;
const polyNfts = ref([]) as Ref<IProfileNFT[]>;
const poaps = ref([]) as Ref<IProfilePOAP[]>;
const hasMore = reactive({
poaps: true,
ethTokens: true,
polyTokens: true,
ethNfts: true,
polyNfts: true,
});
const getToastRef = () => toastState;
const alltokens = ref({}) as Ref<
UniSwapPortfolioResponse["data"]["portfolios"][0]["tokenBalances"]
>;
const shownTokens = ref([]) as Ref<
UniSwapPortfolioResponse["data"]["portfolios"][0]["tokenBalances"]
>;
const resources = ["nfts", "poaps", "tokens"];
const chains = ["ethereum", "polygon"];
const assetsValue = ref({}) as Ref<
UniSwapPortfolioResponse["data"]["portfolios"][0]["tokensTotalDenominatedValue"]
>;
const fetchFromWallet = async ({
apiBase = "https://api.yup.io",
address,
resource = resources[0],
chain = chains[0],
start = 0,
limit = 10,
}: {
apiBase?: string;
address: string;
resource?: string;
chain?: string;
start?: number;
limit?: number;
}) => {
try {
const res = await fetch(
`${apiBase}/web3-profiles/${resource}/${address}?chain=${chain}&start=${start}&limit=${limit}`
);
const req = await res.json();
if (res.ok) {
return req;
} else {
return null;
}
} catch (error) {
console.info("ERROR: Failed to fetch web3 profiles", error);
const assetsChange = ref({}) as Ref<
UniSwapPortfolioResponse["data"]["portfolios"][0]["tokensTotalDenominatedValueChange"]
>;
const UNISWAP_API_ENDPOINT = "https://interface.gateway.uniswap.org/v1/graphql";
const getUniwapAssets = async (ownerAddress: string) => {
const headers = {
"user-agent": "Desktop",
accept: "*/*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US,en;q=0.9",
"content-type": "application/json",
dnt: "1",
"Content-Type": "application/json",
origin: "https://app.uniswap.org",
referer: "https://app.uniswap.org/",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"sec-gpc": "1",
};
const query = {
operationName: "PortfolioBalancesWeb",
variables: {
includeSmallBalances: false,
includeSpamTokens: false,
ownerAddress,
chains: [
"ETHEREUM",
"OPTIMISM",
"BNB",
"POLYGON",
"ZKSYNC",
"BASE",
"ARBITRUM",
"CELO",
"AVALANCHE",
"BLAST",
"ZORA",
],
},
query:
"query PortfolioBalancesWeb($ownerAddress: String!, $chains: [Chain!]!, $includeSmallBalances: Boolean = false, $includeSpamTokens: Boolean = false) {\n portfolios(\n ownerAddresses: [$ownerAddress]\n chains: $chains\n valueModifiers: [{ownerAddress: $ownerAddress, includeSpamTokens: $includeSpamTokens, includeSmallBalances: $includeSmallBalances}]\n ) {\n id\n tokensTotalDenominatedValue {\n id\n value\n __typename\n }\n tokensTotalDenominatedValueChange(duration: DAY) {\n absolute {\n id\n value\n __typename\n }\n percentage {\n id\n value\n __typename\n }\n __typename\n }\n tokenBalances {\n ...PortfolioTokenBalanceParts\n __typename\n }\n __typename\n }\n}\n\nfragment PortfolioTokenBalanceParts on TokenBalance {\n id\n quantity\n denominatedValue {\n id\n currency\n value\n __typename\n }\n token {\n ...SimpleTokenDetails\n id\n address\n chain\n symbol\n name\n decimals\n standard\n project {\n id\n name\n logo {\n id\n url\n __typename\n }\n safetyLevel\n logoUrl\n isSpam\n __typename\n }\n __typename\n }\n tokenProjectMarket {\n id\n pricePercentChange(duration: DAY) {\n id\n value\n __typename\n }\n tokenProject {\n id\n logoUrl\n isSpam\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment SimpleTokenDetails on Token {\n id\n address\n chain\n symbol\n name\n decimals\n standard\n project {\n id\n name\n logo {\n id\n url\n __typename\n }\n safetyLevel\n logoUrl\n isSpam\n __typename\n }\n __typename\n}",
};
const req = await fetch(UNISWAP_API_ENDPOINT, {
method: "POST",
headers,
body: JSON.stringify(query),
});
if (!req.ok) {
return null;
}
};
const walletLoadArgs = {
address: "",
start: 0,
limit: 11,
res: resources,
ch: chains,
apiBase: "https://api.yup.io",
};
const getProfileWallet = async ({
address,
start,
limit,
res,
ch,
apiBase,
}: {
address: string;
start: number;
limit: number;
res: string[];
ch: string[];
apiBase: string;
}) => {
const r = {
poaps: [] as IProfilePOAP[],
ethNfts: [] as IProfileNFT[],
polyNfts: [] as IProfileNFT[],
ethTokens: [] as IProfileToken[],
polyTokens: [] as IProfileToken[],
};
try {
const promises = [];
if (res.includes("poaps")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "poaps",
chain: "ethereum",
}).then((rz) => {
r.poaps = rz ?? [];
})
);
}
if (res.includes("nfts")) {
if (ch.includes("ethereum")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "nfts",
chain: "ethereum",
}).then((rz) => {
r.ethNfts = rz ?? [];
})
);
}
if (ch.includes("polygon")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "nfts",
chain: "polygon",
}).then((rz) => {
r.polyNfts = rz ?? [];
})
);
}
}
if (res.includes("tokens")) {
if (ch.includes("ethereum")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "tokens",
chain: "ethereum",
}).then((rz) => {
r.ethTokens = rz ?? [];
})
);
}
if (ch.includes("polygon")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "tokens",
chain: "polygon",
}).then((rz) => {
r.polyTokens = rz ?? [];
})
);
}
}
await Promise.all(promises);
return r;
} catch {
return r;
}
const res = await req.json();
return res as UniSwapPortfolioResponse;
};
onIonViewWillEnter(async () => {
selectedAccount.value = await getSelectedAccount();
walletLoadArgs.address = selectedAccount.value.address;
const r = await getProfileWallet(walletLoadArgs);
ethNfts.value = r.ethNfts.slice(0, 10);
if (r.ethNfts.length !== walletLoadArgs.limit) {
hasMore.ethNfts = false;
const result = await getUniwapAssets(selectedAccount.value.address);
if (!result) {
isError.value = true;
loading.value = false;
return;
}
polyNfts.value = r.polyNfts.slice(0, 10);
if (r.polyNfts.length !== walletLoadArgs.limit) {
hasMore.polyNfts = false;
if (result?.data?.portfolios?.length) {
alltokens.value = result.data.portfolios[0].tokenBalances.filter(
(token) => token.denominatedValue && !token.token.project.isSpam
);
shownTokens.value = alltokens.value.slice(0, 10);
assetsValue.value = result.data.portfolios[0].tokensTotalDenominatedValue;
assetsChange.value = result.data.portfolios[0].tokensTotalDenominatedValueChange;
} else {
noAssets.value = true;
}
ethTokens.value = r.ethTokens.slice(0, 10);
if (r.ethTokens.length !== walletLoadArgs.limit) {
hasMore.ethTokens = false;
}
polyTokens.value = r.polyTokens.slice(0, 10);
if (r.polyTokens.length !== walletLoadArgs.limit) {
hasMore.polyTokens = false;
}
poaps.value = r.poaps.slice(0, -1);
if (r.poaps.length !== walletLoadArgs.limit) {
hasMore.poaps = false;
}
noAssets.value =
poaps.value.length &&
ethNfts.value.length &&
polyNfts.value.length &&
ethTokens.value.length &&
polyTokens.value.length
? false
: true;
loading.value = false;
// const req = await fetch(`${yupAssetsApi}/${selectedAccount.value.address}`);
// if (req.ok) {
// assets.value = (await req.json()) ?? {};
// if (
// !("poaps" in assets.value) &&
// !("tokens" in assets.value) &&
// !("nfts" in assets.value)
// ) {
// noAssets.value = true;
// }
// if ("poaps" in assets.value) {
// poaps.value = assets.value?.poaps?.slice(0, 10) ?? [];
// if (poaps.value.length >= (assets.value?.poaps?.length ?? 0)) {
// hasMore.poaps = false;
// }
// }
// if ("nfts" in assets.value) {
// ethNfts.value =
// assets.value?.nfts?.filter((n) => n.chain === "ethereum").slice(0, 10) ?? [];
// if (
// ethNfts.value.length >=
// (assets.value?.nfts?.filter((n) => n.chain === "ethereum").length ?? 0)
// ) {
// hasMore.ethNfts = false;
// }
// polyNfts.value =
// assets.value?.nfts?.filter((n) => n.chain === "polygon").slice(0, 10) ?? [];
// if (
// polyNfts.value.length >=
// (assets.value?.nfts?.filter((n) => n.chain === "polygon").length ?? 0)
// ) {
// hasMore.polyNfts = false;
// }
// }
// if ("tokens" in assets.value) {
// ethTokens.value =
// assets.value?.tokens?.filter((n) => n.chain === "ethereum").slice(0, 10) ??
// [];
// if (
// ethTokens.value.length >=
// (assets.value?.tokens?.filter((n) => n.chain === "ethereum").length ?? 0)
// ) {
// hasMore.ethTokens = false;
// }
// polyTokens.value =
// assets.value?.tokens?.filter((n) => n.chain === "polygon").slice(0, 10) ?? [];
// if (
// polyTokens.value.length >=
// (assets.value?.tokens?.filter((n) => n.chain === "polygon").length ?? 0)
// ) {
// hasMore.polyTokens = false;
// }
// }
// } else {
// isError.value = true;
// }
// loading.value = false;
});
const loadMore = async (type: string) => {
switch (type) {
case "ethTokens": {
walletLoadArgs.start = ethTokens.value.length;
walletLoadArgs.res = ["tokens"];
walletLoadArgs.ch = ["ethereum"];
const r = await getProfileWallet(walletLoadArgs);
if (r.ethTokens.length !== walletLoadArgs.limit) {
hasMore.ethTokens = false;
return;
}
ethTokens.value = [...ethTokens.value, ...r.ethTokens.slice(0, 10)];
break;
}
case "polyTokens": {
walletLoadArgs.start = polyTokens.value.length;
walletLoadArgs.res = ["tokens"];
walletLoadArgs.ch = ["polygon"];
const r = await getProfileWallet(walletLoadArgs);
if (r.polyTokens.length !== walletLoadArgs.limit) {
hasMore.polyTokens = false;
return;
}
polyTokens.value = [...polyTokens.value, ...r.polyTokens.slice(0, 10)];
break;
}
case "ethNfts": {
walletLoadArgs.start = ethNfts.value.length;
walletLoadArgs.res = ["nfts"];
walletLoadArgs.ch = ["ethereum"];
const r = await getProfileWallet(walletLoadArgs);
if (r.ethNfts.length !== walletLoadArgs.limit) {
hasMore.ethNfts = false;
return;
}
ethNfts.value = [...ethNfts.value, ...r.ethNfts.slice(0, 10)];
break;
}
case "polyNfts": {
walletLoadArgs.start = polyNfts.value.length;
walletLoadArgs.res = ["nfts"];
walletLoadArgs.ch = ["polygon"];
const r = await getProfileWallet(walletLoadArgs);
if (r.polyNfts.length !== walletLoadArgs.limit) {
hasMore.polyNfts = false;
return;
}
polyNfts.value = [...polyNfts.value, ...r.polyNfts.slice(0, 10)];
break;
}
case "poaps": {
walletLoadArgs.start = poaps.value.length;
walletLoadArgs.res = ["poaps"];
walletLoadArgs.ch = ["ethereum"];
const r = await getProfileWallet(walletLoadArgs);
if (r.poaps.length !== walletLoadArgs.limit) {
hasMore.poaps = false;
return;
}
poaps.value = [...poaps.value, ...r.poaps.slice(0, 10)];
break;
}
}
};
return {
selectedAccount,
loading,
@ -570,16 +275,49 @@ export default defineComponent({
getToastRef,
copyText,
copyOutline,
ethTokens,
polyTokens,
ethNfts,
poaps,
hasMore,
polyNfts,
loadMore,
toastState,
getUrl,
openTab,
alltokens,
shownTokens,
assetsValue,
assetsChange,
formatNumber,
};
},
});
</script>
<style lang="scss" scoped>
.bridge-button {
padding-top: 0.5rem;
padding-bottom: 0.6rem;
margin-top: 0.25rem;
margin-bottom: 0.25rem;
border-radius: 0.5rem;
width: 100%;
font-size: 0.9rem;
line-height: 1.2rem;
font-weight: 500;
text-align: center;
color: #fff;
background-color: #312e81;
padding-right: 1rem;
padding-left: 1rem;
box-shadow: 0 1px 3px #0000001a, 0 1px 2px #0000000f;
svg {
margin-right: 0.3rem;
top: 0.3rem;
display: inline-block;
width: 1.3rem;
position: relative;
}
}
.bridge-button:hover {
background-color: #37368f;
transform: scale(1.05);
transition: all 0.3s;
}
</style>

View File

@ -15,7 +15,6 @@
v-if="version"
style="
position: absolute;
top: 0.3rem;
right: 1.1rem;
margin-left: 0.3rem;
color: coral;
@ -24,6 +23,11 @@
"
>Version: {{ version }}</span
>
<span
class="github-icon"
@click="openTab('https://github.com/andrei0x309/clear-wallet/')"
><GitHub
/></span>
</ion-title>
</ion-toolbar>
</ion-header>
@ -285,6 +289,7 @@ import { allTemplateNets } from "@/utils/networks";
import router from "@/router";
import { triggerListner } from "@/extension/listners";
import { copyOutline } from "ionicons/icons";
import GitHub from "@/components/icons/GitHub.vue";
const version = getVersion();
@ -309,6 +314,7 @@ export default defineComponent({
IonToast,
IonIcon,
IonAvatar,
GitHub,
},
setup: () => {
const loading = ref(false);
@ -434,4 +440,21 @@ export default defineComponent({
transition: opacity 0.2s ease-in-out;
transform: scale(1.05);
}
.github-icon {
position: absolute;
top: 0.9rem;
right: 2.4rem;
margin-left: 0.3rem;
color: coral;
font-weight: bold;
font-size: 0.65rem;
cursor: pointer;
}
.github-icon:hover {
opacity: 0.8;
transition: opacity 0.2s ease-in-out;
transform: scale(1.05);
}
</style>

View File

@ -11,7 +11,12 @@
<ion-label>Message to Sign</ion-label>
</ion-item>
<ion-item>
<div style="white-space: pre-wrap" disabled>{{ signMsg }}</div>
<div
style="white-space: pre-wrap; width: 100%; height: 250px; overflow-y: scroll"
disabled
>
{{ signMsg }}
</div>
</ion-item>
<ion-item>
<ion-button @click="onCancel">Cancel</ion-button>
@ -80,7 +85,19 @@ export default defineComponent({
const route = useRoute();
const loading = ref(false);
const rid = (route?.params?.rid as string) ?? "";
const signMsg = ref(hexTostr(hexTostr((route?.params?.param as string) ?? "")));
let sigmMsg: string = "";
try {
const typeSign = JSON.parse(
hexTostr(hexTostr((route?.params?.param as string) ?? ""))
);
sigmMsg = JSON.stringify(typeSign, null, 2);
} catch (e) {
sigmMsg = hexTostr(hexTostr((route?.params?.param as string) ?? ""));
}
const signMsg = ref(sigmMsg);
const alertOpen = ref(false);
const alertMsg = ref("");
const timerReject = ref(140);

View File

@ -67,7 +67,7 @@
<ion-label>Raw TX:</ion-label>
<ion-textarea
aria-label="raw tx"
style="overflow-y: scroll"
style="overflow-y: scroll; width: 400px; height: 200px"
:rows="10"
:cols="20"
:value="signTxData"
@ -250,6 +250,7 @@ export default defineComponent({
const gasPriceModal = ref(false);
const inGasPrice = ref(0);
const inGasLimit = ref(0);
let gasFeed = {} as Awaited<ReturnType<typeof getGasPrice>>["feed"];
let interval = 0;
const bars = ref(0);
@ -317,11 +318,10 @@ export default defineComponent({
const newGasData = async () => {
await walletSendData(rid, {
gas: numToHexStr(gasLimit.value),
gasPrice: numToHexStr(BigInt(Math.trunc(gasPrice.value * 1e9))),
supportsEIP1559: gasFeed?.maxFeePerGas !== null,
});
await walletSendData(rid, {
gasPrice: numToHexStr(BigInt(Math.trunc(gasPrice.value * 1e9))),
});
gasFee.value = Number(
ethers.formatUnits(Math.trunc(gasLimit.value * gasPrice.value), "gwei")
);
@ -345,8 +345,10 @@ export default defineComponent({
userBalance.value = Number(
ethers.formatEther((await pBalance).toString() ?? "0x0")
);
const { feed, price } = await pGasPrice;
gasFeed = feed;
gasPrice.value = parseFloat((await pGasPrice).toString() ?? 0.1);
gasPrice.value = parseFloat(price.toString() ?? 0.1);
try {
gasLimit.value = parseInt((await pEstimateGas).toString(), 10);
@ -378,7 +380,9 @@ export default defineComponent({
if (timerFee.value <= 0) {
timerFee.value = 20;
loading.value = true;
gasPrice.value = parseFloat((await getGasPrice()).toString() ?? 0.1);
const { feed, price } = await getGasPrice();
gasFeed = feed;
gasPrice.value = parseFloat(price.toString() ?? 0.1);
await newGasData();
loading.value = false;
}

View File

@ -41,12 +41,12 @@
>
<ion-item>
<ion-avatar
v-if="(existingNetworks as any)[networkId]?.icon"
v-if="(allTemplateNets as any)[(existingNetworks as any)[networkId]?.chainId]?.icon"
style="margin-right: 1rem; width: 1.6rem; height: 1.6rem"
>
<img
:alt="(existingNetworks as any)[networkId]?.name"
:src="getUrl('assets/chain-icons/' + (existingNetworks as any)[networkId].icon)"
:src="getUrl('assets/chain-icons/' + (allTemplateNets as any)[(existingNetworks as any)[networkId]?.chainId].icon)"
/>
</ion-avatar>
<ion-label

View File

@ -39,7 +39,7 @@
type="password"
@ion-input="mpPass = String($event.target.value)"
fill="solid"
ref="ionInput"
ref="passInput"
></ion-input>
<!-- <ion-input
@ -77,7 +77,7 @@
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from "vue";
import { defineComponent, ref, Ref, onMounted } from "vue";
import {
IonContent,
IonHeader,
@ -124,6 +124,7 @@ export default defineComponent({
const loading = ref(false);
const alertOpen = ref(false);
const alertMsg = ref("");
const passInput = ref() as Ref<typeof IonInput>;
const close = () => {
return modalController?.dismiss(null, "cancel");
@ -153,12 +154,14 @@ export default defineComponent({
}
};
onMounted(async () => {
await new Promise((resolve) => setTimeout(resolve, 150));
const el = document?.querySelector(
".password-input .native-input"
) as HTMLInputElement;
el?.focus?.();
onMounted(() => {
requestAnimationFrame(async () => {
if (passInput.value) {
await new Promise((resolve) => setTimeout(resolve, 50));
console.log("passInput.value", passInput.value);
passInput.value.$el.setFocus();
}
});
});
return {
@ -168,6 +171,7 @@ export default defineComponent({
alertOpen,
alertMsg,
close,
passInput,
};
},
});