2022-10-16 22:25:20 +00:00
import { getSelectedAccount , getSelectedNetwork , smallRandomString , getSettings , clearPk , openTab , getUrl , addToHistory } from '@/utils/platform' ;
2022-10-07 17:07:59 +00:00
import { userApprove , userReject , rIdWin , rIdData } from '@/extension/userRequest'
2022-10-16 22:25:20 +00:00
import { signMsg , getBalance , getBlockNumber , estimateGas , sendTransaction , getGasPrice , getBlockByNumber , evmCall , getTxByHash , getTxReceipt , signTypedData } from '@/utils/wallet'
2022-10-07 17:07:59 +00:00
import type { RequestArguments } from '@/extension/types'
import { rpcError } from '@/extension/rpcConstants'
import { updatePrices } from '@/utils/gecko'
2022-10-10 00:52:23 +00:00
let notificationUrl : string
2022-10-07 17:07:59 +00:00
chrome . runtime . onInstalled . addListener ( ( ) = > {
console . log ( 'Service worker installed' ) ;
chrome . runtime . connect ( null as unknown as string , {
name : 'sw-connection'
} )
} )
2022-10-10 00:52:23 +00:00
chrome . runtime . onConnect . addListener ( port = > port . onDisconnect . addListener ( ( ) = >
2022-10-07 17:07:59 +00:00
{
2022-10-10 00:52:23 +00:00
console . log ( 'Service worker connected' ) ;
2022-10-07 17:07:59 +00:00
} ) )
chrome . runtime . onStartup . addListener ( ( ) = > {
console . log ( 'Service worker startup' ) ;
} )
chrome . runtime . onSuspend . addListener ( ( ) = > {
console . log ( 'Service worker suspend' ) ;
} )
chrome . alarms . create ( 'updatePrices' , {
periodInMinutes : 1
} )
chrome . alarms . onAlarm . addListener ( ( alarm ) = > {
if ( alarm . name === 'updatePrices' ) {
updatePrices ( ) . then ( ( ) = > {
console . log ( 'Prices updated' )
} )
}
2022-10-10 23:01:14 +00:00
getSettings ( ) . then ( ( settings ) = > {
if ( ( ( settings . lastLock + settings . lockOutPeriod * 6 e4 ) < Date . now ( ) ) && settings . lockOutEnabled && ! settings . lockOutBlocked ) {
settings . lastLock = Date . now ( )
clearPk ( )
}
} )
2022-10-07 17:07:59 +00:00
} )
2022-10-10 00:52:23 +00:00
chrome . windows . onRemoved . addListener ( async ( winId ) = > {
2022-10-07 17:07:59 +00:00
if ( winId in ( userReject ? ? { } ) ) {
userReject [ winId ] ? . ( )
}
userReject [ winId ] = undefined
userApprove [ winId ] = undefined
rIdWin [ winId ] = undefined
rIdData [ winId ] = undefined
2022-10-10 00:52:23 +00:00
const wins = await chrome . windows . getAll ( )
2022-10-07 17:07:59 +00:00
if ( wins . length === 0 ) {
2022-10-10 00:52:23 +00:00
const s = await getSettings ( )
if ( s . enableStorageEnctyption ) {
await clearPk ( )
}
2022-10-07 17:07:59 +00:00
}
} )
2022-10-10 00:52:23 +00:00
const viewTxListner = async ( id : string ) = > {
try {
const url = new URL ( notificationUrl )
openTab ( url . href )
chrome . notifications . clear ( id )
} catch {
// ignore
}
}
if ( ! chrome . notifications . onButtonClicked . hasListener ( viewTxListner ) ) {
chrome . notifications . onButtonClicked . addListener ( viewTxListner )
}
2022-10-07 17:07:59 +00:00
chrome . runtime . onMessage . addListener ( ( message : RequestArguments , sender , sendResponse ) = > {
2022-10-15 20:20:33 +00:00
if ( message ? . type !== "CLWALLET_CONTENT_MSG" ) {
return true
}
2022-10-07 17:07:59 +00:00
( async ( ) = > {
if ( ! ( 'method' in message ) ) {
sendResponse ( {
code : 500 ,
message : 'Invalid request method'
} )
} else {
// ETH API
switch ( message . method ) {
case 'eth_call' : {
2022-10-16 22:25:20 +00:00
sendResponse ( await evmCall ( message ? . params ? . [ 0 ] ) )
2022-10-07 17:07:59 +00:00
break
}
2022-10-10 00:52:23 +00:00
case 'eth_getBlockByNumber' : {
const params = message ? . params ? . [ 0 ] as any
2022-10-10 23:01:14 +00:00
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 )
2022-10-10 00:52:23 +00:00
break ;
}
2022-10-16 22:25:20 +00:00
case 'eth_getTransactionByHash' : {
sendResponse ( await getTxByHash ( message ? . params ? . [ 0 ] as string ) )
break
}
case 'eth_getTransactionReceipt' : {
sendResponse ( await getTxReceipt ( message ? . params ? . [ 0 ] as string ) )
break
}
2022-10-10 00:52:23 +00:00
case 'eth_gasPrice' : {
2022-10-10 23:01:14 +00:00
sendResponse ( ( await getGasPrice ( ) ) . toHexString ( ) )
2022-10-10 00:52:23 +00:00
break ;
}
2022-10-07 17:07:59 +00:00
case 'eth_getBalance' : {
sendResponse ( await getBalance ( ) )
break
}
case 'eth_blockNumber' : {
sendResponse ( await getBlockNumber ( ) )
break
}
case 'eth_estimateGas' : {
const params = message ? . params ? . [ 0 ] as any
if ( ! params ) {
sendResponse ( {
error : true ,
code : rpcError.INVALID_PARAM ,
message : 'Invalid param for gas estimate'
} )
break
}
sendResponse ( await estimateGas ( {
to : params?.to ? ? '' ,
from : params ? . from ? ? '' ,
data : params?.data ? ? '' ,
value : params?.value ? ? '0x0'
} ) )
break
}
2022-10-16 22:25:20 +00:00
case 'eth_requestAccounts' :
2022-10-07 17:07:59 +00:00
case 'eth_accounts' : {
2022-10-16 22:25:20 +00:00
// give only the selected address for better privacy
2022-10-07 17:07:59 +00:00
const account = await getSelectedAccount ( )
const address = account ? . address ? [ account ? . address ] : [ ]
sendResponse ( address )
break
}
case 'eth_chainId' : {
const network = await getSelectedNetwork ( )
const chainId = network ? . chainId ? ? 0
sendResponse ( ` 0x ${ chainId . toString ( 16 ) } ` )
break
}
case 'eth_sendTransaction' : {
try {
const params = message ? . params ? . [ 0 ] as any
if ( ! params ) {
sendResponse ( {
error : true ,
code : rpcError.INVALID_PARAM ,
message : 'Invalid param for send transaction'
} )
break
}
const [ account , network ] = await Promise . all ( [ getSelectedAccount ( ) , getSelectedNetwork ( ) ] )
2022-10-13 20:48:07 +00:00
if ( ! account || ! ( 'address' in account ) ) {
await chrome . windows . create ( {
height : 450 ,
width : 400 ,
2022-10-15 20:20:33 +00:00
url : chrome.runtime.getURL ( ` index.html?route=wallet-error¶m= ${ encodeURIComponent ( 'No account is selected you need to have an account selected before trying to make a transaction' ) } &rid= ${ String ( message ? . resId ? ? '' ) } ` ) ,
2022-10-13 20:48:07 +00:00
type : 'popup'
} )
return
}
if ( ! network || ! ( 'chainId' in network ) ) {
await chrome . windows . create ( {
height : 450 ,
width : 400 ,
2022-10-15 20:20:33 +00:00
url : chrome.runtime.getURL ( ` index.html?route=wallet-error¶m= ${ encodeURIComponent ( 'No network is selected you need to have a network selected before trying to make a transaction' ) } &rid= ${ String ( message ? . resId ? ? '' ) } ` ) ,
2022-10-13 20:48:07 +00:00
type : 'popup'
} )
2022-10-07 17:07:59 +00:00
return
}
2022-10-10 23:01:14 +00:00
params . from = account . address
2022-10-07 17:07:59 +00:00
const serializeParams = encodeURIComponent ( JSON . stringify ( params ) ) ? ? ''
const pEstimateGas = estimateGas ( {
to : params?.to ? ? '' ,
from : params ? . from ? ? '' ,
data : params?.data ? ? '' ,
value : params?.value ? ? '0x0'
} )
const pGasPrice = getGasPrice ( )
let gWin : any
await new Promise ( ( resolve , reject ) = > {
chrome . windows . create ( {
height : 450 ,
width : 400 ,
url : chrome.runtime.getURL ( ` index.html?route=sign-tx¶m= ${ 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 ) ] = { }
} )
2022-10-10 23:01:14 +00:00
2022-10-07 17:07:59 +00:00
} )
2022-10-10 00:52:23 +00:00
try {
const tx = await sendTransaction ( { . . . params , . . . ( rIdData ? . [ String ( gWin ? . id ? ? 0 ) ] ? ? { } ) } , pEstimateGas , pGasPrice )
2022-10-16 22:25:20 +00:00
sendResponse ( tx . hash )
2022-10-10 00:52:23 +00:00
const buttons = { } as any
const network = await getSelectedNetwork ( )
2022-10-13 20:48:07 +00:00
addToHistory ( {
date : Date.now ( ) ,
txHash : tx.hash ,
chainId : network.chainId ,
. . . ( network . explorer ? { txUrl : ` ${ network . explorer } /tx/ ${ tx . hash } ` . replace ( '//' , '/' ) } : { } ) ,
webiste : ( message ? . website )
} )
2022-10-10 00:52:23 +00:00
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
}
} , 6 e4 )
}
2022-10-10 23:01:14 +00:00
chrome . notifications . create ( notificationId , {
2022-10-10 00:52:23 +00:00
message : 'Transaction Confirmed' ,
title : 'Success' ,
2022-10-10 23:01:14 +00:00
iconUrl : getUrl ( 'assets/extension-icon/wallet_128.png' ) ,
type : 'basic' ,
2022-10-10 00:52:23 +00:00
. . . ( buttons )
} as any )
2022-10-16 22:25:20 +00:00
const settings = await getSettings ( )
if ( settings . encryptAfterEveryTx ) {
clearPk ( )
}
2022-10-10 23:01:14 +00:00
} catch ( err ) {
2022-10-10 00:52:23 +00:00
sendResponse ( {
error : true ,
code : rpcError.USER_REJECTED ,
message : 'TX Failed'
} )
2022-10-15 20:20:33 +00:00
chrome . windows . create ( {
height : 450 ,
width : 400 ,
url : chrome.runtime.getURL ( ` index.html?route=wallet-error¶m= ${ encodeURIComponent ( String ( err ) ) } &rid= ${ String ( message ? . resId ? ? '' ) } ` ) ,
type : 'popup'
} )
2022-10-10 00:52:23 +00:00
chrome . notifications . create ( {
message : 'Transaction Failed' ,
title : 'Error' ,
2022-10-10 23:01:14 +00:00
iconUrl : getUrl ( 'assets/extension-icon/wallet_128.png' ) ,
type : 'basic'
2022-10-10 00:52:23 +00:00
} as any )
}
2022-10-07 17:07:59 +00:00
} catch ( err ) {
2022-10-10 23:01:14 +00:00
// console.log(err)
2022-10-07 17:07:59 +00:00
sendResponse ( {
error : true ,
code : rpcError.USER_REJECTED ,
message : 'User Rejected Signature'
} )
}
break
}
2022-10-16 22:25:20 +00:00
case 'signTypedData' :
case 'eth_signTypedData' :
case 'signTypedData_v1' :
case 'eth_signTypedData_v1' :
case 'signTypedData_v3' :
case 'eth_signTypedData_v3' :
case 'signTypedData_v4' :
case 'eth_signTypedData_v4' :
case 'personal_sign' :
case 'eth_sign' : {
2022-10-07 17:07:59 +00:00
try {
2022-10-13 20:48:07 +00:00
const account = await getSelectedAccount ( )
if ( ! account || ! ( 'address' in account ) ) {
await chrome . windows . create ( {
height : 450 ,
width : 400 ,
2022-10-15 20:20:33 +00:00
url : chrome.runtime.getURL ( ` index.html?route=wallet-error¶m= ${ encodeURIComponent ( 'No account is selected you need to have an account selected before trying sign a message' ) } &rid= ${ String ( message ? . resId ? ? '' ) } ` ) ,
2022-10-13 20:48:07 +00:00
type : 'popup'
} )
return
}
2022-10-16 22:25:20 +00:00
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 ] ? ? '' ) ;
2022-10-07 17:07:59 +00:00
await new Promise ( ( resolve , reject ) = > {
chrome . windows . create ( {
height : 450 ,
width : 400 ,
2022-10-16 22:25:20 +00:00
url : chrome.runtime.getURL ( ` index.html?route=sign-msg¶m= ${ signMsgData } &rid= ${ String ( message ? . resId ? ? '' ) } ` ) ,
2022-10-07 17:07:59 +00:00
type : 'popup'
} ) . then ( ( win ) = > {
userReject [ String ( win . id ) ] = reject
userApprove [ String ( win . id ) ] = resolve
rIdWin [ String ( win . id ) ] = String ( message . resId )
} )
} )
sendResponse (
2022-10-16 22:25:20 +00:00
isTypedSigned ?
await signTypedData ( signMsgData ) :
await signMsg ( signMsgData )
2022-10-07 17:07:59 +00:00
)
2022-10-16 22:25:20 +00:00
const settings = await getSettings ( )
if ( settings . encryptAfterEveryTx ) {
clearPk ( )
}
} catch ( e ) {
console . error ( e )
2022-10-07 17:07:59 +00:00
sendResponse ( {
error : true ,
code : rpcError.USER_REJECTED ,
message : 'User Rejected Signature'
} )
}
break
}
// NON Standard metamask API
case 'wallet_requestPermissions' : {
const account = await getSelectedAccount ( )
const address = account ? . address ? [ account ? . address ] : [ ]
sendResponse ( [ {
caveats : {
type : '' ,
value : address
} ,
invoker : '' ,
date : Date.now ( ) ,
id : smallRandomString ( ) ,
parentCapability : Object.keys ( message ? . params ? . [ 0 ] ? ? { } ) ? . [ 0 ] ? ? 'unknown'
} ] )
break
}
case 'net_version' : {
const network = await getSelectedNetwork ( )
const chainId = network ? . chainId ? ? 0
sendResponse ( chainId )
break
}
case 'wallet_switchEthereumChain' : {
try {
await new Promise ( ( resolve , reject ) = > {
chrome . windows . create ( {
height : 450 ,
width : 400 ,
2022-10-10 00:52:23 +00:00
url : chrome.runtime.getURL ( ` index.html?route=switch-network¶m= ${ String ( message ? . params ? . [ 0 ] ? . chainId ? ? '' ) } &rid= ${ String ( message ? . resId ? ? '' ) } ` ) ,
2022-10-07 17:07:59 +00:00
type : 'popup'
} ) . then ( ( win ) = > {
userReject [ String ( win . id ) ] = reject
userApprove [ String ( win . id ) ] = resolve
rIdWin [ String ( win . id ) ] = String ( message . resId )
} )
} )
2022-10-10 00:52:23 +00:00
sendResponse ( null )
2022-10-07 17:07:59 +00:00
} catch {
sendResponse ( {
error : true ,
code : rpcError.USER_REJECTED ,
message : 'User Rejected Signature'
} )
}
break
}
// internal messeges
case 'wallet_approve' : {
if ( String ( sender . tab ? . windowId ) in rIdWin ) {
userApprove [ String ( sender . tab ? . windowId ) ] ? . ( true )
}
try {
chrome . windows . remove ( sender . tab ? . windowId ? ? 0 )
2022-10-15 20:20:33 +00:00
} catch ( e ) {
console . log ( e )
2022-10-07 17:07:59 +00:00
// ignore
}
break
}
case 'wallet_send_data' : {
if ( String ( sender . tab ? . windowId ) in rIdData ) {
rIdData [ String ( sender ? . tab ? . windowId ? ? '' ) ] = ( message as any ) ? . data ? ? { }
sendResponse ( true )
}
break
}
case 'wallet_get_data' : {
if ( String ( sender . tab ? . windowId ) in rIdData ) {
sendResponse ( rIdData [ String ( sender ? . tab ? . windowId ? ? '' ) ] ? ? { } )
}
break
}
case 'wallet_ping' : {
sendResponse ( true )
break
}
default : {
sendResponse ( {
error : true ,
code : rpcError.INVALID_PARAM ,
message : 'Invalid request method'
} )
break
}
}
}
}
) ( ) ;
return true ;
} ) ;