From cafa2fb3d020788697b590819cadb8f56362bf28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Dec 2021 21:20:51 +0000 Subject: [PATCH 01/72] Bump typescript from 4.5.2 to 4.5.4 (#140) --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index d24cbe1..791b5ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", - "typescript": "^4.5.2", + "typescript": "^4.5.4", "use-keyboard-shortcut": "^1.0.6", "web-vitals": "^1.0.1" }, @@ -18005,9 +18005,9 @@ } }, "node_modules/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -31512,9 +31512,9 @@ } }, "typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==" + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==" }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4" diff --git a/package.json b/package.json index e9e3b77..5f5e332 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", - "typescript": "^4.5.2", + "typescript": "^4.5.4", "use-keyboard-shortcut": "^1.0.6", "web-vitals": "^1.0.1" }, From 192032390b0cb0ad0213faf7bcac8b32011bf962 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Dec 2021 21:21:17 +0000 Subject: [PATCH 02/72] Bump @craco/craco from 6.4.2 to 6.4.3 (#137) --- package-lock.json | 241 ++++++++++++++++++++++++++++++++++++++++------ package.json | 2 +- 2 files changed, 213 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 791b5ee..13aecff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@blackbox-vision/react-qr-reader": "^5.0.0", "@chainlink/contracts": "^0.2.2", - "@craco/craco": "^6.4.2", + "@craco/craco": "^6.4.3", "@fontsource/fira-code": "^4.5.2", "@fontsource/roboto": "^4.5.1", "@fontsource/roboto-mono": "^4.5.0", @@ -1247,11 +1247,12 @@ } }, "node_modules/@craco/craco": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.4.2.tgz", - "integrity": "sha512-egIooyvuzKM5dsvWe/U5ISyFpZwLnG9uuTF1fU4s/6b/hE8MvoxyaxKymQKgbtpfOZeH0ebtEP4cbH7xZ4XRbw==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.4.3.tgz", + "integrity": "sha512-RzkXYmNzRCGUyG7mM+IUMM+nvrpSfA34352sPSGQN76UivAmCAht3sI4v5JKgzO05oUK9Zwi6abCKD7iKXI8hQ==", "dependencies": { "cosmiconfig": "^7.0.1", + "cosmiconfig-typescript-loader": "^1.0.0", "cross-spawn": "^7.0.0", "lodash": "^4.17.15", "semver": "^7.3.2", @@ -1267,6 +1268,30 @@ "react-scripts": "^4.0.0" } }, + "node_modules/@craco/craco/node_modules/acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@craco/craco/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@craco/craco/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, "node_modules/@craco/craco/node_modules/cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -1282,6 +1307,24 @@ "node": ">=10" } }, + "node_modules/@craco/craco/node_modules/cosmiconfig-typescript-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-1.0.0.tgz", + "integrity": "sha512-Ky5EjOcer3sKf+lWRPC1pM8pca6OtxFi07Xaf5rS0G4NP4pf873W32lq/M0Idm2+DSx0NCZv6h0X9yWguyCE8Q==", + "dependencies": { + "cosmiconfig": "^7", + "ts-node": "^10.4.0" + }, + "engines": { + "node": ">=12", + "npm": ">=7" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=7", + "typescript": ">=3" + } + }, "node_modules/@craco/craco/node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -1296,6 +1339,65 @@ "node": ">=10" } }, + "node_modules/@craco/craco/node_modules/ts-node": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@csstools/convert-colors": { "version": "1.4.0", "license": "CC0-1.0", @@ -2965,6 +3067,26 @@ "node": ">=6.9.0" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" + }, "node_modules/@types/anymatch": { "version": "1.3.1", "license": "MIT" @@ -6171,9 +6293,7 @@ "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "optional": true, - "peer": true + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -6911,8 +7031,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "optional": true, - "peer": true, "engines": { "node": ">=0.3.1" } @@ -11430,9 +11548,7 @@ "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "optional": true, - "peer": true + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" }, "node_modules/makeerror": { "version": "1.0.11", @@ -19449,8 +19565,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "optional": true, - "peer": true, "engines": { "node": ">=6" } @@ -20296,17 +20410,33 @@ } }, "@craco/craco": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.4.2.tgz", - "integrity": "sha512-egIooyvuzKM5dsvWe/U5ISyFpZwLnG9uuTF1fU4s/6b/hE8MvoxyaxKymQKgbtpfOZeH0ebtEP4cbH7xZ4XRbw==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.4.3.tgz", + "integrity": "sha512-RzkXYmNzRCGUyG7mM+IUMM+nvrpSfA34352sPSGQN76UivAmCAht3sI4v5JKgzO05oUK9Zwi6abCKD7iKXI8hQ==", "requires": { "cosmiconfig": "^7.0.1", + "cosmiconfig-typescript-loader": "^1.0.0", "cross-spawn": "^7.0.0", "lodash": "^4.17.15", "semver": "^7.3.2", "webpack-merge": "^4.2.2" }, "dependencies": { + "acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==" + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, "cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -20319,6 +20449,15 @@ "yaml": "^1.10.0" } }, + "cosmiconfig-typescript-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-1.0.0.tgz", + "integrity": "sha512-Ky5EjOcer3sKf+lWRPC1pM8pca6OtxFi07Xaf5rS0G4NP4pf873W32lq/M0Idm2+DSx0NCZv6h0X9yWguyCE8Q==", + "requires": { + "cosmiconfig": "^7", + "ts-node": "^10.4.0" + } + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -20326,9 +20465,41 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "ts-node": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "requires": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + } } } }, + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==" + }, + "@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, "@csstools/convert-colors": { "version": "1.4.0" }, @@ -21320,6 +21491,26 @@ } } }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" + }, "@types/anymatch": { "version": "1.3.1" }, @@ -23580,9 +23771,7 @@ "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "optional": true, - "peer": true + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "cross-spawn": { "version": "7.0.3", @@ -24066,9 +24255,7 @@ "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "optional": true, - "peer": true + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" }, "diff-sequences": { "version": "26.6.2" @@ -27026,9 +27213,7 @@ "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "optional": true, - "peer": true + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" }, "makeerror": { "version": "1.0.11", @@ -32513,9 +32698,7 @@ "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "optional": true, - "peer": true + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" }, "yocto-queue": { "version": "0.1.0" diff --git a/package.json b/package.json index 5f5e332..ceac9a0 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dependencies": { "@blackbox-vision/react-qr-reader": "^5.0.0", "@chainlink/contracts": "^0.2.2", - "@craco/craco": "^6.4.2", + "@craco/craco": "^6.4.3", "@fontsource/fira-code": "^4.5.2", "@fontsource/roboto": "^4.5.1", "@fontsource/roboto-mono": "^4.5.0", From a7f52390ab5483e5ca33e4b22b24b2d7592b934e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Dec 2021 01:42:39 +0000 Subject: [PATCH 03/72] Bump @chainlink/contracts from 0.2.2 to 0.3.0 (#138) --- package-lock.json | 15 ++++++++------- package.json | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 13aecff..f5019c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@blackbox-vision/react-qr-reader": "^5.0.0", - "@chainlink/contracts": "^0.2.2", + "@chainlink/contracts": "^0.3.0", "@craco/craco": "^6.4.3", "@fontsource/fira-code": "^4.5.2", "@fontsource/roboto": "^4.5.1", @@ -1228,9 +1228,10 @@ } }, "node_modules/@chainlink/contracts": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.2.2.tgz", - "integrity": "sha512-wxXPbt7O3aZaUSG34ufFASC5amRSL6eeYCqsa+2gpqbB8Hk7B7FydEDCI5dqvAC444TlFskPHcVbizCZkRHPpw==" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.3.0.tgz", + "integrity": "sha512-Pxu5qMTa0gc28Sxf9hyBkvwhPMn3HD62cGXy54RkL9PcabOHlUsk1i3BoFf3rwFr7T30N/obYn4de3ZrG12PDA==", + "hasInstallScript": true }, "node_modules/@cnakazawa/watch": { "version": "1.0.4", @@ -20398,9 +20399,9 @@ } }, "@chainlink/contracts": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.2.2.tgz", - "integrity": "sha512-wxXPbt7O3aZaUSG34ufFASC5amRSL6eeYCqsa+2gpqbB8Hk7B7FydEDCI5dqvAC444TlFskPHcVbizCZkRHPpw==" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.3.0.tgz", + "integrity": "sha512-Pxu5qMTa0gc28Sxf9hyBkvwhPMn3HD62cGXy54RkL9PcabOHlUsk1i3BoFf3rwFr7T30N/obYn4de3ZrG12PDA==" }, "@cnakazawa/watch": { "version": "1.0.4", diff --git a/package.json b/package.json index ceac9a0..de934fa 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "MIT", "dependencies": { "@blackbox-vision/react-qr-reader": "^5.0.0", - "@chainlink/contracts": "^0.2.2", + "@chainlink/contracts": "^0.3.0", "@craco/craco": "^6.4.3", "@fontsource/fira-code": "^4.5.2", "@fontsource/roboto": "^4.5.1", From 9575d79e595bfe9b21d199a476d3f12d6c4909c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Dec 2021 01:45:23 +0000 Subject: [PATCH 04/72] Bump react-router-dom from 6.0.2 to 6.1.1 (#142) --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5019c3..2b15c78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "react-error-boundary": "^3.1.4", "react-helmet-async": "^1.1.2", "react-image": "^4.0.3", - "react-router-dom": "^6.0.2", + "react-router-dom": "^6.1.1", "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", @@ -14620,9 +14620,9 @@ } }, "node_modules/react-router": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.0.2.tgz", - "integrity": "sha512-8/Wm3Ed8t7TuedXjAvV39+c8j0vwrI5qVsYqjFr5WkJjsJpEvNSoLRUbtqSEYzqaTUj1IV+sbPJxvO+accvU0Q==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.1.1.tgz", + "integrity": "sha512-55o96RiDZmC0uD17DPqVmzzfdNd2Dc+EjkYvMAmHl43du/GItaTdFr5WwjTryNWPXZ+OOVQxQhwAX25UwxpHtw==", "dependencies": { "history": "^5.1.0" }, @@ -14631,12 +14631,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.2.tgz", - "integrity": "sha512-cOpJ4B6raFutr0EG8O/M2fEoyQmwvZWomf1c6W2YXBZuFBx8oTk/zqjXghwScyhfrtnt0lANXV2182NQblRxFA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.1.1.tgz", + "integrity": "sha512-O3UH89DI4o+swd2q6lF4dSmpuNCxwkUXcj0zAFcVc1H+YoPE6T7uwoFMX0ws1pUvCY8lYDucFpOqCCdal6VFzg==", "dependencies": { "history": "^5.1.0", - "react-router": "6.0.2" + "react-router": "6.1.1" }, "peerDependencies": { "react": ">=16.8", @@ -29279,20 +29279,20 @@ "version": "0.8.3" }, "react-router": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.0.2.tgz", - "integrity": "sha512-8/Wm3Ed8t7TuedXjAvV39+c8j0vwrI5qVsYqjFr5WkJjsJpEvNSoLRUbtqSEYzqaTUj1IV+sbPJxvO+accvU0Q==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.1.1.tgz", + "integrity": "sha512-55o96RiDZmC0uD17DPqVmzzfdNd2Dc+EjkYvMAmHl43du/GItaTdFr5WwjTryNWPXZ+OOVQxQhwAX25UwxpHtw==", "requires": { "history": "^5.1.0" } }, "react-router-dom": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.2.tgz", - "integrity": "sha512-cOpJ4B6raFutr0EG8O/M2fEoyQmwvZWomf1c6W2YXBZuFBx8oTk/zqjXghwScyhfrtnt0lANXV2182NQblRxFA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.1.1.tgz", + "integrity": "sha512-O3UH89DI4o+swd2q6lF4dSmpuNCxwkUXcj0zAFcVc1H+YoPE6T7uwoFMX0ws1pUvCY8lYDucFpOqCCdal6VFzg==", "requires": { "history": "^5.1.0", - "react-router": "6.0.2" + "react-router": "6.1.1" } }, "react-scripts": { diff --git a/package.json b/package.json index de934fa..3fc0fa2 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "react-error-boundary": "^3.1.4", "react-helmet-async": "^1.1.2", "react-image": "^4.0.3", - "react-router-dom": "^6.0.2", + "react-router-dom": "^6.1.1", "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", From 5a72f6f91ae855b45047afdca1c70822f87cb70c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Dec 2021 01:45:40 +0000 Subject: [PATCH 05/72] Bump use-keyboard-shortcut from 1.0.6 to 1.1.0 (#141) --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b15c78..358f4e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", "typescript": "^4.5.4", - "use-keyboard-shortcut": "^1.0.6", + "use-keyboard-shortcut": "^1.1.0", "web-vitals": "^1.0.1" }, "devDependencies": { @@ -18365,9 +18365,9 @@ } }, "node_modules/use-keyboard-shortcut": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.0.6.tgz", - "integrity": "sha512-xdH5+XZ6fpey4Cvuh/T0q0tBYu9RGlhg1Xf2W6THtGi5CnI/M90kVaCpfy/Ew8ZDyJnUEwSh4gYolVI2QtgciQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.1.0.tgz", + "integrity": "sha512-92CzhdDqdH6518HHCpq1Nuey5RpJ2MuI6E60Y9Pl8G5hPZpGnOY4IrNttsTafDvser4uutfkepksE4MHzEczwg==", "peerDependencies": { "react": "^16.8.0", "react-dom": "^16.8.0" @@ -31850,9 +31850,9 @@ "version": "3.1.1" }, "use-keyboard-shortcut": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.0.6.tgz", - "integrity": "sha512-xdH5+XZ6fpey4Cvuh/T0q0tBYu9RGlhg1Xf2W6THtGi5CnI/M90kVaCpfy/Ew8ZDyJnUEwSh4gYolVI2QtgciQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.1.0.tgz", + "integrity": "sha512-92CzhdDqdH6518HHCpq1Nuey5RpJ2MuI6E60Y9Pl8G5hPZpGnOY4IrNttsTafDvser4uutfkepksE4MHzEczwg==", "requires": {} }, "util": { diff --git a/package.json b/package.json index 3fc0fa2..75bc304 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", "typescript": "^4.5.4", - "use-keyboard-shortcut": "^1.0.6", + "use-keyboard-shortcut": "^1.1.0", "web-vitals": "^1.0.1" }, "scripts": { From 3d762fe67ee44598ed264cd30fd5d83356531696 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 14 Dec 2021 22:47:30 -0300 Subject: [PATCH 06/72] Revert "Bump @chainlink/contracts from 0.2.2 to 0.3.0 (#138)" This reverts commit a7f52390ab5483e5ca33e4b22b24b2d7592b934e. --- package-lock.json | 15 +++++++-------- package.json | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 358f4e9..fe69f79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@blackbox-vision/react-qr-reader": "^5.0.0", - "@chainlink/contracts": "^0.3.0", + "@chainlink/contracts": "^0.2.2", "@craco/craco": "^6.4.3", "@fontsource/fira-code": "^4.5.2", "@fontsource/roboto": "^4.5.1", @@ -1228,10 +1228,9 @@ } }, "node_modules/@chainlink/contracts": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.3.0.tgz", - "integrity": "sha512-Pxu5qMTa0gc28Sxf9hyBkvwhPMn3HD62cGXy54RkL9PcabOHlUsk1i3BoFf3rwFr7T30N/obYn4de3ZrG12PDA==", - "hasInstallScript": true + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.2.2.tgz", + "integrity": "sha512-wxXPbt7O3aZaUSG34ufFASC5amRSL6eeYCqsa+2gpqbB8Hk7B7FydEDCI5dqvAC444TlFskPHcVbizCZkRHPpw==" }, "node_modules/@cnakazawa/watch": { "version": "1.0.4", @@ -20399,9 +20398,9 @@ } }, "@chainlink/contracts": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.3.0.tgz", - "integrity": "sha512-Pxu5qMTa0gc28Sxf9hyBkvwhPMn3HD62cGXy54RkL9PcabOHlUsk1i3BoFf3rwFr7T30N/obYn4de3ZrG12PDA==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.2.2.tgz", + "integrity": "sha512-wxXPbt7O3aZaUSG34ufFASC5amRSL6eeYCqsa+2gpqbB8Hk7B7FydEDCI5dqvAC444TlFskPHcVbizCZkRHPpw==" }, "@cnakazawa/watch": { "version": "1.0.4", diff --git a/package.json b/package.json index 75bc304..28b3ae8 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "MIT", "dependencies": { "@blackbox-vision/react-qr-reader": "^5.0.0", - "@chainlink/contracts": "^0.3.0", + "@chainlink/contracts": "^0.2.2", "@craco/craco": "^6.4.3", "@fontsource/fira-code": "^4.5.2", "@fontsource/roboto": "^4.5.1", From 08b80e1efc174fe8eab52abaf9f49df51dbe5119 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 17:37:45 -0300 Subject: [PATCH 07/72] Fix wrong @types/node major version --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe69f79..cf4556c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@types/jest": "^26.0.24", - "@types/node": "^14.17.5", + "@types/node": "^16.11.14", "@types/react": "^17.0.37", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.11", @@ -3207,9 +3207,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "14.17.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.5.tgz", - "integrity": "sha512-bjqH2cX/O33jXT/UmReo2pM7DIJREPMnarixbQ57DOOzzFaI6D2+IcwaJQaJpv0M1E9TIhPCYVxrkcityLjlqA==" + "version": "16.11.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.14.tgz", + "integrity": "sha512-mK6BKLpL0bG6v2CxHbm0ed6RcZrAtTHBTd/ZpnlVPVa3HkumsqLE4BC4u6TQ8D7pnrRbOU0am6epuALs+Ncnzw==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.0", @@ -21614,9 +21614,9 @@ "version": "3.0.3" }, "@types/node": { - "version": "14.17.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.5.tgz", - "integrity": "sha512-bjqH2cX/O33jXT/UmReo2pM7DIJREPMnarixbQ57DOOzzFaI6D2+IcwaJQaJpv0M1E9TIhPCYVxrkcityLjlqA==" + "version": "16.11.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.14.tgz", + "integrity": "sha512-mK6BKLpL0bG6v2CxHbm0ed6RcZrAtTHBTd/ZpnlVPVa3HkumsqLE4BC4u6TQ8D7pnrRbOU0am6epuALs+Ncnzw==" }, "@types/normalize-package-data": { "version": "2.4.0" diff --git a/package.json b/package.json index 28b3ae8..f738e5d 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@types/jest": "^26.0.24", - "@types/node": "^14.17.5", + "@types/node": "^16.11.14", "@types/react": "^17.0.37", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.11", From d5905216830399a2af8430508348f44db3a38df1 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 17:45:15 -0300 Subject: [PATCH 08/72] Add swr dependency --- package-lock.json | 15 +++++++++++++++ package.json | 1 + 2 files changed, 16 insertions(+) diff --git a/package-lock.json b/package-lock.json index cf4556c..2c79b40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", + "swr": "^1.1.1", "typescript": "^4.5.4", "use-keyboard-shortcut": "^1.1.0", "web-vitals": "^1.0.1" @@ -17205,6 +17206,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/swr/-/swr-1.1.1.tgz", + "integrity": "sha512-ZpUHyU3N3snj2QGFeE2Fd3BXl1CVS6YQIQGb1ttPAkTmvwZqDyV3GRMNPsaeAYCBM74tfn4XbKx28FVQR0mS7Q==", + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "license": "MIT" @@ -31069,6 +31078,12 @@ } } }, + "swr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/swr/-/swr-1.1.1.tgz", + "integrity": "sha512-ZpUHyU3N3snj2QGFeE2Fd3BXl1CVS6YQIQGb1ttPAkTmvwZqDyV3GRMNPsaeAYCBM74tfn4XbKx28FVQR0mS7Q==", + "requires": {} + }, "symbol-tree": { "version": "3.2.4" }, diff --git a/package.json b/package.json index f738e5d..fc8845f 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", + "swr": "^1.1.1", "typescript": "^4.5.4", "use-keyboard-shortcut": "^1.1.0", "web-vitals": "^1.0.1" From d5ea295eecaa5e435e9ea1d3663c38023e0c6303 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 18:35:49 -0300 Subject: [PATCH 09/72] Rework use4Bytes hook to use swr; fix handling of 0x00 data --- src/use4Bytes.ts | 66 ++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index 690d5a3..b619ca0 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -4,9 +4,10 @@ import { Interface, TransactionDescription, } from "@ethersproject/abi"; +import { BigNumberish } from "@ethersproject/bignumber"; +import useSWR from "swr"; import { RuntimeContext } from "./useRuntime"; import { fourBytesURL } from "./url"; -import { BigNumberish } from "@ethersproject/bignumber"; export type FourBytesEntry = { name: string; @@ -20,8 +21,6 @@ const simpleTransfer: FourBytesEntry = { signature: undefined, }; -const fullCache = new Map(); - export const extract4Bytes = (rawInput: string): string | null => { if (rawInput.length < 10) { return null; @@ -100,54 +99,45 @@ export const useBatch4Bytes = ( export const use4Bytes = ( rawFourBytes: string ): FourBytesEntry | null | undefined => { - if (rawFourBytes !== "0x") { - if (rawFourBytes.length !== 10 || !rawFourBytes.startsWith("0x")) { - throw new Error( - `rawFourBytes must contain a 4 bytes hex method signature starting with 0x; received value: "${rawFourBytes}"` - ); - } + if (!rawFourBytes.startsWith("0x")) { + throw new Error( + `rawFourBytes must contain a bytes hex string starting with 0x; received value: "${rawFourBytes}"` + ); } - const runtime = useContext(RuntimeContext); - const assetsURLPrefix = runtime.config?.assetsURLPrefix; + const { config } = useContext(RuntimeContext); + const assetsURLPrefix = config?.assetsURLPrefix; - const fourBytes = rawFourBytes.slice(2); - const [entry, setEntry] = useState( - fullCache.get(fourBytes) - ); - useEffect(() => { + const fourBytesFetcher = (key: string) => { + // TODO: throw error? if (assetsURLPrefix === undefined) { - return; + return undefined; } - if (fourBytes === "") { - return; + if (key === "0x") { + return undefined; } - const loadSig = async () => { - const entry = await fetch4Bytes(assetsURLPrefix, fourBytes); - fullCache.set(fourBytes, entry); - setEntry(entry); - }; - loadSig(); - }, [fourBytes, assetsURLPrefix]); + // Handle simple transfers with invalid selector like tx: + // 0x8bcbdcc1589b5c34c1e55909c8269a411f0267a4fed59a73dd4348cc71addbb9, + // which contains 0x00 as data + if (key.length !== 10) { + return undefined; + } + return fetch4Bytes(assetsURLPrefix, key.slice(2)); + }; + + const { data, error } = useSWR( + rawFourBytes, + fourBytesFetcher + ); if (rawFourBytes === "0x") { return simpleTransfer; } - if (assetsURLPrefix === undefined) { + if (error) { return undefined; } - - // Try to resolve 4bytes name - if (entry === null || entry === undefined) { - return entry; - } - - // Simulates LRU - // TODO: implement LRU purging - fullCache.delete(fourBytes); - fullCache.set(fourBytes, entry); - return entry; + return data; }; export const useTransactionDescription = ( From 478dd84710113f5f1fe44ac66b5f7a8ded4ec00f Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 19:08:04 -0300 Subject: [PATCH 10/72] Eliminate simple transfer singleton entry --- src/components/MethodName.tsx | 10 ++++++---- src/use4Bytes.ts | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/components/MethodName.tsx b/src/components/MethodName.tsx index c965a81..f084fa5 100644 --- a/src/components/MethodName.tsx +++ b/src/components/MethodName.tsx @@ -1,15 +1,17 @@ import React from "react"; -import { rawInputTo4Bytes, use4Bytes } from "../use4Bytes"; +import { extract4Bytes, use4Bytes } from "../use4Bytes"; type MethodNameProps = { data: string; }; const MethodName: React.FC = ({ data }) => { - const rawFourBytes = rawInputTo4Bytes(data); + const rawFourBytes = extract4Bytes(data); const fourBytesEntry = use4Bytes(rawFourBytes); - const methodName = fourBytesEntry?.name ?? rawFourBytes; - const isSimpleTransfer = rawFourBytes === "0x"; + const isSimpleTransfer = data === "0x"; + const methodName = isSimpleTransfer + ? "transfer" + : fourBytesEntry?.name ?? rawFourBytes ?? "-"; const methodTitle = isSimpleTransfer ? "ETH Transfer" : methodName === rawFourBytes diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index b619ca0..c003bd3 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -16,11 +16,13 @@ export type FourBytesEntry = { export type FourBytesMap = Record; -const simpleTransfer: FourBytesEntry = { - name: "transfer", - signature: undefined, -}; - +/** + * Given a hex input data; extract the method selector + * + * @param rawInput Raw tx input including the "0x" + * @returns the first 4 bytes, including the "0x" or null if the input + * contains an invalid selector, e.g., txs with 0x00 data + */ export const extract4Bytes = (rawInput: string): string | null => { if (rawInput.length < 10) { return null; @@ -60,6 +62,7 @@ const fetch4Bytes = async ( } }; +// TODO: migrate to swr and merge with use4Bytes export const useBatch4Bytes = ( rawFourByteSigs: string[] | undefined ): FourBytesMap => { @@ -97,9 +100,9 @@ export const useBatch4Bytes = ( * @param rawFourBytes an hex string containing the 4bytes signature in the "0xXXXXXXXX" format. */ export const use4Bytes = ( - rawFourBytes: string + rawFourBytes: string | null ): FourBytesEntry | null | undefined => { - if (!rawFourBytes.startsWith("0x")) { + if (rawFourBytes !== null && !rawFourBytes.startsWith("0x")) { throw new Error( `rawFourBytes must contain a bytes hex string starting with 0x; received value: "${rawFourBytes}"` ); @@ -108,12 +111,12 @@ export const use4Bytes = ( const { config } = useContext(RuntimeContext); const assetsURLPrefix = config?.assetsURLPrefix; - const fourBytesFetcher = (key: string) => { + const fourBytesFetcher = (key: string | null) => { // TODO: throw error? if (assetsURLPrefix === undefined) { return undefined; } - if (key === "0x") { + if (key === null || key === "0x") { return undefined; } @@ -131,9 +134,6 @@ export const use4Bytes = ( rawFourBytes, fourBytesFetcher ); - if (rawFourBytes === "0x") { - return simpleTransfer; - } if (error) { return undefined; } From e9f32e3530882051f42cdc9dacf803d82fae64e1 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 19:12:55 -0300 Subject: [PATCH 11/72] Extract method formatter --- src/components/MethodName.tsx | 14 ++------------ src/use4Bytes.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/components/MethodName.tsx b/src/components/MethodName.tsx index f084fa5..539db12 100644 --- a/src/components/MethodName.tsx +++ b/src/components/MethodName.tsx @@ -1,22 +1,12 @@ import React from "react"; -import { extract4Bytes, use4Bytes } from "../use4Bytes"; +import { useMethodSelector } from "../use4Bytes"; type MethodNameProps = { data: string; }; const MethodName: React.FC = ({ data }) => { - const rawFourBytes = extract4Bytes(data); - const fourBytesEntry = use4Bytes(rawFourBytes); - const isSimpleTransfer = data === "0x"; - const methodName = isSimpleTransfer - ? "transfer" - : fourBytesEntry?.name ?? rawFourBytes ?? "-"; - const methodTitle = isSimpleTransfer - ? "ETH Transfer" - : methodName === rawFourBytes - ? methodName - : `${methodName} [${rawFourBytes}]`; + const [isSimpleTransfer, methodName, methodTitle] = useMethodSelector(data); return (
{ + const rawFourBytes = extract4Bytes(data); + const fourBytesEntry = use4Bytes(rawFourBytes); + const isSimpleTransfer = data === "0x"; + const methodName = isSimpleTransfer + ? "transfer" + : fourBytesEntry?.name ?? rawFourBytes ?? "-"; + const methodTitle = isSimpleTransfer + ? "ETH Transfer" + : methodName === rawFourBytes + ? methodName + : `${methodName} [${rawFourBytes}]`; + + return [isSimpleTransfer, methodName, methodTitle]; +}; + export const useTransactionDescription = ( fourBytesEntry: FourBytesEntry | null | undefined, data: string | undefined, From 28a216ff5f5ea822c5114e64776b12ed022de8c8 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 19:22:13 -0300 Subject: [PATCH 12/72] Eliminate redundant function --- src/transaction/Details.tsx | 5 +++-- src/use4Bytes.ts | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 66c34e9..d414d1e 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -37,7 +37,7 @@ import PercentagePosition from "../components/PercentagePosition"; import DecodedParamsTable from "./decoder/DecodedParamsTable"; import InputDecoder from "./decoder/InputDecoder"; import { - rawInputTo4Bytes, + extract4Bytes, use4Bytes, useTransactionDescription, } from "../use4Bytes"; @@ -74,7 +74,8 @@ const Details: React.FC = ({ txData.confirmedData?.blockBaseFeePerGas !== undefined && txData.confirmedData?.blockBaseFeePerGas !== null; - const fourBytes = txData.to !== null ? rawInputTo4Bytes(txData.data) : "0x"; + const fourBytes = + txData.to !== null ? extract4Bytes(txData.data) ?? "0x" : "0x"; const fourBytesEntry = use4Bytes(fourBytes); const fourBytesTxDesc = useTransactionDescription( fourBytesEntry, diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index 233fa3f..ea4f28c 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -21,7 +21,8 @@ export type FourBytesMap = Record; * * @param rawInput Raw tx input including the "0x" * @returns the first 4 bytes, including the "0x" or null if the input - * contains an invalid selector, e.g., txs with 0x00 data + * contains an invalid selector, e.g., txs with 0x00 data; simple transfers (0x) + * return null as well as it is not a method selector */ export const extract4Bytes = (rawInput: string): string | null => { if (rawInput.length < 10) { @@ -30,8 +31,6 @@ export const extract4Bytes = (rawInput: string): string | null => { return rawInput.slice(0, 10); }; -export const rawInputTo4Bytes = (rawInput: string) => rawInput.slice(0, 10); - const fetch4Bytes = async ( assetsURLPrefix: string, fourBytes: string From ff0fd63ab9633bbffbea4980ffe013a86f7895f1 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 23:17:13 -0300 Subject: [PATCH 13/72] Replace batch calls of address resolution caches and deep props down with in-place useSWR calls --- src/TokenTransferItem.tsx | 11 +------ src/Transaction.tsx | 25 ++-------------- src/address/AddressTransactionResults.tsx | 2 -- src/block/BlockTransactionResults.tsx | 4 +-- src/components/AddressOrENSName.tsx | 14 ++++----- src/components/DecoratedAddressLink.tsx | 4 --- src/components/InternalCreate.tsx | 20 ++----------- src/components/InternalSelfDestruct.tsx | 22 ++------------ .../InternalTransactionOperation.tsx | 21 +++---------- src/components/InternalTransfer.tsx | 5 ---- src/components/TransactionAddress.tsx | 4 --- src/search/TransactionItem.tsx | 6 ---- src/transaction/Details.tsx | 14 +-------- src/transaction/LogEntry.tsx | 11 +------ src/transaction/Logs.tsx | 5 +--- src/transaction/Trace.tsx | 23 ++------------ src/transaction/TraceInput.tsx | 14 ++------- src/transaction/TraceItem.tsx | 25 +++------------- src/transaction/decoder/AddressDecoder.tsx | 9 ++---- src/transaction/decoder/DecodedParamRow.tsx | 10 +------ .../decoder/DecodedParamsTable.tsx | 4 --- src/transaction/decoder/InputDecoder.tsx | 4 --- src/useResolvedAddresses.ts | 30 +++++++++++++++++-- 23 files changed, 64 insertions(+), 223 deletions(-) diff --git a/src/TokenTransferItem.tsx b/src/TokenTransferItem.tsx index 9e49ee4..2cf91a0 100644 --- a/src/TokenTransferItem.tsx +++ b/src/TokenTransferItem.tsx @@ -10,13 +10,11 @@ import { TokenMeta, TokenTransfer, } from "./types"; -import { ResolvedAddresses } from "./api/address-resolver"; import { Metadata } from "./sourcify/useSourcify"; type TokenTransferItemProps = { t: TokenTransfer; tokenMeta?: TokenMeta | null | undefined; - resolvedAddresses: ResolvedAddresses | undefined; metadatas: Record; }; @@ -24,7 +22,6 @@ type TokenTransferItemProps = { const TokenTransferItem: React.FC = ({ t, tokenMeta, - resolvedAddresses, metadatas, }) => (
@@ -37,7 +34,6 @@ const TokenTransferItem: React.FC = ({
@@ -46,7 +42,6 @@ const TokenTransferItem: React.FC = ({
@@ -60,11 +55,7 @@ const TokenTransferItem: React.FC = ({ /> - + diff --git a/src/Transaction.tsx b/src/Transaction.tsx index 5101193..62a23d6 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -11,10 +11,7 @@ import { useInternalOperations, useTxData } from "./useErigonHooks"; import { useETHUSDOracle } from "./usePriceOracle"; import { useAppConfigContext } from "./useAppConfig"; import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify"; -import { - transactionDataCollector, - useResolvedAddresses, -} from "./useResolvedAddresses"; +import { transactionDataCollector } from "./useResolvedAddresses"; import { SelectedTransactionContext } from "./useSelectedTransaction"; const Details = React.lazy( @@ -49,7 +46,6 @@ const Transaction: React.FC = () => { () => transactionDataCollector(txData), [txData] ); - const resolvedAddresses = useResolvedAddresses(provider, addrCollector); const internalOps = useInternalOperations(provider, txData); const sendsEthToMiner = useMemo(() => { @@ -119,29 +115,14 @@ const Transaction: React.FC = () => { internalOps={internalOps} sendsEthToMiner={sendsEthToMiner} ethUSDPrice={blockETHUSDPrice} - resolvedAddresses={resolvedAddresses} /> } /> - } - /> - - } + element={} /> + } /> diff --git a/src/address/AddressTransactionResults.tsx b/src/address/AddressTransactionResults.tsx index f1a373e..0b62115 100644 --- a/src/address/AddressTransactionResults.tsx +++ b/src/address/AddressTransactionResults.tsx @@ -104,7 +104,6 @@ const AddressTransactionResults: React.FC = ({ // Resolve all addresses that appear on this page results const addrCollector = useMemo(() => pageCollector(page), [page]); - const resolvedAddresses = useResolvedAddresses(provider, addrCollector); // Calculate Sourcify metadata for all addresses that appear on this page results const addresses = useMemo(() => { @@ -152,7 +151,6 @@ const AddressTransactionResults: React.FC = ({ = ({ const selectionCtx = useSelection(); const [feeDisplay, feeDisplayToggler] = useFeeToggler(); const addrCollector = useMemo(() => pageCollector(page), [page]); - const resolvedAddresses = useResolvedAddresses(provider, addrCollector); const blockTags = useMemo(() => [blockTag], [blockTag]); const priceMap = useMultipleETHUSDOracle(provider, blockTags); @@ -79,7 +78,6 @@ const BlockTransactionResults: React.FC = ({ = ({ address, selectedAddress, dontOverrideColors, - resolvedAddresses, }) => { - const resolvedAddress = resolvedAddresses?.[address]; + const { provider } = useContext(RuntimeContext); + const resolvedAddress = useResolvedAddress(provider, address); const linkable = address !== selectedAddress; if (!resolvedAddress) { diff --git a/src/components/DecoratedAddressLink.tsx b/src/components/DecoratedAddressLink.tsx index 0f13f2c..1b3870a 100644 --- a/src/components/DecoratedAddressLink.tsx +++ b/src/components/DecoratedAddressLink.tsx @@ -9,7 +9,6 @@ import { faCoins } from "@fortawesome/free-solid-svg-icons/faCoins"; import AddressOrENSName from "./AddressOrENSName"; import SourcifyLogo from "../sourcify/SourcifyLogo"; import { AddressContext, ZERO_ADDRESS } from "../types"; -import { ResolvedAddresses } from "../api/address-resolver"; import { Metadata } from "../sourcify/useSourcify"; type DecoratedAddressLinkProps = { @@ -21,7 +20,6 @@ type DecoratedAddressLinkProps = { selfDestruct?: boolean; txFrom?: boolean; txTo?: boolean; - resolvedAddresses?: ResolvedAddresses | undefined; metadata?: Metadata | null | undefined; }; @@ -34,7 +32,6 @@ const DecoratedAddressLink: React.FC = ({ selfDestruct, txFrom, txTo, - resolvedAddresses, metadata, }) => { const mint = addressCtx === AddressContext.FROM && address === ZERO_ADDRESS; @@ -87,7 +84,6 @@ const DecoratedAddressLink: React.FC = ({ address={address} selectedAddress={selectedAddress} dontOverrideColors={mint || burn} - resolvedAddresses={resolvedAddresses} /> ); diff --git a/src/components/InternalCreate.tsx b/src/components/InternalCreate.tsx index e086e9d..cc13650 100644 --- a/src/components/InternalCreate.tsx +++ b/src/components/InternalCreate.tsx @@ -5,17 +5,12 @@ import TransactionAddress from "./TransactionAddress"; import AddressHighlighter from "./AddressHighlighter"; import DecoratedAddressLink from "./DecoratedAddressLink"; import { InternalOperation } from "../types"; -import { ResolvedAddresses } from "../api/address-resolver"; type InternalCreateProps = { internalOp: InternalOperation; - resolvedAddresses: ResolvedAddresses | undefined; }; -const InternalCreate: React.FC = ({ - internalOp, - resolvedAddresses, -}) => ( +const InternalCreate: React.FC = ({ internalOp }) => (
CREATE @@ -23,20 +18,11 @@ const InternalCreate: React.FC = ({ Contract
- +
- (Creator:{" "} - - ) + (Creator: )
); diff --git a/src/components/InternalSelfDestruct.tsx b/src/components/InternalSelfDestruct.tsx index 794b987..4df2119 100644 --- a/src/components/InternalSelfDestruct.tsx +++ b/src/components/InternalSelfDestruct.tsx @@ -5,19 +5,16 @@ import { faAngleRight } from "@fortawesome/free-solid-svg-icons/faAngleRight"; import AddressHighlighter from "./AddressHighlighter"; import DecoratedAddressLink from "./DecoratedAddressLink"; import { TransactionData, InternalOperation } from "../types"; -import { ResolvedAddresses } from "../api/address-resolver"; import TransactionAddress from "./TransactionAddress"; type InternalSelfDestructProps = { txData: TransactionData; internalOp: InternalOperation; - resolvedAddresses: ResolvedAddresses | undefined; }; const InternalSelfDestruct: React.FC = ({ txData, internalOp, - resolvedAddresses, }) => { const toMiner = txData.confirmedData?.miner !== undefined && @@ -32,21 +29,12 @@ const InternalSelfDestruct: React.FC = ({ Contract
- +
{internalOp.value.isZero() && (
- (To:{" "} - - ) + (To: )
)} @@ -64,11 +52,7 @@ const InternalSelfDestruct: React.FC = ({ toMiner ? "rounded px-2 py-1 bg-yellow-100" : "" }`} > - + diff --git a/src/components/InternalTransactionOperation.tsx b/src/components/InternalTransactionOperation.tsx index f167eb5..5870336 100644 --- a/src/components/InternalTransactionOperation.tsx +++ b/src/components/InternalTransactionOperation.tsx @@ -3,37 +3,24 @@ import InternalTransfer from "./InternalTransfer"; import InternalSelfDestruct from "./InternalSelfDestruct"; import InternalCreate from "./InternalCreate"; import { TransactionData, InternalOperation, OperationType } from "../types"; -import { ResolvedAddresses } from "../api/address-resolver"; type InternalTransactionOperationProps = { txData: TransactionData; internalOp: InternalOperation; - resolvedAddresses: ResolvedAddresses | undefined; }; const InternalTransactionOperation: React.FC = - ({ txData, internalOp, resolvedAddresses }) => ( + ({ txData, internalOp }) => ( <> {internalOp.type === OperationType.TRANSFER && ( - + )} {internalOp.type === OperationType.SELF_DESTRUCT && ( - + )} {(internalOp.type === OperationType.CREATE || internalOp.type === OperationType.CREATE2) && ( - + )} ); diff --git a/src/components/InternalTransfer.tsx b/src/components/InternalTransfer.tsx index bf46778..0600e95 100644 --- a/src/components/InternalTransfer.tsx +++ b/src/components/InternalTransfer.tsx @@ -5,18 +5,15 @@ import { faAngleRight } from "@fortawesome/free-solid-svg-icons/faAngleRight"; import AddressHighlighter from "./AddressHighlighter"; import DecoratedAddressLink from "./DecoratedAddressLink"; import { TransactionData, InternalOperation } from "../types"; -import { ResolvedAddresses } from "../api/address-resolver"; type InternalTransferProps = { txData: TransactionData; internalOp: InternalOperation; - resolvedAddresses: ResolvedAddresses | undefined; }; const InternalTransfer: React.FC = ({ txData, internalOp, - resolvedAddresses, }) => { const fromMiner = txData.confirmedData?.miner !== undefined && @@ -44,7 +41,6 @@ const InternalTransfer: React.FC = ({ miner={fromMiner} txFrom={internalOp.from === txData.from} txTo={internalOp.from === txData.to} - resolvedAddresses={resolvedAddresses} /> @@ -62,7 +58,6 @@ const InternalTransfer: React.FC = ({ miner={toMiner} txFrom={internalOp.to === txData.from} txTo={internalOp.to === txData.to} - resolvedAddresses={resolvedAddresses} /> diff --git a/src/components/TransactionAddress.tsx b/src/components/TransactionAddress.tsx index cb1edc9..a313b24 100644 --- a/src/components/TransactionAddress.tsx +++ b/src/components/TransactionAddress.tsx @@ -1,7 +1,6 @@ import React from "react"; import AddressHighlighter from "./AddressHighlighter"; import DecoratedAddressLink from "./DecoratedAddressLink"; -import { ResolvedAddresses } from "../api/address-resolver"; import { useSelectedTransaction } from "../useSelectedTransaction"; import { AddressContext } from "../types"; import { Metadata } from "../sourcify/useSourcify"; @@ -9,14 +8,12 @@ import { Metadata } from "../sourcify/useSourcify"; type TransactionAddressProps = { address: string; addressCtx?: AddressContext | undefined; - resolvedAddresses: ResolvedAddresses | undefined; metadata?: Metadata | null | undefined; }; const TransactionAddress: React.FC = ({ address, addressCtx, - resolvedAddresses, metadata, }) => { const txData = useSelectedTransaction(); @@ -32,7 +29,6 @@ const TransactionAddress: React.FC = ({ txFrom={address === txData?.from} txTo={address === txData?.to || creation} creation={creation} - resolvedAddresses={resolvedAddresses} metadata={metadata} /> diff --git a/src/search/TransactionItem.tsx b/src/search/TransactionItem.tsx index b6cfe1e..daa0088 100644 --- a/src/search/TransactionItem.tsx +++ b/src/search/TransactionItem.tsx @@ -18,12 +18,10 @@ import { ChecksummedAddress, ProcessedTransaction } from "../types"; import { FeeDisplay } from "./useFeeToggler"; import { formatValue } from "../components/formatter"; import ETH2USDValue from "../components/ETH2USDValue"; -import { ResolvedAddresses } from "../api/address-resolver"; import { Metadata } from "../sourcify/useSourcify"; type TransactionItemProps = { tx: ProcessedTransaction; - resolvedAddresses?: ResolvedAddresses; selectedAddress?: string; feeDisplay: FeeDisplay; priceMap: Record; @@ -32,7 +30,6 @@ type TransactionItemProps = { const TransactionItem: React.FC = ({ tx, - resolvedAddresses, selectedAddress, feeDisplay, priceMap, @@ -87,7 +84,6 @@ const TransactionItem: React.FC = ({ address={tx.from} selectedAddress={selectedAddress} miner={tx.miner === tx.from} - resolvedAddresses={resolvedAddresses} /> )} @@ -110,7 +106,6 @@ const TransactionItem: React.FC = ({ address={tx.to} selectedAddress={selectedAddress} miner={tx.miner === tx.to} - resolvedAddresses={resolvedAddresses} metadata={metadatas[tx.to]} /> @@ -120,7 +115,6 @@ const TransactionItem: React.FC = ({ address={tx.createdContractAddress!} selectedAddress={selectedAddress} creation - resolvedAddresses={resolvedAddresses} metadata={metadatas[tx.createdContractAddress!]} /> diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index d414d1e..695a686 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -42,7 +42,6 @@ import { useTransactionDescription, } from "../use4Bytes"; import { DevDoc, Metadata, useError, UserDoc } from "../sourcify/useSourcify"; -import { ResolvedAddresses } from "../api/address-resolver"; import { RuntimeContext } from "../useRuntime"; import { useContractsMetadata } from "../hooks"; import { useTransactionError } from "../useErigonHooks"; @@ -56,7 +55,6 @@ type DetailsProps = { internalOps?: InternalOperation[]; sendsEthToMiner: boolean; ethUSDPrice: BigNumber | undefined; - resolvedAddresses: ResolvedAddresses | undefined; }; const Details: React.FC = ({ @@ -68,7 +66,6 @@ const Details: React.FC = ({ internalOps, sendsEthToMiner, ethUSDPrice, - resolvedAddresses, }) => { const hasEIP1559 = txData.confirmedData?.blockBaseFeePerGas !== undefined && @@ -200,7 +197,6 @@ const Details: React.FC = ({ hasParamNames userMethod={userError} devMethod={devError} - resolvedAddresses={resolvedAddresses} /> )} @@ -252,10 +248,7 @@ const Details: React.FC = ({
- +
@@ -268,7 +261,6 @@ const Details: React.FC = ({
@@ -281,7 +273,6 @@ const Details: React.FC = ({
= ({ key={i} txData={txData} internalOp={op} - resolvedAddresses={resolvedAddresses} /> ))}
@@ -314,7 +304,6 @@ const Details: React.FC = ({ key={i} t={t} tokenMeta={txData.tokenMetas[t.token]} - resolvedAddresses={resolvedAddresses} metadatas={metadatas} /> ))} @@ -438,7 +427,6 @@ const Details: React.FC = ({ data={txData.data} userMethod={userMethod} devMethod={devMethod} - resolvedAddresses={resolvedAddresses} /> diff --git a/src/transaction/LogEntry.tsx b/src/transaction/LogEntry.tsx index 07be33f..90f9bee 100644 --- a/src/transaction/LogEntry.tsx +++ b/src/transaction/LogEntry.tsx @@ -8,23 +8,16 @@ import ModeTab from "../components/ModeTab"; import DecodedParamsTable from "./decoder/DecodedParamsTable"; import DecodedLogSignature from "./decoder/DecodedLogSignature"; import { useTopic0 } from "../useTopic0"; -import { ResolvedAddresses } from "../api/address-resolver"; import { ChecksummedAddress } from "../types"; import { Metadata } from "../sourcify/useSourcify"; type LogEntryProps = { log: Log; logDesc: LogDescription | null | undefined; - resolvedAddresses: ResolvedAddresses | undefined; metadatas: Record; }; -const LogEntry: React.FC = ({ - log, - logDesc, - resolvedAddresses, - metadatas, -}) => { +const LogEntry: React.FC = ({ log, logDesc, metadatas }) => { const rawTopic0 = log.topics[0]; const topic0 = useTopic0(rawTopic0); @@ -65,7 +58,6 @@ const LogEntry: React.FC = ({
@@ -109,7 +101,6 @@ const LogEntry: React.FC = ({ args={resolvedLogDesc.args} paramTypes={resolvedLogDesc.eventFragment.inputs} hasParamNames={resolvedLogDesc === logDesc} - resolvedAddresses={resolvedAddresses} />
diff --git a/src/transaction/Logs.tsx b/src/transaction/Logs.tsx index eba82e1..9b903b7 100644 --- a/src/transaction/Logs.tsx +++ b/src/transaction/Logs.tsx @@ -4,17 +4,15 @@ import ContentFrame from "../ContentFrame"; import LogEntry from "./LogEntry"; import { TransactionData } from "../types"; import { Metadata } from "../sourcify/useSourcify"; -import { ResolvedAddresses } from "../api/address-resolver"; import { RuntimeContext } from "../useRuntime"; import { useContractsMetadata } from "../hooks"; type LogsProps = { txData: TransactionData; metadata: Metadata | null | undefined; - resolvedAddresses: ResolvedAddresses | undefined; }; -const Logs: React.FC = ({ txData, metadata, resolvedAddresses }) => { +const Logs: React.FC = ({ txData, metadata }) => { const baseMetadatas = useMemo((): Record => { if (!txData.to || metadata === undefined) { return {}; @@ -68,7 +66,6 @@ const Logs: React.FC = ({ txData, metadata, resolvedAddresses }) => { key={i} log={l} logDesc={logDescs?.[i]} - resolvedAddresses={resolvedAddresses} metadatas={metadatas} /> ))} diff --git a/src/transaction/Trace.tsx b/src/transaction/Trace.tsx index 5002391..dcdab74 100644 --- a/src/transaction/Trace.tsx +++ b/src/transaction/Trace.tsx @@ -6,32 +6,19 @@ import { TransactionData } from "../types"; import { useBatch4Bytes } from "../use4Bytes"; import { useTraceTransaction, useUniqueSignatures } from "../useErigonHooks"; import { RuntimeContext } from "../useRuntime"; -import { ResolvedAddresses } from "../api/address-resolver"; -import { tracesCollector, useResolvedAddresses } from "../useResolvedAddresses"; +import { tracesCollector } from "../useResolvedAddresses"; type TraceProps = { txData: TransactionData; - resolvedAddresses: ResolvedAddresses | undefined; }; -const Trace: React.FC = ({ txData, resolvedAddresses }) => { +const Trace: React.FC = ({ txData }) => { const { provider } = useContext(RuntimeContext); const traces = useTraceTransaction(provider, txData.transactionHash); const uniqueSignatures = useUniqueSignatures(traces); const sigMap = useBatch4Bytes(uniqueSignatures); const addrCollector = useMemo(() => tracesCollector(traces), [traces]); - const traceResolvedAddresses = useResolvedAddresses(provider, addrCollector); - const mergedResolvedAddresses = useMemo(() => { - const merge = {}; - if (resolvedAddresses) { - Object.assign(merge, resolvedAddresses); - } - if (traceResolvedAddresses) { - Object.assign(merge, traceResolvedAddresses); - } - return merge; - }, [resolvedAddresses, traceResolvedAddresses]); return ( @@ -39,10 +26,7 @@ const Trace: React.FC = ({ txData, resolvedAddresses }) => { {traces ? ( <>
- +
{traces.map((t, i, a) => ( @@ -51,7 +35,6 @@ const Trace: React.FC = ({ txData, resolvedAddresses }) => { t={t} last={i === a.length - 1} fourBytesMap={sigMap} - resolvedAddresses={mergedResolvedAddresses} /> ))}
diff --git a/src/transaction/TraceInput.tsx b/src/transaction/TraceInput.tsx index f7e4713..a13234d 100644 --- a/src/transaction/TraceInput.tsx +++ b/src/transaction/TraceInput.tsx @@ -7,7 +7,6 @@ import FunctionSignature from "./FunctionSignature"; import InputDecoder from "./decoder/InputDecoder"; import ExpanderSwitch from "../components/ExpanderSwitch"; import { TraceEntry } from "../useErigonHooks"; -import { ResolvedAddresses } from "../api/address-resolver"; import { extract4Bytes, FourBytesEntry, @@ -17,14 +16,9 @@ import { type TraceInputProps = { t: TraceEntry; fourBytesMap: Record; - resolvedAddresses: ResolvedAddresses | undefined; }; -const TraceInput: React.FC = ({ - t, - fourBytesMap, - resolvedAddresses, -}) => { +const TraceInput: React.FC = ({ t, fourBytesMap }) => { const raw4Bytes = extract4Bytes(t.input); const fourBytes = raw4Bytes !== null ? fourBytesMap[raw4Bytes] : null; const sigText = @@ -54,10 +48,7 @@ const TraceInput: React.FC = ({ ) : ( <> - + {t.type !== "CREATE" && t.type !== "CREATE2" && ( <> @@ -93,7 +84,6 @@ const TraceInput: React.FC = ({ data={t.input} userMethod={undefined} devMethod={undefined} - resolvedAddresses={resolvedAddresses} />
)
diff --git a/src/transaction/TraceItem.tsx b/src/transaction/TraceItem.tsx index 60e4d85..9b91d0e 100644 --- a/src/transaction/TraceItem.tsx +++ b/src/transaction/TraceItem.tsx @@ -5,22 +5,15 @@ import { faMinusSquare } from "@fortawesome/free-regular-svg-icons/faMinusSquare import { Switch } from "@headlessui/react"; import { FourBytesEntry } from "../use4Bytes"; import { TraceGroup } from "../useErigonHooks"; -import { ResolvedAddresses } from "../api/address-resolver"; import TraceInput from "./TraceInput"; type TraceItemProps = { t: TraceGroup; last: boolean; fourBytesMap: Record; - resolvedAddresses: ResolvedAddresses | undefined; }; -const TraceItem: React.FC = ({ - t, - last, - fourBytesMap, - resolvedAddresses, -}) => { +const TraceItem: React.FC = ({ t, last, fourBytesMap }) => { const [expanded, setExpanded] = useState(true); return ( @@ -42,11 +35,7 @@ const TraceItem: React.FC = ({ /> )} - +
{t.children && (
= ({ expanded ? "" : "hidden" }`} > - +
)} @@ -68,11 +53,10 @@ const TraceItem: React.FC = ({ type TraceChildrenProps = { c: TraceGroup[]; fourBytesMap: Record; - resolvedAddresses: ResolvedAddresses | undefined; }; const TraceChildren: React.FC = React.memo( - ({ c, fourBytesMap, resolvedAddresses }) => { + ({ c, fourBytesMap }) => { return ( <> {c.map((tc, i, a) => ( @@ -81,7 +65,6 @@ const TraceChildren: React.FC = React.memo( t={tc} last={i === a.length - 1} fourBytesMap={fourBytesMap} - resolvedAddresses={resolvedAddresses} /> ))} diff --git a/src/transaction/decoder/AddressDecoder.tsx b/src/transaction/decoder/AddressDecoder.tsx index 49726e6..7c5c3ce 100644 --- a/src/transaction/decoder/AddressDecoder.tsx +++ b/src/transaction/decoder/AddressDecoder.tsx @@ -1,19 +1,14 @@ import React from "react"; import TransactionAddress from "../../components/TransactionAddress"; import Copy from "../../components/Copy"; -import { ResolvedAddresses } from "../../api/address-resolver"; type AddressDecoderProps = { r: string; - resolvedAddresses?: ResolvedAddresses | undefined; }; -const AddressDecoder: React.FC = ({ - r, - resolvedAddresses, -}) => ( +const AddressDecoder: React.FC = ({ r }) => (
- +
); diff --git a/src/transaction/decoder/DecodedParamRow.tsx b/src/transaction/decoder/DecodedParamRow.tsx index 3ad47cc..f30381d 100644 --- a/src/transaction/decoder/DecodedParamRow.tsx +++ b/src/transaction/decoder/DecodedParamRow.tsx @@ -8,7 +8,6 @@ import Uint256Decoder from "./Uint256Decoder"; import AddressDecoder from "./AddressDecoder"; import BooleanDecoder from "./BooleanDecoder"; import BytesDecoder from "./BytesDecoder"; -import { ResolvedAddresses } from "../../api/address-resolver"; import SelectionHighlighter, { valueSelector, } from "../../components/SelectionHighlighter"; @@ -20,7 +19,6 @@ type DecodedParamRowProps = { paramType: ParamType; arrayElem?: number | undefined; help?: string | undefined; - resolvedAddresses: ResolvedAddresses | undefined; }; const DecodedParamRow: React.FC = ({ @@ -30,7 +28,6 @@ const DecodedParamRow: React.FC = ({ paramType, arrayElem, help, - resolvedAddresses, }) => { const [showHelp, setShowHelp] = useState(false); @@ -80,10 +77,7 @@ const DecodedParamRow: React.FC = ({ {paramType.baseType === "uint256" ? ( ) : paramType.baseType === "address" ? ( - + ) : paramType.baseType === "bool" ? ( ) : paramType.baseType === "bytes" ? ( @@ -111,7 +105,6 @@ const DecodedParamRow: React.FC = ({ i={idx} r={e} paramType={paramType.components[idx]} - resolvedAddresses={resolvedAddresses} /> ))} {paramType.baseType === "array" && @@ -122,7 +115,6 @@ const DecodedParamRow: React.FC = ({ r={e} paramType={paramType.arrayChildren} arrayElem={idx} - resolvedAddresses={resolvedAddresses} /> ))} diff --git a/src/transaction/decoder/DecodedParamsTable.tsx b/src/transaction/decoder/DecodedParamsTable.tsx index 78d94e6..40dd473 100644 --- a/src/transaction/decoder/DecodedParamsTable.tsx +++ b/src/transaction/decoder/DecodedParamsTable.tsx @@ -2,7 +2,6 @@ import React from "react"; import { ParamType, Result } from "@ethersproject/abi"; import DecodedParamRow from "./DecodedParamRow"; import { DevMethod, UserMethod } from "../../sourcify/useSourcify"; -import { ResolvedAddresses } from "../../api/address-resolver"; type DecodedParamsTableProps = { args: Result; @@ -10,7 +9,6 @@ type DecodedParamsTableProps = { hasParamNames?: boolean; userMethod?: UserMethod | undefined; devMethod?: DevMethod | undefined; - resolvedAddresses?: ResolvedAddresses | undefined; }; const DecodedParamsTable: React.FC = ({ @@ -18,7 +16,6 @@ const DecodedParamsTable: React.FC = ({ paramTypes, hasParamNames = true, devMethod, - resolvedAddresses, }) => ( @@ -47,7 +44,6 @@ const DecodedParamsTable: React.FC = ({ r={r} paramType={paramTypes[i]} help={devMethod?.params?.[paramTypes[i].name]} - resolvedAddresses={resolvedAddresses} /> ))} diff --git a/src/transaction/decoder/InputDecoder.tsx b/src/transaction/decoder/InputDecoder.tsx index 0bd9835..07d6f80 100644 --- a/src/transaction/decoder/InputDecoder.tsx +++ b/src/transaction/decoder/InputDecoder.tsx @@ -5,7 +5,6 @@ import { Tab } from "@headlessui/react"; import ModeTab from "../../components/ModeTab"; import DecodedParamsTable from "./DecodedParamsTable"; import { DevMethod, UserMethod } from "../../sourcify/useSourcify"; -import { ResolvedAddresses } from "../../api/address-resolver"; type InputDecoderProps = { fourBytes: string; @@ -14,7 +13,6 @@ type InputDecoderProps = { data: string; userMethod: UserMethod | undefined; devMethod: DevMethod | undefined; - resolvedAddresses: ResolvedAddresses | undefined; }; const InputDecoder: React.FC = ({ @@ -24,7 +22,6 @@ const InputDecoder: React.FC = ({ data, userMethod, devMethod, - resolvedAddresses, }) => { const utfInput = useMemo(() => { try { @@ -57,7 +54,6 @@ const InputDecoder: React.FC = ({ hasParamNames={hasParamNames} userMethod={userMethod} devMethod={devMethod} - resolvedAddresses={resolvedAddresses} /> )} diff --git a/src/useResolvedAddresses.ts b/src/useResolvedAddresses.ts index 9250232..d7c3cb9 100644 --- a/src/useResolvedAddresses.ts +++ b/src/useResolvedAddresses.ts @@ -1,7 +1,13 @@ import { useState, useEffect, useRef, useContext } from "react"; -import { JsonRpcProvider } from "@ethersproject/providers"; +import { BaseProvider, JsonRpcProvider } from "@ethersproject/providers"; import { getAddress, isAddress } from "@ethersproject/address"; -import { batchPopulate, ResolvedAddresses } from "./api/address-resolver"; +import useSWR from "swr"; +import { + batchPopulate, + mainResolver, + ResolvedAddresses, +} from "./api/address-resolver"; +import { SelectedResolvedName } from "./api/address-resolver/CompositeAddressResolver"; import { TraceGroup } from "./useErigonHooks"; import { RuntimeContext } from "./useRuntime"; import { @@ -184,3 +190,23 @@ export const useResolvedAddresses = ( return names; }; + +export const useResolvedAddress = ( + provider: BaseProvider | undefined, + address: ChecksummedAddress +): SelectedResolvedName | undefined => { + const fetcher = async ( + key: string + ): Promise | undefined> => { + if (!provider) { + return undefined; + } + return mainResolver.resolveAddress(provider, address); + }; + + const { data, error } = useSWR(address, fetcher); + if (error) { + return undefined; + } + return data; +}; From 7df1babd27c13a95324c42310910a99c3d7db3b5 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 23:22:56 -0300 Subject: [PATCH 14/72] Remove AddressCollectors and batchResolver --- src/Transaction.tsx | 6 - src/address/AddressTransactionResults.tsx | 4 - src/api/address-resolver/index.ts | 28 ----- src/block/BlockTransactionResults.tsx | 2 - src/transaction/Trace.tsx | 5 +- src/useResolvedAddresses.ts | 133 +--------------------- 6 files changed, 5 insertions(+), 173 deletions(-) diff --git a/src/Transaction.tsx b/src/Transaction.tsx index 62a23d6..22873d0 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -11,7 +11,6 @@ import { useInternalOperations, useTxData } from "./useErigonHooks"; import { useETHUSDOracle } from "./usePriceOracle"; import { useAppConfigContext } from "./useAppConfig"; import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify"; -import { transactionDataCollector } from "./useResolvedAddresses"; import { SelectedTransactionContext } from "./useSelectedTransaction"; const Details = React.lazy( @@ -42,11 +41,6 @@ const Transaction: React.FC = () => { } const txData = useTxData(provider, txhash); - const addrCollector = useMemo( - () => transactionDataCollector(txData), - [txData] - ); - const internalOps = useInternalOperations(provider, txData); const sendsEthToMiner = useMemo(() => { if (!txData || !internalOps) { diff --git a/src/address/AddressTransactionResults.tsx b/src/address/AddressTransactionResults.tsx index 0b62115..8ca2118 100644 --- a/src/address/AddressTransactionResults.tsx +++ b/src/address/AddressTransactionResults.tsx @@ -10,7 +10,6 @@ import { useFeeToggler } from "../search/useFeeToggler"; import { SelectionContext, useSelection } from "../useSelection"; import { useMultipleETHUSDOracle } from "../usePriceOracle"; import { RuntimeContext } from "../useRuntime"; -import { pageCollector, useResolvedAddresses } from "../useResolvedAddresses"; import { useParams, useSearchParams } from "react-router-dom"; import { ChecksummedAddress } from "../types"; import { useContractsMetadata } from "../hooks"; @@ -102,9 +101,6 @@ const AddressTransactionResults: React.FC = ({ }, [page]); const priceMap = useMultipleETHUSDOracle(provider, blockTags); - // Resolve all addresses that appear on this page results - const addrCollector = useMemo(() => pageCollector(page), [page]); - // Calculate Sourcify metadata for all addresses that appear on this page results const addresses = useMemo(() => { const _addresses = [address]; diff --git a/src/api/address-resolver/index.ts b/src/api/address-resolver/index.ts index c6a1687..57f1b37 100644 --- a/src/api/address-resolver/index.ts +++ b/src/api/address-resolver/index.ts @@ -1,4 +1,3 @@ -import { BaseProvider } from "@ethersproject/providers"; import { ensRenderer } from "../../components/ENSName"; import { plainStringRenderer } from "../../components/PlainString"; import { tokenRenderer } from "../../components/TokenName"; @@ -48,30 +47,3 @@ resolverRendererRegistry.set(uniswapV2Resolver, uniswapV2PairRenderer); resolverRendererRegistry.set(uniswapV3Resolver, uniswapV3PairRenderer); resolverRendererRegistry.set(ercTokenResolver, tokenRenderer); resolverRendererRegistry.set(hardcodedResolver, plainStringRenderer); - -// TODO: implement progressive resolving -export const batchPopulate = async ( - provider: BaseProvider, - addresses: string[], - currentMap: ResolvedAddresses | undefined -): Promise => { - const solvers: Promise | undefined>[] = []; - const unresolvedAddresses = addresses.filter( - (a) => currentMap?.[a] === undefined - ); - for (const a of unresolvedAddresses) { - solvers.push(mainResolver.resolveAddress(provider, a)); - } - - const resultMap: ResolvedAddresses = currentMap ? { ...currentMap } : {}; - const results = await Promise.all(solvers); - for (let i = 0; i < results.length; i++) { - const r = results[i]; - if (r === undefined) { - continue; - } - resultMap[unresolvedAddresses[i]] = r; - } - - return resultMap; -}; diff --git a/src/block/BlockTransactionResults.tsx b/src/block/BlockTransactionResults.tsx index f41a6f4..2d768cc 100644 --- a/src/block/BlockTransactionResults.tsx +++ b/src/block/BlockTransactionResults.tsx @@ -8,7 +8,6 @@ import TransactionItem from "../search/TransactionItem"; import { useFeeToggler } from "../search/useFeeToggler"; import { RuntimeContext } from "../useRuntime"; import { SelectionContext, useSelection } from "../useSelection"; -import { pageCollector } from "../useResolvedAddresses"; import { ChecksummedAddress, ProcessedTransaction } from "../types"; import { PAGE_SIZE } from "../params"; import { useMultipleETHUSDOracle } from "../usePriceOracle"; @@ -30,7 +29,6 @@ const BlockTransactionResults: React.FC = ({ const { provider } = useContext(RuntimeContext); const selectionCtx = useSelection(); const [feeDisplay, feeDisplayToggler] = useFeeToggler(); - const addrCollector = useMemo(() => pageCollector(page), [page]); const blockTags = useMemo(() => [blockTag], [blockTag]); const priceMap = useMultipleETHUSDOracle(provider, blockTags); diff --git a/src/transaction/Trace.tsx b/src/transaction/Trace.tsx index dcdab74..565192e 100644 --- a/src/transaction/Trace.tsx +++ b/src/transaction/Trace.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useMemo } from "react"; +import React, { useContext } from "react"; import ContentFrame from "../ContentFrame"; import TransactionAddress from "../components/TransactionAddress"; import TraceItem from "./TraceItem"; @@ -6,7 +6,6 @@ import { TransactionData } from "../types"; import { useBatch4Bytes } from "../use4Bytes"; import { useTraceTransaction, useUniqueSignatures } from "../useErigonHooks"; import { RuntimeContext } from "../useRuntime"; -import { tracesCollector } from "../useResolvedAddresses"; type TraceProps = { txData: TransactionData; @@ -18,8 +17,6 @@ const Trace: React.FC = ({ txData }) => { const uniqueSignatures = useUniqueSignatures(traces); const sigMap = useBatch4Bytes(uniqueSignatures); - const addrCollector = useMemo(() => tracesCollector(traces), [traces]); - return (
diff --git a/src/useResolvedAddresses.ts b/src/useResolvedAddresses.ts index d7c3cb9..266a377 100644 --- a/src/useResolvedAddresses.ts +++ b/src/useResolvedAddresses.ts @@ -1,20 +1,11 @@ -import { useState, useEffect, useRef, useContext } from "react"; -import { BaseProvider, JsonRpcProvider } from "@ethersproject/providers"; +import { useState, useEffect, useContext } from "react"; +import { BaseProvider } from "@ethersproject/providers"; import { getAddress, isAddress } from "@ethersproject/address"; import useSWR from "swr"; -import { - batchPopulate, - mainResolver, - ResolvedAddresses, -} from "./api/address-resolver"; +import { mainResolver } from "./api/address-resolver"; import { SelectedResolvedName } from "./api/address-resolver/CompositeAddressResolver"; -import { TraceGroup } from "./useErigonHooks"; import { RuntimeContext } from "./useRuntime"; -import { - ChecksummedAddress, - ProcessedTransaction, - TransactionData, -} from "./types"; +import { ChecksummedAddress } from "./types"; export const useAddressOrENSFromURL = ( addressOrName: string, @@ -75,122 +66,6 @@ export const useAddressOrENSFromURL = ( return [checksummedAddress, isENS, error]; }; -export type AddressCollector = () => string[]; - -export const pageCollector = - (page: ProcessedTransaction[] | undefined): AddressCollector => - () => { - if (!page) { - return []; - } - - const uniqueAddresses = new Set(); - for (const tx of page) { - if (tx.from) { - uniqueAddresses.add(tx.from); - } - if (tx.to) { - uniqueAddresses.add(tx.to); - } - if (tx.createdContractAddress) { - uniqueAddresses.add(tx.createdContractAddress); - } - } - - return Array.from(uniqueAddresses); - }; - -export const transactionDataCollector = - (txData: TransactionData | null | undefined): AddressCollector => - () => { - if (!txData) { - return []; - } - - const uniqueAddresses = new Set(); - - // Standard fields - uniqueAddresses.add(txData.from); - if (txData.to) { - uniqueAddresses.add(txData.to); - } - if (txData.confirmedData?.createdContractAddress) { - uniqueAddresses.add(txData.confirmedData?.createdContractAddress); - } - - // Dig token transfers - for (const t of txData.tokenTransfers) { - uniqueAddresses.add(t.from); - uniqueAddresses.add(t.to); - uniqueAddresses.add(t.token); - } - - // Dig log addresses - if (txData.confirmedData) { - for (const l of txData.confirmedData.logs) { - uniqueAddresses.add(l.address); - // TODO: find a way to dig over decoded address log attributes - } - } - - return Array.from(uniqueAddresses); - }; - -export const tracesCollector = - (traces: TraceGroup[] | undefined): AddressCollector => - () => { - if (traces === undefined) { - return []; - } - - const uniqueAddresses = new Set(); - let searchTraces = [...traces]; - while (searchTraces.length > 0) { - const nextSearch: TraceGroup[] = []; - - for (const g of searchTraces) { - uniqueAddresses.add(g.from); - uniqueAddresses.add(g.to); - if (g.children) { - nextSearch.push(...g.children); - } - } - - searchTraces = nextSearch; - } - return Array.from(uniqueAddresses); - }; - -export const useResolvedAddresses = ( - provider: JsonRpcProvider | undefined, - addrCollector: AddressCollector -) => { - const [names, setNames] = useState(); - const ref = useRef(); - useEffect(() => { - ref.current = names; - }); - - useEffect( - () => { - if (!provider) { - return; - } - - const populate = async () => { - const _addresses = addrCollector(); - const _names = await batchPopulate(provider, _addresses, ref.current); - setNames(_names); - }; - populate(); - }, - // DON'T put names variables in dependency array; this is intentional; useRef - [provider, addrCollector] - ); - - return names; -}; - export const useResolvedAddress = ( provider: BaseProvider | undefined, address: ChecksummedAddress From 61383a36d93f67a864dee546fb452b3a20e2dce6 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 18 Dec 2021 07:09:10 -0300 Subject: [PATCH 15/72] Change to immutable SWR --- src/use4Bytes.ts | 4 ++-- src/useResolvedAddresses.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index ea4f28c..8e6ef41 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -5,7 +5,7 @@ import { TransactionDescription, } from "@ethersproject/abi"; import { BigNumberish } from "@ethersproject/bignumber"; -import useSWR from "swr"; +import useSWRImmutable from "swr/immutable"; import { RuntimeContext } from "./useRuntime"; import { fourBytesURL } from "./url"; @@ -129,7 +129,7 @@ export const use4Bytes = ( return fetch4Bytes(assetsURLPrefix, key.slice(2)); }; - const { data, error } = useSWR( + const { data, error } = useSWRImmutable( rawFourBytes, fourBytesFetcher ); diff --git a/src/useResolvedAddresses.ts b/src/useResolvedAddresses.ts index 266a377..adc3819 100644 --- a/src/useResolvedAddresses.ts +++ b/src/useResolvedAddresses.ts @@ -1,7 +1,7 @@ import { useState, useEffect, useContext } from "react"; import { BaseProvider } from "@ethersproject/providers"; import { getAddress, isAddress } from "@ethersproject/address"; -import useSWR from "swr"; +import useSWRImmutable from "swr/immutable"; import { mainResolver } from "./api/address-resolver"; import { SelectedResolvedName } from "./api/address-resolver/CompositeAddressResolver"; import { RuntimeContext } from "./useRuntime"; @@ -79,7 +79,7 @@ export const useResolvedAddress = ( return mainResolver.resolveAddress(provider, address); }; - const { data, error } = useSWR(address, fetcher); + const { data, error } = useSWRImmutable(address, fetcher); if (error) { return undefined; } From d67cd52ceb9090ae25a59a8b5a995c0eb9698a4c Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 18 Dec 2021 07:31:03 -0300 Subject: [PATCH 16/72] Move check to outside useSWR --- src/use4Bytes.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index 8e6ef41..a1adfdb 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -111,10 +111,6 @@ export const use4Bytes = ( const assetsURLPrefix = config?.assetsURLPrefix; const fourBytesFetcher = (key: string | null) => { - // TODO: throw error? - if (assetsURLPrefix === undefined) { - return undefined; - } if (key === null || key === "0x") { return undefined; } @@ -126,17 +122,14 @@ export const use4Bytes = ( return undefined; } - return fetch4Bytes(assetsURLPrefix, key.slice(2)); + return fetch4Bytes(assetsURLPrefix!, key.slice(2)); }; const { data, error } = useSWRImmutable( - rawFourBytes, + assetsURLPrefix !== undefined ? rawFourBytes : null, fourBytesFetcher ); - if (error) { - return undefined; - } - return data; + return error ? undefined : data; }; export const useMethodSelector = (data: string): [boolean, string, string] => { From a934282ab0bfcb2506a3d6348ef245209dcbbd49 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 18 Dec 2021 21:13:35 -0300 Subject: [PATCH 17/72] Remove fourBytesMap usage; replace everything by swr --- src/transaction/Trace.tsx | 12 ++-------- src/transaction/TraceInput.tsx | 7 +++--- src/transaction/TraceItem.tsx | 34 ++++++++++----------------- src/use4Bytes.ts | 36 +--------------------------- src/useErigonHooks.ts | 43 +--------------------------------- 5 files changed, 19 insertions(+), 113 deletions(-) diff --git a/src/transaction/Trace.tsx b/src/transaction/Trace.tsx index 565192e..a04bcdb 100644 --- a/src/transaction/Trace.tsx +++ b/src/transaction/Trace.tsx @@ -3,8 +3,7 @@ import ContentFrame from "../ContentFrame"; import TransactionAddress from "../components/TransactionAddress"; import TraceItem from "./TraceItem"; import { TransactionData } from "../types"; -import { useBatch4Bytes } from "../use4Bytes"; -import { useTraceTransaction, useUniqueSignatures } from "../useErigonHooks"; +import { useTraceTransaction } from "../useErigonHooks"; import { RuntimeContext } from "../useRuntime"; type TraceProps = { @@ -14,8 +13,6 @@ type TraceProps = { const Trace: React.FC = ({ txData }) => { const { provider } = useContext(RuntimeContext); const traces = useTraceTransaction(provider, txData.transactionHash); - const uniqueSignatures = useUniqueSignatures(traces); - const sigMap = useBatch4Bytes(uniqueSignatures); return ( @@ -27,12 +24,7 @@ const Trace: React.FC = ({ txData }) => {
{traces.map((t, i, a) => ( - + ))}
diff --git a/src/transaction/TraceInput.tsx b/src/transaction/TraceInput.tsx index a13234d..0524501 100644 --- a/src/transaction/TraceInput.tsx +++ b/src/transaction/TraceInput.tsx @@ -9,18 +9,17 @@ import ExpanderSwitch from "../components/ExpanderSwitch"; import { TraceEntry } from "../useErigonHooks"; import { extract4Bytes, - FourBytesEntry, + use4Bytes, useTransactionDescription, } from "../use4Bytes"; type TraceInputProps = { t: TraceEntry; - fourBytesMap: Record; }; -const TraceInput: React.FC = ({ t, fourBytesMap }) => { +const TraceInput: React.FC = ({ t }) => { const raw4Bytes = extract4Bytes(t.input); - const fourBytes = raw4Bytes !== null ? fourBytesMap[raw4Bytes] : null; + const fourBytes = use4Bytes(raw4Bytes); const sigText = raw4Bytes === null ? "" : fourBytes?.name ?? raw4Bytes; const hasParams = t.input.length > 10; diff --git a/src/transaction/TraceItem.tsx b/src/transaction/TraceItem.tsx index 9b91d0e..d6a4d7d 100644 --- a/src/transaction/TraceItem.tsx +++ b/src/transaction/TraceItem.tsx @@ -3,17 +3,15 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPlusSquare } from "@fortawesome/free-regular-svg-icons/faPlusSquare"; import { faMinusSquare } from "@fortawesome/free-regular-svg-icons/faMinusSquare"; import { Switch } from "@headlessui/react"; -import { FourBytesEntry } from "../use4Bytes"; import { TraceGroup } from "../useErigonHooks"; import TraceInput from "./TraceInput"; type TraceItemProps = { t: TraceGroup; last: boolean; - fourBytesMap: Record; }; -const TraceItem: React.FC = ({ t, last, fourBytesMap }) => { +const TraceItem: React.FC = ({ t, last }) => { const [expanded, setExpanded] = useState(true); return ( @@ -35,7 +33,7 @@ const TraceItem: React.FC = ({ t, last, fourBytesMap }) => { /> )} - + {t.children && (
= ({ t, last, fourBytesMap }) => { expanded ? "" : "hidden" }`} > - +
)} @@ -52,24 +50,16 @@ const TraceItem: React.FC = ({ t, last, fourBytesMap }) => { type TraceChildrenProps = { c: TraceGroup[]; - fourBytesMap: Record; }; -const TraceChildren: React.FC = React.memo( - ({ c, fourBytesMap }) => { - return ( - <> - {c.map((tc, i, a) => ( - - ))} - - ); - } -); +const TraceChildren: React.FC = React.memo(({ c }) => { + return ( + <> + {c.map((tc, i, a) => ( + + ))} + + ); +}); export default TraceItem; diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index a1adfdb..85f4e98 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useContext, useMemo } from "react"; +import { useContext, useMemo } from "react"; import { Fragment, Interface, @@ -14,8 +14,6 @@ export type FourBytesEntry = { signature: string | undefined; }; -export type FourBytesMap = Record; - /** * Given a hex input data; extract the method selector * @@ -61,38 +59,6 @@ const fetch4Bytes = async ( } }; -// TODO: migrate to swr and merge with use4Bytes -export const useBatch4Bytes = ( - rawFourByteSigs: string[] | undefined -): FourBytesMap => { - const runtime = useContext(RuntimeContext); - const assetsURLPrefix = runtime.config?.assetsURLPrefix; - - const [fourBytesMap, setFourBytesMap] = useState({}); - useEffect(() => { - if (!rawFourByteSigs || assetsURLPrefix === undefined) { - setFourBytesMap({}); - return; - } - - const loadSigs = async () => { - const promises = rawFourByteSigs.map((s) => - fetch4Bytes(assetsURLPrefix, s.slice(2)) - ); - const results = await Promise.all(promises); - - const _fourBytesMap: Record = {}; - for (let i = 0; i < rawFourByteSigs.length; i++) { - _fourBytesMap[rawFourByteSigs[i]] = results[i]; - } - setFourBytesMap(_fourBytesMap); - }; - loadSigs(); - }, [assetsURLPrefix, rawFourByteSigs]); - - return fourBytesMap; -}; - /** * Extract 4bytes DB info * diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index 66fe480..e63a718 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from "react"; +import { useState, useEffect } from "react"; import { Block, BlockWithTransactions } from "@ethersproject/abstract-provider"; import { JsonRpcProvider } from "@ethersproject/providers"; import { getAddress } from "@ethersproject/address"; @@ -6,7 +6,6 @@ import { Contract } from "@ethersproject/contracts"; import { defaultAbiCoder } from "@ethersproject/abi"; import { BigNumber } from "@ethersproject/bignumber"; import { arrayify, hexDataSlice, isHexString } from "@ethersproject/bytes"; -import { extract4Bytes } from "./use4Bytes"; import { getInternalOperations } from "./nodeFunctions"; import { TokenMetas, @@ -408,46 +407,6 @@ export const useTraceTransaction = ( return traceGroups; }; -/** - * Flatten a trace tree and extract and dedup 4byte function signatures - */ -export const useUniqueSignatures = (traces: TraceGroup[] | undefined) => { - const uniqueSignatures = useMemo(() => { - if (!traces) { - return undefined; - } - - const sigs = new Set(); - let nextTraces: TraceGroup[] = [...traces]; - while (nextTraces.length > 0) { - const traces = nextTraces; - nextTraces = []; - - for (const t of traces) { - if ( - t.type === "CALL" || - t.type === "DELEGATECALL" || - t.type === "STATICCALL" || - t.type === "CALLCODE" - ) { - const fourBytes = extract4Bytes(t.input); - if (fourBytes) { - sigs.add(fourBytes); - } - } - - if (t.children) { - nextTraces.push(...t.children); - } - } - } - - return [...sigs]; - }, [traces]); - - return uniqueSignatures; -}; - const hasCode = async ( provider: JsonRpcProvider, address: ChecksummedAddress From 10f8f0e137f20d043800bed13e2bfbc1822bd7a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Dec 2021 17:28:27 +0000 Subject: [PATCH 18/72] Bump highlightjs-solidity from 2.0.2 to 2.0.3 (#148) --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2c79b40..54e4c4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "@types/react-syntax-highlighter": "^13.5.2", "chart.js": "^3.6.2", "ethers": "^5.5.2", - "highlightjs-solidity": "^2.0.2", + "highlightjs-solidity": "^2.0.3", "react": "^17.0.2", "react-blockies": "^1.4.1", "react-chartjs-2": "^4.0.0", @@ -9362,9 +9362,9 @@ } }, "node_modules/highlightjs-solidity": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-2.0.2.tgz", - "integrity": "sha512-q0aYUKiZ9MPQg41qx/KpXKaCpqql50qTvmwGYyLFfcjt9AE/+C9CwjVIdJZc7EYj6NGgJuFJ4im1gfgrzUU1fQ==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-2.0.3.tgz", + "integrity": "sha512-tjFm5dtIE61VQBzjlZmkCtY5fLs3CaEABbVuUNyXeW+UuOCsxMg3MsPFy0kCelHP74hPpkoqDejLrbnV1axAIw==" }, "node_modules/history": { "version": "5.1.0", @@ -25797,9 +25797,9 @@ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" }, "highlightjs-solidity": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-2.0.2.tgz", - "integrity": "sha512-q0aYUKiZ9MPQg41qx/KpXKaCpqql50qTvmwGYyLFfcjt9AE/+C9CwjVIdJZc7EYj6NGgJuFJ4im1gfgrzUU1fQ==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-2.0.3.tgz", + "integrity": "sha512-tjFm5dtIE61VQBzjlZmkCtY5fLs3CaEABbVuUNyXeW+UuOCsxMg3MsPFy0kCelHP74hPpkoqDejLrbnV1axAIw==" }, "history": { "version": "5.1.0", diff --git a/package.json b/package.json index fc8845f..3cf65d0 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@types/react-syntax-highlighter": "^13.5.2", "chart.js": "^3.6.2", "ethers": "^5.5.2", - "highlightjs-solidity": "^2.0.2", + "highlightjs-solidity": "^2.0.3", "react": "^17.0.2", "react-blockies": "^1.4.1", "react-chartjs-2": "^4.0.0", From 4f01a2a279b28102e918bc2ccd086b8803cd54c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Dec 2021 17:29:15 +0000 Subject: [PATCH 19/72] Bump react-router-dom from 6.1.1 to 6.2.1 (#147) --- package-lock.json | 50 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 54e4c4c..89a0cb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "react-error-boundary": "^3.1.4", "react-helmet-async": "^1.1.2", "react-image": "^4.0.3", - "react-router-dom": "^6.1.1", + "react-router-dom": "^6.2.1", "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", @@ -9367,9 +9367,9 @@ "integrity": "sha512-tjFm5dtIE61VQBzjlZmkCtY5fLs3CaEABbVuUNyXeW+UuOCsxMg3MsPFy0kCelHP74hPpkoqDejLrbnV1axAIw==" }, "node_modules/history": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/history/-/history-5.1.0.tgz", - "integrity": "sha512-zPuQgPacm2vH2xdORvGGz1wQMuHSIB56yNAy5FnLuwOwgSYyPKptJtcMm6Ev+hRGeS+GzhbmRacHzvlESbFwDg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz", + "integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==", "dependencies": { "@babel/runtime": "^7.7.6" } @@ -14620,23 +14620,23 @@ } }, "node_modules/react-router": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.1.1.tgz", - "integrity": "sha512-55o96RiDZmC0uD17DPqVmzzfdNd2Dc+EjkYvMAmHl43du/GItaTdFr5WwjTryNWPXZ+OOVQxQhwAX25UwxpHtw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz", + "integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==", "dependencies": { - "history": "^5.1.0" + "history": "^5.2.0" }, "peerDependencies": { "react": ">=16.8" } }, "node_modules/react-router-dom": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.1.1.tgz", - "integrity": "sha512-O3UH89DI4o+swd2q6lF4dSmpuNCxwkUXcj0zAFcVc1H+YoPE6T7uwoFMX0ws1pUvCY8lYDucFpOqCCdal6VFzg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz", + "integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==", "dependencies": { - "history": "^5.1.0", - "react-router": "6.1.1" + "history": "^5.2.0", + "react-router": "6.2.1" }, "peerDependencies": { "react": ">=16.8", @@ -25802,9 +25802,9 @@ "integrity": "sha512-tjFm5dtIE61VQBzjlZmkCtY5fLs3CaEABbVuUNyXeW+UuOCsxMg3MsPFy0kCelHP74hPpkoqDejLrbnV1axAIw==" }, "history": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/history/-/history-5.1.0.tgz", - "integrity": "sha512-zPuQgPacm2vH2xdORvGGz1wQMuHSIB56yNAy5FnLuwOwgSYyPKptJtcMm6Ev+hRGeS+GzhbmRacHzvlESbFwDg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz", + "integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==", "requires": { "@babel/runtime": "^7.7.6" } @@ -29287,20 +29287,20 @@ "version": "0.8.3" }, "react-router": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.1.1.tgz", - "integrity": "sha512-55o96RiDZmC0uD17DPqVmzzfdNd2Dc+EjkYvMAmHl43du/GItaTdFr5WwjTryNWPXZ+OOVQxQhwAX25UwxpHtw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz", + "integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==", "requires": { - "history": "^5.1.0" + "history": "^5.2.0" } }, "react-router-dom": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.1.1.tgz", - "integrity": "sha512-O3UH89DI4o+swd2q6lF4dSmpuNCxwkUXcj0zAFcVc1H+YoPE6T7uwoFMX0ws1pUvCY8lYDucFpOqCCdal6VFzg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz", + "integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==", "requires": { - "history": "^5.1.0", - "react-router": "6.1.1" + "history": "^5.2.0", + "react-router": "6.2.1" } }, "react-scripts": { diff --git a/package.json b/package.json index 3cf65d0..477938d 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "react-error-boundary": "^3.1.4", "react-helmet-async": "^1.1.2", "react-image": "^4.0.3", - "react-router-dom": "^6.1.1", + "react-router-dom": "^6.2.1", "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", From cb98721073d5c26ecc4dbd598ffe371cfd5da255 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Dec 2021 02:19:44 +0000 Subject: [PATCH 20/72] Bump @types/react from 17.0.37 to 17.0.38 (#150) --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89a0cb8..bcb59fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "@testing-library/user-event": "^12.1.10", "@types/jest": "^26.0.24", "@types/node": "^16.11.14", - "@types/react": "^17.0.37", + "@types/react": "^17.0.38", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.11", "@types/react-highlight": "^0.12.5", @@ -3233,9 +3233,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "17.0.37", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.37.tgz", - "integrity": "sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg==", + "version": "17.0.38", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", + "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -21643,9 +21643,9 @@ "version": "1.5.4" }, "@types/react": { - "version": "17.0.37", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.37.tgz", - "integrity": "sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg==", + "version": "17.0.38", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", + "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", diff --git a/package.json b/package.json index 477938d..2bd7fc3 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@testing-library/user-event": "^12.1.10", "@types/jest": "^26.0.24", "@types/node": "^16.11.14", - "@types/react": "^17.0.37", + "@types/react": "^17.0.38", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.11", "@types/react-highlight": "^0.12.5", From 5ae7c0f6304914ebfcbd8dcb50b655379c36c741 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Dec 2021 02:20:34 +0000 Subject: [PATCH 21/72] Bump chart.js from 3.6.2 to 3.7.0 (#149) --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index bcb59fa..b17380d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "@types/react-dom": "^17.0.11", "@types/react-highlight": "^0.12.5", "@types/react-syntax-highlighter": "^13.5.2", - "chart.js": "^3.6.2", + "chart.js": "^3.7.0", "ethers": "^5.5.2", "highlightjs-solidity": "^2.0.3", "react": "^17.0.2", @@ -5689,9 +5689,9 @@ } }, "node_modules/chart.js": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.6.2.tgz", - "integrity": "sha512-Xz7f/fgtVltfQYWq0zL1Xbv7N2inpG+B54p3D5FSvpCdy3sM+oZhbqa42eNuYXltaVvajgX5UpKCU2GeeJIgxg==" + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz", + "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg==" }, "node_modules/check-types": { "version": "11.1.2", @@ -23348,9 +23348,9 @@ "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" }, "chart.js": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.6.2.tgz", - "integrity": "sha512-Xz7f/fgtVltfQYWq0zL1Xbv7N2inpG+B54p3D5FSvpCdy3sM+oZhbqa42eNuYXltaVvajgX5UpKCU2GeeJIgxg==" + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz", + "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg==" }, "check-types": { "version": "11.1.2" diff --git a/package.json b/package.json index 2bd7fc3..c4d371d 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@types/react-dom": "^17.0.11", "@types/react-highlight": "^0.12.5", "@types/react-syntax-highlighter": "^13.5.2", - "chart.js": "^3.6.2", + "chart.js": "^3.7.0", "ethers": "^5.5.2", "highlightjs-solidity": "^2.0.3", "react": "^17.0.2", From a54cc9b23bfe872e0047af3c7e76d5225f8d927f Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 4 Jan 2022 18:49:20 -0300 Subject: [PATCH 22/72] First draft of custom json-rpc API docs --- docs/custom-jsonrpc.md | 174 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 docs/custom-jsonrpc.md diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md new file mode 100644 index 0000000..b8d814a --- /dev/null +++ b/docs/custom-jsonrpc.md @@ -0,0 +1,174 @@ +# Otterscan JSON-RPC API extensions + +The [standard Ethereum JSON-RPC APIs](https://ethereum.org/en/developers/docs/apis/json-rpc/) are very limitting and in some cases non-performant for what you can do with an archive node. + +There is plenty of useful data that can be extracted and we implemented some extra RPC methods for them. + +They are all used by Otterscan, but we are documenting them here so others can try it, give feedback and eventually get it merged upstream if they are generalized enough. + +We take an incremental approach when design the APIs, so there may be some methods very specific to Otterscan use cases, others that look more generic. + +## Why don't you use _Some Product XXX_ for Otterscan? And why shouldn't I? + +If you are happy using _Some Product XXX_, go ahead. + +Otterscan pursues a minimalistic approach and at the same time it is very easy to modify Erigon for your own needs. + +Most of the features we implemented are quite basic and it is unfortunate they are not part of the standard API. + +> We believe most people end up using _Some Product XXX_ not because of its own unique features, but because the standard JSON-RPC API is quite limited even for basic features. + +Implementing everything in-node allows you to plug a dapp directly to your node itself. No need to install any additional indexer middleware or SQL database, each of it own consuming extra disk space and CPU. + +> Take Otterscan as an example, **ALL** you need is Otterscan itself (a SPA, can be served by any static provider) and our modified Erigon's rpcdaemon. + +## Method summary + +All methods are prefixed with the `ots_` namespace in order to make it clear it is vendor-specific and there is no name clash with other same-name implementations. + +| Name | Description | Reasoning | +|-------------------|------------------|-----------| +| `ots_getApiLevel` | Totally Otterscan internal API, absolutely no reason for anything outside Otterscan to use it. | Used by Otterscan to check if it's connecting to a compatible patched Erigon node and display a friendly message if it is not. | +| `ots_getInternalOperations` | Return the internal ETH transfers inside a transaction. | For complex contract interactions, there may be internal calls that forward ETH between addresses. A very common example is someone swapping some token for ETH, in this case there is a ETH send to the sender address which is only unveiled by examining the internal calls. | +| `ots_hasCode` | Check if a certain address contains a deployed code. | A common way to check if an address is a contract or an EOA is calling `eth_getCode` to see if it has some code deployed. However this call is expensive regarding this purpose, as it returns the entire contract code over the network just for the client to check its presence. This call just returns a boolean. | +| `ots_getTransactionError` | Extract the transaction raw error output. | In order to get the error message or custom error from a failed transaction, you need to get its error output and decoded it. This info is not exposed through standard APIs. | +| `ots_traceTransaction` | Extract all variations of calls, contract creation and self-destructs and returns a call tree. | This is an optimized version of tracing; regular tracing returns lots of data, and custom tracing using a JS tracer could be slow. | +| `ots_getBlockDetails` | Tailor-made and expanded version of `eth_getBlock*` for block details page in Otterscan. | The standard `eth_getBlock*` is quite verbose and it doesn't bring all info we need. We explicitly remove the transaction list (unnecessary for that page and also this call doesn't scale well), log blooms and other unnecessary fields. We add issuance and block fees info and return all of this in just one call. | +| `ots_getBlockTransactions` | Get paginated transactions for a certain block. Also remove some verbose fields like logs. | As block size increases, getting all transactions from a block at once doesn't scale, so the first point here is to add pagination support. The second point is that receipts may have big, unnecessary information, like logs. So we cap all of them to save network bandwidth. | +| `ots_searchTransactionsBefore` and `ots_searchTransactionsAfter` | Gets paginated inbound/outbound transaction calls for a certain address. | There is no native support for any kind of transaction search in the standard JSON-RPC API. We don't want to introduce an additional indexer middleware in Otterscan, so we implemented in-node search. | +| `ots_getTransactionBySenderAndNonce` | Gets the transaction hash for a certain sender address, given its nonce. | There is no native support for this search in the standard JSON-RPC API. Otterscan needs it to allow user navigation between nonces from the same sender address. | + +### `ots_getApiLevel` + +Very simple API versioning scheme. Every time we add a new capability, the number is incremented. This allows for Otterscan to check if the Erigon node contains all API it needs. + +Parameters: + +`` + +Returns: + +`number` containing the API version. + +### `ots_getInternalOperations` + +Trace internal ETH transfers, contracts creation (CREATE/CREATE2) and self-destructs for a certain transaction. + +Parameters: + +`txhash` - The transaction hash. + +Returns: + +`array` of operations, sorted by their occurrence inside the transaction. + +The operation is an object with the following fields: + +`type` - transfer (`0`), self-destruct (`1`), create (`2`) or create2 (`3`). + +`from` - the ETH sender, contract creator or contract address being self-destructed. + +`to` - the ETH receiver, newly created contract address or the target ETH receiver resulting of the self-destruction. + +`value` - the amount of ETH transferred. + +### `ots_hasCode` + +Check if an ETH address contains a deployed code. + +Parameters: + +`address` - The ETH address to be checked. + +`block` - The block number or "latest" to check the latest state. + +Returns: + +`boolean` indicating if the address contains a bytecode or not. + +### `ots_traceTransaction` + +Trace a transaction and generate a trace call tree. + +Parameters: + +`txhash` - The transaction hash. + +Returns: + +`object` containing the trace tree. + +### `ots_getTransactionError` + +Given a transaction hash, returns its raw revert reason. + +The returned byte blob should be ABI decoded in order to be presented to the user. + +For instance, the most common error format is a `string` revert message; in this case, it should be decoded using the `Error(string)` method selector, which will allow you to extract the string message. + +If it is not the case, it should probably be a solidity custom error, so you must have the custom error ABI in order to decoded it. + +Parameters: + +`txhash` - The transaction hash. + +Returns: + +`string` containing the hexadecimal-formatted error blob or simply a "0x" if the transaction was sucessfully executed. + +### `ots_getBlockDetails` + +Given a block number, return its data. Similar to the standard `eth_getBlockByNumber/Hash` method, but optimized. + +Parameters: + +`number` representing the desired block number. + +Returns: + +`object` in a format similar to the one returned by `eth_getBlockByNumber/Hash` (please refer to their docs), with some small differences: + +- the block data comes nested inside a `block` attribute. +- the `transactions` attribute is not returned. The reason is that it doesn't scale, the standard methods return either the transaction hash list or the transaction list with their bodies. So we cap the transaction list entirely to avoid unnecessary network traffic. +- the transaction count is returned in a `transactionCount` attribute. +- the `logsBloom` attribute comes with `null`. It is a byte blob thas is rarely used, so we cap it to avoid unnecessary network traffic. +- an extra `issuance` attribute returns an `object` with the fields: + - `blockReward` - the miner reward. + - `uncleReward` - the total reward issued to uncle blocks. + - `issuance` - the total ETH issued in this block (miner + uncle rewards). +- an extra `totalFees` attribute containing the sum of fees paid by senders in this block. Note that due to EIP-1559 this is **NOT** the same amount earned by the miner as block fees since it contains the amount paid as base fee. + +### `ots_getBlockTransactions` + +Gets paginated transaction data for a certain block. Think of an optimized `eth_getBlockBy*` + `eth_getTransactionReceipt`. + +The `transactions` field contains the transaction list with their bodies in a similar format of `eth_getBlockBy*` with transaction bodies, with a few differences: + +- the `input` field returns only the 4 bytes method selector instead of the entire calldata byte blob. + +The `receipts` attribute contains the transactions receipt list, in the same sort order as the block transactions. Returning it here avoid the caller of making N+1 calls (`eth_getBlockBy*` and `eth_getTransactionReceipt`). + +For receipts, it contains some differences from the `eth_getTransactionReceipt` object format: + +- `logs` attribute returns `null`. +- `logsBloom` attribute returns `null`. + +### `ots_searchTransactionsBefore` and `ots_searchTransactionsAfter` + +These are address history navigation methods. They are similar, the difference is `ots_searchTransactionsBefore` searches the history backwards and `ots_searchTransactionsAfter` searches forward a certain point in time. + +They are paginated, you **MUST** inform the page size. Some addresses like exchange addresses or very popular DeFi contracts like Uniswap Router will return millions of results. + +### `ots_getTransactionBySenderAndNonce` + +Given a sender address and a nonce, returns the tx hash or `null` if not found. It returns only the tx hash on success, you can use the standard `eth_getTransactionByHash` after that to get the full transaction data. + +Parameters: + +`sender` - The sender ETH address. + +`nonce` - The sender nonce. + +Returns: + +`string` containing the corresponding transaction hash or `null` if it doesn't exist. From 75031aa324969f44d6d91b33591e714044030ce8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jan 2022 21:53:20 +0000 Subject: [PATCH 23/72] Bump swr from 1.1.1 to 1.1.2 (#151) --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index b17380d..4458149 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", - "swr": "^1.1.1", + "swr": "^1.1.2", "typescript": "^4.5.4", "use-keyboard-shortcut": "^1.1.0", "web-vitals": "^1.0.1" @@ -17207,9 +17207,9 @@ } }, "node_modules/swr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/swr/-/swr-1.1.1.tgz", - "integrity": "sha512-ZpUHyU3N3snj2QGFeE2Fd3BXl1CVS6YQIQGb1ttPAkTmvwZqDyV3GRMNPsaeAYCBM74tfn4XbKx28FVQR0mS7Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swr/-/swr-1.1.2.tgz", + "integrity": "sha512-UsM0eo5T+kRPyWFZtWRx2XR5qzohs/LS4lDC0GCyLpCYFmsfTk28UCVDbOE9+KtoXY4FnwHYiF+ZYEU3hnJ1lQ==", "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } @@ -31079,9 +31079,9 @@ } }, "swr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/swr/-/swr-1.1.1.tgz", - "integrity": "sha512-ZpUHyU3N3snj2QGFeE2Fd3BXl1CVS6YQIQGb1ttPAkTmvwZqDyV3GRMNPsaeAYCBM74tfn4XbKx28FVQR0mS7Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swr/-/swr-1.1.2.tgz", + "integrity": "sha512-UsM0eo5T+kRPyWFZtWRx2XR5qzohs/LS4lDC0GCyLpCYFmsfTk28UCVDbOE9+KtoXY4FnwHYiF+ZYEU3hnJ1lQ==", "requires": {} }, "symbol-tree": { diff --git a/package.json b/package.json index c4d371d..6b62a8a 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", - "swr": "^1.1.1", + "swr": "^1.1.2", "typescript": "^4.5.4", "use-keyboard-shortcut": "^1.1.0", "web-vitals": "^1.0.1" From c41f3cd630f5853d6bba5db22bd41ed2bf84c6f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jan 2022 21:55:46 +0000 Subject: [PATCH 24/72] Bump use-keyboard-shortcut from 1.1.0 to 1.1.2 (#152) --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4458149..1a17078 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "serve": "^13.0.2", "swr": "^1.1.2", "typescript": "^4.5.4", - "use-keyboard-shortcut": "^1.1.0", + "use-keyboard-shortcut": "^1.1.2", "web-vitals": "^1.0.1" }, "devDependencies": { @@ -18373,9 +18373,9 @@ } }, "node_modules/use-keyboard-shortcut": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.1.0.tgz", - "integrity": "sha512-92CzhdDqdH6518HHCpq1Nuey5RpJ2MuI6E60Y9Pl8G5hPZpGnOY4IrNttsTafDvser4uutfkepksE4MHzEczwg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.1.2.tgz", + "integrity": "sha512-VrAu1avPLuFHShGo1RiPtCZ6htwsnLRlZc/w4+jmK99HCvIGG5WThz1KsoGgP8KCxP5c8a+pDjtnCgBg+3bXzA==", "peerDependencies": { "react": "^16.8.0", "react-dom": "^16.8.0" @@ -31864,9 +31864,9 @@ "version": "3.1.1" }, "use-keyboard-shortcut": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.1.0.tgz", - "integrity": "sha512-92CzhdDqdH6518HHCpq1Nuey5RpJ2MuI6E60Y9Pl8G5hPZpGnOY4IrNttsTafDvser4uutfkepksE4MHzEczwg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.1.2.tgz", + "integrity": "sha512-VrAu1avPLuFHShGo1RiPtCZ6htwsnLRlZc/w4+jmK99HCvIGG5WThz1KsoGgP8KCxP5c8a+pDjtnCgBg+3bXzA==", "requires": {} }, "util": { diff --git a/package.json b/package.json index 6b62a8a..be1adae 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "serve": "^13.0.2", "swr": "^1.1.2", "typescript": "^4.5.4", - "use-keyboard-shortcut": "^1.1.0", + "use-keyboard-shortcut": "^1.1.2", "web-vitals": "^1.0.1" }, "scripts": { From 738303301c0188b8098e63170c90eecb32f34e6b Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 4 Jan 2022 19:12:25 -0300 Subject: [PATCH 25/72] Update 4bytes repo --- 4bytes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/4bytes b/4bytes index 2053752..0ee722e 160000 --- a/4bytes +++ b/4bytes @@ -1 +1 @@ -Subproject commit 20537524bfb01bee859c9cfa9a8784baacbcc7ae +Subproject commit 0ee722e516c91dc6a3de01c26ea06955123eeddb From 8e471e82c2679229c07715530052ed97d6caecc5 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 4 Jan 2022 19:13:42 -0300 Subject: [PATCH 26/72] Update trustwallet assets --- trustwallet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trustwallet b/trustwallet index e779c7b..0e2488c 160000 --- a/trustwallet +++ b/trustwallet @@ -1 +1 @@ -Subproject commit e779c7b400fc479f8442066f13565555be5bfcf3 +Subproject commit 0e2488c4b4c366c0ed54d5d85b2feaa0f0940b05 From a1bfd3a7568e8f9982df6efd3e5c5b3b27dc596d Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 4 Jan 2022 21:53:06 -0300 Subject: [PATCH 27/72] Update topic0 db to v0.2.3 --- topic0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topic0 b/topic0 index 63794c4..cb6abe8 160000 --- a/topic0 +++ b/topic0 @@ -1 +1 @@ -Subproject commit 63794c46467dea47fd99ec47db745c482887367e +Subproject commit cb6abe87055d2e2d54ba6a985903031420c4cbb1 From 0c2d862e660d1213c7d8fa20f831ced8b411117b Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 15 Jan 2022 23:21:39 -0300 Subject: [PATCH 28/72] Add docs for ots_search* methods --- docs/custom-jsonrpc.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index b8d814a..6083a2f 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -8,7 +8,9 @@ They are all used by Otterscan, but we are documenting them here so others can t We take an incremental approach when design the APIs, so there may be some methods very specific to Otterscan use cases, others that look more generic. -## Why don't you use _Some Product XXX_ for Otterscan? And why shouldn't I? +## FAQ + +### Why don't you use _Some Product XXX_ for Otterscan? And why shouldn't I? If you are happy using _Some Product XXX_, go ahead. @@ -22,6 +24,14 @@ Implementing everything in-node allows you to plug a dapp directly to your node > Take Otterscan as an example, **ALL** you need is Otterscan itself (a SPA, can be served by any static provider) and our modified Erigon's rpcdaemon. +### But your API doesn't scale and it is slower than _Some Product XXX_!!! + +Not everyone needs to serve thousands of request per second. Go ahead and use _Some Product XXX_. + +Some people just want to run standalone development tools and calculating some data on-the-fly works fine for single user local apps. + +Even so, we may introduce custom indexes for some operations in future if there is such demand, so you may opt-in for a better performance by spending more disk space. + ## Method summary All methods are prefixed with the `ots_` namespace in order to make it clear it is vendor-specific and there is no name clash with other same-name implementations. @@ -159,6 +169,25 @@ These are address history navigation methods. They are similar, the difference i They are paginated, you **MUST** inform the page size. Some addresses like exchange addresses or very popular DeFi contracts like Uniswap Router will return millions of results. +Parameters: + +`address` - The ETH address to be searched. +`blockNumber` - It searches for occurrences of `address` starting from `blockNumber` (inclusive). A value of `0` means you want to search from the most recent block (`ots_searchTransactionsBefore`) or from the genesis (`ots_searchTransactionsAfter`). +`pageSize` - How many transactions it may return. See the detailed explanation about this parameter bellow. + +Returns: + +`object` containing two attributes: + +- `txs` - An array of objects representing the transaction results. The results are returned sorted from the most recent to the older one (descending order). +- `receipts` - An array of objects containing the transaction receipts for the transactions returned in the `txs` attribute. +- `firstPage` - Boolean indicating this is the first page. It should be `true` when calling `ots_searchTransactionsBefore` with `blockNumber` == 0 (search from `latest`); because the results are in descending order, the search from the most recent block is the "first" one. It should also return `true` when calling `ots_searchTransactionsAfter` with a `blockNumber` which results in no more transactions after the returned ones because it searched forward up to the tip of the chain. +- `lastPage` - Boolean indicating this is the last page. It should be `true` when calling `ots_searchTransactionsAfter` with `blockNumber` == 0 (search from genesis); because the results are in descending order, the genesis page is the "last" one. It should also return `true` when calling `ots_searchTransactionsBefore` with a `blockNumber` which results in no more transactions before the returned ones because it searched backwards up to the genesis block. + +There is a small gotcha regarding `pageSize`. If there are less results than `pageSize`, they are just returned as is. + +But if there are more than `pageSize` results, they are capped by the last found block. For example, let's say you are searching for Uniswap Router address and it already found 24 matches; it then looks at the next block containing this addresses occurrences and there are 5 matches inside the block. They are all returned, so it returns 30 transaction results. The caller code should be aware of this. + ### `ots_getTransactionBySenderAndNonce` Given a sender address and a nonce, returns the tx hash or `null` if not found. It returns only the tx hash on success, you can use the standard `eth_getTransactionByHash` after that to get the full transaction data. From 0e83ec307cefe3b20407c2c58db5f0f314b65014 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 16:02:33 -0300 Subject: [PATCH 29/72] Update estimated Erigon disk usage/sync time --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1aef5b..4c452f5 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ It depends heavily on a working Erigon installation with Otterscan patches appli You will need an Erigon executing node (`erigon`). Also you will need Erigon RPC daemon (`rpcdaemon`) with Otterscan patches. Since setting up an Erigon environment itself can take some work, make sure to follow their instructions and have a working archive node before continuing. -My personal experience: at the moment of this writing (~block 12,700,000), setting up an archive node takes over 5-6 days and ~1.3 TB of SSD. +My personal experience: at the moment of this writing (~block 14,000,000), setting up an archive node takes over 5-6 days and ~1.7 TB of SSD. They have weekly stable releases, make sure you are running on of them, not development ones. From 71b76f77441e073094aeb289548758d2612cd6e6 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 16:13:51 -0300 Subject: [PATCH 30/72] Update patched Erigon run instructions --- README.md | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 4c452f5..97aa3ef 100644 --- a/README.md +++ b/README.md @@ -68,35 +68,15 @@ My personal experience: at the moment of this writing (~block 14,000,000), setti They have weekly stable releases, make sure you are running on of them, not development ones. -### Install Otterscan patches on top of Erigon +### Install Otterscan-patched rpcdaemon -Add our forked Erigon git tree as an additional remote and checkout the corresponding branch. +We rely on custom JSON-RPC APIs which are not available in a standard ETH node. We keep a separated repository containing an Erigon fork here: https://github.com/wmitsuda/erigon. -The repository with Otterscan patches is [here](https://github.com/wmitsuda/erigon). +Please follow the instructions in the repository `README` and replace the original Erigon `rpcdaemon` with our patched one. -``` -git remote add otterscan https://github.com/wmitsuda/erigon.git -``` +### Enable Otterscan namespace on rpcdaemon -Checkout the tag corresponding to the stable version you are running. For each supported Erigon version, there should be a corresponding tag containing Otterscan patches. - -For example, if you are running Erigon from `v2021.07.01` tag, checkout the tag `v2021.07.01-otterscan` and rebuild `rpcdaemon`. - -We intend to release a compatible rebased version containing our changes every week just after Erigon's weekly release, as time permits. - -``` -git fetch --all -git fetch otterscan --tags -git checkout -``` - -Build the patched `rpcdaemon` binary. - -``` -make rpcdaemon -``` - -Run it paying attention to enable the `erigon`, `ots`, `eth` apis to whatever cli options you are using to start `rpcdaemon`. +When running `rpcdaemon`, make sure to enable the `erigon`, `ots`, `eth` APIs in addition to whatever cli options you are using to start `rpcdaemon`. `ots` stands for Otterscan and it is the namespace we use for our own custom APIs. @@ -106,9 +86,9 @@ Run it paying attention to enable the `erigon`, `ots`, `eth` apis to whatever cl Be sure to include both `--private.api.addr` and `--datadir` parameter so you run it in dual mode, otherwise the performance will be much worse. -Also pay attention to the `--http.corsdomain` parameter, CORS is required for the browser to call the node directly. +Also pay attention to the `--http.corsdomain` parameter, CORS is **required** for the browser to call the node directly. -Now you should have an Erigon node with Otterscan jsonrpc APIs enabled, running in dual mode with CORS enabled. +Now you should have an Erigon node with Otterscan JSON-RPC APIs enabled, running in dual mode with CORS enabled. ### Run Otterscan docker image from Docker Hub From ccab6d90c658a96845ebf698db8fea31b89ec986 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 16:24:44 -0300 Subject: [PATCH 31/72] Split install instructions page --- README.md | 64 ++----------------------------------------------- docs/install.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 62 deletions(-) create mode 100644 docs/install.md diff --git a/README.md b/README.md index 97aa3ef..6c051a2 100644 --- a/README.md +++ b/README.md @@ -56,69 +56,9 @@ However, you will see that we made many UI improvements. ## Install instructions -This software is currently distributed as a docker image. +[Here](./docs/install.md). -It depends heavily on a working Erigon installation with Otterscan patches applied, so let's begin with it first. - -### Install Erigon - -You will need an Erigon executing node (`erigon`). Also you will need Erigon RPC daemon (`rpcdaemon`) with Otterscan patches. Since setting up an Erigon environment itself can take some work, make sure to follow their instructions and have a working archive node before continuing. - -My personal experience: at the moment of this writing (~block 14,000,000), setting up an archive node takes over 5-6 days and ~1.7 TB of SSD. - -They have weekly stable releases, make sure you are running on of them, not development ones. - -### Install Otterscan-patched rpcdaemon - -We rely on custom JSON-RPC APIs which are not available in a standard ETH node. We keep a separated repository containing an Erigon fork here: https://github.com/wmitsuda/erigon. - -Please follow the instructions in the repository `README` and replace the original Erigon `rpcdaemon` with our patched one. - -### Enable Otterscan namespace on rpcdaemon - -When running `rpcdaemon`, make sure to enable the `erigon`, `ots`, `eth` APIs in addition to whatever cli options you are using to start `rpcdaemon`. - -`ots` stands for Otterscan and it is the namespace we use for our own custom APIs. - -``` -./build/bin/rpcdaemon --http.api "eth,erigon,ots," --private.api.addr 127.0.0.1:9090 --datadir --http.corsdomain "*" -``` - -Be sure to include both `--private.api.addr` and `--datadir` parameter so you run it in dual mode, otherwise the performance will be much worse. - -Also pay attention to the `--http.corsdomain` parameter, CORS is **required** for the browser to call the node directly. - -Now you should have an Erigon node with Otterscan JSON-RPC APIs enabled, running in dual mode with CORS enabled. - -### Run Otterscan docker image from Docker Hub - -The Otterscan official repo on Docker Hub is [here](https://hub.docker.com/orgs/otterscan/repositories). - -``` -docker run --rm -p 5000:80 --name otterscan -d otterscan/otterscan: -``` - -This will download the Otterscan image from Docker Hub, run it locally using the default parameters, binding it to port 5000 (see the `-p` docker run parameter). - -To stop Otterscan service, run: - -``` -docker stop otterscan -``` - -By default it assumes your Erigon node is at `http://127.0.0.1:8545`. You can override the URL by setting the `ERIGON_URL` env variable on `docker run`: - -``` -docker run --rm -p 5000:80 --name otterscan -d --env ERIGON_URL="" otterscan/otterscan: -``` - -This is the preferred way to run Otterscan. You can read about other ways [here](docs/other-ways-to-run-otterscan.md). - -## Validating the installation (all methods) - -You can make sure it is working correctly if the homepage is able to show the latest block/timestamp your Erigon node is at just bellow the search button. - -## Source verification +## Contract verification We make use of [Sourcify](https://sourcify.dev/) for displaying contract verification info. diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..e092834 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,63 @@ +# Install instructions + +This software is currently distributed as a docker image. + +It depends heavily on a working Erigon installation with Otterscan patches applied, so let's begin with it first. + +## Install Erigon + +You will need an Erigon executing node (`erigon`). Also you will need Erigon RPC daemon (`rpcdaemon`) with Otterscan patches. Since setting up an Erigon environment itself can take some work, make sure to follow their instructions and have a working archive node before continuing. + +My personal experience: at the moment of this writing (~block 14,000,000), setting up an archive node takes over 5-6 days and ~1.7 TB of SSD. + +They have weekly stable releases, make sure you are running on of them, not development ones. + +## Install Otterscan-patched rpcdaemon + +We rely on custom JSON-RPC APIs which are not available in a standard ETH node. We keep a separated repository containing an Erigon fork here: https://github.com/wmitsuda/erigon. + +Please follow the instructions in the repository `README` and replace the original Erigon `rpcdaemon` with our patched one. + +## Enable Otterscan namespace on rpcdaemon + +When running `rpcdaemon`, make sure to enable the `erigon`, `ots`, `eth` APIs in addition to whatever cli options you are using to start `rpcdaemon`. + +`ots` stands for Otterscan and it is the namespace we use for our own custom APIs. + +``` +/rpcdaemon --http.api "eth,erigon,ots," --private.api.addr 127.0.0.1:9090 --datadir --http.corsdomain "*" +``` + +Be sure to include both `--private.api.addr` and `--datadir` parameter so you run it in dual mode, otherwise the performance will be much worse. + +Also pay attention to the `--http.corsdomain` parameter, CORS is **required** for the browser to call the node directly. + +Now you should have an Erigon node with Otterscan JSON-RPC APIs enabled, running in dual mode with CORS enabled. + +## Run Otterscan docker image from Docker Hub + +The Otterscan official repo on Docker Hub is [here](https://hub.docker.com/orgs/otterscan/repositories). + +``` +docker run --rm -p 5000:80 --name otterscan -d otterscan/otterscan: +``` + +This will download the Otterscan image from Docker Hub, run it locally using the default parameters, binding it to port 5000 (see the `-p` docker run parameter). + +To stop Otterscan service, run: + +``` +docker stop otterscan +``` + +By default it assumes your Erigon node is at `http://127.0.0.1:8545`. You can override the URL by setting the `ERIGON_URL` env variable on `docker run`: + +``` +docker run --rm -p 5000:80 --name otterscan -d --env ERIGON_URL="" otterscan/otterscan: +``` + +This is the preferred way to run Otterscan. You can read about other ways [here](./other-ways-to-run-otterscan.md). + +## Validating the installation (all methods) + +You can make sure it is working correctly if the homepage is able to show the latest block/timestamp your Erigon node is at just bellow the search button. From 0ac907c59d382e853ddc8ff13ae89b5ddab5b0c6 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 16:27:48 -0300 Subject: [PATCH 32/72] Improve formatting --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 6c051a2..0e345be 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,7 @@ However, you will see that we made many UI improvements. ## Contract verification -We make use of [Sourcify](https://sourcify.dev/) for displaying contract verification info. - -More info [here](docs/sourcify.md). +We make use of [Sourcify](https://sourcify.dev/) for displaying contract verification info. More info [here](docs/sourcify.md). ## Kudos (in no particular order) From 678ade43413ea71aee76e442e1bc7af08dc27ae0 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 16:31:07 -0300 Subject: [PATCH 33/72] Expose custom jsonrpc docs in README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0e345be..ceab17e 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,10 @@ However, you will see that we made many UI improvements. We make use of [Sourcify](https://sourcify.dev/) for displaying contract verification info. More info [here](docs/sourcify.md). +## Otterscan JSON-RPC API extensions + +We implemented new JSON-RPC APIs to expose information not available in a standard ETH node. They can be useful for non-Otterscan users and their specification is available [here](./docs/custom-jsonrpc.md). + ## Kudos (in no particular order) We make use of many open-source software and integrate many public datasources, mainly: From 8a523a39d52c845627d6a7a3568322cf7b200564 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 17:02:38 -0300 Subject: [PATCH 34/72] Add more docs --- docs/custom-jsonrpc.md | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index 6083a2f..54cefe1 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -1,6 +1,6 @@ # Otterscan JSON-RPC API extensions -The [standard Ethereum JSON-RPC APIs](https://ethereum.org/en/developers/docs/apis/json-rpc/) are very limitting and in some cases non-performant for what you can do with an archive node. +The [standard Ethereum JSON-RPC APIs](https://ethereum.org/en/developers/docs/apis/json-rpc/) are very limited and in some cases non-performant for what you can do with an archive node. There is plenty of useful data that can be extracted and we implemented some extra RPC methods for them. @@ -8,7 +8,7 @@ They are all used by Otterscan, but we are documenting them here so others can t We take an incremental approach when design the APIs, so there may be some methods very specific to Otterscan use cases, others that look more generic. -## FAQ +## Quick FAQ ### Why don't you use _Some Product XXX_ for Otterscan? And why shouldn't I? @@ -26,11 +26,39 @@ Implementing everything in-node allows you to plug a dapp directly to your node ### But your API doesn't scale and it is slower than _Some Product XXX_!!! -Not everyone needs to serve thousands of request per second. Go ahead and use _Some Product XXX_. +Not everyone needs to serve thousands of requests per second. Go ahead and use _Some Product XXX_. Some people just want to run standalone development tools and calculating some data on-the-fly works fine for single user local apps. -Even so, we may introduce custom indexes for some operations in future if there is such demand, so you may opt-in for a better performance by spending more disk space. +Even so, we may introduce custom indexes to speed up some operations in future if there is such demand, so you may opt-in for a better performance by spending more disk space. + +### Wen PR upstream? + +API design is hard and once it goes public you have to support it forever. For this reason we are primarily keeping it in our own fork and under a vendor specific namespace (`ots_`). + +Also, the quality level of the current APIs differs, some are very generic, some are very Otterscan specific. Our API design has been driven mainly by Otterscan feature needs, which is a good thing (tm), so no useless features. + +Having said that, we want to have people experimenting with our APIs, bringing other use cases, and driving the API evolution. If there are enough users vouching for a certain feature, we would gladly submit a PR to Erigon upstream repo. + +The first step to achieving that is having this own page properly documenting our APIs so people don't have to look at our source code 😅. + +Your feedback is important, please get in touch using our communication channels. + +## How to use it? + +They are all JSON-RPC methods, so your favorite web3 library _should_ have some way to custom call them. + +For example, ethers.js wraps standard calls in nice, user-friendly classes and parses results into easy-to-use objects, but also allows you to do custom calls and get raw results while still taking advantage of their capabilities like automatic batching, network timeout handling, etc. + +I'll use ethers.js as an example here because it is what I use in Otterscan, please check your web3 library docs for custom call support. + +Let's call the `ots_getTransactionError` method to obtain the revert reason of a failed transaction. It accepts one string parameter containing the transaction hash and returns a byte blob that can be ABI-decoded: + +``` +const provider = ...; // Obtain a JsonRpcProvider object +const txHash = "..."; // Set the transaction hash +const result = (await provider.send("ots_getTransactionError", [txHash])) as string; +``` ## Method summary From 9d014b20c509084624d36434834ff55ebd5ae327 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 17:09:54 -0300 Subject: [PATCH 35/72] Add more explanation to search methods --- docs/custom-jsonrpc.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index 54cefe1..01b622d 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -197,10 +197,12 @@ These are address history navigation methods. They are similar, the difference i They are paginated, you **MUST** inform the page size. Some addresses like exchange addresses or very popular DeFi contracts like Uniswap Router will return millions of results. +They return inbound (`to`), outbound (`from`) and "internal" transactions. By internal it means that if a transaction calls a contract and somewhere in the call stack it sends ETH to the address you are searching for or the address is a contract and it calls a method on it, the transaction is matched and returned in the search results. + Parameters: `address` - The ETH address to be searched. -`blockNumber` - It searches for occurrences of `address` starting from `blockNumber` (inclusive). A value of `0` means you want to search from the most recent block (`ots_searchTransactionsBefore`) or from the genesis (`ots_searchTransactionsAfter`). +`blockNumber` - It searches for occurrences of `address` before/after `blockNumber`. A value of `0` means you want to search from the most recent block (`ots_searchTransactionsBefore`) or from the genesis (`ots_searchTransactionsAfter`). `pageSize` - How many transactions it may return. See the detailed explanation about this parameter bellow. Returns: From 00da18463b58f93ff4e3116e802aa3e86de57f7d Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 20:36:21 -0300 Subject: [PATCH 36/72] Improve formatting --- docs/custom-jsonrpc.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index 01b622d..0e7d624 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -86,7 +86,7 @@ Parameters: Returns: -`number` containing the API version. +1. `number` containing the API version. ### `ots_getInternalOperations` @@ -94,21 +94,18 @@ Trace internal ETH transfers, contracts creation (CREATE/CREATE2) and self-destr Parameters: -`txhash` - The transaction hash. +1. `txhash` - The transaction hash. Returns: -`array` of operations, sorted by their occurrence inside the transaction. +1. `array` of operations, sorted by their occurrence inside the transaction. The operation is an object with the following fields: -`type` - transfer (`0`), self-destruct (`1`), create (`2`) or create2 (`3`). - -`from` - the ETH sender, contract creator or contract address being self-destructed. - -`to` - the ETH receiver, newly created contract address or the target ETH receiver resulting of the self-destruction. - -`value` - the amount of ETH transferred. +- `type` - transfer (`0`), self-destruct (`1`), create (`2`) or create2 (`3`). +- `from` - the ETH sender, contract creator or contract address being self-destructed. +- `to` - the ETH receiver, newly created contract address or the target ETH receiver resulting of the self-destruction. +- `value` - the amount of ETH transferred. ### `ots_hasCode` @@ -116,13 +113,12 @@ Check if an ETH address contains a deployed code. Parameters: -`address` - The ETH address to be checked. - -`block` - The block number or "latest" to check the latest state. +1. `address` - The ETH address to be checked. +2. `block` - The block number or "latest" to check the latest state. Returns: -`boolean` indicating if the address contains a bytecode or not. +1. `boolean` indicating if the address contains a bytecode or not. ### `ots_traceTransaction` @@ -130,11 +126,11 @@ Trace a transaction and generate a trace call tree. Parameters: -`txhash` - The transaction hash. +1. `txhash` - The transaction hash. Returns: -`object` containing the trace tree. +1. `object` containing the trace tree. ### `ots_getTransactionError` From db5d23fe54079201584dbc872bb27582bc2e6e0a Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 20:42:36 -0300 Subject: [PATCH 37/72] Improve formatting --- docs/custom-jsonrpc.md | 59 ++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index 0e7d624..ecaecba 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -86,7 +86,7 @@ Parameters: Returns: -1. `number` containing the API version. +- `number` containing the API version. ### `ots_getInternalOperations` @@ -98,7 +98,7 @@ Parameters: Returns: -1. `array` of operations, sorted by their occurrence inside the transaction. +- `array` of operations, sorted by their occurrence inside the transaction. The operation is an object with the following fields: @@ -118,7 +118,7 @@ Parameters: Returns: -1. `boolean` indicating if the address contains a bytecode or not. +- `boolean` indicating if the address contains a bytecode or not. ### `ots_traceTransaction` @@ -130,7 +130,7 @@ Parameters: Returns: -1. `object` containing the trace tree. +- `object` containing the trace tree. ### `ots_getTransactionError` @@ -144,11 +144,11 @@ If it is not the case, it should probably be a solidity custom error, so you mus Parameters: -`txhash` - The transaction hash. +1. `txhash` - The transaction hash. Returns: -`string` containing the hexadecimal-formatted error blob or simply a "0x" if the transaction was sucessfully executed. +- `string` containing the hexadecimal-formatted error blob or simply a "0x" if the transaction was sucessfully executed. ### `ots_getBlockDetails` @@ -156,21 +156,20 @@ Given a block number, return its data. Similar to the standard `eth_getBlockByNu Parameters: -`number` representing the desired block number. +1. `number` representing the desired block number. Returns: -`object` in a format similar to the one returned by `eth_getBlockByNumber/Hash` (please refer to their docs), with some small differences: - -- the block data comes nested inside a `block` attribute. -- the `transactions` attribute is not returned. The reason is that it doesn't scale, the standard methods return either the transaction hash list or the transaction list with their bodies. So we cap the transaction list entirely to avoid unnecessary network traffic. -- the transaction count is returned in a `transactionCount` attribute. -- the `logsBloom` attribute comes with `null`. It is a byte blob thas is rarely used, so we cap it to avoid unnecessary network traffic. -- an extra `issuance` attribute returns an `object` with the fields: - - `blockReward` - the miner reward. - - `uncleReward` - the total reward issued to uncle blocks. - - `issuance` - the total ETH issued in this block (miner + uncle rewards). -- an extra `totalFees` attribute containing the sum of fees paid by senders in this block. Note that due to EIP-1559 this is **NOT** the same amount earned by the miner as block fees since it contains the amount paid as base fee. +- `object` in a format _similar_ to the one returned by `eth_getBlockByNumber/Hash` (please refer to their docs), with some small differences: + - the block data comes nested inside a `block` attribute. + - the `transactions` attribute is not returned. The reason is that it doesn't scale, the standard methods return either the transaction hash list or the transaction list with their bodies. So we cap the transaction list entirely to avoid unnecessary network traffic. + - the transaction count is returned in a `transactionCount` attribute. + - the `logsBloom` attribute comes with `null`. It is a byte blob thas is rarely used, so we cap it to avoid unnecessary network traffic. + - an extra `issuance` attribute returns an `object` with the fields: + - `blockReward` - the miner reward. + - `uncleReward` - the total reward issued to uncle blocks. + - `issuance` - the total ETH issued in this block (miner + uncle rewards). + - an extra `totalFees` attribute containing the sum of fees paid by senders in this block. Note that due to EIP-1559 this is **NOT** the same amount earned by the miner as block fees since it contains the amount paid as base fee. ### `ots_getBlockTransactions` @@ -197,18 +196,17 @@ They return inbound (`to`), outbound (`from`) and "internal" transactions. By in Parameters: -`address` - The ETH address to be searched. -`blockNumber` - It searches for occurrences of `address` before/after `blockNumber`. A value of `0` means you want to search from the most recent block (`ots_searchTransactionsBefore`) or from the genesis (`ots_searchTransactionsAfter`). -`pageSize` - How many transactions it may return. See the detailed explanation about this parameter bellow. +1. `address` - The ETH address to be searched. +2. `blockNumber` - It searches for occurrences of `address` before/after `blockNumber`. A value of `0` means you want to search from the most recent block (`ots_searchTransactionsBefore`) or from the genesis (`ots_searchTransactionsAfter`). +3. `pageSize` - How many transactions it may return. See the detailed explanation about this parameter bellow. Returns: -`object` containing two attributes: - -- `txs` - An array of objects representing the transaction results. The results are returned sorted from the most recent to the older one (descending order). -- `receipts` - An array of objects containing the transaction receipts for the transactions returned in the `txs` attribute. -- `firstPage` - Boolean indicating this is the first page. It should be `true` when calling `ots_searchTransactionsBefore` with `blockNumber` == 0 (search from `latest`); because the results are in descending order, the search from the most recent block is the "first" one. It should also return `true` when calling `ots_searchTransactionsAfter` with a `blockNumber` which results in no more transactions after the returned ones because it searched forward up to the tip of the chain. -- `lastPage` - Boolean indicating this is the last page. It should be `true` when calling `ots_searchTransactionsAfter` with `blockNumber` == 0 (search from genesis); because the results are in descending order, the genesis page is the "last" one. It should also return `true` when calling `ots_searchTransactionsBefore` with a `blockNumber` which results in no more transactions before the returned ones because it searched backwards up to the genesis block. +- `object` containing the following attributes: + - `txs` - An array of objects representing the transaction results. The results are returned sorted from the most recent to the older one (descending order). + - `receipts` - An array of objects containing the transaction receipts for the transactions returned in the `txs` attribute. + - `firstPage` - Boolean indicating this is the first page. It should be `true` when calling `ots_searchTransactionsBefore` with `blockNumber` == 0 (search from `latest`); because the results are in descending order, the search from the most recent block is the "first" one. It should also return `true` when calling `ots_searchTransactionsAfter` with a `blockNumber` which results in no more transactions after the returned ones because it searched forward up to the tip of the chain. + - `lastPage` - Boolean indicating this is the last page. It should be `true` when calling `ots_searchTransactionsAfter` with `blockNumber` == 0 (search from genesis); because the results are in descending order, the genesis page is the "last" one. It should also return `true` when calling `ots_searchTransactionsBefore` with a `blockNumber` which results in no more transactions before the returned ones because it searched backwards up to the genesis block. There is a small gotcha regarding `pageSize`. If there are less results than `pageSize`, they are just returned as is. @@ -220,10 +218,9 @@ Given a sender address and a nonce, returns the tx hash or `null` if not found. Parameters: -`sender` - The sender ETH address. - -`nonce` - The sender nonce. +1. `sender` - The sender ETH address. +2. `nonce` - The sender nonce. Returns: -`string` containing the corresponding transaction hash or `null` if it doesn't exist. +- `string` containing the corresponding transaction hash or `null` if it doesn't exist. From 5e61cb4ffdd633d61ddbd1bccfce72f3276c2011 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 21:13:51 -0300 Subject: [PATCH 38/72] Add examples --- docs/custom-jsonrpc.md | 112 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index ecaecba..fab9671 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -76,6 +76,10 @@ All methods are prefixed with the `ots_` namespace in order to make it clear it | `ots_searchTransactionsBefore` and `ots_searchTransactionsAfter` | Gets paginated inbound/outbound transaction calls for a certain address. | There is no native support for any kind of transaction search in the standard JSON-RPC API. We don't want to introduce an additional indexer middleware in Otterscan, so we implemented in-node search. | | `ots_getTransactionBySenderAndNonce` | Gets the transaction hash for a certain sender address, given its nonce. | There is no native support for this search in the standard JSON-RPC API. Otterscan needs it to allow user navigation between nonces from the same sender address. | +## Method details + +> Some methods include a sample call so you call try it from cli. The examples use `curl` and assume you are running `rpcdaemon` at `http://127.0.0.1:8545`. + ### `ots_getApiLevel` Very simple API versioning scheme. Every time we add a new capability, the number is incremented. This allows for Otterscan to check if the Erigon node contains all API it needs. @@ -114,12 +118,48 @@ Check if an ETH address contains a deployed code. Parameters: 1. `address` - The ETH address to be checked. -2. `block` - The block number or "latest" to check the latest state. +2. `block` - The block number at which the code presence will be checked or "latest" to check the latest state. Returns: - `boolean` indicating if the address contains a bytecode or not. +Example 1: does Uniswap V1 Router address have a code deployed? (yes, it is a contract) + +Request: + +``` +$ curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "id": 1, "method":"ots_hasCode","params":["0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95", "latest"]}' http://127.0.0.1:8545 +``` + +Response: + +``` +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +``` + +Example 2: does Vitalik's public address have a code deployed? (no, it is an EOA) + +Request: + +``` +$ curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "id": 1, "method":"ots_hasCode","params":["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest"]}' http://127.0.0.1:8545 +``` + +Response: + +``` +{ + "jsonrpc": "2.0", + "id": 1, + "result": false +} +``` + ### `ots_traceTransaction` Trace a transaction and generate a trace call tree. @@ -148,7 +188,26 @@ Parameters: Returns: -- `string` containing the hexadecimal-formatted error blob or simply a "0x" if the transaction was sucessfully executed. +- `string` containing the hexadecimal-formatted error blob or simply a "0x" if the transaction was sucessfully executed. It is returns "0x" if it failed with no revert reason or out of gas, make sure to analyze this return value together with the transaction success/fail result. + +Example: get the revert reason of a random transaction spotted in the wild to Uniswap V3. + +Request: + +``` +curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "id": 1, "method":"ots_getTransactionError","params":["0xcdb0e53c4f1b5f37ea7f0d2a8428b13a5bff47fb457d11ef9bc85ccdc489635b"]}' http://127.0.0.1:8545 +``` + +Response: + +``` +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000135472616e73616374696f6e20746f6f206f6c6400000000000000000000000000" +}``` + +> ABI-decode this byte string against `Error(string)` should result in the "Transaction too old" error message. ### `ots_getBlockDetails` @@ -212,6 +271,37 @@ There is a small gotcha regarding `pageSize`. If there are less results than `pa But if there are more than `pageSize` results, they are capped by the last found block. For example, let's say you are searching for Uniswap Router address and it already found 24 matches; it then looks at the next block containing this addresses occurrences and there are 5 matches inside the block. They are all returned, so it returns 30 transaction results. The caller code should be aware of this. +Example: get the first 5 transactions that touch Uniswap V1 router (including the contract creation). + +Request: + +``` +$ curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "id": 1, "method":"ots_searchTransactionsAfter","params":["0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95", 0, 5]}' http://127.0.0.1:8545 +``` + +Response: + +``` +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "txs": [ + { + "blockHash": "0x06a77abe52c486f58696665eaebd707f17fbe97eb54480c6533db725769ce3b7", + "blockNumber": "0x652284", + "from": "0xd1c24f50d05946b3fabefbae3cd0a7e9938c63f2", + "gas": "0xf4240", + "gasPrice": "0x2cb417800", + "hash": "0x14455f1af43a52112d4ccf6043cb081fea4ea3a07d90dd57f2a9e1278114be94", + "input": "0x1648f38e000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498", + "nonce": "0x6", + "to": "0xc0a47dfe034b400b47bdad5fecda2621de6c4d95", + "transactionIndex": "0x71", + ... + } +``` + ### `ots_getTransactionBySenderAndNonce` Given a sender address and a nonce, returns the tx hash or `null` if not found. It returns only the tx hash on success, you can use the standard `eth_getTransactionByHash` after that to get the full transaction data. @@ -224,3 +314,21 @@ Parameters: Returns: - `string` containing the corresponding transaction hash or `null` if it doesn't exist. + +Example: get the 4th transaction sent by Vitalik's public address (nonce == 3). + +Request: + +``` +$ curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "id": 1, "method":"ots_getTransactionBySenderAndNonce","params":["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", 3]}' http://127.0.0.1:8545 +``` + +Response: + +``` +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0x021304206b2517c3f8f2df07014a55b79aac2ae097488fa807cc88eccd851a50" +} +``` From ac9a61102f1ed11066794d6b1a75671020953a68 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 21:14:37 -0300 Subject: [PATCH 39/72] Fix formatting --- docs/custom-jsonrpc.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index fab9671..efdad95 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -205,7 +205,8 @@ Response: "jsonrpc": "2.0", "id": 1, "result": "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000135472616e73616374696f6e20746f6f206f6c6400000000000000000000000000" -}``` +} +``` > ABI-decode this byte string against `Error(string)` should result in the "Transaction too old" error message. From fb2d0f95fae2b14dd6d957dae6c3e27571031ed9 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 21:19:41 -0300 Subject: [PATCH 40/72] Fix typos --- docs/custom-jsonrpc.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index efdad95..928f66f 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -67,7 +67,7 @@ All methods are prefixed with the `ots_` namespace in order to make it clear it | Name | Description | Reasoning | |-------------------|------------------|-----------| | `ots_getApiLevel` | Totally Otterscan internal API, absolutely no reason for anything outside Otterscan to use it. | Used by Otterscan to check if it's connecting to a compatible patched Erigon node and display a friendly message if it is not. | -| `ots_getInternalOperations` | Return the internal ETH transfers inside a transaction. | For complex contract interactions, there may be internal calls that forward ETH between addresses. A very common example is someone swapping some token for ETH, in this case there is a ETH send to the sender address which is only unveiled by examining the internal calls. | +| `ots_getInternalOperations` | Return the internal ETH transfers inside a transaction. | For complex contract interactions, there may be internal calls that forward ETH between addresses. A very common example is someone swapping some token for ETH, in this case there is an ETH send to the sender address which is only unveiled by examining the internal calls. | | `ots_hasCode` | Check if a certain address contains a deployed code. | A common way to check if an address is a contract or an EOA is calling `eth_getCode` to see if it has some code deployed. However this call is expensive regarding this purpose, as it returns the entire contract code over the network just for the client to check its presence. This call just returns a boolean. | | `ots_getTransactionError` | Extract the transaction raw error output. | In order to get the error message or custom error from a failed transaction, you need to get its error output and decoded it. This info is not exposed through standard APIs. | | `ots_traceTransaction` | Extract all variations of calls, contract creation and self-destructs and returns a call tree. | This is an optimized version of tracing; regular tracing returns lots of data, and custom tracing using a JS tracer could be slow. | @@ -208,7 +208,7 @@ Response: } ``` -> ABI-decode this byte string against `Error(string)` should result in the "Transaction too old" error message. +> ABI-decoding this byte string against `Error(string)` should result in the "Transaction too old" error message. ### `ots_getBlockDetails` @@ -272,7 +272,7 @@ There is a small gotcha regarding `pageSize`. If there are less results than `pa But if there are more than `pageSize` results, they are capped by the last found block. For example, let's say you are searching for Uniswap Router address and it already found 24 matches; it then looks at the next block containing this addresses occurrences and there are 5 matches inside the block. They are all returned, so it returns 30 transaction results. The caller code should be aware of this. -Example: get the first 5 transactions that touch Uniswap V1 router (including the contract creation). +Example: get the first 5 transactions that touched Uniswap V1 router (including the contract creation). Request: From b56feb541241770fd135cbab74d61b6dac998668 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 20 Jan 2022 21:22:17 -0300 Subject: [PATCH 41/72] Fix typos --- docs/custom-jsonrpc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index 928f66f..353e749 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -195,7 +195,7 @@ Example: get the revert reason of a random transaction spotted in the wild to Un Request: ``` -curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "id": 1, "method":"ots_getTransactionError","params":["0xcdb0e53c4f1b5f37ea7f0d2a8428b13a5bff47fb457d11ef9bc85ccdc489635b"]}' http://127.0.0.1:8545 +$ curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "id": 1, "method":"ots_getTransactionError","params":["0xcdb0e53c4f1b5f37ea7f0d2a8428b13a5bff47fb457d11ef9bc85ccdc489635b"]}' http://127.0.0.1:8545 ``` Response: From 75a37f79d3c558d72b27cb76ec0b4348e5fb559f Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Fri, 21 Jan 2022 04:06:46 -0300 Subject: [PATCH 42/72] Add install instructions --- docs/custom-jsonrpc.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/custom-jsonrpc.md b/docs/custom-jsonrpc.md index 353e749..497800b 100644 --- a/docs/custom-jsonrpc.md +++ b/docs/custom-jsonrpc.md @@ -8,6 +8,8 @@ They are all used by Otterscan, but we are documenting them here so others can t We take an incremental approach when design the APIs, so there may be some methods very specific to Otterscan use cases, others that look more generic. +Please see the [install instructions](./install.md) if you want to run a patched Erigon with those customizations enabled. + ## Quick FAQ ### Why don't you use _Some Product XXX_ for Otterscan? And why shouldn't I? From 47cbe36c8f81b8aa504c2a5db39958b0d8459cfb Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 27 Dec 2021 15:48:46 -0300 Subject: [PATCH 43/72] First working iteration of sender/nonce navigation --- src/components/TransactionLink.tsx | 3 +- src/params.ts | 2 +- src/transaction/Details.tsx | 6 ++++ src/transaction/NavButton.tsx | 33 ++++++++++++++++++ src/transaction/NavNonce.tsx | 56 ++++++++++++++++++++++++++++++ src/url.ts | 2 ++ src/useErigonHooks.ts | 40 +++++++++++++++++++++ 7 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 src/transaction/NavButton.tsx create mode 100644 src/transaction/NavNonce.tsx diff --git a/src/components/TransactionLink.tsx b/src/components/TransactionLink.tsx index 8db23f3..06fde64 100644 --- a/src/components/TransactionLink.tsx +++ b/src/components/TransactionLink.tsx @@ -1,5 +1,6 @@ import React from "react"; import { NavLink } from "react-router-dom"; +import { transactionURL } from "../url"; type TransactionLinkProps = { txHash: string; @@ -8,7 +9,7 @@ type TransactionLinkProps = { const TransactionLink: React.FC = ({ txHash }) => (

{txHash}

diff --git a/src/params.ts b/src/params.ts index d1464c6..b1ef13b 100644 --- a/src/params.ts +++ b/src/params.ts @@ -1,3 +1,3 @@ -export const MIN_API_LEVEL = 5; +export const MIN_API_LEVEL = 6; export const PAGE_SIZE = 25; diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 695a686..8c0d779 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -15,6 +15,7 @@ import BlockConfirmations from "../components/BlockConfirmations"; import TransactionAddress from "../components/TransactionAddress"; import Copy from "../components/Copy"; import Nonce from "../components/Nonce"; +import NavNonce from "./NavNonce"; import Timestamp from "../components/Timestamp"; import InternalTransactionOperation from "../components/InternalTransactionOperation"; import MethodName from "../components/MethodName"; @@ -253,6 +254,11 @@ const Details: React.FC = ({
+
diff --git a/src/transaction/NavButton.tsx b/src/transaction/NavButton.tsx new file mode 100644 index 0000000..34f847d --- /dev/null +++ b/src/transaction/NavButton.tsx @@ -0,0 +1,33 @@ +import { NavLink } from "react-router-dom"; +import { transactionURL } from "../url"; + +// TODO: extract common component with block/NavButton +type NavButtonProps = { + txHash: string | undefined; + disabled?: boolean; +}; + +const NavButton: React.FC = ({ + txHash, + disabled, + children, +}) => { + if (disabled || txHash === undefined) { + return ( + + {children} + + ); + } + + return ( + + {children} + + ); +}; + +export default NavButton; diff --git a/src/transaction/NavNonce.tsx b/src/transaction/NavNonce.tsx new file mode 100644 index 0000000..f17c5cc --- /dev/null +++ b/src/transaction/NavNonce.tsx @@ -0,0 +1,56 @@ +import React, { useContext } from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChevronLeft } from "@fortawesome/free-solid-svg-icons/faChevronLeft"; +import { faChevronRight } from "@fortawesome/free-solid-svg-icons/faChevronRight"; +import NavButton from "./NavButton"; +import { ChecksummedAddress } from "../types"; +import { RuntimeContext } from "../useRuntime"; +import { useTransactionBySenderAndNonce } from "../useErigonHooks"; + +type NavNonceProps = { + sender: ChecksummedAddress; + nonce: number; + latestBlockNumber: number | undefined; +}; + +const NavNonce: React.FC = ({ + sender, + nonce, + latestBlockNumber, +}) => { + const { provider } = useContext(RuntimeContext); + const prevTxHash = useTransactionBySenderAndNonce( + provider, + sender, + nonce - 1 + ); + const nextTxHash = useTransactionBySenderAndNonce( + provider, + sender, + nonce + 1 + ); + const lastTxHash = nextTxHash; + + return ( +
+ + + + = latestBlockNumber} + > + + + = latestBlockNumber} + > + + + +
+ ); +}; + +export default React.memo(NavNonce); diff --git a/src/url.ts b/src/url.ts index 471c9d8..2216bf6 100644 --- a/src/url.ts +++ b/src/url.ts @@ -18,6 +18,8 @@ export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`; export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`; +export const transactionURL = (txHash: string) => `/tx/${txHash}`; + export enum SourcifySource { // Resolve trusted IPNS for root IPFS IPFS_IPNS, diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index e63a718..d26fcd0 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -502,3 +502,43 @@ export const useTransactionError = ( return [errorMsg, data, isCustomError]; }; + +export const useTransactionBySenderAndNonce = ( + provider: JsonRpcProvider | undefined, + sender: ChecksummedAddress | undefined, + nonce: number | undefined +): string | undefined => { + const [txHash, setTxHash] = useState(); + + useEffect(() => { + // Reset + setTxHash(undefined); + + if ( + provider === undefined || + sender === undefined || + nonce === undefined || + nonce < 0 + ) { + return; + } + + const readTxHash = async () => { + const result = (await provider.send( + "ots_getTransactionBySenderAndNonce", + [sender, nonce] + )) as string; + + // Empty or success + if (result === "0x") { + setTxHash(undefined); + return; + } + + setTxHash(result); + }; + readTxHash(); + }, [provider, sender, nonce]); + + return txHash; +}; From e012165696805c87ef5fa3c3f32a75ba5bac9b36 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 27 Dec 2021 15:51:31 -0300 Subject: [PATCH 44/72] Improve readability of nonce --- src/components/Nonce.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Nonce.tsx b/src/components/Nonce.tsx index f03eaad..2a59258 100644 --- a/src/components/Nonce.tsx +++ b/src/components/Nonce.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { commify } from "@ethersproject/units"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faArrowUp } from "@fortawesome/free-solid-svg-icons/faArrowUp"; @@ -14,7 +15,7 @@ const Nonce: React.FC = ({ value }) => ( - {value} + {commify(value)} ); From 0d2a1a593d268662e6b3d47ed355818443e53f95 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 27 Dec 2021 19:20:13 -0300 Subject: [PATCH 45/72] Add next/last nonce navigation support --- src/transaction/Details.tsx | 6 +----- src/transaction/NavNonce.tsx | 23 +++++++++++++---------- src/useErigonHooks.ts | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 8c0d779..261e651 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -254,11 +254,7 @@ const Details: React.FC = ({
- +
diff --git a/src/transaction/NavNonce.tsx b/src/transaction/NavNonce.tsx index f17c5cc..236b30f 100644 --- a/src/transaction/NavNonce.tsx +++ b/src/transaction/NavNonce.tsx @@ -5,19 +5,17 @@ import { faChevronRight } from "@fortawesome/free-solid-svg-icons/faChevronRight import NavButton from "./NavButton"; import { ChecksummedAddress } from "../types"; import { RuntimeContext } from "../useRuntime"; -import { useTransactionBySenderAndNonce } from "../useErigonHooks"; +import { + useTransactionBySenderAndNonce, + useTransactionCount, +} from "../useErigonHooks"; type NavNonceProps = { sender: ChecksummedAddress; nonce: number; - latestBlockNumber: number | undefined; }; -const NavNonce: React.FC = ({ - sender, - nonce, - latestBlockNumber, -}) => { +const NavNonce: React.FC = ({ sender, nonce }) => { const { provider } = useContext(RuntimeContext); const prevTxHash = useTransactionBySenderAndNonce( provider, @@ -29,7 +27,12 @@ const NavNonce: React.FC = ({ sender, nonce + 1 ); - const lastTxHash = nextTxHash; + const count = useTransactionCount(provider, sender); + const lastTxHash = useTransactionBySenderAndNonce( + provider, + sender, + count !== undefined ? count - 1 : undefined + ); return (
@@ -38,13 +41,13 @@ const NavNonce: React.FC = ({ = latestBlockNumber} + disabled={count === undefined || nonce >= count - 1} > = latestBlockNumber} + disabled={count === undefined || nonce >= count - 1} > diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index d26fcd0..71b95cc 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -503,6 +503,30 @@ export const useTransactionError = ( return [errorMsg, data, isCustomError]; }; +export const useTransactionCount = ( + provider: JsonRpcProvider | undefined, + sender: ChecksummedAddress | undefined +): number | undefined => { + const [count, setCount] = useState(); + + useEffect(() => { + // Reset + setCount(undefined); + + if (provider === undefined || sender === undefined) { + return; + } + + const readCount = async () => { + const _count = await provider.getTransactionCount(sender); + setCount(_count); + }; + readCount(); + }, [provider, sender]); + + return count; +}; + export const useTransactionBySenderAndNonce = ( provider: JsonRpcProvider | undefined, sender: ChecksummedAddress | undefined, From f1db3f23485842f6a6d83d1db1ddd240caf26e52 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 27 Dec 2021 21:16:33 -0300 Subject: [PATCH 46/72] Add prefetch to nonce +-2 --- src/transaction/NavButton.tsx | 4 +- src/transaction/NavNonce.tsx | 23 +++++++- src/useErigonHooks.ts | 103 +++++++++++++++++++--------------- 3 files changed, 81 insertions(+), 49 deletions(-) diff --git a/src/transaction/NavButton.tsx b/src/transaction/NavButton.tsx index 34f847d..8eb9e16 100644 --- a/src/transaction/NavButton.tsx +++ b/src/transaction/NavButton.tsx @@ -14,7 +14,7 @@ const NavButton: React.FC = ({ }) => { if (disabled || txHash === undefined) { return ( - + {children} ); @@ -22,7 +22,7 @@ const NavButton: React.FC = ({ return ( {children} diff --git a/src/transaction/NavNonce.tsx b/src/transaction/NavNonce.tsx index 236b30f..787deac 100644 --- a/src/transaction/NavNonce.tsx +++ b/src/transaction/NavNonce.tsx @@ -6,9 +6,11 @@ import NavButton from "./NavButton"; import { ChecksummedAddress } from "../types"; import { RuntimeContext } from "../useRuntime"; import { + prefetchTransactionBySenderAndNonce, useTransactionBySenderAndNonce, useTransactionCount, } from "../useErigonHooks"; +import { useSWRConfig } from "swr"; type NavNonceProps = { sender: ChecksummedAddress; @@ -34,8 +36,27 @@ const NavNonce: React.FC = ({ sender, nonce }) => { count !== undefined ? count - 1 : undefined ); + // Prefetch + const swrConfig = useSWRConfig(); + const prefetch = () => { + if (provider && sender && nonce !== undefined) { + prefetchTransactionBySenderAndNonce( + swrConfig, + provider, + sender, + nonce - 2 + ); + prefetchTransactionBySenderAndNonce( + swrConfig, + provider, + sender, + nonce + 2 + ); + } + }; + return ( -
+
diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index 71b95cc..737c6d7 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -6,6 +6,7 @@ import { Contract } from "@ethersproject/contracts"; import { defaultAbiCoder } from "@ethersproject/abi"; import { BigNumber } from "@ethersproject/bignumber"; import { arrayify, hexDataSlice, isHexString } from "@ethersproject/bytes"; +import useSWR, { useSWRConfig } from "swr"; import { getInternalOperations } from "./nodeFunctions"; import { TokenMetas, @@ -507,24 +508,52 @@ export const useTransactionCount = ( provider: JsonRpcProvider | undefined, sender: ChecksummedAddress | undefined ): number | undefined => { - const [count, setCount] = useState(); + const { data, error } = useSWR( + provider && sender ? { provider, sender } : null, + async ({ provider, sender }): Promise => + provider.getTransactionCount(sender) + ); - useEffect(() => { - // Reset - setCount(undefined); + if (error) { + return undefined; + } + return data; +}; - if (provider === undefined || sender === undefined) { - return; - } +type TransactionBySenderAndNonceKey = { + provider: JsonRpcProvider; + sender: ChecksummedAddress; + nonce: number; +}; - const readCount = async () => { - const _count = await provider.getTransactionCount(sender); - setCount(_count); - }; - readCount(); - }, [provider, sender]); +const getTransactionBySenderAndNonceFetcher = async ({ + provider, + sender, + nonce, +}: TransactionBySenderAndNonceKey): Promise => { + const result = (await provider.send("ots_getTransactionBySenderAndNonce", [ + sender, + nonce, + ])) as string; - return count; + // Empty or success + if (result === "0x") { + return undefined; + } + + return result; +}; + +export const prefetchTransactionBySenderAndNonce = ( + { cache, mutate }: ReturnType, + provider: JsonRpcProvider, + sender: ChecksummedAddress, + nonce: number +) => { + const key: TransactionBySenderAndNonceKey = { provider, sender, nonce }; + if (cache.get(key)) { + mutate(key, getTransactionBySenderAndNonceFetcher(key)); + } }; export const useTransactionBySenderAndNonce = ( @@ -532,37 +561,19 @@ export const useTransactionBySenderAndNonce = ( sender: ChecksummedAddress | undefined, nonce: number | undefined ): string | undefined => { - const [txHash, setTxHash] = useState(); + const { data, error } = useSWR< + string | undefined, + any, + TransactionBySenderAndNonceKey | null + >( + provider && sender && nonce !== undefined + ? { provider, sender, nonce } + : null, + getTransactionBySenderAndNonceFetcher + ); - useEffect(() => { - // Reset - setTxHash(undefined); - - if ( - provider === undefined || - sender === undefined || - nonce === undefined || - nonce < 0 - ) { - return; - } - - const readTxHash = async () => { - const result = (await provider.send( - "ots_getTransactionBySenderAndNonce", - [sender, nonce] - )) as string; - - // Empty or success - if (result === "0x") { - setTxHash(undefined); - return; - } - - setTxHash(result); - }; - readTxHash(); - }, [provider, sender, nonce]); - - return txHash; + if (error) { + return undefined; + } + return data; }; From 0c6137656fea770cce29be3b09405a4d59b08fd6 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 28 Dec 2021 02:45:42 -0300 Subject: [PATCH 47/72] Always prefetch --- src/transaction/NavNonce.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/transaction/NavNonce.tsx b/src/transaction/NavNonce.tsx index 787deac..ee30a4b 100644 --- a/src/transaction/NavNonce.tsx +++ b/src/transaction/NavNonce.tsx @@ -55,8 +55,11 @@ const NavNonce: React.FC = ({ sender, nonce }) => { } }; + // Always prefetch + prefetch(); + return ( -
+
From 7d87345e9806d27194fd348f3b97d9662837898f Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 28 Dec 2021 02:46:09 -0300 Subject: [PATCH 48/72] Prevent passing negative nonce because of prefetch --- src/useErigonHooks.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index 737c6d7..2a38e09 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -531,6 +531,10 @@ const getTransactionBySenderAndNonceFetcher = async ({ sender, nonce, }: TransactionBySenderAndNonceKey): Promise => { + if (nonce < 0) { + return undefined; + } + const result = (await provider.send("ots_getTransactionBySenderAndNonce", [ sender, nonce, From fe673bd0f87d2cc56c8ace630901b456c38c9f39 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 29 Dec 2021 22:00:41 -0300 Subject: [PATCH 49/72] Small fixes --- src/transaction/NavButton.tsx | 4 ++-- src/useErigonHooks.ts | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/transaction/NavButton.tsx b/src/transaction/NavButton.tsx index 8eb9e16..04d421c 100644 --- a/src/transaction/NavButton.tsx +++ b/src/transaction/NavButton.tsx @@ -3,7 +3,7 @@ import { transactionURL } from "../url"; // TODO: extract common component with block/NavButton type NavButtonProps = { - txHash: string | undefined; + txHash: string | null | undefined; disabled?: boolean; }; @@ -12,7 +12,7 @@ const NavButton: React.FC = ({ disabled, children, }) => { - if (disabled || txHash === undefined) { + if (disabled || txHash === null || txHash === undefined) { return ( {children} diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index 2a38e09..dbf9e8f 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -530,7 +530,7 @@ const getTransactionBySenderAndNonceFetcher = async ({ provider, sender, nonce, -}: TransactionBySenderAndNonceKey): Promise => { +}: TransactionBySenderAndNonceKey): Promise => { if (nonce < 0) { return undefined; } @@ -541,10 +541,6 @@ const getTransactionBySenderAndNonceFetcher = async ({ ])) as string; // Empty or success - if (result === "0x") { - return undefined; - } - return result; }; @@ -564,9 +560,9 @@ export const useTransactionBySenderAndNonce = ( provider: JsonRpcProvider | undefined, sender: ChecksummedAddress | undefined, nonce: number | undefined -): string | undefined => { +): string | null | undefined => { const { data, error } = useSWR< - string | undefined, + string | null | undefined, any, TransactionBySenderAndNonceKey | null >( From 80ef1cf82067ea755afe75203bc29b723f9040e4 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 22 Jan 2022 15:02:00 -0300 Subject: [PATCH 50/72] Add initial support for address:nonce search --- src/search/search.ts | 77 +++++++++++++++++++++++++++++++++++++++---- src/useErigonHooks.ts | 2 +- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/search/search.ts b/src/search/search.ts index 57a5958..6994444 100644 --- a/src/search/search.ts +++ b/src/search/search.ts @@ -2,6 +2,7 @@ import { ChangeEventHandler, FormEventHandler, RefObject, + useContext, useRef, useState, } from "react"; @@ -12,6 +13,8 @@ import { isHexString } from "@ethersproject/bytes"; import useKeyboardShortcut from "use-keyboard-shortcut"; import { PAGE_SIZE } from "../params"; import { ProcessedTransaction, TransactionChunk } from "../types"; +import { RuntimeContext } from "../useRuntime"; +import { getTransactionBySenderAndNonceFetcher } from "../useErigonHooks"; export class SearchController { private txs: ProcessedTransaction[]; @@ -206,9 +209,30 @@ export class SearchController { } } -const doSearch = (q: string, navigate: NavigateFunction) => { - if (isAddress(q)) { - navigate(`/address/${q}`, { replace: true }); +const doSearch = async ( + provider: JsonRpcProvider, + q: string, + navigate: NavigateFunction +) => { + // Cleanup + q = q.trim(); + + let maybeAddress = q; + let maybeIndex: string | undefined; + const sepIndex = q.lastIndexOf(":"); + if (sepIndex !== -1) { + maybeAddress = q.substring(0, sepIndex); + maybeIndex = q.substring(sepIndex + 1); + } + + if (isAddress(maybeAddress)) { + // Plain address + nonce? + if (await navigateToTx(provider, maybeAddress, maybeIndex, navigate)) { + return; + } + + // Plain address + navigate(`/address/${maybeAddress}`, { replace: true }); return; } @@ -224,7 +248,47 @@ const doSearch = (q: string, navigate: NavigateFunction) => { } // Assume it is an ENS name - navigate(`/address/${q}`); + const resolvedName = await provider.resolveName(maybeAddress); + if (resolvedName !== null) { + await navigateToTx(provider, resolvedName, maybeIndex, navigate); + } + + // TODO: handle default +}; + +const navigateToTx = async ( + provider: JsonRpcProvider, + maybeAddress: string, + maybeIndex: string | undefined, + navigate: NavigateFunction +): Promise => { + if (maybeIndex !== undefined) { + try { + let nonce = 0; + if (maybeIndex === "latest") { + const count = await provider.getTransactionCount(maybeAddress); + if (count > 0) { + nonce = count - 1; + } + } else { + nonce = parseInt(maybeIndex); + } + + const txHash = await getTransactionBySenderAndNonceFetcher({ + provider, + sender: maybeAddress, + nonce, + }); + if (txHash) { + navigate(`/tx/${txHash}`, { replace: true }); + } + return true; + } catch (err) { + // ignore + } + } + + return false; }; export const useGenericSearch = (): [ @@ -232,6 +296,7 @@ export const useGenericSearch = (): [ ChangeEventHandler, FormEventHandler ] => { + const { provider } = useContext(RuntimeContext); const [searchString, setSearchString] = useState(""); const [canSubmit, setCanSubmit] = useState(false); const navigate = useNavigate(); @@ -244,14 +309,14 @@ export const useGenericSearch = (): [ const handleSubmit: React.FormEventHandler = (e) => { e.preventDefault(); - if (!canSubmit) { + if (!canSubmit || !provider) { return; } if (searchRef.current) { searchRef.current.value = ""; } - doSearch(searchString, navigate); + doSearch(provider, searchString, navigate); }; const searchRef = useRef(null); diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index dbf9e8f..e2916b3 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -526,7 +526,7 @@ type TransactionBySenderAndNonceKey = { nonce: number; }; -const getTransactionBySenderAndNonceFetcher = async ({ +export const getTransactionBySenderAndNonceFetcher = async ({ provider, sender, nonce, From af15aaba0c9d18bbc6077d0c7ffea43eaeea0575 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 24 Jan 2022 16:18:28 -0300 Subject: [PATCH 51/72] Rename, since the /:addressorName now may not route to just address search results --- src/{AddressTransactions.tsx => Address.tsx} | 4 ++-- src/App.tsx | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) rename src/{AddressTransactions.tsx => Address.tsx} (98%) diff --git a/src/AddressTransactions.tsx b/src/Address.tsx similarity index 98% rename from src/AddressTransactions.tsx rename to src/Address.tsx index d7222c0..eb0213d 100644 --- a/src/AddressTransactions.tsx +++ b/src/Address.tsx @@ -25,7 +25,7 @@ import { useMultipleMetadata } from "./sourcify/useSourcify"; import { ChecksummedAddress } from "./types"; import { useAddressesWithCode } from "./useErigonHooks"; -const AddressTransactions: React.FC = () => { +const Address: React.FC = () => { const { provider } = useContext(RuntimeContext); const { addressOrName, direction } = useParams(); if (addressOrName === undefined) { @@ -175,4 +175,4 @@ const AddressTransactions: React.FC = () => { ); }; -export default AddressTransactions; +export default Address; diff --git a/src/App.tsx b/src/App.tsx index f31fff6..aae4ab8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,11 +17,9 @@ const BlockTransactions = React.lazy( /* webpackChunkName: "blocktxs", webpackPrefetch: true */ "./BlockTransactions" ) ); -const AddressTransactions = React.lazy( +const Address = React.lazy( () => - import( - /* webpackChunkName: "address", webpackPrefetch: true */ "./AddressTransactions" - ) + import(/* webpackChunkName: "address", webpackPrefetch: true */ "./Address") ); const Transaction = React.lazy( () => @@ -61,7 +59,7 @@ const App = () => { } /> } + element={
} /> } /> From e62068c65a4d3c4b1a6ea28cb3721b351d90f8df Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 24 Jan 2022 16:23:16 -0300 Subject: [PATCH 52/72] Fix ens name without nonce search --- src/search/search.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/search/search.ts b/src/search/search.ts index 6994444..e1e6730 100644 --- a/src/search/search.ts +++ b/src/search/search.ts @@ -250,7 +250,14 @@ const doSearch = async ( // Assume it is an ENS name const resolvedName = await provider.resolveName(maybeAddress); if (resolvedName !== null) { - await navigateToTx(provider, resolvedName, maybeIndex, navigate); + // ENS name + nonce? + if (await navigateToTx(provider, resolvedName, maybeIndex, navigate)) { + return; + } + + // ENS name + navigate(`/address/${maybeAddress}`, { replace: true }); + return; } // TODO: handle default From f138444eee1fa08f7623c44614ebe84300ff2282 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 24 Jan 2022 16:36:16 -0300 Subject: [PATCH 53/72] Fix signalling tx not found on network error --- src/useErigonHooks.ts | 187 ++++++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 89 deletions(-) diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index e2916b3..7eb847c 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -191,98 +191,107 @@ export const useTxData = ( } const readTxData = async () => { - const [_response, _receipt] = await Promise.all([ - provider.getTransaction(txhash), - provider.getTransactionReceipt(txhash), - ]); - if (_response === null) { + try { + const [_response, _receipt] = await Promise.all([ + provider.getTransaction(txhash), + provider.getTransactionReceipt(txhash), + ]); + if (_response === null) { + setTxData(null); + return; + } + + let _block: ExtendedBlock | undefined; + if (_response.blockNumber) { + _block = await readBlock(provider, _response.blockNumber.toString()); + } + + document.title = `Transaction ${_response.hash} | Otterscan`; + + // Extract token transfers + const tokenTransfers: TokenTransfer[] = []; + if (_receipt) { + for (const l of _receipt.logs) { + if (l.topics.length !== 3) { + continue; + } + if (l.topics[0] !== TRANSFER_TOPIC) { + continue; + } + tokenTransfers.push({ + token: l.address, + from: getAddress(hexDataSlice(arrayify(l.topics[1]), 12)), + to: getAddress(hexDataSlice(arrayify(l.topics[2]), 12)), + value: BigNumber.from(l.data), + }); + } + } + + // Extract token meta + const tokenMetas: TokenMetas = {}; + for (const t of tokenTransfers) { + if (tokenMetas[t.token] !== undefined) { + continue; + } + const erc20Contract = new Contract(t.token, erc20, provider); + try { + const [name, symbol, decimals] = await Promise.all([ + erc20Contract.name(), + erc20Contract.symbol(), + erc20Contract.decimals(), + ]); + tokenMetas[t.token] = { + name, + symbol, + decimals, + }; + } catch (err) { + tokenMetas[t.token] = null; + console.warn( + `Couldn't get token ${t.token} metadata; ignoring`, + err + ); + } + } + + setTxData({ + transactionHash: _response.hash, + from: _response.from, + to: _response.to, + value: _response.value, + tokenTransfers, + tokenMetas, + type: _response.type ?? 0, + maxFeePerGas: _response.maxFeePerGas, + maxPriorityFeePerGas: _response.maxPriorityFeePerGas, + gasPrice: _response.gasPrice!, + gasLimit: _response.gasLimit, + nonce: _response.nonce, + data: _response.data, + confirmedData: + _receipt === null + ? undefined + : { + status: _receipt.status === 1, + blockNumber: _receipt.blockNumber, + transactionIndex: _receipt.transactionIndex, + blockBaseFeePerGas: _block!.baseFeePerGas, + blockTransactionCount: _block!.transactionCount, + confirmations: _receipt.confirmations, + timestamp: _block!.timestamp, + miner: _block!.miner, + createdContractAddress: _receipt.contractAddress, + fee: _response.gasPrice!.mul(_receipt.gasUsed), + gasUsed: _receipt.gasUsed, + logs: _receipt.logs, + }, + }); + } catch (err) { + console.error(err); setTxData(null); - return; } - - let _block: ExtendedBlock | undefined; - if (_response.blockNumber) { - _block = await readBlock(provider, _response.blockNumber.toString()); - } - - document.title = `Transaction ${_response.hash} | Otterscan`; - - // Extract token transfers - const tokenTransfers: TokenTransfer[] = []; - if (_receipt) { - for (const l of _receipt.logs) { - if (l.topics.length !== 3) { - continue; - } - if (l.topics[0] !== TRANSFER_TOPIC) { - continue; - } - tokenTransfers.push({ - token: l.address, - from: getAddress(hexDataSlice(arrayify(l.topics[1]), 12)), - to: getAddress(hexDataSlice(arrayify(l.topics[2]), 12)), - value: BigNumber.from(l.data), - }); - } - } - - // Extract token meta - const tokenMetas: TokenMetas = {}; - for (const t of tokenTransfers) { - if (tokenMetas[t.token] !== undefined) { - continue; - } - const erc20Contract = new Contract(t.token, erc20, provider); - try { - const [name, symbol, decimals] = await Promise.all([ - erc20Contract.name(), - erc20Contract.symbol(), - erc20Contract.decimals(), - ]); - tokenMetas[t.token] = { - name, - symbol, - decimals, - }; - } catch (err) { - tokenMetas[t.token] = null; - console.warn(`Couldn't get token ${t.token} metadata; ignoring`, err); - } - } - - setTxData({ - transactionHash: _response.hash, - from: _response.from, - to: _response.to, - value: _response.value, - tokenTransfers, - tokenMetas, - type: _response.type ?? 0, - maxFeePerGas: _response.maxFeePerGas, - maxPriorityFeePerGas: _response.maxPriorityFeePerGas, - gasPrice: _response.gasPrice!, - gasLimit: _response.gasLimit, - nonce: _response.nonce, - data: _response.data, - confirmedData: - _receipt === null - ? undefined - : { - status: _receipt.status === 1, - blockNumber: _receipt.blockNumber, - transactionIndex: _receipt.transactionIndex, - blockBaseFeePerGas: _block!.baseFeePerGas, - blockTransactionCount: _block!.transactionCount, - confirmations: _receipt.confirmations, - timestamp: _block!.timestamp, - miner: _block!.miner, - createdContractAddress: _receipt.contractAddress, - fee: _response.gasPrice!.mul(_receipt.gasUsed), - gasUsed: _receipt.gasUsed, - logs: _receipt.logs, - }, - }); }; + readTxData(); }, [provider, txhash]); From 2c47f04384436cdccdbbda543635757a0a6dc9ff Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 25 Jan 2022 22:34:09 -0300 Subject: [PATCH 54/72] Improve git status performance by not checking submodules --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitmodules b/.gitmodules index 3ee2444..e55ad32 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,12 @@ [submodule "4bytes"] path = 4bytes url = https://github.com/ethereum-lists/4bytes.git + ignore = dirty [submodule "trustwallet"] path = trustwallet url = https://github.com/trustwallet/assets.git + ignore = dirty [submodule "topic0"] path = topic0 url = https://github.com/wmitsuda/topic0.git + ignore = dirty From cb6d1f3bcd37eaf9736ca0c904ecac06d250ad88 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 29 Jan 2022 19:04:52 -0300 Subject: [PATCH 55/72] Extract tx page content component --- src/Transaction.tsx | 124 +------------------------------ src/TransactionPageContent.tsx | 131 +++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 120 deletions(-) create mode 100644 src/TransactionPageContent.tsx diff --git a/src/Transaction.tsx b/src/Transaction.tsx index 22873d0..988c5d2 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -1,129 +1,13 @@ -import React, { useMemo, useContext } from "react"; -import { useParams, Routes, Route } from "react-router-dom"; -import { Tab } from "@headlessui/react"; -import StandardFrame from "./StandardFrame"; -import StandardSubtitle from "./StandardSubtitle"; -import ContentFrame from "./ContentFrame"; -import NavTab from "./components/NavTab"; -import { RuntimeContext } from "./useRuntime"; -import { SelectionContext, useSelection } from "./useSelection"; -import { useInternalOperations, useTxData } from "./useErigonHooks"; -import { useETHUSDOracle } from "./usePriceOracle"; -import { useAppConfigContext } from "./useAppConfig"; -import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify"; -import { SelectedTransactionContext } from "./useSelectedTransaction"; - -const Details = React.lazy( - () => - import( - /* webpackChunkName: "txdetails", webpackPrefetch: true */ - "./transaction/Details" - ) -); -const Logs = React.lazy( - () => - import( - /* webpackChunkName: "txlogs", webpackPrefetch: true */ "./transaction/Logs" - ) -); -const Trace = React.lazy( - () => - import( - /* webpackChunkName: "txtrace", webpackPrefetch: true */ "./transaction/Trace" - ) -); +import React from "react"; +import { useParams } from "react-router-dom"; +import TransactionPageContent from "./TransactionPageContent"; const Transaction: React.FC = () => { - const { provider } = useContext(RuntimeContext); const { txhash } = useParams(); if (txhash === undefined) { throw new Error("txhash couldn't be undefined here"); } - - const txData = useTxData(provider, txhash); - const internalOps = useInternalOperations(provider, txData); - const sendsEthToMiner = useMemo(() => { - if (!txData || !internalOps) { - return false; - } - - for (const t of internalOps) { - if (t.to === txData.confirmedData?.miner) { - return true; - } - } - return false; - }, [txData, internalOps]); - - const selectionCtx = useSelection(); - - const blockETHUSDPrice = useETHUSDOracle( - provider, - txData?.confirmedData?.blockNumber - ); - - const { sourcifySource } = useAppConfigContext(); - const metadata = useSourcify( - txData?.to, - provider?.network.chainId, - sourcifySource - ); - const txDesc = useTransactionDescription(metadata, txData); - - return ( - - - Transaction Details - {txData === null && ( - -
- Transaction {txhash} not found. -
-
- )} - {txData && ( - - - - Overview - {txData.confirmedData?.blockNumber !== undefined && ( - - Logs - {txData && ` (${txData.confirmedData?.logs?.length ?? 0})`} - - )} - Trace - - - - - - } - /> - } - /> - } /> - - - - )} -
-
- ); + return ; }; export default Transaction; diff --git a/src/TransactionPageContent.tsx b/src/TransactionPageContent.tsx new file mode 100644 index 0000000..ab88d46 --- /dev/null +++ b/src/TransactionPageContent.tsx @@ -0,0 +1,131 @@ +import React, { useContext, useMemo } from "react"; +import { Route, Routes } from "react-router-dom"; +import { Tab } from "@headlessui/react"; +import StandardFrame from "./StandardFrame"; +import StandardSubtitle from "./StandardSubtitle"; +import ContentFrame from "./ContentFrame"; +import NavTab from "./components/NavTab"; +import { RuntimeContext } from "./useRuntime"; +import { useInternalOperations, useTxData } from "./useErigonHooks"; +import { SelectionContext, useSelection } from "./useSelection"; +import { SelectedTransactionContext } from "./useSelectedTransaction"; +import { useETHUSDOracle } from "./usePriceOracle"; +import { useAppConfigContext } from "./useAppConfig"; +import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify"; + +const Details = React.lazy( + () => + import( + /* webpackChunkName: "txdetails", webpackPrefetch: true */ + "./transaction/Details" + ) +); +const Logs = React.lazy( + () => + import( + /* webpackChunkName: "txlogs", webpackPrefetch: true */ "./transaction/Logs" + ) +); +const Trace = React.lazy( + () => + import( + /* webpackChunkName: "txtrace", webpackPrefetch: true */ "./transaction/Trace" + ) +); + +type TransactionPageContentProps = { + txhash: string; +}; + +const TransactionPageContent: React.FC = ({ + txhash, +}) => { + const { provider } = useContext(RuntimeContext); + + const txData = useTxData(provider, txhash); + const internalOps = useInternalOperations(provider, txData); + const sendsEthToMiner = useMemo(() => { + if (!txData || !internalOps) { + return false; + } + + for (const t of internalOps) { + if (t.to === txData.confirmedData?.miner) { + return true; + } + } + return false; + }, [txData, internalOps]); + + const selectionCtx = useSelection(); + + const blockETHUSDPrice = useETHUSDOracle( + provider, + txData?.confirmedData?.blockNumber + ); + + const { sourcifySource } = useAppConfigContext(); + const metadata = useSourcify( + txData?.to, + provider?.network.chainId, + sourcifySource + ); + const txDesc = useTransactionDescription(metadata, txData); + + return ( + + + Transaction Details + {txData === null && ( + +
+ Transaction {txhash} not found. +
+
+ )} + {txData && ( + + + + Overview + {txData.confirmedData?.blockNumber !== undefined && ( + + Logs + {txData && ` (${txData.confirmedData?.logs?.length ?? 0})`} + + )} + Trace + + + + + + } + /> + } + /> + } /> + + + + )} +
+
+ ); +}; + +export default TransactionPageContent; From 17b6c03a33400084b6ad1bcd2898d9aa9292480b Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 29 Jan 2022 19:40:26 -0300 Subject: [PATCH 56/72] Basic nonce locator URL --- src/AddressTransaction.tsx | 35 ++++++++++++++++++++++++++++++++++ src/App.tsx | 10 ++++++++++ src/Transaction.tsx | 2 +- src/TransactionPageContent.tsx | 8 ++++---- 4 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 src/AddressTransaction.tsx diff --git a/src/AddressTransaction.tsx b/src/AddressTransaction.tsx new file mode 100644 index 0000000..a6b1a52 --- /dev/null +++ b/src/AddressTransaction.tsx @@ -0,0 +1,35 @@ +import React, { useContext } from "react"; +import { useNavigate, useParams, useSearchParams } from "react-router-dom"; +import StandardFrame from "./StandardFrame"; +import { transactionURL } from "./url"; +import { useTransactionBySenderAndNonce } from "./useErigonHooks"; +import { RuntimeContext } from "./useRuntime"; + +const AddressTransaction: React.FC = () => { + const { provider } = useContext(RuntimeContext); + const navigate = useNavigate(); + const { addressOrName } = useParams(); + if (addressOrName === undefined) { + throw new Error("addressOrName couldn't be undefined here"); + } + + const [searchParams] = useSearchParams(); + const rawNonce = searchParams.get("nonce"); + if (rawNonce === null) { + throw new Error("rawNonce couldn't be undefined here"); + } + let nonce: number | undefined = undefined; + try { + nonce = parseInt(rawNonce); + } catch (err) { + // ignore + } + + const txHash = useTransactionBySenderAndNonce(provider, addressOrName, nonce); + if (txHash) { + navigate(transactionURL(txHash)); + } + return ; +}; + +export default AddressTransaction; diff --git a/src/App.tsx b/src/App.tsx index aae4ab8..c75c474 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,6 +21,12 @@ const Address = React.lazy( () => import(/* webpackChunkName: "address", webpackPrefetch: true */ "./Address") ); +const AddressTransaction = React.lazy( + () => + import( + /* webpackChunkName: "addresstx", webpackPrefetch: true */ "./AddressTransaction" + ) +); const Transaction = React.lazy( () => import(/* webpackChunkName: "tx", webpackPrefetch: true */ "./Transaction") @@ -61,6 +67,10 @@ const App = () => { path="address/:addressOrName/*" element={
} /> + } + /> } /> diff --git a/src/Transaction.tsx b/src/Transaction.tsx index 988c5d2..94b2e43 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -7,7 +7,7 @@ const Transaction: React.FC = () => { if (txhash === undefined) { throw new Error("txhash couldn't be undefined here"); } - return ; + return ; }; export default Transaction; diff --git a/src/TransactionPageContent.tsx b/src/TransactionPageContent.tsx index ab88d46..377c345 100644 --- a/src/TransactionPageContent.tsx +++ b/src/TransactionPageContent.tsx @@ -34,15 +34,15 @@ const Trace = React.lazy( ); type TransactionPageContentProps = { - txhash: string; + txHash: string; }; const TransactionPageContent: React.FC = ({ - txhash, + txHash, }) => { const { provider } = useContext(RuntimeContext); - const txData = useTxData(provider, txhash); + const txData = useTxData(provider, txHash); const internalOps = useInternalOperations(provider, txData); const sendsEthToMiner = useMemo(() => { if (!txData || !internalOps) { @@ -79,7 +79,7 @@ const TransactionPageContent: React.FC = ({ {txData === null && (
- Transaction {txhash} not found. + Transaction {txHash} not found.
)} From aeda92e41c2e4f5f95a222e185e39107e8017854 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 29 Jan 2022 19:54:21 -0300 Subject: [PATCH 57/72] Add ENS support to search by nonce url --- src/Address.tsx | 4 ++-- src/AddressTransaction.tsx | 31 +++++++++++++++++++++++++++++-- src/useResolvedAddresses.ts | 2 +- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/Address.tsx b/src/Address.tsx index eb0213d..950b04e 100644 --- a/src/Address.tsx +++ b/src/Address.tsx @@ -20,7 +20,7 @@ import AddressTransactionResults from "./address/AddressTransactionResults"; import Contracts from "./address/Contracts"; import { RuntimeContext } from "./useRuntime"; import { useAppConfigContext } from "./useAppConfig"; -import { useAddressOrENSFromURL } from "./useResolvedAddresses"; +import { useAddressOrENS } from "./useResolvedAddresses"; import { useMultipleMetadata } from "./sourcify/useSourcify"; import { ChecksummedAddress } from "./types"; import { useAddressesWithCode } from "./useErigonHooks"; @@ -45,7 +45,7 @@ const Address: React.FC = () => { }, [navigate, direction, searchParams] ); - const [checksummedAddress, isENS, error] = useAddressOrENSFromURL( + const [checksummedAddress, isENS, error] = useAddressOrENS( addressOrName, urlFixer ); diff --git a/src/AddressTransaction.tsx b/src/AddressTransaction.tsx index a6b1a52..84d5394 100644 --- a/src/AddressTransaction.tsx +++ b/src/AddressTransaction.tsx @@ -1,8 +1,10 @@ -import React, { useContext } from "react"; +import React, { useCallback, useContext } from "react"; import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import StandardFrame from "./StandardFrame"; +import { ChecksummedAddress } from "./types"; import { transactionURL } from "./url"; import { useTransactionBySenderAndNonce } from "./useErigonHooks"; +import { useAddressOrENS } from "./useResolvedAddresses"; import { RuntimeContext } from "./useRuntime"; const AddressTransaction: React.FC = () => { @@ -14,6 +16,19 @@ const AddressTransaction: React.FC = () => { } const [searchParams] = useSearchParams(); + const urlFixer = useCallback( + (address: ChecksummedAddress) => { + navigate(`/address/${address}/tx?${searchParams.toString()}`, { + replace: true, + }); + }, + [navigate, searchParams] + ); + const [checksummedAddress, isENS, error] = useAddressOrENS( + addressOrName, + urlFixer + ); + const rawNonce = searchParams.get("nonce"); if (rawNonce === null) { throw new Error("rawNonce couldn't be undefined here"); @@ -25,7 +40,19 @@ const AddressTransaction: React.FC = () => { // ignore } - const txHash = useTransactionBySenderAndNonce(provider, addressOrName, nonce); + const txHash = useTransactionBySenderAndNonce( + provider, + checksummedAddress, + nonce + ); + + if (error) { + return ( + + "{addressOrName}" is not an ETH address or ENS name. + + ); + } if (txHash) { navigate(transactionURL(txHash)); } diff --git a/src/useResolvedAddresses.ts b/src/useResolvedAddresses.ts index adc3819..056fa98 100644 --- a/src/useResolvedAddresses.ts +++ b/src/useResolvedAddresses.ts @@ -7,7 +7,7 @@ import { SelectedResolvedName } from "./api/address-resolver/CompositeAddressRes import { RuntimeContext } from "./useRuntime"; import { ChecksummedAddress } from "./types"; -export const useAddressOrENSFromURL = ( +export const useAddressOrENS = ( addressOrName: string, urlFixer: (address: ChecksummedAddress) => void ): [ From d70c22b5a02e258b8dd67335af26cd75ec85d8cd Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 29 Jan 2022 20:00:01 -0300 Subject: [PATCH 58/72] Extract common component for address or ens name not found --- src/Address.tsx | 5 ++--- src/AddressTransaction.tsx | 7 ++----- src/components/AddressOrENSNameNotFound.tsx | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 src/components/AddressOrENSNameNotFound.tsx diff --git a/src/Address.tsx b/src/Address.tsx index 950b04e..3bfbd41 100644 --- a/src/Address.tsx +++ b/src/Address.tsx @@ -13,6 +13,7 @@ import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch"; import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons/faQuestionCircle"; import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; +import AddressOrENSNameNotFound from "./components/AddressOrENSNameNotFound"; import Copy from "./components/Copy"; import NavTab from "./components/NavTab"; import SourcifyLogo from "./sourcify/SourcifyLogo"; @@ -81,9 +82,7 @@ const Address: React.FC = () => { return ( {error ? ( - - "{addressOrName}" is not an ETH address or ENS name. - + ) : ( checksummedAddress && ( <> diff --git a/src/AddressTransaction.tsx b/src/AddressTransaction.tsx index 84d5394..da709ed 100644 --- a/src/AddressTransaction.tsx +++ b/src/AddressTransaction.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useContext } from "react"; import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import StandardFrame from "./StandardFrame"; +import AddressOrENSNameNotFound from "./components/AddressOrENSNameNotFound"; import { ChecksummedAddress } from "./types"; import { transactionURL } from "./url"; import { useTransactionBySenderAndNonce } from "./useErigonHooks"; @@ -47,11 +48,7 @@ const AddressTransaction: React.FC = () => { ); if (error) { - return ( - - "{addressOrName}" is not an ETH address or ENS name. - - ); + return ; } if (txHash) { navigate(transactionURL(txHash)); diff --git a/src/components/AddressOrENSNameNotFound.tsx b/src/components/AddressOrENSNameNotFound.tsx new file mode 100644 index 0000000..6798124 --- /dev/null +++ b/src/components/AddressOrENSNameNotFound.tsx @@ -0,0 +1,15 @@ +import React from "react"; + +type AddressOrENSNameNotFoundProps = { + addressOrENSName: string; +}; + +const AddressOrENSNameNotFound: React.FC = ({ + addressOrENSName, +}) => ( + + "{addressOrENSName}" is not an ETH address or ENS name. + +); + +export default React.memo(AddressOrENSNameNotFound); From 20fc443009b4fdb45afa8bc4473a4d03d905f27e Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 29 Jan 2022 20:44:26 -0300 Subject: [PATCH 59/72] Error handling UI improvements --- src/AddressTransaction.tsx | 38 ++++++++++++++++--- src/components/AddressOrENSName.tsx | 5 ++- .../AddressOrENSNameInvalidNonce.tsx | 25 ++++++++++++ src/components/AddressOrENSNameNotFound.tsx | 13 +++++-- 4 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 src/components/AddressOrENSNameInvalidNonce.tsx diff --git a/src/AddressTransaction.tsx b/src/AddressTransaction.tsx index da709ed..7c4ee76 100644 --- a/src/AddressTransaction.tsx +++ b/src/AddressTransaction.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useContext } from "react"; import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import StandardFrame from "./StandardFrame"; import AddressOrENSNameNotFound from "./components/AddressOrENSNameNotFound"; +import AddressOrENSNameInvalidNonce from "./components/AddressOrENSNameInvalidNonce"; import { ChecksummedAddress } from "./types"; import { transactionURL } from "./url"; import { useTransactionBySenderAndNonce } from "./useErigonHooks"; @@ -25,18 +26,15 @@ const AddressTransaction: React.FC = () => { }, [navigate, searchParams] ); - const [checksummedAddress, isENS, error] = useAddressOrENS( + const [checksummedAddress, , error] = useAddressOrENS( addressOrName, urlFixer ); const rawNonce = searchParams.get("nonce"); - if (rawNonce === null) { - throw new Error("rawNonce couldn't be undefined here"); - } let nonce: number | undefined = undefined; try { - nonce = parseInt(rawNonce); + nonce = rawNonce === null ? undefined : parseInt(rawNonce); } catch (err) { // ignore } @@ -48,7 +46,35 @@ const AddressTransaction: React.FC = () => { ); if (error) { - return ; + return ( + + + + ); + } + if (checksummedAddress !== undefined && rawNonce === null) { + return ( + + + + ); + } + if ( + checksummedAddress !== undefined && + nonce !== undefined && + txHash === null + ) { + return ( + + + + ); } if (txHash) { navigate(transactionURL(txHash)); diff --git a/src/components/AddressOrENSName.tsx b/src/components/AddressOrENSName.tsx index 9f43662..b884c48 100644 --- a/src/components/AddressOrENSName.tsx +++ b/src/components/AddressOrENSName.tsx @@ -1,11 +1,12 @@ import React, { useContext } from "react"; +import PlainAddress from "./PlainAddress"; import { resolverRendererRegistry } from "../api/address-resolver"; import { useResolvedAddress } from "../useResolvedAddresses"; import { RuntimeContext } from "../useRuntime"; -import PlainAddress from "./PlainAddress"; +import { ChecksummedAddress } from "../types"; type AddressOrENSNameProps = { - address: string; + address: ChecksummedAddress; selectedAddress?: string; dontOverrideColors?: boolean; }; diff --git a/src/components/AddressOrENSNameInvalidNonce.tsx b/src/components/AddressOrENSNameInvalidNonce.tsx new file mode 100644 index 0000000..a92496a --- /dev/null +++ b/src/components/AddressOrENSNameInvalidNonce.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import StandardSubtitle from "../StandardSubtitle"; +import ContentFrame from "../ContentFrame"; +import AddressOrENSName from "./AddressOrENSName"; + +type AddressOrENSNameInvalidNonceProps = { + addressOrENSName: string; + nonce: string; +}; + +const AddressOrENSNameInvalidNonce: React.FC< + AddressOrENSNameInvalidNonceProps +> = ({ addressOrENSName, nonce }) => ( + <> + Transaction Details + +
+ + : no transaction found for nonce="{nonce}". +
+
+ +); + +export default React.memo(AddressOrENSNameInvalidNonce); diff --git a/src/components/AddressOrENSNameNotFound.tsx b/src/components/AddressOrENSNameNotFound.tsx index 6798124..437aa6c 100644 --- a/src/components/AddressOrENSNameNotFound.tsx +++ b/src/components/AddressOrENSNameNotFound.tsx @@ -1,4 +1,6 @@ import React from "react"; +import StandardSubtitle from "../StandardSubtitle"; +import ContentFrame from "../ContentFrame"; type AddressOrENSNameNotFoundProps = { addressOrENSName: string; @@ -7,9 +9,14 @@ type AddressOrENSNameNotFoundProps = { const AddressOrENSNameNotFound: React.FC = ({ addressOrENSName, }) => ( - - "{addressOrENSName}" is not an ETH address or ENS name. - + <> + Transaction Details + +
+ "{addressOrENSName}" is not an ETH address or ENS name. +
+
+ ); export default React.memo(AddressOrENSNameNotFound); From e6d60f1fcd6f68929b529a4a203f137acd4379ea Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 31 Jan 2022 15:37:29 -0300 Subject: [PATCH 60/72] =?UTF-8?q?Change=20address=20tx=20by=20nonce=20url?= =?UTF-8?q?=20to=20addressOrName=3Fnonce=3D=3F=20format?= --- src/Address.tsx | 24 +++++++++ src/AddressTransaction.tsx | 85 ------------------------------- src/AddressTransactionByNonce.tsx | 57 +++++++++++++++++++++ src/App.tsx | 10 ---- 4 files changed, 81 insertions(+), 95 deletions(-) delete mode 100644 src/AddressTransaction.tsx create mode 100644 src/AddressTransactionByNonce.tsx diff --git a/src/Address.tsx b/src/Address.tsx index 3bfbd41..da1f959 100644 --- a/src/Address.tsx +++ b/src/Address.tsx @@ -26,6 +26,13 @@ import { useMultipleMetadata } from "./sourcify/useSourcify"; import { ChecksummedAddress } from "./types"; import { useAddressesWithCode } from "./useErigonHooks"; +const AddressTransactionByNonce = React.lazy( + () => + import( + /* webpackChunkName: "addresstxbynonce", webpackPrefetch: true */ "./AddressTransactionByNonce" + ) +); + const Address: React.FC = () => { const { provider } = useContext(RuntimeContext); const { addressOrName, direction } = useParams(); @@ -79,6 +86,23 @@ const Address: React.FC = () => { ? metadatas[checksummedAddress] : undefined; + // Search address by nonce === transaction @ nonce + const rawNonce = searchParams.get("nonce"); + if (rawNonce !== null) { + let nonce: number | undefined = undefined; + try { + nonce = parseInt(rawNonce); + } catch (err) { + // ignore + } + return ( + + ); + } + return ( {error ? ( diff --git a/src/AddressTransaction.tsx b/src/AddressTransaction.tsx deleted file mode 100644 index 7c4ee76..0000000 --- a/src/AddressTransaction.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { useCallback, useContext } from "react"; -import { useNavigate, useParams, useSearchParams } from "react-router-dom"; -import StandardFrame from "./StandardFrame"; -import AddressOrENSNameNotFound from "./components/AddressOrENSNameNotFound"; -import AddressOrENSNameInvalidNonce from "./components/AddressOrENSNameInvalidNonce"; -import { ChecksummedAddress } from "./types"; -import { transactionURL } from "./url"; -import { useTransactionBySenderAndNonce } from "./useErigonHooks"; -import { useAddressOrENS } from "./useResolvedAddresses"; -import { RuntimeContext } from "./useRuntime"; - -const AddressTransaction: React.FC = () => { - const { provider } = useContext(RuntimeContext); - const navigate = useNavigate(); - const { addressOrName } = useParams(); - if (addressOrName === undefined) { - throw new Error("addressOrName couldn't be undefined here"); - } - - const [searchParams] = useSearchParams(); - const urlFixer = useCallback( - (address: ChecksummedAddress) => { - navigate(`/address/${address}/tx?${searchParams.toString()}`, { - replace: true, - }); - }, - [navigate, searchParams] - ); - const [checksummedAddress, , error] = useAddressOrENS( - addressOrName, - urlFixer - ); - - const rawNonce = searchParams.get("nonce"); - let nonce: number | undefined = undefined; - try { - nonce = rawNonce === null ? undefined : parseInt(rawNonce); - } catch (err) { - // ignore - } - - const txHash = useTransactionBySenderAndNonce( - provider, - checksummedAddress, - nonce - ); - - if (error) { - return ( - - - - ); - } - if (checksummedAddress !== undefined && rawNonce === null) { - return ( - - - - ); - } - if ( - checksummedAddress !== undefined && - nonce !== undefined && - txHash === null - ) { - return ( - - - - ); - } - if (txHash) { - navigate(transactionURL(txHash)); - } - return ; -}; - -export default AddressTransaction; diff --git a/src/AddressTransactionByNonce.tsx b/src/AddressTransactionByNonce.tsx new file mode 100644 index 0000000..c720bd9 --- /dev/null +++ b/src/AddressTransactionByNonce.tsx @@ -0,0 +1,57 @@ +import React, { useContext } from "react"; +import { useNavigate } from "react-router-dom"; +import StandardFrame from "./StandardFrame"; +import AddressOrENSNameInvalidNonce from "./components/AddressOrENSNameInvalidNonce"; +import { ChecksummedAddress } from "./types"; +import { transactionURL } from "./url"; +import { useTransactionBySenderAndNonce } from "./useErigonHooks"; +import { RuntimeContext } from "./useRuntime"; + +type AddressTransactionByNonceProps = { + checksummedAddress: ChecksummedAddress | undefined; + nonce: number | undefined; +}; + +const AddressTransactionByNonce: React.FC = ({ + checksummedAddress, + nonce, +}) => { + const { provider } = useContext(RuntimeContext); + const txHash = useTransactionBySenderAndNonce( + provider, + checksummedAddress, + nonce + ); + const navigate = useNavigate(); + + if (checksummedAddress !== undefined && nonce === undefined) { + return ( + + + + ); + } + if ( + checksummedAddress !== undefined && + nonce !== undefined && + txHash === null + ) { + return ( + + + + ); + } + if (txHash) { + navigate(transactionURL(txHash)); + } + return ; +}; + +export default AddressTransactionByNonce; diff --git a/src/App.tsx b/src/App.tsx index c75c474..aae4ab8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,12 +21,6 @@ const Address = React.lazy( () => import(/* webpackChunkName: "address", webpackPrefetch: true */ "./Address") ); -const AddressTransaction = React.lazy( - () => - import( - /* webpackChunkName: "addresstx", webpackPrefetch: true */ "./AddressTransaction" - ) -); const Transaction = React.lazy( () => import(/* webpackChunkName: "tx", webpackPrefetch: true */ "./Transaction") @@ -67,10 +61,6 @@ const App = () => { path="address/:addressOrName/*" element={
} /> - } - /> } /> From 17194c089a431a71c5b250a7e950cedeab7b89e7 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 31 Jan 2022 15:53:25 -0300 Subject: [PATCH 61/72] Add 404 page --- src/App.tsx | 8 +++++++- src/PageNotFound.tsx | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/PageNotFound.tsx diff --git a/src/App.tsx b/src/App.tsx index aae4ab8..62d3b2c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -31,6 +31,12 @@ const London = React.lazy( /* webpackChunkName: "london", webpackPrefetch: true */ "./special/london/London" ) ); +const PageNotFound = React.lazy( + () => + import( + /* webpackChunkName: "notfound", webpackPrefetch: true */ "./PageNotFound" + ) +); const App = () => { const runtime = useRuntime(); @@ -61,7 +67,7 @@ const App = () => { path="address/:addressOrName/*" element={
} /> - } /> + } /> diff --git a/src/PageNotFound.tsx b/src/PageNotFound.tsx new file mode 100644 index 0000000..690f0a0 --- /dev/null +++ b/src/PageNotFound.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { NavLink } from "react-router-dom"; +import StandardFrame from "./StandardFrame"; + +const PageNotFound: React.FC = () => ( + +
+ Page not found! + + Click here to go to home + +
+
+); + +export default PageNotFound; From 5abf171be96bb39d0690884e41a890be85931fb8 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 31 Jan 2022 16:26:27 -0300 Subject: [PATCH 62/72] Fix invalid nonce handling --- src/Address.tsx | 8 +------- src/AddressTransactionByNonce.tsx | 18 ++++++++---------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/Address.tsx b/src/Address.tsx index da1f959..3b9b62d 100644 --- a/src/Address.tsx +++ b/src/Address.tsx @@ -89,16 +89,10 @@ const Address: React.FC = () => { // Search address by nonce === transaction @ nonce const rawNonce = searchParams.get("nonce"); if (rawNonce !== null) { - let nonce: number | undefined = undefined; - try { - nonce = parseInt(rawNonce); - } catch (err) { - // ignore - } return ( ); } diff --git a/src/AddressTransactionByNonce.tsx b/src/AddressTransactionByNonce.tsx index c720bd9..1478092 100644 --- a/src/AddressTransactionByNonce.tsx +++ b/src/AddressTransactionByNonce.tsx @@ -9,36 +9,34 @@ import { RuntimeContext } from "./useRuntime"; type AddressTransactionByNonceProps = { checksummedAddress: ChecksummedAddress | undefined; - nonce: number | undefined; + rawNonce: string; }; const AddressTransactionByNonce: React.FC = ({ checksummedAddress, - nonce, + rawNonce, }) => { const { provider } = useContext(RuntimeContext); + + const nonce = parseInt(rawNonce, 10); const txHash = useTransactionBySenderAndNonce( provider, checksummedAddress, - nonce + isNaN(nonce) ? undefined : nonce ); const navigate = useNavigate(); - if (checksummedAddress !== undefined && nonce === undefined) { + if (checksummedAddress !== undefined && isNaN(nonce)) { return ( ); } - if ( - checksummedAddress !== undefined && - nonce !== undefined && - txHash === null - ) { + if (checksummedAddress !== undefined && !isNaN(nonce) && txHash === null) { return ( Date: Mon, 31 Jan 2022 16:28:08 -0300 Subject: [PATCH 63/72] Fix extra redirect --- src/AddressTransactionByNonce.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AddressTransactionByNonce.tsx b/src/AddressTransactionByNonce.tsx index 1478092..9e1763b 100644 --- a/src/AddressTransactionByNonce.tsx +++ b/src/AddressTransactionByNonce.tsx @@ -47,7 +47,7 @@ const AddressTransactionByNonce: React.FC = ({ ); } if (txHash) { - navigate(transactionURL(txHash)); + navigate(transactionURL(txHash), { replace: true }); } return ; }; From b0c31441c45da9da63a72044474b321d11f08a30 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 31 Jan 2022 16:35:34 -0300 Subject: [PATCH 64/72] Simplify logic --- src/AddressTransactionByNonce.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/AddressTransactionByNonce.tsx b/src/AddressTransactionByNonce.tsx index 9e1763b..d752138 100644 --- a/src/AddressTransactionByNonce.tsx +++ b/src/AddressTransactionByNonce.tsx @@ -26,7 +26,12 @@ const AddressTransactionByNonce: React.FC = ({ ); const navigate = useNavigate(); - if (checksummedAddress !== undefined && isNaN(nonce)) { + if (checksummedAddress === undefined) { + return ; + } + + // Garbage nonce + if (isNaN(nonce)) { return ( = ({ ); } - if (checksummedAddress !== undefined && !isNaN(nonce) && txHash === null) { + + // Valid nonce, but no tx found + if (!txHash) { return ( = ({ ); } - if (txHash) { - navigate(transactionURL(txHash), { replace: true }); - } + + // Success; replace and render filler + navigate(transactionURL(txHash), { replace: true }); return ; }; From 9d7709ca635580939eeadae1a39088a4b1b474b9 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 31 Jan 2022 16:49:58 -0300 Subject: [PATCH 65/72] Add "latest" support for nonce param --- src/AddressTransactionByNonce.tsx | 34 +++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/AddressTransactionByNonce.tsx b/src/AddressTransactionByNonce.tsx index d752138..a5bb17e 100644 --- a/src/AddressTransactionByNonce.tsx +++ b/src/AddressTransactionByNonce.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import StandardFrame from "./StandardFrame"; import AddressOrENSNameInvalidNonce from "./components/AddressOrENSNameInvalidNonce"; @@ -18,15 +18,41 @@ const AddressTransactionByNonce: React.FC = ({ }) => { const { provider } = useContext(RuntimeContext); - const nonce = parseInt(rawNonce, 10); + // Calculate txCount ONLY when asked for latest nonce + const [txCount, setTxCount] = useState(); + useEffect(() => { + if (!provider || !checksummedAddress || rawNonce !== "latest") { + setTxCount(undefined); + return; + } + + const readTxCount = async () => { + const count = await provider.getTransactionCount(checksummedAddress); + setTxCount(count); + }; + readTxCount(); + }, [provider, checksummedAddress, rawNonce]); + + // Determine desired nonce from parse int query param or txCount - 1 nonce + // in case of latest + let nonce: number | undefined; + if (rawNonce === "latest") { + if (txCount !== undefined && txCount > 0) { + nonce = txCount - 1; + } + } else { + nonce = parseInt(rawNonce, 10); + } + + // Given all base params are determined, get the corresponding tx const txHash = useTransactionBySenderAndNonce( provider, checksummedAddress, - isNaN(nonce) ? undefined : nonce + nonce !== undefined && isNaN(nonce) ? undefined : nonce ); const navigate = useNavigate(); - if (checksummedAddress === undefined) { + if (checksummedAddress === undefined || nonce === undefined) { return ; } From c728dd881d16c60724acfb67e7873218f8c9707f Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 31 Jan 2022 17:06:49 -0300 Subject: [PATCH 66/72] Handle search for latest nonce on uninitialized addresses --- src/AddressTransactionByNonce.tsx | 13 ++++++++++++- src/components/AddressOrENSNameNoTx.tsx | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/components/AddressOrENSNameNoTx.tsx diff --git a/src/AddressTransactionByNonce.tsx b/src/AddressTransactionByNonce.tsx index a5bb17e..f7e1a8a 100644 --- a/src/AddressTransactionByNonce.tsx +++ b/src/AddressTransactionByNonce.tsx @@ -2,6 +2,7 @@ import React, { useContext, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import StandardFrame from "./StandardFrame"; import AddressOrENSNameInvalidNonce from "./components/AddressOrENSNameInvalidNonce"; +import AddressOrENSNameNoTx from "./components/AddressOrENSNameNoTx"; import { ChecksummedAddress } from "./types"; import { transactionURL } from "./url"; import { useTransactionBySenderAndNonce } from "./useErigonHooks"; @@ -37,7 +38,7 @@ const AddressTransactionByNonce: React.FC = ({ // in case of latest let nonce: number | undefined; if (rawNonce === "latest") { - if (txCount !== undefined && txCount > 0) { + if (txCount !== undefined) { nonce = txCount - 1; } } else { @@ -52,10 +53,20 @@ const AddressTransactionByNonce: React.FC = ({ ); const navigate = useNavigate(); + // Loading... if (checksummedAddress === undefined || nonce === undefined) { return ; } + // Address hasn't made the first outbound tx yet + if (nonce < 0) { + return ( + + + + ); + } + // Garbage nonce if (isNaN(nonce)) { return ( diff --git a/src/components/AddressOrENSNameNoTx.tsx b/src/components/AddressOrENSNameNoTx.tsx new file mode 100644 index 0000000..c4f6178 --- /dev/null +++ b/src/components/AddressOrENSNameNoTx.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import StandardSubtitle from "../StandardSubtitle"; +import ContentFrame from "../ContentFrame"; +import AddressOrENSName from "./AddressOrENSName"; + +type AddressOrENSNameNoTxProps = { + addressOrENSName: string; +}; + +const AddressOrENSNameNoTx: React.FC = ({ + addressOrENSName, +}) => ( + <> + Transaction Details + +
+ + : no outbound transactions found. +
+
+ +); + +export default React.memo(AddressOrENSNameNoTx); From 3695db552bb1c6ef90dd81d6a7e35d0f24e75e6c Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 1 Feb 2022 15:19:42 -0300 Subject: [PATCH 67/72] Handle negative nonce --- src/AddressTransactionByNonce.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/AddressTransactionByNonce.tsx b/src/AddressTransactionByNonce.tsx index f7e1a8a..21324f6 100644 --- a/src/AddressTransactionByNonce.tsx +++ b/src/AddressTransactionByNonce.tsx @@ -43,6 +43,9 @@ const AddressTransactionByNonce: React.FC = ({ } } else { nonce = parseInt(rawNonce, 10); + if (nonce < 0) { + nonce = NaN; + } } // Given all base params are determined, get the corresponding tx From 6a6e2cdb149786d54bd7b362718b010e9fa369ea Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 1 Feb 2022 15:31:07 -0300 Subject: [PATCH 68/72] Retrofit search urls into search --- src/search/search.ts | 86 ++++++++++---------------------------------- 1 file changed, 19 insertions(+), 67 deletions(-) diff --git a/src/search/search.ts b/src/search/search.ts index e1e6730..95ebdce 100644 --- a/src/search/search.ts +++ b/src/search/search.ts @@ -2,7 +2,6 @@ import { ChangeEventHandler, FormEventHandler, RefObject, - useContext, useRef, useState, } from "react"; @@ -13,8 +12,6 @@ import { isHexString } from "@ethersproject/bytes"; import useKeyboardShortcut from "use-keyboard-shortcut"; import { PAGE_SIZE } from "../params"; import { ProcessedTransaction, TransactionChunk } from "../types"; -import { RuntimeContext } from "../useRuntime"; -import { getTransactionBySenderAndNonceFetcher } from "../useErigonHooks"; export class SearchController { private txs: ProcessedTransaction[]; @@ -209,38 +206,36 @@ export class SearchController { } } -const doSearch = async ( - provider: JsonRpcProvider, - q: string, - navigate: NavigateFunction -) => { +const doSearch = async (q: string, navigate: NavigateFunction) => { // Cleanup q = q.trim(); let maybeAddress = q; - let maybeIndex: string | undefined; + let maybeIndex = ""; const sepIndex = q.lastIndexOf(":"); if (sepIndex !== -1) { maybeAddress = q.substring(0, sepIndex); maybeIndex = q.substring(sepIndex + 1); } + // Plain address? if (isAddress(maybeAddress)) { - // Plain address + nonce? - if (await navigateToTx(provider, maybeAddress, maybeIndex, navigate)) { - return; - } - - // Plain address - navigate(`/address/${maybeAddress}`, { replace: true }); + navigate( + `/address/${maybeAddress}${ + maybeIndex !== "" ? `?nonce=${maybeIndex}` : "" + }`, + { replace: true } + ); return; } + // Tx hash? if (isHexString(q, 32)) { navigate(`/tx/${q}`, { replace: true }); return; } + // Block number? const blockNumber = parseInt(q); if (!isNaN(blockNumber)) { navigate(`/block/${blockNumber}`, { replace: true }); @@ -248,54 +243,12 @@ const doSearch = async ( } // Assume it is an ENS name - const resolvedName = await provider.resolveName(maybeAddress); - if (resolvedName !== null) { - // ENS name + nonce? - if (await navigateToTx(provider, resolvedName, maybeIndex, navigate)) { - return; - } - - // ENS name - navigate(`/address/${maybeAddress}`, { replace: true }); - return; - } - - // TODO: handle default -}; - -const navigateToTx = async ( - provider: JsonRpcProvider, - maybeAddress: string, - maybeIndex: string | undefined, - navigate: NavigateFunction -): Promise => { - if (maybeIndex !== undefined) { - try { - let nonce = 0; - if (maybeIndex === "latest") { - const count = await provider.getTransactionCount(maybeAddress); - if (count > 0) { - nonce = count - 1; - } - } else { - nonce = parseInt(maybeIndex); - } - - const txHash = await getTransactionBySenderAndNonceFetcher({ - provider, - sender: maybeAddress, - nonce, - }); - if (txHash) { - navigate(`/tx/${txHash}`, { replace: true }); - } - return true; - } catch (err) { - // ignore - } - } - - return false; + navigate( + `/address/${maybeAddress}${ + maybeIndex !== "" ? `?nonce=${maybeIndex}` : "" + }`, + { replace: true } + ); }; export const useGenericSearch = (): [ @@ -303,7 +256,6 @@ export const useGenericSearch = (): [ ChangeEventHandler, FormEventHandler ] => { - const { provider } = useContext(RuntimeContext); const [searchString, setSearchString] = useState(""); const [canSubmit, setCanSubmit] = useState(false); const navigate = useNavigate(); @@ -316,14 +268,14 @@ export const useGenericSearch = (): [ const handleSubmit: React.FormEventHandler = (e) => { e.preventDefault(); - if (!canSubmit || !provider) { + if (!canSubmit) { return; } if (searchRef.current) { searchRef.current.value = ""; } - doSearch(provider, searchString, navigate); + doSearch(searchString, navigate); }; const searchRef = useRef(null); From f9f7a569e2ad52928d1c9f922f798c9e1126e3a1 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 1 Feb 2022 15:53:45 -0300 Subject: [PATCH 69/72] Make nav nonce control dumb; use just urls for navigation; speed up with prefetch --- src/transaction/NavButton.tsx | 13 ++++++++----- src/transaction/NavNonce.tsx | 24 +++++------------------- src/url.ts | 3 +++ 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/transaction/NavButton.tsx b/src/transaction/NavButton.tsx index 04d421c..cad49d2 100644 --- a/src/transaction/NavButton.tsx +++ b/src/transaction/NavButton.tsx @@ -1,18 +1,21 @@ import { NavLink } from "react-router-dom"; -import { transactionURL } from "../url"; +import { ChecksummedAddress } from "../types"; +import { addressByNonceURL } from "../url"; // TODO: extract common component with block/NavButton type NavButtonProps = { - txHash: string | null | undefined; + sender: ChecksummedAddress; + nonce: number; disabled?: boolean; }; const NavButton: React.FC = ({ - txHash, + sender, + nonce, disabled, children, }) => { - if (disabled || txHash === null || txHash === undefined) { + if (disabled) { return ( {children} @@ -23,7 +26,7 @@ const NavButton: React.FC = ({ return ( {children} diff --git a/src/transaction/NavNonce.tsx b/src/transaction/NavNonce.tsx index ee30a4b..fb94b66 100644 --- a/src/transaction/NavNonce.tsx +++ b/src/transaction/NavNonce.tsx @@ -7,7 +7,6 @@ import { ChecksummedAddress } from "../types"; import { RuntimeContext } from "../useRuntime"; import { prefetchTransactionBySenderAndNonce, - useTransactionBySenderAndNonce, useTransactionCount, } from "../useErigonHooks"; import { useSWRConfig } from "swr"; @@ -19,22 +18,7 @@ type NavNonceProps = { const NavNonce: React.FC = ({ sender, nonce }) => { const { provider } = useContext(RuntimeContext); - const prevTxHash = useTransactionBySenderAndNonce( - provider, - sender, - nonce - 1 - ); - const nextTxHash = useTransactionBySenderAndNonce( - provider, - sender, - nonce + 1 - ); const count = useTransactionCount(provider, sender); - const lastTxHash = useTransactionBySenderAndNonce( - provider, - sender, - count !== undefined ? count - 1 : undefined - ); // Prefetch const swrConfig = useSWRConfig(); @@ -60,17 +44,19 @@ const NavNonce: React.FC = ({ sender, nonce }) => { return (
- + = count - 1} > = count - 1} > diff --git a/src/url.ts b/src/url.ts index 2216bf6..cf44ab6 100644 --- a/src/url.ts +++ b/src/url.ts @@ -20,6 +20,9 @@ export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`; export const transactionURL = (txHash: string) => `/tx/${txHash}`; +export const addressByNonceURL = (address: ChecksummedAddress, nonce: number) => + `/address/${address}?nonce=${nonce}`; + export enum SourcifySource { // Resolve trusted IPNS for root IPFS IPFS_IPNS, From 97f6ae89fe5ea641e28e2846eaa656786c08ae8e Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 1 Feb 2022 16:01:23 -0300 Subject: [PATCH 70/72] Fix display of error message while loading tx by nonce --- src/AddressTransactionByNonce.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/AddressTransactionByNonce.tsx b/src/AddressTransactionByNonce.tsx index 21324f6..e804f84 100644 --- a/src/AddressTransactionByNonce.tsx +++ b/src/AddressTransactionByNonce.tsx @@ -57,7 +57,11 @@ const AddressTransactionByNonce: React.FC = ({ const navigate = useNavigate(); // Loading... - if (checksummedAddress === undefined || nonce === undefined) { + if ( + checksummedAddress === undefined || + nonce === undefined || + txHash === undefined + ) { return ; } @@ -83,7 +87,7 @@ const AddressTransactionByNonce: React.FC = ({ } // Valid nonce, but no tx found - if (!txHash) { + if (txHash === null) { return ( Date: Tue, 1 Feb 2022 19:14:05 -0300 Subject: [PATCH 71/72] Fix prefetch --- src/transaction/NavNonce.tsx | 25 +++++++--------- src/useErigonHooks.ts | 58 ++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/transaction/NavNonce.tsx b/src/transaction/NavNonce.tsx index fb94b66..9efd47f 100644 --- a/src/transaction/NavNonce.tsx +++ b/src/transaction/NavNonce.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from "react"; +import React, { useContext, useEffect } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChevronLeft } from "@fortawesome/free-solid-svg-icons/faChevronLeft"; import { faChevronRight } from "@fortawesome/free-solid-svg-icons/faChevronRight"; @@ -22,25 +22,22 @@ const NavNonce: React.FC = ({ sender, nonce }) => { // Prefetch const swrConfig = useSWRConfig(); - const prefetch = () => { - if (provider && sender && nonce !== undefined) { + useEffect(() => { + if (!provider || !sender || nonce === undefined || count === undefined) { + return; + } + + prefetchTransactionBySenderAndNonce(swrConfig, provider, sender, nonce - 1); + prefetchTransactionBySenderAndNonce(swrConfig, provider, sender, nonce + 1); + if (count > 0) { prefetchTransactionBySenderAndNonce( swrConfig, provider, sender, - nonce - 2 - ); - prefetchTransactionBySenderAndNonce( - swrConfig, - provider, - sender, - nonce + 2 + count - 1 ); } - }; - - // Always prefetch - prefetch(); + }, [swrConfig, provider, sender, nonce, count]); return (
diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index 7eb847c..45378f5 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -530,39 +530,49 @@ export const useTransactionCount = ( }; type TransactionBySenderAndNonceKey = { - provider: JsonRpcProvider; + network: number; sender: ChecksummedAddress; nonce: number; }; -export const getTransactionBySenderAndNonceFetcher = async ({ - provider, - sender, - nonce, -}: TransactionBySenderAndNonceKey): Promise => { - if (nonce < 0) { - return undefined; - } - - const result = (await provider.send("ots_getTransactionBySenderAndNonce", [ +const getTransactionBySenderAndNonceFetcher = + (provider: JsonRpcProvider) => + async ({ + network, sender, nonce, - ])) as string; + }: TransactionBySenderAndNonceKey): Promise => { + if (nonce < 0) { + return undefined; + } - // Empty or success - return result; -}; + const result = (await provider.send("ots_getTransactionBySenderAndNonce", [ + sender, + nonce, + ])) as string; + + // Empty or success + return result; + }; export const prefetchTransactionBySenderAndNonce = ( - { cache, mutate }: ReturnType, + { mutate }: ReturnType, provider: JsonRpcProvider, sender: ChecksummedAddress, nonce: number ) => { - const key: TransactionBySenderAndNonceKey = { provider, sender, nonce }; - if (cache.get(key)) { - mutate(key, getTransactionBySenderAndNonceFetcher(key)); - } + const key: TransactionBySenderAndNonceKey = { + network: provider.network.chainId, + sender, + nonce, + }; + mutate(key, (curr: any) => { + if (curr) { + return curr; + } + return getTransactionBySenderAndNonceFetcher(provider)(key); + }); + // } }; export const useTransactionBySenderAndNonce = ( @@ -576,9 +586,13 @@ export const useTransactionBySenderAndNonce = ( TransactionBySenderAndNonceKey | null >( provider && sender && nonce !== undefined - ? { provider, sender, nonce } + ? { + network: provider.network.chainId, + sender, + nonce, + } : null, - getTransactionBySenderAndNonceFetcher + getTransactionBySenderAndNonceFetcher(provider!) ); if (error) { From f52119a5400a317cf6ccfeb3778cb469e5291824 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 3 Feb 2022 15:14:05 -0300 Subject: [PATCH 72/72] Handle non-used addresses in search fix#67 --- src/address/AddressTransactionResults.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/address/AddressTransactionResults.tsx b/src/address/AddressTransactionResults.tsx index 8ca2118..cc8665e 100644 --- a/src/address/AddressTransactionResults.tsx +++ b/src/address/AddressTransactionResults.tsx @@ -132,8 +132,8 @@ const AddressTransactionResults: React.FC = ({ address={address} isFirst={controller?.isFirst} isLast={controller?.isLast} - prevHash={page ? page[0].hash : ""} - nextHash={page ? page[page.length - 1].hash : ""} + prevHash={page?.[0]?.hash ?? ""} + nextHash={page?.[page.length - 1]?.hash ?? ""} disabled={controller === undefined} />
@@ -165,8 +165,8 @@ const AddressTransactionResults: React.FC = ({ address={address} isFirst={controller?.isFirst} isLast={controller?.isLast} - prevHash={page ? page[0].hash : ""} - nextHash={page ? page[page.length - 1].hash : ""} + prevHash={page?.[0]?.hash ?? ""} + nextHash={page?.[page.length - 1]?.hash ?? ""} disabled={controller === undefined} />