Commit 389cbeb4 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into tx-state-api

parents 54cae6f5 fcddd709
...@@ -62,3 +62,4 @@ NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=__PLACEHOLDER_FOR_NEXT_PUBLIC_GOOGLE_AN ...@@ -62,3 +62,4 @@ NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=__PLACEHOLDER_FOR_NEXT_PUBLIC_GOOGLE_AN
# l2 config # l2 config
NEXT_PUBLIC_IS_L2_NETWORK=__PLACEHOLDER_FOR_NEXT_PUBLIC_IS_L2_NETWORKL__ NEXT_PUBLIC_IS_L2_NETWORK=__PLACEHOLDER_FOR_NEXT_PUBLIC_IS_L2_NETWORKL__
NEXT_PUBLIC_L1_BASE_URL=__PLACEHOLDER_FOR_NEXT_PUBLIC_L1_BASE_URL__ NEXT_PUBLIC_L1_BASE_URL=__PLACEHOLDER_FOR_NEXT_PUBLIC_L1_BASE_URL__
NEXT_PUBLIC_L2_WITHDRAWAL_URL=__PLACEHOLDER_FOR_NEXT_PUBLIC_L2_WITHDRAWAL_URL__
...@@ -52,7 +52,6 @@ jobs: ...@@ -52,7 +52,6 @@ jobs:
name: Run unit tests with Jest name: Run unit tests with Jest
needs: [ lint, type_check ] needs: [ lint, type_check ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ false }} # disable since there are no jest test yet
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v3 uses: actions/checkout@v3
......
...@@ -139,6 +139,7 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -139,6 +139,7 @@ The app instance could be customized by passing following variables to NodeJS en
| --- | --- | --- | --- | | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_L2_NETWORK | `boolean` *(optional)* | Set to true for L2 solutions (Optimism Bedrock based) | false | | NEXT_PUBLIC_IS_L2_NETWORK | `boolean` *(optional)* | Set to true for L2 solutions (Optimism Bedrock based) | false |
| NEXT_PUBLIC_L1_BASE_URL | `string` *(optional)* | Base Blockscout URL for L1 network | `'http://eth-goerli.blockscout.com'` | | NEXT_PUBLIC_L1_BASE_URL | `string` *(optional)* | Base Blockscout URL for L1 network | `'http://eth-goerli.blockscout.com'` |
| NEXT_PUBLIC_L2_WITHDRAWAL_URL | `string` *(optional)* | URL for L2 -> L1 withdrawals | `https://app.optimism.io/bridge/withdraw` |
### Marketplace app configuration properties ### Marketplace app configuration properties
......
...@@ -108,6 +108,7 @@ const config = Object.freeze({ ...@@ -108,6 +108,7 @@ const config = Object.freeze({
L2: { L2: {
isL2Network: getEnvValue(process.env.NEXT_PUBLIC_IS_L2_NETWORK) === 'true', isL2Network: getEnvValue(process.env.NEXT_PUBLIC_IS_L2_NETWORK) === 'true',
L1BaseUrl: getEnvValue(process.env.NEXT_PUBLIC_L1_BASE_URL), L1BaseUrl: getEnvValue(process.env.NEXT_PUBLIC_L1_BASE_URL),
withdrawalUrl: getEnvValue(process.env.NEXT_PUBLIC_L2_WITHDRAWAL_URL) || '',
}, },
statsApi: { statsApi: {
endpoint: getEnvValue(process.env.NEXT_PUBLIC_STATS_API_HOST), endpoint: getEnvValue(process.env.NEXT_PUBLIC_STATS_API_HOST),
......
This diff is collapsed.
...@@ -29,5 +29,5 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-test.aws-k8s.blockscout.com ...@@ -29,5 +29,5 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-test.aws-k8s.blockscout.com
# l2 config # l2 config
NEXT_PUBLIC_IS_L2_NETWORK=true NEXT_PUBLIC_IS_L2_NETWORK=true
NEXT_PUBLIC_L1_BASE_URL=https://blockscout-main.test.aws-k8s.blockscout.com/ NEXT_PUBLIC_L1_BASE_URL=https://blockscout-main.test.aws-k8s.blockscout.com
NEXT_PUBLIC_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw NEXT_PUBLIC_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw
\ No newline at end of file
...@@ -5,7 +5,7 @@ blockscout: ...@@ -5,7 +5,7 @@ blockscout:
app: blockscout app: blockscout
enabled: true enabled: true
image: image:
_default: &image blockscout/blockscout-optimism-l2-advanced:5.1.0-prerelease-7a8c745e _default: &image blockscout/blockscout-optimism-l2-advanced:5.1.0-prerelease-303a0afa
replicas: replicas:
app: 1 app: 1
# init container # init container
...@@ -110,33 +110,37 @@ blockscout: ...@@ -110,33 +110,37 @@ blockscout:
API_V2_ENABLED: API_V2_ENABLED:
_default: 'true' _default: 'true'
FIRST_BLOCK: FIRST_BLOCK:
_default: '4066066' _default: '4667000'
TRACE_FIRST_BLOCK: TRACE_FIRST_BLOCK:
_default: '4066066' _default: '4667000'
LAST_BLOCK: LAST_BLOCK:
_default: '4076066' _default: '4677000'
TRACE_LAST_BLOCK: TRACE_LAST_BLOCK:
_default: '4076066' _default: '4677000'
DISABLE_REALTIME_INDEXER: DISABLE_REALTIME_INDEXER:
_default: 'true' _default: 'false'
INDEXER_OPTIMISM_PORTAL_L1: INDEXER_OPTIMISM_L1_RPC:
_default: http://65.108.226.29:8545
INDEXER_OPTIMISM_L1_PORTAL_CONTRACT:
_default: 0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383 _default: 0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383
INDEXER_OPTIMISM_WITHDRAWALS_START_BLOCK_L1: INDEXER_OPTIMISM_L1_WITHDRAWALS_START_BLOCK:
_default: '8299683' _default: '8299683'
INDEXER_OPTIMISM_WITHDRAWALS_START_BLOCK_L2: INDEXER_OPTIMISM_L2_WITHDRAWALS_START_BLOCK:
_default: '4066066' _default: '4066066'
INDEXER_OPTIMISM_MESSAGE_PASSER_L2: INDEXER_OPTIMISM_L2_MESSAGE_PASSER_CONTRACT:
_default: 0x4200000000000000000000000000000000000016 _default: 0x4200000000000000000000000000000000000016
INDEXER_OPTIMISM_OUTPUT_ROOTS_START_BLOCK_L1: INDEXER_OPTIMISM_L1_OUTPUT_ROOTS_START_BLOCK:
_default: '8299683' _default: '8299683'
INDEXER_OPTIMISM_OUTPUT_ORACLE_L1: INDEXER_OPTIMISM_L1_OUTPUT_ORACLE_CONTRACT:
_default: 0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0 _default: 0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0
INDEXER_OPTIMISM_BATCH_START_BLOCK_L1: INDEXER_OPTIMISM_L1_BATCH_START_BLOCK:
_default: '8381594' _default: '8381594'
INDEXER_OPTIMISM_BATCH_INBOX: INDEXER_OPTIMISM_L1_BATCH_INBOX:
_default: 0xff00000000000000000000000000000000000420 _default: 0xff00000000000000000000000000000000000420
INDEXER_OPTIMISM_BATCH_SUBMITTER: INDEXER_OPTIMISM_L1_BATCH_SUBMITTER:
_default: 0x7431310e026b69bfc676c0013e12a1a11411eec9 _default: 0x7431310e026b69bfc676c0013e12a1a11411eec9
INDEXER_OPTIMISM_L1_DEPOSITS_START_BLOCK:
_default: '8381594'
postgres: postgres:
enabled: true enabled: true
...@@ -310,13 +314,16 @@ frontend: ...@@ -310,13 +314,16 @@ frontend:
- "/search-results" - "/search-results"
- "/token" - "/token"
- "/tokens" - "/tokens"
- "/accounts"
- "/visualize"
- "/api-docs"
- "/csv-export" - "/csv-export"
- "/verified-contracts" - "/verified-contracts"
- "/graphiql" - "/graphiql"
- "/accounts"
- "/visualize"
- "/api-docs"
- "/output-roots"
- "/txn-batches"
- "/withdrawals"
- "/deposits"
resources: resources:
limits: limits:
memory: memory:
...@@ -396,7 +403,7 @@ frontend: ...@@ -396,7 +403,7 @@ frontend:
NEXT_PUBLIC_IS_L2_NETWORK: NEXT_PUBLIC_IS_L2_NETWORK:
_default: "true" _default: "true"
NEXT_PUBLIC_L1_BASE_URL: NEXT_PUBLIC_L1_BASE_URL:
_default: https://blockscout-main.test.aws-k8s.blockscout.com _default: https://eth-goerli.blockscout.com/
NEXT_PUBLIC_L2_WITHDRAWAL_URL: NEXT_PUBLIC_L2_WITHDRAWAL_URL:
_default: https://app.optimism.io/bridge/withdraw _default: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: NEXT_PUBLIC_GRAPHIQL_TRANSACTION:
......
...@@ -175,12 +175,12 @@ postgres: ...@@ -175,12 +175,12 @@ postgres:
resources: resources:
limits: limits:
memory: memory:
_default: "6Gi" _default: "8Gi"
cpu: cpu:
_default: "2" _default: "2"
requests: requests:
memory: memory:
_default: "6Gi" _default: "8Gi"
cpu: cpu:
_default: "2" _default: "2"
# node label # node label
...@@ -303,12 +303,12 @@ frontend: ...@@ -303,12 +303,12 @@ frontend:
memory: memory:
_default: "0.1Gi" _default: "0.1Gi"
cpu: cpu:
_default: "0.2" _default: "0.5"
requests: requests:
memory: memory:
_default: "0.1Gi" _default: "0.1Gi"
cpu: cpu:
_default: "0.2" _default: "0.5"
environment: environment:
# ui config # ui config
NEXT_PUBLIC_FEATURED_NETWORKS: NEXT_PUBLIC_FEATURED_NETWORKS:
......
...@@ -40,6 +40,10 @@ frontend: ...@@ -40,6 +40,10 @@ frontend:
- "/csv-export" - "/csv-export"
- "/verified-contracts" - "/verified-contracts"
- "/graphiql" - "/graphiql"
- "/output-roots"
- "/txn-batches"
- "/withdrawals"
- "/deposits"
resources: resources:
limits: limits:
......
...@@ -44,14 +44,14 @@ frontend: ...@@ -44,14 +44,14 @@ frontend:
resources: resources:
limits: limits:
memory: memory:
_default: "0.1Gi" _default: "2Gi"
cpu: cpu:
_default: "0.1" _default: "2"
requests: requests:
memory: memory:
_default: "0.1Gi" _default: "2Gi"
cpu: cpu:
_default: "0.1" _default: "2"
environment: environment:
NEXT_PUBLIC_BLOCKSCOUT_VERSION: NEXT_PUBLIC_BLOCKSCOUT_VERSION:
_default: v4.1.8-beta _default: v4.1.8-beta
......
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path fill="currentColor" fill-rule="evenodd" d="M14.806 21.313H4.582A.583.583 0 0 1 4 20.731V6.179c0-.322.261-.583.582-.583h2.654V3.27c0-.321.26-.582.582-.582h7.313c.007 0 .013.002.02.004.005.001.01.003.017.003a.558.558 0 0 1 .153.031l.02.006a.575.575 0 0 1 .194.118l4.285 4.11-.002.002a.58.58 0 0 1 .181.419v9.064a1.96 1.96 0 0 1-1.958 1.959h-1.277v.95a1.96 1.96 0 0 1-1.96 1.959ZM18.31 6.798l-2.598-2.506v1.713c0 .437.356.793.793.793h1.805Zm-3.42-3.289H8.058v14.072h10.327a.794.794 0 0 0 .793-.793V7.62h-2.671c-1.08 0-1.616-.535-1.616-1.615V3.509ZM7.236 6.42H4.822v14.07H15.15c.437 0 .793-.7.793-1.136v-.951H7.818a.582.582 0 0 1-.582-.583V6.42Zm9.482 4.532a.39.39 0 1 0 0-.78h-5.12l.502-.504a.39.39 0 0 0-.55-.549l-1.168 1.169a.39.39 0 0 0 .059.599.39.39 0 0 0 .216.065h6.06Zm-.012 1.363h-6.061a.39.39 0 0 0 0 .779h5.12l-.502.504a.39.39 0 1 0 .549.55l1.168-1.17a.39.39 0 0 0-.058-.598.39.39 0 0 0-.216-.065Z" clip-rule="evenodd"/>
<path fill="currentColor" d="M7.236 5.596v.25h.25v-.25h-.25Zm7.915-2.905-.07.24.07-.24Zm.017.003.014-.25-.014.25Zm.153.031.085-.235-.003-.001-.082.236Zm.02.006.072-.239-.072.24Zm.031.012.104-.228h-.001l-.103.228Zm.162.106.173-.18-.173.18Zm4.287 4.111.177.177.18-.18-.184-.177-.173.18Zm-.002.002-.176-.177-.18.18.183.177.173-.18Zm-3.054 11.442v-.25h-.25v.25h.25ZM15.713 4.292l.174-.18-.424-.408v.588h.25Zm2.598 2.506v.25h.62l-.446-.43-.174.18ZM8.058 3.51v-.25h-.25v.25h.25Zm6.833 0h.25v-.25h-.25v.25ZM8.058 17.581h-.25v.25h.25v-.25Zm11.12-9.96h.25v-.25h-.25v.25ZM4.822 6.418v-.25h-.25v.25h.25Zm2.414 0h.25v-.25h-.25v.25ZM4.822 20.49h-.25v.25h.25v-.25Zm11.12-2.087h.25v-.25h-.25v.25Zm1.051-7.567-.177-.177.177.177Zm0-.551-.177.177.177-.177Zm-5.396-.114-.177-.177-.425.427h.602v-.25Zm.503-.505.177.177.007-.007.006-.007-.19-.163Zm.093-.268.25-.01-.25.01Zm-.114-.26.177-.177-.177.177Zm-.26-.114.01-.25-.01.25Zm-.268.093-.163-.19-.007.007-.007.007.177.176Zm-1.169 1.169.177.177-.177-.177Zm-.107.199.245.05-.245-.05Zm.022.225-.232.095.001.001.23-.096Zm.143.175.139-.209-.139.209Zm.217.065v-.25.25Zm6.049 1.363v.25-.25Zm-6.337.114.177.177-.177-.177Zm0 .55.177-.176-.177.177Zm5.396.115.177.176.425-.426h-.602v.25Zm-.502.504.162.19.008-.007.007-.007-.177-.176Zm-.098.126.225.11-.225-.11Zm-.038.155-.25-.01.25.01Zm.026.157.233-.091-.233.09Zm.088.133-.177.177.177-.177Zm.29.114-.01-.25.01.25Zm.154-.039.11.225-.11-.225Zm.127-.097-.177-.177-.007.007-.006.007.19.163Zm1.168-1.168-.176-.178v.001l.176.177Zm.086-.425.23-.095v-.001l-.23.096Zm-.144-.174-.139.208.139-.208Zm-12.34 9.184h10.224v-.5H4.582v.5Zm-.832-.832c0 .46.373.832.832.832v-.5a.333.333 0 0 1-.332-.332h-.5Zm0-14.552V20.73h.5V6.179h-.5Zm.832-.833a.833.833 0 0 0-.832.833h.5c0-.184.149-.333.332-.333v-.5Zm2.654 0H4.582v.5h2.654v-.5Zm-.25-2.077v2.327h.5V3.27h-.5Zm.832-.832a.832.832 0 0 0-.832.832h.5c0-.183.148-.332.332-.332v-.5Zm7.313 0H7.818v.5h7.313v-.5Zm.09.014c-.002 0-.041-.014-.09-.014v.5a.202.202 0 0 1-.042-.004c-.007-.002-.013-.004-.009-.002l.14-.48Zm-.04-.006a.2.2 0 0 1 .034.004l.006.002-.14.48s.034.01.073.013l.027-.5Zm.222.044a.808.808 0 0 0-.22-.044l-.03.499a.307.307 0 0 1 .085.017l.165-.472Zm.01.003a2.077 2.077 0 0 1-.007-.002s-.001 0 0 0l-.17.47.032.01.145-.478Zm.062.022a.443.443 0 0 0-.062-.022l-.145.479a.198.198 0 0 1 .008.002l-.006-.002.205-.457Zm.232.154a.823.823 0 0 0-.231-.153l-.208.455c.041.018.07.038.093.06l.346-.362Zm4.287 4.112-4.287-4.111-.346.36 4.287 4.112.346-.361Zm.002.359.002-.002-.354-.354-.001.002.353.354Zm.254.242a.83.83 0 0 0-.257-.6l-.347.361a.33.33 0 0 1 .104.239h.5Zm0 9.064V7.381h-.5v9.064h.5Zm-2.208 2.209a2.21 2.21 0 0 0 2.208-2.209h-.5a1.71 1.71 0 0 1-1.708 1.709v.5Zm-1.277 0h1.277v-.5h-1.277v.5Zm.25.7v-.95h-.5v.95h.5Zm-2.21 2.209a2.21 2.21 0 0 0 2.21-2.208h-.5a1.71 1.71 0 0 1-1.71 1.708v.5Zm.735-17.09 2.598 2.505.347-.36-2.598-2.506-.348.36Zm.423 1.532V4.292h-.5v1.713h.5Zm.543.543a.544.544 0 0 1-.543-.543h-.5c0 .575.468 1.043 1.043 1.043v-.5Zm1.805 0h-1.805v.5h1.805v-.5ZM8.058 3.76h6.833v-.5H8.058v.5Zm.25 13.822V3.51h-.5v14.072h.5Zm10.077-.25H8.058v.5h10.327v-.5Zm.543-.543c0 .3-.244.543-.543.543v.5c.575 0 1.043-.468 1.043-1.043h-.5Zm0-9.168v9.168h.5V7.62h-.5Zm-2.421.25h2.67v-.5h-2.67v.5ZM14.64 6.005c0 .578.143 1.057.476 1.39.333.332.812.475 1.39.475v-.5c-.502 0-.831-.124-1.037-.33-.205-.205-.33-.533-.33-1.035h-.5Zm0-2.496v2.496h.5V3.509h-.5ZM4.822 6.67h2.414v-.5H4.822v.5Zm.25 13.822V6.419h-.5V20.49h.5Zm10.077-.25H4.822v.5h10.326v-.5Zm.543-.886c0 .162-.07.401-.195.599-.13.21-.264.287-.348.287v.5c.352 0 .616-.272.772-.522.164-.26.27-.59.27-.864h-.5Zm0-.951v.95h.5v-.95h-.5Zm-7.874.25h8.124v-.5H7.818v.5Zm-.832-.833c0 .46.372.833.832.833v-.5a.332.332 0 0 1-.332-.333h-.5Zm0-11.402v11.4h.5V6.42h-.5Zm9.83 4.24a.14.14 0 0 1-.098.042v.5a.64.64 0 0 0 .452-.188l-.354-.353Zm.041-.098a.14.14 0 0 1-.04.099l.353.353a.64.64 0 0 0 .187-.452h-.5Zm-.04-.098a.14.14 0 0 1 .04.098h.5a.64.64 0 0 0-.187-.452l-.354.354Zm-.1-.041a.14.14 0 0 1 .1.04l.353-.353a.64.64 0 0 0-.452-.187v.5Zm-5.12 0h5.12v-.5h-5.12v.5Zm.326-.931-.503.504.355.353.502-.504-.354-.353Zm.02-.082a.14.14 0 0 1-.033.096l.38.325a.64.64 0 0 0 .153-.44l-.5.019Zm-.04-.093a.14.14 0 0 1 .04.093l.5-.02a.64.64 0 0 0-.187-.427l-.353.354Zm-.094-.041a.14.14 0 0 1 .094.04l.353-.353a.64.64 0 0 0-.427-.187l-.02.5Zm-.096.033a.14.14 0 0 1 .096-.033l.02-.5a.64.64 0 0 0-.44.153l.324.38Zm-1.154 1.155 1.168-1.168-.353-.353-1.168 1.168.353.354Zm-.039.072a.139.139 0 0 1 .039-.071l-.353-.355a.64.64 0 0 0-.176.328l.49.098Zm.008.081a.139.139 0 0 1-.008-.08l-.49-.1a.64.64 0 0 0 .036.37l.462-.19Zm.05.061a.14.14 0 0 1-.05-.062l-.462.192c.049.117.13.217.236.287l.277-.417Zm.078.024a.14.14 0 0 1-.077-.024l-.277.417a.64.64 0 0 0 .356.107l-.002-.5Zm6.062 0h-6.061v.5h6.06v-.5Zm-6.073 1.863h6.06v-.5h-6.06v.5Zm-.099.04a.14.14 0 0 1 .099-.04v-.5a.64.64 0 0 0-.452.187l.353.354Zm-.04.1a.14.14 0 0 1 .04-.1l-.353-.353a.639.639 0 0 0-.188.452h.5Zm.04.098a.14.14 0 0 1-.04-.099h-.5c0 .17.067.333.187.452l.353-.353Zm.099.04a.14.14 0 0 1-.099-.04l-.353.353a.64.64 0 0 0 .452.188v-.5Zm5.12 0h-5.12v.5h5.12v-.5Zm-.325.931.502-.504-.354-.353-.502.505.354.352Zm-.05.06a.138.138 0 0 1 .035-.046l-.325-.38a.639.639 0 0 0-.16.207l.45.218Zm-.014.054a.14.14 0 0 1 .014-.055l-.45-.218a.64.64 0 0 0-.063.254l.5.02Zm.01.057a.139.139 0 0 1-.01-.057l-.5-.019a.64.64 0 0 0 .045.258l.465-.182Zm.031.047a.139.139 0 0 1-.03-.047l-.466.182a.64.64 0 0 0 .143.219l.353-.354Zm.048.032a.139.139 0 0 1-.048-.032l-.353.354a.64.64 0 0 0 .219.143l.182-.465Zm.056.01a.139.139 0 0 1-.056-.01l-.182.465a.64.64 0 0 0 .258.044l-.02-.5Zm.056-.015a.138.138 0 0 1-.056.014l.02.5a.64.64 0 0 0 .253-.064l-.217-.45Zm.045-.035a.138.138 0 0 1-.045.035l.217.45a.64.64 0 0 0 .208-.16l-.38-.325Zm1.181-1.182-1.168 1.168.354.354 1.168-1.168-.354-.354Zm.04-.072a.139.139 0 0 1-.04.071l.353.355a.64.64 0 0 0 .176-.327l-.49-.099Zm-.009-.08a.14.14 0 0 1 .008.08l.49.099a.64.64 0 0 0-.035-.37l-.463.19Zm-.05-.062a.14.14 0 0 1 .051.063l.461-.193a.64.64 0 0 0-.236-.286l-.276.416Zm-.078-.023a.14.14 0 0 1 .078.023l.276-.416a.639.639 0 0 0-.355-.107l.001.5Z"/>
</svg>
...@@ -17,10 +17,12 @@ import type { AddressesResponse } from 'types/api/addresses'; ...@@ -17,10 +17,12 @@ import type { AddressesResponse } from 'types/api/addresses';
import type { BlocksResponse, BlockTransactionsResponse, Block, BlockFilters } from 'types/api/block'; import type { BlocksResponse, BlockTransactionsResponse, Block, BlockFilters } from 'types/api/block';
import type { ChartMarketResponse, ChartTransactionResponse } from 'types/api/charts'; import type { ChartMarketResponse, ChartTransactionResponse } from 'types/api/charts';
import type { SmartContract, SmartContractReadMethod, SmartContractWriteMethod, SmartContractVerificationConfig } from 'types/api/contract'; import type { SmartContract, SmartContractReadMethod, SmartContractWriteMethod, SmartContractVerificationConfig } from 'types/api/contract';
import type { VerifiedContractsResponse, VerifiedContractsFilters } from 'types/api/contracts'; import type { VerifiedContractsResponse, VerifiedContractsFilters, VerifiedContractsCounters } from 'types/api/contracts';
import type { DepositsResponse } from 'types/api/deposits';
import type { IndexingStatus } from 'types/api/indexingStatus'; import type { IndexingStatus } from 'types/api/indexingStatus';
import type { InternalTransactionsResponse } from 'types/api/internalTransaction'; import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
import type { LogsResponseTx, LogsResponseAddress } from 'types/api/log'; import type { LogsResponseTx, LogsResponseAddress } from 'types/api/log';
import type { OutputRootsResponse } from 'types/api/outputRoots';
import type { RawTracesResponse } from 'types/api/rawTrace'; import type { RawTracesResponse } from 'types/api/rawTrace';
import type { SearchResult, SearchResultFilters } from 'types/api/search'; import type { SearchResult, SearchResultFilters } from 'types/api/search';
import type { Counters, StatsCharts, StatsChart, HomeStats } from 'types/api/stats'; import type { Counters, StatsCharts, StatsChart, HomeStats } from 'types/api/stats';
...@@ -35,9 +37,11 @@ import type { ...@@ -35,9 +37,11 @@ import type {
import type { TokensResponse, TokensFilters, TokenInstanceTransferResponse } from 'types/api/tokens'; import type { TokensResponse, TokensFilters, TokenInstanceTransferResponse } from 'types/api/tokens';
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer'; import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction } from 'types/api/transaction'; import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction } from 'types/api/transaction';
import type { TxnBatchesResponse } from 'types/api/txnBatches';
import type { TTxsFilters } from 'types/api/txsFilters'; import type { TTxsFilters } from 'types/api/txsFilters';
import type { TxStateChanges } from 'types/api/txStateChanges'; import type { TxStateChanges } from 'types/api/txStateChanges';
import type { VisualizedContract } from 'types/api/visualization'; import type { VisualizedContract } from 'types/api/visualization';
import type { WithdrawalsResponse } from 'types/api/withdrawals';
import type ArrayElement from 'types/utils/ArrayElement'; import type ArrayElement from 'types/utils/ArrayElement';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
...@@ -269,6 +273,9 @@ export const RESOURCES = { ...@@ -269,6 +273,9 @@ export const RESOURCES = {
paginationFields: [ 'items_count' as const, 'smart_contract_id' as const ], paginationFields: [ 'items_count' as const, 'smart_contract_id' as const ],
filterFields: [ 'q' as const, 'filter' as const ], filterFields: [ 'q' as const, 'filter' as const ],
}, },
verified_contracts_counters: {
path: '/api/v2/smart-contracts/counters',
},
// TOKEN // TOKEN
token: { token: {
...@@ -364,6 +371,47 @@ export const RESOURCES = { ...@@ -364,6 +371,47 @@ export const RESOURCES = {
path: '/graphql', path: '/graphql',
}, },
// L2
deposits: {
path: '/api/v2/optimism/deposits',
paginationFields: [ 'nonce' as const, 'items_count' as const ],
filterFields: [],
},
deposits_count: {
path: '/api/v2/optimism/deposits/count',
},
withdrawals: {
path: '/api/v2/optimism/withdrawals',
paginationFields: [ 'nonce' as const, 'items_count' as const ],
filterFields: [],
},
withdrawals_count: {
path: '/api/v2/optimism/withdrawals/count',
},
output_roots: {
path: '/api/v2/optimism/output-roots',
paginationFields: [ 'index' as const, 'items_count' as const ],
filterFields: [],
},
output_roots_count: {
path: '/api/v2/optimism/output-roots/count',
},
txn_batches: {
path: '/api/v2/optimism/txn-batches',
paginationFields: [ 'block_number' as const, 'items_count' as const ],
filterFields: [],
},
txn_batches_count: {
path: '/api/v2/optimism/txn-batches/count',
},
// DEPRECATED // DEPRECATED
old_api: { old_api: {
path: '/api', path: '/api',
...@@ -422,7 +470,8 @@ export type PaginatedResources = 'blocks' | 'block_txs' | ...@@ -422,7 +470,8 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
'address_logs' | 'address_tokens' | 'address_logs' | 'address_tokens' |
'token_transfers' | 'token_holders' | 'token_inventory' | 'tokens' | 'token_transfers' | 'token_holders' | 'token_inventory' | 'tokens' |
'token_instance_transfers' | 'token_instance_transfers' |
'verified_contracts'; 'verified_contracts' |
'output_roots' | 'withdrawals' | 'txn_batches' | 'deposits';
export type PaginatedResponse<Q extends PaginatedResources> = ResourcePayload<Q>; export type PaginatedResponse<Q extends PaginatedResources> = ResourcePayload<Q>;
...@@ -482,8 +531,17 @@ Q extends 'contract_methods_read_proxy' ? Array<SmartContractReadMethod> : ...@@ -482,8 +531,17 @@ Q extends 'contract_methods_read_proxy' ? Array<SmartContractReadMethod> :
Q extends 'contract_methods_write' ? Array<SmartContractWriteMethod> : Q extends 'contract_methods_write' ? Array<SmartContractWriteMethod> :
Q extends 'contract_methods_write_proxy' ? Array<SmartContractWriteMethod> : Q extends 'contract_methods_write_proxy' ? Array<SmartContractWriteMethod> :
Q extends 'verified_contracts' ? VerifiedContractsResponse : Q extends 'verified_contracts' ? VerifiedContractsResponse :
Q extends 'verified_contracts_counters' ? VerifiedContractsCounters :
Q extends 'visualize_sol2uml' ? VisualizedContract : Q extends 'visualize_sol2uml' ? VisualizedContract :
Q extends 'contract_verification_config' ? SmartContractVerificationConfig : Q extends 'contract_verification_config' ? SmartContractVerificationConfig :
Q extends 'output_roots' ? OutputRootsResponse :
Q extends 'withdrawals' ? WithdrawalsResponse :
Q extends 'deposits' ? DepositsResponse :
Q extends 'txn_batches' ? TxnBatchesResponse :
Q extends 'output_roots_count' ? number :
Q extends 'withdrawals_count' ? number :
Q extends 'deposits_count' ? number :
Q extends 'txn_batches_count' ? number :
never; never;
/* eslint-enable @typescript-eslint/indent */ /* eslint-enable @typescript-eslint/indent */
......
...@@ -8,6 +8,7 @@ function generateCspPolicy() { ...@@ -8,6 +8,7 @@ function generateCspPolicy() {
descriptors.googleAnalytics(), descriptors.googleAnalytics(),
descriptors.googleFonts(), descriptors.googleFonts(),
descriptors.googleReCaptcha(), descriptors.googleReCaptcha(),
descriptors.monaco(),
descriptors.sentry(), descriptors.sentry(),
descriptors.walletConnect(), descriptors.walletConnect(),
); );
......
...@@ -3,5 +3,6 @@ export { app } from './app'; ...@@ -3,5 +3,6 @@ export { app } from './app';
export { googleAnalytics } from './googleAnalytics'; export { googleAnalytics } from './googleAnalytics';
export { googleFonts } from './googleFonts'; export { googleFonts } from './googleFonts';
export { googleReCaptcha } from './googleReCaptcha'; export { googleReCaptcha } from './googleReCaptcha';
export { monaco } from './monaco';
export { sentry } from './sentry'; export { sentry } from './sentry';
export { walletConnect } from './walletConnect'; export { walletConnect } from './walletConnect';
import type CspDev from 'csp-dev';
import { KEY_WORDS } from '../utils';
export function monaco(): CspDev.DirectiveDescriptor {
return {
'script-src': [
KEY_WORDS.BLOB,
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/loader.js',
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/editor/editor.main.js',
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/editor/editor.main.nls.js',
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/basic-languages/solidity/solidity.js',
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/base/worker/workerMain.js',
],
'style-src': [
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/editor/editor.main.css',
],
'font-src': [
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/base/browser/ui/codicons/codicon/codicon.ttf',
],
};
}
...@@ -6,12 +6,13 @@ import appConfig from 'configs/app/config'; ...@@ -6,12 +6,13 @@ import appConfig from 'configs/app/config';
import abiIcon from 'icons/ABI.svg'; import abiIcon from 'icons/ABI.svg';
import apiKeysIcon from 'icons/API.svg'; import apiKeysIcon from 'icons/API.svg';
import appsIcon from 'icons/apps.svg'; import appsIcon from 'icons/apps.svg';
// import withdrawalsIcon from 'icons/arrows/north-east.svg'; import withdrawalsIcon from 'icons/arrows/north-east.svg';
import depositsIcon from 'icons/arrows/south-east.svg';
import blocksIcon from 'icons/block.svg'; import blocksIcon from 'icons/block.svg';
import gearIcon from 'icons/gear.svg'; import gearIcon from 'icons/gear.svg';
import globeIcon from 'icons/globe-b.svg'; import globeIcon from 'icons/globe-b.svg';
import graphQLIcon from 'icons/graphQL.svg'; import graphQLIcon from 'icons/graphQL.svg';
// import outputRootsIcon from 'icons/output_roots.svg'; import outputRootsIcon from 'icons/output_roots.svg';
import privateTagIcon from 'icons/privattags.svg'; import privateTagIcon from 'icons/privattags.svg';
import profileIcon from 'icons/profile.svg'; import profileIcon from 'icons/profile.svg';
import publicTagIcon from 'icons/publictags.svg'; import publicTagIcon from 'icons/publictags.svg';
...@@ -21,11 +22,10 @@ import statsIcon from 'icons/stats.svg'; ...@@ -21,11 +22,10 @@ import statsIcon from 'icons/stats.svg';
import tokensIcon from 'icons/token.svg'; import tokensIcon from 'icons/token.svg';
import topAccountsIcon from 'icons/top-accounts.svg'; import topAccountsIcon from 'icons/top-accounts.svg';
import transactionsIcon from 'icons/transactions.svg'; import transactionsIcon from 'icons/transactions.svg';
// import depositsIcon from 'icons/arrows/south-east.svg'; import txnBatchIcon from 'icons/txn_batches.svg';
// import txnBatchIcon from 'icons/txn_batches.svg';
import verifiedIcon from 'icons/verified.svg'; import verifiedIcon from 'icons/verified.svg';
import watchlistIcon from 'icons/watchlist.svg'; import watchlistIcon from 'icons/watchlist.svg';
// import { rightLineArrow } from 'lib/html-entities'; import { rightLineArrow } from 'lib/html-entities';
type NavItemCommon = { type NavItemCommon = {
text: string; text: string;
...@@ -83,14 +83,15 @@ export default function useNavItems(): ReturnType { ...@@ -83,14 +83,15 @@ export default function useNavItems(): ReturnType {
const blocks = { const blocks = {
text: 'Blocks', text: 'Blocks',
nextRoute: { pathname: '/blocks' as const }, nextRoute: { pathname: '/blocks' as const },
icon: blocksIcon, isActive: pathname.startsWith('/block'), icon: blocksIcon,
isActive: pathname === '/blocks' || pathname === '/block/[height]',
isNewUi: true, isNewUi: true,
}; };
const txs = { const txs = {
text: 'Transactions', text: 'Transactions',
nextRoute: { pathname: '/txs' as const }, nextRoute: { pathname: '/txs' as const },
icon: transactionsIcon, icon: transactionsIcon,
isActive: pathname.startsWith('/tx'), isActive: pathname === '/txs' || pathname === '/tx/[hash]',
isNewUi: true, isNewUi: true,
}; };
const verifiedContracts = const verifiedContracts =
...@@ -102,16 +103,16 @@ export default function useNavItems(): ReturnType { ...@@ -102,16 +103,16 @@ export default function useNavItems(): ReturnType {
[ [
txs, txs,
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
// { text: `Deposits (L1${ rightLineArrow }L2)`, nextRoute: { pathname: '/deposits' as const }, icon: depositsIcon, isActive: pathname === '/deposits', isNewUi: true }, { text: `Deposits (L1${ rightLineArrow }L2)`, nextRoute: { pathname: '/deposits' as const }, icon: depositsIcon, isActive: pathname === '/deposits', isNewUi: true },
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
// { text: `Withdrawals (L2${ rightLineArrow }L1)`, nextRoute: { pathname: '/withdrawals' as const }, icon: withdrawalsIcon, isActive: pathname === '/withdrawals', isNewUi: true }, { text: `Withdrawals (L2${ rightLineArrow }L1)`, nextRoute: { pathname: '/withdrawals' as const }, icon: withdrawalsIcon, isActive: pathname === '/withdrawals', isNewUi: true },
], ],
[ [
blocks, blocks,
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
// { text: 'Txn batches', nextRoute: { pathname: '/txn-batches' as const }, icon: txnBatchIcon, isActive: pathname === '/txn-batches', isNewUi: true }, { text: 'Txn batches', nextRoute: { pathname: '/txn-batches' as const }, icon: txnBatchIcon, isActive: pathname === '/txn-batches', isNewUi: true },
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
// { text: 'Output roots', nextRoute: { pathname: '/output-roots' as const }, icon: outputRootsIcon, isActive: pathname === '/output-roots', isNewUi: true }, { text: 'Output roots', nextRoute: { pathname: '/output-roots' as const }, icon: outputRootsIcon, isActive: pathname === '/output-roots', isNewUi: true },
], ],
[ [
topAccounts, topAccounts,
......
...@@ -19,3 +19,6 @@ export const minus = String.fromCharCode(8722); // − ...@@ -19,3 +19,6 @@ export const minus = String.fromCharCode(8722); // −
export const leftLineArrow = String.fromCharCode(8592); // ← export const leftLineArrow = String.fromCharCode(8592); // ←
export const rightLineArrow = String.fromCharCode(8594); // → export const rightLineArrow = String.fromCharCode(8594); // →
export const apos = String.fromCharCode(39); // apostrophe ' export const apos = String.fromCharCode(39); // apostrophe '
export const shift = String.fromCharCode(8679); // upwards white arrow ⇧
export const cmd = String.fromCharCode(8984); // place of interest sign ⌘
export const alt = String.fromCharCode(9095); // alternate key symbol ⎇
export default function isMetaKey(event: React.KeyboardEvent) {
return event.metaKey || event.getModifierState('Meta') || event.getModifierState('OS');
}
const stripLeadingSlash = (str: string) => str[0] === '/' ? str.slice(1) : str;
export default stripLeadingSlash;
import type { VerifiedContractsCounters } from 'types/api/contracts';
export const verifiedContractsCountersMock: VerifiedContractsCounters = {
smart_contracts: '123456789',
new_smart_contracts_24h: '12345',
verified_smart_contracts: '654321',
new_verified_smart_contracts_24h: '0',
};
export const data = {
items: [
{
l1_block_number: 8382841,
l1_block_timestamp: '2022-05-27T01:13:48.000000Z',
l1_tx_hash: '0xaf3e5f4ef03eac22a622b3434c5dc9f4465aa291900a86bcf0ad9fb14429f05e',
l1_tx_origin: '0x6197d1eef304eb5284a0f6720f79403b4e9bf3a5',
l2_tx_gas_limit: '2156928',
l2_tx_hash: '0xb9212c76069b926917816767e4c5a0ef80e519b1ac1c3d3fb5818078f4984667',
},
{
l1_block_number: 8382841,
l1_block_timestamp: '2022-05-27T01:13:48.000000Z',
l1_tx_hash: '0xa280f18cc72f9ad904087eb262c236048e935ad184a85bbd042d544c172c10bf',
l1_tx_origin: '0x6197d1eef304eb5284a0f6720f79403b4e9bf3a5',
l2_tx_gas_limit: '1216064',
l2_tx_hash: '0xaaaeb47a78b5c42d870f8d831a683a7cefe1b031a992170b28b43b82bd08318c',
},
{
l1_block_number: 8382834,
l1_block_timestamp: '2022-06-27T01:11:48.000000Z',
l1_tx_hash: '0xfca8cc5440bffa8b975873c02bba3ff3380dd75fbc3260d10179e282cf72d6d4',
l1_tx_origin: '0x6197d1eef304eb5284a0f6720f79403b4e9bf3a5',
l2_tx_gas_limit: '405824',
l2_tx_hash: '0xa0604ebf2614ad708aeefa83f766fb25928dadb5ffb2f45028f5b4f1fa4d9358',
},
],
next_page_params: {
items_count: 50,
l1_block_number: 8382363,
tx_hash: '0x2012f0ce966ce6573e7826e9235f227edf5a2f8382b8d646c979f85a77e15c05',
},
};
export const outputRootsData = {
items: [
{
l1_block_number: 8456113,
l1_timestamp: '2022-02-08T12:08:48.000000Z',
l1_tx_hash: '0x19455a53758d5de89070164ff09c40d93f1b4447e721090f03aa150f6159265a',
l2_block_number: 5214988,
l2_output_index: 9926,
output_root: '0xa7de9bd3986ce5ca8de9f0ab6c7473f4cebe225fb13b57cc5c8472de84a8bab3',
},
{
l1_block_number: 8456099,
l1_timestamp: '2022-02-08T12:05:24.000000Z',
l1_tx_hash: '0x6aa081e8e33a085e4ec7124fcd8a5f7d36aac0828f176e80d4b70e313a11695b',
l2_block_number: 5214868,
l2_output_index: 9925,
output_root: '0x4ec2822d2f7b4f834d693d88f8a4cf15899882915980a21756d29cfd9f9f3898',
},
{
l1_block_number: 8456078,
l1_timestamp: '2022-02-08T12:00:48.000000Z',
l1_tx_hash: '0x4238988b0959e41a7b09cef67f58698e05e3bcc29b8d2f60e6c77dc68c91f16e',
l2_block_number: 5214748,
l2_output_index: 9924,
output_root: '0x78b2e13c20f4bbfb4a008127edaaf25aa476f933669edd4856305bf4ab64a92b',
},
],
next_page_params: {
index: 9877,
items_count: 50,
},
};
export const txnBatchesData = {
items: [
{
epoch_number: 8547349,
l1_tx_hashes: [
'0x5bc94d02b65743dfaa9e10a2d6e175aff2a05cce2128c8eaf848bd84ab9325c5',
'0x92a51bc623111dbb91f243e3452e60fab6f090710357f9d9b75ac8a0f67dfd9d',
],
l1_timestamp: '2023-02-24T10:16:12.000000Z',
l2_block_number: 5902836,
tx_count: 0,
},
{
epoch_number: 8547348,
l1_tx_hashes: [
'0xc45f846ee28ce9ba116ce2d378d3dd00b55d324b833b3ecd4241c919c572c4aa',
],
l1_timestamp: '2023-02-24T10:16:00.000000Z',
l2_block_number: 5902835,
tx_count: 0,
},
{
epoch_number: 8547348,
l1_tx_hashes: [
'0x48139721f792d3a68c3781b4cf50e66e8fc7dbb38adff778e09066ea5be9adb8',
],
l1_timestamp: '2023-02-24T10:16:00.000000Z',
l2_block_number: 5902834,
tx_count: 0,
},
],
next_page_params: {
block_number: 5902834,
items_count: 50,
},
};
export const data = {
items: [
{
challenge_period_end: null,
from: {
hash: '0x67aab90c548b284be30b05c376001b4db90b87ba',
implementation_name: null,
is_contract: false,
is_verified: false,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
},
l1_tx_hash: '0x1a235bee32ac10cb7efdad98415737484ca66386e491cde9e17d42b136dca684',
l2_timestamp: '2022-02-15T12:50:02.000000Z',
l2_tx_hash: '0x918cd8c5c24c17e06cd02b0379510c4ad56324bf153578fb9caaaa2fe4e7dc35',
msg_nonce: 396,
msg_nonce_raw: '1766847064778384329583297500742918515827483896875618958121606201292620172',
msg_nonce_version: 1,
status: 'Ready to prove',
},
{
challenge_period_end: null,
from: null,
l1_tx_hash: null,
l2_timestamp: null,
l2_tx_hash: '0x2f117bee32ac10cb7efdad98415737484ca66386e491cde9e17d42b136def593',
msg_nonce: 391,
msg_nonce_raw: '1766847064778384329583297500742918515827483896875618958121606201292620167',
msg_nonce_version: 1,
status: 'Ready to prove',
},
{
challenge_period_end: '2022-11-11T12:50:02.000000Z',
from: null,
l1_tx_hash: null,
l2_timestamp: null,
l2_tx_hash: '0xe14b1f46838176702244a5343629bcecf728ca2d9881d47b4ce46e00c387d7e3',
msg_nonce: 390,
msg_nonce_raw: '1766847064778384329583297500742918515827483896875618958121606201292620166',
msg_nonce_version: 1,
status: 'Ready for relay',
},
],
next_page_params: {
items_count: 50,
nonce: '1766847064778384329583297500742918515827483896875618958121606201292620123',
},
};
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
"@emotion/react": "^11.10.4", "@emotion/react": "^11.10.4",
"@emotion/styled": "^11.10.4", "@emotion/styled": "^11.10.4",
"@metamask/providers": "^10.2.1", "@metamask/providers": "^10.2.1",
"@monaco-editor/react": "^4.4.6",
"@sentry/nextjs": "^7.12.1", "@sentry/nextjs": "^7.12.1",
"@sentry/react": "^7.24.0", "@sentry/react": "^7.24.0",
"@sentry/tracing": "^7.24.0", "@sentry/tracing": "^7.24.0",
...@@ -43,7 +44,6 @@ ...@@ -43,7 +44,6 @@
"@types/react-scroll": "^1.8.4", "@types/react-scroll": "^1.8.4",
"@web3modal/ethereum": "^2.0.0-rc.2", "@web3modal/ethereum": "^2.0.0-rc.2",
"@web3modal/react": "^2.0.0-rc.2", "@web3modal/react": "^2.0.0-rc.2",
"ace-builds": "^1.14.0",
"bignumber.js": "^9.1.0", "bignumber.js": "^9.1.0",
"chakra-react-select": "^4.4.3", "chakra-react-select": "^4.4.3",
"d3": "^7.6.1", "d3": "^7.6.1",
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
"graphql-ws": "^5.11.3", "graphql-ws": "^5.11.3",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"lodash": "^4.0.0", "lodash": "^4.0.0",
"monaco-editor": "^0.34.1",
"next": "12.2.5", "next": "12.2.5",
"nextjs-routes": "^1.0.8", "nextjs-routes": "^1.0.8",
"node-fetch": "^3.2.9", "node-fetch": "^3.2.9",
...@@ -66,7 +67,6 @@ ...@@ -66,7 +67,6 @@
"pino-pretty": "^9.1.1", "pino-pretty": "^9.1.1",
"qrcode": "^1.5.1", "qrcode": "^1.5.1",
"react": "18.2.0", "react": "18.2.0",
"react-ace": "^10.1.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-google-recaptcha": "^2.1.0", "react-google-recaptcha": "^2.1.0",
"react-hook-form": "^7.33.1", "react-hook-form": "^7.33.1",
...@@ -96,6 +96,7 @@ ...@@ -96,6 +96,7 @@
"@types/swagger-ui-react": "^4.11.0", "@types/swagger-ui-react": "^4.11.0",
"@types/ws": "^8.5.3", "@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.53.0", "@typescript-eslint/eslint-plugin": "^5.53.0",
"css-loader": "^6.7.3",
"dotenv-cli": "^6.0.0", "dotenv-cli": "^6.0.0",
"eslint": "^8.32.0", "eslint": "^8.32.0",
"eslint-config-next": "^12.3.0", "eslint-config-next": "^12.3.0",
...@@ -112,6 +113,7 @@ ...@@ -112,6 +113,7 @@
"mockdate": "^3.0.5", "mockdate": "^3.0.5",
"next-transpile-modules": "^10.0.0", "next-transpile-modules": "^10.0.0",
"playwright": "1.31.0", "playwright": "1.31.0",
"style-loader": "^3.3.1",
"svgo": "^2.8.0", "svgo": "^2.8.0",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
......
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import Deposits from 'ui/pages/Deposits';
const DepositsPage: NextPage = () => {
const title = getNetworkTitle();
return (
<>
<Head>
<title>{ title }</title>
</Head>
<Deposits/>
</>
);
};
export default DepositsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsL2';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import OutputRoots from 'ui/pages/OutputRoots';
const OutputRootsPage: NextPage = () => {
const title = getNetworkTitle();
return (
<>
<Head>
<title>{ title }</title>
</Head>
<OutputRoots/>
</>
);
};
export default OutputRootsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsL2';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import TxnBatches from 'ui/pages/TxnBatches';
const TxnBatchesPage: NextPage = () => {
const title = getNetworkTitle();
return (
<>
<Head>
<title>{ title }</title>
</Head>
<TxnBatches/>
</>
);
};
export default TxnBatchesPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsL2';
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import Withdrawals from 'ui/pages/Withdrawals';
const WithdrawalsPage: NextPage = () => {
const title = getNetworkTitle();
return (
<>
<Head>
<title>{ title }</title>
</Head>
<Withdrawals/>
</>
);
};
export default WithdrawalsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsL2';
...@@ -96,6 +96,7 @@ const baseStyle = definePartsStyle({ ...@@ -96,6 +96,7 @@ const baseStyle = definePartsStyle({
borderTopRightRadius: 'base', borderTopRightRadius: 'base',
overflow: 'unset', overflow: 'unset',
fontVariant: 'normal', fontVariant: 'normal',
fontVariantLigatures: 'no-contextual',
}, },
}); });
......
...@@ -8,6 +8,7 @@ const global = (props: StyleFunctionProps) => ({ ...@@ -8,6 +8,7 @@ const global = (props: StyleFunctionProps) => ({
bg: mode('white', 'black')(props), bg: mode('white', 'black')(props),
...getDefaultTransitionProps(), ...getDefaultTransitionProps(),
'-webkit-tap-highlight-color': 'transparent', '-webkit-tap-highlight-color': 'transparent',
'font-variant-ligatures': 'no-contextual',
}, },
mark: { mark: {
bgColor: 'yellow.200', bgColor: 'yellow.200',
......
...@@ -24,3 +24,10 @@ export interface VerifiedContractsFilters { ...@@ -24,3 +24,10 @@ export interface VerifiedContractsFilters {
q: string | undefined; q: string | undefined;
filter: 'vyper' | 'solidity' | undefined; filter: 'vyper' | 'solidity' | undefined;
} }
export type VerifiedContractsCounters = {
new_smart_contracts_24h: string;
new_verified_smart_contracts_24h: string;
smart_contracts: string;
verified_smart_contracts: string;
}
export type DepositsItem = {
l1_block_number: number;
l1_tx_hash: string;
l1_block_timestamp: string;
l1_tx_origin: string;
l2_tx_gas_limit: string;
l2_tx_hash: string;
}
export type DepositsResponse = {
items: Array<DepositsItem>;
next_page_params: {
items_count: number;
l1_block_number: number;
tx_hash: string;
};
total: number;
}
export type OutputRootsItem = {
l1_block_number: number;
l1_timestamp: string;
l1_tx_hash: string;
l2_block_number: number;
l2_output_index: number;
output_root: string;
}
export type OutputRootsResponse = {
items: Array<OutputRootsItem>;
total: number;
next_page_params: {
index: number;
items_count: number;
};
}
export type TxnBatchesItem = {
epoch_number: number;
l1_tx_hashes: Array<string>;
l1_timestamp: string;
l2_block_number: number;
tx_count: number;
}
export type TxnBatchesResponse = {
items: Array<TxnBatchesItem>;
total: number;
next_page_params: {
index: number;
items_count: number;
};
}
import type { AddressParam } from './addressParams';
export type WithdrawalsItem = {
'challenge_period_end': string | null;
'from': AddressParam | null;
'l1_tx_hash': string | null;
'l2_timestamp': string | null;
'l2_tx_hash': string;
'msg_nonce': number;
'msg_nonce_version': number;
'status': string;
}
export type WithdrawalStatus =
'In challenge period' |
'Ready for relay' |
'Relayed' |
'Waiting for state root' |
'Ready to prove';
export type WithdrawalsResponse = {
items: Array<WithdrawalsItem>;
'next_page_params': {
'items_count': number;
'nonce': string;
};
total: number;
}
...@@ -25,19 +25,23 @@ declare module "nextjs-routes" { ...@@ -25,19 +25,23 @@ declare module "nextjs-routes" {
| DynamicRoute<"/block/[height]", { "height": string }> | DynamicRoute<"/block/[height]", { "height": string }>
| StaticRoute<"/blocks"> | StaticRoute<"/blocks">
| StaticRoute<"/csv-export"> | StaticRoute<"/csv-export">
| StaticRoute<"/deposits">
| StaticRoute<"/graph"> | StaticRoute<"/graph">
| StaticRoute<"/graphiql"> | StaticRoute<"/graphiql">
| StaticRoute<"/"> | StaticRoute<"/">
| StaticRoute<"/login"> | StaticRoute<"/login">
| StaticRoute<"/output-roots">
| StaticRoute<"/search-results"> | StaticRoute<"/search-results">
| StaticRoute<"/stats"> | StaticRoute<"/stats">
| DynamicRoute<"/token/[hash]", { "hash": string }> | DynamicRoute<"/token/[hash]", { "hash": string }>
| DynamicRoute<"/token/[hash]/instance/[id]", { "hash": string; "id": string }> | DynamicRoute<"/token/[hash]/instance/[id]", { "hash": string; "id": string }>
| StaticRoute<"/tokens"> | StaticRoute<"/tokens">
| DynamicRoute<"/tx/[hash]", { "hash": string }> | DynamicRoute<"/tx/[hash]", { "hash": string }>
| StaticRoute<"/txn-batches">
| StaticRoute<"/txs"> | StaticRoute<"/txs">
| StaticRoute<"/verified-contracts"> | StaticRoute<"/verified-contracts">
| StaticRoute<"/visualize/sol2uml">; | StaticRoute<"/visualize/sol2uml">
| StaticRoute<"/withdrawals">;
interface StaticRoute<Pathname> { interface StaticRoute<Pathname> {
pathname: Pathname; pathname: Pathname;
......
import { Box, Hide, Show, Table, Tbody, Th, Tr } from '@chakra-ui/react'; import { Hide, Show, Table, Tbody, Th, Tr } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -12,10 +12,8 @@ import useQueryWithPages from 'lib/hooks/useQueryWithPages'; ...@@ -12,10 +12,8 @@ import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
import SkeletonList from 'ui/shared/skeletons/SkeletonList';
import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
import SocketAlert from 'ui/shared/SocketAlert'; import SocketAlert from 'ui/shared/SocketAlert';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
...@@ -71,67 +69,51 @@ const AddressBlocksValidated = ({ scrollRef }: Props) => { ...@@ -71,67 +69,51 @@ const AddressBlocksValidated = ({ scrollRef }: Props) => {
handler: handleNewSocketMessage, handler: handleNewSocketMessage,
}); });
const content = (() => { const content = query.data?.items ? (
if (query.isLoading) { <>
return ( { socketAlert && <SocketAlert mb={ 6 }/> }
<> <Hide below="lg" ssr={ false }>
<Hide below="lg" ssr={ false }> <Table variant="simple" size="sm">
<SkeletonTable columns={ [ '17%', '17%', '16%', '25%', '25%' ] } isLong/> <Thead top={ query.isPaginationVisible ? 80 : 0 }>
</Hide> <Tr>
<Show below="lg" ssr={ false }> <Th width="17%">Block</Th>
<SkeletonList/> <Th width="17%">Age</Th>
</Show> <Th width="16%">Txn</Th>
</> <Th width="25%">GasUsed</Th>
); <Th width="25%" isNumeric>Reward { appConfig.network.currency.symbol }</Th>
} </Tr>
</Thead>
if (query.isError) { <Tbody>
return <DataFetchAlert/>; { query.data.items.map((item) => (
} <AddressBlocksValidatedTableItem key={ item.height } { ...item } page={ query.pagination.page }/>
)) }
if (query.data.items.length === 0) { </Tbody>
return 'There is no validated blocks for this address'; </Table>
} </Hide>
<Show below="lg" ssr={ false }>
return ( { query.data.items.map((item) => (
<> <AddressBlocksValidatedListItem key={ item.height } { ...item } page={ query.pagination.page }/>
<Hide below="lg" ssr={ false }> )) }
<Table variant="simple" size="sm"> </Show>
<Thead top={ query.isPaginationVisible ? 80 : 0 }> </>
<Tr> ) : null;
<Th width="17%">Block</Th>
<Th width="17%">Age</Th> const actionBar = query.isPaginationVisible ? (
<Th width="16%">Txn</Th> <ActionBar mt={ -6 } showShadow={ query.isLoading }>
<Th width="25%">GasUsed</Th> <Pagination ml="auto" { ...query.pagination }/>
<Th width="25%" isNumeric>Reward { appConfig.network.currency.symbol }</Th> </ActionBar>
</Tr> ) : null;
</Thead>
<Tbody>
{ query.data.items.map((item) => (
<AddressBlocksValidatedTableItem key={ item.height } { ...item } page={ query.pagination.page }/>
)) }
</Tbody>
</Table>
</Hide>
<Show below="lg" ssr={ false }>
{ query.data.items.map((item) => (
<AddressBlocksValidatedListItem key={ item.height } { ...item } page={ query.pagination.page }/>
)) }
</Show>
</>
);
})();
return ( return (
<Box> <DataListDisplay
{ query.isPaginationVisible && ( isError={ query.isError }
<ActionBar mt={ -6 } showShadow={ query.isLoading }> isLoading={ query.isLoading }
<Pagination ml="auto" { ...query.pagination }/> items={ query.data?.items }
</ActionBar> skeletonProps={{ isLongSkeleton: true, skeletonDesktopColumns: [ '17%', '17%', '16%', '25%', '25%' ] }}
) } emptyText="There are no validated blocks for this address."
{ socketAlert && <SocketAlert mb={ 6 }/> } content={ content }
{ content } actionBar={ actionBar }
</Box> />
); );
}; };
......
import { Text, Show, Hide } from '@chakra-ui/react'; import { Show, Hide } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -10,12 +10,9 @@ import useQueryWithPages from 'lib/hooks/useQueryWithPages'; ...@@ -10,12 +10,9 @@ import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import AddressIntTxsTable from 'ui/address/internals/AddressIntTxsTable'; import AddressIntTxsTable from 'ui/address/internals/AddressIntTxsTable';
import EmptySearchResult from 'ui/apps/EmptySearchResult';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
import SkeletonList from 'ui/shared/skeletons/SkeletonList';
import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
import AddressCsvExportLink from './AddressCsvExportLink'; import AddressCsvExportLink from './AddressCsvExportLink';
import AddressTxsFilter from './AddressTxsFilter'; import AddressTxsFilter from './AddressTxsFilter';
...@@ -43,57 +40,40 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE ...@@ -43,57 +40,40 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
onFilterChange({ filter: newVal }); onFilterChange({ filter: newVal });
}, [ onFilterChange ]); }, [ onFilterChange ]);
const content = (() => { const content = data?.items ? (
if (isError) {
return <DataFetchAlert/>;
}
if (isLoading) {
return (
<>
<Show below="lg" ssr={ false }>
<SkeletonList/>
</Show>
<Hide below="lg" ssr={ false }>
<SkeletonTable columns={ [ '15%', '15%', '10%', '20%', '20%', '20%' ] } isLong/>
</Hide>
</>
);
}
if (data.items.length === 0 && !filterValue) {
return <Text as="span">There are no internal transactions for this address.</Text>;
}
if (data.items.length === 0) {
return <EmptySearchResult text={ `Couldn${ apos }t find any transaction that matches your query.` }/>;
}
return (
<>
<Show below="lg" ssr={ false }>
<AddressIntTxsList data={ data.items } currentAddress={ hash }/>
</Show>
<Hide below="lg" ssr={ false }>
<AddressIntTxsTable data={ data.items } currentAddress={ hash }/>
</Hide>
</>
);
})();
return (
<> <>
<ActionBar mt={ -6 } justifyContent="left" showShadow={ isLoading }> <Show below="lg" ssr={ false }>
<AddressTxsFilter <AddressIntTxsList data={ data.items } currentAddress={ hash }/>
defaultFilter={ filterValue } </Show>
onFilterChange={ handleFilterChange } <Hide below="lg" ssr={ false }>
isActive={ Boolean(filterValue) } <AddressIntTxsTable data={ data.items } currentAddress={ hash }/>
/> </Hide>
<AddressCsvExportLink address={ hash } type="internal-transactions" ml={{ base: 2, lg: 'auto' }}/>
{ isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> }
</ActionBar>
{ content }
</> </>
) : null ;
const actionBar = (
<ActionBar mt={ -6 } justifyContent="left" showShadow={ isLoading }>
<AddressTxsFilter
defaultFilter={ filterValue }
onFilterChange={ handleFilterChange }
isActive={ Boolean(filterValue) }
/>
<AddressCsvExportLink address={ hash } type="internal-transactions" ml={{ base: 2, lg: 'auto' }}/>
{ isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> }
</ActionBar>
);
return (
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
items={ data?.items }
skeletonProps={{ isLongSkeleton: true, skeletonDesktopColumns: [ '15%', '15%', '10%', '20%', '20%', '20%' ] }}
filterProps={{ emptyFilteredText: `Couldn${ apos }t find any transaction that matches your query.`, hasActiveFilters: Boolean(filterValue) }}
emptyText="There are no internal transactions for this address."
content={ content }
actionBar={ actionBar }
/>
); );
}; };
......
import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import useQueryWithPages from 'lib/hooks/useQueryWithPages'; import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataListDisplay from 'ui/shared/DataListDisplay';
import LogItem from 'ui/shared/logs/LogItem'; import LogItem from 'ui/shared/logs/LogItem';
import LogSkeleton from 'ui/shared/logs/LogSkeleton'; import LogSkeleton from 'ui/shared/logs/LogSkeleton';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
...@@ -20,35 +19,26 @@ const AddressLogs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement> ...@@ -20,35 +19,26 @@ const AddressLogs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>
scrollRef, scrollRef,
}); });
if (isError) { const actionBar = isPaginationVisible ? (
return <DataFetchAlert/>;
}
const bar = isPaginationVisible ? (
<ActionBar mt={ -6 } showShadow> <ActionBar mt={ -6 } showShadow>
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
) : null; ) : null;
if (isLoading) { const content = data?.items ? data.items.map((item, index) => <LogItem key={ index } { ...item } type="address"/>) : null;
return (
<Box>
{ bar }
<LogSkeleton/>
<LogSkeleton/>
</Box>
);
}
if (data.items.length === 0) { const skeleton = <><LogSkeleton/><LogSkeleton/></>;
return <span>There are no logs for this address.</span>;
}
return ( return (
<> <DataListDisplay
{ bar } isError={ isError }
{ data.items.map((item, index) => <LogItem key={ index } { ...item } type="address"/>) } isLoading={ isLoading }
</> items={ data?.items }
emptyText="There are no logs for this address."
content={ content }
actionBar={ actionBar }
skeletonProps={{ customSkeleton: skeleton }}
/>
); );
}; };
......
...@@ -13,7 +13,7 @@ const API_URL = buildApiUrl('address_token_transfers', { hash: '0xd789a607CEac2f ...@@ -13,7 +13,7 @@ const API_URL = buildApiUrl('address_token_transfers', { hash: '0xd789a607CEac2f
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', token_hash: '0x1189a607CEac2f0E14867de4EB15b15C9FFB5859' }, query: { hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', token: '0x1189a607CEac2f0E14867de4EB15b15C9FFB5859' },
}, },
}; };
......
...@@ -20,13 +20,10 @@ import getQueryParamString from 'lib/router/getQueryParamString'; ...@@ -20,13 +20,10 @@ import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import TOKEN_TYPE from 'lib/token/tokenTypes'; import TOKEN_TYPE from 'lib/token/tokenTypes';
import EmptySearchResult from 'ui/apps/EmptySearchResult';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataListDisplay from 'ui/shared/DataListDisplay';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
import SkeletonList from 'ui/shared/skeletons/SkeletonList';
import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TokenLogo from 'ui/shared/TokenLogo'; import TokenLogo from 'ui/shared/TokenLogo';
import { flattenTotal } from 'ui/shared/TokenTransfer/helpers'; import { flattenTotal } from 'ui/shared/TokenTransfer/helpers';
...@@ -76,7 +73,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -76,7 +73,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
const [ socketAlert, setSocketAlert ] = React.useState(''); const [ socketAlert, setSocketAlert ] = React.useState('');
const [ newItemsCount, setNewItemsCount ] = React.useState(0); const [ newItemsCount, setNewItemsCount ] = React.useState(0);
const tokenFilter = getQueryParamString(router.query.token_hash) || undefined; const tokenFilter = getQueryParamString(router.query.token) || undefined;
const [ filters, setFilters ] = React.useState<Filters>( const [ filters, setFilters ] = React.useState<Filters>(
{ {
...@@ -164,67 +161,40 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -164,67 +161,40 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
const numActiveFilters = (filters.type?.length || 0) + (filters.filter ? 1 : 0); const numActiveFilters = (filters.type?.length || 0) + (filters.filter ? 1 : 0);
const isActionBarHidden = !tokenFilter && !numActiveFilters && !data?.items.length && !currentAddress; const isActionBarHidden = !tokenFilter && !numActiveFilters && !data?.items.length && !currentAddress;
const content = (() => { const items = data?.items?.reduce(flattenTotal, []);
if (isLoading) { const content = items ? (
return ( <>
<> <Hide below="lg" ssr={ false }>
<Hide below="lg" ssr={ false }> <TokenTransferTable
<SkeletonTable columns={ [ '44px', '185px', '160px', '25%', '25%', '25%', '25%' ] } isLong/> data={ items }
</Hide> baseAddress={ currentAddress }
<Show below="lg" ssr={ false }> showTxInfo
<SkeletonList/> top={ isActionBarHidden ? 0 : 80 }
</Show> enableTimeIncrement
</> showSocketInfo={ pagination.page === 1 && !tokenFilter }
); socketInfoAlert={ socketAlert }
} socketInfoNum={ newItemsCount }
/>
if (isError) { </Hide>
return <DataFetchAlert/>; <Show below="lg" ssr={ false }>
} { pagination.page === 1 && !tokenFilter && (
<SocketNewItemsNotice
if (!data.items?.length && !numActiveFilters) { url={ window.location.href }
return <Text as="span">There are no token transfers</Text>; num={ newItemsCount }
} alert={ socketAlert }
type="token_transfer"
if (!data.items?.length) { borderBottomRadius={ 0 }
return <EmptySearchResult text={ `Couldn${ apos }t find any token transfer that matches your query.` }/>;
}
const items = data.items.reduce(flattenTotal, []);
return (
<>
<Hide below="lg" ssr={ false }>
<TokenTransferTable
data={ items }
baseAddress={ currentAddress }
showTxInfo
top={ isActionBarHidden ? 0 : 80 }
enableTimeIncrement
showSocketInfo={ pagination.page === 1 && !tokenFilter }
socketInfoAlert={ socketAlert }
socketInfoNum={ newItemsCount }
/>
</Hide>
<Show below="lg" ssr={ false }>
{ pagination.page === 1 && !tokenFilter && (
<SocketNewItemsNotice
url={ window.location.href }
num={ newItemsCount }
alert={ socketAlert }
type="token_transfer"
borderBottomRadius={ 0 }
/>
) }
<TokenTransferList
data={ items }
baseAddress={ currentAddress }
showTxInfo
enableTimeIncrement
/> />
</Show> ) }
</> <TokenTransferList
); data={ items }
})(); baseAddress={ currentAddress }
showTxInfo
enableTimeIncrement
/>
</Show>
</>
) : null;
const tokenFilterComponent = tokenFilter && ( const tokenFilterComponent = tokenFilter && (
<Flex alignItems="center" flexWrap="wrap" mb={{ base: isActionBarHidden ? 3 : 6, lg: 0 }} mr={ 4 }> <Flex alignItems="center" flexWrap="wrap" mb={{ base: isActionBarHidden ? 3 : 6, lg: 0 }} mr={ 4 }>
...@@ -249,7 +219,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -249,7 +219,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
</Flex> </Flex>
); );
return ( const actionBar = (
<> <>
{ isMobile && tokenFilterComponent } { isMobile && tokenFilterComponent }
{ !isActionBarHidden && ( { !isActionBarHidden && (
...@@ -269,9 +239,27 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -269,9 +239,27 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
{ isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> } { isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> }
</ActionBar> </ActionBar>
) } ) }
{ content }
</> </>
); );
return (
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
items={ data?.items }
skeletonProps={{
isLongSkeleton: true,
skeletonDesktopColumns: [ '44px', '185px', '160px', '25%', '25%', '25%', '25%' ],
}}
emptyText="There are no token transfers."
filterProps={{
emptyFilteredText: `Couldn${ apos }t find any token transfer that matches your query.`,
hasActiveFilters: Boolean(numActiveFilters),
}}
content={ content }
actionBar={ actionBar }
/>
);
}; };
export default AddressTokenTransfers; export default AddressTokenTransfers;
...@@ -9,7 +9,7 @@ import appConfig from 'configs/app/config'; ...@@ -9,7 +9,7 @@ import appConfig from 'configs/app/config';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
type Props = Block & { type Props = Block & {
......
import { Box, Hide, Show, Table, Tbody, Th, Tr } from '@chakra-ui/react'; import { Hide, Show, Table, Tbody, Th, Tr } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
...@@ -6,11 +6,9 @@ import type { AddressCoinBalanceHistoryResponse } from 'types/api/address'; ...@@ -6,11 +6,9 @@ import type { AddressCoinBalanceHistoryResponse } from 'types/api/address';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataListDisplay from 'ui/shared/DataListDisplay';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { Props as PaginationProps } from 'ui/shared/Pagination';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
import SkeletonList from 'ui/shared/skeletons/SkeletonList';
import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import AddressCoinBalanceListItem from './AddressCoinBalanceListItem'; import AddressCoinBalanceListItem from './AddressCoinBalanceListItem';
...@@ -25,66 +23,51 @@ interface Props { ...@@ -25,66 +23,51 @@ interface Props {
const AddressCoinBalanceHistory = ({ query }: Props) => { const AddressCoinBalanceHistory = ({ query }: Props) => {
const content = (() => { const content = query.data?.items ? (
if (query.isLoading) { <>
return ( <Hide below="lg" ssr={ false }>
<> <Table variant="simple" size="sm">
<Hide below="lg" ssr={ false }> <Thead top={ query.isPaginationVisible ? 80 : 0 }>
<SkeletonTable columns={ [ '25%', '25%', '25%', '25%', '120px' ] }/> <Tr>
</Hide> <Th width="20%">Block</Th>
<Show below="lg" ssr={ false }> <Th width="20%">Txn</Th>
<SkeletonList/> <Th width="20%">Age</Th>
</Show> <Th width="20%" isNumeric pr={ 1 }>Balance { appConfig.network.currency.symbol }</Th>
</> <Th width="20%" isNumeric>Delta</Th>
); </Tr>
} </Thead>
<Tbody>
{ query.data.items.map((item) => (
<AddressCoinBalanceTableItem key={ item.block_number } { ...item } page={ query.pagination.page }/>
)) }
</Tbody>
</Table>
</Hide>
<Show below="lg" ssr={ false }>
{ query.data.items.map((item) => (
<AddressCoinBalanceListItem key={ item.block_number } { ...item } page={ query.pagination.page }/>
)) }
</Show>
</>
) : null;
if (query.isError) { const actionBar = query.isPaginationVisible ? (
return <DataFetchAlert/>; <ActionBar mt={ -6 }>
} <Pagination ml="auto" { ...query.pagination }/>
</ActionBar>
if (query.data.items.length === 0 && !query.isPaginationVisible) { ) : null;
return <span>There is no coin balance history for this address</span>;
}
return (
<>
<Hide below="lg" ssr={ false }>
<Table variant="simple" size="sm">
<Thead top={ query.isPaginationVisible ? 80 : 0 }>
<Tr>
<Th width="20%">Block</Th>
<Th width="20%">Txn</Th>
<Th width="20%">Age</Th>
<Th width="20%" isNumeric pr={ 1 }>Balance { appConfig.network.currency.symbol }</Th>
<Th width="20%" isNumeric>Delta</Th>
</Tr>
</Thead>
<Tbody>
{ query.data.items.map((item) => (
<AddressCoinBalanceTableItem key={ item.block_number } { ...item } page={ query.pagination.page }/>
)) }
</Tbody>
</Table>
</Hide>
<Show below="lg" ssr={ false }>
{ query.data.items.map((item) => (
<AddressCoinBalanceListItem key={ item.block_number } { ...item } page={ query.pagination.page }/>
)) }
</Show>
</>
);
})();
return ( return (
<Box mt={ 8 }> <DataListDisplay
{ query.isPaginationVisible && ( mt={ 8 }
<ActionBar mt={ -6 }> isError={ query.isError }
<Pagination ml="auto" { ...query.pagination }/> isLoading={ query.isLoading }
</ActionBar> items={ query.data?.items }
) } skeletonProps={{ skeletonDesktopColumns: [ '25%', '25%', '25%', '25%', '120px' ] }}
{ content } emptyText="There is no coin balance history for this address."
</Box> content={ content }
actionBar={ actionBar }
/>
); );
}; };
......
...@@ -11,7 +11,7 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; ...@@ -11,7 +11,7 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
type Props = AddressCoinBalanceHistoryItem & { type Props = AddressCoinBalanceHistoryItem & {
page: number; page: number;
......
...@@ -20,6 +20,7 @@ test('verified with changed byte code +@mobile +@dark-mode', async({ mount, page ...@@ -20,6 +20,7 @@ test('verified with changed byte code +@mobile +@dark-mode', async({ mount, page
status: 200, status: 200,
body: JSON.stringify(contractMock.withChangedByteCode), body: JSON.stringify(contractMock.withChangedByteCode),
})); }));
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**', (route) => route.abort());
const component = await mount( const component = await mount(
<TestApp> <TestApp>
...@@ -36,6 +37,7 @@ test('verified with multiple sources +@mobile', async({ mount, page }) => { ...@@ -36,6 +37,7 @@ test('verified with multiple sources +@mobile', async({ mount, page }) => {
status: 200, status: 200,
body: JSON.stringify(contractMock.withMultiplePaths), body: JSON.stringify(contractMock.withMultiplePaths),
})); }));
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**', (route) => route.abort());
await mount( await mount(
<TestApp> <TestApp>
...@@ -54,6 +56,7 @@ test('verified via sourcify', async({ mount, page }) => { ...@@ -54,6 +56,7 @@ test('verified via sourcify', async({ mount, page }) => {
status: 200, status: 200,
body: JSON.stringify(contractMock.verifiedViaSourcify), body: JSON.stringify(contractMock.verifiedViaSourcify),
})); }));
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**', (route) => route.abort());
await mount( await mount(
<TestApp> <TestApp>
...@@ -70,6 +73,7 @@ test('self destructed', async({ mount, page }) => { ...@@ -70,6 +73,7 @@ test('self destructed', async({ mount, page }) => {
status: 200, status: 200,
body: JSON.stringify(contractMock.selfDestructed), body: JSON.stringify(contractMock.selfDestructed),
})); }));
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**', (route) => route.abort());
await mount( await mount(
<TestApp> <TestApp>
...@@ -87,6 +91,7 @@ test('with twin address alert +@mobile', async({ mount, page }) => { ...@@ -87,6 +91,7 @@ test('with twin address alert +@mobile', async({ mount, page }) => {
status: 200, status: 200,
body: JSON.stringify(contractMock.withTwinAddress), body: JSON.stringify(contractMock.withTwinAddress),
})); }));
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**', (route) => route.abort());
const component = await mount( const component = await mount(
<TestApp> <TestApp>
...@@ -103,6 +108,7 @@ test('with proxy address alert +@mobile', async({ mount, page }) => { ...@@ -103,6 +108,7 @@ test('with proxy address alert +@mobile', async({ mount, page }) => {
status: 200, status: 200,
body: JSON.stringify(contractMock.withProxyAddress), body: JSON.stringify(contractMock.withProxyAddress),
})); }));
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**', (route) => route.abort());
const component = await mount( const component = await mount(
<TestApp> <TestApp>
...@@ -119,6 +125,7 @@ test('non verified', async({ mount, page }) => { ...@@ -119,6 +125,7 @@ test('non verified', async({ mount, page }) => {
status: 200, status: 200,
body: JSON.stringify(contractMock.nonVerified), body: JSON.stringify(contractMock.nonVerified),
})); }));
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**', (route) => route.abort());
const component = await mount( const component = await mount(
<TestApp> <TestApp>
......
import { Box, chakra, Flex, Text, Tooltip } from '@chakra-ui/react'; import { Flex, Text, Tooltip } from '@chakra-ui/react';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SmartContract } from 'types/api/contract'; import type { SmartContract } from 'types/api/contract';
import CodeEditor from 'ui/shared/CodeEditor';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
import CodeEditor from 'ui/shared/monaco/CodeEditor';
import formatFilePath from 'ui/shared/monaco/utils/formatFilePath';
interface Props { interface Props {
data: string; data: string;
...@@ -37,38 +38,25 @@ const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, addi ...@@ -37,38 +38,25 @@ const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, addi
</Tooltip> </Tooltip>
) : null; ) : null;
if (!additionalSource?.length) { const editorData = React.useMemo(() => {
return ( const defaultName = isViper ? '/index.vy' : '/index.sol';
<section> return [
<Flex justifyContent="space-between" alignItems="center" mb={ 3 }> { file_path: formatFilePath(filePath || defaultName), source_code: data },
{ heading } ...(additionalSource || []).map((source) => ({ ...source, file_path: formatFilePath(source.file_path) })) ];
{ diagramLink } }, [ additionalSource, data, filePath, isViper ]);
<CopyToClipboard text={ data }/>
</Flex> const copyToClipboard = editorData.length === 1 ?
<CodeEditor value={ data } id="source_code"/> <CopyToClipboard text={ editorData[0].source_code }/> :
</section> null;
);
}
return ( return (
<section> <section>
<Flex justifyContent="space-between" alignItems="center" mb={ 3 }> <Flex justifyContent="space-between" alignItems="center" mb={ 3 }>
{ heading } { heading }
{ diagramLink } { diagramLink }
{ copyToClipboard }
</Flex> </Flex>
<Flex flexDir="column" rowGap={ 3 }> <CodeEditor data={ editorData }/>
{ [ { file_path: filePath, source_code: data }, ...additionalSource ].map((item, index, array) => (
<Box key={ index }>
<Flex justifyContent="space-between" alignItems="flex-end" mb={ 3 }>
<chakra.span fontSize="sm" wordBreak="break-all" lineHeight="20px">
File { index + 1 } of { array.length }: { item.file_path }
</chakra.span>
<CopyToClipboard text={ item.source_code } ml={ 4 }/>
</Flex>
<CodeEditor value={ item.source_code } id={ `source_code_${ index }` }/>
</Box>
)) }
</Flex>
</section> </section>
); );
}; };
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment