Merge branch 'feature/sourcify-tests' into develop
This commit is contained in:
commit
08ae32ad91
14
README.md
14
README.md
|
@ -138,7 +138,15 @@ This is the preferred way to run Otterscan. You can read about other ways [here]
|
|||
|
||||
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.
|
||||
|
||||
## Kudos
|
||||
## Source verification
|
||||
|
||||
We make use of [Sourcify](https://sourcify.dev/) for displaying contract verification info.
|
||||
|
||||
More info [here](docs/sourcify.md).
|
||||
|
||||
## Kudos (in no particular order)
|
||||
|
||||
We make use of many open-source software and integrate many public datasources, mainly:
|
||||
|
||||
To the [Geth](https://geth.ethereum.org/) team whose code Erigon is based on.
|
||||
|
||||
|
@ -150,6 +158,8 @@ To [Trust Wallet](https://github.com/trustwallet/assets) who sponsor and make av
|
|||
|
||||
To the owners of the [4bytes repository](https://github.com/ethereum-lists/4bytes) that we import and use to translate the method selectors to human-friendly strings.
|
||||
|
||||
To [Sourcify](https://sourcify.dev/), a public decentralized source code and metadata verification service.
|
||||
|
||||
To [Ethers](https://github.com/ethers-io/ethers.js/) which is the client library we used to interact with the ETH node. It is high level enough to hide most jsonrpc particularities, but flexible enough to allow easy interaction with custom jsonrpc methods.
|
||||
|
||||
## Future
|
||||
|
@ -180,4 +190,4 @@ Follow the creator on Twitter for updates ([@wmitsuda](https://twitter.com/wmits
|
|||
|
||||
### Donation address
|
||||
|
||||
If you like this project, feel free to send donations to `otterscan.eth`
|
||||
If you like this project, feel free to send donations to `otterscan.eth` or use our gitcoin grant page: https://gitcoin.co/grants/3224/otterscan
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# Sourcify
|
||||
|
||||
We get the contract source code and metadata from [Sourcify](https://sourcify.dev/).
|
||||
|
||||
There are multiple ways to consume their data we support, each one with pros and cons:
|
||||
|
||||
## IPNS/IPFS
|
||||
|
||||
This is the default integration method, we resolve the public Sourcify IPNS to get the latest known IPFS root hash of their repository.
|
||||
|
||||
The downside is that recently verified contracts may not have yet been added to the root hash and republished into IPNS.
|
||||
|
||||
## Direct HTTP connection to Sourcify's repository
|
||||
|
||||
Standard HTTP connection to their repo at https://repo.sourcify.dev/
|
||||
|
||||
Fast access to fresh verified data. On the other hand it is less private and centralized.
|
||||
|
||||
## Local snapshot
|
||||
|
||||
As a midterm solution, we are making available a snapshot docker image of their repository, containing only mainnet full verified contracts.
|
||||
|
||||
This would allow you to play with existing contracts up to the snapshot date/time locally, not depending on their service or IPFS connectivity availability.
|
|
@ -30,9 +30,12 @@
|
|||
"@types/react": "^17.0.20",
|
||||
"@types/react-blockies": "^1.4.1",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/react-highlight": "^0.12.3",
|
||||
"@types/react-router-dom": "^5.1.8",
|
||||
"@types/react-syntax-highlighter": "^13.5.2",
|
||||
"chart.js": "^3.5.1",
|
||||
"ethers": "^5.4.1",
|
||||
"highlightjs-solidity": "^1.2.0",
|
||||
"query-string": "^7.0.1",
|
||||
"react": "^17.0.2",
|
||||
"react-blockies": "^1.4.1",
|
||||
|
@ -42,6 +45,7 @@
|
|||
"react-image": "^4.0.3",
|
||||
"react-router-dom": "^5.2.1",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-syntax-highlighter": "^15.4.4",
|
||||
"serve": "^12.0.0",
|
||||
"typescript": "^4.4.2",
|
||||
"use-keyboard-shortcut": "^1.0.6",
|
||||
|
@ -3004,6 +3008,14 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/hast": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.2.tgz",
|
||||
"integrity": "sha512-Op5W7jYgZI7AWKY5wQ0/QNMzQM7dGQPyW1rXKNiymVCy5iTfdPuGu4HhYNOM2sIv8gUfIuIdcYlXmAepwaowow==",
|
||||
"dependencies": {
|
||||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/history": {
|
||||
"version": "4.7.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz",
|
||||
|
@ -3103,6 +3115,14 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-highlight": {
|
||||
"version": "0.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-highlight/-/react-highlight-0.12.3.tgz",
|
||||
"integrity": "sha512-mfhuHdE3dUjvRv1lvZIvda2B+VW7rkG1ufnFLKbDcRUp/L73bGUmEuEfpnjgdLgeWYho88ahQZRcMSh9GsZA0g==",
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-router": {
|
||||
"version": "5.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.15.tgz",
|
||||
|
@ -3122,6 +3142,14 @@
|
|||
"@types/react-router": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-syntax-highlighter": {
|
||||
"version": "13.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-13.5.2.tgz",
|
||||
"integrity": "sha512-sRZoKZBGKaE7CzMvTTgz+0x/aVR58ZYUTfB7HN76vC+yQnvo1FWtzNARBt0fGqcLGEVakEzMu/CtPzssmanu8Q==",
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "0.0.8",
|
||||
"license": "MIT",
|
||||
|
@ -3159,6 +3187,11 @@
|
|||
"source-map": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
|
||||
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="
|
||||
},
|
||||
"node_modules/@types/webpack": {
|
||||
"version": "4.41.26",
|
||||
"license": "MIT",
|
||||
|
@ -5575,6 +5608,33 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/character-entities": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
|
||||
"integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/character-entities-legacy": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
|
||||
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/character-reference-invalid": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
|
||||
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.1.tgz",
|
||||
|
@ -5868,6 +5928,15 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/comma-separated-tokens": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
|
||||
"integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"license": "MIT"
|
||||
|
@ -8426,6 +8495,18 @@
|
|||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fault": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
|
||||
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
|
||||
"dependencies": {
|
||||
"format": "^0.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/faye-websocket": {
|
||||
"version": "0.11.3",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -8802,6 +8883,14 @@
|
|||
"node": ">= 0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/format": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
|
||||
"integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=",
|
||||
"engines": {
|
||||
"node": ">=0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.1.2",
|
||||
"license": "MIT",
|
||||
|
@ -9196,6 +9285,31 @@
|
|||
"minimalistic-assert": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-parse-selector": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
|
||||
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hastscript": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
|
||||
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
|
||||
"dependencies": {
|
||||
"@types/hast": "^2.0.0",
|
||||
"comma-separated-tokens": "^1.0.0",
|
||||
"hast-util-parse-selector": "^2.0.0",
|
||||
"property-information": "^5.0.0",
|
||||
"space-separated-tokens": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/he": {
|
||||
"version": "1.2.0",
|
||||
"license": "MIT",
|
||||
|
@ -9207,6 +9321,19 @@
|
|||
"version": "1.1.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "10.7.3",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/highlightjs-solidity": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-1.2.0.tgz",
|
||||
"integrity": "sha512-KXYcVzBRof3CBWHsxGffsSEAJF0YsPaOk1jgIYv2xSzrBSxkfNUJFXrlE2oZEWvYQKbPqLe4qprJyNbSDV+LZA=="
|
||||
},
|
||||
"node_modules/history": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
||||
|
@ -9756,6 +9883,28 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphabetical": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
|
||||
"integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphanumerical": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
|
||||
"integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
|
||||
"dependencies": {
|
||||
"is-alphabetical": "^1.0.0",
|
||||
"is-decimal": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.1.0",
|
||||
"license": "MIT",
|
||||
|
@ -9856,6 +10005,15 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-decimal": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
|
||||
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-descriptor": {
|
||||
"version": "1.0.2",
|
||||
"license": "MIT",
|
||||
|
@ -9933,6 +10091,15 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-hexadecimal": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
|
||||
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-module": {
|
||||
"version": "1.0.0",
|
||||
"license": "MIT"
|
||||
|
@ -11300,6 +11467,19 @@
|
|||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/lowlight": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
|
||||
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
|
||||
"dependencies": {
|
||||
"fault": "^1.0.0",
|
||||
"highlight.js": "~10.7.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"license": "ISC",
|
||||
|
@ -12454,6 +12634,23 @@
|
|||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-entities": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
|
||||
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
|
||||
"dependencies": {
|
||||
"character-entities": "^1.0.0",
|
||||
"character-entities-legacy": "^1.0.0",
|
||||
"character-reference-invalid": "^1.0.0",
|
||||
"is-alphanumerical": "^1.0.0",
|
||||
"is-decimal": "^1.0.0",
|
||||
"is-hexadecimal": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"license": "MIT",
|
||||
|
@ -13897,6 +14094,11 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.24.1",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
|
||||
"integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow=="
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"license": "MIT",
|
||||
|
@ -13950,6 +14152,18 @@
|
|||
"version": "16.13.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/property-information": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
|
||||
"integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.6",
|
||||
"license": "MIT",
|
||||
|
@ -14766,6 +14980,21 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-syntax-highlighter": {
|
||||
"version": "15.4.4",
|
||||
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.4.tgz",
|
||||
"integrity": "sha512-PsOFHNTzkb3OroXdoR897eKN5EZ6grht1iM+f1lJSq7/L0YVnkJaNVwC3wEUYPOAmeyl5xyer1DjL6MrumO6Zw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"highlight.js": "^10.4.1",
|
||||
"lowlight": "^1.17.0",
|
||||
"prismjs": "^1.22.0",
|
||||
"refractor": "^3.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 0.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-pkg": {
|
||||
"version": "5.2.0",
|
||||
"license": "MIT",
|
||||
|
@ -14929,6 +15158,20 @@
|
|||
"postcss-value-parser": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/refractor": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.4.0.tgz",
|
||||
"integrity": "sha512-dBeD02lC5eytm9Gld2Mx0cMcnR+zhSnsTfPpWqFaMgUMJfC9A6bcN3Br/NaXrnBJcuxnLFR90k1jrkaSyV8umg==",
|
||||
"dependencies": {
|
||||
"hastscript": "^6.0.0",
|
||||
"parse-entities": "^2.0.0",
|
||||
"prismjs": "~1.24.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerate": {
|
||||
"version": "1.4.2",
|
||||
"license": "MIT"
|
||||
|
@ -16461,6 +16704,15 @@
|
|||
"version": "1.4.8",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/space-separated-tokens": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
|
||||
"integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-correct": {
|
||||
"version": "3.1.1",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -21378,6 +21630,14 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/hast": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.2.tgz",
|
||||
"integrity": "sha512-Op5W7jYgZI7AWKY5wQ0/QNMzQM7dGQPyW1rXKNiymVCy5iTfdPuGu4HhYNOM2sIv8gUfIuIdcYlXmAepwaowow==",
|
||||
"requires": {
|
||||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"@types/history": {
|
||||
"version": "4.7.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz",
|
||||
|
@ -21465,6 +21725,14 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-highlight": {
|
||||
"version": "0.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-highlight/-/react-highlight-0.12.3.tgz",
|
||||
"integrity": "sha512-mfhuHdE3dUjvRv1lvZIvda2B+VW7rkG1ufnFLKbDcRUp/L73bGUmEuEfpnjgdLgeWYho88ahQZRcMSh9GsZA0g==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-router": {
|
||||
"version": "5.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.15.tgz",
|
||||
|
@ -21484,6 +21752,14 @@
|
|||
"@types/react-router": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-syntax-highlighter": {
|
||||
"version": "13.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-13.5.2.tgz",
|
||||
"integrity": "sha512-sRZoKZBGKaE7CzMvTTgz+0x/aVR58ZYUTfB7HN76vC+yQnvo1FWtzNARBt0fGqcLGEVakEzMu/CtPzssmanu8Q==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/resolve": {
|
||||
"version": "0.0.8",
|
||||
"requires": {
|
||||
|
@ -21514,6 +21790,11 @@
|
|||
"source-map": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"@types/unist": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
|
||||
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="
|
||||
},
|
||||
"@types/webpack": {
|
||||
"version": "4.41.26",
|
||||
"requires": {
|
||||
|
@ -23186,6 +23467,21 @@
|
|||
"char-regex": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"character-entities": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
|
||||
"integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw=="
|
||||
},
|
||||
"character-entities-legacy": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
|
||||
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA=="
|
||||
},
|
||||
"character-reference-invalid": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
|
||||
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg=="
|
||||
},
|
||||
"chart.js": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.1.tgz",
|
||||
|
@ -23403,6 +23699,11 @@
|
|||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"comma-separated-tokens": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
|
||||
"integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw=="
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3"
|
||||
},
|
||||
|
@ -25106,6 +25407,14 @@
|
|||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"fault": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
|
||||
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
|
||||
"requires": {
|
||||
"format": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"faye-websocket": {
|
||||
"version": "0.11.3",
|
||||
"requires": {
|
||||
|
@ -25349,6 +25658,11 @@
|
|||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"format": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
|
||||
"integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs="
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2"
|
||||
},
|
||||
|
@ -25599,12 +25913,39 @@
|
|||
"minimalistic-assert": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"hast-util-parse-selector": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
|
||||
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ=="
|
||||
},
|
||||
"hastscript": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
|
||||
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
|
||||
"requires": {
|
||||
"@types/hast": "^2.0.0",
|
||||
"comma-separated-tokens": "^1.0.0",
|
||||
"hast-util-parse-selector": "^2.0.0",
|
||||
"property-information": "^5.0.0",
|
||||
"space-separated-tokens": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"he": {
|
||||
"version": "1.2.0"
|
||||
},
|
||||
"hex-color-regex": {
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "10.7.3",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
|
||||
},
|
||||
"highlightjs-solidity": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-1.2.0.tgz",
|
||||
"integrity": "sha512-KXYcVzBRof3CBWHsxGffsSEAJF0YsPaOk1jgIYv2xSzrBSxkfNUJFXrlE2oZEWvYQKbPqLe4qprJyNbSDV+LZA=="
|
||||
},
|
||||
"history": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
||||
|
@ -25972,6 +26313,20 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"is-alphabetical": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
|
||||
"integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="
|
||||
},
|
||||
"is-alphanumerical": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
|
||||
"integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
|
||||
"requires": {
|
||||
"is-alphabetical": "^1.0.0",
|
||||
"is-decimal": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-arguments": {
|
||||
"version": "1.1.0",
|
||||
"requires": {
|
||||
|
@ -26030,6 +26385,11 @@
|
|||
"is-date-object": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"is-decimal": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
|
||||
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw=="
|
||||
},
|
||||
"is-descriptor": {
|
||||
"version": "1.0.2",
|
||||
"requires": {
|
||||
|
@ -26067,6 +26427,11 @@
|
|||
"is-extglob": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"is-hexadecimal": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
|
||||
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="
|
||||
},
|
||||
"is-module": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
|
@ -26976,6 +27341,15 @@
|
|||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"lowlight": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
|
||||
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
|
||||
"requires": {
|
||||
"fault": "^1.0.0",
|
||||
"highlight.js": "~10.7.0"
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"requires": {
|
||||
|
@ -27720,6 +28094,19 @@
|
|||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"parse-entities": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
|
||||
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
|
||||
"requires": {
|
||||
"character-entities": "^1.0.0",
|
||||
"character-entities-legacy": "^1.0.0",
|
||||
"character-reference-invalid": "^1.0.0",
|
||||
"is-alphanumerical": "^1.0.0",
|
||||
"is-decimal": "^1.0.0",
|
||||
"is-hexadecimal": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "5.2.0",
|
||||
"requires": {
|
||||
|
@ -28708,6 +29095,11 @@
|
|||
"integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
|
||||
"dev": true
|
||||
},
|
||||
"prismjs": {
|
||||
"version": "1.24.1",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
|
||||
"integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow=="
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10"
|
||||
},
|
||||
|
@ -28746,6 +29138,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"property-information": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
|
||||
"integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
|
||||
"requires": {
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.6",
|
||||
"requires": {
|
||||
|
@ -29309,6 +29709,18 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"react-syntax-highlighter": {
|
||||
"version": "15.4.4",
|
||||
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.4.tgz",
|
||||
"integrity": "sha512-PsOFHNTzkb3OroXdoR897eKN5EZ6grht1iM+f1lJSq7/L0YVnkJaNVwC3wEUYPOAmeyl5xyer1DjL6MrumO6Zw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"highlight.js": "^10.4.1",
|
||||
"lowlight": "^1.17.0",
|
||||
"prismjs": "^1.22.0",
|
||||
"refractor": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "5.2.0",
|
||||
"requires": {
|
||||
|
@ -29428,6 +29840,16 @@
|
|||
"postcss-value-parser": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"refractor": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.4.0.tgz",
|
||||
"integrity": "sha512-dBeD02lC5eytm9Gld2Mx0cMcnR+zhSnsTfPpWqFaMgUMJfC9A6bcN3Br/NaXrnBJcuxnLFR90k1jrkaSyV8umg==",
|
||||
"requires": {
|
||||
"hastscript": "^6.0.0",
|
||||
"parse-entities": "^2.0.0",
|
||||
"prismjs": "~1.24.0"
|
||||
}
|
||||
},
|
||||
"regenerate": {
|
||||
"version": "1.4.2"
|
||||
},
|
||||
|
@ -30492,6 +30914,11 @@
|
|||
"sourcemap-codec": {
|
||||
"version": "1.4.8"
|
||||
},
|
||||
"space-separated-tokens": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
|
||||
"integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA=="
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "3.1.1",
|
||||
"requires": {
|
||||
|
@ -32583,4 +33010,4 @@
|
|||
"version": "0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,9 +25,12 @@
|
|||
"@types/react": "^17.0.20",
|
||||
"@types/react-blockies": "^1.4.1",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/react-highlight": "^0.12.3",
|
||||
"@types/react-router-dom": "^5.1.8",
|
||||
"@types/react-syntax-highlighter": "^13.5.2",
|
||||
"chart.js": "^3.5.1",
|
||||
"ethers": "^5.4.1",
|
||||
"highlightjs-solidity": "^1.2.0",
|
||||
"query-string": "^7.0.1",
|
||||
"react": "^17.0.2",
|
||||
"react-blockies": "^1.4.1",
|
||||
|
@ -37,6 +40,7 @@
|
|||
"react-image": "^4.0.3",
|
||||
"react-router-dom": "^5.2.1",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-syntax-highlighter": "^15.4.4",
|
||||
"serve": "^12.0.0",
|
||||
"typescript": "^4.4.2",
|
||||
"use-keyboard-shortcut": "^1.0.6",
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
import React, { useState, useEffect, useMemo, useContext } from "react";
|
||||
import { useParams, useLocation, useHistory } from "react-router-dom";
|
||||
import {
|
||||
useParams,
|
||||
useLocation,
|
||||
useHistory,
|
||||
Switch,
|
||||
Route,
|
||||
} from "react-router-dom";
|
||||
import { BlockTag } from "@ethersproject/abstract-provider";
|
||||
import { getAddress, isAddress } from "@ethersproject/address";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import queryString from "query-string";
|
||||
import Blockies from "react-blockies";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
|
||||
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons/faCheckCircle";
|
||||
import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons/faQuestionCircle";
|
||||
import StandardFrame from "./StandardFrame";
|
||||
import StandardSubtitle from "./StandardSubtitle";
|
||||
import Copy from "./components/Copy";
|
||||
import ContentFrame from "./ContentFrame";
|
||||
import NavTab from "./components/NavTab";
|
||||
import Contracts from "./address/Contracts";
|
||||
import UndefinedPageControl from "./search/UndefinedPageControl";
|
||||
import ResultHeader from "./search/ResultHeader";
|
||||
import PendingResults from "./search/PendingResults";
|
||||
|
@ -18,6 +31,8 @@ import { useENSCache } from "./useReverseCache";
|
|||
import { useFeeToggler } from "./search/useFeeToggler";
|
||||
import { SelectionContext, useSelection } from "./useSelection";
|
||||
import { useMultipleETHUSDOracle } from "./usePriceOracle";
|
||||
import { useSourcify } from "./useSourcify";
|
||||
import { SourcifySource } from "./url";
|
||||
|
||||
type BlockParams = {
|
||||
addressOrName: string;
|
||||
|
@ -165,6 +180,14 @@ const AddressTransactions: React.FC = () => {
|
|||
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
|
||||
|
||||
const selectionCtx = useSelection();
|
||||
const [sourcifySource, setSourcifySource] = useState<SourcifySource>(
|
||||
SourcifySource.IPFS_IPNS
|
||||
);
|
||||
const rawMetadata = useSourcify(
|
||||
checksummedAddress,
|
||||
provider?.network.chainId,
|
||||
sourcifySource
|
||||
);
|
||||
|
||||
return (
|
||||
<StandardFrame>
|
||||
|
@ -194,59 +217,112 @@ const AddressTransactions: React.FC = () => {
|
|||
)}
|
||||
</div>
|
||||
</StandardSubtitle>
|
||||
<ContentFrame>
|
||||
<div className="flex justify-between items-baseline py-3">
|
||||
<div className="text-sm text-gray-500">
|
||||
{page === undefined ? (
|
||||
<>Waiting for search results...</>
|
||||
) : (
|
||||
<>{page.length} transactions on this page</>
|
||||
)}
|
||||
</div>
|
||||
<UndefinedPageControl
|
||||
address={params.addressOrName}
|
||||
isFirst={controller?.isFirst}
|
||||
isLast={controller?.isLast}
|
||||
prevHash={page ? page[0].hash : ""}
|
||||
nextHash={page ? page[page.length - 1].hash : ""}
|
||||
disabled={controller === undefined}
|
||||
/>
|
||||
</div>
|
||||
<ResultHeader
|
||||
feeDisplay={feeDisplay}
|
||||
feeDisplayToggler={feeDisplayToggler}
|
||||
/>
|
||||
{controller ? (
|
||||
<SelectionContext.Provider value={selectionCtx}>
|
||||
{controller.getPage().map((tx) => (
|
||||
<TransactionItem
|
||||
key={tx.hash}
|
||||
tx={tx}
|
||||
ensCache={reverseCache}
|
||||
selectedAddress={checksummedAddress}
|
||||
feeDisplay={feeDisplay}
|
||||
priceMap={priceMap}
|
||||
/>
|
||||
))}
|
||||
<div className="flex justify-between items-baseline py-3">
|
||||
<div className="text-sm text-gray-500">
|
||||
{page !== undefined && (
|
||||
<>{page.length} transactions on this page</>
|
||||
<Tab.Group>
|
||||
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
|
||||
<NavTab href={`/address/${checksummedAddress}`}>
|
||||
Overview
|
||||
</NavTab>
|
||||
<NavTab href={`/address/${checksummedAddress}/contract`}>
|
||||
<span
|
||||
className={`flex items-baseline space-x-2 ${
|
||||
rawMetadata === undefined ? "italic opacity-50" : ""
|
||||
}`}
|
||||
>
|
||||
<span>Contract</span>
|
||||
{rawMetadata === undefined ? (
|
||||
<span className="self-center">
|
||||
<FontAwesomeIcon
|
||||
className="animate-spin"
|
||||
icon={faCircleNotch}
|
||||
/>
|
||||
</span>
|
||||
) : rawMetadata === null ? (
|
||||
<span className="self-center text-red-500">
|
||||
<FontAwesomeIcon icon={faQuestionCircle} />
|
||||
</span>
|
||||
) : (
|
||||
<span className="self-center text-green-500">
|
||||
<FontAwesomeIcon icon={faCheckCircle} />
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</NavTab>
|
||||
</Tab.List>
|
||||
<Tab.Panels>
|
||||
<Switch>
|
||||
<Route path="/address/:addressOrName" exact>
|
||||
<ContentFrame tabs>
|
||||
<div className="flex justify-between items-baseline py-3">
|
||||
<div className="text-sm text-gray-500">
|
||||
{page === undefined ? (
|
||||
<>Waiting for search results...</>
|
||||
) : (
|
||||
<>{page.length} transactions on this page</>
|
||||
)}
|
||||
</div>
|
||||
<UndefinedPageControl
|
||||
address={params.addressOrName}
|
||||
isFirst={controller?.isFirst}
|
||||
isLast={controller?.isLast}
|
||||
prevHash={page ? page[0].hash : ""}
|
||||
nextHash={page ? page[page.length - 1].hash : ""}
|
||||
disabled={controller === undefined}
|
||||
/>
|
||||
</div>
|
||||
<ResultHeader
|
||||
feeDisplay={feeDisplay}
|
||||
feeDisplayToggler={feeDisplayToggler}
|
||||
/>
|
||||
{controller ? (
|
||||
<SelectionContext.Provider value={selectionCtx}>
|
||||
{controller.getPage().map((tx) => (
|
||||
<TransactionItem
|
||||
key={tx.hash}
|
||||
tx={tx}
|
||||
ensCache={reverseCache}
|
||||
selectedAddress={checksummedAddress}
|
||||
feeDisplay={feeDisplay}
|
||||
priceMap={priceMap}
|
||||
/>
|
||||
))}
|
||||
<div className="flex justify-between items-baseline py-3">
|
||||
<div className="text-sm text-gray-500">
|
||||
{page === undefined ? (
|
||||
<>Waiting for search results...</>
|
||||
) : (
|
||||
<>{page.length} transactions on this page</>
|
||||
)}
|
||||
</div>
|
||||
<UndefinedPageControl
|
||||
address={params.addressOrName}
|
||||
isFirst={controller?.isFirst}
|
||||
isLast={controller?.isLast}
|
||||
prevHash={page ? page[0].hash : ""}
|
||||
nextHash={page ? page[page.length - 1].hash : ""}
|
||||
disabled={controller === undefined}
|
||||
/>
|
||||
</div>
|
||||
<ResultHeader
|
||||
feeDisplay={feeDisplay}
|
||||
feeDisplayToggler={feeDisplayToggler}
|
||||
/>
|
||||
</SelectionContext.Provider>
|
||||
) : (
|
||||
<PendingResults />
|
||||
)}
|
||||
</div>
|
||||
<UndefinedPageControl
|
||||
address={params.addressOrName}
|
||||
isFirst={controller.isFirst}
|
||||
isLast={controller.isLast}
|
||||
prevHash={page ? page[0].hash : ""}
|
||||
nextHash={page ? page[page.length - 1].hash : ""}
|
||||
</ContentFrame>
|
||||
</Route>
|
||||
<Route path="/address/:addressOrName/contract" exact>
|
||||
<Contracts
|
||||
checksummedAddress={checksummedAddress}
|
||||
rawMetadata={rawMetadata}
|
||||
sourcifySource={sourcifySource}
|
||||
setSourcifySource={setSourcifySource}
|
||||
/>
|
||||
</div>
|
||||
</SelectionContext.Provider>
|
||||
) : (
|
||||
<PendingResults />
|
||||
)}
|
||||
</ContentFrame>
|
||||
</Route>
|
||||
</Switch>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</>
|
||||
)
|
||||
)}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React, { useMemo, useContext } from "react";
|
||||
import { Route, Switch, useParams } from "react-router-dom";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import StandardFrame from "./StandardFrame";
|
||||
import StandardSubtitle from "./StandardSubtitle";
|
||||
import ContentFrame from "./ContentFrame";
|
||||
import Tab from "./components/Tab";
|
||||
import NavTab from "./components/NavTab";
|
||||
import Details from "./transaction/Details";
|
||||
import Logs from "./transaction/Logs";
|
||||
import { RuntimeContext } from "./useRuntime";
|
||||
|
@ -55,14 +56,17 @@ const Transaction: React.FC = () => {
|
|||
)}
|
||||
{txData && (
|
||||
<SelectionContext.Provider value={selectionCtx}>
|
||||
<div className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
|
||||
<Tab href={`/tx/${txhash}`}>Overview</Tab>
|
||||
{txData.confirmedData?.blockNumber !== undefined && (
|
||||
<Tab href={`/tx/${txhash}/logs`}>
|
||||
Logs{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`}
|
||||
</Tab>
|
||||
)}
|
||||
</div>
|
||||
<Tab.Group>
|
||||
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
|
||||
<NavTab href={`/tx/${txhash}`}>Overview</NavTab>
|
||||
{txData.confirmedData?.blockNumber !== undefined && (
|
||||
<NavTab href={`/tx/${txhash}/logs`}>
|
||||
Logs
|
||||
{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`}
|
||||
</NavTab>
|
||||
)}
|
||||
</Tab.List>
|
||||
</Tab.Group>
|
||||
<Switch>
|
||||
<Route path="/tx/:txhash/" exact>
|
||||
<Details
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import React from "react";
|
||||
import { Light as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import hljs from "highlight.js";
|
||||
import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco";
|
||||
|
||||
import hljsDefineSolidity from "highlightjs-solidity";
|
||||
hljsDefineSolidity(hljs);
|
||||
|
||||
type ABIProps = {
|
||||
abi: any[];
|
||||
};
|
||||
|
||||
const ABI: React.FC<ABIProps> = ({ abi }) => (
|
||||
<SyntaxHighlighter
|
||||
className="w-full h-60 border font-code text-base"
|
||||
language="json"
|
||||
style={docco}
|
||||
showLineNumbers
|
||||
>
|
||||
{JSON.stringify(abi, null, " ") ?? ""}
|
||||
</SyntaxHighlighter>
|
||||
);
|
||||
|
||||
export default React.memo(ABI);
|
|
@ -0,0 +1,46 @@
|
|||
import React from "react";
|
||||
import { Light as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import hljs from "highlight.js";
|
||||
import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco";
|
||||
import { useContract } from "../useSourcify";
|
||||
import { SourcifySource } from "../url";
|
||||
|
||||
import hljsDefineSolidity from "highlightjs-solidity";
|
||||
hljsDefineSolidity(hljs);
|
||||
|
||||
type ContractProps = {
|
||||
checksummedAddress: string;
|
||||
networkId: number;
|
||||
filename: string;
|
||||
source: any;
|
||||
sourcifySource: SourcifySource;
|
||||
};
|
||||
|
||||
const Contract: React.FC<ContractProps> = ({
|
||||
checksummedAddress,
|
||||
networkId,
|
||||
filename,
|
||||
source,
|
||||
sourcifySource,
|
||||
}) => {
|
||||
const content = useContract(
|
||||
checksummedAddress,
|
||||
networkId,
|
||||
filename,
|
||||
source,
|
||||
sourcifySource
|
||||
);
|
||||
|
||||
return (
|
||||
<SyntaxHighlighter
|
||||
className="w-full h-full border font-code text-base"
|
||||
language="solidity"
|
||||
style={docco}
|
||||
showLineNumbers
|
||||
>
|
||||
{content ?? ""}
|
||||
</SyntaxHighlighter>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Contract);
|
|
@ -0,0 +1,157 @@
|
|||
import React, { useState, useEffect, useContext, Fragment } from "react";
|
||||
import { commify } from "@ethersproject/units";
|
||||
import { Menu, RadioGroup } from "@headlessui/react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
||||
import ContentFrame from "../ContentFrame";
|
||||
import InfoRow from "../components/InfoRow";
|
||||
import ABI from "./ABI";
|
||||
import Contract from "./Contract";
|
||||
import { RuntimeContext } from "../useRuntime";
|
||||
import { Metadata } from "../useSourcify";
|
||||
import ExternalLink from "../components/ExternalLink";
|
||||
import { openInRemixURL, SourcifySource } from "../url";
|
||||
import RadioButton from "./RadioButton";
|
||||
|
||||
type ContractsProps = {
|
||||
checksummedAddress: string;
|
||||
rawMetadata: Metadata | null | undefined;
|
||||
sourcifySource: SourcifySource;
|
||||
setSourcifySource: (sourcifySource: SourcifySource) => void;
|
||||
};
|
||||
|
||||
const Contracts: React.FC<ContractsProps> = ({
|
||||
checksummedAddress,
|
||||
rawMetadata,
|
||||
sourcifySource,
|
||||
setSourcifySource,
|
||||
}) => {
|
||||
const { provider } = useContext(RuntimeContext);
|
||||
|
||||
const [selected, setSelected] = useState<string>();
|
||||
useEffect(() => {
|
||||
if (rawMetadata) {
|
||||
setSelected(Object.keys(rawMetadata.sources)[0]);
|
||||
}
|
||||
}, [rawMetadata]);
|
||||
const optimizer = rawMetadata?.settings?.optimizer;
|
||||
|
||||
return (
|
||||
<ContentFrame tabs>
|
||||
<InfoRow title="Sourcify integration">
|
||||
<RadioGroup value={sourcifySource} onChange={setSourcifySource}>
|
||||
<div className="flex space-x-2">
|
||||
<RadioButton value={SourcifySource.IPFS_IPNS}>
|
||||
Resolve IPNS
|
||||
</RadioButton>
|
||||
<RadioButton value={SourcifySource.CENTRAL_SERVER}>
|
||||
Sourcify Servers
|
||||
</RadioButton>
|
||||
<RadioButton value={SourcifySource.CUSTOM_SNAPSHOT_SERVER}>
|
||||
Local Snapshot
|
||||
</RadioButton>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</InfoRow>
|
||||
{rawMetadata && (
|
||||
<>
|
||||
<InfoRow title="Language">
|
||||
<span>{rawMetadata.language}</span>
|
||||
</InfoRow>
|
||||
<InfoRow title="Compiler">
|
||||
<span>{rawMetadata.compiler.version}</span>
|
||||
</InfoRow>
|
||||
<InfoRow title="Optimizer Enabled">
|
||||
{optimizer?.enabled ? (
|
||||
<span>
|
||||
<span className="font-bold text-green-600">Yes</span> with{" "}
|
||||
<span className="font-bold text-green-600">
|
||||
{commify(optimizer?.runs)}
|
||||
</span>{" "}
|
||||
runs
|
||||
</span>
|
||||
) : (
|
||||
<span className="font-bold text-red-600">No</span>
|
||||
)}
|
||||
</InfoRow>
|
||||
</>
|
||||
)}
|
||||
<div className="py-5">
|
||||
{rawMetadata === undefined && (
|
||||
<span>Getting data from Sourcify repository...</span>
|
||||
)}
|
||||
{rawMetadata === null && (
|
||||
<span>
|
||||
Address is not a contract or couldn't find contract metadata in
|
||||
Sourcify repository.
|
||||
</span>
|
||||
)}
|
||||
{rawMetadata !== undefined && rawMetadata !== null && (
|
||||
<>
|
||||
{rawMetadata.output.abi && (
|
||||
<div className="mb-3">
|
||||
<div className="flex space-x-2 text-sm border-l border-r border-t rounded-t px-2 py-1">
|
||||
ABI
|
||||
</div>
|
||||
<ABI abi={rawMetadata.output.abi} />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Menu>
|
||||
<div className="flex space-x-2 justify-between items-baseline">
|
||||
<Menu.Button className="flex space-x-2 text-sm border-l border-r border-t rounded-t px-2 py-1">
|
||||
<span>{selected}</span>
|
||||
<span className="self-center">
|
||||
<FontAwesomeIcon icon={faChevronDown} size="xs" />
|
||||
</span>
|
||||
</Menu.Button>
|
||||
{provider && (
|
||||
<div className="text-sm">
|
||||
<ExternalLink
|
||||
href={openInRemixURL(
|
||||
checksummedAddress,
|
||||
provider.network.chainId
|
||||
)}
|
||||
>
|
||||
Open in Remix
|
||||
</ExternalLink>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative">
|
||||
<Menu.Items className="absolute border p-1 rounded-b bg-white flex flex-col">
|
||||
{Object.entries(rawMetadata.sources).map(([k]) => (
|
||||
<Menu.Item key={k}>
|
||||
<button
|
||||
className={`flex text-sm px-2 py-1 ${
|
||||
selected === k
|
||||
? "font-bold bg-gray-200 text-gray-500"
|
||||
: "hover:border-orange-200 hover:text-gray-500 text-gray-400 transition-transform transition-colors duration-75"
|
||||
}`}
|
||||
onClick={() => setSelected(k)}
|
||||
>
|
||||
{k}
|
||||
</button>
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu.Items>
|
||||
</div>
|
||||
</Menu>
|
||||
{selected && (
|
||||
<Contract
|
||||
checksummedAddress={checksummedAddress}
|
||||
networkId={provider!.network.chainId}
|
||||
filename={selected}
|
||||
source={rawMetadata.sources[selected]}
|
||||
sourcifySource={sourcifySource}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</ContentFrame>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Contracts);
|
|
@ -0,0 +1,24 @@
|
|||
import React from "react";
|
||||
import { RadioGroup } from "@headlessui/react";
|
||||
import { SourcifySource } from "../url";
|
||||
|
||||
type RadioButtonProps = {
|
||||
value: SourcifySource;
|
||||
};
|
||||
|
||||
const RadioButton: React.FC<RadioButtonProps> = ({ value, children }) => (
|
||||
<RadioGroup.Option
|
||||
className={({ checked }) =>
|
||||
`border rounded px-2 py-1 cursor-pointer ${
|
||||
checked
|
||||
? "bg-blue-400 hover:bg-blue-500 text-white"
|
||||
: "hover:bg-gray-200"
|
||||
}`
|
||||
}
|
||||
value={value}
|
||||
>
|
||||
{children}
|
||||
</RadioGroup.Option>
|
||||
);
|
||||
|
||||
export default RadioButton;
|
|
@ -0,0 +1,23 @@
|
|||
import React, { Fragment } from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { Tab } from "@headlessui/react";
|
||||
|
||||
type NavTabProps = {
|
||||
href: string;
|
||||
};
|
||||
|
||||
const NavTab: React.FC<NavTabProps> = ({ href, children }) => (
|
||||
<Tab as={Fragment}>
|
||||
<NavLink
|
||||
className="text-gray-500 border-transparent hover:text-link-blue text-sm font-bold px-3 py-3 border-b-2"
|
||||
activeClassName="text-link-blue border-link-blue"
|
||||
to={href}
|
||||
exact
|
||||
replace
|
||||
>
|
||||
{children}
|
||||
</NavLink>
|
||||
</Tab>
|
||||
);
|
||||
|
||||
export default NavTab;
|
|
@ -1,20 +0,0 @@
|
|||
import React from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
|
||||
type TabProps = {
|
||||
href: string;
|
||||
};
|
||||
|
||||
const Tab: React.FC<TabProps> = ({ href, children }) => (
|
||||
<NavLink
|
||||
className="text-gray-500 border-transparent hover:text-link-blue text-sm font-bold px-3 py-3 border-b-2"
|
||||
activeClassName="text-link-blue border-link-blue"
|
||||
to={href}
|
||||
exact
|
||||
replace
|
||||
>
|
||||
{children}
|
||||
</NavLink>
|
||||
);
|
||||
|
||||
export default Tab;
|
|
@ -1,2 +1,3 @@
|
|||
/// <reference types="react-scripts" />
|
||||
declare module "use-keyboard-shortcut";
|
||||
declare module "highlightjs-solidity";
|
||||
|
|
50
src/url.ts
50
src/url.ts
|
@ -13,3 +13,53 @@ export const tokenLogoURL = (
|
|||
export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`;
|
||||
|
||||
export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`;
|
||||
|
||||
export enum SourcifySource {
|
||||
// Resolve trusted IPNS for root IPFS
|
||||
IPFS_IPNS,
|
||||
|
||||
// Centralized Sourcify servers
|
||||
CENTRAL_SERVER,
|
||||
|
||||
// Snapshot server
|
||||
CUSTOM_SNAPSHOT_SERVER,
|
||||
}
|
||||
|
||||
const sourcifyIPNS =
|
||||
"k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p";
|
||||
const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyIPNS}`;
|
||||
// const ipfsGatewayPrefix = "http://localhost:8080/ipfs/QmWQoGfrLcizHueg3YkgDCh1S7SkfSP9A7H8YeZmUDfbnn"
|
||||
const sourcifyHttpRepoPrefix = `https://repo.sourcify.dev`;
|
||||
const snapshotPrefix = "http://localhost:3006";
|
||||
|
||||
const resolveSourcifySource = (source: SourcifySource) => {
|
||||
if (source === SourcifySource.IPFS_IPNS) {
|
||||
return ipfsGatewayPrefix;
|
||||
}
|
||||
if (source === SourcifySource.CENTRAL_SERVER) {
|
||||
return sourcifyHttpRepoPrefix;
|
||||
}
|
||||
return snapshotPrefix;
|
||||
};
|
||||
|
||||
export const sourcifyMetadata = (
|
||||
checksummedAddress: string,
|
||||
networkId: number,
|
||||
source: SourcifySource
|
||||
) =>
|
||||
`${resolveSourcifySource(
|
||||
source
|
||||
)}/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`;
|
||||
|
||||
export const sourcifySourceFile = (
|
||||
checksummedAddress: string,
|
||||
networkId: number,
|
||||
filepath: string,
|
||||
source: SourcifySource
|
||||
) =>
|
||||
`${resolveSourcifySource(
|
||||
source
|
||||
)}/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`;
|
||||
|
||||
export const openInRemixURL = (checksummedAddress: string, networkId: number) =>
|
||||
`https://remix.ethereum.org/#call=source-verification//fetchAndSave//${checksummedAddress}//${networkId}`;
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "./url";
|
||||
|
||||
export type Metadata = {
|
||||
version: string;
|
||||
language: string;
|
||||
compiler: {
|
||||
version: string;
|
||||
keccak256?: string | undefined;
|
||||
};
|
||||
sources: {
|
||||
[filename: string]: {
|
||||
keccak256: string;
|
||||
content?: string | undefined;
|
||||
urls?: string[];
|
||||
license?: string;
|
||||
};
|
||||
};
|
||||
settings: {
|
||||
remappings: string[];
|
||||
optimizer?: {
|
||||
enabled: boolean;
|
||||
runs: number;
|
||||
};
|
||||
compilationTarget: {
|
||||
[filename: string]: string;
|
||||
};
|
||||
libraries: {
|
||||
[filename: string]: string;
|
||||
};
|
||||
};
|
||||
output: {
|
||||
abi: any[];
|
||||
userdocs: any[];
|
||||
devdoc: any[];
|
||||
};
|
||||
};
|
||||
|
||||
export const useSourcify = (
|
||||
checksummedAddress: string | undefined,
|
||||
chainId: number | undefined,
|
||||
source: SourcifySource
|
||||
) => {
|
||||
const [rawMetadata, setRawMetadata] = useState<Metadata | null | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!checksummedAddress || chainId === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
setRawMetadata(undefined);
|
||||
const fetchMetadata = async () => {
|
||||
try {
|
||||
const contractMetadataURL = sourcifyMetadata(
|
||||
checksummedAddress,
|
||||
chainId,
|
||||
source
|
||||
);
|
||||
const result = await fetch(contractMetadataURL);
|
||||
if (result.ok) {
|
||||
const _metadata = await result.json();
|
||||
setRawMetadata(_metadata);
|
||||
} else {
|
||||
setRawMetadata(null);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setRawMetadata(null);
|
||||
}
|
||||
};
|
||||
fetchMetadata();
|
||||
}, [checksummedAddress, chainId, source]);
|
||||
|
||||
return rawMetadata;
|
||||
};
|
||||
|
||||
export const useContract = (
|
||||
checksummedAddress: string,
|
||||
networkId: number,
|
||||
filename: string,
|
||||
source: any,
|
||||
sourcifySource: SourcifySource
|
||||
) => {
|
||||
const [content, setContent] = useState<string>(source.content);
|
||||
|
||||
useEffect(() => {
|
||||
if (source.content) {
|
||||
return;
|
||||
}
|
||||
|
||||
const readContent = async () => {
|
||||
const normalizedFilename = filename.replaceAll(/[@:]/g, "_");
|
||||
const url = sourcifySourceFile(
|
||||
checksummedAddress,
|
||||
networkId,
|
||||
normalizedFilename,
|
||||
sourcifySource
|
||||
);
|
||||
const res = await fetch(url);
|
||||
if (res.ok) {
|
||||
const _content = await res.text();
|
||||
setContent(_content);
|
||||
}
|
||||
};
|
||||
readContent();
|
||||
}, [checksummedAddress, networkId, filename, source.content, sourcifySource]);
|
||||
|
||||
return content;
|
||||
};
|
|
@ -27,6 +27,7 @@ module.exports = {
|
|||
data: ["Roboto Mono"],
|
||||
balance: ["Fira Code"],
|
||||
blocknum: ["Roboto"],
|
||||
code: ["Fira Code"],
|
||||
},
|
||||
borderColor: {
|
||||
skin: {
|
||||
|
|
Loading…
Reference in New Issue