Merge branch 'release/v2021.09.04-otterscan'
This commit is contained in:
commit
5b2e471368
|
@ -11,7 +11,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blackbox-vision/react-qr-reader": "^5.0.0",
|
"@blackbox-vision/react-qr-reader": "^5.0.0",
|
||||||
"@chainlink/contracts": "^0.2.1",
|
"@chainlink/contracts": "^0.2.1",
|
||||||
"@craco/craco": "^6.2.0",
|
"@craco/craco": "^6.3.0",
|
||||||
"@fontsource/fira-code": "^4.5.1",
|
"@fontsource/fira-code": "^4.5.1",
|
||||||
"@fontsource/roboto": "^4.5.0",
|
"@fontsource/roboto": "^4.5.0",
|
||||||
"@fontsource/roboto-mono": "^4.5.0",
|
"@fontsource/roboto-mono": "^4.5.0",
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
"@types/react-router-dom": "^5.1.8",
|
"@types/react-router-dom": "^5.1.8",
|
||||||
"@types/react-syntax-highlighter": "^13.5.2",
|
"@types/react-syntax-highlighter": "^13.5.2",
|
||||||
"chart.js": "^3.5.1",
|
"chart.js": "^3.5.1",
|
||||||
"ethers": "^5.4.1",
|
"ethers": "^5.4.6",
|
||||||
"highlightjs-solidity": "^1.2.0",
|
"highlightjs-solidity": "^1.2.0",
|
||||||
"query-string": "^7.0.1",
|
"query-string": "^7.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
@ -43,11 +43,11 @@
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-error-boundary": "^3.1.3",
|
"react-error-boundary": "^3.1.3",
|
||||||
"react-image": "^4.0.3",
|
"react-image": "^4.0.3",
|
||||||
"react-router-dom": "^5.2.1",
|
"react-router-dom": "^5.3.0",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
"react-syntax-highlighter": "^15.4.4",
|
"react-syntax-highlighter": "^15.4.4",
|
||||||
"serve": "^12.0.0",
|
"serve": "^12.0.1",
|
||||||
"typescript": "^4.4.2",
|
"typescript": "^4.4.3",
|
||||||
"use-keyboard-shortcut": "^1.0.6",
|
"use-keyboard-shortcut": "^1.0.6",
|
||||||
"web-vitals": "^1.0.1"
|
"web-vitals": "^1.0.1"
|
||||||
},
|
},
|
||||||
|
@ -1247,9 +1247,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@craco/craco": {
|
"node_modules/@craco/craco": {
|
||||||
"version": "6.2.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.3.0.tgz",
|
||||||
"integrity": "sha512-kLc4GSdgR9D5JiZmSxtzbvBKcUFSJqMXImRjjYf5pacwiyAs3XfQwai7T+pExfLQNUnytgkL8jRFUJeYrkVr7g==",
|
"integrity": "sha512-SCnfEQxT/6NAbU/3sIWw7gQXtzjjiTp/EZFdJTd8inPURILIy0YajrC2p8qBG2KhFo5cwgOrEDyaGyAFvvuyuA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.0",
|
"cross-spawn": "^7.0.0",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
|
@ -1324,9 +1324,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ethersproject/abi": {
|
"node_modules/@ethersproject/abi": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.4.1.tgz",
|
||||||
"integrity": "sha512-9gU2H+/yK1j2eVMdzm6xvHSnMxk8waIHQGYCZg5uvAyH0rsAzxkModzBSpbAkAuhKFEovC2S9hM4nPuLym8IZw==",
|
"integrity": "sha512-9mhbjUk76BiSluiiW4BaYyI58KSbDMMQpCLdsAR+RsT2GyATiNYxVv+pGWRrekmsIdY3I+hOqsYQSTkc8L/mcg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
|
@ -1350,9 +1350,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ethersproject/abstract-provider": {
|
"node_modules/@ethersproject/abstract-provider": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.4.1.tgz",
|
||||||
"integrity": "sha512-vPBR7HKUBY0lpdllIn7tLIzNN7DrVnhCLKSzY0l8WAwxz686m/aL7ASDzrVxV93GJtIub6N2t4dfZ29CkPOxgA==",
|
"integrity": "sha512-3EedfKI3LVpjSKgAxoUaI+gB27frKsxzm+r21w9G60Ugk+3wVLQwhi1LsEJAKNV7WoZc8CIpNrATlL1QFABjtQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
|
@ -1511,9 +1511,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ethersproject/contracts": {
|
"node_modules/@ethersproject/contracts": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.4.1.tgz",
|
||||||
"integrity": "sha512-hkO3L3IhS1Z3ZtHtaAG/T87nQ7KiPV+/qnvutag35I0IkiQ8G3ZpCQ9NNOpSCzn4pWSW4CfzmtE02FcqnLI+hw==",
|
"integrity": "sha512-m+z2ZgPy4pyR15Je//dUaymRUZq5MtDajF6GwFbGAVmKz/RF+DNIPwF0k5qEcL3wPGVqUjFg2/krlCRVTU4T5w==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
|
@ -1641,9 +1641,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ethersproject/logger": {
|
"node_modules/@ethersproject/logger": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.4.1.tgz",
|
||||||
"integrity": "sha512-xYdWGGQ9P2cxBayt64d8LC8aPFJk6yWCawQi/4eJ4+oJdMMjEBMrIcIMZ9AxhwpPVmnBPrsB10PcXGmGAqgUEQ==",
|
"integrity": "sha512-DZ+bRinnYLPw1yAC64oRl0QyVZj43QeHIhVKfD/+YwSz4wsv1pfwb5SOFjz+r710YEWzU6LrhuSjpSO+6PeE4A==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
|
@ -1656,9 +1656,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@ethersproject/networks": {
|
"node_modules/@ethersproject/networks": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.4.2.tgz",
|
||||||
"integrity": "sha512-8SvowCKz9Uf4xC5DTKI8+il8lWqOr78kmiqAVLYT9lzB8aSmJHQMD1GSuJI0CW4hMAnzocpGpZLgiMdzsNSPig==",
|
"integrity": "sha512-eekOhvJyBnuibfJnhtK46b8HimBc5+4gqpvd1/H9LEl7Q7/qhsIhM81dI9Fcnjpk3jB1aTy6bj0hz3cifhNeYw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
|
@ -1693,9 +1693,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ethersproject/properties": {
|
"node_modules/@ethersproject/properties": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.4.1.tgz",
|
||||||
"integrity": "sha512-7jczalGVRAJ+XSRvNA6D5sAwT4gavLq3OXPuV/74o3Rd2wuzSL035IMpIMgei4CYyBdialJMrTqkOnzccLHn4A==",
|
"integrity": "sha512-cyCGlF8wWlIZyizsj2PpbJ9I7rIlUAfnHYwy/T90pdkSn/NFTa5YWZx2wTJBe9V7dD65dcrrEMisCRUJiq6n3w==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
|
@ -1711,9 +1711,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ethersproject/providers": {
|
"node_modules/@ethersproject/providers": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.4.5.tgz",
|
||||||
"integrity": "sha512-p06eiFKz8nu/5Ju0kIX024gzEQIgE5pvvGrBCngpyVjpuLtUIWT3097Agw4mTn9/dEA0FMcfByzFqacBMSgCVg==",
|
"integrity": "sha512-1GkrvkiAw3Fj28cwi1Sqm8ED1RtERtpdXmRfwIBGmqBSN5MoeRUHuwHPppMtbPayPgpFcvD7/Gdc9doO5fGYgw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
|
@ -8119,9 +8119,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ethers": {
|
"node_modules/ethers": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/ethers/-/ethers-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/ethers/-/ethers-5.4.6.tgz",
|
||||||
"integrity": "sha512-SrcddMdCgP1hukDvCPd87Aipbf4NWjQvdfAbZ65XSZGbfyuYPtIrUJPDH5B1SBRsdlfiEgX3eoz28DdBDzMNFg==",
|
"integrity": "sha512-F7LXARyB/Px3AQC6/QKedWZ8eqCkgOLORqL4B/F0Mag/K+qJSFGqsR36EaOZ6fKg3ZonI+pdbhb4A8Knt/43jQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
|
@ -8133,25 +8133,25 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersproject/abi": "5.4.0",
|
"@ethersproject/abi": "5.4.1",
|
||||||
"@ethersproject/abstract-provider": "5.4.0",
|
"@ethersproject/abstract-provider": "5.4.1",
|
||||||
"@ethersproject/abstract-signer": "5.4.0",
|
"@ethersproject/abstract-signer": "5.4.1",
|
||||||
"@ethersproject/address": "5.4.0",
|
"@ethersproject/address": "5.4.0",
|
||||||
"@ethersproject/base64": "5.4.0",
|
"@ethersproject/base64": "5.4.0",
|
||||||
"@ethersproject/basex": "5.4.0",
|
"@ethersproject/basex": "5.4.0",
|
||||||
"@ethersproject/bignumber": "5.4.0",
|
"@ethersproject/bignumber": "5.4.1",
|
||||||
"@ethersproject/bytes": "5.4.0",
|
"@ethersproject/bytes": "5.4.0",
|
||||||
"@ethersproject/constants": "5.4.0",
|
"@ethersproject/constants": "5.4.0",
|
||||||
"@ethersproject/contracts": "5.4.0",
|
"@ethersproject/contracts": "5.4.1",
|
||||||
"@ethersproject/hash": "5.4.0",
|
"@ethersproject/hash": "5.4.0",
|
||||||
"@ethersproject/hdnode": "5.4.0",
|
"@ethersproject/hdnode": "5.4.0",
|
||||||
"@ethersproject/json-wallets": "5.4.0",
|
"@ethersproject/json-wallets": "5.4.0",
|
||||||
"@ethersproject/keccak256": "5.4.0",
|
"@ethersproject/keccak256": "5.4.0",
|
||||||
"@ethersproject/logger": "5.4.0",
|
"@ethersproject/logger": "5.4.1",
|
||||||
"@ethersproject/networks": "5.4.1",
|
"@ethersproject/networks": "5.4.2",
|
||||||
"@ethersproject/pbkdf2": "5.4.0",
|
"@ethersproject/pbkdf2": "5.4.0",
|
||||||
"@ethersproject/properties": "5.4.0",
|
"@ethersproject/properties": "5.4.1",
|
||||||
"@ethersproject/providers": "5.4.1",
|
"@ethersproject/providers": "5.4.5",
|
||||||
"@ethersproject/random": "5.4.0",
|
"@ethersproject/random": "5.4.0",
|
||||||
"@ethersproject/rlp": "5.4.0",
|
"@ethersproject/rlp": "5.4.0",
|
||||||
"@ethersproject/sha2": "5.4.0",
|
"@ethersproject/sha2": "5.4.0",
|
||||||
|
@ -8165,48 +8165,6 @@
|
||||||
"@ethersproject/wordlists": "5.4.0"
|
"@ethersproject/wordlists": "5.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ethers/node_modules/@ethersproject/abstract-signer": {
|
|
||||||
"version": "5.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.4.0.tgz",
|
|
||||||
"integrity": "sha512-AieQAzt05HJZS2bMofpuxMEp81AHufA5D6M4ScKwtolj041nrfIbIi8ciNW7+F59VYxXq+V4c3d568Q6l2m8ew==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://www.buymeacoffee.com/ricmoo"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"@ethersproject/abstract-provider": "^5.4.0",
|
|
||||||
"@ethersproject/bignumber": "^5.4.0",
|
|
||||||
"@ethersproject/bytes": "^5.4.0",
|
|
||||||
"@ethersproject/logger": "^5.4.0",
|
|
||||||
"@ethersproject/properties": "^5.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ethers/node_modules/@ethersproject/bignumber": {
|
|
||||||
"version": "5.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.4.0.tgz",
|
|
||||||
"integrity": "sha512-OXUu9f9hO3vGRIPxU40cignXZVaYyfx6j9NNMjebKdnaCL3anCLSSy8/b8d03vY6dh7duCC0kW72GEC4tZer2w==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://www.buymeacoffee.com/ricmoo"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"@ethersproject/bytes": "^5.4.0",
|
|
||||||
"@ethersproject/logger": "^5.4.0",
|
|
||||||
"bn.js": "^4.11.9"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eventemitter3": {
|
"node_modules/eventemitter3": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.7",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
@ -14675,9 +14633,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router-dom": {
|
"node_modules/react-router-dom": {
|
||||||
"version": "5.2.1",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz",
|
||||||
"integrity": "sha512-xhFFkBGVcIVPbWM2KEYzED+nuHQPmulVa7sqIs3ESxzYd1pYg8N8rxPnQ4T2o1zu/2QeDUWcaqST131SO1LR3w==",
|
"integrity": "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.13",
|
"@babel/runtime": "^7.12.13",
|
||||||
"history": "^4.9.0",
|
"history": "^4.9.0",
|
||||||
|
@ -16073,9 +16031,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/serve": {
|
"node_modules/serve": {
|
||||||
"version": "12.0.0",
|
"version": "12.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/serve/-/serve-12.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/serve/-/serve-12.0.1.tgz",
|
||||||
"integrity": "sha512-BkTsETQYynAZ7rXX414kg4X6EvuZQS3UVs1NY0VQYdRHSTYWPYcH38nnDh48D0x6ONuislgjag8uKlU2gTBImA==",
|
"integrity": "sha512-CQ4ikLpxg/wmNM7yivulpS6fhjRiFG6OjmP8ty3/c1SBnSk23fpKmLAV4HboTA2KrZhkUPlDfjDhnRmAjQ5Phw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zeit/schemas": "2.6.0",
|
"@zeit/schemas": "2.6.0",
|
||||||
"ajv": "6.12.6",
|
"ajv": "6.12.6",
|
||||||
|
@ -18242,9 +18200,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
|
||||||
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
|
"integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
@ -20567,9 +20525,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@craco/craco": {
|
"@craco/craco": {
|
||||||
"version": "6.2.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@craco/craco/-/craco-6.3.0.tgz",
|
||||||
"integrity": "sha512-kLc4GSdgR9D5JiZmSxtzbvBKcUFSJqMXImRjjYf5pacwiyAs3XfQwai7T+pExfLQNUnytgkL8jRFUJeYrkVr7g==",
|
"integrity": "sha512-SCnfEQxT/6NAbU/3sIWw7gQXtzjjiTp/EZFdJTd8inPURILIy0YajrC2p8qBG2KhFo5cwgOrEDyaGyAFvvuyuA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"cross-spawn": "^7.0.0",
|
"cross-spawn": "^7.0.0",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
|
@ -20617,9 +20575,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ethersproject/abi": {
|
"@ethersproject/abi": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.4.1.tgz",
|
||||||
"integrity": "sha512-9gU2H+/yK1j2eVMdzm6xvHSnMxk8waIHQGYCZg5uvAyH0rsAzxkModzBSpbAkAuhKFEovC2S9hM4nPuLym8IZw==",
|
"integrity": "sha512-9mhbjUk76BiSluiiW4BaYyI58KSbDMMQpCLdsAR+RsT2GyATiNYxVv+pGWRrekmsIdY3I+hOqsYQSTkc8L/mcg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ethersproject/address": "^5.4.0",
|
"@ethersproject/address": "^5.4.0",
|
||||||
"@ethersproject/bignumber": "^5.4.0",
|
"@ethersproject/bignumber": "^5.4.0",
|
||||||
|
@ -20633,9 +20591,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ethersproject/abstract-provider": {
|
"@ethersproject/abstract-provider": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.4.1.tgz",
|
||||||
"integrity": "sha512-vPBR7HKUBY0lpdllIn7tLIzNN7DrVnhCLKSzY0l8WAwxz686m/aL7ASDzrVxV93GJtIub6N2t4dfZ29CkPOxgA==",
|
"integrity": "sha512-3EedfKI3LVpjSKgAxoUaI+gB27frKsxzm+r21w9G60Ugk+3wVLQwhi1LsEJAKNV7WoZc8CIpNrATlL1QFABjtQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ethersproject/bignumber": "^5.4.0",
|
"@ethersproject/bignumber": "^5.4.0",
|
||||||
"@ethersproject/bytes": "^5.4.0",
|
"@ethersproject/bytes": "^5.4.0",
|
||||||
|
@ -20714,9 +20672,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ethersproject/contracts": {
|
"@ethersproject/contracts": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.4.1.tgz",
|
||||||
"integrity": "sha512-hkO3L3IhS1Z3ZtHtaAG/T87nQ7KiPV+/qnvutag35I0IkiQ8G3ZpCQ9NNOpSCzn4pWSW4CfzmtE02FcqnLI+hw==",
|
"integrity": "sha512-m+z2ZgPy4pyR15Je//dUaymRUZq5MtDajF6GwFbGAVmKz/RF+DNIPwF0k5qEcL3wPGVqUjFg2/krlCRVTU4T5w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ethersproject/abi": "^5.4.0",
|
"@ethersproject/abi": "^5.4.0",
|
||||||
"@ethersproject/abstract-provider": "^5.4.0",
|
"@ethersproject/abstract-provider": "^5.4.0",
|
||||||
|
@ -20794,14 +20752,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ethersproject/logger": {
|
"@ethersproject/logger": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.4.1.tgz",
|
||||||
"integrity": "sha512-xYdWGGQ9P2cxBayt64d8LC8aPFJk6yWCawQi/4eJ4+oJdMMjEBMrIcIMZ9AxhwpPVmnBPrsB10PcXGmGAqgUEQ=="
|
"integrity": "sha512-DZ+bRinnYLPw1yAC64oRl0QyVZj43QeHIhVKfD/+YwSz4wsv1pfwb5SOFjz+r710YEWzU6LrhuSjpSO+6PeE4A=="
|
||||||
},
|
},
|
||||||
"@ethersproject/networks": {
|
"@ethersproject/networks": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.4.2.tgz",
|
||||||
"integrity": "sha512-8SvowCKz9Uf4xC5DTKI8+il8lWqOr78kmiqAVLYT9lzB8aSmJHQMD1GSuJI0CW4hMAnzocpGpZLgiMdzsNSPig==",
|
"integrity": "sha512-eekOhvJyBnuibfJnhtK46b8HimBc5+4gqpvd1/H9LEl7Q7/qhsIhM81dI9Fcnjpk3jB1aTy6bj0hz3cifhNeYw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ethersproject/logger": "^5.4.0"
|
"@ethersproject/logger": "^5.4.0"
|
||||||
}
|
}
|
||||||
|
@ -20816,17 +20774,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ethersproject/properties": {
|
"@ethersproject/properties": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.4.1.tgz",
|
||||||
"integrity": "sha512-7jczalGVRAJ+XSRvNA6D5sAwT4gavLq3OXPuV/74o3Rd2wuzSL035IMpIMgei4CYyBdialJMrTqkOnzccLHn4A==",
|
"integrity": "sha512-cyCGlF8wWlIZyizsj2PpbJ9I7rIlUAfnHYwy/T90pdkSn/NFTa5YWZx2wTJBe9V7dD65dcrrEMisCRUJiq6n3w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ethersproject/logger": "^5.4.0"
|
"@ethersproject/logger": "^5.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ethersproject/providers": {
|
"@ethersproject/providers": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.4.5.tgz",
|
||||||
"integrity": "sha512-p06eiFKz8nu/5Ju0kIX024gzEQIgE5pvvGrBCngpyVjpuLtUIWT3097Agw4mTn9/dEA0FMcfByzFqacBMSgCVg==",
|
"integrity": "sha512-1GkrvkiAw3Fj28cwi1Sqm8ED1RtERtpdXmRfwIBGmqBSN5MoeRUHuwHPppMtbPayPgpFcvD7/Gdc9doO5fGYgw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ethersproject/abstract-provider": "^5.4.0",
|
"@ethersproject/abstract-provider": "^5.4.0",
|
||||||
"@ethersproject/abstract-signer": "^5.4.0",
|
"@ethersproject/abstract-signer": "^5.4.0",
|
||||||
|
@ -25120,29 +25078,29 @@
|
||||||
"version": "1.8.1"
|
"version": "1.8.1"
|
||||||
},
|
},
|
||||||
"ethers": {
|
"ethers": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/ethers/-/ethers-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/ethers/-/ethers-5.4.6.tgz",
|
||||||
"integrity": "sha512-SrcddMdCgP1hukDvCPd87Aipbf4NWjQvdfAbZ65XSZGbfyuYPtIrUJPDH5B1SBRsdlfiEgX3eoz28DdBDzMNFg==",
|
"integrity": "sha512-F7LXARyB/Px3AQC6/QKedWZ8eqCkgOLORqL4B/F0Mag/K+qJSFGqsR36EaOZ6fKg3ZonI+pdbhb4A8Knt/43jQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ethersproject/abi": "5.4.0",
|
"@ethersproject/abi": "5.4.1",
|
||||||
"@ethersproject/abstract-provider": "5.4.0",
|
"@ethersproject/abstract-provider": "5.4.1",
|
||||||
"@ethersproject/abstract-signer": "5.4.0",
|
"@ethersproject/abstract-signer": "5.4.1",
|
||||||
"@ethersproject/address": "5.4.0",
|
"@ethersproject/address": "5.4.0",
|
||||||
"@ethersproject/base64": "5.4.0",
|
"@ethersproject/base64": "5.4.0",
|
||||||
"@ethersproject/basex": "5.4.0",
|
"@ethersproject/basex": "5.4.0",
|
||||||
"@ethersproject/bignumber": "5.4.0",
|
"@ethersproject/bignumber": "5.4.1",
|
||||||
"@ethersproject/bytes": "5.4.0",
|
"@ethersproject/bytes": "5.4.0",
|
||||||
"@ethersproject/constants": "5.4.0",
|
"@ethersproject/constants": "5.4.0",
|
||||||
"@ethersproject/contracts": "5.4.0",
|
"@ethersproject/contracts": "5.4.1",
|
||||||
"@ethersproject/hash": "5.4.0",
|
"@ethersproject/hash": "5.4.0",
|
||||||
"@ethersproject/hdnode": "5.4.0",
|
"@ethersproject/hdnode": "5.4.0",
|
||||||
"@ethersproject/json-wallets": "5.4.0",
|
"@ethersproject/json-wallets": "5.4.0",
|
||||||
"@ethersproject/keccak256": "5.4.0",
|
"@ethersproject/keccak256": "5.4.0",
|
||||||
"@ethersproject/logger": "5.4.0",
|
"@ethersproject/logger": "5.4.1",
|
||||||
"@ethersproject/networks": "5.4.1",
|
"@ethersproject/networks": "5.4.2",
|
||||||
"@ethersproject/pbkdf2": "5.4.0",
|
"@ethersproject/pbkdf2": "5.4.0",
|
||||||
"@ethersproject/properties": "5.4.0",
|
"@ethersproject/properties": "5.4.1",
|
||||||
"@ethersproject/providers": "5.4.1",
|
"@ethersproject/providers": "5.4.5",
|
||||||
"@ethersproject/random": "5.4.0",
|
"@ethersproject/random": "5.4.0",
|
||||||
"@ethersproject/rlp": "5.4.0",
|
"@ethersproject/rlp": "5.4.0",
|
||||||
"@ethersproject/sha2": "5.4.0",
|
"@ethersproject/sha2": "5.4.0",
|
||||||
|
@ -25154,30 +25112,6 @@
|
||||||
"@ethersproject/wallet": "5.4.0",
|
"@ethersproject/wallet": "5.4.0",
|
||||||
"@ethersproject/web": "5.4.0",
|
"@ethersproject/web": "5.4.0",
|
||||||
"@ethersproject/wordlists": "5.4.0"
|
"@ethersproject/wordlists": "5.4.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@ethersproject/abstract-signer": {
|
|
||||||
"version": "5.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.4.0.tgz",
|
|
||||||
"integrity": "sha512-AieQAzt05HJZS2bMofpuxMEp81AHufA5D6M4ScKwtolj041nrfIbIi8ciNW7+F59VYxXq+V4c3d568Q6l2m8ew==",
|
|
||||||
"requires": {
|
|
||||||
"@ethersproject/abstract-provider": "^5.4.0",
|
|
||||||
"@ethersproject/bignumber": "^5.4.0",
|
|
||||||
"@ethersproject/bytes": "^5.4.0",
|
|
||||||
"@ethersproject/logger": "^5.4.0",
|
|
||||||
"@ethersproject/properties": "^5.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@ethersproject/bignumber": {
|
|
||||||
"version": "5.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.4.0.tgz",
|
|
||||||
"integrity": "sha512-OXUu9f9hO3vGRIPxU40cignXZVaYyfx6j9NNMjebKdnaCL3anCLSSy8/b8d03vY6dh7duCC0kW72GEC4tZer2w==",
|
|
||||||
"requires": {
|
|
||||||
"@ethersproject/bytes": "^5.4.0",
|
|
||||||
"@ethersproject/logger": "^5.4.0",
|
|
||||||
"bn.js": "^4.11.9"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eventemitter3": {
|
"eventemitter3": {
|
||||||
|
@ -29515,9 +29449,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-router-dom": {
|
"react-router-dom": {
|
||||||
"version": "5.2.1",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz",
|
||||||
"integrity": "sha512-xhFFkBGVcIVPbWM2KEYzED+nuHQPmulVa7sqIs3ESxzYd1pYg8N8rxPnQ4T2o1zu/2QeDUWcaqST131SO1LR3w==",
|
"integrity": "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.12.13",
|
"@babel/runtime": "^7.12.13",
|
||||||
"history": "^4.9.0",
|
"history": "^4.9.0",
|
||||||
|
@ -30436,9 +30370,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"version": "12.0.0",
|
"version": "12.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/serve/-/serve-12.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/serve/-/serve-12.0.1.tgz",
|
||||||
"integrity": "sha512-BkTsETQYynAZ7rXX414kg4X6EvuZQS3UVs1NY0VQYdRHSTYWPYcH38nnDh48D0x6ONuislgjag8uKlU2gTBImA==",
|
"integrity": "sha512-CQ4ikLpxg/wmNM7yivulpS6fhjRiFG6OjmP8ty3/c1SBnSk23fpKmLAV4HboTA2KrZhkUPlDfjDhnRmAjQ5Phw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@zeit/schemas": "2.6.0",
|
"@zeit/schemas": "2.6.0",
|
||||||
"ajv": "6.12.6",
|
"ajv": "6.12.6",
|
||||||
|
@ -31974,9 +31908,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
|
||||||
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ=="
|
"integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA=="
|
||||||
},
|
},
|
||||||
"unicode-canonical-property-names-ecmascript": {
|
"unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "1.0.4"
|
"version": "1.0.4"
|
||||||
|
|
10
package.json
10
package.json
|
@ -6,7 +6,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blackbox-vision/react-qr-reader": "^5.0.0",
|
"@blackbox-vision/react-qr-reader": "^5.0.0",
|
||||||
"@chainlink/contracts": "^0.2.1",
|
"@chainlink/contracts": "^0.2.1",
|
||||||
"@craco/craco": "^6.2.0",
|
"@craco/craco": "^6.3.0",
|
||||||
"@fontsource/fira-code": "^4.5.1",
|
"@fontsource/fira-code": "^4.5.1",
|
||||||
"@fontsource/roboto": "^4.5.0",
|
"@fontsource/roboto": "^4.5.0",
|
||||||
"@fontsource/roboto-mono": "^4.5.0",
|
"@fontsource/roboto-mono": "^4.5.0",
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
"@types/react-router-dom": "^5.1.8",
|
"@types/react-router-dom": "^5.1.8",
|
||||||
"@types/react-syntax-highlighter": "^13.5.2",
|
"@types/react-syntax-highlighter": "^13.5.2",
|
||||||
"chart.js": "^3.5.1",
|
"chart.js": "^3.5.1",
|
||||||
"ethers": "^5.4.1",
|
"ethers": "^5.4.6",
|
||||||
"highlightjs-solidity": "^1.2.0",
|
"highlightjs-solidity": "^1.2.0",
|
||||||
"query-string": "^7.0.1",
|
"query-string": "^7.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
@ -38,11 +38,11 @@
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-error-boundary": "^3.1.3",
|
"react-error-boundary": "^3.1.3",
|
||||||
"react-image": "^4.0.3",
|
"react-image": "^4.0.3",
|
||||||
"react-router-dom": "^5.2.1",
|
"react-router-dom": "^5.3.0",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
"react-syntax-highlighter": "^15.4.4",
|
"react-syntax-highlighter": "^15.4.4",
|
||||||
"serve": "^12.0.0",
|
"serve": "^12.0.1",
|
||||||
"typescript": "^4.4.2",
|
"typescript": "^4.4.3",
|
||||||
"use-keyboard-shortcut": "^1.0.6",
|
"use-keyboard-shortcut": "^1.0.6",
|
||||||
"web-vitals": "^1.0.1"
|
"web-vitals": "^1.0.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -31,8 +31,8 @@ import { useENSCache } from "./useReverseCache";
|
||||||
import { useFeeToggler } from "./search/useFeeToggler";
|
import { useFeeToggler } from "./search/useFeeToggler";
|
||||||
import { SelectionContext, useSelection } from "./useSelection";
|
import { SelectionContext, useSelection } from "./useSelection";
|
||||||
import { useMultipleETHUSDOracle } from "./usePriceOracle";
|
import { useMultipleETHUSDOracle } from "./usePriceOracle";
|
||||||
|
import { useAppConfigContext } from "./useAppConfig";
|
||||||
import { useSourcify } from "./useSourcify";
|
import { useSourcify } from "./useSourcify";
|
||||||
import { SourcifySource } from "./url";
|
|
||||||
|
|
||||||
type BlockParams = {
|
type BlockParams = {
|
||||||
addressOrName: string;
|
addressOrName: string;
|
||||||
|
@ -180,9 +180,7 @@ const AddressTransactions: React.FC = () => {
|
||||||
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
|
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
|
||||||
|
|
||||||
const selectionCtx = useSelection();
|
const selectionCtx = useSelection();
|
||||||
const [sourcifySource, setSourcifySource] = useState<SourcifySource>(
|
const { sourcifySource } = useAppConfigContext();
|
||||||
SourcifySource.IPFS_IPNS
|
|
||||||
);
|
|
||||||
const rawMetadata = useSourcify(
|
const rawMetadata = useSourcify(
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
provider?.network.chainId,
|
provider?.network.chainId,
|
||||||
|
@ -316,8 +314,6 @@ const AddressTransactions: React.FC = () => {
|
||||||
<Contracts
|
<Contracts
|
||||||
checksummedAddress={checksummedAddress}
|
checksummedAddress={checksummedAddress}
|
||||||
rawMetadata={rawMetadata}
|
rawMetadata={rawMetadata}
|
||||||
sourcifySource={sourcifySource}
|
|
||||||
setSourcifySource={setSourcifySource}
|
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
45
src/App.tsx
45
src/App.tsx
|
@ -1,4 +1,4 @@
|
||||||
import React, { Suspense } from "react";
|
import React, { Suspense, useMemo, useState } from "react";
|
||||||
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
|
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
|
||||||
import WarningHeader from "./WarningHeader";
|
import WarningHeader from "./WarningHeader";
|
||||||
import Home from "./Home";
|
import Home from "./Home";
|
||||||
|
@ -9,6 +9,8 @@ import London from "./special/london/London";
|
||||||
import Footer from "./Footer";
|
import Footer from "./Footer";
|
||||||
import { ConnectionStatus } from "./types";
|
import { ConnectionStatus } from "./types";
|
||||||
import { RuntimeContext, useRuntime } from "./useRuntime";
|
import { RuntimeContext, useRuntime } from "./useRuntime";
|
||||||
|
import { AppConfig, AppConfigContext } from "./useAppConfig";
|
||||||
|
import { SourcifySource } from "./url";
|
||||||
|
|
||||||
const Block = React.lazy(() => import("./Block"));
|
const Block = React.lazy(() => import("./Block"));
|
||||||
const BlockTransactions = React.lazy(() => import("./BlockTransactions"));
|
const BlockTransactions = React.lazy(() => import("./BlockTransactions"));
|
||||||
|
@ -17,6 +19,15 @@ const Transaction = React.lazy(() => import("./Transaction"));
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const runtime = useRuntime();
|
const runtime = useRuntime();
|
||||||
|
const [sourcifySource, setSourcifySource] = useState<SourcifySource>(
|
||||||
|
SourcifySource.IPFS_IPNS
|
||||||
|
);
|
||||||
|
const appConfig = useMemo((): AppConfig => {
|
||||||
|
return {
|
||||||
|
sourcifySource,
|
||||||
|
setSourcifySource,
|
||||||
|
};
|
||||||
|
}, [sourcifySource, setSourcifySource]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<>LOADING</>}>
|
<Suspense fallback={<>LOADING</>}>
|
||||||
|
@ -41,21 +52,23 @@ const App = () => {
|
||||||
<London />
|
<London />
|
||||||
</Route>
|
</Route>
|
||||||
<Route>
|
<Route>
|
||||||
<div className="mb-auto">
|
<AppConfigContext.Provider value={appConfig}>
|
||||||
<Title />
|
<div className="mb-auto">
|
||||||
<Route path="/block/:blockNumberOrHash" exact>
|
<Title />
|
||||||
<Block />
|
<Route path="/block/:blockNumberOrHash" exact>
|
||||||
</Route>
|
<Block />
|
||||||
<Route path="/block/:blockNumber/txs" exact>
|
</Route>
|
||||||
<BlockTransactions />
|
<Route path="/block/:blockNumber/txs" exact>
|
||||||
</Route>
|
<BlockTransactions />
|
||||||
<Route path="/tx/:txhash">
|
</Route>
|
||||||
<Transaction />
|
<Route path="/tx/:txhash">
|
||||||
</Route>
|
<Transaction />
|
||||||
<Route path="/address/:addressOrName/:direction?">
|
</Route>
|
||||||
<AddressTransactions />
|
<Route path="/address/:addressOrName/:direction?">
|
||||||
</Route>
|
<AddressTransactions />
|
||||||
</div>
|
</Route>
|
||||||
|
</div>
|
||||||
|
</AppConfigContext.Provider>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Menu } from "@headlessui/react";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faBars } from "@fortawesome/free-solid-svg-icons/faBars";
|
||||||
|
import { SourcifySource } from "./url";
|
||||||
|
import { useAppConfigContext } from "./useAppConfig";
|
||||||
|
|
||||||
|
const SourcifyMenu: React.FC = () => {
|
||||||
|
const { sourcifySource, setSourcifySource } = useAppConfigContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<div className="relative self-stretch">
|
||||||
|
<Menu.Button className="w-full h-full flex justify-center items-center space-x-2 text-sm border rounded px-2 py-1">
|
||||||
|
<FontAwesomeIcon icon={faBars} size="1x" />
|
||||||
|
</Menu.Button>
|
||||||
|
<Menu.Items className="absolute right-0 mt-1 border p-1 rounded-b bg-white flex flex-col text-sm min-w-max">
|
||||||
|
<div className="px-2 py-1 text-xs border-b border-gray-300">
|
||||||
|
Sourcify Datasource
|
||||||
|
</div>
|
||||||
|
<SourcifyMenuItem
|
||||||
|
checked={sourcifySource === SourcifySource.IPFS_IPNS}
|
||||||
|
onClick={() => setSourcifySource(SourcifySource.IPFS_IPNS)}
|
||||||
|
>
|
||||||
|
Resolve IPNS
|
||||||
|
</SourcifyMenuItem>
|
||||||
|
<SourcifyMenuItem
|
||||||
|
checked={sourcifySource === SourcifySource.CENTRAL_SERVER}
|
||||||
|
onClick={() => setSourcifySource(SourcifySource.CENTRAL_SERVER)}
|
||||||
|
>
|
||||||
|
Sourcify Servers
|
||||||
|
</SourcifyMenuItem>
|
||||||
|
<SourcifyMenuItem
|
||||||
|
checked={sourcifySource === SourcifySource.CUSTOM_SNAPSHOT_SERVER}
|
||||||
|
onClick={() =>
|
||||||
|
setSourcifySource(SourcifySource.CUSTOM_SNAPSHOT_SERVER)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Local Snapshot
|
||||||
|
</SourcifyMenuItem>
|
||||||
|
</Menu.Items>
|
||||||
|
</div>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type SourcifyMenuItemProps = {
|
||||||
|
checked?: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SourcifyMenuItem: React.FC<SourcifyMenuItemProps> = ({
|
||||||
|
checked = false,
|
||||||
|
onClick,
|
||||||
|
children,
|
||||||
|
}) => (
|
||||||
|
<Menu.Item>
|
||||||
|
{({ active }) => (
|
||||||
|
<button
|
||||||
|
className={`text-sm text-left px-2 py-1 ${
|
||||||
|
active ? "border-orange-200 text-gray-500" : "text-gray-400"
|
||||||
|
} transition-transform transition-colors duration-75 ${
|
||||||
|
checked ? "text-gray-900" : ""
|
||||||
|
}`}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default React.memo(SourcifyMenu);
|
|
@ -5,6 +5,7 @@ import { faQrcode } from "@fortawesome/free-solid-svg-icons/faQrcode";
|
||||||
import useKeyboardShortcut from "use-keyboard-shortcut";
|
import useKeyboardShortcut from "use-keyboard-shortcut";
|
||||||
import PriceBox from "./PriceBox";
|
import PriceBox from "./PriceBox";
|
||||||
import CameraScanner from "./search/CameraScanner";
|
import CameraScanner from "./search/CameraScanner";
|
||||||
|
import SourcifyMenu from "./SourcifyMenu";
|
||||||
import { RuntimeContext } from "./useRuntime";
|
import { RuntimeContext } from "./useRuntime";
|
||||||
|
|
||||||
const Title: React.FC = () => {
|
const Title: React.FC = () => {
|
||||||
|
@ -82,6 +83,7 @@ const Title: React.FC = () => {
|
||||||
Search
|
Search
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
<SourcifyMenu />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -18,6 +18,7 @@ type TokenTransferItemProps = {
|
||||||
tokenMetas: TokenMetas;
|
tokenMetas: TokenMetas;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: handle partial
|
||||||
const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
|
const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
|
||||||
t,
|
t,
|
||||||
txData,
|
txData,
|
||||||
|
|
|
@ -11,6 +11,8 @@ import { RuntimeContext } from "./useRuntime";
|
||||||
import { SelectionContext, useSelection } from "./useSelection";
|
import { SelectionContext, useSelection } from "./useSelection";
|
||||||
import { useInternalOperations, useTxData } from "./useErigonHooks";
|
import { useInternalOperations, useTxData } from "./useErigonHooks";
|
||||||
import { useETHUSDOracle } from "./usePriceOracle";
|
import { useETHUSDOracle } from "./usePriceOracle";
|
||||||
|
import { useAppConfigContext } from "./useAppConfig";
|
||||||
|
import { useSourcify, useTransactionDescription } from "./useSourcify";
|
||||||
|
|
||||||
type TransactionParams = {
|
type TransactionParams = {
|
||||||
txhash: string;
|
txhash: string;
|
||||||
|
@ -44,6 +46,14 @@ const Transaction: React.FC = () => {
|
||||||
txData?.confirmedData?.blockNumber
|
txData?.confirmedData?.blockNumber
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { sourcifySource } = useAppConfigContext();
|
||||||
|
const metadata = useSourcify(
|
||||||
|
txData?.to,
|
||||||
|
provider?.network.chainId,
|
||||||
|
sourcifySource
|
||||||
|
);
|
||||||
|
const txDesc = useTransactionDescription(metadata, txData);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StandardFrame>
|
<StandardFrame>
|
||||||
<StandardSubtitle>Transaction Details</StandardSubtitle>
|
<StandardSubtitle>Transaction Details</StandardSubtitle>
|
||||||
|
@ -71,13 +81,14 @@ const Transaction: React.FC = () => {
|
||||||
<Route path="/tx/:txhash/" exact>
|
<Route path="/tx/:txhash/" exact>
|
||||||
<Details
|
<Details
|
||||||
txData={txData}
|
txData={txData}
|
||||||
|
txDesc={txDesc}
|
||||||
internalOps={internalOps}
|
internalOps={internalOps}
|
||||||
sendsEthToMiner={sendsEthToMiner}
|
sendsEthToMiner={sendsEthToMiner}
|
||||||
ethUSDPrice={blockETHUSDPrice}
|
ethUSDPrice={blockETHUSDPrice}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/tx/:txhash/logs/" exact>
|
<Route path="/tx/:txhash/logs/" exact>
|
||||||
<Logs txData={txData} />
|
<Logs txData={txData} metadata={metadata} />
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</SelectionContext.Provider>
|
</SelectionContext.Provider>
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { Light as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import hljs from "highlight.js";
|
import hljs from "highlight.js";
|
||||||
import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco";
|
import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco";
|
||||||
import { useContract } from "../useSourcify";
|
import { useContract } from "../useSourcify";
|
||||||
import { SourcifySource } from "../url";
|
|
||||||
|
|
||||||
import hljsDefineSolidity from "highlightjs-solidity";
|
import hljsDefineSolidity from "highlightjs-solidity";
|
||||||
|
import { useAppConfigContext } from "../useAppConfig";
|
||||||
hljsDefineSolidity(hljs);
|
hljsDefineSolidity(hljs);
|
||||||
|
|
||||||
type ContractProps = {
|
type ContractProps = {
|
||||||
|
@ -13,7 +13,6 @@ type ContractProps = {
|
||||||
networkId: number;
|
networkId: number;
|
||||||
filename: string;
|
filename: string;
|
||||||
source: any;
|
source: any;
|
||||||
sourcifySource: SourcifySource;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Contract: React.FC<ContractProps> = ({
|
const Contract: React.FC<ContractProps> = ({
|
||||||
|
@ -21,8 +20,8 @@ const Contract: React.FC<ContractProps> = ({
|
||||||
networkId,
|
networkId,
|
||||||
filename,
|
filename,
|
||||||
source,
|
source,
|
||||||
sourcifySource,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const { sourcifySource } = useAppConfigContext();
|
||||||
const content = useContract(
|
const content = useContract(
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
networkId,
|
networkId,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState, useEffect, useContext, Fragment } from "react";
|
import React, { useState, useEffect, useContext, Fragment } from "react";
|
||||||
import { commify } from "@ethersproject/units";
|
import { commify } from "@ethersproject/units";
|
||||||
import { Menu, RadioGroup } from "@headlessui/react";
|
import { Menu } from "@headlessui/react";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
||||||
import ContentFrame from "../ContentFrame";
|
import ContentFrame from "../ContentFrame";
|
||||||
|
@ -11,21 +11,16 @@ import Contract from "./Contract";
|
||||||
import { RuntimeContext } from "../useRuntime";
|
import { RuntimeContext } from "../useRuntime";
|
||||||
import { Metadata } from "../useSourcify";
|
import { Metadata } from "../useSourcify";
|
||||||
import ExternalLink from "../components/ExternalLink";
|
import ExternalLink from "../components/ExternalLink";
|
||||||
import { openInRemixURL, SourcifySource } from "../url";
|
import { openInRemixURL } from "../url";
|
||||||
import RadioButton from "./RadioButton";
|
|
||||||
|
|
||||||
type ContractsProps = {
|
type ContractsProps = {
|
||||||
checksummedAddress: string;
|
checksummedAddress: string;
|
||||||
rawMetadata: Metadata | null | undefined;
|
rawMetadata: Metadata | null | undefined;
|
||||||
sourcifySource: SourcifySource;
|
|
||||||
setSourcifySource: (sourcifySource: SourcifySource) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Contracts: React.FC<ContractsProps> = ({
|
const Contracts: React.FC<ContractsProps> = ({
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
rawMetadata,
|
rawMetadata,
|
||||||
sourcifySource,
|
|
||||||
setSourcifySource,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { provider } = useContext(RuntimeContext);
|
const { provider } = useContext(RuntimeContext);
|
||||||
|
|
||||||
|
@ -39,21 +34,6 @@ const Contracts: React.FC<ContractsProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentFrame tabs>
|
<ContentFrame tabs>
|
||||||
<InfoRow title="Sourcify integration">
|
|
||||||
<RadioGroup value={sourcifySource} onChange={setSourcifySource}>
|
|
||||||
<div className="flex space-x-2">
|
|
||||||
<RadioButton value={SourcifySource.IPFS_IPNS}>
|
|
||||||
Resolve IPNS @localhost:8080 gateway
|
|
||||||
</RadioButton>
|
|
||||||
<RadioButton value={SourcifySource.CENTRAL_SERVER}>
|
|
||||||
Sourcify Servers
|
|
||||||
</RadioButton>
|
|
||||||
<RadioButton value={SourcifySource.CUSTOM_SNAPSHOT_SERVER}>
|
|
||||||
Local Snapshot @localhost:3006
|
|
||||||
</RadioButton>
|
|
||||||
</div>
|
|
||||||
</RadioGroup>
|
|
||||||
</InfoRow>
|
|
||||||
{rawMetadata && (
|
{rawMetadata && (
|
||||||
<>
|
<>
|
||||||
<InfoRow title="Language">
|
<InfoRow title="Language">
|
||||||
|
@ -145,7 +125,6 @@ const Contracts: React.FC<ContractsProps> = ({
|
||||||
networkId={provider!.network.chainId}
|
networkId={provider!.network.chainId}
|
||||||
filename={selected}
|
filename={selected}
|
||||||
source={rawMetadata.sources[selected]}
|
source={rawMetadata.sources[selected]}
|
||||||
sourcifySource={sourcifySource}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
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,16 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Tab } from "@headlessui/react";
|
||||||
|
|
||||||
|
const ModeTab: React.FC = ({ children }) => (
|
||||||
|
<Tab
|
||||||
|
className={({ selected }) =>
|
||||||
|
`border rounded-lg px-2 py-1 bg-gray-100 hover:bg-gray-200 hover:shadow text-xs text-gray-500 hover:text-gray-600 ${
|
||||||
|
selected ? "border-blue-300" : ""
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Tab>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ModeTab;
|
|
@ -6,6 +6,8 @@ type NavTabProps = {
|
||||||
href: string;
|
href: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: migrate activeClassName because of: https://github.com/remix-run/react-router/releases/tag/v5.3.0
|
||||||
|
// TODO: @types/react-router-dom still doesn't support function in className
|
||||||
const NavTab: React.FC<NavTabProps> = ({ href, children }) => (
|
const NavTab: React.FC<NavTabProps> = ({ href, children }) => (
|
||||||
<Tab as={Fragment}>
|
<Tab as={Fragment}>
|
||||||
<NavLink
|
<NavLink
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from "react";
|
||||||
|
import { EventFragment } from "@ethersproject/abi";
|
||||||
|
|
||||||
|
type DecodedLogSignatureProps = {
|
||||||
|
event: EventFragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DecodedLogSignature: React.FC<DecodedLogSignatureProps> = ({ event }) => {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<span className="text-blue-900 font-bold">{event.name}</span>(
|
||||||
|
{event.inputs.map((input, i) => (
|
||||||
|
<span key={i}>
|
||||||
|
{i > 0 ? ", " : ""}
|
||||||
|
<span>{input.format("full")}</span>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
){event.anonymous ? " anonymous" : ""}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(DecodedLogSignature);
|
|
@ -0,0 +1,79 @@
|
||||||
|
import React from "react";
|
||||||
|
import AddressHighlighter from "../components/AddressHighlighter";
|
||||||
|
import DecoratedAddressLink from "../components/DecoratedAddressLink";
|
||||||
|
import Copy from "../components/Copy";
|
||||||
|
import { ParamType } from "@ethersproject/abi";
|
||||||
|
import { TransactionData } from "../types";
|
||||||
|
|
||||||
|
type DecodedParamRowProps = {
|
||||||
|
prefix?: string;
|
||||||
|
i?: number | undefined;
|
||||||
|
r: any;
|
||||||
|
paramType: ParamType;
|
||||||
|
txData: TransactionData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DecodedParamRow: React.FC<DecodedParamRowProps> = ({
|
||||||
|
prefix,
|
||||||
|
i,
|
||||||
|
r,
|
||||||
|
paramType,
|
||||||
|
txData,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<tr className="grid grid-cols-12 gap-x-2 py-2 hover:bg-gray-100">
|
||||||
|
<td className="col-span-3 pl-1">
|
||||||
|
{prefix && <span className="text-gray-300">{prefix}</span>}
|
||||||
|
{paramType.name}{" "}
|
||||||
|
{i !== undefined && (
|
||||||
|
<span className="text-gray-400 text-xs">({i})</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className="col-span-1 text-gray-500">{paramType.type}</td>
|
||||||
|
<td className="col-span-8 pr-1 font-code break-all">
|
||||||
|
{paramType.baseType === "address" ? (
|
||||||
|
<div className="flex items-baseline space-x-2 -ml-1 mr-3">
|
||||||
|
<AddressHighlighter address={r.toString()}>
|
||||||
|
<DecoratedAddressLink
|
||||||
|
address={r.toString()}
|
||||||
|
miner={r.toString() === txData.confirmedData?.miner}
|
||||||
|
txFrom={r.toString() === txData.from}
|
||||||
|
txTo={r.toString() === txData.to}
|
||||||
|
/>
|
||||||
|
</AddressHighlighter>
|
||||||
|
<Copy value={r.toString()} />
|
||||||
|
</div>
|
||||||
|
) : paramType.baseType === "bool" ? (
|
||||||
|
<span className={`${r ? "text-green-700" : "text-red-700"}`}>
|
||||||
|
{r.toString()}
|
||||||
|
</span>
|
||||||
|
) : paramType.baseType === "bytes" ? (
|
||||||
|
<span>
|
||||||
|
{r.toString()}{" "}
|
||||||
|
<span className="font-sans text-xs text-gray-400">
|
||||||
|
{r.toString().length / 2 - 1}{" "}
|
||||||
|
{r.toString().length / 2 - 1 === 1 ? "byte" : "bytes"}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
) : paramType.baseType === "tuple" ? (
|
||||||
|
<></>
|
||||||
|
) : (
|
||||||
|
r.toString()
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{paramType.baseType === "tuple" &&
|
||||||
|
r.map((e: any, idx: number) => (
|
||||||
|
<DecodedParamRow key={idx}
|
||||||
|
prefix={paramType.name + "."}
|
||||||
|
r={e}
|
||||||
|
paramType={paramType.components[idx]}
|
||||||
|
txData={txData}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(DecodedParamRow);
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React from "react";
|
||||||
|
import { ParamType, Result } from "@ethersproject/abi";
|
||||||
|
import DecodedParamRow from "./DecodedParamRow";
|
||||||
|
import { TransactionData } from "../types";
|
||||||
|
|
||||||
|
type DecodedParamsTableProps = {
|
||||||
|
args: Result;
|
||||||
|
paramTypes: ParamType[];
|
||||||
|
txData: TransactionData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DecodedParamsTable: React.FC<DecodedParamsTableProps> = ({
|
||||||
|
args,
|
||||||
|
paramTypes,
|
||||||
|
txData,
|
||||||
|
}) => (
|
||||||
|
<table className="border rounded w-full">
|
||||||
|
<thead>
|
||||||
|
<tr className="grid grid-cols-12 text-left gap-x-2 py-2 bg-gray-100">
|
||||||
|
<th className="col-span-3 pl-1">
|
||||||
|
name <span className="text-gray-400 text-xs">(index)</span>
|
||||||
|
</th>
|
||||||
|
<th className="col-span-1">type</th>
|
||||||
|
<th className="col-span-8 pr-1">value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y">
|
||||||
|
{args.map((r, i) => (
|
||||||
|
<DecodedParamRow
|
||||||
|
key={i}
|
||||||
|
i={i}
|
||||||
|
r={r}
|
||||||
|
paramType={paramTypes[i]}
|
||||||
|
txData={txData}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default React.memo(DecodedParamsTable);
|
|
@ -1,6 +1,8 @@
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo } from "react";
|
||||||
|
import { TransactionDescription } from "@ethersproject/abi";
|
||||||
import { BigNumber } from "@ethersproject/bignumber";
|
import { BigNumber } from "@ethersproject/bignumber";
|
||||||
import { toUtf8String } from "@ethersproject/strings";
|
import { toUtf8String } from "@ethersproject/strings";
|
||||||
|
import { Tab } from "@headlessui/react";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faCheckCircle } from "@fortawesome/free-solid-svg-icons/faCheckCircle";
|
import { faCheckCircle } from "@fortawesome/free-solid-svg-icons/faCheckCircle";
|
||||||
import { faCube } from "@fortawesome/free-solid-svg-icons/faCube";
|
import { faCube } from "@fortawesome/free-solid-svg-icons/faCube";
|
||||||
|
@ -28,9 +30,12 @@ import PercentageBar from "../components/PercentageBar";
|
||||||
import ExternalLink from "../components/ExternalLink";
|
import ExternalLink from "../components/ExternalLink";
|
||||||
import RelativePosition from "../components/RelativePosition";
|
import RelativePosition from "../components/RelativePosition";
|
||||||
import PercentagePosition from "../components/PercentagePosition";
|
import PercentagePosition from "../components/PercentagePosition";
|
||||||
|
import ModeTab from "../components/ModeTab";
|
||||||
|
import DecodedParamsTable from "./DecodedParamsTable";
|
||||||
|
|
||||||
type DetailsProps = {
|
type DetailsProps = {
|
||||||
txData: TransactionData;
|
txData: TransactionData;
|
||||||
|
txDesc: TransactionDescription | null | undefined;
|
||||||
internalOps?: InternalOperation[];
|
internalOps?: InternalOperation[];
|
||||||
sendsEthToMiner: boolean;
|
sendsEthToMiner: boolean;
|
||||||
ethUSDPrice: BigNumber | undefined;
|
ethUSDPrice: BigNumber | undefined;
|
||||||
|
@ -38,6 +43,7 @@ type DetailsProps = {
|
||||||
|
|
||||||
const Details: React.FC<DetailsProps> = ({
|
const Details: React.FC<DetailsProps> = ({
|
||||||
txData,
|
txData,
|
||||||
|
txDesc,
|
||||||
internalOps,
|
internalOps,
|
||||||
sendsEthToMiner,
|
sendsEthToMiner,
|
||||||
ethUSDPrice,
|
ethUSDPrice,
|
||||||
|
@ -45,7 +51,6 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
const hasEIP1559 =
|
const hasEIP1559 =
|
||||||
txData.confirmedData?.blockBaseFeePerGas !== undefined &&
|
txData.confirmedData?.blockBaseFeePerGas !== undefined &&
|
||||||
txData.confirmedData?.blockBaseFeePerGas !== null;
|
txData.confirmedData?.blockBaseFeePerGas !== null;
|
||||||
const [inputMode, setInputMode] = useState<number>(0);
|
|
||||||
|
|
||||||
const utfInput = useMemo(() => {
|
const utfInput = useMemo(() => {
|
||||||
try {
|
try {
|
||||||
|
@ -309,31 +314,42 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<InfoRow title="Input Data">
|
<InfoRow title="Input Data">
|
||||||
<div className="space-y-1">
|
<Tab.Group>
|
||||||
<div className="flex space-x-1">
|
<Tab.List className="flex space-x-1 mb-1">
|
||||||
<button
|
<ModeTab>Decoded</ModeTab>
|
||||||
className={`border rounded-lg px-2 py-1 bg-gray-100 hover:bg-gray-200 hover:shadow text-xs text-gray-500 hover:text-gray-600 ${
|
<ModeTab>Raw</ModeTab>
|
||||||
inputMode === 0 ? "border-blue-300" : ""
|
<ModeTab>UTF-8</ModeTab>
|
||||||
}`}
|
</Tab.List>
|
||||||
onClick={() => setInputMode(0)}
|
<Tab.Panels>
|
||||||
>
|
<Tab.Panel>
|
||||||
Raw
|
{txDesc === undefined ? (
|
||||||
</button>
|
<>Waiting for data...</>
|
||||||
<button
|
) : txDesc === null ? (
|
||||||
className={`border rounded-lg px-2 py-1 bg-gray-100 hover:bg-gray-200 hover:shadow text-xs text-gray-500 hover:text-gray-600 ${
|
<>No decoded data</>
|
||||||
inputMode === 1 ? "border-blue-300" : ""
|
) : (
|
||||||
}`}
|
<DecodedParamsTable
|
||||||
onClick={() => setInputMode(1)}
|
args={txDesc.args}
|
||||||
>
|
paramTypes={txDesc.functionFragment.inputs}
|
||||||
UTF-8
|
txData={txData}
|
||||||
</button>
|
/>
|
||||||
</div>
|
)}
|
||||||
<textarea
|
</Tab.Panel>
|
||||||
className="w-full h-40 bg-gray-50 text-gray-500 font-mono focus:outline-none border rounded p-2"
|
<Tab.Panel>
|
||||||
value={inputMode === 0 ? txData.data : utfInput}
|
<textarea
|
||||||
readOnly
|
className="w-full h-40 bg-gray-50 text-gray-500 font-mono focus:outline-none border rounded p-2"
|
||||||
/>
|
value={txData.data}
|
||||||
</div>
|
readOnly
|
||||||
|
/>
|
||||||
|
</Tab.Panel>
|
||||||
|
<Tab.Panel>
|
||||||
|
<textarea
|
||||||
|
className="w-full h-40 bg-gray-50 text-gray-500 font-mono focus:outline-none border rounded p-2"
|
||||||
|
value={utfInput}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</Tab.Panel>
|
||||||
|
</Tab.Panels>
|
||||||
|
</Tab.Group>
|
||||||
</InfoRow>
|
</InfoRow>
|
||||||
</ContentFrame>
|
</ContentFrame>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
import React, { Fragment } from "react";
|
||||||
|
import { Log } from "@ethersproject/abstract-provider";
|
||||||
|
import { LogDescription } from "@ethersproject/abi";
|
||||||
|
import { Tab } from "@headlessui/react";
|
||||||
|
import AddressHighlighter from "../components/AddressHighlighter";
|
||||||
|
import DecoratedAddressLink from "../components/DecoratedAddressLink";
|
||||||
|
import Copy from "../components/Copy";
|
||||||
|
import ModeTab from "../components/ModeTab";
|
||||||
|
import DecodedParamsTable from "./DecodedParamsTable";
|
||||||
|
import DecodedLogSignature from "./DecodedLogSignature";
|
||||||
|
import { TransactionData } from "../types";
|
||||||
|
|
||||||
|
type LogEntryProps = {
|
||||||
|
txData: TransactionData;
|
||||||
|
log: Log;
|
||||||
|
logDesc: LogDescription | null | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LogEntry: React.FC<LogEntryProps> = ({ txData, log, logDesc }) => (
|
||||||
|
<div className="flex space-x-10 py-5">
|
||||||
|
<div>
|
||||||
|
<span className="rounded-full w-12 h-12 flex items-center justify-center bg-green-50 text-green-500">
|
||||||
|
{log.logIndex}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full space-y-2">
|
||||||
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
|
<div className="font-bold text-right">Address</div>
|
||||||
|
<div className="col-span-11 mr-auto">
|
||||||
|
<div className="flex items-baseline space-x-2 -ml-1 mr-3">
|
||||||
|
<AddressHighlighter address={log.address}>
|
||||||
|
<DecoratedAddressLink
|
||||||
|
address={log.address}
|
||||||
|
miner={log.address === txData.confirmedData?.miner}
|
||||||
|
txFrom={log.address === txData.from}
|
||||||
|
txTo={log.address === txData.to}
|
||||||
|
/>
|
||||||
|
</AddressHighlighter>
|
||||||
|
<Copy value={log.address} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Tab.Group>
|
||||||
|
<Tab.List className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
|
<div className="text-right">Parameters</div>
|
||||||
|
<div className="col-span-11 flex space-x-1 mb-1">
|
||||||
|
<ModeTab>Decoded</ModeTab>
|
||||||
|
<ModeTab>Raw</ModeTab>
|
||||||
|
</div>
|
||||||
|
</Tab.List>
|
||||||
|
<Tab.Panels as={Fragment}>
|
||||||
|
<Tab.Panel className="space-y-2">
|
||||||
|
{logDesc === undefined ? (
|
||||||
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
|
<div className="col-start-2 flex space-x-2 items-center col-span-11">
|
||||||
|
Waiting for data...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : logDesc === null ? (
|
||||||
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
|
<div className="col-start-2 flex space-x-2 items-center col-span-11">
|
||||||
|
No decoded data
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
|
<div className="col-start-2 flex space-x-2 items-center col-span-11 font-mono">
|
||||||
|
<DecodedLogSignature event={logDesc.eventFragment} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
|
<div className="col-start-2 flex space-x-2 items-center col-span-11">
|
||||||
|
<DecodedParamsTable
|
||||||
|
args={logDesc.args}
|
||||||
|
paramTypes={logDesc.eventFragment.inputs}
|
||||||
|
txData={txData}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Tab.Panel>
|
||||||
|
<Tab.Panel className="space-y-2">
|
||||||
|
{log.topics.map((t, i) => (
|
||||||
|
<div
|
||||||
|
className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm"
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
<div className="text-right">{i === 0 && "Topics"}</div>
|
||||||
|
<div className="flex space-x-2 items-center col-span-11 font-mono">
|
||||||
|
<span className="rounded bg-gray-100 text-gray-500 px-2 py-1 text-xs">
|
||||||
|
{i}
|
||||||
|
</span>
|
||||||
|
<span>{t}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
|
<div className="text-right pt-2">Data</div>
|
||||||
|
<div className="col-span-11">
|
||||||
|
<textarea
|
||||||
|
className="w-full h-40 bg-gray-50 font-mono focus:outline-none border rounded p-2"
|
||||||
|
value={log.data}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab.Panel>
|
||||||
|
</Tab.Panels>
|
||||||
|
</Tab.Group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default React.memo(LogEntry);
|
|
@ -1,63 +1,85 @@
|
||||||
import React from "react";
|
import React, { useMemo } from "react";
|
||||||
|
import { Interface } from "@ethersproject/abi";
|
||||||
import ContentFrame from "../ContentFrame";
|
import ContentFrame from "../ContentFrame";
|
||||||
import DecoratedAddressLink from "../components/DecoratedAddressLink";
|
import LogEntry from "./LogEntry";
|
||||||
import { TransactionData } from "../types";
|
import { TransactionData } from "../types";
|
||||||
|
import { useAppConfigContext } from "../useAppConfig";
|
||||||
|
import { Metadata, useMultipleMetadata } from "../useSourcify";
|
||||||
|
|
||||||
type LogsProps = {
|
type LogsProps = {
|
||||||
txData: TransactionData;
|
txData: TransactionData;
|
||||||
|
metadata: Metadata | null | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Logs: React.FC<LogsProps> = ({ txData }) => (
|
const Logs: React.FC<LogsProps> = ({ txData, metadata }) => {
|
||||||
<ContentFrame tabs>
|
const baseMetadatas = useMemo((): Record<string, Metadata | null> => {
|
||||||
<div className="text-sm py-4">Transaction Receipt Event Logs</div>
|
if (!txData.to || metadata === undefined) {
|
||||||
{txData.confirmedData &&
|
return {};
|
||||||
txData.confirmedData.logs.map((l, i) => (
|
}
|
||||||
<div className="flex space-x-10 py-5" key={i}>
|
|
||||||
<div>
|
const md: Record<string, Metadata | null> = {};
|
||||||
<span className="rounded-full w-12 h-12 flex items-center justify-center bg-green-50 text-green-500">
|
md[txData.to] = metadata;
|
||||||
{l.logIndex}
|
return md;
|
||||||
</span>
|
}, [txData.to, metadata]);
|
||||||
</div>
|
|
||||||
<div className="w-full space-y-2">
|
const { sourcifySource } = useAppConfigContext();
|
||||||
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
const logAddresses = useMemo(
|
||||||
<div className="font-bold text-right">Address</div>
|
() => txData.confirmedData?.logs.map((l) => l.address) ?? [],
|
||||||
<div className="col-span-11 mr-auto">
|
[txData]
|
||||||
<DecoratedAddressLink
|
);
|
||||||
address={l.address}
|
const metadatas = useMultipleMetadata(
|
||||||
miner={l.address === txData.confirmedData?.miner}
|
baseMetadatas,
|
||||||
txFrom={l.address === txData.from}
|
logAddresses,
|
||||||
txTo={l.address === txData.to}
|
1,
|
||||||
|
sourcifySource
|
||||||
|
);
|
||||||
|
const logDescs = useMemo(() => {
|
||||||
|
if (!txData) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return txData.confirmedData?.logs.map((l) => {
|
||||||
|
const mt = metadatas[l.address];
|
||||||
|
if (!mt) {
|
||||||
|
return mt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const abi = mt.output.abi;
|
||||||
|
const intf = new Interface(abi as any);
|
||||||
|
try {
|
||||||
|
return intf.parseLog({
|
||||||
|
topics: l.topics,
|
||||||
|
data: l.data,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Couldn't find function signature", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [metadatas, txData]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContentFrame tabs>
|
||||||
|
{txData.confirmedData && (
|
||||||
|
<>
|
||||||
|
{txData.confirmedData.logs.length > 0 ? (
|
||||||
|
<>
|
||||||
|
{txData.confirmedData.logs.map((l, i) => (
|
||||||
|
<LogEntry
|
||||||
|
key={i}
|
||||||
|
txData={txData}
|
||||||
|
log={l}
|
||||||
|
logDesc={logDescs?.[i]}
|
||||||
/>
|
/>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</>
|
||||||
{l.topics.map((t, i) => (
|
) : (
|
||||||
<div
|
<div className="text-sm py-4">Transaction didn't emit any logs</div>
|
||||||
className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm"
|
)}
|
||||||
key={i}
|
</>
|
||||||
>
|
)}
|
||||||
<div className="text-right">{i === 0 && "Topics"}</div>
|
</ContentFrame>
|
||||||
<div className="flex space-x-2 items-center col-span-11 font-mono">
|
);
|
||||||
<span className="rounded bg-gray-100 text-gray-500 px-2 py-1 text-xs">
|
};
|
||||||
{i}
|
|
||||||
</span>
|
|
||||||
<span>{t}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
|
||||||
<div className="text-right pt-2">Data</div>
|
|
||||||
<div className="col-span-11">
|
|
||||||
<textarea
|
|
||||||
className="w-full h-20 bg-gray-50 font-mono focus:outline-none border rounded p-2"
|
|
||||||
value={l.data}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</ContentFrame>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default React.memo(Logs);
|
export default React.memo(Logs);
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import React, { useContext } from "react";
|
||||||
|
import { SourcifySource } from "./url";
|
||||||
|
|
||||||
|
export type AppConfig = {
|
||||||
|
sourcifySource: SourcifySource;
|
||||||
|
setSourcifySource: (newSourcifySource: SourcifySource) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AppConfigContext = React.createContext<AppConfig>(undefined!);
|
||||||
|
|
||||||
|
export const useAppConfigContext = () => {
|
||||||
|
return useContext(AppConfigContext);
|
||||||
|
};
|
|
@ -225,16 +225,20 @@ export const useTxData = (
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const erc20Contract = new Contract(t.token, erc20, provider);
|
const erc20Contract = new Contract(t.token, erc20, provider);
|
||||||
const [name, symbol, decimals] = await Promise.all([
|
try {
|
||||||
erc20Contract.name(),
|
const [name, symbol, decimals] = await Promise.all([
|
||||||
erc20Contract.symbol(),
|
erc20Contract.name(),
|
||||||
erc20Contract.decimals(),
|
erc20Contract.symbol(),
|
||||||
]);
|
erc20Contract.decimals(),
|
||||||
tokenMetas[t.token] = {
|
]);
|
||||||
name,
|
tokenMetas[t.token] = {
|
||||||
symbol,
|
name,
|
||||||
decimals,
|
symbol,
|
||||||
};
|
decimals,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Couldn't get token ${t.token} metadata; ignoring`, err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTxData({
|
setTxData({
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useMemo } from "react";
|
||||||
|
import { Interface } from "@ethersproject/abi";
|
||||||
|
import { TransactionData } from "./types";
|
||||||
import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "./url";
|
import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "./url";
|
||||||
|
|
||||||
export type Metadata = {
|
export type Metadata = {
|
||||||
|
@ -36,44 +38,114 @@ export type Metadata = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchSourcifyMetadata = async (
|
||||||
|
checksummedAddress: string,
|
||||||
|
chainId: number,
|
||||||
|
source: SourcifySource,
|
||||||
|
abortController: AbortController
|
||||||
|
): Promise<Metadata | null> => {
|
||||||
|
try {
|
||||||
|
const contractMetadataURL = sourcifyMetadata(
|
||||||
|
checksummedAddress,
|
||||||
|
chainId,
|
||||||
|
source
|
||||||
|
);
|
||||||
|
const result = await fetch(contractMetadataURL, {
|
||||||
|
signal: abortController.signal,
|
||||||
|
});
|
||||||
|
if (result.ok) {
|
||||||
|
const _metadata = await result.json();
|
||||||
|
return _metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const useSourcify = (
|
export const useSourcify = (
|
||||||
checksummedAddress: string | undefined,
|
checksummedAddress: string | undefined,
|
||||||
chainId: number | undefined,
|
chainId: number | undefined,
|
||||||
source: SourcifySource
|
source: SourcifySource
|
||||||
) => {
|
): Metadata | null | undefined => {
|
||||||
const [rawMetadata, setRawMetadata] = useState<Metadata | null | undefined>();
|
const [rawMetadata, setRawMetadata] = useState<Metadata | null | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!checksummedAddress || chainId === undefined) {
|
if (!checksummedAddress || chainId === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setRawMetadata(undefined);
|
setRawMetadata(undefined);
|
||||||
|
|
||||||
|
const abortController = new AbortController();
|
||||||
const fetchMetadata = async () => {
|
const fetchMetadata = async () => {
|
||||||
try {
|
const _metadata = await fetchSourcifyMetadata(
|
||||||
const contractMetadataURL = sourcifyMetadata(
|
checksummedAddress,
|
||||||
checksummedAddress,
|
chainId,
|
||||||
chainId,
|
source,
|
||||||
source
|
abortController
|
||||||
);
|
);
|
||||||
const result = await fetch(contractMetadataURL);
|
setRawMetadata(_metadata);
|
||||||
if (result.ok) {
|
|
||||||
const _metadata = await result.json();
|
|
||||||
setRawMetadata(_metadata);
|
|
||||||
} else {
|
|
||||||
setRawMetadata(null);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
setRawMetadata(null);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
fetchMetadata();
|
fetchMetadata();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
abortController.abort();
|
||||||
|
};
|
||||||
}, [checksummedAddress, chainId, source]);
|
}, [checksummedAddress, chainId, source]);
|
||||||
|
|
||||||
return rawMetadata;
|
return rawMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useMultipleMetadata = (
|
||||||
|
baseMetadatas: Record<string, Metadata | null>,
|
||||||
|
checksummedAddress: (string | undefined)[],
|
||||||
|
chainId: number | undefined,
|
||||||
|
source: SourcifySource
|
||||||
|
): Record<string, Metadata | null | undefined> => {
|
||||||
|
const [rawMetadata, setRawMetadata] = useState<
|
||||||
|
Record<string, Metadata | null | undefined>
|
||||||
|
>({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!checksummedAddress || chainId === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setRawMetadata({});
|
||||||
|
|
||||||
|
const abortController = new AbortController();
|
||||||
|
const fetchMetadata = async (addresses: string[]) => {
|
||||||
|
const promises: Promise<Metadata | null>[] = [];
|
||||||
|
for (const addr of addresses) {
|
||||||
|
promises.push(
|
||||||
|
fetchSourcifyMetadata(addr, chainId, source, abortController)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await Promise.all(promises);
|
||||||
|
const metadatas: Record<string, Metadata | null> = { ...baseMetadatas };
|
||||||
|
for (let i = 0; i < results.length; i++) {
|
||||||
|
metadatas[addresses[i]] = results[i];
|
||||||
|
}
|
||||||
|
setRawMetadata(metadatas);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deduped = new Set(
|
||||||
|
checksummedAddress.filter(
|
||||||
|
(a): a is string => a !== undefined && baseMetadatas[a] === undefined
|
||||||
|
)
|
||||||
|
);
|
||||||
|
fetchMetadata(Array.from(deduped));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
abortController.abort();
|
||||||
|
};
|
||||||
|
}, [baseMetadatas, checksummedAddress, chainId, source]);
|
||||||
|
|
||||||
|
return rawMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
export const useContract = (
|
export const useContract = (
|
||||||
checksummedAddress: string,
|
checksummedAddress: string,
|
||||||
networkId: number,
|
networkId: number,
|
||||||
|
@ -88,6 +160,7 @@ export const useContract = (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const abortController = new AbortController();
|
||||||
const readContent = async () => {
|
const readContent = async () => {
|
||||||
const normalizedFilename = filename.replaceAll(/[@:]/g, "_");
|
const normalizedFilename = filename.replaceAll(/[@:]/g, "_");
|
||||||
const url = sourcifySourceFile(
|
const url = sourcifySourceFile(
|
||||||
|
@ -96,14 +169,46 @@ export const useContract = (
|
||||||
normalizedFilename,
|
normalizedFilename,
|
||||||
sourcifySource
|
sourcifySource
|
||||||
);
|
);
|
||||||
const res = await fetch(url);
|
const res = await fetch(url, { signal: abortController.signal });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const _content = await res.text();
|
const _content = await res.text();
|
||||||
setContent(_content);
|
setContent(_content);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
readContent();
|
readContent();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
abortController.abort();
|
||||||
|
};
|
||||||
}, [checksummedAddress, networkId, filename, source.content, sourcifySource]);
|
}, [checksummedAddress, networkId, filename, source.content, sourcifySource]);
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useTransactionDescription = (
|
||||||
|
metadata: Metadata | null | undefined,
|
||||||
|
txData: TransactionData | null | undefined
|
||||||
|
) => {
|
||||||
|
const txDesc = useMemo(() => {
|
||||||
|
if (metadata === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!metadata || !txData) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const abi = metadata.output.abi;
|
||||||
|
const intf = new Interface(abi as any);
|
||||||
|
try {
|
||||||
|
return intf.parseTransaction({
|
||||||
|
data: txData.data,
|
||||||
|
value: txData.value,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Couldn't find function signature", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, [metadata, txData]);
|
||||||
|
|
||||||
|
return txDesc;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue