Commit a313fd7e authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into tx-revert-reason

parents 553aad0e 00c7792b
...@@ -4,7 +4,6 @@ NEXT_PUBLIC_FOOTER_GITHUB_LINK=APP_NEXT_NEXT_PUBLIC_FOOTER_GITHUB_LINK ...@@ -4,7 +4,6 @@ NEXT_PUBLIC_FOOTER_GITHUB_LINK=APP_NEXT_NEXT_PUBLIC_FOOTER_GITHUB_LINK
NEXT_PUBLIC_FOOTER_TWITTER_LINK=APP_NEXT_NEXT_PUBLIC_FOOTER_TWITTER_LINK NEXT_PUBLIC_FOOTER_TWITTER_LINK=APP_NEXT_NEXT_PUBLIC_FOOTER_TWITTER_LINK
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=APP_NEXT_NEXT_PUBLIC_FOOTER_TELEGRAM_LINK NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=APP_NEXT_NEXT_PUBLIC_FOOTER_TELEGRAM_LINK
NEXT_PUBLIC_FOOTER_STAKING_LINK=APP_NEXT_NEXT_PUBLIC_FOOTER_STAKING_LINK NEXT_PUBLIC_FOOTER_STAKING_LINK=APP_NEXT_NEXT_PUBLIC_FOOTER_STAKING_LINK
NEXT_PUBLIC_FOOTER_STAKING_LINK=APP_NEXT_NEXT_PUBLIC_FOOTER_STAKING_LINK
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=APP_NEXT_NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=APP_NEXT_NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM
NEXT_PUBLIC_SENTRY_DSN=APP_NEXT_NEXT_PUBLIC_SENTRY_DSN NEXT_PUBLIC_SENTRY_DSN=APP_NEXT_NEXT_PUBLIC_SENTRY_DSN
NEXT_PUBLIC_APP_INSTANCE=APP_NEXT_NEXT_PUBLIC_APP_INSTANCE NEXT_PUBLIC_APP_INSTANCE=APP_NEXT_NEXT_PUBLIC_APP_INSTANCE
......
...@@ -4,11 +4,12 @@ on: ...@@ -4,11 +4,12 @@ on:
pull_request: pull_request:
types: types:
- closed - closed
- merged
workflow_dispatch:
jobs: jobs:
if_merged: cleanup:
if: github.event.pull_request.merged == true uses: blockscout/blockscout-ci-cd/.github/workflows/cleanup.yaml@master
uses: blockscout/blockscout-ci-cd/.github/workflows/cleanup.yaml@fix-e2e-tests
with: with:
appNamespace: review-front-$GITHUB_HEAD_REF_SLUG appNamespace: review-front-$GITHUB_HEAD_REF_SLUG
secrets: inherit secrets: inherit
name: Run E2E tests k8s name: Run E2E tests k8s
on: on:
push: # push:
# pull_request: # pull_request:
workflow_dispatch
env: env:
K8S_LOCAL_PORT: ${{ secrets.K8S_LOCAL_PORT }} K8S_LOCAL_PORT: ${{ secrets.K8S_LOCAL_PORT }}
...@@ -65,7 +66,7 @@ jobs: ...@@ -65,7 +66,7 @@ jobs:
deploy_and_tests: deploy_and_tests:
needs: push_to_registry needs: push_to_registry
uses: blockscout/blockscout-ci-cd/.github/workflows/e2e_new.yaml@fix-e2e-tests uses: blockscout/blockscout-ci-cd/.github/workflows/e2e_new.yaml@master
with: with:
valuesDir: deploy/values/e2e valuesDir: deploy/values/e2e
appName: e2e-front appName: e2e-front
......
name: Run code checks name: Linters
on: [pull_request] on:
pull_request:
push:
branches:
- main
jobs: jobs:
check_code: check_code:
name: Check code name: Run code checks
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'yarn'
- name: Install dependencies - name: Install dependencies
run: yarn run: yarn install --frozen-lockfile
- name: Run ESLint - name: Run ESLint
run: yarn lint:eslint run: yarn lint:eslint
- name: Compile TypeScript - name: Compile TypeScript
......
name: playwright name: Playwright
on: [pull_request] on: [pull_request]
jobs: jobs:
test: test:
name: Run components visual tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: mcr.microsoft.com/playwright:v1.27.0-focal image: mcr.microsoft.com/playwright:v1.27.0-focal
...@@ -11,11 +12,12 @@ jobs: ...@@ -11,11 +12,12 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
lfs: 'true' lfs: 'true'
- uses: actions/setup-node@v2 - uses: actions/setup-node@v3
with: with:
node-version: '16' node-version: '16'
cache: 'yarn'
- name: Install dependencies - name: Install dependencies
run: yarn run: yarn install --frozen-lockfile
- name: Run your tests - name: Run your tests
run: HOME=/root yarn test-ct run: HOME=/root yarn test-ct
- name: Upload test results - name: Upload test results
......
...@@ -3,6 +3,7 @@ name: Deploy review environment ...@@ -3,6 +3,7 @@ name: Deploy review environment
on: on:
# push: # push:
pull_request: pull_request:
workflow_dispatch:
env: env:
K8S_LOCAL_PORT: ${{ secrets.K8S_LOCAL_PORT }} K8S_LOCAL_PORT: ${{ secrets.K8S_LOCAL_PORT }}
...@@ -65,7 +66,7 @@ jobs: ...@@ -65,7 +66,7 @@ jobs:
deploy_frontend: deploy_frontend:
needs: push_to_registry needs: push_to_registry
uses: blockscout/blockscout-ci-cd/.github/workflows/deploy.yaml@fix-e2e-tests uses: blockscout/blockscout-ci-cd/.github/workflows/deploy.yaml@master
with: with:
valuesDir: deploy/values/review valuesDir: deploy/values/review
appNamespace: review-front-$GITHUB_HEAD_REF_SLUG appNamespace: review-front-$GITHUB_HEAD_REF_SLUG
......
...@@ -54,11 +54,15 @@ jobs: ...@@ -54,11 +54,15 @@ jobs:
SENTRY_CSP_REPORT_URI=${{ secrets.SENTRY_CSP_REPORT_URI }} SENTRY_CSP_REPORT_URI=${{ secrets.SENTRY_CSP_REPORT_URI }}
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
deploy_frontend: deploy_main:
needs: push_to_registry needs: push_to_registry
uses: blockscout/blockscout-ci-cd/.github/workflows/deploy.yaml@deploy-smart-contract-verifier uses: blockscout/blockscout-ci-cd/.github/workflows/deploy.yaml@master
with: with:
valuesDir: deploy/values valuesDir: deploy/values/review
appNamespace: frontend-main appNamespace: front-main
appName: frontend blockscoutIngressHost: blockscout
frontendIngressHost: frontend
frontendImage: ghcr.io/blockscout/frontend:main
gethIngressHost: geth
scVerifierIngressHost: sc-verifier
secrets: inherit secrets: inherit
...@@ -33,11 +33,13 @@ We use [playwright experimental components testing](https://playwright.dev/docs/ ...@@ -33,11 +33,13 @@ We use [playwright experimental components testing](https://playwright.dev/docs/
To perform testing locally you need to install docker and run `yarn test-docker` To perform testing locally you need to install docker and run `yarn test-docker`
## Environment variables ## Environment variables
### Variables list
The app instance could be customized by passing following variables to NodeJS environment at runtime. The app instance could be customized by passing following variables to NodeJS environment at runtime.
**IMPORTANT NOTE!** For _production_ build purposes all json-like values should be single-quoted **IMPORTANT NOTE!** For _production_ build purposes all json-like values should be single-quoted
### Network configuration
| Variable | Type | Description | Default value | Variable | Type | Description | Default value
| --- | --- | --- | --- | | --- | --- | --- | --- |
| NEXT_PUBLIC_NETWORK_NAME | `string` | Displayed name of the network | `Gnosis Chain` | | NEXT_PUBLIC_NETWORK_NAME | `string` | Displayed name of the network | `Gnosis Chain` |
...@@ -50,21 +52,37 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -50,21 +52,37 @@ The app instance could be customized by passing following variables to NodeJS en
| NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME | `string` *(optional)* | Network name for constructing url of token logos according to template `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/${assetsNamePath}/assets/${tokenAddress}/logo.png`. It should match network name in TrustWallet assets repo, see the full list [here](https://github.com/trustwallet/assets/tree/master/blockchains). If not provided, the network type will be used as its assets path part | `ethereum` | | NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME | `string` *(optional)* | Network name for constructing url of token logos according to template `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/${assetsNamePath}/assets/${tokenAddress}/logo.png`. It should match network name in TrustWallet assets repo, see the full list [here](https://github.com/trustwallet/assets/tree/master/blockchains). If not provided, the network type will be used as its assets path part | `ethereum` |
| NEXT_PUBLIC_NETWORK_LOGO | `string` *(optional)* | Network logo; if not provided, will fallback to logo predefined in the project; if the project doesn't have logo for such network then the common placeholder will be shown; *Note* that logo height should be 20px and width less than 120px | `https://www.fillmurray.com/240/40` | | NEXT_PUBLIC_NETWORK_LOGO | `string` *(optional)* | Network logo; if not provided, will fallback to logo predefined in the project; if the project doesn't have logo for such network then the common placeholder will be shown; *Note* that logo height should be 20px and width less than 120px | `https://www.fillmurray.com/240/40` |
| NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED | `boolean` *(optional)* | Set to true if network has account feature | `true` | | NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED | `boolean` *(optional)* | Set to true if network has account feature | `true` |
| NEXT_PUBLIC_FEATURED_NETWORKS | `Array<FeaturedNetwork>` where `FeaturedNetwork` can have following [properties](#network-configuration-properties) | Configuration of featured networks that will be shown in the app menu | `[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'}]` |
*Note* the base path for the network is built up from its `type` and `subType` like so `https://blockscout.com/<type>/<subType>`
### UI configuration
| Variable | Type | Description | Default value
| --- | --- | --- | --- |
| NEXT_PUBLIC_FEATURED_NETWORKS | `Array<FeaturedNetwork>` where `FeaturedNetwork` can have following [properties](#featured-network-configuration-properties) | Configuration of featured networks that will be shown in the network menu | `[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'}]` |
| NEXT_PUBLIC_BLOCKSCOUT_VERSION | `string` *(optional)* | Current running version of Blockscout (used to display link to release in the footer) | | NEXT_PUBLIC_BLOCKSCOUT_VERSION | `string` *(optional)* | Current running version of Blockscout (used to display link to release in the footer) |
| NEXT_PUBLIC_FOOTER_GITHUB_LINK | `string` *(optional)* | Link to Github in the footer | `https://github.com/blockscout/blockscout` | | NEXT_PUBLIC_FOOTER_GITHUB_LINK | `string` *(optional)* | Link to Github in the footer | `https://github.com/blockscout/blockscout` |
| NEXT_PUBLIC_FOOTER_TWITTER_LINK | `string` *(optional)* | Link to Twitter in the footer | `https://www.twitter.com/blockscoutcom` | | NEXT_PUBLIC_FOOTER_TWITTER_LINK | `string` *(optional)* | Link to Twitter in the footer | `https://www.twitter.com/blockscoutcom` |
| NEXT_PUBLIC_FOOTER_TELEGRAM_LINK | `string` *(optional)* | Link to Telegram in the footer | `https://t.me/poa_network` | | NEXT_PUBLIC_FOOTER_TELEGRAM_LINK | `string` *(optional)* | Link to Telegram in the footer | `https://t.me/poa_network` |
| NEXT_PUBLIC_FOOTER_STAKING_LINK | `string` *(optional)* | Link to staking dashboard in the footer | `https://duneanalytics.com/maxaleks/xdai-staking` | | NEXT_PUBLIC_FOOTER_STAKING_LINK | `string` *(optional)* | Link to staking dashboard in the footer | `https://duneanalytics.com/maxaleks/xdai-staking` |
| NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | `https://airtable.com/shrqUAcjgGJ4jU88C` | | NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | `https://airtable.com/shrqUAcjgGJ4jU88C` |
### App configuration
| Variable | Type | Description | Default value
| --- | --- | --- | --- |
| NEXT_PUBLIC_APP_INSTANCE | `string` *(optional)* | Name of app instance | `wonderful_kepler` | | NEXT_PUBLIC_APP_INSTANCE | `string` *(optional)* | Name of app instance | `wonderful_kepler` |
| NEXT_PUBLIC_APP_PROTOCOL | `http \| https` *(optional)* | App protocol (`https` used as default value) | `https` | | NEXT_PUBLIC_APP_PROTOCOL | `http \| https` *(optional)* | App protocol (`https` used as default value) | `https` |
| NEXT_PUBLIC_APP_HOST | `string` | App host | `blockscout.com` | | NEXT_PUBLIC_APP_HOST | `string` | App host | `blockscout.com` |
| NEXT_PUBLIC_APP_PORT | `number` *(optional)* | Port where app is running. Have to be provided if it is different to default port | `3000` | | NEXT_PUBLIC_APP_PORT | `number` *(optional)* | Port where app is running. Have to be provided if it is different to default port | `3000` |
### API configuration
| Variable | Type | Description | Default value
| --- | --- | --- | --- |
| NEXT_PUBLIC_API_ENDPOINT | `string` *(optional)* | By default the API endpoint base URL will be set as `https://blockscout.com`. If it is not the case, pass the API endpoint base URL in this variable | `https://blockscout.com` | | NEXT_PUBLIC_API_ENDPOINT | `string` *(optional)* | By default the API endpoint base URL will be set as `https://blockscout.com`. If it is not the case, pass the API endpoint base URL in this variable | `https://blockscout.com` |
| NEXT_PUBLIC_API_BASE_PATH | `string` *(optional)* | Base path for API endpoint url | `/poa/core` | | NEXT_PUBLIC_API_BASE_PATH | `string` *(optional)* | Base path for API endpoint url | `/poa/core` |
| NEXT_PUBLIC_SENTRY_DSN | `string` *(optional)* | Client key for your Senty.io app | `<secret>` |
| SENTRY_CSP_REPORT_URI | `string` *(optional)* | URL for sending CSP-reports to your Senty.io app | `<secret>` |
### Featured network configuration properties ### Featured network configuration properties
...@@ -75,8 +93,9 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -75,8 +93,9 @@ The app instance could be customized by passing following variables to NodeJS en
| group | `mainnets \| testnets \| other` | Indicates in which tab network appears in the menu | `'mainnets'` | | group | `mainnets \| testnets \| other` | Indicates in which tab network appears in the menu | `'mainnets'` |
| icon | `string` *(optional)* | Network icon; if not provided, will fallback to icon predefined in the project; if the project doesn't have icon for such network then the common placeholder will be shown; *Note* that icon size should be 30px by 30px | `'https://www.fillmurray.com/60/60'` | | icon | `string` *(optional)* | Network icon; if not provided, will fallback to icon predefined in the project; if the project doesn't have icon for such network then the common placeholder will be shown; *Note* that icon size should be 30px by 30px | `'https://www.fillmurray.com/60/60'` |
*Note* the base path for the network is built up from its `type` and `subType` like so `https://blockscout.com/<type>/<subType>` ### External services configuration
### Sentry.io setup
TBD | Variable | Type | Description | Default value
\ No newline at end of file | --- | --- | --- | --- |
| NEXT_PUBLIC_SENTRY_DSN | `string` *(optional)* | Client key for your Senty.io app | `<secret>` |
| SENTRY_CSP_REPORT_URI | `string` *(optional)* | URL for sending CSP-reports to your Senty.io app | `<secret>` |
\ No newline at end of file
# app config
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local
# nav and footer config
NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'}] NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'}]
# marketplace config
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
# api config
NEXT_PUBLIC_API_ENDPOINT=https://blockscout.com
\ No newline at end of file
# nav and footer config
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=https://t.me/poa_network NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=https://t.me/poa_network
NEXT_PUBLIC_FOOTER_STAKING_LINK=https://duneanalytics.com/maxaleks/xdai-staking NEXT_PUBLIC_FOOTER_STAKING_LINK=https://duneanalytics.com/maxaleks/xdai-staking
NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'},{'title':'Optimism on Gnosis Chain','basePath':'/xdai/optimism','group':'mainnets','icon':'https://www.fillmurray.com/60/60'},{'title':'Arbitrum on xDai','basePath':'/xdai/aox','group':'mainnets'},{'title':'Ethereum','basePath':'/eth/mainnet','group':'mainnets'},{'title':'Ethereum Classic','basePath':'/etx/mainnet','group':'mainnets'},{'title':'POA','basePath':'/poa/core','group':'mainnets'},{'title':'RSK','basePath':'/rsk/mainnet','group':'mainnets'},{'title':'Gnosis Chain Testnet','basePath':'/xdai/testnet','group':'testnets'},{'title':'POA Sokol','basePath':'/poa/sokol','group':'testnets'},{'title':'ARTIS Σ1','basePath':'/artis/sigma1','group':'other'},{'title':'LUKSO L14','basePath':'/lukso/l14','group':'other'},{'title':'Astar','basePath':'/astar','group':'other'}]
# current network config
NEXT_PUBLIC_NETWORK_NAME=POA NEXT_PUBLIC_NETWORK_NAME=POA
NEXT_PUBLIC_NETWORK_SHORT_NAME=POA NEXT_PUBLIC_NETWORK_SHORT_NAME=POA
NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME=poa NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME=poa
...@@ -9,6 +13,6 @@ NEXT_PUBLIC_NETWORK_ID=99 ...@@ -9,6 +13,6 @@ NEXT_PUBLIC_NETWORK_ID=99
NEXT_PUBLIC_NETWORK_CURRENCY=POA NEXT_PUBLIC_NETWORK_CURRENCY=POA
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0x029a799563238d0e75e20be2f4bda0ea68d00172 NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=0x029a799563238d0e75e20be2f4bda0ea68d00172
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'},{'title':'Optimism on Gnosis Chain','basePath':'/xdai/optimism','group':'mainnets','icon':'https://www.fillmurray.com/60/60'},{'title':'Arbitrum on xDai','basePath':'/xdai/aox','group':'mainnets'},{'title':'Ethereum','basePath':'/eth/mainnet','group':'mainnets'},{'title':'Ethereum Classic','basePath':'/etx/mainnet','group':'mainnets'},{'title':'POA','basePath':'/poa/core','group':'mainnets'},{'title':'RSK','basePath':'/rsk/mainnet','group':'mainnets'},{'title':'Gnosis Chain Testnet','basePath':'/xdai/testnet','group':'testnets'},{'title':'POA Sokol','basePath':'/poa/sokol','group':'testnets'},{'title':'ARTIS Σ1','basePath':'/artis/sigma1','group':'other'},{'title':'LUKSO L14','basePath':'/lukso/l14','group':'other'},{'title':'Astar','basePath':'/astar','group':'other'}]
NEXT_PUBLIC_API_ENDPOINT=https://blockscout.com # api config
NEXT_PUBLIC_API_BASE_PATH=/poa/core NEXT_PUBLIC_API_BASE_PATH=/poa/core
const BASE_PATH = require('../../lib/link/basePath.js');
const PATHS = require('../../lib/link/paths.js');
const oldUrls = [
{
oldPath: '/account/tag_address',
newPath: `${ PATHS.private_tags }?tab=address`,
},
{
oldPath: '/account/tag_transaction',
newPath: `${ PATHS.private_tags }?tab=tx`,
},
{
oldPath: '/pending-transactions',
newPath: `${ PATHS.txs }?tab=pending`,
},
{
oldPath: '/tx/:id/internal-transactions',
newPath: `${ PATHS.tx }?tab=internal`,
},
{
oldPath: '/tx/:id/logs',
newPath: `${ PATHS.tx }?tab=logs`,
},
{
oldPath: '/tx/:id/raw-trace',
newPath: `${ PATHS.tx }?tab=raw_trace`,
},
{
oldPath: '/tx/:id/state',
newPath: `${ PATHS.tx }?tab=state`,
},
{
oldPath: '/uncles',
newPath: `${ PATHS.blocks }?tab=uncles`,
},
{
oldPath: '/reorgs',
newPath: `${ PATHS.blocks }?tab=reorgs`,
},
{
oldPath: '/block/:id/transactions',
newPath: `${ PATHS.block }?tab=txs`,
},
];
async function redirects() { async function redirects() {
const homePagePath = '/' + [ process.env.NEXT_PUBLIC_NETWORK_TYPE, process.env.NEXT_PUBLIC_NETWORK_SUBTYPE ].filter(Boolean).join('/'); const homePagePath = '/' + [ process.env.NEXT_PUBLIC_NETWORK_TYPE, process.env.NEXT_PUBLIC_NETWORK_SUBTYPE ].filter(Boolean).join('/');
return [ return [
{ {
source: '/', source: '/',
destination: homePagePath, destination: homePagePath,
permanent: false, permanent: false,
}, },
...oldUrls.map(item => {
const source = BASE_PATH.replaceAll('[', ':').replaceAll(']', '') + item.oldPath;
const destination = item.newPath.replaceAll('[', ':').replaceAll(']', '');
return { source, destination, permanent: false };
}),
]; ];
} }
......
[ [
{ {
"chainIds": [ "chainIds": [
1 "1"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "easy-staking", "id": "easy-staking",
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "curve", "id": "curve",
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
1 "1"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "honwyswap", "id": "honwyswap",
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "sushi", "id": "sushi",
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
100 "100"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "bao-finance", "id": "bao-finance",
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "component", "id": "component",
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
1 "1"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "pooltogether", "id": "pooltogether",
...@@ -141,7 +141,7 @@ ...@@ -141,7 +141,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "swapr", "id": "swapr",
...@@ -161,7 +161,7 @@ ...@@ -161,7 +161,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
1 "1"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "levinswap", "id": "levinswap",
...@@ -181,7 +181,7 @@ ...@@ -181,7 +181,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "omen", "id": "omen",
...@@ -201,7 +201,7 @@ ...@@ -201,7 +201,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
1 "1"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "nifty-ink", "id": "nifty-ink",
...@@ -221,7 +221,7 @@ ...@@ -221,7 +221,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "treasure-chess", "id": "treasure-chess",
...@@ -241,7 +241,7 @@ ...@@ -241,7 +241,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
1 "1"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "unique-one", "id": "unique-one",
...@@ -261,7 +261,7 @@ ...@@ -261,7 +261,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "cold-truth-culture", "id": "cold-truth-culture",
...@@ -281,7 +281,7 @@ ...@@ -281,7 +281,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
1 "1"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "xdai-bridge", "id": "xdai-bridge",
...@@ -301,7 +301,7 @@ ...@@ -301,7 +301,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "omni-bridge", "id": "omni-bridge",
...@@ -321,7 +321,7 @@ ...@@ -321,7 +321,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
1 "1"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "gnosis-safe", "id": "gnosis-safe",
...@@ -341,7 +341,7 @@ ...@@ -341,7 +341,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "multisender", "id": "multisender",
...@@ -361,7 +361,7 @@ ...@@ -361,7 +361,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
1 "1"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "disperse", "id": "disperse",
...@@ -381,7 +381,7 @@ ...@@ -381,7 +381,7 @@
}, },
{ {
"chainIds": [ "chainIds": [
99 "99"
], ],
"author": "xDaichain", "author": "xDaichain",
"id": "symmetric", "id": "symmetric",
......
/* eslint-disable max-len */
export const tx = {
hash: '0x1ea365d2144796f793883534aa51bf20d23292b19478994eede23dfc599e7c34',
status: 'ok' as Transaction['status'],
block_num: 15006918,
confirmation_num: 283,
confirmation_duration: 30,
timestamp: 1662623567695,
address_from: {
hash: '0x97Aa2EfcF35c0f4c9AaDDCa8c2330fa7A9533830',
type: 'Address',
alias: '',
},
address_to: {
hash: '0x35317007D203b8a86CA727ad44E473E40450E378',
type: 'Contract',
alias: '',
},
amount: {
value: 0.03,
value_usd: 35.5,
},
fee: {
value: 0.002395904453623692,
value_usd: 2.84,
},
gas_price: 0.000000017716513811,
gas_limit: 208420,
gas_used: 159319,
gas_fees: {
base: 13.538410068,
max: 20.27657523,
max_priority: 1.5,
},
burnt_fees: {
value: 0.002156925953623692,
value_usd: 2.55,
},
type: {
value: '2',
eip: 'EIP-1559',
},
nonce: 4,
position: 342,
input_hex: '0x42842e0e0000000000000000000000007767dac225a233ea1055d79fb227b1696d538b75000000000000000000000000fc3017c31fe752fc48e904050ea5d6edfc38a1b00000000000000000000000000000000000000000000000000000000000000e3b',
transferred_tokens: [
{ from: '0x12E80C27BfFBB76b4A8d26FF2bfd3C9f310FFA01', to: '0xF7A558692dFB5F456e291791da7FAE8Dd046574e', token: { symbol: 'VIK', hash: '0xADFE00d92e5A16e773891F59780e6e54f40B532e', name: 'Viktor Coin' }, amount: 192.7, usd: 194.05 },
{ from: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45', to: '0x12E80C27BfFBB76b4A8d26FF2bfd3C9f310FFA01', token: { symbol: 'PAO', hash: '0xC98a06220239818B086CD96756d4E3bC41EC848f', name: 'POA Candy' }, amount: 76.1851851851846, usd: 194.05 },
],
txType: 'transaction' as TxType,
};
export type TxType = 'contract-call' | 'transaction' | 'token-transfer' | 'internal-tx' | 'multicall';
import type { Transaction } from 'types/api/transaction';
import type { Transaction } from 'types/api/transaction';
import { tx } from './tx';
import type { TxType } from './tx';
export const txs = [
{
...tx,
method: 'Withdraw',
txType: 'transaction' as TxType,
errorText: '',
},
{
...tx,
status: 'error' as Transaction['status'],
errorText: 'Error: (Awaiting internal transactions for reason)',
txType: 'contract-call' as TxType,
method: 'CommitHash CommitHash CommitHash CommitHash',
amount: {
value: 0.04,
value_usd: 35.5,
},
fee: {
value: 0.002295904453623692,
value_usd: 2.84,
},
},
{
...tx,
status: null,
txType: 'token-transfer' as TxType,
method: 'Multicall',
address_from: {
hash: '0x97Aa2EfcF35c0f4c9AaDDCa8c2330fa7A9533830',
alias: 'tkdkdkdkdkdkdkdkdkdkdkdkdkdkd.eth',
type: 'ENS name',
},
amount: {
value: 0.02,
value_usd: 35.5,
},
fee: {
value: 0.002495904453623692,
value_usd: 2.84,
},
errorText: '',
},
];
...@@ -23,10 +23,10 @@ function replace_envs { ...@@ -23,10 +23,10 @@ function replace_envs {
envValue=$(env | grep "^$configName=" | grep -oe '[^=]*$'); envValue=$(env | grep "^$configName=" | grep -oe '[^=]*$');
# if config found # if config found
if [ -n "$configValue" ] && [ -n "$envValue" ]; then if [ -n "$configValue" ]; then
# replace all # replace all
echo "Replace: ${configValue} with: ${envValue}" echo "Replace: ${configValue} with: ${envValue}"
find $nextFolder \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#$configValue#$envValue#g" find $nextFolder \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#$configValue#${envValue-''}#g"
fi fi
done < $envFilename done < $envFilename
} }
......
...@@ -65,19 +65,21 @@ geth: ...@@ -65,19 +65,21 @@ geth:
frontend: frontend:
environment: environment:
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS: NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS:
_default: ENC[AES256_GCM,data:fhH2QYodAkC/fIi5yU7Ur567buyIqQeTNUgserFNPs31yUeySk+BItpt,iv:7T7o5yX0Mvt1bYD9Cb6LxW9qHX1nzA99ykejJ4/KdpY=,tag:lfNUdrHGwU3BtsSvrnccYA==,type:str] _default: ENC[AES256_GCM,data:IVCGBLqP7IdmbDe9UbIFvJSBD7+g52chKzakELt2XuHDp9JvC4E+7xxp,iv:bDHp2llHAqhgI5N8swQALSDc6X3S0JCsXbJnEEDDJOc=,tag:Z1WsJXkqtq79WydIgUiDiA==,type:str]
NEXT_PUBLIC_SENTRY_DSN: NEXT_PUBLIC_SENTRY_DSN:
_default: ENC[AES256_GCM,data:n/H2AH2n9ovn265iFbbrqeOOWS3s7FXgDv5FXJ2Dz133GuhTaqIU5psWjTraZ/Vh+dVM8M9zBLFfUanCWOz7cerq/QUoSVZjKvIvcyp6F072DGU=,iv:Co/pSR+U0vfkmWR+LDpxcQKDJl0WNbaEZihpzrRJcbc=,tag:HUiCX8WGJXWCDB59Y6iIbw==,type:str] _default: ENC[AES256_GCM,data:n/H2AH2n9ovn265iFbbrqeOOWS3s7FXgDv5FXJ2Dz133GuhTaqIU5psWjTraZ/Vh+dVM8M9zBLFfUanCWOz7cerq/QUoSVZjKvIvcyp6F072DGU=,iv:Co/pSR+U0vfkmWR+LDpxcQKDJl0WNbaEZihpzrRJcbc=,tag:HUiCX8WGJXWCDB59Y6iIbw==,type:str]
SENTRY_CSP_REPORT_URI: SENTRY_CSP_REPORT_URI:
_default: ENC[AES256_GCM,data:Hf4azYsGh2lysotK7afaHI85IaLBJeQmPlGE/lwokmX2eaQHVZJ/i5RsaoKoOCSiNyfAowr7P+6IBEC16BUTQMpbhYveBd7c/xjIsoomoHbTdoAdZA/QxNVpS4a2qVthruFudyT1BoZUHIGQ,iv:Mid+PfbslOyivrFSXopdeW96YvOcLP+g2RGcw1o7B98=,tag:IuPUsGdbZQodCMyi1DR04Q==,type:str] _default: ENC[AES256_GCM,data:Hf4azYsGh2lysotK7afaHI85IaLBJeQmPlGE/lwokmX2eaQHVZJ/i5RsaoKoOCSiNyfAowr7P+6IBEC16BUTQMpbhYveBd7c/xjIsoomoHbTdoAdZA/QxNVpS4a2qVthruFudyT1BoZUHIGQ,iv:Mid+PfbslOyivrFSXopdeW96YvOcLP+g2RGcw1o7B98=,tag:IuPUsGdbZQodCMyi1DR04Q==,type:str]
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: null
_default: ENC[AES256_GCM,data:6MLOmBMJoB+dYoW8L4JYslO3F5tSFzhrkI+6rGo1a51s9sXZa9c=,iv:126unfUWSieigaq4Zne8321tSYoNy3EHk/qwEodqgH8=,tag:6BlrNkck5yANO2rqECjkuQ==,type:str]
sops: sops:
kms: [] kms: []
gcp_kms: [] gcp_kms: []
azure_kv: [] azure_kv: []
hc_vault: [] hc_vault: []
age: [] age: []
lastmodified: "2022-10-10T10:14:07Z" lastmodified: "2022-10-14T12:09:11Z"
mac: ENC[AES256_GCM,data:eflai5xePeklnUiY+nkOHl0nEQ6UMrDS6COo6W24402u5w2S3A0XP7ZhgrWn4K+AogId2nxwwDt+CcsM9E2m8tIvWQeY7S1ywI9kY10NiTamQrho+b9J3ZPBQC1zfbskyrtLeb93h+utSXOs1lipKxBMiD609TboQNj6mzZWFMk=,iv:LR7XW6sDulFT102H5lr+e4+f6n1erBz8lgeyMOgrevk=,tag:2jVNlPm6dMckewseADrMiA==,type:str] mac: ENC[AES256_GCM,data:CnD+9hsC9ZyVhZPo+DXZfPH8svMuk50llaAm3JxgOlzhbJ4yp969WxLhZSORvj520b9geBPLZRU7ujLGiHKhrNzAK438LI2QttKQDt3WSbPwkIGDh/zuA201+gpT73awUNfMKCoHVjq4iQ6ty4KP/NCw1ZMcS/c1WVuRYE9RTl8=,iv:rl8eKiXwrBDjns2hiwJ6f28XyuhjH2soHeR1MBBu2Ig=,tag:vu5jGEPMkvmcl7m8huWl7g==,type:str]
pgp: pgp:
- created_at: "2022-09-14T13:42:28Z" - created_at: "2022-09-14T13:42:28Z"
enc: | enc: |
......
...@@ -249,7 +249,7 @@ scVerifier: ...@@ -249,7 +249,7 @@ scVerifier:
frontend: frontend:
enabled: true enabled: true
image: image:
_default: ghcr.io/blockscout/frontend:prerelease-5ca79e55 _default: ghcr.io/blockscout/frontend:main
replicas: replicas:
app: 1 app: 1
docker: docker:
...@@ -284,9 +284,9 @@ frontend: ...@@ -284,9 +284,9 @@ frontend:
NEXT_PUBLIC_APP_HOST: NEXT_PUBLIC_APP_HOST:
_default: localhost _default: localhost
NEXT_PUBLIC_APP_PORT: NEXT_PUBLIC_APP_PORT:
_default: 3000 _default: 80
NEXT_PUBLIC_BLOCKSCOUT_VERSION: NEXT_PUBLIC_BLOCKSCOUT_VERSION:
_default: v4.1.7-beta _default: v4.1.8-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK: NEXT_PUBLIC_FOOTER_GITHUB_LINK:
_default: https://github.com/blockscout/blockscout _default: https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK: NEXT_PUBLIC_FOOTER_TWITTER_LINK:
...@@ -298,7 +298,7 @@ frontend: ...@@ -298,7 +298,7 @@ frontend:
NEXT_PUBLIC_FOOTER_STAKING_LINK: NEXT_PUBLIC_FOOTER_STAKING_LINK:
_default: https://duneanalytics.com/maxaleks/xdai-staking _default: https://duneanalytics.com/maxaleks/xdai-staking
NEXT_PUBLIC_NETWORK_NAME: NEXT_PUBLIC_NETWORK_NAME:
_default: POA _default: Sokol
NEXT_PUBLIC_NETWORK_SHORT_NAME: NEXT_PUBLIC_NETWORK_SHORT_NAME:
_default: POA _default: POA
NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME: NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME:
...@@ -306,15 +306,16 @@ frontend: ...@@ -306,15 +306,16 @@ frontend:
NEXT_PUBLIC_NETWORK_TYPE: NEXT_PUBLIC_NETWORK_TYPE:
_default: poa _default: poa
NEXT_PUBLIC_NETWORK_SUBTYPE: NEXT_PUBLIC_NETWORK_SUBTYPE:
_default: core _default: sokol
NEXT_PUBLIC_NETWORK_ID: NEXT_PUBLIC_NETWORK_ID:
_default: 99 _default: 77
NEXT_PUBLIC_NETWORK_CURRENCY: NEXT_PUBLIC_NETWORK_CURRENCY:
_default: POA _default: SPOA
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED: NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED:
_default: 'true' _default: 'true'
NEXT_PUBLIC_FEATURED_NETWORKS: NEXT_PUBLIC_FEATURED_NETWORKS:
_default: "[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'},{'title':'Optimism on Gnosis Chain','basePath':'/xdai/optimism','group':'mainnets','icon':'https://www.fillmurray.com/60/60'},{'title':'Arbitrum on xDai','basePath':'/xdai/aox','group':'mainnets'},{'title':'Ethereum','basePath':'/eth/mainnet','group':'mainnets'},{'title':'Ethereum Classic','basePath':'/etx/mainnet','group':'mainnets'},{'title':'POA','basePath':'/poa/core','group':'mainnets'},{'title':'RSK','basePath':'/rsk/mainnet','group':'mainnets'},{'title':'Gnosis Chain Testnet','basePath':'/xdai/testnet','group':'testnets'},{'title':'POA Sokol','basePath':'/poa/sokol','group':'testnets'},{'title':'ARTIS Σ1','basePath':'/artis/sigma1','group':'other'},{'title':'LUKSO L14','basePath':'/lukso/l14','group':'other'},{'title':'Astar','basePath':'/astar','group':'other'}]" _default: "[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'},{'title':'Optimism on Gnosis Chain','basePath':'/xdai/optimism','group':'mainnets','icon':'https://www.fillmurray.com/60/60'},{'title':'Arbitrum on xDai','basePath':'/xdai/aox','group':'mainnets'},{'title':'Ethereum','basePath':'/eth/mainnet','group':'mainnets'},{'title':'Ethereum Classic','basePath':'/etx/mainnet','group':'mainnets'},{'title':'POA','basePath':'/poa/core','group':'mainnets'},{'title':'RSK','basePath':'/rsk/mainnet','group':'mainnets'},{'title':'Gnosis Chain Testnet','basePath':'/xdai/testnet','group':'testnets'},{'title':'POA Sokol','basePath':'/poa/sokol','group':'testnets'},{'title':'ARTIS Σ1','basePath':'/artis/sigma1','group':'other'},{'title':'LUKSO L14','basePath':'/lukso/l14','group':'other'},{'title':'Astar','basePath':'/astar','group':'other'}]"
NEXT_PUBLIC_API_ENDPOINT: NEXT_PUBLIC_API_ENDPOINT:
_default: https://blockscout.com _default: https://blockscout.com
NEXT_PUBLIC_API_BASE_PATH: / NEXT_PUBLIC_API_BASE_PATH:
_default: /
...@@ -65,19 +65,21 @@ geth: ...@@ -65,19 +65,21 @@ geth:
frontend: frontend:
environment: environment:
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS: NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS:
_default: ENC[AES256_GCM,data:fhH2QYodAkC/fIi5yU7Ur567buyIqQeTNUgserFNPs31yUeySk+BItpt,iv:7T7o5yX0Mvt1bYD9Cb6LxW9qHX1nzA99ykejJ4/KdpY=,tag:lfNUdrHGwU3BtsSvrnccYA==,type:str] _default: ENC[AES256_GCM,data:1SAbzZhCs/vzdftIX0WVLtImH27NJ6SwENee4uTu2p+ZyUso3nQCLUUm,iv:apyLxt2dQ5RN33ra1Q1sAy2cyplG9FSryksQru2ghlA=,tag:PVcCNt0bz1TfQewUebV5LA==,type:str]
NEXT_PUBLIC_SENTRY_DSN: NEXT_PUBLIC_SENTRY_DSN:
_default: ENC[AES256_GCM,data:n/H2AH2n9ovn265iFbbrqeOOWS3s7FXgDv5FXJ2Dz133GuhTaqIU5psWjTraZ/Vh+dVM8M9zBLFfUanCWOz7cerq/QUoSVZjKvIvcyp6F072DGU=,iv:Co/pSR+U0vfkmWR+LDpxcQKDJl0WNbaEZihpzrRJcbc=,tag:HUiCX8WGJXWCDB59Y6iIbw==,type:str] _default: ENC[AES256_GCM,data:n/H2AH2n9ovn265iFbbrqeOOWS3s7FXgDv5FXJ2Dz133GuhTaqIU5psWjTraZ/Vh+dVM8M9zBLFfUanCWOz7cerq/QUoSVZjKvIvcyp6F072DGU=,iv:Co/pSR+U0vfkmWR+LDpxcQKDJl0WNbaEZihpzrRJcbc=,tag:HUiCX8WGJXWCDB59Y6iIbw==,type:str]
SENTRY_CSP_REPORT_URI: SENTRY_CSP_REPORT_URI:
_default: ENC[AES256_GCM,data:Hf4azYsGh2lysotK7afaHI85IaLBJeQmPlGE/lwokmX2eaQHVZJ/i5RsaoKoOCSiNyfAowr7P+6IBEC16BUTQMpbhYveBd7c/xjIsoomoHbTdoAdZA/QxNVpS4a2qVthruFudyT1BoZUHIGQ,iv:Mid+PfbslOyivrFSXopdeW96YvOcLP+g2RGcw1o7B98=,tag:IuPUsGdbZQodCMyi1DR04Q==,type:str] _default: ENC[AES256_GCM,data:Hf4azYsGh2lysotK7afaHI85IaLBJeQmPlGE/lwokmX2eaQHVZJ/i5RsaoKoOCSiNyfAowr7P+6IBEC16BUTQMpbhYveBd7c/xjIsoomoHbTdoAdZA/QxNVpS4a2qVthruFudyT1BoZUHIGQ,iv:Mid+PfbslOyivrFSXopdeW96YvOcLP+g2RGcw1o7B98=,tag:IuPUsGdbZQodCMyi1DR04Q==,type:str]
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: null
_default: ENC[AES256_GCM,data:KVzLMvMQc6f37c+OWu7vpAdDYwKI048XVY7EVijS2zz6jRRFacg=,iv:LeWSYZeaPdBsOxcGcca8L1Rp3ilsR+R13icX8Q/VUBA=,tag:Bj6NTTqb3R8oxAprZ+7CVQ==,type:str]
sops: sops:
kms: [] kms: []
gcp_kms: [] gcp_kms: []
azure_kv: [] azure_kv: []
hc_vault: [] hc_vault: []
age: [] age: []
lastmodified: "2022-10-10T10:14:07Z" lastmodified: "2022-10-14T12:08:39Z"
mac: ENC[AES256_GCM,data:eflai5xePeklnUiY+nkOHl0nEQ6UMrDS6COo6W24402u5w2S3A0XP7ZhgrWn4K+AogId2nxwwDt+CcsM9E2m8tIvWQeY7S1ywI9kY10NiTamQrho+b9J3ZPBQC1zfbskyrtLeb93h+utSXOs1lipKxBMiD609TboQNj6mzZWFMk=,iv:LR7XW6sDulFT102H5lr+e4+f6n1erBz8lgeyMOgrevk=,tag:2jVNlPm6dMckewseADrMiA==,type:str] mac: ENC[AES256_GCM,data:esHv2aUvW4lMmoD5yRWu4OJEpOMkCa7TOyPS0HDkQL25g4TOdE+AfVZBE5wLeL1rePaId4bHnX0sF2Tov0d8xhCH3mv6+Vvmgi+75Oqu8+logQ4LyZSI0yIcvmdGVHhaO6u3u1qwYXHrityIVmiXQdBck5oq67uyT+jtSh1pXpc=,iv:YNWhRxSY0WLRm+wbVURpfxU2K67MAB5dpSILSMy9oCE=,tag:TsCcsGmMG4id5ITR5LrDjg==,type:str]
pgp: pgp:
- created_at: "2022-09-14T13:42:28Z" - created_at: "2022-09-14T13:42:28Z"
enc: | enc: |
......
...@@ -249,7 +249,7 @@ scVerifier: ...@@ -249,7 +249,7 @@ scVerifier:
frontend: frontend:
enabled: true enabled: true
image: image:
_default: ghcr.io/blockscout/frontend:prerelease-5ca79e55 _default: ghcr.io/blockscout/frontend:main
replicas: replicas:
app: 1 app: 1
docker: docker:
...@@ -284,7 +284,7 @@ frontend: ...@@ -284,7 +284,7 @@ frontend:
NEXT_PUBLIC_APP_HOST: NEXT_PUBLIC_APP_HOST:
_default: localhost _default: localhost
NEXT_PUBLIC_APP_PORT: NEXT_PUBLIC_APP_PORT:
_default: 3000 _default: 80
NEXT_PUBLIC_BLOCKSCOUT_VERSION: NEXT_PUBLIC_BLOCKSCOUT_VERSION:
_default: v4.1.8-beta _default: v4.1.8-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK: NEXT_PUBLIC_FOOTER_GITHUB_LINK:
...@@ -298,7 +298,7 @@ frontend: ...@@ -298,7 +298,7 @@ frontend:
NEXT_PUBLIC_FOOTER_STAKING_LINK: NEXT_PUBLIC_FOOTER_STAKING_LINK:
_default: https://duneanalytics.com/maxaleks/xdai-staking _default: https://duneanalytics.com/maxaleks/xdai-staking
NEXT_PUBLIC_NETWORK_NAME: NEXT_PUBLIC_NETWORK_NAME:
_default: POA _default: Sokol
NEXT_PUBLIC_NETWORK_SHORT_NAME: NEXT_PUBLIC_NETWORK_SHORT_NAME:
_default: POA _default: POA
NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME: NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME:
...@@ -306,15 +306,16 @@ frontend: ...@@ -306,15 +306,16 @@ frontend:
NEXT_PUBLIC_NETWORK_TYPE: NEXT_PUBLIC_NETWORK_TYPE:
_default: poa _default: poa
NEXT_PUBLIC_NETWORK_SUBTYPE: NEXT_PUBLIC_NETWORK_SUBTYPE:
_default: core _default: sokol
NEXT_PUBLIC_NETWORK_ID: NEXT_PUBLIC_NETWORK_ID:
_default: 99 _default: 77
NEXT_PUBLIC_NETWORK_CURRENCY: NEXT_PUBLIC_NETWORK_CURRENCY:
_default: POA _default: SPOA
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED: NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED:
_default: 'true' _default: 'true'
NEXT_PUBLIC_FEATURED_NETWORKS: NEXT_PUBLIC_FEATURED_NETWORKS:
_default: "[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'},{'title':'Optimism on Gnosis Chain','basePath':'/xdai/optimism','group':'mainnets','icon':'https://www.fillmurray.com/60/60'},{'title':'Arbitrum on xDai','basePath':'/xdai/aox','group':'mainnets'},{'title':'Ethereum','basePath':'/eth/mainnet','group':'mainnets'},{'title':'Ethereum Classic','basePath':'/etx/mainnet','group':'mainnets'},{'title':'POA','basePath':'/poa/core','group':'mainnets'},{'title':'RSK','basePath':'/rsk/mainnet','group':'mainnets'},{'title':'Gnosis Chain Testnet','basePath':'/xdai/testnet','group':'testnets'},{'title':'POA Sokol','basePath':'/poa/sokol','group':'testnets'},{'title':'ARTIS Σ1','basePath':'/artis/sigma1','group':'other'},{'title':'LUKSO L14','basePath':'/lukso/l14','group':'other'},{'title':'Astar','basePath':'/astar','group':'other'}]" _default: "[{'title':'Gnosis Chain','basePath':'/xdai/mainnet','group':'mainnets'},{'title':'Optimism on Gnosis Chain','basePath':'/xdai/optimism','group':'mainnets','icon':'https://www.fillmurray.com/60/60'},{'title':'Arbitrum on xDai','basePath':'/xdai/aox','group':'mainnets'},{'title':'Ethereum','basePath':'/eth/mainnet','group':'mainnets'},{'title':'Ethereum Classic','basePath':'/etx/mainnet','group':'mainnets'},{'title':'POA','basePath':'/poa/core','group':'mainnets'},{'title':'RSK','basePath':'/rsk/mainnet','group':'mainnets'},{'title':'Gnosis Chain Testnet','basePath':'/xdai/testnet','group':'testnets'},{'title':'POA Sokol','basePath':'/poa/sokol','group':'testnets'},{'title':'ARTIS Σ1','basePath':'/artis/sigma1','group':'other'},{'title':'LUKSO L14','basePath':'/lukso/l14','group':'other'},{'title':'Astar','basePath':'/astar','group':'other'}]"
NEXT_PUBLIC_API_ENDPOINT: NEXT_PUBLIC_API_ENDPOINT:
_default: https://blockscout.com _default: https://blockscout.com
NEXT_PUBLIC_API_BASE_PATH: / NEXT_PUBLIC_API_BASE_PATH:
_default: /
import BigNumber from 'bignumber.js';
export default function compareBns(value1: string | number, value2: string | number) {
const value1Bn = new BigNumber(value1);
const value2Bn = new BigNumber(value2);
if (value1Bn.isGreaterThan(value2Bn)) {
return 1;
}
if (value1Bn.isLessThan(value2Bn)) {
return -1;
}
return 0;
}
import BigNumber from 'bignumber.js';
import { WEI, GWEI } from 'lib/consts';
export default function getValueWithUnit(value: string | number, unit: 'wei' | 'gwei' | 'ether' = 'wei') {
let unitBn: BigNumber.Value;
switch (unit) {
case 'wei':
unitBn = WEI;
break;
case 'gwei':
unitBn = GWEI;
break;
default:
unitBn = new BigNumber(1);
}
const valueBn = new BigNumber(value);
const valueCurr = valueBn.dividedBy(unitBn);
return valueCurr;
}
const BASE_PATH = '/[network_type]/[network_sub_type]';
module.exports = BASE_PATH;
const BASE_PATH = require('./basePath');
const paths = {
network_index: `${ BASE_PATH }`,
watchlist: `${ BASE_PATH }/account/watchlist`,
private_tags: `${ BASE_PATH }/account/tag_address`,
public_tags: `${ BASE_PATH }/account/public_tags_request`,
api_keys: `${ BASE_PATH }/account/api_key`,
custom_abi: `${ BASE_PATH }/account/custom_abi`,
profile: `${ BASE_PATH }/auth/profile`,
txs: `${ BASE_PATH }/txs`,
tx: `${ BASE_PATH }/tx/[id]`,
blocks: `${ BASE_PATH }/blocks`,
block: `${ BASE_PATH }/block/[id]`,
tokens: `${ BASE_PATH }/tokens`,
token_index: `${ BASE_PATH }/token/[hash]`,
token_instance_item: `${ BASE_PATH }/token/[hash]/instance/[id]`,
address_index: `${ BASE_PATH }/address/[id]`,
address_contract_verification: `${ BASE_PATH }/address/[id]/contract_verifications/new`,
apps: `${ BASE_PATH }/apps`,
app_index: `${ BASE_PATH }/apps/[id]`,
search_results: `${ BASE_PATH }/search-results`,
other: `${ BASE_PATH }/search-results`,
// no slash required, it is correct
auth: `${ BASE_PATH }auth/auth0`,
};
module.exports = paths;
import appConfig from 'configs/app/config';
import PATHS from './paths.js';
export interface Route { export interface Route {
pattern: string; pattern: string;
crossNetworkNavigation?: boolean; // route will not change when switching networks crossNetworkNavigation?: boolean; // route will not change when switching networks
} }
import appConfig from 'configs/app/config';
export type RouteName = keyof typeof ROUTES; export type RouteName = keyof typeof ROUTES;
const BASE_PATH = '/[network_type]/[network_sub_type]';
export const ROUTES = { export const ROUTES = {
// NETWORK MAIN PAGE // NETWORK MAIN PAGE
network_index: { network_index: {
pattern: `${ BASE_PATH }`, pattern: PATHS.network_index,
crossNetworkNavigation: true, crossNetworkNavigation: true,
}, },
// ACCOUNT // ACCOUNT
watchlist: { watchlist: {
pattern: `${ BASE_PATH }/account/watchlist`, pattern: PATHS.watchlist,
}, },
private_tags: { private_tags: {
pattern: `${ BASE_PATH }/account/tag_address`, pattern: PATHS.private_tags,
}, },
public_tags: { public_tags: {
pattern: `${ BASE_PATH }/account/public_tags_request`, pattern: PATHS.public_tags,
}, },
api_keys: { api_keys: {
pattern: `${ BASE_PATH }/account/api_key`, pattern: PATHS.api_keys,
}, },
custom_abi: { custom_abi: {
pattern: `${ BASE_PATH }/account/custom_abi`, pattern: PATHS.custom_abi,
}, },
profile: { profile: {
pattern: `${ BASE_PATH }/auth/profile`, pattern: PATHS.profile,
}, },
// TRANSACTIONS // TRANSACTIONS
txs: { txs: {
pattern: `${ BASE_PATH }/txs`, pattern: PATHS.txs,
crossNetworkNavigation: true, crossNetworkNavigation: true,
}, },
tx: { tx: {
pattern: `${ BASE_PATH }/tx/[id]`, pattern: PATHS.tx,
}, },
// BLOCKS // BLOCKS
blocks: { blocks: {
pattern: `${ BASE_PATH }/blocks`, pattern: PATHS.blocks,
crossNetworkNavigation: true, crossNetworkNavigation: true,
}, },
block: { block: {
pattern: `${ BASE_PATH }/block/[id]`, pattern: PATHS.block,
}, },
// TOKENS // TOKENS
tokens: { tokens: {
pattern: `${ BASE_PATH }/tokens`, pattern: PATHS.tokens,
crossNetworkNavigation: true, crossNetworkNavigation: true,
}, },
token_index: { token_index: {
pattern: `${ BASE_PATH }/token/[hash]`, pattern: PATHS.token_index,
crossNetworkNavigation: true, crossNetworkNavigation: true,
}, },
token_instance_item: { token_instance_item: {
pattern: `${ BASE_PATH }/token/[hash]/instance/[id]`, pattern: PATHS.token_instance_item,
}, },
// ADDRESSES // ADDRESSES
address_index: { address_index: {
pattern: `${ BASE_PATH }/address/[id]`, pattern: PATHS.address_index,
crossNetworkNavigation: true, crossNetworkNavigation: true,
}, },
address_contract_verification: { address_contract_verification: {
pattern: `${ BASE_PATH }/address/[id]/contract_verifications/new`, pattern: PATHS.address_contract_verification,
crossNetworkNavigation: true, crossNetworkNavigation: true,
}, },
// APPS // APPS
apps: { apps: {
pattern: `${ BASE_PATH }/apps`, pattern: PATHS.apps,
}, },
app_index: { app_index: {
pattern: `${ BASE_PATH }/apps/[id]`, pattern: PATHS.app_index,
}, },
// SEARCH // SEARCH
search_results: { search_results: {
pattern: `${ BASE_PATH }/apps`, pattern: PATHS.search_results,
}, },
// ??? what URL will be here // ??? what URL will be here
other: { other: {
pattern: `${ BASE_PATH }/search-results`, pattern: PATHS.other,
}, },
// AUTH // AUTH
auth: { auth: {
// no slash required, it is correct pattern: PATHS.auth,
pattern: `${ BASE_PATH }auth/auth0`,
}, },
}; };
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
"build:vercel": "./node_modules/.bin/dotenv -e ./configs/envs/.env.poa_core -e ./configs/envs/.env.common next build", "build:vercel": "./node_modules/.bin/dotenv -e ./configs/envs/.env.poa_core -e ./configs/envs/.env.common next build",
"build:docker": "docker build --build-arg GIT_COMMIT_SHA=$(git rev-parse HEAD) -t blockscout ./", "build:docker": "docker build --build-arg GIT_COMMIT_SHA=$(git rev-parse HEAD) -t blockscout ./",
"start": "next start", "start": "next start",
"start:docker:poa_core": "docker run -p 3000:3000 --env-file ./configs/envs/.env.poa_core --env-file ./configs/envs/.env.common --env-file ./configs/envs/.env.secrets blockscout", "start:docker:poa_core": "docker run -p 3000:3000 --env-file ./configs/envs/.env.common --env-file ./configs/envs/.env.poa_core --env-file ./configs/envs/.env.secrets blockscout",
"lint:eslint": "./node_modules/.bin/eslint . --ext .js,.jsx,.ts,.tsx", "lint:eslint": "./node_modules/.bin/eslint . --ext .js,.jsx,.ts,.tsx",
"lint:tsc": "./node_modules/.bin/tsc -p ./tsconfig.json", "lint:tsc": "./node_modules/.bin/tsc -p ./tsconfig.json",
"prepare": "husky install", "prepare": "husky install",
......
import type { NextApiRequest } from 'next';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
const searchParams: Record<string, string> = {};
Object.entries(req.query).forEach(([ key, value ]) => {
searchParams[key] = Array.isArray(value) ? value.join(',') : (value || '');
});
const searchParamsStr = new URLSearchParams(searchParams).toString();
return `/v2/transactions/${ searchParamsStr ? '?' + searchParamsStr : '' }`;
};
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
...@@ -19,7 +19,7 @@ export interface Transaction { ...@@ -19,7 +19,7 @@ export interface Transaction {
from: AddressParam; from: AddressParam;
to: AddressParam; to: AddressParam;
created_contract: AddressParam; created_contract: AddressParam;
value: number; value: string;
fee: Fee; fee: Fee;
gas_price: number; gas_price: number;
type: number; type: number;
......
...@@ -26,7 +26,7 @@ export type AppItemPreview = { ...@@ -26,7 +26,7 @@ export type AppItemPreview = {
} }
export type AppItemOverview = AppItemPreview & { export type AppItemOverview = AppItemPreview & {
chainIds: Array<number>; chainIds: Array<string>;
author: string; author: string;
url: string; url: string;
description: string; description: string;
......
export type Unit = 'wei' | 'gwei' | 'ether';
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import TxsContent from 'ui/txs/TxsContent'; import TxsContent from 'ui/txs/TxsContent';
const BlockTxs = () => { const BlockTxs = () => {
return <TxsContent showDescription={ false } showSortButton={ false }/>; return <TxsContent showDescription={ false } showSortButton={ false } txs={ [] }/>;
}; };
export default BlockTxs; export default BlockTxs;
...@@ -30,7 +30,7 @@ const MarketplaceApp = ({ app, isLoading }: Props) => { ...@@ -30,7 +30,7 @@ const MarketplaceApp = ({ app, isLoading }: Props) => {
useEffect(() => { useEffect(() => {
if (app && !isFrameLoading) { if (app && !isFrameLoading) {
ref?.current?.contentWindow?.postMessage({ blockscoutColorMode: colorMode, blockscoutChainId: appConfig.network.id }, app.url); ref?.current?.contentWindow?.postMessage({ blockscoutColorMode: colorMode, blockscoutChainId: Number(appConfig.network.id) }, app.url);
} }
}, [ isFrameLoading, app, colorMode, ref ]); }, [ isFrameLoading, app, colorMode, ref ]);
...@@ -38,7 +38,7 @@ const MarketplaceApp = ({ app, isLoading }: Props) => { ...@@ -38,7 +38,7 @@ const MarketplaceApp = ({ app, isLoading }: Props) => {
<Page wrapChildren={ false }> <Page wrapChildren={ false }>
<Center <Center
as="main" as="main"
h="100%" h="100vh"
paddingTop={{ base: '138px', lg: 0 }} paddingTop={{ base: '138px', lg: 0 }}
> >
{ (isFrameLoading) && ( { (isFrameLoading) && (
......
...@@ -2,45 +2,47 @@ import { Box, Text, chakra } from '@chakra-ui/react'; ...@@ -2,45 +2,47 @@ import { Box, Text, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import { WEI, GWEI } from 'lib/consts'; import type { Unit } from 'types/unit';
import getValueWithUnit from 'lib/getValueWithUnit';
interface Props { interface Props {
value: string; value: string;
unit?: 'wei' | 'gwei' | 'ether'; unit?: Unit;
currency?: string; currency?: string;
exchangeRate?: string; exchangeRate?: string | null;
className?: string; className?: string;
accuracy?: number; accuracy?: number;
accuracyUsd?: number; accuracyUsd?: number;
} }
const CurrencyValue = ({ value, currency = '', unit = 'wei', exchangeRate, className, accuracy, accuracyUsd }: Props) => { const CurrencyValue = ({ value, currency = '', unit, exchangeRate, className, accuracy, accuracyUsd }: Props) => {
let unitBn: BigNumber.Value; const valueCurr = getValueWithUnit(value, unit);
switch (unit) { const valueResult = accuracy ? valueCurr.dp(accuracy).toFormat() : valueCurr.toFormat();
case 'wei':
unitBn = WEI;
break;
case 'gwei':
unitBn = GWEI;
break;
default:
unitBn = new BigNumber(1);
}
const valueBn = new BigNumber(value); let usdContent;
const valueCurr = valueBn.dividedBy(unitBn); if (exchangeRate !== undefined && exchangeRate !== null) {
const exchangeRateBn = new BigNumber(exchangeRate || 0); const exchangeRateBn = new BigNumber(exchangeRate);
const usdBn = valueCurr.times(exchangeRateBn); const usdBn = valueCurr.times(exchangeRateBn);
let usdResult: string;
if (accuracyUsd && !usdBn.isEqualTo(0)) {
const usdBnDp = usdBn.dp(accuracyUsd);
usdResult = usdBnDp.isEqualTo(0) ? usdBn.precision(accuracyUsd).toFormat() : usdBnDp.toFormat();
} else {
usdResult = usdBn.toFormat();
}
usdContent = (
<Text as="span" variant="secondary" fontWeight={ 400 }>(${ usdResult })</Text>
);
}
return ( return (
<Box as="span" className={ className } display="inline-flex" rowGap={ 3 } columnGap={ 1 }> <Box as="span" className={ className } display="inline-flex" rowGap={ 3 } columnGap={ 1 }>
<Text as="span"> <Text display="inline-block">
{ accuracy ? valueCurr.toFixed(accuracy) : valueCurr.toFixed() }{ currency ? ` ${ currency }` : '' } { valueResult }{ currency ? ` ${ currency }` : '' }
</Text> </Text>
{ exchangeRate !== undefined && exchangeRate !== null && { usdContent }
// TODO: mb need to implement rounding to the first significant digit
<Text as="span" variant="secondary" fontWeight={ 400 }>(${ accuracyUsd ? usdBn.toFixed(accuracyUsd) : usdBn.toFixed() })</Text>
}
</Box> </Box>
); );
}; };
......
...@@ -13,7 +13,7 @@ const SOCIAL_LINKS = [ ...@@ -13,7 +13,7 @@ const SOCIAL_LINKS = [
{ link: appConfig.footerLinks.twitter, icon: twIcon, label: 'Twitter link' }, { link: appConfig.footerLinks.twitter, icon: twIcon, label: 'Twitter link' },
{ link: appConfig.footerLinks.telegram, icon: tgIcon, label: 'Telegram link' }, { link: appConfig.footerLinks.telegram, icon: tgIcon, label: 'Telegram link' },
{ link: appConfig.footerLinks.staking, icon: statsIcon, label: 'Staking analytic link' }, { link: appConfig.footerLinks.staking, icon: statsIcon, label: 'Staking analytic link' },
].filter(({ link }) => link !== undefined); ].filter(({ link }) => link);
const VERSION_URL = `https://github.com/blockscout/blockscout/tree/${ appConfig.blockScoutVersion }`; const VERSION_URL = `https://github.com/blockscout/blockscout/tree/${ appConfig.blockScoutVersion }`;
...@@ -47,6 +47,7 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => { ...@@ -47,6 +47,7 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => {
fontSize="xs" fontSize="xs"
{ ...getDefaultTransitionProps({ transitionProperty: 'width' }) } { ...getDefaultTransitionProps({ transitionProperty: 'width' }) }
> >
{ SOCIAL_LINKS.length > 0 && (
<Stack direction={{ base: 'row', lg: isExpanded ? 'row' : 'column', xl: isCollapsed ? 'column' : 'row' }}> <Stack direction={{ base: 'row', lg: isExpanded ? 'row' : 'column', xl: isCollapsed ? 'column' : 'row' }}>
{ SOCIAL_LINKS.map(sl => { { SOCIAL_LINKS.map(sl => {
return ( return (
...@@ -56,11 +57,13 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => { ...@@ -56,11 +57,13 @@ const NavFooter = ({ isCollapsed, hasAccount }: Props) => {
); );
}) } }) }
</Stack> </Stack>
) }
<Box display={{ base: 'block', lg: isExpanded ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }}> <Box display={{ base: 'block', lg: isExpanded ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }}>
<Text variant="secondary" mb={ 8 }> <Text variant="secondary" mb={ 8 }>
Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks. Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks.
</Text> </Text>
<Text variant="secondary">Version: <Link href={ VERSION_URL } target="_blank">{ appConfig.blockScoutVersion }</Link></Text> { appConfig.blockScoutVersion &&
<Text variant="secondary">Version: <Link href={ VERSION_URL } target="_blank">{ appConfig.blockScoutVersion }</Link></Text> }
</Box> </Box>
</VStack> </VStack>
); );
......
...@@ -183,14 +183,14 @@ const TxDetails = () => { ...@@ -183,14 +183,14 @@ const TxDetails = () => {
title="Value" title="Value"
hint="Value sent in the native token (and USD) if applicable." hint="Value sent in the native token (and USD) if applicable."
> >
<CurrencyValue value={ String(data.value) } currency={ appConfig.network.currency } exchangeRate={ data.exchange_rate }/> <CurrencyValue value={ data.value } currency={ appConfig.network.currency } exchangeRate={ data.exchange_rate }/>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Transaction fee" title="Transaction fee"
hint="Total transaction fee." hint="Total transaction fee."
> >
<CurrencyValue <CurrencyValue
value={ String(data.fee.value) } value={ data.fee.value }
currency={ appConfig.network.currency } currency={ appConfig.network.currency }
exchangeRate={ data.exchange_rate } exchangeRate={ data.exchange_rate }
flexWrap="wrap" flexWrap="wrap"
......
import { Box, Heading, Text, Flex, Link, useColorModeValue } from '@chakra-ui/react'; import { Box, Heading, Text, Flex, Link, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import React from 'react'; import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement'; import type { Transaction } from 'types/api/transaction';
import type { txs } from 'data/txs'; import getValueWithUnit from 'lib/getValueWithUnit';
import { nbsp } from 'lib/html-entities';
import link from 'lib/link/link'; import link from 'lib/link/link';
import CurrencyValue from 'ui/shared/CurrencyValue';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization'; import Utilization from 'ui/shared/Utilization';
const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => { const TxAdditionalInfo = ({ tx }: { tx: Transaction }) => {
const sectionBorderColor = useColorModeValue('gray.200', 'whiteAlpha.200'); const sectionBorderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
const sectionProps = { const sectionProps = {
borderBottom: '1px solid', borderBottom: '1px solid',
...@@ -31,40 +32,54 @@ const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => { ...@@ -31,40 +32,54 @@ const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => {
<Box { ...sectionProps } mb={ 4 }> <Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Transaction fee</Text> <Text { ...sectionTitleProps }>Transaction fee</Text>
<Flex> <Flex>
<Text>{ tx.fee.value }{ nbsp }{ appConfig.network.currency }</Text> <CurrencyValue
<Text variant="secondary" ml={ 1 }>(${ tx.fee.value_usd.toFixed(2) })</Text> value={ tx.fee.value }
currency={ appConfig.network.currency }
exchangeRate={ tx.exchange_rate }
accuracyUsd={ 2 }
/>
</Flex> </Flex>
</Box> </Box>
{ tx.gas_used !== null && (
<Box { ...sectionProps } mb={ 4 }> <Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Gas limit & usage by transaction</Text> <Text { ...sectionTitleProps }>Gas limit & usage by transaction</Text>
<Flex> <Flex>
<Text>{ tx.gas_used.toLocaleString('en') }</Text> <Text>{ BigNumber(tx.gas_used).toFormat() }</Text>
<TextSeparator/> <TextSeparator/>
<Text>{ tx.gas_limit.toLocaleString('en') }</Text> <Text>{ BigNumber(tx.gas_limit).toFormat() }</Text>
<Utilization ml={ 4 } value={ tx.gas_used / tx.gas_limit }/> <Utilization ml={ 4 } value={ Number(BigNumber(tx.gas_used).dividedBy(BigNumber(tx.gas_limit)).toFixed(2)) }/>
</Flex> </Flex>
</Box> </Box>
) }
{ (tx.base_fee_per_gas !== null || tx.max_fee_per_gas !== null || tx.max_priority_fee_per_gas !== null) && (
<Box { ...sectionProps } mb={ 4 }> <Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Gas fees (Gwei)</Text> <Text { ...sectionTitleProps }>Gas fees (Gwei)</Text>
{ tx.base_fee_per_gas !== null && (
<Box> <Box>
<Text as="span" fontWeight="500">Base: </Text> <Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ tx.gas_fees.base }</Text> <Text fontWeight="600" as="span">{ getValueWithUnit(tx.base_fee_per_gas, 'gwei').toFormat() }</Text>
</Box> </Box>
) }
{ tx.max_fee_per_gas !== null && (
<Box> <Box>
<Text as="span" fontWeight="500">Max: </Text> <Text as="span" fontWeight="500">Max: </Text>
<Text fontWeight="600" as="span">{ tx.gas_fees.max }</Text> <Text fontWeight="600" as="span">{ getValueWithUnit(tx.max_fee_per_gas, 'gwei').toFormat() }</Text>
</Box> </Box>
) }
{ tx.max_priority_fee_per_gas !== null && (
<Box> <Box>
<Text as="span" fontWeight="500">Max priority: </Text> <Text as="span" fontWeight="500">Max priority: </Text>
<Text fontWeight="600" as="span">{ tx.gas_fees.max_priority }</Text> <Text fontWeight="600" as="span">{ getValueWithUnit(tx.max_priority_fee_per_gas, 'gwei').toFormat() }</Text>
</Box> </Box>
) }
</Box> </Box>
) }
<Box { ...sectionProps } mb={ 4 }> <Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Others</Text> <Text { ...sectionTitleProps }>Others</Text>
<Box> <Box>
<Text as="span" fontWeight="500">Txn type: </Text> <Text as="span" fontWeight="500">Txn type: </Text>
<Text fontWeight="600" as="span">{ tx.type.value }</Text> <Text fontWeight="600" as="span">{ tx.type }</Text>
<Text fontWeight="400" as="span" ml={ 1 } color="gray.500">({ tx.type.eip })</Text> { tx.type === 2 && <Text fontWeight="400" as="span" ml={ 1 } color="gray.500">(EIP-1559)</Text> }
</Box> </Box>
<Box> <Box>
<Text as="span" fontWeight="500">Nonce: </Text> <Text as="span" fontWeight="500">Nonce: </Text>
......
import { Box, HStack, Show } from '@chakra-ui/react'; import { Box, HStack, Show } from '@chakra-ui/react';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import type { TransactionsResponse } from 'types/api/transaction';
import type { Sort } from 'types/client/txs-sort'; import type { Sort } from 'types/client/txs-sort';
import { txs } from 'data/txs'; import compareBns from 'lib/bigint/compareBns';
import FilterButton from 'ui/shared/FilterButton'; import FilterButton from 'ui/shared/FilterButton';
import FilterInput from 'ui/shared/FilterInput'; import FilterInput from 'ui/shared/FilterInput';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
...@@ -13,11 +14,12 @@ import TxsListItem from './TxsListItem'; ...@@ -13,11 +14,12 @@ import TxsListItem from './TxsListItem';
import TxsTable from './TxsTable'; import TxsTable from './TxsTable';
type Props = { type Props = {
txs: TransactionsResponse['items'];
showDescription?: boolean; showDescription?: boolean;
showSortButton?: boolean; showSortButton?: boolean;
} }
const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => { const TxsContent = ({ showSortButton = true, showDescription = true, txs }: Props) => {
const [ sorting, setSorting ] = useState<Sort>(); const [ sorting, setSorting ] = useState<Sort>();
const [ sortedTxs, setSortedTxs ] = useState(txs); const [ sortedTxs, setSortedTxs ] = useState(txs);
...@@ -50,21 +52,21 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => ...@@ -50,21 +52,21 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) =>
useEffect(() => { useEffect(() => {
switch (sorting) { switch (sorting) {
case 'val-desc': case 'val-desc':
setSortedTxs([ ...txs ].sort((tx1, tx2) => tx1.amount.value - tx2.amount.value)); setSortedTxs([ ...txs ].sort((tx1, tx2) => compareBns(tx1.value, tx2.value)));
break; break;
case 'val-asc': case 'val-asc':
setSortedTxs([ ...txs ].sort((tx1, tx2) => tx2.amount.value - tx1.amount.value)); setSortedTxs([ ...txs ].sort((tx1, tx2) => compareBns(tx2.value, tx1.value)));
break; break;
case 'fee-desc': case 'fee-desc':
setSortedTxs([ ...txs ].sort((tx1, tx2) => tx1.fee.value - tx2.fee.value)); setSortedTxs([ ...txs ].sort((tx1, tx2) => compareBns(tx1.fee.value, tx2.fee.value)));
break; break;
case 'fee-asc': case 'fee-asc':
setSortedTxs([ ...txs ].sort((tx1, tx2) => tx2.fee.value - tx1.fee.value)); setSortedTxs([ ...txs ].sort((tx1, tx2) => compareBns(tx2.fee.value, tx1.fee.value)));
break; break;
default: default:
setSortedTxs(txs); setSortedTxs(txs);
} }
}, [ sorting ]); }, [ sorting, txs ]);
return ( return (
<> <>
...@@ -93,7 +95,7 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => ...@@ -93,7 +95,7 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) =>
placeholder="Search by addresses, hash, method..." placeholder="Search by addresses, hash, method..."
/> />
</HStack> </HStack>
<Show below="lg">{ sortedTxs.map(tx => <TxsListItem tx={ tx } key={ tx.hash }/>) }</Show> <Show below="lg"><Box>{ sortedTxs.map(tx => <TxsListItem tx={ tx } key={ tx.hash }/>) }</Box></Show>
<Show above="lg"><TxsTable txs={ sortedTxs } sort={ sort } sorting={ sorting }/></Show> <Show above="lg"><TxsTable txs={ sortedTxs } sort={ sort } sorting={ sorting }/></Show>
<Box mx={{ base: 0, lg: 6 }} my={{ base: 6, lg: 3 }}> <Box mx={{ base: 0, lg: 6 }} my={{ base: 6, lg: 3 }}>
<Pagination currentPage={ 1 }/> <Pagination currentPage={ 1 }/>
......
...@@ -13,12 +13,12 @@ import { ...@@ -13,12 +13,12 @@ import {
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import React from 'react'; import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement'; import type { Transaction } from 'types/api/transaction';
import type { txs } from 'data/txs';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import getValueWithUnit from 'lib/getValueWithUnit';
import link from 'lib/link/link'; import link from 'lib/link/link';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
...@@ -28,7 +28,7 @@ import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; ...@@ -28,7 +28,7 @@ import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton'; import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton';
import TxType from 'ui/txs/TxType'; import TxType from 'ui/txs/TxType';
const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { const TxsListItem = ({ tx }: {tx: Transaction}) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const iconColor = useColorModeValue('blue.600', 'blue.300'); const iconColor = useColorModeValue('blue.600', 'blue.300');
...@@ -36,11 +36,13 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -36,11 +36,13 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
return ( return (
<> <>
<Box width="100%" borderBottom="1px solid" borderColor={ borderColor } _first={{ borderTop: '1px solid', borderColor: { borderColor } }}> <Box width="100%" borderBottom="1px solid" borderColor={ borderColor } _first={{ borderTop: '1px solid', borderColor }}>
<Flex justifyContent="space-between" mt={ 4 }> <Flex justifyContent="space-between" mt={ 4 }>
<HStack> <HStack>
<TxType type={ tx.txType }/> { /* TODO: we don't recieve type from api */ }
<TxStatus status={ tx.status } errorText={ tx.errorText }/> { /* <TxType type={ tx.type }/> */ }
<TxType type="transaction"/>
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/>
</HStack> </HStack>
<TxAdditionalInfoButton onClick={ onOpen }/> <TxAdditionalInfoButton onClick={ onOpen }/>
</Flex> </Flex>
...@@ -65,6 +67,7 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -65,6 +67,7 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Flex> </Flex>
<Flex mt={ 3 }> <Flex mt={ 3 }>
<Text as="span" whiteSpace="pre">Method </Text> <Text as="span" whiteSpace="pre">Method </Text>
{ /* TODO: we don't recieve method from api */ }
<Text <Text
as="span" as="span"
variant="secondary" variant="secondary"
...@@ -72,19 +75,22 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -72,19 +75,22 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
whiteSpace="nowrap" whiteSpace="nowrap"
textOverflow="ellipsis" textOverflow="ellipsis"
> >
{ tx.method } { /* { tx.method } */ }
CommitHash
</Text> </Text>
</Flex> </Flex>
{ tx.block !== null && (
<Box mt={ 2 }> <Box mt={ 2 }>
<Text as="span">Block </Text> <Text as="span">Block </Text>
<Link href={ link('block', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link> <Link href={ link('block', { id: tx.block.toString() }) }>{ tx.block }</Link>
</Box> </Box>
) }
<Flex alignItems="center" height={ 6 } mt={ 6 }> <Flex alignItems="center" height={ 6 } mt={ 6 }>
<Address width="calc((100%-40px)/2)"> <Address width="calc((100%-40px)/2)">
<AddressIcon hash={ tx.address_from.hash }/> <AddressIcon hash={ tx.from.hash }/>
<AddressLink <AddressLink
hash={ tx.address_from.hash } hash={ tx.from.hash }
alias={ tx.address_from.alias } alias={ tx.from.name }
fontWeight="500" fontWeight="500"
ml={ 2 } ml={ 2 }
/> />
...@@ -96,10 +102,10 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -96,10 +102,10 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
color="gray.500" color="gray.500"
/> />
<Address width="calc((100%-40px)/2)"> <Address width="calc((100%-40px)/2)">
<AddressIcon hash={ tx.address_to.hash }/> <AddressIcon hash={ tx.to.hash }/>
<AddressLink <AddressLink
hash={ tx.address_to.hash } hash={ tx.to.hash }
alias={ tx.address_to.alias } alias={ tx.to.name }
fontWeight="500" fontWeight="500"
ml={ 2 } ml={ 2 }
/> />
...@@ -107,11 +113,11 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -107,11 +113,11 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Flex> </Flex>
<Box mt={ 2 }> <Box mt={ 2 }>
<Text as="span">Value { appConfig.network.currency } </Text> <Text as="span">Value { appConfig.network.currency } </Text>
<Text as="span" variant="secondary">{ tx.amount.value.toFixed(8) }</Text> <Text as="span" variant="secondary">{ getValueWithUnit(tx.value).toFormat() }</Text>
</Box> </Box>
<Box mt={ 2 } mb={ 3 }> <Box mt={ 2 } mb={ 3 }>
<Text as="span">Fee { appConfig.network.currency } </Text> <Text as="span">Fee { appConfig.network.currency } </Text>
<Text as="span" variant="secondary">{ tx.fee.value.toFixed(8) }</Text> <Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).toFormat() }</Text>
</Box> </Box>
</Box> </Box>
<Modal isOpen={ isOpen } onClose={ onClose } size="full"> <Modal isOpen={ isOpen } onClose={ onClose } size="full">
......
import { Show, Alert } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { TransactionsResponse } from 'types/api/transaction';
import useFetch from 'lib/hooks/useFetch';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxsContent from './TxsContent'; import TxsContent from './TxsContent';
import TxsSkeletonDesktop from './TxsSkeletonDesktop';
import TxsSkeletonMobile from './TxsSkeletonMobile';
const TxsValidated = () => {
const fetch = useFetch();
const { data, isLoading, isError } =
useQuery<unknown, unknown, TransactionsResponse>([ 'transactions_pending' ], async() => fetch('/api/transactions/?filter=pending'));
if (isError) {
return <DataFetchAlert/>;
}
if (isLoading) {
return (
<>
<Show below="lg"><TxsSkeletonMobile isPending/></Show>
<Show above="lg"><TxsSkeletonDesktop isPending/></Show>
</>
);
}
if (!data || !data.items) {
return <Alert>There are no transactions.</Alert>;
}
const TxsPending = () => { return <TxsContent txs={ data.items } showDescription={ false }/>;
return <TxsContent showDescription={ false }/>;
}; };
export default TxsPending; export default TxsValidated;
import { Skeleton, Flex } from '@chakra-ui/react';
import React from 'react';
import SkeletonTable from 'ui/shared/SkeletonTable';
const TxsInternalsSkeletonDesktop = ({ isPending }: {isPending?: boolean}) => {
return (
<>
{ !isPending && <Skeleton h={ 6 } w="100%" mb={ 12 }/> }
<Flex columnGap={ 3 } h={ 8 } mb={ 6 }>
<Skeleton w="78px"/>
<Skeleton w="360px"/>
</Flex>
<SkeletonTable columns={ [ '32px', '20%', '18%', '15%', '11%', '292px', '18%', '18%' ] }/>
</>
);
};
export default TxsInternalsSkeletonDesktop;
import { Skeleton, Flex, Box, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
const TxInternalsSkeletonMobile = ({ isPending }: {isPending?: boolean}) => {
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
return (
<>
{ !isPending && <Skeleton h={ 6 } w="100%" mb={ 12 }/> }
<Flex columnGap={ 3 } h={ 8 } mb={ 6 }>
<Skeleton w="36px" flexShrink={ 0 }/>
<Skeleton w="36px" flexShrink={ 0 }/>
<Skeleton w="100%"/>
</Flex>
<Box>
{ Array.from(Array(2)).map((item, index) => (
<Flex
key={ index }
flexDirection="column"
paddingBottom={ 3 }
paddingTop={ 4 }
borderTopWidth="1px"
borderColor={ borderColor }
_last={{
borderBottomWidth: '1px',
}}
>
<Flex h={ 6 }>
<Skeleton w="100px" mr={ 2 } h={ 6 }/>
<Skeleton w="100px" h={ 6 }/>
</Flex>
<Skeleton w="100%" h="30px" mt={ 3 }/>
<Skeleton w="50%" h={ 6 } mt={ 3 }/>
<Skeleton w="50%" h={ 6 } mt={ 2 }/>
<Skeleton w="100%" h={ 6 } mt={ 6 }/>
<Skeleton w="50%" h={ 6 } mt={ 2 }/>
<Skeleton w="50%" h={ 6 } mt={ 2 }/>
</Flex>
)) }
</Box>
</>
);
};
export default TxInternalsSkeletonMobile;
...@@ -2,15 +2,15 @@ import { Link, Table, Thead, Tbody, Tr, Th, TableContainer, Icon } from '@chakra ...@@ -2,15 +2,15 @@ import { Link, Table, Thead, Tbody, Tr, Th, TableContainer, Icon } from '@chakra
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction';
import type { Sort } from 'types/client/txs-sort'; import type { Sort } from 'types/client/txs-sort';
import type { txs as data } from 'data/txs';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import TxsTableItem from './TxsTableItem'; import TxsTableItem from './TxsTableItem';
type Props = { type Props = {
txs: typeof data; txs: Array<Transaction>;
sort: (field: 'val' | 'fee') => () => void; sort: (field: 'val' | 'fee') => () => void;
sorting: Sort; sorting: Sort;
} }
......
...@@ -18,15 +18,15 @@ import { ...@@ -18,15 +18,15 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement'; import type { Transaction } from 'types/api/transaction';
import type { txs } from 'data/txs';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import link from 'lib/link/link'; import link from 'lib/link/link';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import CurrencyValue from 'ui/shared/CurrencyValue';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
...@@ -34,23 +34,22 @@ import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton'; ...@@ -34,23 +34,22 @@ import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton';
import TxType from './TxType'; import TxType from './TxType';
const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { const TxsTableItem = ({ tx }: {tx: Transaction}) => {
const addressFrom = ( const addressFrom = (
<Address> <Address>
<Tooltip label={ tx.address_from.type }> <Tooltip label={ tx.from.implementation_name }>
<Box display="flex"><AddressIcon hash={ tx.address_from.hash }/></Box> <Box display="flex"><AddressIcon hash={ tx.from.hash }/></Box>
</Tooltip> </Tooltip>
<AddressLink hash={ tx.address_from.hash } alias={ tx.address_from.alias } fontWeight="500" ml={ 2 }/> <AddressLink hash={ tx.from.hash } alias={ tx.from.name } fontWeight="500" ml={ 2 }/>
</Address> </Address>
); );
const addressTo = ( const addressTo = (
<Address> <Address>
<Tooltip label={ tx.address_to.type }> <Tooltip label={ tx.to.implementation_name }>
<Box display="flex"> <AddressIcon hash={ tx.address_to.hash }/></Box> <Box display="flex"><AddressIcon hash={ tx.to.hash }/></Box>
</Tooltip> </Tooltip>
<AddressLink hash={ tx.address_to.hash } alias={ tx.address_to.alias } fontWeight="500" ml={ 2 }/> <AddressLink hash={ tx.to.hash } alias={ tx.to.name } fontWeight="500" ml={ 2 }/>
</Address> </Address>
); );
...@@ -77,8 +76,10 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -77,8 +76,10 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Td> </Td>
<Td> <Td>
<VStack alignItems="start"> <VStack alignItems="start">
<TxType type={ tx.txType }/> { /* TODO: we don't recieve type from api */ }
<TxStatus status={ tx.status } errorText={ tx.errorText }/> { /* <TxType type={ tx.type }/> */ }
<TxType type="transaction"/>
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/>
</VStack> </VStack>
</Td> </Td>
<Td> <Td>
...@@ -94,17 +95,26 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -94,17 +95,26 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</VStack> </VStack>
</Td> </Td>
<Td> <Td>
<TruncatedTextTooltip label={ tx.method }> { /* TODO: we don't recieve method from api */ }
{ /* <TruncatedTextTooltip label={ tx.method }>
<Tag <Tag
colorScheme={ tx.method === 'Multicall' ? 'teal' : 'gray' } colorScheme={ tx.method === 'Multicall' ? 'teal' : 'gray' }
> >
{ tx.method } { tx.method }
</Tag> </Tag>
</TruncatedTextTooltip> */ }
<TruncatedTextTooltip label="CommitHash">
<Tag
colorScheme="gray"
>
CommitHash
</Tag>
</TruncatedTextTooltip> </TruncatedTextTooltip>
</Td> </Td>
<Td> <Td>
<Link href={ link('block', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link> { tx.block && <Link href={ link('block', { id: tx.block.toString() }) }>{ tx.block }</Link> }
</Td> </Td>
{ /* TODO: fix "show" problem */ }
<Show above="xl"> <Show above="xl">
<Td> <Td>
{ addressFrom } { addressFrom }
...@@ -133,10 +143,10 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -133,10 +143,10 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Td> </Td>
</Show> </Show>
<Td isNumeric> <Td isNumeric>
{ tx.amount.value.toFixed(8) } <CurrencyValue value={ tx.value }/>
</Td> </Td>
<Td isNumeric> <Td isNumeric>
{ tx.fee.value.toFixed(8) } <CurrencyValue value={ tx.fee.value } accuracy={ 8 }/>
</Td> </Td>
</Tr> </Tr>
); );
......
import { Show, Alert } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { TransactionsResponse } from 'types/api/transaction';
import useFetch from 'lib/hooks/useFetch';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxsContent from './TxsContent'; import TxsContent from './TxsContent';
import TxsSkeletonDesktop from './TxsSkeletonDesktop';
import TxsSkeletonMobile from './TxsSkeletonMobile';
const TxsValidated = () => { const TxsValidated = () => {
return <TxsContent/>; const fetch = useFetch();
const { data, isLoading, isError } =
useQuery<unknown, unknown, TransactionsResponse>([ 'transactions_validated' ], async() => fetch('/api/transactions/?filter=validated'));
if (isError) {
return <DataFetchAlert/>;
}
if (isLoading) {
return (
<>
<Show below="lg"><TxsSkeletonMobile/></Show>
<Show above="lg"><TxsSkeletonDesktop/></Show>
</>
);
}
if (!data || !data.items) {
return <Alert>There are no transactions.</Alert>;
}
return <TxsContent txs={ data.items }/>;
}; };
export default TxsValidated; export default TxsValidated;
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