diff --git a/src/composables/aeSdk.ts b/src/composables/aeSdk.ts index 5c710478c..8d09a353a 100644 --- a/src/composables/aeSdk.ts +++ b/src/composables/aeSdk.ts @@ -12,7 +12,6 @@ import { RPC_STATUS, Encoded, } from '@aeternity/aepp-sdk'; -import { isEmpty } from 'lodash-es'; import type { INetwork, @@ -82,7 +81,6 @@ export function useAeSdk() { onNetworkChange, } = useNetworks(); const { - activeAccount, accountsAddressList, getLastActiveProtocolAccount, onAccountChange, @@ -156,8 +154,6 @@ export function useAeSdk() { const aepp = aeppInfo[aeppId]; const host = IS_OFFSCREEN_TAB ? aepp.origin : origin; if (await checkOrAskPermission(METHODS.subscribeAddress, host)) { - // Waiting for activeAccount to sync back to the background - await watchUntilTruthy(() => !isEmpty(activeAccount.value)); return getLastActiveProtocolAccount(PROTOCOLS.aeternity)!.address; } return Promise.reject(new RpcRejectedByUserError()); @@ -166,8 +162,6 @@ export function useAeSdk() { const aepp = aeppInfo[aeppId]; const host = IS_OFFSCREEN_TAB ? aepp.origin : origin; if (await checkOrAskPermission(METHODS.address, host)) { - // Waiting for activeAccount to sync back to the background - await watchUntilTruthy(() => !isEmpty(activeAccount.value)); return accountsAddressList.value; } return Promise.reject(new RpcRejectedByUserError()); diff --git a/src/composables/permissions.ts b/src/composables/permissions.ts index 6c0be9839..5b3c21f8c 100644 --- a/src/composables/permissions.ts +++ b/src/composables/permissions.ts @@ -1,4 +1,5 @@ import { METHODS } from '@aeternity/aepp-sdk'; +import { isEmpty } from 'lodash-es'; import type { IAppData, @@ -24,10 +25,11 @@ import { STORAGE_KEYS, PROTOCOLS, } from '@/constants'; -import { getCleanModalOptions } from '@/utils'; +import { getCleanModalOptions, watchUntilTruthy } from '@/utils'; import { aettosToAe, isTxOfASupportedType } from '@/protocols/aeternity/helpers'; import { openPopup } from '@/offscreen/popupHandler'; import migratePermissionsVuexToComposable from '@/migrations/003-permissions-vuex-to-composable'; +import { useAccounts } from '@/composables'; import { useStorageRef } from './storageRef'; import { useModals } from './modals'; @@ -164,11 +166,22 @@ export function usePermissions() { ): Promise { let app: IAppData | undefined; let props = getCleanModalOptions(modalProps); + const { activeAccount } = useAccounts(); if (fullUrl) { const url = new URL(fullUrl); if (checkPermission(url.host, method, modalProps.tx)) { - return true; + try { + await Promise.race( + [ + watchUntilTruthy(() => !isEmpty(activeAccount.value)), + new Promise((_r, reject) => setTimeout(reject, 1000)), + ], + ); + return true; + } catch (error) { + // Intentionally ignoring the error + } } app = { diff --git a/src/composables/walletConnect.ts b/src/composables/walletConnect.ts index 9d5d79be5..5d03f6920 100644 --- a/src/composables/walletConnect.ts +++ b/src/composables/walletConnect.ts @@ -165,7 +165,7 @@ export function useWalletConnect({ offscreen } = { offscreen: false }) { // Connected DAPP requested action, e.g.: signing web3wallet?.on('session_request', async ({ topic, params: proposal, id }) => { const { url, name } = wcSession.value?.peer.metadata! || {}; - const result = await handleEthereumRpcMethod( + const { result, error } = await handleEthereumRpcMethod( url, proposal.request.method as EthRpcSupportedMethods, proposal.request.params[0], @@ -177,7 +177,14 @@ export function useWalletConnect({ offscreen } = { offscreen: false }) { response: { id, jsonrpc: '2.0', - ...(result) ? { result } : { error: { code: 5000, message: 'User rejected.' } }, + ...(result + ? { result } + : { + error: { + code: error?.code || 5000, + message: error?.message || 'User rejected.', + }, + }), }, }); }); diff --git a/src/content-scripts/inject.ts b/src/content-scripts/inject.ts index 418de511e..1f865ffcf 100644 --- a/src/content-scripts/inject.ts +++ b/src/content-scripts/inject.ts @@ -35,7 +35,7 @@ const runContentScript = () => { method: BackgroundMethod, params: IEthRpcMethodParameters, ) { - const result = await sendToOffscreen(method, { + const { result, error }: any = await sendToOffscreen(method, { rpcMethodParams: params, aepp: event.origin, }); @@ -45,6 +45,7 @@ const runContentScript = () => { event.source.postMessage({ jsonrpc: '2.0', result, + ...(error ? { error } : {}), method: event.data.method, superheroWalletApproved: true, type: 'result', @@ -64,6 +65,23 @@ const runContentScript = () => { if (method === ETH_RPC_METHODS.getBalance) { handleEthRpcRequest(event, method, { address: event.data.params[0] }); + } else if (method === ETH_RPC_METHODS.signPersonal) { + handleEthRpcRequest(event, method, { data: event.data.params?.[0] }); + } else if ( + method === ETH_RPC_ETHERSCAN_PROXY_METHODS.getTransactionByHash + || method === ETH_RPC_ETHERSCAN_PROXY_METHODS.getTransactionReceipt + ) { + handleEthRpcRequest(event, method, { txhash: event.data.params?.[0] }); + } else if (method === ETH_RPC_ETHERSCAN_PROXY_METHODS.getBlockByNumber) { + handleEthRpcRequest(event, method, { + tag: event.data.params?.[0], + boolean: event.data.params?.[1], + }); + } else if (method === ETH_RPC_ETHERSCAN_PROXY_METHODS.getUncleByBlockNumberAndIndex) { + handleEthRpcRequest(event, method, { + tag: event.data.params?.[0], + index: event.data.params?.[1], + }); } else if ( Object.values(ETH_RPC_METHODS).includes(method) || Object.values(ETH_RPC_ETHERSCAN_PROXY_METHODS).includes(method) diff --git a/src/content-scripts/inpage.ts b/src/content-scripts/inpage.ts index f9d70fb94..c52403d06 100644 --- a/src/content-scripts/inpage.ts +++ b/src/content-scripts/inpage.ts @@ -169,6 +169,51 @@ interface EIP6963ProviderInfo { rdns: string; } +function shouldInjectProvider() { + return doctypeCheck() && suffixCheck() && documentElementCheck(); +} + +/** + * Checks the doctype of the current document if it exists + */ +function doctypeCheck() { + const { doctype } = window.document; + if (doctype) { + return doctype.name === 'html'; + } + return true; +} + +/** + * Returns whether or not the extension (suffix) of the current document is prohibited + * + * This checks {@code window.location.pathname} against a set of file extensions + * that we should not inject the provider into. This check is indifferent of + * query parameters in the location. + */ +function suffixCheck() { + const prohibitedTypes = [/\.xml$/u, /\.pdf$/u]; + const currentUrl = window.location.pathname; + // eslint-disable-next-line no-plusplus + for (let i = 0; i < prohibitedTypes.length; i++) { + if (prohibitedTypes[i].test(currentUrl)) { + return false; + } + } + return true; +} + +/** + * Checks the documentElement of the current document + */ +function documentElementCheck() { + const documentElement = document.documentElement.nodeName; + if (documentElement) { + return documentElement.toLowerCase() === 'html'; + } + return true; +} + type SingleSendAsyncParam = { readonly id: string | number | null; readonly method: string; readonly params: readonly unknown[] } type OnMessage = 'accountsChanged' | 'message' | 'connect' | 'close' | 'disconnect' | 'chainChanged' @@ -208,7 +253,9 @@ class SuperheroWalletMessageListener { private pendingSignerAddressRequest: Future | undefined = undefined; public constructor() { - this.injectEthereumIntoWindow(); + if (shouldInjectProvider()) { + this.injectEthereumIntoWindow(); + } this.onPageLoad(); } @@ -660,58 +707,7 @@ class SuperheroWalletMessageListener { }; public readonly injectEthereumIntoWindow = () => { - if (!('ethereum' in window) || !window.ethereum) { - // no existing signer found - window.ethereum = { - isSuperheroWallet: true, - isConnected: this.WindowEthereumIsConnected.bind(window.ethereum), - request: this.WindowEthereumRequest.bind(window.ethereum), - send: this.WindowEthereumSend.bind(window.ethereum), - sendAsync: this.WindowEthereumSendAsync.bind(window.ethereum), - on: this.WindowEthereumOn.bind(window.ethereum), - removeListener: this.WindowEthereumRemoveListener.bind(window.ethereum), - enable: this.WindowEthereumEnable.bind(window.ethereum), - ...this.unsupportedMethods(window.ethereum), - }; - this.connected = true; - return; - } - if (window.ethereum.isSuperheroWallet) return; - - // subscribe for signers events - window.ethereum.on('accountsChanged', (accounts: readonly string[]) => { - this.WindowEthereumRequest({ method: 'eth_accounts_reply', params: [{ type: 'success', accounts, requestAccounts: false }] }); - }); - window.ethereum.on('disconnect', (_error: ProviderRpcError) => { - this.sendMessageToBackgroundPage({ method: 'connected_to_signer', params: [false, this.currentSigner] }); - }); - window.ethereum.on('chainChanged', (chainId: string) => { - // TODO: this is a hack to get coinbase working that calls this numbers in base 10 instead of in base 16 - // eslint-disable-next-line radix - const params = /\d/.test(chainId) ? [`0x${parseInt(chainId).toString(16)}`] : [chainId]; - this.WindowEthereumRequest({ method: 'signer_chainChanged', params }); - }); - - this.connected = !window.ethereum.isConnected || window.ethereum.isConnected(); - this.signerWindowEthereumRequest = window.ethereum.request.bind(window.ethereum); // store the request object to signer - - if (window.ethereum.isBraveWallet || window.ethereum.providerMap || window.ethereum.isCoinbaseWallet) { - const oldWinEthereum = (window.ethereum.providerMap ? window.ethereum.providerMap.get('CoinbaseWallet') : undefined) ?? window.ethereum; - window.ethereum = { - isSuperheroWallet: true, - isConnected: this.WindowEthereumIsConnected.bind(oldWinEthereum), - request: this.WindowEthereumRequest.bind(oldWinEthereum), - send: this.WindowEthereumSend.bind(oldWinEthereum), - sendAsync: this.WindowEthereumSendAsync.bind(oldWinEthereum), - on: this.WindowEthereumOn.bind(oldWinEthereum), - removeListener: this.WindowEthereumRemoveListener.bind(oldWinEthereum), - enable: this.WindowEthereumEnable.bind(oldWinEthereum), - ...this.unsupportedMethods(oldWinEthereum), - }; - return; - } - // we cannot inject window.ethereum alone here as it seems like window.ethereum is cached (maybe ethers.js does that?) - Object.assign(window.ethereum, { + const superheroWalletProvider = { isSuperheroWallet: true, isConnected: this.WindowEthereumIsConnected.bind(window.ethereum), request: this.WindowEthereumRequest.bind(window.ethereum), @@ -721,7 +717,50 @@ class SuperheroWalletMessageListener { removeListener: this.WindowEthereumRemoveListener.bind(window.ethereum), enable: this.WindowEthereumEnable.bind(window.ethereum), ...this.unsupportedMethods(window.ethereum), + }; + Object.defineProperties(window, { + superheroWallet: { + value: superheroWalletProvider, + configurable: false, + writable: false, + }, + ethereum: { + get() { + // @ts-expect-error + return window.superheroWalletRouter.currentProvider; + }, + set(newProvider) { + // @ts-expect-error + window.superheroWalletRouter?.addProvider(newProvider); + }, + configurable: false, + }, + superheroWalletRouter: { + value: { + superheroWalletProvider, + lastInjectedProvider: window.ethereum, + currentProvider: superheroWalletProvider, + providers: [ + superheroWalletProvider, + ...(window.ethereum ? [window.ethereum] : []), + ], + addProvider(provider: any) { + // @ts-expect-error + if (!window.superheroWalletRouter?.providers?.includes(provider)) { + // @ts-expect-error + window.superheroWalletRouter?.providers?.push(provider); + } + if (superheroWalletProvider !== provider) { + // @ts-expect-error + window.superheroWalletRouter.lastInjectedProvider = provider; + } + }, + }, + configurable: false, + writable: false, + }, }); + this.connected = true; }; } @@ -729,17 +768,6 @@ function injectSuperheroWallet() { const superheroWalletMessageListener = new SuperheroWalletMessageListener(); window.addEventListener('message', superheroWalletMessageListener.onMessage); window.dispatchEvent(new Event('ethereum#initialized')); - - // listen if Metamask injects (I think this method of injection is only supported by Metamask currently) their payload, and if so, reinject - const superheroWalletCapturedDispatcher = window.dispatchEvent; - window.dispatchEvent = (event: Event) => { - superheroWalletCapturedDispatcher(event); - if (!(typeof event === 'object' && event !== null && 'type' in event && typeof event.type === 'string')) return true; - if (event.type !== 'ethereum#initialized') return true; - superheroWalletMessageListener.injectEthereumIntoWindow(); - window.dispatchEvent = superheroWalletCapturedDispatcher; - return true; - }; } injectSuperheroWallet(); diff --git a/src/popup/pages/Popups/MessageSign.vue b/src/popup/pages/Popups/MessageSign.vue index c22c508e6..ef99c0ebf 100644 --- a/src/popup/pages/Popups/MessageSign.vue +++ b/src/popup/pages/Popups/MessageSign.vue @@ -91,7 +91,8 @@ export default defineComponent({ setPopupProps, } = usePopupProps(); - const activeAccount = getLastActiveProtocolAccount(PROTOCOLS.aeternity); + const protocol = popupProps.value?.protocol || PROTOCOLS.aeternity; + const activeAccount = getLastActiveProtocolAccount(protocol); async function approve() { const { openModal } = useModals(); diff --git a/src/popup/router/index.ts b/src/popup/router/index.ts index f9dd9b1e6..5255642bc 100644 --- a/src/popup/router/index.ts +++ b/src/popup/router/index.ts @@ -65,6 +65,12 @@ RouteQueryActionsController.init(router); RouteLastUsedRoutes.init(router); router.beforeEach(async (to, from, next) => { + let popupProps: IPopupProps | null = null; + + if (RUNNING_IN_POPUP && to.name !== ROUTE_NOT_FOUND && !Object.keys(to.params).length) { + popupProps = await getPopupProps(); + } + await checkUserAuth(); await watchUntilTruthy(areAccountsReady); @@ -107,10 +113,7 @@ router.beforeEach(async (to, from, next) => { [POPUP_TYPE_UNSAFE_SIGN]: ROUTE_POPUP_UNSAFE_SIGN, }[POPUP_TYPE]; - let popupProps: IPopupProps | null = null; - if (!Object.keys(to.params).length) { - popupProps = await getPopupProps(); if (!popupProps?.app) { next({ name: ROUTE_NOT_FOUND, params: { hideHomeButton: true as any } }); return; diff --git a/src/protocols/ethereum/config.ts b/src/protocols/ethereum/config.ts index 7f944744e..356b0490e 100644 --- a/src/protocols/ethereum/config.ts +++ b/src/protocols/ethereum/config.ts @@ -114,8 +114,10 @@ export const ETH_RPC_METHODS = { getBlockNumber: 'eth_blockNumber', getChainId: 'eth_chainId', getAccounts: 'eth_accounts', - sendTransaction: 'eth_sendTransaction', requestAccounts: 'eth_requestAccounts', + // signing + sendTransaction: 'eth_sendTransaction', + signPersonal: 'personal_sign', // wallet requestPermissions: 'wallet_requestPermissions', switchNetwork: 'wallet_switchEthereumChain', diff --git a/src/protocols/ethereum/libs/EthereumRpcMethodsHandler.ts b/src/protocols/ethereum/libs/EthereumRpcMethodsHandler.ts index 36fe2dd1c..e0adcaa3b 100644 --- a/src/protocols/ethereum/libs/EthereumRpcMethodsHandler.ts +++ b/src/protocols/ethereum/libs/EthereumRpcMethodsHandler.ts @@ -1,15 +1,22 @@ import { ref } from 'vue'; import { METHODS, Tag } from '@aeternity/aepp-sdk'; -import { fromWei, toChecksumAddress, toWei } from 'web3-utils'; +import { + fromWei, + toChecksumAddress, + toWei, + hexToString, +} from 'web3-utils'; +import { sign } from 'web3-eth-accounts'; import Web3Eth, { getBlock } from 'web3-eth'; import { DEFAULT_RETURN_FORMAT } from 'web3-types'; +import { isEmpty } from 'lodash-es'; import type { IModalProps } from '@/types'; import type { IEthRpcMethodParameters, EthRpcSupportedMethods } from '@/protocols/ethereum/types'; -import { sleep, watchUntilTruthy } from '@/utils'; +import { watchUntilTruthy } from '@/utils'; import { ProtocolAdapterFactory } from '@/lib/ProtocolAdapterFactory'; -import { EtherscanService } from '@/protocols/ethereum/libs/EtherscanService'; +import { EtherscanService, EtherscanDefaultResponse } from '@/protocols/ethereum/libs/EtherscanService'; import { useEthNetworkSettings } from '@/protocols/ethereum/composables/ethNetworkSettings'; import { useEthFeeCalculation } from '@/protocols/ethereum/composables/ethFeeCalculation'; import { @@ -21,7 +28,6 @@ import { import { CONNECT_PERMISSIONS, - PERMISSION_DEFAULTS, PROTOCOLS, } from '@/constants'; import { @@ -30,15 +36,23 @@ import { usePermissions, } from '@/composables'; +function getUnknownError(message: string) { + // ERROR_BLANKET_ERROR + return { error: { code: -32603, message } }; +} + +const ERROR_USER_REJECTED_REQUEST = { + error: { + code: 4001, + message: 'User have rejected the request', + }, +}; + const isCheckingPermissions = ref(false); async function checkOrAskEthPermission(aepp: string) { - const { - addPermission, - checkOrAskPermission, - permissions, - } = usePermissions(); - + const { checkOrAskPermission } = usePermissions(); + const { activeAccount } = useAccounts(); await watchUntilTruthy(() => !isCheckingPermissions.value); isCheckingPermissions.value = true; @@ -54,20 +68,7 @@ async function checkOrAskEthPermission(aepp: string) { ], }, ); - const { hostname: host } = new URL(aepp); - if (permission && !permissions.value[host]?.address) { - // awaiting for default permissions to be synced - // with background after being set in `checkOrAskPermission` - await sleep(50); - addPermission({ - ...PERMISSION_DEFAULTS, - ...(permissions.value[host] || {}), - address: true, - addressList: true, - host, - name: host, - }); - } + await watchUntilTruthy(() => !isEmpty(activeAccount.value)); isCheckingPermissions.value = false; return permission; @@ -78,35 +79,58 @@ export async function handleEthereumRpcMethod( method: EthRpcSupportedMethods, params: IEthRpcMethodParameters, name?: string, -) { - const { checkOrAskPermission, removePermission } = usePermissions(); +): Promise<{ result?: any; error?: { code: number; message: string } }> { + const { checkPermission, checkOrAskPermission, removePermission } = usePermissions(); const { getLastActiveProtocolAccount } = useAccounts(); const { activeNetwork, networks, switchNetwork } = useNetworks(); const { ethActiveNetworkSettings, ethActiveNetworkPredefinedSettings } = useEthNetworkSettings(); if (method === ETH_RPC_METHODS.requestPermissions) { - return (await checkOrAskEthPermission(aepp)) ? { eth_accounts: true } : {}; + return (await checkOrAskEthPermission(aepp)) + ? { result: { eth_accounts: true } } + : ERROR_USER_REJECTED_REQUEST; } - if (method === ETH_RPC_METHODS.requestAccounts || method === ETH_RPC_METHODS.getAccounts) { - return (await checkOrAskEthPermission(aepp)) - ? [getLastActiveProtocolAccount(PROTOCOLS.ethereum)!.address] - : []; + if (method === ETH_RPC_METHODS.getAccounts) { + const { host } = new URL(aepp); + + if (checkPermission(host, METHODS.connect)) { + const ethereumAccount = getLastActiveProtocolAccount(PROTOCOLS.ethereum); + + return { + result: ethereumAccount?.address + ? [ethereumAccount?.address] + : [], + }; + } + return { result: [] }; + } + + if (method === ETH_RPC_METHODS.requestAccounts) { + if (await checkOrAskEthPermission(aepp)) { + return { result: [getLastActiveProtocolAccount(PROTOCOLS.ethereum)!.address] }; + } + return ERROR_USER_REJECTED_REQUEST; } if (method === ETH_RPC_METHODS.revokePermissions) { const { host } = new URL(aepp); removePermission(host); - return null; + return { result: null }; } if (method === ETH_RPC_METHODS.getBalance) { + let balance: string; const adapter = ProtocolAdapterFactory.getAdapter(PROTOCOLS.ethereum); - const balance = await adapter.fetchBalance(toChecksumAddress(params?.address!)); - return balance ? toWei(balance, 'ether') : 0; + try { + balance = await adapter.fetchBalance(toChecksumAddress(params?.address!)); + } catch (error: any) { + return getUnknownError(error.message); + } + return { result: balance ? toWei(balance, 'ether') : 0 }; } if (method === ETH_RPC_METHODS.getChainId) { - return `0x${BigInt(networks.value[activeNetwork.value.name].protocols[PROTOCOLS.ethereum].chainId).toString(16)}`; + return { result: `0x${BigInt(networks.value[activeNetwork.value.name].protocols[PROTOCOLS.ethereum].chainId).toString(16)}` }; } if (method === ETH_RPC_METHODS.switchNetwork) { const network = Object.values(networks.value) @@ -115,13 +139,25 @@ export async function handleEthereumRpcMethod( )); if (network) { switchNetwork(network.name); + return { result: null }; } - return null; + return { + error: { + code: 4902, + message: `Chain ${params?.chainId} is currently not supported`, + }, + }; } if (method === ETH_RPC_METHODS.getBlockNumber) { + let currentBlock; const { nodeUrl } = ethActiveNetworkSettings.value; - const currentBlock = await getBlock(new Web3Eth(nodeUrl), 'latest', true, DEFAULT_RETURN_FORMAT); - return currentBlock?.number; + + try { + currentBlock = await getBlock(new Web3Eth(nodeUrl), 'latest', true, DEFAULT_RETURN_FORMAT); + } catch (error: any) { + return getUnknownError(error.message); + } + return { result: currentBlock?.number }; } if (method === ETH_RPC_METHODS.sendTransaction) { const { updateFeeList, maxFeePerGas } = useEthFeeCalculation(); @@ -172,30 +208,73 @@ export async function handleEthereumRpcMethod( ); if (permitted) { if (adapter?.transferPreparedTransaction) { - const actionResult = await adapter.transferPreparedTransaction({ - ...params, - ...(params?.gas ? {} : { gas: estimatedGas }), - }); - return actionResult?.hash ?? false; + try { + const actionResult = await adapter.transferPreparedTransaction({ + ...params, + ...(params?.gas ? {} : { gas: estimatedGas }), + }); + return actionResult?.hash + ? { result: actionResult?.hash } + : getUnknownError('Failed to sign and send transaction'); + } catch (error: any) { + return getUnknownError(error.message); + } } } - return false; + return ERROR_USER_REJECTED_REQUEST; + } + if (method === ETH_RPC_METHODS.signPersonal) { + const ethereumAccount = getLastActiveProtocolAccount(PROTOCOLS.ethereum); + + let rawMessage: string; + + try { + rawMessage = hexToString(params.data!); + } catch (e: any) { + return { error: { code: -32602, message: e.message } }; + } + + const permitted = await checkOrAskPermission( + METHODS.signMessage, + aepp, + { + protocol: PROTOCOLS.ethereum, + message: rawMessage, + }, + ); + if (permitted && ethereumAccount?.secretKey) { + try { + const signedMessage = await sign(rawMessage, `0x${Buffer.from(ethereumAccount.secretKey).toString('hex')}`); + return { result: signedMessage.signature }; + } catch (e: any) { + return getUnknownError(e.message); + } + } + return ERROR_USER_REJECTED_REQUEST; } if ( method !== ETH_RPC_WALLET_EVENTS.chainChanged && Object.values(ETH_RPC_ETHERSCAN_PROXY_METHODS).includes(method) ) { const apiUrl = ethActiveNetworkPredefinedSettings.value.middlewareUrl; - const result = await new EtherscanService(apiUrl) - .fetchFromApi({ - module: 'proxy', - action: method, - ...params, - }); - return result?.result; + let response: EtherscanDefaultResponse | null; + try { + response = await new EtherscanService(apiUrl) + .fetchFromApi({ + module: 'proxy', + action: method, + ...params, + }); + if (!response?.message?.startsWith('OK')) { + getUnknownError(response?.result || 'Unknown error'); + } + return { result: response?.result }; + } catch (error: any) { + return getUnknownError(error.message); + } } // eslint-disable-next-line no-console console.warn(`Method ${method} is not supported.`); - return null; + return { error: { code: -32004, message: 'Method is not supported' } }; } diff --git a/src/protocols/ethereum/libs/EtherscanService.ts b/src/protocols/ethereum/libs/EtherscanService.ts index 39d4da039..269595ef3 100644 --- a/src/protocols/ethereum/libs/EtherscanService.ts +++ b/src/protocols/ethereum/libs/EtherscanService.ts @@ -11,7 +11,7 @@ import { toEthChecksumAddress } from '../helpers'; const NO_TRANSACTIONS_FOUND_MESSAGE = 'No transactions found'; -interface EtherscanDefaultResponse { +export interface EtherscanDefaultResponse { status: '1' | '0'; message: string; // 'OK' / 'OK-' result: any; diff --git a/src/protocols/ethereum/types.ts b/src/protocols/ethereum/types.ts index 984a0c287..7068d682f 100644 --- a/src/protocols/ethereum/types.ts +++ b/src/protocols/ethereum/types.ts @@ -35,6 +35,8 @@ export interface IEthRpcMethodParameters { value?: string; chainId?: string; result?: string; + /** This exist for proxy Etherscan methods */ + [key: string]: string | undefined; } export type EthRpcMethod = ObjectValues;