diff --git a/.github/workflows/docker_build_push.yml b/.github/workflows/docker_build_push.yml index c7da381..3c7a0c9 100644 --- a/.github/workflows/docker_build_push.yml +++ b/.github/workflows/docker_build_push.yml @@ -58,7 +58,8 @@ jobs: VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS=${{ vars.SUBNET_REGISTRATOR_CONTRACT_ADDRESS }} VITE_ERC20_MESSAGING_CONTRACT_ADDRESS=${{ vars.ERC20_MESSAGING_CONTRACT_ADDRESS }} VITE_TOPOS_CORE_PROXY_CONTRACT_ADDRESS=${{ vars.TOPOS_CORE_PROXY_CONTRACT_ADDRESS }} - VITE_TOPOS_SUBNET_ENDPOINT=${{ vars.TOPOS_SUBNET_ENDPOINT }} + VITE_TOPOS_SUBNET_ENDPOINT_WS=${{ vars.TOPOS_SUBNET_ENDPOINT_WS }} + VITE_TOPOS_SUBNET_ENDPOINT_HTTP=${{ vars.TOPOS_SUBNET_ENDPOINT_HTTP }} VITE_ELASTIC_APM_ENDPOINT=${{ secrets.ELASTIC_APM_ENDPOINT }} VITE_TRACING_SERVICE_NAME=${{ vars.TRACING_SERVICE_NAME }} VITE_TRACING_SERVICE_VERSION=${{ vars.TRACING_SERVICE_VERSION }} diff --git a/Dockerfile b/Dockerfile index 880b75d..3bbe53d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,8 @@ ARG VITE_EXECUTOR_SERVICE_ENDPOINT ARG VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS ARG VITE_ERC20_MESSAGING_CONTRACT_ADDRESS ARG VITE_TOPOS_CORE_PROXY_CONTRACT_ADDRESS -ARG VITE_TOPOS_SUBNET_ENDPOINT +ARG VITE_TOPOS_SUBNET_ENDPOINT_WS +ARG VITE_TOPOS_SUBNET_ENDPOINT_HTTP ARG VITE_ELASTIC_APM_ENDPOINT ARG VITE_TRACING_SERVICE_NAME ARG VITE_TRACING_SERVICE_VERSION diff --git a/Dockerfile.ci b/Dockerfile.ci index a3821db..270b549 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -4,7 +4,8 @@ ARG VITE_EXECUTOR_SERVICE_ENDPOINT ARG VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS ARG VITE_ERC20_MESSAGING_CONTRACT_ADDRESS ARG VITE_TOPOS_CORE_PROXY_CONTRACT_ADDRESS -ARG VITE_TOPOS_SUBNET_ENDPOINT +ARG VITE_TOPOS_SUBNET_ENDPOINT_WS +ARG VITE_TOPOS_SUBNET_ENDPOINT_HTTP ARG VITE_ELASTIC_APM_ENDPOINT ARG VITE_TRACING_SERVICE_NAME ARG VITE_TRACING_SERVICE_VERSION diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index d293a81..48e1ad8 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -10,7 +10,8 @@ services: - VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS=${VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS} - VITE_ERC20_MESSAGING_CONTRACT_ADDRESS=${VITE_ERC20_MESSAGING_CONTRACT_ADDRESS} - VITE_TOPOS_CORE_PROXY_CONTRACT_ADDRESS=${VITE_TOPOS_CORE_PROXY_CONTRACT_ADDRESS} - - VITE_TOPOS_SUBNET_ENDPOINT=${VITE_TOPOS_SUBNET_ENDPOINT} + - VITE_TOPOS_SUBNET_ENDPOINT_WS=${VITE_TOPOS_SUBNET_ENDPOINT_WS} + - VITE_TOPOS_SUBNET_ENDPOINT_HTTP=${VITE_TOPOS_SUBNET_ENDPOINT_HTTP} - VITE_ELASTIC_APM_ENDPOINT=${VITE_ELASTIC_APM_ENDPOINT} - VITE_TRACING_SERVICE_NAME=${VITE_TRACING_SERVICE_NAME} - VITE_TRACING_SERVICE_VERSION=${VITE_TRACING_SERVICE_VERSION} diff --git a/package-lock.json b/package-lock.json index 052de4b..601fb0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5741,9 +5741,9 @@ } }, "node_modules/@topos-protocol/topos-smart-contracts": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@topos-protocol/topos-smart-contracts/-/topos-smart-contracts-1.2.2.tgz", - "integrity": "sha512-x0rkGALx493ty8E2AKeaVhaUvC7SzenMwjAf2UsMwF9V9n9lfmAQv2T2DquGa/GG+Hdyl3BHO3U4f8V+Q6ytwA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@topos-protocol/topos-smart-contracts/-/topos-smart-contracts-2.0.0.tgz", + "integrity": "sha512-9ElAqlPg7puWCH0F58l7TgnXheybzkmyNr2ssla/olwKRxcfKJl10k3U5CN03KaTkbkZY+Jhp5JW7BLL+8xhNA==", "dependencies": { "@openzeppelin/contracts": "^4.8.3", "ethers": "^5.7.2", @@ -21057,7 +21057,7 @@ "@ethereumjs/trie": "^5.0.4", "@ethereumjs/util": "^8.0.5", "@ethersproject/abstract-provider": "^5.7.0", - "@topos-protocol/topos-smart-contracts": "^1.2.2", + "@topos-protocol/topos-smart-contracts": "^2.0.0", "@types/event-source-polyfill": "^1.0.1", "antd": "^5.4.0", "axios": "^1.0.0", @@ -25167,9 +25167,9 @@ "dev": true }, "@topos-protocol/topos-smart-contracts": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@topos-protocol/topos-smart-contracts/-/topos-smart-contracts-1.2.2.tgz", - "integrity": "sha512-x0rkGALx493ty8E2AKeaVhaUvC7SzenMwjAf2UsMwF9V9n9lfmAQv2T2DquGa/GG+Hdyl3BHO3U4f8V+Q6ytwA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@topos-protocol/topos-smart-contracts/-/topos-smart-contracts-2.0.0.tgz", + "integrity": "sha512-9ElAqlPg7puWCH0F58l7TgnXheybzkmyNr2ssla/olwKRxcfKJl10k3U5CN03KaTkbkZY+Jhp5JW7BLL+8xhNA==", "requires": { "@openzeppelin/contracts": "^4.8.3", "ethers": "^5.7.2", @@ -30116,7 +30116,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "14.0.0", "@testing-library/user-event": "^14.4.3", - "@topos-protocol/topos-smart-contracts": "^1.2.2", + "@topos-protocol/topos-smart-contracts": "^2.0.0", "@types/event-source-polyfill": "^1.0.1", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", diff --git a/packages/frontend/.env.example b/packages/frontend/.env.example index 536edbf..30677b1 100644 --- a/packages/frontend/.env.example +++ b/packages/frontend/.env.example @@ -2,7 +2,8 @@ VITE_ERC20_MESSAGING_CONTRACT_ADDRESS= VITE_EXECUTOR_SERVICE_ENDPOINT= VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS= VITE_TOPOS_CORE_PROXY_CONTRACT_ADDRESS= -VITE_TOPOS_SUBNET_ENDPOINT= +VITE_TOPOS_SUBNET_ENDPOINT_HTTP= +VITE_TOPOS_SUBNET_ENDPOINT_WS= VITE_ELASTIC_APM_ENDPOINT= VITE_TRACING_SERVICE_NAME= VITE_TRACING_SERVICE_VERSION= diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 54471fe..0cbcedf 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -23,7 +23,7 @@ "@ethereumjs/trie": "^5.0.4", "@ethereumjs/util": "^8.0.5", "@ethersproject/abstract-provider": "^5.7.0", - "@topos-protocol/topos-smart-contracts": "^1.2.2", + "@topos-protocol/topos-smart-contracts": "^2.0.0", "@types/event-source-polyfill": "^1.0.1", "antd": "^5.4.0", "axios": "^1.0.0", diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 11a4098..455faf7 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -12,7 +12,6 @@ import useRegisteredSubnets from './hooks/useRegisteredSubnets' import useTheme from './hooks/useTheme' import { toposCoreContract } from './contracts' import { SubnetWithId } from './types' -import { sanitizeURLProtocol } from './utils' const { Content: _Content } = _Layout @@ -43,12 +42,15 @@ const App = () => { function onRegisteredSubnetsChange() { async function _() { if (registeredSubnets) { - const toposSubnetEndpoint = import.meta.env.VITE_TOPOS_SUBNET_ENDPOINT + const toposSubnetEndpointHttp = import.meta.env + .VITE_TOPOS_SUBNET_ENDPOINT_HTTP + const toposSubnetEndpointWs = import.meta.env + .VITE_TOPOS_SUBNET_ENDPOINT_WS let toposSubnet: SubnetWithId | undefined - if (toposSubnetEndpoint) { + if (toposSubnetEndpointHttp && toposSubnetEndpointWs) { const provider = new ethers.providers.JsonRpcProvider( - sanitizeURLProtocol('http', toposSubnetEndpoint) + toposSubnetEndpointHttp ) const network = await provider.getNetwork() const chainId = network.chainId @@ -58,7 +60,8 @@ const App = () => { toposSubnet = { chainId: BigNumber.from(chainId.toString()), - endpoint: toposSubnetEndpoint, + endpointHttp: toposSubnetEndpointHttp, + endpointWs: toposSubnetEndpointWs, currencySymbol: 'TOPOS', id: subnetId, logoURL: '/logo.svg', diff --git a/packages/frontend/src/components/SubnetSelect.test.tsx b/packages/frontend/src/components/SubnetSelect.test.tsx index defdf12..d914907 100644 --- a/packages/frontend/src/components/SubnetSelect.test.tsx +++ b/packages/frontend/src/components/SubnetSelect.test.tsx @@ -10,7 +10,8 @@ const subnetsMock: SubnetWithId[] = [ { chainId: BigNumber.from(1), currencySymbol: 'TST', - endpoint: '', + endpointHttp: '', + endpointWs: '', id: '', logoURL: '', name: 'subnetMock', diff --git a/packages/frontend/src/hooks/useCheckTokenOnReceivingSubnet.test.ts b/packages/frontend/src/hooks/useCheckTokenOnReceivingSubnet.test.ts index 98be112..cf5b378 100644 --- a/packages/frontend/src/hooks/useCheckTokenOnReceivingSubnet.test.ts +++ b/packages/frontend/src/hooks/useCheckTokenOnReceivingSubnet.test.ts @@ -18,7 +18,8 @@ const subnetsMock: SubnetWithId[] = [ chainId: BigNumber.from(1), currencySymbol: '', id: 'id', - endpoint: 'endpoint', + endpointHttp: 'http://endpoint', + endpointWs: 'ws://endpoint', logoURL: '', name: '', }, @@ -59,9 +60,7 @@ describe('useCheckTokenOnReceivingSubnet', () => { .checkTokenOnSubnet(tokenMock, subnetsMock[0].id) .then(() => { expect(result.current.loading).toBe(true) - expect(providerSpy).toHaveBeenCalledWith( - `ws://${subnetsMock[0].endpoint}/ws` - ) + expect(providerSpy).toHaveBeenCalledWith(subnetsMock[0].endpointWs) expect(contractSpy).toHaveBeenCalled() expect(getCodeMock).toHaveBeenCalledWith(contractMock.address) diff --git a/packages/frontend/src/hooks/useCheckTokenOnReceivingSubnet.ts b/packages/frontend/src/hooks/useCheckTokenOnReceivingSubnet.ts index 9591f50..256c398 100644 --- a/packages/frontend/src/hooks/useCheckTokenOnReceivingSubnet.ts +++ b/packages/frontend/src/hooks/useCheckTokenOnReceivingSubnet.ts @@ -1,11 +1,10 @@ -import { ethers } from 'ethers' +import { providers } from 'ethers' import { useCallback, useContext, useState } from 'react' import { ErrorsContext } from '../contexts/errors' import { SubnetsContext } from '../contexts/subnets' import { erc20MessagingContract } from '../contracts' import { Token } from '../types' -import { sanitizeURLProtocol } from '../utils' export default function useCheckTokenOnSubnet() { const { setErrors } = useContext(ErrorsContext) @@ -19,33 +18,35 @@ export default function useCheckTokenOnSubnet() { const subnet = subnets?.find((s) => s.id === subnetId) - const subnetProvider = subnet?.endpoint - ? new ethers.providers.WebSocketProvider( - sanitizeURLProtocol('ws', `${subnet?.endpoint}/ws`) - ) - : null - - if (subnet && subnetProvider && token) { - if ( - (await subnetProvider.getCode(erc20MessagingContract.address)) === - '0x' - ) { - setLoading(false) - return Promise.reject( - `ToposCore contract could not be found on ${subnet.name}!` - ) - } else { - const contract = erc20MessagingContract.connect(subnetProvider) - - const onChainToken = await contract - .getTokenByAddress(token.addr) - .finally(() => { - setLoading(false) - }) - - if (!onChainToken.symbol) { + if (subnet && token) { + const endpoint = subnet?.endpointWs || subnet?.endpointHttp + const url = new URL(endpoint) + const subnetProvider = url.protocol.startsWith('ws') + ? new providers.WebSocketProvider(endpoint) + : new providers.JsonRpcProvider(endpoint) + + if (subnetProvider) { + if ( + (await subnetProvider.getCode(erc20MessagingContract.address)) === + '0x' + ) { setLoading(false) - reject(`${token.symbol} is not registered on ${subnet.name}!`) + return Promise.reject( + `ToposCore contract could not be found on ${subnet.name}!` + ) + } else { + const contract = erc20MessagingContract.connect(subnetProvider) + + const onChainToken = await contract + .getTokenByAddress(token.addr) + .finally(() => { + setLoading(false) + }) + + if (!onChainToken.symbol) { + setLoading(false) + reject(`${token.symbol} is not registered on ${subnet.name}!`) + } } } } diff --git a/packages/frontend/src/hooks/useEthers.test.ts b/packages/frontend/src/hooks/useEthers.test.ts index e56b2fc..a459d7a 100644 --- a/packages/frontend/src/hooks/useEthers.test.ts +++ b/packages/frontend/src/hooks/useEthers.test.ts @@ -7,15 +7,15 @@ import useEthers from './useEthers' import React from 'react' const existingChainIds = ['1', '2'].map((x) => BigNumber.from(x)) -const subnetMock = { chainId: existingChainIds[0], endpoint: 'endpoint' } +const subnetMock = { chainId: existingChainIds[0], endpointWs: 'ws://endpoint' } const subnetOtherMock = { chainId: existingChainIds[1], - endpoint: 'other-endpoint', + endpointWs: 'ws://other-endpoint', } const newSubnetMock = { chainId: BigNumber.from('3'), currencySymbol: '', - endpoint: 'other-other-endpoint', + endpointWs: 'ws://other-other-endpoint', name: '', } @@ -53,7 +53,7 @@ describe('useEthers', () => { it('should use webSocket provider with Topos Subnet if not requested to use metaMask and no subnet', () => { renderHook(() => useEthers({ viaMetaMask: false })) expect(webSocketProviderSpy).toHaveBeenCalledWith( - `ws://${import.meta.env.VITE_TOPOS_SUBNET_ENDPOINT}/ws` + import.meta.env.VITE_TOPOS_SUBNET_ENDPOINT_WS ) }) @@ -61,9 +61,7 @@ describe('useEthers', () => { renderHook(() => useEthers({ subnet: subnetMock as any, viaMetaMask: false }) ) - expect(webSocketProviderSpy).toHaveBeenCalledWith( - `ws://${subnetMock.endpoint}/ws` - ) + expect(webSocketProviderSpy).toHaveBeenCalledWith(subnetMock.endpointWs) }) it('should use web3 provider if not requested to use metaMask', () => { diff --git a/packages/frontend/src/hooks/useEthers.ts b/packages/frontend/src/hooks/useEthers.ts index 7984132..63e74f1 100644 --- a/packages/frontend/src/hooks/useEthers.ts +++ b/packages/frontend/src/hooks/useEthers.ts @@ -1,9 +1,8 @@ -import { ethers } from 'ethers' +import { providers, utils } from 'ethers' import { useEffect, useMemo } from 'react' import { useMetaMask } from 'metamask-react' import { Subnet } from '../types' -import { sanitizeURLProtocol } from '../utils' interface Args { subnet?: Subnet @@ -15,29 +14,31 @@ export default function useEthers({ subnet, viaMetaMask }: Args = {}) { useMetaMask() const provider = useMemo< - ethers.providers.Web3Provider | ethers.providers.JsonRpcProvider - >( - () => - viaMetaMask && ethereum - ? new ethers.providers.Web3Provider(ethereum) - : new ethers.providers.WebSocketProvider( - sanitizeURLProtocol( - 'ws', - `${ - subnet?.endpoint || import.meta.env.VITE_TOPOS_SUBNET_ENDPOINT - }/ws` - ) - ), - [subnet, viaMetaMask, ethereum] - ) + providers.Web3Provider | providers.JsonRpcProvider + >(() => { + if (viaMetaMask && ethereum) { + return new providers.Web3Provider(ethereum) + } + + if (!subnet) { + const toposSubnetEndpointWs = import.meta.env + .VITE_TOPOS_SUBNET_ENDPOINT_WS + return new providers.WebSocketProvider(toposSubnetEndpointWs) + } + + const endpoint = subnet.endpointWs || subnet.endpointHttp + const url = new URL(endpoint) + + return url.protocol.startsWith('ws') + ? new providers.WebSocketProvider(endpoint) + : new providers.JsonRpcProvider(endpoint) + }, [subnet, viaMetaMask, ethereum]) useEffect( function switchNetworkAndConnect() { const _ = async () => { if (subnet && viaMetaMask && ethereum) { - const chainId = ethers.utils.hexStripZeros( - subnet.chainId.toHexString() - ) + const chainId = utils.hexStripZeros(subnet.chainId.toHexString()) if (ethereum.networkVersion !== chainId) { try { @@ -53,7 +54,7 @@ export default function useEthers({ subnet, viaMetaMask }: Args = {}) { symbol: subnet.currencySymbol, decimals: 18, }, - rpcUrls: [sanitizeURLProtocol('http', subnet.endpoint)], + rpcUrls: [subnet.endpointHttp, subnet.endpointWs], }) } } diff --git a/packages/frontend/src/hooks/useRegisteredSubnets.test.ts b/packages/frontend/src/hooks/useRegisteredSubnets.test.ts index 1a1ad2a..c51bf32 100644 --- a/packages/frontend/src/hooks/useRegisteredSubnets.test.ts +++ b/packages/frontend/src/hooks/useRegisteredSubnets.test.ts @@ -10,21 +10,24 @@ const registeredSubnets: { [x: string]: Subnet } = { subnet1: { chainId: BigNumber.from(1), currencySymbol: 'TST', - endpoint: '', + endpointHttp: '', + endpointWs: '', logoURL: '', name: 'subnetMock', }, subnet2: { chainId: BigNumber.from(2), currencySymbol: 'TST2', - endpoint: '', + endpointHttp: '', + endpointWs: '', logoURL: '', name: 'subnetMock', }, incal: { chainId: BigNumber.from(2), currencySymbol: 'TST2', - endpoint: '', + endpointHttp: '', + endpointWs: '', logoURL: '', name: 'Incal', }, diff --git a/packages/frontend/src/hooks/useRegisteredTokens.test.ts b/packages/frontend/src/hooks/useRegisteredTokens.test.ts index 93b8e82..9c6fec3 100644 --- a/packages/frontend/src/hooks/useRegisteredTokens.test.ts +++ b/packages/frontend/src/hooks/useRegisteredTokens.test.ts @@ -9,7 +9,8 @@ import { BigNumber } from 'ethers' const subnetMock = { chainId: BigNumber.from(1), currencySymbol: 'TST', - endpoint: '', + endpointHttp: '', + endpointWs: '', logoURL: '', name: 'subnetMock', } diff --git a/packages/frontend/src/hooks/useTokenBalance.test.ts b/packages/frontend/src/hooks/useTokenBalance.test.ts index 89e894c..53e9c61 100644 --- a/packages/frontend/src/hooks/useTokenBalance.test.ts +++ b/packages/frontend/src/hooks/useTokenBalance.test.ts @@ -8,7 +8,8 @@ import useTokenBalance from './useTokenBalance' const subnetMock: Subnet = { chainId: BigNumber.from(1), currencySymbol: 'TST', - endpoint: '', + endpointHttp: '', + endpointWs: '', logoURL: '', name: 'subnetMock', } diff --git a/packages/frontend/src/types.ts b/packages/frontend/src/types.ts index 80b1a44..e0451db 100644 --- a/packages/frontend/src/types.ts +++ b/packages/frontend/src/types.ts @@ -3,7 +3,8 @@ import { BigNumber } from 'ethers' export interface Subnet { chainId: BigNumber currencySymbol: string - endpoint: string + endpointHttp: string + endpointWs: string logoURL: string name: string } diff --git a/packages/frontend/src/utils/index.ts b/packages/frontend/src/utils/index.ts index 0d2c8c8..095ce31 100644 --- a/packages/frontend/src/utils/index.ts +++ b/packages/frontend/src/utils/index.ts @@ -6,9 +6,3 @@ export function shortenAddress( address.length - prefixSuffixLength )}` } - -export function sanitizeURLProtocol(protocol: 'ws' | 'http', endpoint: string) { - return location.protocol.startsWith('https') - ? `${protocol}s://${endpoint}` - : `${protocol}://${endpoint}` -}