From 91170ad9235f2ce7c1d62fe3c41fdbc6ac594646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Dan?= Date: Tue, 9 Jan 2024 11:19:05 +0100 Subject: [PATCH] feat: migrate ethers from v5 to v6 (#15) --- package-lock.json | 163 +++++++++++++++++- packages/frontend/package.json | 4 +- packages/frontend/src/App.tsx | 17 +- .../src/components/SubnetSelect.test.tsx | 3 +- packages/frontend/src/contracts.ts | 17 -- .../src/hooks/useRegisteredSubnets.test.ts | 15 +- .../src/hooks/useRegisteredSubnets.ts | 30 ++-- packages/frontend/src/index.tsx | 8 + packages/frontend/src/types.ts | 4 +- 9 files changed, 200 insertions(+), 61 deletions(-) delete mode 100644 packages/frontend/src/contracts.ts diff --git a/package-lock.json b/package-lock.json index eca7dd2..7c26519 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,11 @@ "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", "dev": true }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -3369,6 +3374,28 @@ "reflect-metadata": "^0.1.13" } }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4433,15 +4460,77 @@ } }, "node_modules/@topos-protocol/topos-smart-contracts": { - "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==", + "version": "3.1.0-rc2", + "resolved": "https://registry.npmjs.org/@topos-protocol/topos-smart-contracts/-/topos-smart-contracts-3.1.0-rc2.tgz", + "integrity": "sha512-3839jo+jVMkwxgnlHFSduI78B5YzVDN+CCCd09lcxCrISsrRQJdmbsAlLYTWAc8TPwY/76HuKAP6KIGGw3g8bg==", "dependencies": { "@openzeppelin/contracts": "^4.8.3", - "ethers": "^5.7.2", + "ethers": "^6.9.0", "solidity-rlp": "2.0.7" } }, + "node_modules/@topos-protocol/topos-smart-contracts/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" + }, + "node_modules/@topos-protocol/topos-smart-contracts/node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" + }, + "node_modules/@topos-protocol/topos-smart-contracts/node_modules/ethers": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.9.1.tgz", + "integrity": "sha512-kuV8fGd4/8Gj7wkurbsuUsm1DCG6N5gKGYdw3fnWG/7QGknhy1xtHD7kbkCWQAcbAYmzLCLqCPedS3FYncFkKQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@topos-protocol/topos-smart-contracts/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@topos-protocol/topos-smart-contracts/node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -15792,10 +15881,10 @@ "@opentelemetry/exporter-trace-otlp-proto": "^0.45.0", "@opentelemetry/sdk-metrics": "^1.17.1", "@opentelemetry/sdk-trace-web": "^1.17.1", - "@topos-protocol/topos-smart-contracts": "^2.0.0", + "@topos-protocol/topos-smart-contracts": "^3.1.0-rc1", "antd": "^5.4.0", "axios": "^1.4.0", - "ethers": "^5.7.2", + "ethers": "^6.9.0", "react": "18.2.0", "react-dom": "18.2.0", "react-google-recaptcha": "^3.1.0", @@ -16081,6 +16170,16 @@ "pretty-format": "^29.0.0" } }, + "packages/frontend/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" + }, + "packages/frontend/node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" + }, "packages/frontend/node_modules/ansi-styles": { "version": "5.2.0", "dev": true, @@ -16109,6 +16208,33 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "packages/frontend/node_modules/ethers": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.9.1.tgz", + "integrity": "sha512-kuV8fGd4/8Gj7wkurbsuUsm1DCG6N5gKGYdw3fnWG/7QGknhy1xtHD7kbkCWQAcbAYmzLCLqCPedS3FYncFkKQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "packages/frontend/node_modules/expect": { "version": "29.6.2", "dev": true, @@ -16249,6 +16375,11 @@ "node": ">=14.0.0" } }, + "packages/frontend/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "packages/frontend/node_modules/vitest": { "version": "0.33.0", "dev": true, @@ -16324,6 +16455,26 @@ "optional": true } } + }, + "packages/frontend/node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } } } diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 674f468..f94e794 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -20,10 +20,10 @@ "@opentelemetry/exporter-trace-otlp-proto": "^0.45.0", "@opentelemetry/sdk-metrics": "^1.17.1", "@opentelemetry/sdk-trace-web": "^1.17.1", - "@topos-protocol/topos-smart-contracts": "^2.0.0", + "@topos-protocol/topos-smart-contracts": "^3.1.0", "antd": "^5.4.0", "axios": "^1.4.0", - "ethers": "^5.7.2", + "ethers": "^6.9.0", "react": "18.2.0", "react-dom": "18.2.0", "react-google-recaptcha": "^3.1.0", diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 0124f94..4ea6c4e 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -1,6 +1,8 @@ import { ThemeProvider } from '@emotion/react' import styled from '@emotion/styled' +import { ToposCore__factory } from '@topos-protocol/topos-smart-contracts/typechain-types/factories/contracts/topos-core/ToposCore__factory' import { Alert, Layout as AntdLayout } from 'antd' +import { getDefaultProvider } from 'ethers' import { useEffect, useState } from 'react' import { ErrorsContext } from './contexts/errors' @@ -13,9 +15,7 @@ import FaucetForm from './components/FaucetForm' import Content from './components/Content' import { SubnetWithId } from './types' import useRegisteredSubnets from './hooks/useRegisteredSubnets' -import { BigNumber, ethers } from 'ethers' import { SubnetsContext } from './contexts/subnets' -import { toposCoreContract } from './contracts' import { SuccessesContext } from './contexts/successes' const Errors = styled.div` @@ -54,17 +54,18 @@ const App = () => { let toposSubnet: SubnetWithId | undefined if (toposSubnetEndpointHttp && toposSubnetEndpointWs) { - const provider = new ethers.providers.JsonRpcProvider( - toposSubnetEndpointHttp - ) + const provider = getDefaultProvider(toposSubnetEndpointHttp) const network = await provider.getNetwork() const chainId = network.chainId - const contract = toposCoreContract.connect(provider) - const subnetId = await contract.networkSubnetId() + const toposCore = ToposCore__factory.connect( + import.meta.env.VITE_TOPOS_CORE_PROXY_CONTRACT_ADDRESS, + provider + ) + const subnetId = await toposCore.networkSubnetId() toposSubnet = { - chainId: BigNumber.from(chainId.toString()), + chainId: BigInt(chainId.toString()), endpointHttp: toposSubnetEndpointHttp, endpointWs: toposSubnetEndpointWs, currencySymbol: 'TOPOS', diff --git a/packages/frontend/src/components/SubnetSelect.test.tsx b/packages/frontend/src/components/SubnetSelect.test.tsx index d914907..5bdd434 100644 --- a/packages/frontend/src/components/SubnetSelect.test.tsx +++ b/packages/frontend/src/components/SubnetSelect.test.tsx @@ -3,12 +3,11 @@ import { describe, it, expect, vi } from 'vitest' import SubnetSelect from './SubnetSelect' import { SubnetWithId } from '../types' -import { BigNumber } from 'ethers' import { userEvent } from '../utils/tests' const subnetsMock: SubnetWithId[] = [ { - chainId: BigNumber.from(1), + chainId: BigInt(1), currencySymbol: 'TST', endpointHttp: '', endpointWs: '', diff --git a/packages/frontend/src/contracts.ts b/packages/frontend/src/contracts.ts deleted file mode 100644 index 174cdc1..0000000 --- a/packages/frontend/src/contracts.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as SubnetRegistratorJSON from '@topos-protocol/topos-smart-contracts/artifacts/contracts/topos-core/SubnetRegistrator.sol/SubnetRegistrator.json' -import * as ToposCoreJSON from '@topos-protocol/topos-smart-contracts/artifacts/contracts/topos-core/ToposCore.sol/ToposCore.json' -import { - SubnetRegistrator, - ToposCore, -} from '@topos-protocol/topos-smart-contracts/typechain-types/contracts/topos-core' -import { ethers } from 'ethers' - -export const subnetRegistratorContract = new ethers.Contract( - import.meta.env.VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS || '', - SubnetRegistratorJSON.abi -) as SubnetRegistrator - -export const toposCoreContract = new ethers.Contract( - import.meta.env.VITE_TOPOS_CORE_PROXY_CONTRACT_ADDRESS || '', - ToposCoreJSON.abi -) as ToposCore diff --git a/packages/frontend/src/hooks/useRegisteredSubnets.test.ts b/packages/frontend/src/hooks/useRegisteredSubnets.test.ts index 1e2b7fa..5f47611 100644 --- a/packages/frontend/src/hooks/useRegisteredSubnets.test.ts +++ b/packages/frontend/src/hooks/useRegisteredSubnets.test.ts @@ -1,14 +1,13 @@ +import { SubnetRegistrator__factory } from '@topos-protocol/topos-smart-contracts/typechain-types/factories/contracts/topos-core/SubnetRegistrator__factory' import { renderHook, waitFor } from '@testing-library/react' -import { BigNumber } from 'ethers' import { vi } from 'vitest' import useRegisteredSubnets from './useRegisteredSubnets' -import * as contractExports from '../contracts' import { Subnet } from '../types' const registeredSubnets: { [x: string]: Subnet } = { subnet1: { - chainId: BigNumber.from(1), + chainId: BigInt(1), currencySymbol: 'TST', endpointHttp: '', endpointWs: '', @@ -16,7 +15,7 @@ const registeredSubnets: { [x: string]: Subnet } = { name: 'subnetMock', }, subnet2: { - chainId: BigNumber.from(2), + chainId: BigInt(2), currencySymbol: 'TST2', endpointHttp: '', endpointWs: '', @@ -34,7 +33,7 @@ const expectedSubnets = subnetIdsByIndexes.map((id) => ({ const getSubnetCountMock = vi .fn() - .mockResolvedValue(BigNumber.from(subnetIdsByIndexes.length)) + .mockResolvedValue(BigInt(subnetIdsByIndexes.length)) const getSubnetIdAtIndexMock = vi .fn() @@ -54,9 +53,9 @@ const contractConnectMock = vi.fn().mockReturnValue({ subnets: subnetsMock, }) -vi.spyOn(contractExports, 'subnetRegistratorContract', 'get').mockReturnValue({ - connect: contractConnectMock, -} as any) +vi.spyOn(SubnetRegistrator__factory, 'connect').mockReturnValue( + contractConnectMock +) vi.mock('./useEthers', () => ({ default: vi.fn().mockReturnValue({ provider: {} }), diff --git a/packages/frontend/src/hooks/useRegisteredSubnets.ts b/packages/frontend/src/hooks/useRegisteredSubnets.ts index dfd83dd..a8d03bf 100644 --- a/packages/frontend/src/hooks/useRegisteredSubnets.ts +++ b/packages/frontend/src/hooks/useRegisteredSubnets.ts @@ -1,9 +1,9 @@ -import { ethers } from 'ethers' +import { getDefaultProvider } from 'ethers' import { useCallback, useContext, useEffect, useMemo, useState } from 'react' +import { SubnetRegistrator__factory } from '@topos-protocol/topos-smart-contracts/typechain-types' -import { subnetRegistratorContract } from '../contracts' -import { Subnet, SubnetWithId } from '../types' import { ErrorsContext } from '../contexts/errors' +import { SubnetWithId } from '../types' export default function useRegisteredSubnets() { const { setErrors } = useContext(ErrorsContext) @@ -11,21 +11,21 @@ export default function useRegisteredSubnets() { const [registeredSubnets, setRegisteredSubnets] = useState() const provider = useMemo( - () => - new ethers.providers.WebSocketProvider( - import.meta.env.VITE_TOPOS_SUBNET_ENDPOINT_WS - ), + () => getDefaultProvider(import.meta.env.VITE_TOPOS_SUBNET_ENDPOINT_WS), [] ) - const contract = subnetRegistratorContract.connect(provider) - const getRegisteredSubnets = useCallback(async () => { setLoading(true) - const registeredSubnetsCount = await contract + const subnetRegistrator = SubnetRegistrator__factory.connect( + import.meta.env.VITE_SUBNET_REGISTRATOR_CONTRACT_ADDRESS, + provider + ) + + const registeredSubnetsCount = await subnetRegistrator .getSubnetCount() - .then((count: ethers.BigNumber) => count.toNumber()) + .then((count) => Number(count)) .catch((error: any) => { console.error(error) setErrors((e) => [ @@ -38,7 +38,7 @@ export default function useRegisteredSubnets() { const promises = [] let i = 0 while (i < registeredSubnetsCount) { - const subnetId = await contract + const subnetId = await subnetRegistrator .getSubnetIdAtIndex(i) .catch((error: any) => { console.error(error) @@ -50,10 +50,10 @@ export default function useRegisteredSubnets() { if (subnetId !== undefined) { promises.push( - contract + subnetRegistrator .subnets(subnetId) - .then((subnet: Subnet) => ({ - ...subnet, + .then((subnet) => ({ + ...(subnet as any).toObject(), // toObject method of ES6 Proxy id: subnetId, })) .catch((error: Error) => { diff --git a/packages/frontend/src/index.tsx b/packages/frontend/src/index.tsx index c8fe8f7..4c86a91 100644 --- a/packages/frontend/src/index.tsx +++ b/packages/frontend/src/index.tsx @@ -6,6 +6,14 @@ import App from './App' import reportWebVitals from './reportWebVitals' import './telemetry' +// Allow for JSON.stringify for objects containing bigints +Object.defineProperty(BigInt.prototype, 'toJSON', { + get() { + 'use strict' + return () => String(this) + }, +}) + const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) root.render( diff --git a/packages/frontend/src/types.ts b/packages/frontend/src/types.ts index 0f86a38..c0fcbc5 100644 --- a/packages/frontend/src/types.ts +++ b/packages/frontend/src/types.ts @@ -1,7 +1,5 @@ -import { BigNumber } from 'ethers' - export interface Subnet { - chainId: BigNumber + chainId: BigInt currencySymbol: string endpointHttp: string endpointWs: string