Compare commits

..
Author SHA1 Message Date
Bartłomiej Głownia ec1a9f8818 feat(ui-toolkit): add trading button component 2023-07-18 14:17:46 +02:00
Matthew Russell e64b38c9d2 feat: market selector changes 2023-07-14 19:02:49 +01:00
Matthew Russell 871ab350b9 fix: move default bg and text classes to body tag 2023-07-14 16:58:42 +01:00
Matthew Russell 4ed33a532b fix: add volume separator border 2023-07-14 16:50:47 +01:00
Matthew Russell a2caf407ee fix: dropdown-menu 2023-07-14 16:50:47 +01:00
Matthew Russell 11cfc0f52f chore: move settings to bottom 2023-07-14 16:50:47 +01:00
Matthew Russell e47b96e82b chore: tweak dark grayscale 2023-07-14 16:50:47 +01:00
Matthew Russell 0d0a4aa77a chore: adjust cdark colors 2023-07-14 16:50:47 +01:00
Matthew Russell 257643eec3 test: fix settings test 2023-07-14 16:50:47 +01:00
Matthew Russell 4c43c96c16 chore: re add init flag 2023-07-14 16:50:47 +01:00
Matthew Russell 8d6d6a9ad5 chore: make select only have additional padding to accomodate dropdown chevron 2023-07-14 16:50:46 +01:00
Matthew Russell 739523d945 chore: fix test after removal of side bar init flag 2023-07-14 16:50:46 +01:00
Matthew Russell 4b0b8ef6bc feat: remove init flag in sidebar store, use persist 2023-07-14 16:50:46 +01:00
Matthew Russell bea3421dc2 chore: fix build-spec 2023-07-14 16:50:46 +01:00
Matthew Russell f43995fbef fix: transfer tests 2023-07-14 16:50:46 +01:00
Matthew Russell cd6a51b571 chore: lint 2023-07-14 16:50:46 +01:00
Matthew Russell 25e998728e feat: reduce size of chevron dropdown 2023-07-14 16:50:46 +01:00
Matthew Russell cca938bf60 feat: adjust separators in header and sidebar 2023-07-14 16:50:46 +01:00
Matthew Russell 0c1a950363 feat: adjust sidebar layout 2023-07-14 16:50:46 +01:00
Matthew Russell d8dceb0f38 chore: reduce dropdown spacing 2023-07-14 16:50:46 +01:00
Matthew Russell 0c39c85fd3 chore: fix long text in default select element 2023-07-14 16:50:46 +01:00
Matthew Russell e3257d4da1 feat: adjust padding and add same scroll element for all sidebar children 2023-07-14 16:50:46 +01:00
Matthew Russell 7434b245ed chore: remove stray apostrophe in render 2023-07-14 16:50:46 +01:00
Matthew Russell 6077028e62 feat: align nav dropdowns 2023-07-14 16:50:46 +01:00
Matthew Russell d5e0637be7 feat: reduce tab font size 2023-07-14 16:50:46 +01:00
Matthew Russell 50301996f0 chore: disable some tests add wait 2023-07-14 16:50:46 +01:00
Matthew Russell 792267d3d0 test: disable flakey trades test and move mocks for transfer test 2023-07-14 16:50:46 +01:00
Matthew Russell f700bfdf95 chore: tidy up dstylesheet 2023-07-14 16:50:46 +01:00
Matthew Russell edce4c715a chore: lint 2023-07-14 16:50:46 +01:00
Matthew Russell 5d041a156a test: fix out of sync ethereum address with form 2023-07-14 16:50:46 +01:00
Matthew Russell 0dbf601809 test: e2e test updates 2023-07-14 16:50:46 +01:00
Matthew Russell 4f60740aac chore: lint 2023-07-14 16:50:46 +01:00
Matthew Russell ab484a56c1 test: fix node health tests 2023-07-14 16:50:46 +01:00
Matthew Russell 3ed1a2eb11 test: updates to ensure e2e tests run 2023-07-14 16:50:46 +01:00
Matthew Russell 24bb450d35 feat: ensure floating buttons stay within their container 2023-07-14 16:50:46 +01:00
Matthew Russell 9a0ac2ffc5 chore: lint 2023-07-14 16:50:46 +01:00
Matthew Russell 39e7a2bd56 fix: ts errors in test files 2023-07-14 16:50:46 +01:00
Matthew Russell b6d22958c7 chore: tidy up 2023-07-14 16:50:46 +01:00
Matthew Russell a3f4b02a37 test: add unit test for sidebar components 2023-07-14 16:50:46 +01:00
Matthew Russell 6dbb062ac3 feat: re org layout so that all pages get navbar 2023-07-14 16:50:46 +01:00
Matthew Russell 90b20bbe5d feat: update to use links from router config 2023-07-14 16:50:46 +01:00
Matthew Russell 77843771c9 fix: remove padding from toast container so no clash with indicator 2023-07-14 16:50:46 +01:00
Matthew Russell 6bd793f9c8 feat: add 0 delay duration for side bar buttons 2023-07-14 16:50:46 +01:00
Matthew Russell 6113f58d86 feat: add ticket icon 2023-07-14 16:50:46 +01:00
Matthew Russell fa218da00f feat: spacing for liquidity and portfolio pages 2023-07-14 16:50:46 +01:00
Matthew Russell abbbea8555 feat: move settings cog to top 2023-07-14 16:50:46 +01:00
Matthew Russell 7ae75fdb7d feat: move sidebar to right 2023-07-14 16:50:46 +01:00
Matthew Russell 46c856b600 feat: adjust table overlay bg color 2023-07-14 16:50:46 +01:00
Matthew Russell e63471a5b4 feat: adjust market selector 2023-07-14 16:50:46 +01:00
Matthew Russell a7d132f6f8 feat: adjust padding of sidebar content 2023-07-14 16:50:46 +01:00
Matthew Russell 8e60890b16 feat: add notification for transfer form 2023-07-14 16:50:46 +01:00
Matthew Russell 72f75281eb feat: make sidebar open by default for trading/portfolio 2023-07-14 16:50:46 +01:00
Matthew Russell 22cbf04800 feat: add titles to some sidebars 2023-07-14 16:50:46 +01:00
Matthew Russell 0927596209 feat: tidy up dialogs 2023-07-14 16:50:46 +01:00
Matthew Russell f11ea73ca6 chore: adjust layout to work for mobile 2023-07-14 16:50:46 +01:00
Matthew Russell 8dca8cae96 feat: add rounded border for panes 2023-07-14 16:50:46 +01:00
Matthew Russell 82dbc38716 feat: adjust tab indicator 2023-07-14 16:50:46 +01:00
Matthew Russell d5949abf07 feat: add console tooltip for sidebar 2023-07-14 16:50:46 +01:00
Matthew Russell 8098b6e68a feat: update colors 2023-07-14 16:50:46 +01:00
Matthew Russell 9601622a40 feat: set default view for trading and portfolio page 2023-07-14 16:50:46 +01:00
Matthew Russell 76e9856b5f test: fix market selector tests 2023-07-14 16:50:46 +01:00
Matthew Russell 571ab992bc test: fix node health tests 2023-07-14 16:50:46 +01:00
Matthew Russell e6fca8df44 feat: adjust node health text size 2023-07-14 16:50:46 +01:00
Matthew Russell 1901ea2e52 feat: adjust settings page to work for sidebar 2023-07-14 16:50:46 +01:00
Matthew Russell d049643baa feat: adjust padding in sidebar, colors for chart controls 2023-07-14 16:50:46 +01:00
Matthew Russell 5f487f40b0 feat: make appName and therefore logo for navigation component optional 2023-07-14 16:50:46 +01:00
Matthew Russell 24dea53e83 chore: re add logo for now 2023-07-14 16:50:46 +01:00
Matthew Russell 3957a295ad feat: adjust styles for small screens and add sidebar as overlay 2023-07-14 16:50:46 +01:00
Matthew Russell aed7033226 feat: tidy up header styles 2023-07-14 16:50:46 +01:00
Matthew Russell 65cbd7a60c feat: add spacing to portfolio page 2023-07-14 16:50:46 +01:00
Matthew Russell 36b83b731c feat: add market info 2023-07-14 16:50:46 +01:00
Matthew Russell cda4c3fa93 feat: adjust tabs and table styles add cog 2023-07-14 16:50:46 +01:00
Matthew Russell 6aa9388a59 chore: re add home to serve as redirect to default market 2023-07-14 16:50:46 +01:00
Matthew Russell e1440cd7f3 feat: adapt market-selector for popover 2023-07-14 16:50:46 +01:00
Matthew Russell c2ff06f4b9 chore: keep using dialogs along side sidebar for now 2023-07-14 16:50:45 +01:00
Matthew Russell e8bd19a348 feat: open transfer sidebar, adjust trade-grid 2023-07-14 16:50:45 +01:00
Matthew Russell c8607e6feb feat: refactor routing and add sidebar 2023-07-14 16:50:45 +01:00
2121 changed files with 52446 additions and 126376 deletions
+2 -5
View File
@@ -51,8 +51,7 @@
"ul": ["list"], "ul": ["list"],
"ol": ["list"] "ol": ["list"]
} }
], ]
"no-console": ["error", { "allow": ["warn", "error"] }]
} }
}, },
{ {
@@ -73,11 +72,9 @@
"error", "error",
{ {
"prefer": "type-imports", "prefer": "type-imports",
"disallowTypeAnnotations": true, "disallowTypeAnnotations": true
"fixStyle": "inline-type-imports"
} }
], ],
"@typescript-eslint/no-useless-constructor": 0,
"curly": ["error", "multi-line"] "curly": ["error", "multi-line"]
} }
}, },
-1
View File
@@ -1,4 +1,3 @@
* text eol=lf * text eol=lf
*.png binary *.png binary
*.ico binary *.ico binary
*.woff2 binary
-28
View File
@@ -1,28 +0,0 @@
---
name: Feature Epic
about: A template to capture and scope user requirements, high level process, and basic mockups for an upcoming feature as part of the initial core spec review process.
title: 'FEATURE EPIC: '
labels: feature-epic
---
## Core Feature
<Name>
## Tasks
- [ ] Define high level requirements
- [ ] Create basic mockups
- [ ] Update "API Requirements" in core spec
- [ ] Update "User-Interface Spec" in relevant front end repo
- [ ] Create detailed user stories using normal template
## High Level Requirements
## Basic Mockups
## Link to API Requirements in Core spec
## Link to User Interface Specs
## Linked User Stories
@@ -7,7 +7,7 @@ on:
jobs: jobs:
after-release: after-release:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
timeout-minutes: 45 timeout-minutes: 30
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -30,20 +30,18 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Wait for publish to complete
uses: lewagon/wait-on-check-action@v1.3.1
with:
ref: ${{ github.event.release.tag_name }}
check-name: '(CD) publish dist / trading'
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
- name: resolve ipfs hashes for release - name: resolve ipfs hashes for release
run: | run: |
echo "Name: ${{ github.event.release.name }}" echo "Name: ${{ github.event.release.name }}"
echo "Description: ${{ github.event.release.body }}" echo "Description: ${{ github.event.release.body }}"
echo "Tag: ${{ github.event.release.tag_name }}" echo "Tag: ${{ github.event.release.tag_name }}"
docker run --rm vegaprotocol/trading:mainnet cat /ipfs-hash > ipfs-hash commit="$(git rev-list -n 1 ${{ github.event.release.tag_name }})"
echo "Commit: $commit"
until docker pull vegaprotocol/trading:$commit; do
echo "Image not pushed yet, waiting 60 seconds"
sleep 60
done
docker run --rm vegaprotocol/trading:$commit cat /ipfs-hash > ipfs-hash
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
tar -xzf kubo.tgz tar -xzf kubo.tgz
export PATH="$PATH:$PWD/kubo" export PATH="$PATH:$PWD/kubo"
@@ -82,38 +80,3 @@ jobs:
https://${{ env.IPFS_V1 }}.ipfs.dweb.link/ https://${{ env.IPFS_V1 }}.ipfs.dweb.link/
https://${{ env.IPFS_V1 }}.ipfs.cf-ipfs.com/ https://${{ env.IPFS_V1 }}.ipfs.cf-ipfs.com/
ipfs://${{ env.IPFS_V0 }}/ ipfs://${{ env.IPFS_V0 }}/
- name: Ensure 'Released' label exists
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
REPO="${{ github.repository }}"
LABEL_EXIST=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/$REPO/labels/Released")
if [[ "$LABEL_EXIST" == *"Not Found"* ]]; then
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-X POST "https://api.github.com/repos/$REPO/labels" \
-d '{"name": "Released", "color": "FFFFFF"}'
fi
- name: Extract issues from release notes
id: extract-issues
run: |
ISSUES=$(echo "${{ github.event.release.body }}" | grep -o -E '#[0-9]+' | tr -d '#' | jq -R . | jq -cs .)
echo "Issues to label: $ISSUES"
echo "::set-output name=issue_numbers::$ISSUES"
- name: Add 'Released' label to issues
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ISSUE_NUMBERS="${{ steps.extract-issues.outputs.issue_numbers }}"
REPO="${{ github.repository }}"
for ISSUE in $(echo "$ISSUE_NUMBERS" | jq -r '.[]'); do
curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-X POST "https://api.github.com/repos/$REPO/issues/$ISSUE/labels" \
-d '{"labels": ["Released"]}'
done
+1 -1
View File
@@ -6,7 +6,7 @@ name: 'Add Issues To Project Board'
types: types:
- opened - opened
env: env:
GH_TOKEN: ${{ secrets.PROJECT_MANAGE_ACTION }} GH_TOKEN: ${{ secrets.GH_NEW_CARD_TO_PROJECT }}
PROJECT_ID: ${{ secrets.FRONT_END_PROJECT_ID }} PROJECT_ID: ${{ secrets.FRONT_END_PROJECT_ID }}
ISSUE_ID: ${{ github.event.issue.node_id }} ISSUE_ID: ${{ github.event.issue.node_id }}
USER: ${{ github.actor }} USER: ${{ github.actor }}
+154 -133
View File
@@ -5,17 +5,15 @@ on:
branches: branches:
- release/* - release/*
- develop - develop
- main
pull_request: pull_request:
types: types:
- opened - opened
- ready_for_review - ready_for_review
- reopened - reopened
- edited
- synchronize - synchronize
jobs: jobs:
node-modules: node-modules:
# All jobs depend on node_modules, so none should run if the PR is in draft
if: github.event.pull_request.draft == false
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
name: 'Cache yarn modules' name: 'Cache yarn modules'
steps: steps:
@@ -23,6 +21,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
ref: ${{ github.event.pull_request.head.sha || github.sha }} ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Cache node modules - name: Cache node modules
id: cache id: cache
uses: actions/cache@v3 uses: actions/cache@v3
@@ -34,21 +33,29 @@ jobs:
${{ runner.os }}-cache-node-modules- ${{ runner.os }}-cache-node-modules-
- name: Setup node - name: Setup node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
if: steps.cache.outputs.cache-hit != 'true' if: steps.cache.outputs.cache-hit != 'true'
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions # https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn cache: yarn
- name: yarn install - name: yarn install
if: steps.cache.outputs.cache-hit != 'true' if: steps.cache.outputs.cache-hit != 'true'
run: yarn install --pure-lockfile run: yarn install --pure-lockfile
lint-format: lint-pr-title:
timeout-minutes: 20 needs: node-modules
if: ${{ github.event_name == 'pull_request' }}
name: Verify PR title
uses: ./.github/workflows/lint-pr.yml
secrets: inherit
lint-test-build:
timeout-minutes: 60
needs: node-modules needs: node-modules
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
name: '(CI) lint + format check' name: '(CI) lint + unit test + build'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -57,7 +64,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha || github.sha }} ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions # https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
@@ -83,85 +90,132 @@ jobs:
- name: Build affected spec - name: Build affected spec
run: yarn nx affected --target=build-spec run: yarn nx affected --target=build-spec
test-affected:
timeout-minutes: 30
needs: build-sources
runs-on: ubuntu-22.04
name: 'run unit test of affected apps'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: yarn
- name: Cache node modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v3
with:
main-branch-name: develop
- name: Test affected - name: Test affected
run: yarn nx affected:test run: yarn nx affected:test
build-sources: - name: Build affected
timeout-minutes: 30 run: yarn nx affected:build || (yarn install && yarn nx affected:build)
needs: lint-format
runs-on: ubuntu-22.04
name: 'Build sources of affected apps'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: yarn
- name: Cache node modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v3
with:
main-branch-name: develop
# See affected apps # See affected apps
- name: See affected apps - name: See affected apps
run: | run: |
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )" branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
python3 tools/ci/check-affected.py --github-ref="${{ github.ref }}" --branch-slug="$branch_slug" --event-name="${{ github.event_name }}"
- name: Verify script result echo ">>>> debug"
run: | echo "NX_BASE: ${{ env.NX_BASE }}"
echo "Check outputs from script" echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo "projects: ${{ env.PROJECTS }}" echo "Affected: ${affected}"
echo "projects-e2e: ${{ env.PROJECTS_E2E }}" echo "Branch slug: ${branch_slug}"
echo "preview_governance: ${{ env.PREVIEW_GOVERNANCE }}" echo "Current ref: ${{ github.ref }}"
echo "preview_trading: ${{ env.PREVIEW_TRADING }}" echo ">>>> eof debug"
echo "preview_explorer: ${{ env.PREVIEW_EXPLORER }}"
echo "preview_tools: ${{ env.PREVIEW_TOOLS }}" projects_array=()
preview_governance="not deployed"
preview_trading="not deployed"
preview_explorer="not deployed"
preview_tools="not deployed"
# parse if affected is any of three main applications, if none - use all of them
if echo "$affected" | grep -q governance; then
echo "Governance is affected"
projects_array+=("governance")
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi
if echo "$affected" | grep -q trading; then
echo "Trading is affected"
projects_array+=("trading")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
fi
if echo "$affected" | grep -q explorer; then
echo "Explorer is affected"
projects_array+=("explorer")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
if [[ ${#projects_array[@]} -eq 0 ]]; then
projects_array=("governance" "trading" "explorer")
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
# applications parsed before this loop are applicable for running e2e-tests
projects_e2e_array=()
for project in "${projects_array[@]}"; do
projects_e2e_array+=("${project}-e2e")
done
# all applications below this loop are not applicable for running e2e-test
# check if pull request event to deploy tools
if [[ "${{ github.event_name }}" = "pull_request" ]]; then
if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected"
echo "Deploying tools on preview"
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
projects_array+=("multisig-signer")
fi
# those apps deploy only from develop to mainnet
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected"
echo "Deploying tools on s3"
projects_array+=("multisig-signer")
fi
if echo "$affected" | grep -q static; then
echo "static is affected"
echo "Deploying static on s3"
projects_array+=("static")
fi
if echo "$affected" | grep -q ui-toolkit; then
echo "ui-toolkit is affected"
echo "Deploying ui-toolkit on s3"
projects_array+=("ui-toolkit")
fi
fi
# if branch starts with release/ and ends with trading / governance or explorer - overwrite the array of affected projects with fixed single application
if [[ "${{ github.ref }}" == *release* ]]; then
echo ">> This is a relase branch"
case "${{ github.ref }}" in
*trading)
echo ">> Only trading will be deployed"
projects_array=(trading)
projects_e2e_array=(trading)
;;
*governance)
echo ">> Only governance will be deployed"
projects_array=(governance)
projects_e2e_array=(governance)
;;
*explorer)
echo ">> Only explorer will be deployed"
projects_array=(explorer)
projects_e2e_array=(explorer)
;;
*)
echo ">> All apps will be deployed"
;;
esac
fi
echo "Projects: ${projects_array[@]}"
echo "Projects E2E: ${projects_e2e_array[@]}"
projects_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_array[@]}")
projects_e2e_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_e2e_array[@]}")
echo PROJECTS_E2E=$projects_e2e_json >> $GITHUB_ENV
echo PROJECTS=$projects_json >> $GITHUB_ENV
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
echo PREVIEW_TOOLS=$preview_tools >> $GITHUB_ENV
- name: Build affected
run: yarn nx affected:build || (yarn install && yarn nx affected:build)
outputs: outputs:
projects: ${{ env.PROJECTS }} projects: ${{ env.PROJECTS }}
projects-e2e: ${{ env.PROJECTS_E2E }} projects-e2e: ${{ env.PROJECTS_E2E }}
@@ -170,62 +224,30 @@ jobs:
preview_explorer: ${{ env.PREVIEW_EXPLORER }} preview_explorer: ${{ env.PREVIEW_EXPLORER }}
preview_tools: ${{ env.PREVIEW_TOOLS }} preview_tools: ${{ env.PREVIEW_TOOLS }}
check-e2e-needed:
runs-on: ubuntu-latest
needs: build-sources
name: '(CI) check if e2e needed'
outputs:
run-tests: ${{ steps.check-test.outputs.e2e-needed }}
steps:
- name: Check branch
id: check-test
run: |
if [[ "${{ github.base_ref }}" == "develop" ]]; then
echo "e2e-needed=true" >> $GITHUB_OUTPUT
elif [[ "${{ github.base_ref }}" == "main" ]]; then
echo "e2e-needed=true" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "push" && "${{ github.ref_name }}" == *"release/"* ]]; then
echo "e2e-needed=true" >> $GITHUB_OUTPUT
else
echo "e2e-needed=false" >> $GITHUB_OUTPUT
fi
- name: Print result
run: |
echo "e2e-needed: ${{ steps.check-test.outputs.e2e-needed }}"
cypress: cypress:
needs: [build-sources, check-e2e-needed] needs: lint-test-build
name: '(CI) cypress' name: '(CI) cypress'
# if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }}
uses: ./.github/workflows/cypress-run.yml uses: ./.github/workflows/cypress-run.yml
secrets: inherit secrets: inherit
if: needs.check-e2e-needed.outputs.run-tests == 'true' && (contains(needs.build-sources.outputs.projects, 'governance') || contains(needs.build-sources.outputs.projects, 'explorer'))
with: with:
projects: ${{ needs.build-sources.outputs.projects-e2e }} projects: ${{ needs.lint-test-build.outputs.projects-e2e }}
tags: '@smoke' tags: '@smoke @regression'
console-e2e:
needs: [build-sources, check-e2e-needed]
name: '(CI) trading e2e python'
uses: ./.github/workflows/console-test-run.yml
secrets: inherit
if: needs.check-e2e-needed.outputs.run-tests == 'true' && contains(needs.build-sources.outputs.projects, 'trading')
with:
github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
publish-dist: publish-dist:
needs: build-sources needs: lint-test-build
name: '(CD) publish dist' name: '(CD) publish dist'
if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo') || github.event_name == 'push' }} if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo') || github.event_name == 'push' }}
uses: ./.github/workflows/publish-dist.yml uses: ./.github/workflows/publish-dist.yml
secrets: inherit secrets: inherit
with: with:
projects: ${{ needs.build-sources.outputs.projects }} projects: ${{ needs.lint-test-build.outputs.projects }}
dist-check: dist-check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- publish-dist - publish-dist
- build-sources - lint-test-build
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo' }} if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo' }}
timeout-minutes: 60 timeout-minutes: 60
name: '(CD) comment preview links' name: '(CD) comment preview links'
@@ -241,27 +263,27 @@ jobs:
run: | run: |
# https://stackoverflow.com/questions/3183444/check-for-valid-link-url # https://stackoverflow.com/questions/3183444/check-for-valid-link-url
regex='(https?|ftp|file)://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]' regex='(https?|ftp|file)://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
if [[ "${{ needs.build-sources.outputs.preview_governance }}" =~ $regex ]]; then if [[ "${{ needs.lint-test-build.outputs.preview_governance }}" =~ $regex ]]; then
until curl --insecure --location --fail "${{ needs.build-sources.outputs.preview_governance }}"; do until curl --insecure --location --fail "${{ needs.lint-test-build.outputs.preview_governance }}"; do
echo "waiting for governance preview: ${{ needs.build-sources.outputs.preview_governance }}" echo "waiting for governance preview: ${{ needs.lint-test-build.outputs.preview_governance }}"
sleep 5 sleep 5
done done
fi fi
if [[ "${{ needs.build-sources.outputs.preview_explorer }}" =~ $regex ]]; then if [[ "${{ needs.lint-test-build.outputs.preview_explorer }}" =~ $regex ]]; then
until curl --insecure --location --fail "${{ needs.build-sources.outputs.preview_explorer }}"; do until curl --insecure --location --fail "${{ needs.lint-test-build.outputs.preview_explorer }}"; do
echo "waiting for explorer preview: ${{ needs.build-sources.outputs.preview_explorer }}" echo "waiting for explorer preview: ${{ needs.lint-test-build.outputs.preview_explorer }}"
sleep 5 sleep 5
done done
fi fi
if [[ "${{ needs.build-sources.outputs.preview_trading }}" =~ $regex ]]; then if [[ "${{ needs.lint-test-build.outputs.preview_trading }}" =~ $regex ]]; then
until curl --insecure --location --fail "${{ needs.build-sources.outputs.preview_trading }}"; do until curl --insecure --location --fail "${{ needs.lint-test-build.outputs.preview_trading }}"; do
echo "waiting for trading preview: ${{ needs.build-sources.outputs.preview_trading }}" echo "waiting for trading preview: ${{ needs.lint-test-build.outputs.preview_trading }}"
sleep 5 sleep 5
done done
fi fi
if [[ "${{ needs.build-sources.outputs.preview_tools }}" =~ $regex ]]; then if [[ "${{ needs.lint-test-build.outputs.preview_tools }}" =~ $regex ]]; then
until curl --insecure --location --fail "${{ needs.build-sources.outputs.preview_tools }}"; do until curl --insecure --location --fail "${{ needs.lint-test-build.outputs.preview_tools }}"; do
echo "waiting for tools preview: ${{ needs.build-sources.outputs.preview_tools }}" echo "waiting for tools preview: ${{ needs.lint-test-build.outputs.preview_tools }}"
sleep 5 sleep 5
done done
fi fi
@@ -273,10 +295,10 @@ jobs:
issue-number: ${{ github.event.pull_request.number }} issue-number: ${{ github.event.pull_request.number }}
body: | body: |
Previews Previews
* governance: ${{ needs.build-sources.outputs.preview_governance }} * governance: ${{ needs.lint-test-build.outputs.preview_governance }}
* explorer: ${{ needs.build-sources.outputs.preview_explorer }} * explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
* trading: ${{ needs.build-sources.outputs.preview_trading }} * trading: ${{ needs.lint-test-build.outputs.preview_trading }}
* tools: ${{ needs.build-sources.outputs.preview_tools }} * tools: ${{ needs.lint-test-build.outputs.preview_tools }}
# Report single result at the end, to avoid mess with required checks in PR # Report single result at the end, to avoid mess with required checks in PR
cypress-check: cypress-check:
@@ -287,7 +309,6 @@ jobs:
steps: steps:
- run: | - run: |
result="${{ needs.cypress.result }}" result="${{ needs.cypress.result }}"
echo "Result: $result"
if [[ $result == "success" || $result == "skipped" ]]; then if [[ $result == "success" || $result == "skipped" ]]; then
exit 0 exit 0
else else
-29
View File
@@ -1,29 +0,0 @@
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#managing-caches
name: cleanup caches by a branch
on:
pull_request:
types:
- closed
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache
echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR
do
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
done
echo "Done"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
+35 -248
View File
@@ -1,272 +1,59 @@
name: (CI) Console tests name: console-test-run
on: on:
workflow_call: workflow_call:
inputs: inputs:
github-sha: github-sha:
required: true required: true
type: string type: string
workflow_dispatch:
inputs:
console-test-branch:
type: choice
description: 'main: v0.73.13, develop: v0.74.0'
options:
- main
- develop
jobs: jobs:
create-docker-image: console-test:
name: Create docker image for console-test
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
#----------------------------------------------
# check-out frontend-monorepo
#----------------------------------------------
- name: Checkout frontend-monorepo
uses: actions/checkout@v3
with:
ref: ${{ inputs.github-sha || github.sha }}
#----------------------------------------------
# cache node modules
#----------------------------------------------
- name: setup node
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: yarn
- name: Cache node modules
id: cache
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
# comment out "restore-keys" if you need to rebuild yarn from 0
restore-keys: |
${{ runner.os }}-cache-node-modules-
#----------------------------------------------
# install deps if cache missing
#----------------------------------------------
- name: yarn install
if: steps.cache.outputs.cache-hit != 'true'
run: yarn install --pure-lockfile
#----------------------------------------------
# build trading
#----------------------------------------------
- name: Build trading app
run: |
yarn env-cmd -f ./apps/trading/.env.stagnet1 yarn nx export trading
DIST_LOCATION=dist/apps/trading/exported
mv $DIST_LOCATION dist-result
tree dist-result
#----------------------------------------------
# export trading app docker image
#----------------------------------------------
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and export to local Docker
id: docker_build
uses: docker/build-push-action@v5
with:
context: .
file: docker/node-outside-docker.Dockerfile
load: true
build-args: |
APP=trading
ENV_NAME=stagnet1
tags: ci/trading:local
outputs: type=docker,dest=/tmp/console-image.tar
- name: Verify docker image created
run: |
echo ${{ steps.docker_build.outputs.digest }}
echo ${{ steps.docker_build.outputs.imageid }}
- name: Upload docker image for console-test usage
uses: actions/upload-artifact@v3
with:
name: console-image
path: /tmp/console-image.tar
console-test-branch:
name: Choose console-test branch to run on
runs-on: ubuntu-22.04
timeout-minutes: 5 timeout-minutes: 5
outputs: runs-on: self-hosted-runner
console-branch: ${{ steps.output-step.outputs.branch }}
steps: steps:
- name: Workflow dispatch input - name: Checkout console test repo
id: dispatch-step
if: github.event_name == 'workflow_dispatch'
run: echo "branch=${{ inputs.console-test-branch }}" >> $GITHUB_OUTPUT
- name: Print Workflow dispatch input
if: github.event_name == 'workflow_dispatch'
run: echo ${{ steps.dispatch-step.outputs.branch }}
- name: Workflow_call input
id: workflow_call-step
if: github.event_name != 'workflow_dispatch'
run: |
if [[ "${{ github.base_ref }}" == "main" ]]; then
echo "branch=main" >> $GITHUB_OUTPUT
elif [[ "${{ github.base_ref }}" == "develop" && "${{ github.ref_name }}" == "main" ]]; then
echo "branch=main" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "push" && "${{ github.ref_name }}" == *"release/mainnet"* ]]; then
echo "branch=main" >> $GITHUB_OUTPUT
else
echo "branch=develop" >> $GITHUB_OUTPUT
fi
- name: Print Workflow_call input
if: github.event_name != 'workflow_dispatch'
run: echo ${{ steps.workflow_call-step.outputs.branch }}
- name: Set output
id: output-step
run: echo "branch=${{ steps.dispatch-step.outputs.branch || steps.workflow_call-step.outputs.branch }}" >> $GITHUB_OUTPUT
- name: Print final output
run: echo ${{ steps.output-step.outputs.branch }}
run-tests:
name: run-tests
runs-on: 8-cores
needs: [create-docker-image, console-test-branch]
timeout-minutes: 45
steps:
#----------------------------------------------
# load docker image
#----------------------------------------------
- name: Download docker image from previous job
uses: actions/download-artifact@v3
with:
name: console-image
path: /tmp
- name: Load Docker image
run: |
docker load --input /tmp/console-image.tar
docker image ls -a
#----------------------------------------------
# check-out frontend-monorepo
#----------------------------------------------
- name: Checkout frontend-monorepo
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
ref: ${{ inputs.github-sha || github.sha }} repository: vegaprotocol/console-test
#---------------------------------------------- path: './console-test'
# get vega version
#---------------------------------------------- - name: Set up Python
- name: Set VEGA_VERSION from .env
id: set_vega_version
run: echo "VEGA_VERSION=$(grep VEGA_VERSION apps/trading/e2e/.env | cut -d '=' -f2)" >> $GITHUB_ENV
#----------------------------------------------
# ----- Setup python -----
#----------------------------------------------
- name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.10' python-version: '3.10.11'
#----------------------------------------------
# ----- install & configure poetry -----
#----------------------------------------------
- name: Install Poetry - name: Install Poetry
uses: snok/install-poetry@v1 uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
virtualenvs-path: .venv
#----------------------------------------------
# install python dependencies
#----------------------------------------------
- name: Install dependencies - name: Install dependencies
run: poetry install --no-interaction --no-root run: |
working-directory: apps/trading/e2e poetry install --no-root
#---------------------------------------------- working-directory: ./console-test
# install vega binaries
#---------------------------------------------- - name: load Binaries
- name: Install vega binaries run: |
run: poetry run python -m vega_sim.tools.load_binaries --force --version ${{ env.VEGA_VERSION }} poetry run python -m vega_sim.tools.load_binaries
working-directory: apps/trading/e2e working-directory: ./console-test
#----------------------------------------------
# install playwrightworking-directory: apps/trading/e2e - name: pull console
#---------------------------------------------- run: |
poetry run docker pull ghcr.io/vegaprotocol/frontend/trading:${{ inputs.github-sha }}
- name: Update container_name in config.py
run: |
sed -i "s/container_name = \".*\"/container_name = \"vegaprotocol\/frontend\/trading:${{ inputs.github-sha }}\"/g" config.py
- name: install playwright - name: install playwright
run: poetry run playwright install --with-deps chromium run: poetry run playwright install
working-directory: apps/trading/e2e working-directory: ./console-test
#----------------------------------------------
# run tests - name: run tests
#---------------------------------------------- run: poetry run pytest --numprocesses auto
- name: Run tests working-directory: ./console-test
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v --numprocesses 4 --dist loadfile --durations=45
working-directory: apps/trading/e2e
#----------------------------------------------
# upload traces
#----------------------------------------------
- name: Upload Playwright Trace - name: Upload Playwright Trace
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
name: playwright-trace name: playwright-trace
path: apps/trading/e2e/traces/ path: ./traces/
retention-days: 15
#----------------------------------------------
# ----- upload logs -----
#----------------------------------------------
- name: Upload worker logs
uses: actions/upload-artifact@v3
if: always()
with:
name: worker-logs
path: ./logs/
retention-days: 15
#----------------------------------------------
# ----- upload market-sim logs -----
#----------------------------------------------
- name: Prepare and Zip market-sim-logs
if: always()
run: |
parent_dir="/tmp/market-sim-logs"
echo "Creating parent directory at $parent_dir"
mkdir -p "$parent_dir"
echo "Waiting for vega-sim-* folders to be created..."
sleep 10 # Waits 10 seconds to ensure all folders are created
echo "Before searching for vega-sim-* folders in /tmp..."
folders=$(find /tmp -mindepth 1 -type d -name 'vega-sim-*' -print) || echo "Find command failed with exit code $?"
echo "After searching for vega-sim-* folders in /tmp..."
if [ -z "$folders" ]; then
echo "No vega-sim-* folders found."
exit 0
fi
echo "Moving vega-sim-* folders to $parent_dir"
echo "$folders" | xargs -I {} mv {} "$parent_dir/"
echo "Checking if $parent_dir is not empty..."
if [ "$(ls -A $parent_dir)" ]; then
echo "Zipping the parent directory..."
zip -r market-sim-logs.zip "$parent_dir" && echo "Zip file created successfully."
else
echo "$parent_dir is empty. No zip file created."
exit 0
fi
shell: /usr/bin/bash -e {0}
- name: Upload market-sim-logs
uses: actions/upload-artifact@v3
if: always()
with:
name: market-sim-logs
path: market-sim-logs.zip
retention-days: 15 retention-days: 15
+36
View File
@@ -0,0 +1,36 @@
name: Cypress Console tests -- live environment
# This workflow runs using provided url
on:
workflow_dispatch:
inputs:
url:
description: 'Url'
required: true
type: string
jobs:
cypress-run:
name: Run Cypress Trading tests -- live environment
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.15.1
- name: Run Cypress tests
uses: cypress-io/github-action@v4
with:
browser: chrome
record: true
project: ./apps/trading-e2e
config: baseUrl=${{ github.event.inputs.url }}
env: grepTags=@live
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -12,6 +12,7 @@ on:
options: options:
- explorer-e2e - explorer-e2e
- governance-e2e - governance-e2e
- trading-e2e
tags: tags:
description: 'Test tags to run' description: 'Test tags to run'
required: true required: true
+1 -1
View File
@@ -10,5 +10,5 @@ jobs:
uses: ./.github/workflows/cypress-run.yml uses: ./.github/workflows/cypress-run.yml
secrets: inherit secrets: inherit
with: with:
projects: '["explorer-e2e","governance-e2e"]' projects: '["explorer-e2e","governance-e2e","trading-e2e"]'
tags: '@smoke @regression @slow' tags: '@smoke @regression @slow'
+3 -36
View File
@@ -13,36 +13,14 @@ on:
type: string type: string
jobs: jobs:
runner-choice:
runs-on: ubuntu-latest
outputs:
runner: ${{ steps.step.outputs.runner }}
steps:
- name: Check branch
id: step
run: |
if [[ "${{ github.base_ref }}" == "main" ]]; then
echo "runner=mainnet-compatible-runner" >> $GITHUB_OUTPUT
elif [[ "${{ github.base_ref }}" == "develop" && "${{ github.ref_name }}" == "main" ]]; then
echo "runner=mainnet-compatible-runner" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "push" && "${{ github.ref_name }}" == *"release/mainnet"* ]]; then
echo "runner=mainnet-compatible-runner" >> $GITHUB_OUTPUT
else
echo "runner=self-hosted-runner" >> $GITHUB_OUTPUT
fi
- name: Print runner
run: echo ${{ steps.step.outputs.runner }}
e2e: e2e:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
project: ${{ fromJSON(inputs.projects) }} project: ${{ fromJSON(inputs.projects) }}
name: ${{ matrix.project }} name: ${{ matrix.project }}
needs: runner-choice runs-on: self-hosted-runner
runs-on: ${{ needs.runner-choice.outputs.runner }} timeout-minutes: 100
timeout-minutes: 120
steps: steps:
# Checks if skip cache was requested # Checks if skip cache was requested
- name: Set skip-nx-cache flag - name: Set skip-nx-cache flag
@@ -57,17 +35,8 @@ jobs:
path: './frontend-monorepo' path: './frontend-monorepo'
ref: ${{ github.event.pull_request.head.sha || github.sha }} ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node
uses: actions/setup-node@v4
with:
node-version-file: './frontend-monorepo/.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
cache-dependency-path: './frontend-monorepo/yarn.lock'
# Restore node_modules from cache if possible # Restore node_modules from cache if possible
- name: Restore node_modules from cache - name: Restore node_modules from cache
id: cache-node-modules
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
path: | path: |
@@ -77,7 +46,6 @@ jobs:
# Install frontend dependencies # Install frontend dependencies
- name: Install root dependencies - name: Install root dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile run: yarn install --frozen-lockfile
working-directory: frontend-monorepo working-directory: frontend-monorepo
@@ -93,14 +61,13 @@ jobs:
- name: Run Vegacapsule network and Vega wallet - name: Run Vegacapsule network and Vega wallet
id: setup-vega id: setup-vega
uses: ./frontend-monorepo/.github/actions/run-vegacapsule uses: ./frontend-monorepo/.github/actions/run-vegacapsule
timeout-minutes: 10
###### ######
## Run some tests ## Run some tests
###### ######
- name: Run Cypress tests - name: Run Cypress tests
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --browser chrome --env.grepTags="${{ inputs.tags }}" run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --browser chrome --env.grepTags="${{ inputs.tags }}"
working-directory: frontend-monorepo working-directory: frontend-monorepo
env: env:
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }} CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup node - name: Setup node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions # https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
+12 -12
View File
@@ -2,12 +2,7 @@
name: Verify PR title name: Verify PR title
on: on:
pull_request: workflow_call:
types:
- opened
- edited
- reopened
- synchronize
jobs: jobs:
lint_pr: lint_pr:
@@ -16,16 +11,21 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
- name: Install dependencies - name: Cache node modules
run: | uses: actions/cache@v3
rm package.json with:
npm install --no-save @commitlint/cli@16.3.0 @commitlint/config-conventional@18.6.1 @commitlint/config-nx-scopes@18.6.1 nx@17.1.2 path: node_modules
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
- name: Check PR title - name: Check PR title
run: echo "${{ github.event.pull_request.title }}" | npx @commitlint/cli@16.3.0 --config ./commitlint.config-ci.js run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
+63 -42
View File
@@ -28,19 +28,7 @@ jobs:
echo IS_MAINNET_RELEASE=false >> $GITHUB_ENV echo IS_MAINNET_RELEASE=false >> $GITHUB_ENV
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
echo IS_S3_RELEASE=false >> $GITHUB_ENV echo IS_S3_RELASE=false >> $GITHUB_ENV
echo IS_DEV_IMAGE=false >> $GITHUB_ENV
echo IS_MAIN_IMAGE=false >> $GITHUB_ENV
- name: Is dev image
if: ${{ github.ref_name == 'develop' && github.event_name == 'push' && matrix.app == 'trading' }}
run: |
echo IS_DEV_IMAGE=true >> $GITHUB_ENV
- name: Is main image
if: ${{ github.ref_name == 'main' && github.event_name == 'push' && matrix.app == 'trading' }}
run: |
echo IS_MAIN_IMAGE=true >> $GITHUB_ENV
- name: Is PR - name: Is PR
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
@@ -48,7 +36,7 @@ jobs:
echo IS_PR=true >> $GITHUB_ENV echo IS_PR=true >> $GITHUB_ENV
- name: Is mainnet release - name: Is mainnet release
if: ${{ contains(github.ref, 'release/mainnet') && !contains(github.ref, 'mirror') }} if: ${{ contains(github.ref, 'release/mainnnet') && !contains(github.ref, 'mirror') }}
run: | run: |
echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV
@@ -63,9 +51,9 @@ jobs:
echo IS_IPFS_RELEASE=true >> $GITHUB_ENV echo IS_IPFS_RELEASE=true >> $GITHUB_ENV
- name: Is S3 Release - name: Is S3 Release
if: ${{ env.IS_IPFS_RELEASE == 'false' && github.event_name == 'push' && github.ref_name != 'main'}} if: ${{ env.IS_IPFS_RELASE == 'false' && github.event_name == 'push' }}
run: | run: |
echo IS_S3_RELEASE=true >> $GITHUB_ENV echo IS_S3_RELASE=true >> $GITHUB_ENV
- name: Set up QEMU - name: Set up QEMU
id: quemu id: quemu
@@ -87,13 +75,13 @@ jobs:
- name: Log in to the Container registry (docker hub) - name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2 uses: docker/login-action@v2
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' || env.IS_MAIN_IMAGE == 'true' }} if: ${{ env.IS_IPFS_RELEASE == 'true' }}
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions # https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
@@ -109,13 +97,52 @@ jobs:
- name: Define dist variables - name: Define dist variables
if: ${{ github.event_name == 'push' }} if: ${{ github.event_name == 'push' }}
run: | run: |
python3 tools/ci/define-dist-variables.py --github-ref="${{ github.ref }}" --app="${{ matrix.app }}" envName=''
domain="vega.rocks"
bucketName=''
- name: Verify script result if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
if: ${{ github.event_name == 'push' }} # remove prefixing release/ and take the first string limited by - which is supposed to be name of the environment for releasing (format: release/testnet-trading)
run: | envName="$(echo ${{ github.ref }} | sed -e "s|refs/heads/release/||" | cut -d '-' -f 1 )"
echo "BUCKET_NAME=${{ env.BUCKET_NAME }}" elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
echo "ENV_NAME=${{ env.ENV_NAME }}" envName="stagnet1"
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then
envName="mainnet"
bucketName="tools.vega.xyz"
fi
if [[ "${{ matrix.app }}" = "static" ]]; then
envName="mainnet"
bucketName="static.vega.xyz"
fi
if [[ "${{ matrix.app }}" = "ui-toolkit" ]]; then
envName="mainnet"
bucketName="ui.vega.rocks"
fi
elif [[ "${{ github.ref }}" =~ .*mainnet$ ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${domain}"
fi
elif [[ "${envName}" = "testnet" ]]; then
domain="fairground.wtf"
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${domain}"
fi
fi
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${envName}.${domain}"
fi
echo "bucket name: ${bucketName}"
echo "env name: ${envName}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
echo ENV_NAME=${envName} >> $GITHUB_ENV
- name: Build local dist - name: Build local dist
run: | run: |
@@ -130,12 +157,8 @@ jobs:
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
NODE_ENV=production yarn nx run ui-toolkit:build-storybook NODE_ENV=production yarn nx run ui-toolkit:build-storybook
DIST_LOCATION=dist/storybook/ui-toolkit DIST_LOCATION=dist/storybook/ui-toolkit
elif [ "${{ matrix.app }}" = "static" ]; then
yarn nx build static || (yarn install && yarn nx build static)
else else
$envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }}) $envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }})
fi
if [[ -z "$DIST_LOCATION" ]]; then
DIST_LOCATION=dist/apps/${{ matrix.app }} DIST_LOCATION=dist/apps/${{ matrix.app }}
fi fi
mv $DIST_LOCATION dist-result mv $DIST_LOCATION dist-result
@@ -161,10 +184,10 @@ jobs:
- name: Sanity check docker image - name: Sanity check docker image
run: | run: |
echo "Check ipfs-hash" echo "Check ipfs-hash"
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local /bin/sh -c 'cat /ipfs-hash' docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local /bin/sh -c 'cat /ipfs-hash' > ${{ matrix.app }}-ipfs-hash docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matrix.app }}-ipfs-hash
echo "List html directory" echo "List html directory"
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local /bin/sh -c 'apk add --update tree; tree /usr/share/nginx/html' docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree /usr/share/nginx/html'
- name: Publish dist as docker image (ghcr) - name: Publish dist as docker image (ghcr)
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
@@ -185,7 +208,7 @@ jobs:
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
continue-on-error: true continue-on-error: true
id: dockerhub-push id: dockerhub-push
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' || env.IS_MAIN_IMAGE == 'true' }} if: ${{ env.IS_IPFS_RELEASE == 'true' }}
with: with:
context: . context: .
file: docker/node-outside-docker.Dockerfile file: docker/node-outside-docker.Dockerfile
@@ -195,7 +218,7 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }} vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || env.IS_MAIN_IMAGE == 'true' && 'main' || '' }} vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
- name: Publish dist as docker image (ghcr - retry) - name: Publish dist as docker image (ghcr - retry)
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
@@ -222,13 +245,13 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }} vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || env.IS_MAIN_IMAGE == 'true' && 'main' || '' }} vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
# bucket creation in github.com/vegaprotocol/terraform//frontend # bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3 - name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master uses: jakejarvis/s3-sync-action@master
# s3 releases are not happening for trading on mainnet - it's IPFS # s3 releases are not happening for trading on mainnet - it's IPFS
if: ${{ env.IS_S3_RELEASE == 'true' }} if: ${{ env.IS_S3_RELASE == 'true' }}
with: with:
args: --acl private --follow-symlinks --delete args: --acl private --follow-symlinks --delete
env: env:
@@ -239,11 +262,11 @@ jobs:
SOURCE_DIR: 'dist-result' SOURCE_DIR: 'dist-result'
- name: Install aws CLI - name: Install aws CLI
if: ${{ env.IS_S3_RELEASE == 'true' }} if: ${{ env.IS_S3_RELASE == 'true' }}
uses: unfor19/install-aws-cli-action@master uses: unfor19/install-aws-cli-action@master
- name: Perform cache invalidation - name: Perform cache invalidation
if: ${{ env.IS_S3_RELEASE == 'true' }} if: ${{ env.IS_S3_RELASE == 'true' }}
env: env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -335,9 +358,7 @@ jobs:
fi fi
# create commit # create commit
if ! git diff --cached --exit-code; then commit_msg="Automated hash update from ${{ github.ref }}"
commit_msg="Automated hash update from ${{ github.ref }}" git commit -m "$commit_msg"
git commit -m "$commit_msg" git push -u origin "main"
git push -u origin "main"
fi
) )
+1 -2
View File
@@ -15,7 +15,6 @@ on:
- types - types
- utils - utils
- i18n - i18n
- wallet
jobs: jobs:
publish: publish:
@@ -29,7 +28,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup node - name: Setup node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions # https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
+1 -9
View File
@@ -48,15 +48,7 @@ cypress.env.json
# Next.js # Next.js
.next .next
# cypress #cypress
/apps/**/cypress/reports/ /apps/**/cypress/reports/
/apps/**/cypress/downloads/ /apps/**/cypress/downloads/
/apps/**/fixtures/wallet/node** /apps/**/fixtures/wallet/node**
# apps/trading/e2e
__pycache__/
apps/trading/e2e/logs/
apps/trading/e2e/.pytest_cache/
apps/trading/e2e/traces/
.nx/
+3
View File
@@ -3,3 +3,6 @@
# Lint commit messages to ensure they follow conventional commit standards # Lint commit messages to ensure they follow conventional commit standards
yarn commitlint --edit "${1}" yarn commitlint --edit "${1}"
# Lint all staged files
yarn lint-staged
-8
View File
@@ -1,8 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Auto-format all files
yarn nx format:write
# Lint all staged files
yarn lint-staged
-8
View File
@@ -1,8 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Lint all staged files - this brings more value as pre-commit
# yarn nx format:check
# Test all projects with changes
# yarn nx affected -t test --exclude trading
+1 -1
View File
@@ -1 +1 @@
20.9.0 16.15.1
-13
View File
@@ -1,7 +1,6 @@
# Add files here to ignore them from prettier formatting # Add files here to ignore them from prettier formatting
/dist /dist
/dist-result
/coverage /coverage
__generated__ __generated__
__generated___ __generated___
@@ -9,15 +8,3 @@ __generated___
apps/static/src/assets/devnet-tranches.json apps/static/src/assets/devnet-tranches.json
apps/static/src/assets/mainnet-tranches.json apps/static/src/assets/mainnet-tranches.json
apps/static/src/assets/testnet-tranches.json apps/static/src/assets/testnet-tranches.json
/apps/**/cypress/reports/
/apps/**/cypress/downloads/
/.nx/cache
# apps/trading/e2e
__pycache__/
apps/trading/e2e/logs/
apps/trading/e2e/.pytest_cache/
apps/trading/e2e/traces/
.pytest_cache/
-28
View File
@@ -1,28 +0,0 @@
# path to a directory with all packages
storage: ../tmp/local-registry/storage
# a list of other known repositories we can talk to
uplinks:
npmjs:
url: https://registry.yarnpkg.com
maxage: 60m
packages:
'**':
# give all users (including non-authenticated users) full access
# because it is a local registry
access: $all
publish: $all
unpublish: $all
# if package is not available locally, proxy requests to npm registry
proxy: npmjs
# log settings
logs:
type: stdout
format: pretty
level: warn
publish:
allow_offline: true # set offline to true to allow publish offline
-2
View File
@@ -1,4 +1,2 @@
* @vegaprotocol/frontend * @vegaprotocol/frontend
apps/ @vegaprotocol/frontend-qa
libs/ @vegaprotocol/frontend-qa
*.graphql @vegaprotocol/core *.graphql @vegaprotocol/core
+1 -1
View File
@@ -11,7 +11,7 @@ recalculate-ipfs:
echo "ipfs hash inside the image" echo "ipfs hash inside the image"
docker run --rm ${TAG} cat /ipfs-hash docker run --rm ${TAG} cat /ipfs-hash
echo "recalculating ipfs hash" echo "recalculating ipfs hash"
docker run --rm ${TAG} ipfs add -rQ /usr/share/nginx/html docker run --rm ${TAG} ipfs add -r /usr/share/nginx/html
.PHONY: eject-ipfs-hash .PHONY: eject-ipfs-hash
unpack: unpack:
+30 -44
View File
@@ -4,7 +4,7 @@ The front-end monorepo provides a toolkit for building apps that interact with V
This repository is managed using [Nx](https://nx.dev). This repository is managed using [Nx](https://nx.dev).
## 🔎 Applications in this repo # 🔎 Applications in this repo
### [Block explorer](./apps/explorer) ### [Block explorer](./apps/explorer)
@@ -30,7 +30,7 @@ Hosting for static content being shared across apps, for example fonts.
The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract. The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract.
## 🧱 Libraries in this repo # 🧱 Libraries in this repo
### [UI toolkit](./libs/ui-toolkit) ### [UI toolkit](./libs/ui-toolkit)
@@ -53,7 +53,7 @@ A utility library for connecting to the Ethereum network and interacting with Ve
Generic react helpers that can be used across multiple applications, along with other utilities. Generic react helpers that can be used across multiple applications, along with other utilities.
## 💻 Develop # 💻 Develop
### Set up ### Set up
@@ -103,7 +103,7 @@ In CI linting, formatting and also run. These checks can be seen in the [CI work
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more. Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
## 🐋 Hosting a console # 🐋 Hosting a console
To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions). To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions).
@@ -113,47 +113,13 @@ In order to run a container on port 3000:
docker run -p 3000:80 [TAG] docker run -p 3000:80 [TAG]
``` ```
On top of that there are two possible scenarios for running docker image - using nginx server (default) of ipfs daemon.
to run ipfs on port 3000:
```bash
docker run -p 3000:80 [TAG] /run-ipfs.sh
```
to run nginx on port 3000:
```bash
docker run -p 3000:80 [TAG]
```
## Build instructions ## Build instructions
The [`docker`](./docker) subfolder has some docker configurations for easily setting up your own hosted version of Console either for the web, or ready for pinning on IPFS. The [`docker`](./docker) subfolder has some docker configurations for easily setting up your own hosted version of console either for the web, or ready for pinning on IPFS
### nx build inside the docker
Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build). The multistage builds ensures consistent CPU architecture and build toolchains are used so that the result will be identical.
```bash
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=20.9.1 --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
```
### Computing ipfs-hash of the build
At the moment this feature is important only for Console releases.
Each docker build finishes with hash calculation for ` dist`` directory. Resulting hash is added to file named as `/ipfs-hash`. Once docker image is produced you can run following commad to display ipfs-hash:
```bash
make recalculate-ipfs TAG=vegaprotocol/trading:{YOUR_VERSION}
```
**updating hash:** recompiling dist directory (even if there are no changed to source code) results in different hash computed by ipfs command.
### nx build outside the docker ### nx build outside the docker
This Docker image packages a pre-built `dist` folder into an [`nginx`](https://hub.docker.com/_/nginx)([server configuration](./nginx/nginx.conf)) docker image. In this case, the application on docker host machine from source. Packaging prepared dist into [`nginx`](https://hub.docker.com/_/nginx)([server configuration](./nginx/nginx.conf)) docker image involves building the application on docker host machine from source.
As a prerequisite you need to perform build of `dist` directory and move its content for specific application to `dist-result` directory. Use following script to do it with a single command: As a prerequisite you need to perform build of `dist` directory and move its content for specific application to `dist-result` directory. Use following script to do it with a single command:
@@ -164,21 +130,41 @@ As a prerequisite you need to perform build of `dist` directory and move its con
You can build any of the containers locally with the following command: You can build any of the containers locally with the following command:
```bash ```bash
docker build -f docker/node-outside-docker.Dockerfile . --tag=[TAG] docker build --dockerfile docker/node-outside-docker.Dockerfile . --tag=[TAG]
``` ```
### nx build inside the docker
Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build) example.
```bash
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=$(cat .nvmrc) --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
```
### Computing ipfs-hash of the build
At the moment this feature is important only for `trading` (console) releases.
Each docker build finishes with hash calculation for dist directory. Resulting hash is added to file named as `/ipfs-hash`. Once docker image is produced you can run following commad to display ipfs-hash:
```bash
make recalculate-ipfs TAG=vegaprotocol/trading:{YOUR_VERSION}
```
**updating hash:** recompiling dist directory (even if there are no changed to source code) results in different hash computed by ipfs command.
### Verifying ipfs-hash of existing current application version ### Verifying ipfs-hash of existing current application version
An IPFS CID will be attached to every [release](https://github.com/vegaprotocol/frontend-monorepo/releases). If you are intending to pin an application on IPFS, you can check that your build matches by running the following steps: An IPFS CID will be attached to every [release](https://github.com/vegaprotocol/frontend-monorepo/releases). If you are intending to pin an application on IPFS, you can check that your build matches by running the following steps:
1. Show latest release by running: `make latest-release`. You need to configure [`gh`](https://cli.github.com/) for this step to work, otherwise please provide release manually from [github](https://github.com/vegaprotocol/frontend-monorepo/releases) or [dockerhub](https://hub.docker.com/r/vegaprotocol/trading) 1. Show latest release by runnning: `make latest-release`. You need to configure [`gh`](https://cli.github.com/) for this step to work, otherwise please provide release manually from [github](https://github.com/vegaprotocol/frontend-monorepo/releases) or [dockerhub](https://hub.docker.com/r/vegaprotocol/trading)
2. Set RELEASE environment variable to value that you want to validate: `export RELEASE=$(make latest-release)` or `export RELEASE=vXX.XX.XX` 2. Set RELEASE environment variable to value that you want to validate: `export RELEASE=$(make latest-release)` or `export RELEASE=vXX.XX.XX`
3. Set TAG environment variable to image that you want to validate: `export TAG=vegaprotocol/trading:$RELEASE` 3. Set TAG environment variable to image that you want to validate: `export TAG=vegaprotocol/trading:$RELEASE`
4. Download docker image with the desired release `docker pull $TAG`. 4. Download docker image with the desired release `docker pull $TAG`.
5. Recalculate hash: `make recalculate-ipfs` 5. Recalculate hash: `make recalculate-ipfs`
6. You should see exactly same hash produced by ipfs command as one placed with the release notes: `make show-latest-release` 6. You should see exactly same hash produced by ipfs command as one placed with the release notes: `make show-latest-release`
7. If you want to extract dist from docker image to your local filesystem you can run following command: `make unpack` 7. If you want to extract dist from docker image to your local filesystem you can run following command: `make unpack`
8. Now `dist` directory contains valid application build 8. Now `dist` directory contains valid application build. **it is not possible to calculate same ipfs hash on files that are result of copy operation**
## Config ## Config
@@ -226,6 +212,6 @@ Note: The script is only needed if capsule was built for first time or fresh. To
vega wallet service run -n DV --load-tokens --tokens-passphrase-file passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet vega wallet service run -n DV --load-tokens --tokens-passphrase-file passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet
``` ```
## 📑 License # 📑 License
[MIT](./LICENSE) [MIT](./LICENSE)
+1 -6
View File
@@ -16,10 +16,5 @@ NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1 NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1 NX_EXPLORER_VALIDATORS=1
CYPRESS_VEGA_URL=http://localhost:3008/graphql
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
CYPRESS_VEGA_WALLET_API_TOKEN= CYPRESS_VEGA_WALLET_API_TOKEN=
CYPRESS_VEGA_URL=http://localhost:3008/graphql
-2
View File
@@ -4,8 +4,6 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
NX_VEGA_ENV=DEVNET NX_VEGA_ENV=DEVNET
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# App flags # App flags
NX_EXPLORER_ASSETS=1 NX_EXPLORER_ASSETS=1
-1
View File
@@ -4,7 +4,6 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocke
NX_VEGA_URL=https://api.vega.community/graphql NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# App flags # App flags
NX_EXPLORER_ASSETS=1 NX_EXPLORER_ASSETS=1
-1
View File
@@ -4,7 +4,6 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_VEGA_ENV=TESTNET NX_VEGA_ENV=TESTNET
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# App flags # App flags
NX_EXPLORER_ASSETS=1 NX_EXPLORER_ASSETS=1
+1 -3
View File
@@ -4,9 +4,7 @@
"overrides": [ "overrides": [
{ {
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": { "rules": {}
"cypress/unsafe-to-chain-command": 0
}
} }
] ]
} }
-1
View File
@@ -23,7 +23,6 @@ module.exports = defineConfig({
viewportWidth: 1440, viewportWidth: 1440,
viewportHeight: 900, viewportHeight: 900,
testIsolation: false, testIsolation: false,
experimentalMemoryManagement: true,
}, },
env: { env: {
environment: 'CUSTOM', environment: 'CUSTOM',
+1 -1
View File
@@ -17,7 +17,7 @@
} }
}, },
"lint": { "lint": {
"executor": "@nx/eslint:lint", "executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"], "outputs": ["{options.outputFile}"],
"options": { "options": {
"lintFilePatterns": ["apps/explorer-e2e/**/*.{js,ts}"] "lintFilePatterns": ["apps/explorer-e2e/**/*.{js,ts}"]
@@ -1,26 +0,0 @@
{
"proposalSubmission": {
"rationale": {
"title": "Test new asset proposal",
"description": "E2E test for proposals"
},
"terms": {
"newAsset": {
"changes": {
"name": "USDT Coin",
"symbol": "USDT",
"decimals": "18",
"quantum": "1",
"erc20": {
"contractAddress": "0xb404c51bbc10dcbe948077f18a4b8e553d160084",
"withdrawThreshold": "10",
"lifetimeLimit": "10"
}
}
},
"closingTimestamp": 1724339572,
"enactmentTimestamp": 1724339572,
"validationTimestamp": 1692799617
}
}
}
+10 -159
View File
@@ -1,168 +1,19 @@
import { createSuccessorMarketProposal } from '../support/governance.functions'; //Tests set to skip until market bug for capsule checkpoint is fixed
context.skip('Market page', { tags: '@regression' }, function () {
context('Market page', { tags: '@regression' }, function () {
describe('Verify elements on page', function () { describe('Verify elements on page', function () {
const marketHeaders = 'markets-heading'; const marketHeaders = '[data-testid="markets-header"]';
const marketNavigation = 'a[href="/markets"]';
before('Create market', function () { it('Markets page is displayed', function () {
cy.visit('/'); cy.visit('/');
cy.createMarket(); cy.get(marketNavigation).click();
cy.common_validate_blocks_data_displayed(marketHeaders);
}); });
beforeEach('Get market id', function () { it('Markets page displayed on mobile', function () {
cy.navigate_to('markets');
cy.get('[col-id="id"]').last().invoke('text').as('createdMarketId');
});
it('Market displayed on market page', function () {
cy.navigate_to('markets');
cy.getByTestId(marketHeaders).should('be.visible');
cy.get(`[row-id="${this.createdMarketId}"]`)
.should('be.visible')
.within(() => {
cy.get_element_by_col_id('code').should('have.text', 'TEST.24h');
cy.get_element_by_col_id('name').should('have.text', 'Test market 1');
cy.get_element_by_col_id('state').should('have.text', 'Pending');
cy.get_element_by_col_id('asset').should('have.text', 'fUSDC');
cy.get_element_by_col_id('id').should(
'have.text',
this.createdMarketId
);
cy.get_element_by_col_id('actions')
.find('a')
.should('have.attr', 'href', `/markets/${this.createdMarketId}`);
});
});
it('Able to go to market details page', function () {
cy.navigate_to('markets');
cy.contains('Test market 1').click();
cy.getByTestId(marketHeaders).should('have.text', 'Test market 1');
cy.validate_element_from_table('Name', 'Test market 1');
cy.validate_element_from_table('Trading Mode', 'Opening auction');
cy.validate_element_from_table('Market Decimal Places', '5');
cy.validate_element_from_table('Position Decimal Places', '5');
cy.validate_element_from_table('Settlement Asset Decimal Places', '5');
// Instrument
cy.validate_element_from_table('Market Name', 'Test market 1');
cy.validate_element_from_table('Code', 'TEST.24h');
cy.validate_element_from_table('Product Type', 'Future');
cy.validate_element_from_table('Quote Name', 'fUSDC');
// Settlement Asset
cy.validate_element_from_table('Quote Name', 'fUSDC');
cy.validate_element_from_table('Symbol', 'fUSDC');
cy.validate_element_from_table('Decimals', '5');
cy.validate_element_from_table('Quantum', '0.00');
cy.validate_element_from_table('Status', 'Enabled');
cy.validate_element_from_table('Max faucet amount', '10,000,000.00');
cy.validate_element_from_table(
'Infrastructure fee account balance',
'0.00'
);
cy.validate_element_from_table(
'Global reward pool account balance',
'0.00'
);
// Metadata
cy.validate_element_from_table('Sector', 'tech');
cy.validate_element_from_table('Source', 'docs.vega.xyz');
// Risk model
cy.validate_element_from_table('Tau', '0.0001140771161');
cy.validate_element_from_table('Risk Aversion Parameter', '0.01');
// Risk parameters
// cy.validate_element_from_table('R', '0.016')
cy.validate_element_from_table('Sigma', '0.5');
// Risk factors
cy.validate_element_from_table('Short', '0.0143218738374871');
cy.validate_element_from_table('Long', '0.0141450471498822');
// Price monitoring settings 1
cy.validate_element_from_table('Horizon Secs', '43,200');
cy.validate_element_from_table('Probability', '1');
cy.validate_element_from_table('Auction Extension Secs', '600');
// Liquidity Monitoring
cy.validate_element_from_table('Triggering Ratio', '0.7');
cy.validate_element_from_table('Time Window', '3,600');
cy.validate_element_from_table('Scaling Factor', '10');
// Liquidity
cy.validate_element_from_table('Target Stake', '0.00 fUSDC');
cy.validate_element_from_table('Supplied Stake', '0.00 fUSDC');
// Liquidity price range
cy.validate_element_from_table(
'Liquidity Price Range',
'95.00% of mid price'
);
cy.validate_element_from_table('Lowest Price', '0.00 fUSDC');
cy.validate_element_from_table('Highest Price', '0.00 fUSDC');
cy.getByTestId('oracle-spec-links')
.should('have.attr', 'href')
.and(
'contain',
'/oracles/bf242aa5c9f64fcbb77808aa8582e73711519f4b35264eb797a80f1803590a24'
);
// Able to view Json
cy.contains('View JSON').click();
cy.get('.language-json').should('exist');
cy.getByTestId('icon-cross').click();
});
// Skipping due to resize observer loop limit error
it.skip('Markets page displayed on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle(); cy.common_switch_to_mobile_and_click_toggle();
cy.navigate_to('markets', true); cy.get(marketNavigation).click();
cy.getByTestId(marketHeaders).should('be.visible'); cy.common_validate_blocks_data_displayed(marketHeaders);
cy.get(`[row-id="${this.createdMarketId}"]`)
.should('be.visible')
.within(() => {
cy.get_element_by_col_id('code').should('have.text', 'TEST.24h');
cy.get_element_by_col_id('name').should('have.text', 'Test market 1');
cy.get_element_by_col_id('state').should('have.text', 'Pending');
cy.get_element_by_col_id('asset').should('have.text', 'fUSDC');
cy.get_element_by_col_id('id').should(
'have.text',
this.createdMarketId
);
cy.get_element_by_col_id('actions')
.find('a')
.should('have.attr', 'href', `/markets/${this.createdMarketId}`);
});
});
it('Able to go to market details page for successor market', function () {
const successionLineItem = 'succession-line-item';
const successionLineMarketId = 'succession-line-item-market-id';
createSuccessorMarketProposal(this.createdMarketId);
cy.navigate_to('markets');
cy.reload();
cy.contains('Token test market', { timeout: 8000 }).should('be.visible');
cy.get('[row-index="0"]')
.invoke('attr', 'row-id')
.as('successorMarketId');
cy.contains('Token test market').click();
cy.getByTestId(marketHeaders).should('have.text', 'Token test market');
cy.validate_element_from_table('Triggering Ratio', '0.7');
cy.validate_element_from_table('Time Window', '3,600');
cy.validate_element_from_table('Scaling Factor', '10');
cy.getByTestId(successionLineItem)
.first()
.within(() => {
cy.contains('Test market 1');
cy.getByTestId(successionLineMarketId).should(
'have.text',
this.createdMarketId
);
});
cy.getByTestId(successionLineItem)
.eq(1)
.within(() => {
cy.contains('Token test market');
cy.getByTestId(successionLineMarketId).should(
'have.text',
this.successorMarketId
);
});
}); });
}); });
}); });
@@ -1,9 +1,12 @@
context('Network parameters page', { tags: '@smoke' }, function () { context('Network parameters page', { tags: '@smoke' }, function () {
before('navigate to network parameter page', function () { before('navigate to network parameter page', function () {
cy.fixture('net_parameter_format_lookup').as('networkParameterFormat'); cy.fixture('net_parameter_format_lookup').as('networkParameterFormat');
cy.visit('/network-parameters');
}); });
describe('Verify elements on page', function () { describe('Verify elements on page', function () {
beforeEach(() => {
cy.visit('/network-parameters');
});
const networkParametersHeader = '[data-testid="network-param-header"]'; const networkParametersHeader = '[data-testid="network-param-header"]';
const tableRows = '[data-testid="key-value-table-row"]'; const tableRows = '[data-testid="key-value-table-row"]';
@@ -13,7 +16,6 @@ context('Network parameters page', { tags: '@smoke' }, function () {
.and('be.visible'); .and('be.visible');
}); });
// 0006-NETW-021
it('should list each of the network parameters available', function () { it('should list each of the network parameters available', function () {
cy.get_network_parameters().then((network_parameters) => { cy.get_network_parameters().then((network_parameters) => {
const numberOfNetworkParametersInSystem = const numberOfNetworkParametersInSystem =
@@ -196,19 +198,6 @@ context('Network parameters page', { tags: '@smoke' }, function () {
}); });
}); });
// 0006-NETW-022 0006-NETW-023
it('governance assets should be correctly grouped', function () {
cy.getByTestId('governance')
.should('exist')
.parent()
.should('have.attr', 'href', '/network-parameters#governance');
cy.get('[id="governance-proposal-asset"]')
.parent()
.within(() => {
cy.getByTestId('key-value-table-row').should('have.length', 8);
});
});
it('should be able to see network parameters - on mobile', function () { it('should be able to see network parameters - on mobile', function () {
cy.switchToMobile(); cy.switchToMobile();
cy.get_network_parameters().then((network_parameters) => { cy.get_network_parameters().then((network_parameters) => {
@@ -40,7 +40,7 @@ context.skip('Node switcher', { tags: '@regression' }, function () {
const errorTypeTxt = 'Error: invalid url'; const errorTypeTxt = 'Error: invalid url';
const nodeErrorTxt = 'fakeUrl is not a valid url.'; const nodeErrorTxt = 'fakeUrl is not a valid url.';
cy.getByTestId('node-url-custom').click({ force: true }); cy.getByTestId('node-url-custom').click();
cy.getByTestId(customNodeBtn).within(() => { cy.getByTestId(customNodeBtn).within(() => {
cy.get('input').clear().type('fakeUrl'); cy.get('input').clear().type('fakeUrl');
@@ -1,85 +0,0 @@
import { getNewAssetTxBody } from '../support/governance.functions';
context('Proposal page', { tags: '@smoke' }, function () {
describe.skip('Verify elements on page', function () {
const proposalHeading = 'proposals-heading';
const dateTimeRegex =
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
before('Create market proposal', function () {
cy.visit('/');
cy.createMarket();
});
it('Able to view proposal', function () {
const proposalTitle = 'Add Lorem Ipsum market';
cy.navigate_to('governanceProposals');
cy.getByTestId(proposalHeading).should('be.visible');
cy.contains(proposalTitle)
.parent()
.parent()
.parent()
.within(() => {
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
cy.get_element_by_col_id('type').should('have.text', 'NewMarket');
cy.get_element_by_col_id('state').should('have.text', 'Enacted');
cy.get('[col-id="cDate"]')
.invoke('text')
.should('match', dateTimeRegex);
cy.get('[col-id="eDate"]')
.invoke('text')
.should('match', dateTimeRegex);
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and('contains', 'https://governance.fairground.wtf/proposals/');
cy.contains('View terms').should('exist').click();
});
cy.getByTestId('dialog-title').should('have.text', proposalTitle);
cy.get('.language-json').should('exist');
cy.getByTestId('icon-cross').click();
});
it('Proposal page displayed on mobile', function () {
const proposalTitle = 'Add Lorem Ipsum market';
cy.common_switch_to_mobile_and_click_toggle();
cy.navigate_to('governanceProposals', true);
cy.getByTestId(proposalHeading).should('be.visible');
cy.get('[row-index="0"]').within(() => {
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
});
});
it.skip('Able to view new asset proposal', function () {
const proposalTitle = 'Test new asset proposal';
const newAssetProposalBody = getNewAssetTxBody();
cy.VegaWalletSubmitProposal(newAssetProposalBody);
cy.visit('/');
cy.navigate_to('governanceProposals');
cy.contains(proposalTitle)
.parent()
.parent()
.parent()
.within(() => {
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
cy.get_element_by_col_id('type').should('have.text', 'NewAsset');
cy.get_element_by_col_id('state').should(
'have.text',
'Waiting for Node Vote'
);
cy.get('[col-id="cDate"]')
.invoke('text')
.should('match', dateTimeRegex);
cy.get('[col-id="eDate"]')
.invoke('text')
.should('match', dateTimeRegex);
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and('contains', 'https://governance.fairground.wtf/proposals/');
cy.contains('View terms').should('exist').click();
});
});
});
});
@@ -40,7 +40,7 @@ context.skip('Transactions page', function () {
.first() .first()
.click({ force: true }); .click({ force: true });
} else { } else {
cy.log('Unable to find any transactions on page'); cy.slack('Unable to find any transactions on page');
cy.screenshot(); cy.screenshot();
} }
}); });
@@ -78,7 +78,7 @@ context.skip('Transactions page', function () {
} }
}); });
} else { } else {
cy.log('Unable to find any transactions on page'); cy.slack('Unable to find any transactions on page');
cy.screenshot(); cy.screenshot();
} }
}); });
@@ -7,11 +7,7 @@ context('Validator page', { tags: '@smoke' }, function () {
it('should be able to see validator tiles', function () { it('should be able to see validator tiles', function () {
cy.getNodes().then((nodes) => { cy.getNodes().then((nodes) => {
nodes.forEach((node) => { nodes.forEach((node) => {
if (node.rankingScore.performanceScore > 0) { cy.get(`[validator-id="${node.id}"]`).should('be.visible');
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
} else {
cy.get(`[validator-id="${node.id}"]`).should('not.exist');
}
}); });
}); });
}); });
@@ -28,7 +28,7 @@ Cypress.Commands.add('switchToMobile', () => {
Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () { Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () {
cy.viewport('iphone-x'); cy.viewport('iphone-x');
cy.visit('/'); cy.visit('/');
cy.get('[data-testid="button-menu-drawer"]').click(); cy.get('[data-testid="open-menu"]').click();
}); });
Cypress.Commands.add('monitor_clipboard', () => { Cypress.Commands.add('monitor_clipboard', () => {
@@ -88,49 +88,3 @@ Cypress.Commands.add('convert_number_to_max_four_decimal', (number) => {
maximumFractionDigits: 4, maximumFractionDigits: 4,
}); });
}); });
Cypress.Commands.add('navigate_to', (page, mobileView = false) => {
const navigation = {
transactions: '/txs',
blocks: '/blocks',
oracles: '/oracles',
validators: '/validators',
parties: '/parties',
assets: '/assets',
markets: '/markets',
governanceProposals: '/governance',
networkParameters: '/network-parameters',
genesisParameters: '/genesis',
};
const topLevelRoutes = ['transactions', 'blocks', 'oracles', 'validators'];
cy.getByTestId('navigation').within(() => {
if (!topLevelRoutes.includes(page) && mobileView === false) {
cy.getByTestId('other').first().click();
}
cy.get(`[href="${navigation[page]}"]`).first().click();
});
cy.url().should('include', navigation[page]);
});
Cypress.Commands.add('get_element_by_col_id', (elementName) => {
cy.get(`[col-id="${elementName}"]`);
});
Cypress.Commands.add(
'validate_element_from_table',
(tableRowName, tableRowValue) => {
cy.contains(tableRowName)
.parentsUntil("[data-testid='key-value-table-row']")
.siblings()
.should('have.text', tableRowValue);
}
);
Cypress.Commands.add(
'validate_proposal_change_type',
(tableRowName, changeType) => {
cy.contains(tableRowName).siblings().should('have.text', changeType);
}
);
@@ -1,186 +0,0 @@
import { addSeconds, millisecondsToSeconds } from 'date-fns';
export function createSuccessorMarketProposal(parentMarketId) {
cy.VegaWalletSubmitProposal(getSuccessorTxBody(parentMarketId));
}
function getSuccessorTxBody(parentMarketId) {
const MIN_CLOSE_SEC = 500;
const MIN_ENACT_SEC = 700;
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
return {
proposalSubmission: {
rationale: {
title: 'Test successor market proposal details',
description: 'E2E test for successor market',
},
terms: {
newMarket: {
changes: {
decimalPlaces: '5',
positionDecimalPlaces: '5',
linearSlippageFactor: '0.001',
quadraticSlippageFactor: '0',
instrument: {
name: 'Token test market',
code: 'TEST.24h',
future: {
settlementAsset:
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc',
quoteName: 'fUSDC',
dataSourceSpecForSettlementData: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'prices.BTC.value',
type: 'TYPE_INTEGER',
numberDecimalPlaces: '0',
},
conditions: [
{
operator: 'OPERATOR_GREATER_THAN',
value: '0',
},
],
},
],
},
},
},
dataSourceSpecForTradingTermination: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'trading.terminated.ETH5',
type: 'TYPE_BOOLEAN',
},
conditions: [
{
operator: 'OPERATOR_EQUALS',
value: 'true',
},
],
},
],
},
},
},
dataSourceSpecBinding: {
settlementDataProperty: 'prices.BTC.value',
tradingTerminationProperty: 'trading.terminated.ETH5',
},
},
},
metadata: [
'sector:food',
'sector:materials',
'source:docs.vega.xyz',
],
priceMonitoringParameters: {
triggers: [
{
horizon: '43200',
probability: '0.9999999',
auctionExtension: '600',
},
],
},
liquidityMonitoringParameters: {
targetStakeParameters: {
timeWindow: '3600',
scalingFactor: 10,
},
triggeringRatio: '0.7',
auctionExtension: '1',
},
logNormal: {
tau: 0.0001140771161,
riskAversionParameter: 0.01,
params: {
mu: 0,
r: 0.016,
sigma: 0.5,
},
},
successor: {
parentMarketId: parentMarketId,
insurancePoolFraction: '0.75',
},
liquiditySlaParameters: {
priceRange: '0.95',
commitmentMinTimeFraction: '0.5',
performanceHysteresisEpochs: 2,
slaCompetitionFactor: '0.75',
},
},
},
closingTimestamp,
enactmentTimestamp,
},
},
};
}
export function getNewAssetTxBody() {
const MIN_CLOSE_SEC = 500;
const MIN_ENACT_SEC = 700;
const MIN_VALID_SEC = 60;
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
const validationDate = addSeconds(new Date(), MIN_VALID_SEC);
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
const validationTimestamp = millisecondsToSeconds(validationDate.getTime());
return {
proposalSubmission: {
rationale: {
title: 'Test new asset proposal',
description: 'E2E test for proposals',
},
terms: {
newAsset: {
changes: {
name: 'USDT Coin',
symbol: 'USDT',
decimals: '18',
quantum: '1',
erc20: {
contractAddress: '0xb404c51bbc10dcbe948077f18a4b8e553d160084',
withdrawThreshold: '10',
lifetimeLimit: '10',
},
},
},
closingTimestamp,
enactmentTimestamp,
validationTimestamp,
},
},
};
}
+2
View File
@@ -1,4 +1,5 @@
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.rocks NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.rocks
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1 NX_VEGA_ENV=STAGNET1
@@ -8,6 +9,7 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.rocks/rest NX_BLOCK_EXPLORER=https://be.stagnet1.vega.rocks/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+3 -4
View File
@@ -1,11 +1,10 @@
# App configuration variables # App configuration variables
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
NX_VEGA_ENV=CUSTOM NX_VEGA_ENV=CUSTOM
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_VEGA_EXPLORER_URL=/ NX_VEGA_EXPLORER_URL=/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# App flags # App flags
NX_EXPLORER_TXS_LIST=0 NX_EXPLORER_TXS_LIST=0
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
+3 -4
View File
@@ -1,13 +1,12 @@
# App configuration variables # App configuration variables
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_VEGA_ENV=DEVNET NX_VEGA_ENV=DEVNET
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://dev.governance.vega.xyz NX_VEGA_GOVERNANCE_URL=https://dev.governance.vega.xyz
NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=/ NX_VEGA_EXPLORER_URL=/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
+3 -4
View File
@@ -1,14 +1,13 @@
# App configuration variables # App configuration variables
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_SENTRY_DSN=https://b3a56b03eda842faad731f3ea9dfd1bc@o286262.ingest.sentry.io/6242427 NX_SENTRY_DSN=https://b3a56b03eda842faad731f3ea9dfd1bc@o286262.ingest.sentry.io/6242427
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.vega.community/rest NX_BLOCK_EXPLORER=https://be.vega.community/rest
NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/ NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
NX_VEGA_CONSOLE_URL=https://console.vega.xyz NX_VEGA_CONSOLE_URL=https://console.vega.xyz
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
+5 -6
View File
@@ -1,14 +1,13 @@
# App configuration variables # App configuration variables
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
NX_VEGA_ENV=MAINNET_MIRROR NX_VEGA_ENV=MAINNET-MIRROR
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/ NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
-1
View File
@@ -1,3 +1,2 @@
# .env is stagnet1, so there are no overrides required # .env is stagnet1, so there are no overrides required
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET1":"https://stagnet1.explorer.vega.xyz"}' NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET1":"https://stagnet1.explorer.vega.xyz"}'
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
+3 -4
View File
@@ -1,14 +1,13 @@
# App configuration variables # App configuration variables
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
NX_VEGA_ENV=TESTNET NX_VEGA_ENV=TESTNET
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
+3 -4
View File
@@ -4,13 +4,12 @@ NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/maste
NX_VEGA_URL=https://api-validators-testnet.vega.rocks/graphql NX_VEGA_URL=https://api-validators-testnet.vega.rocks/graphql
NX_VEGA_REST=https://api-validators-testnet.vega.rocks/ NX_VEGA_REST=https://api-validators-testnet.vega.rocks/
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/ NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/ NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
+2 -2
View File
@@ -3,6 +3,6 @@ NX_TENDERMINT_URL=http://localhost:26607/
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket
NX_VEGA_ENV=CUSTOM NX_VEGA_ENV=CUSTOM
NX_BLOCK_EXPLORER= NX_BLOCK_EXPLORER=
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_VEGA_EXPLORER_URL=/ NX_VEGA_EXPLORER_URL=/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
+4
View File
@@ -0,0 +1,4 @@
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
+19 -3
View File
@@ -53,7 +53,7 @@
} }
}, },
"lint": { "lint": {
"executor": "@nx/eslint:lint", "executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"], "outputs": ["{options.outputFile}"],
"options": { "options": {
"lintFilePatterns": ["apps/explorer/**/*.{ts,tsx,js,jsx}"] "lintFilePatterns": ["apps/explorer/**/*.{ts,tsx,js,jsx}"]
@@ -63,14 +63,30 @@
"executor": "@nx/jest:jest", "executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/apps/explorer"], "outputs": ["{workspaceRoot}/coverage/apps/explorer"],
"options": { "options": {
"jestConfig": "apps/explorer/jest.config.ts" "jestConfig": "apps/explorer/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
} }
}, },
"generate-types": { "generate-types": {
"executor": "nx:run-commands", "executor": "nx:run-commands",
"options": { "options": {
"commands": [ "commands": [
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.73.1/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types" "npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/spec-update-v0.72.0-preview.2/specs/v0.72.0-preview.2/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
]
}
},
"build-netlify": {
"executor": "nx:run-commands",
"options": {
"commands": [
"cp apps/explorer/netlify.toml netlify.toml",
"nx build explorer"
] ]
} }
}, },
+16 -25
View File
@@ -1,7 +1,6 @@
import '../i18n';
import { import {
AppFailure,
NetworkLoader, NetworkLoader,
NodeFailure,
NodeGuard, NodeGuard,
NodeSwitcherDialog, NodeSwitcherDialog,
useEnvironment, useEnvironment,
@@ -12,8 +11,7 @@ import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-web
import { Loader, Splash } from '@vegaprotocol/ui-toolkit'; import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client'; import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
import { RouterProvider } from 'react-router-dom'; import { RouterProvider } from 'react-router-dom';
import { useRouterConfig } from './routes/router-config'; import { router } from './routes/router-config';
import { createBrowserRouter } from 'react-router-dom';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { Suspense } from 'react'; import { Suspense } from 'react';
@@ -30,27 +28,20 @@ function App() {
); );
return ( return (
<TendermintWebsocketProvider> <TendermintWebsocketProvider>
<Suspense fallback={splashLoading}> <NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}> <NodeGuard
<NodeGuard skeleton={<div>{t('Loading')}</div>}
skeleton={<div>{t('Loading')}</div>} failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
failure={ >
<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} /> <Suspense fallback={splashLoading}>
} <RouterProvider router={router} fallbackElement={splashLoading} />
> </Suspense>
<Suspense fallback={splashLoading}> </NodeGuard>
<RouterProvider <NodeSwitcherDialog
router={createBrowserRouter(useRouterConfig())} open={nodeSwitcherOpen}
fallbackElement={splashLoading} setOpen={setNodeSwitcherOpen}
/> />
</Suspense> </NetworkLoader>
</NodeGuard>
<NodeSwitcherDialog
open={nodeSwitcherOpen}
setOpen={setNodeSwitcherOpen}
/>
</NetworkLoader>
</Suspense>
</TendermintWebsocketProvider> </TendermintWebsocketProvider>
); );
} }
@@ -1,5 +1,5 @@
import { useAssetDataProvider } from '@vegaprotocol/assets'; import { useAssetDataProvider } from '@vegaprotocol/assets';
import { addDecimalsFixedFormatNumber } from '@vegaprotocol/utils'; import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { AssetLink } from '../links'; import { AssetLink } from '../links';
export type AssetBalanceProps = { export type AssetBalanceProps = {
@@ -7,7 +7,6 @@ export type AssetBalanceProps = {
price: string; price: string;
showAssetLink?: boolean; showAssetLink?: boolean;
showAssetSymbol?: boolean; showAssetSymbol?: boolean;
rounded?: boolean;
}; };
/** /**
@@ -19,22 +18,17 @@ const AssetBalance = ({
price, price,
showAssetLink = true, showAssetLink = true,
showAssetSymbol = false, showAssetSymbol = false,
rounded = false,
}: AssetBalanceProps) => { }: AssetBalanceProps) => {
const { data: asset, loading } = useAssetDataProvider(assetId); const { data: asset, loading } = useAssetDataProvider(assetId);
const label = const label =
!loading && asset && asset.decimals !loading && asset && asset.decimals
? addDecimalsFixedFormatNumber( ? addDecimalsFormatNumber(price, asset.decimals)
price,
asset.decimals,
rounded ? 0 : undefined
)
: price; : price;
return ( return (
<div className="inline-block"> <div className="inline-block">
<span className="font-mono">{label}</span>{' '} <span>{label}</span>{' '}
{showAssetLink && asset?.id ? ( {showAssetLink && asset?.id ? (
<AssetLink showAssetSymbol={showAssetSymbol} assetId={assetId} /> <AssetLink showAssetSymbol={showAssetSymbol} assetId={assetId} />
) : null} ) : null}
@@ -1,30 +1,24 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { import type { AssetFieldsFragment } from '@vegaprotocol/assets';
useAssetTypeMapping, import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
useAssetStatusMapping,
type AssetFieldsFragment,
} from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { ButtonLink } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { type AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import { AgGrid } from '@vegaprotocol/datagrid'; import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { type VegaICellRendererParams } from '@vegaprotocol/datagrid'; import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
import { useRef, useLayoutEffect } from 'react'; import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints'; import { BREAKPOINT_MD } from '../../config/breakpoints';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { type ColDef } from 'ag-grid-community'; import type { RowClickedEvent, ColDef } from 'ag-grid-community';
import type { RowClickedEvent } from 'ag-grid-community';
type AssetsTableProps = { type AssetsTableProps = {
data: AssetFieldsFragment[] | null; data: AssetFieldsFragment[] | null;
}; };
export const AssetsTable = ({ data }: AssetsTableProps) => { export const AssetsTable = ({ data }: AssetsTableProps) => {
const assetTypeMapping = useAssetTypeMapping();
const assetStatusMapping = useAssetStatusMapping();
const navigate = useNavigate(); const navigate = useNavigate();
const ref = useRef<AgGridReact>(null); const ref = useRef<AgGridReact>(null);
const showColumnsOnDesktop = () => { const showColumnsOnDesktop = () => {
ref.current?.api.setColumnsVisible( ref.current?.columnApi.setColumnsVisible(
['id', 'type', 'status'], ['id', 'type', 'status'],
window.innerWidth > BREAKPOINT_MD window.innerWidth > BREAKPOINT_MD
); );
@@ -53,14 +47,14 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
field: 'source.__typename', field: 'source.__typename',
hide: window.innerWidth < BREAKPOINT_MD, hide: window.innerWidth < BREAKPOINT_MD,
valueFormatter: ({ value }: { value?: string }) => valueFormatter: ({ value }: { value?: string }) =>
value ? assetTypeMapping[value].value : '', value ? AssetTypeMapping[value].value : '',
}, },
{ {
headerName: t('Status'), headerName: t('Status'),
field: 'status', field: 'status',
hide: window.innerWidth < BREAKPOINT_MD, hide: window.innerWidth < BREAKPOINT_MD,
valueFormatter: ({ value }: { value?: string }) => valueFormatter: ({ value }: { value?: string }) =>
value ? assetStatusMapping[value].value : '', value ? AssetStatusMapping[value].value : '',
}, },
{ {
colId: 'actions', colId: 'actions',
@@ -75,7 +69,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) => }: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
value ? ( value ? (
<ButtonLink <ButtonLink
onClick={() => { onClick={(e) => {
navigate(value); navigate(value);
}} }}
> >
@@ -86,7 +80,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
), ),
}, },
], ],
[navigate, assetStatusMapping, assetTypeMapping] [navigate]
); );
return ( return (
@@ -23,7 +23,7 @@ export const Footer = () => {
); );
return ( return (
<footer className="grid grid-cols-[1fr_auto] items-center text-xs md:text-md lg:flex md:col-span-2 px-4 pt-2 pb-3 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200"> <footer className="grid grid-rows-2 lg:grid-cols-[1fr_auto] text-xs md:text-md lg:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
<div className="flex justify-between gap-2 align-middle"> <div className="flex justify-between gap-2 align-middle">
{GIT_COMMIT_HASH && ( {GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4"> <div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
@@ -43,12 +43,9 @@ export const Footer = () => {
</div> </div>
)} )}
<div className="content-center flex pr-4 md:border-r border-neutral-700 dark:border-neutral-300"> <div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
<span className="pr-2">{VEGA_URL && <NodeUrl url={VEGA_URL} />}</span> {VEGA_URL && <NodeUrl url={VEGA_URL} />}
<Link <Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
className="ml-2 underline-offset-4"
onClick={() => setNodeSwitcherOpen(true)}
>
{t('Change')} {t('Change')}
</Link> </Link>
</div> </div>
@@ -62,10 +59,7 @@ export const Footer = () => {
) : null} ) : null}
</div> </div>
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto"> <div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
<RouteLink <RouteLink to={`/${Routes.DISCLAIMER}`} className="underline">
to={`/${Routes.DISCLAIMER}`}
className="underline underline-offset-4"
>
Disclaimer Disclaimer
</RouteLink> </RouteLink>
</div> </div>
@@ -14,7 +14,7 @@ import { Routes } from '../../routes/route-names';
import { NetworkSwitcher } from '@vegaprotocol/environment'; import { NetworkSwitcher } from '@vegaprotocol/environment';
import type { Navigable } from '../../routes/router-config'; import type { Navigable } from '../../routes/router-config';
import { isNavigable } from '../../routes/router-config'; import { isNavigable } from '../../routes/router-config';
import { useRouterConfig } from '../../routes/router-config'; import { routerConfig } from '../../routes/router-config';
import { useMemo } from 'react'; import { useMemo } from 'react';
import compact from 'lodash/compact'; import compact from 'lodash/compact';
import { Search } from '../search'; import { Search } from '../search';
@@ -26,7 +26,6 @@ const routeToNavigationItem = (r: Navigable) => (
); );
export const Header = () => { export const Header = () => {
const routerConfig = useRouterConfig();
const isHome = Boolean(useMatch(Routes.HOME)); const isHome = Boolean(useMatch(Routes.HOME));
const pages = routerConfig[0].children || []; const pages = routerConfig[0].children || [];
const mainItems = compact( const mainItems = compact(
@@ -41,7 +40,6 @@ export const Header = () => {
Routes.ASSETS, Routes.ASSETS,
Routes.MARKETS, Routes.MARKETS,
Routes.GOVERNANCE, Routes.GOVERNANCE,
Routes.TREASURY,
Routes.NETWORK_PARAMETERS, Routes.NETWORK_PARAMETERS,
Routes.GENESIS, Routes.GENESIS,
].map((n) => pages.find((r) => r.path === n)) ].map((n) => pages.find((r) => r.path === n))
@@ -97,10 +95,7 @@ export const Header = () => {
{mainItems.map(routeToNavigationItem)} {mainItems.map(routeToNavigationItem)}
{groupedItems && groupedItems.length > 0 && ( {groupedItems && groupedItems.length > 0 && (
<NavigationItem> <NavigationItem>
<NavigationTrigger <NavigationTrigger isActive={Boolean(isOnOther)}>
isActive={Boolean(isOnOther)}
data-testid="other"
>
{t('Other')} {t('Other')}
</NavigationTrigger> </NavigationTrigger>
<NavigationContent> <NavigationContent>
@@ -0,0 +1,34 @@
import React from 'react';
import { DATA_SOURCES } from '../../../config';
import Hash from '../hash';
export enum EthExplorerLinkTypes {
block = 'block',
address = 'address',
tx = 'tx',
}
export type EthExplorerLinkProps = Partial<typeof HTMLAnchorElement> & {
id: string;
type: EthExplorerLinkTypes;
};
export const EthExplorerLink = ({
id,
type,
...props
}: EthExplorerLinkProps) => {
const link = `${DATA_SOURCES.ethExplorerUrl}/${type}/${id}`;
return (
<a
className="underline external font-mono"
target="_blank"
rel="noopener noreferrer"
{...props}
href={link}
>
<Hash text={id} />
</a>
);
};
@@ -1,34 +0,0 @@
import type { ChainIdMapping } from './external-chain';
import { SUPPORTED_CHAIN_IDS, SUPPORTED_CHAIN_LABELS } from './external-chain';
export const SUPPORTED_CHAIN_ICON_URLS: ChainIdMapping = {
'1': '/assets/chain-eth-logo.svg',
'100': '/assets/chain-gno-logo.svg',
'42161': '/assets/chain-arb-logo.svg',
'11155111': '/assets/chain-eth-logo.svg',
};
export type ExternalChainIconProps = {
chainId?: string;
};
export const ExternalChainIcon = ({
// If chainID is not provided, default to a non-existent chain
chainId = '-1',
}: ExternalChainIconProps) => {
if (SUPPORTED_CHAIN_IDS.includes(chainId)) {
const url = SUPPORTED_CHAIN_ICON_URLS[chainId];
const alt = SUPPORTED_CHAIN_LABELS[chainId];
return (
<img
src={url}
className="inline-block w-4 h-4 mr-1 dark:invert"
alt={alt}
title={alt}
/>
);
} else {
return null;
}
};
@@ -1,38 +0,0 @@
export type ChainIdMapping = {
[K in typeof SUPPORTED_CHAIN_IDS[number]]: string;
};
export const SUPPORTED_CHAIN_IDS: string[] = ['1', '100', '42161', '11155111'];
export const SUPPORTED_CHAIN_LABELS: ChainIdMapping = {
'1': 'Ethereum',
'100': 'Gnosis',
'42161': 'Arbitrum',
'11155111': 'Sepolia',
};
export function getExternalExplorerLink(chainId: string, type: string) {
if (SUPPORTED_CHAIN_IDS.includes(chainId)) {
switch (chainId) {
case '1':
return 'https://etherscan.io';
case '100':
return 'https://gnosisscan.io';
case '42161':
return 'https://arbiscan.io';
case '11155111':
return 'https://sepolia.etherscan.io';
default:
return '#';
}
} else {
return '#';
}
}
export function getExternalChainLabel(chainId?: string) {
if (chainId && SUPPORTED_CHAIN_IDS.includes(chainId)) {
return SUPPORTED_CHAIN_LABELS[chainId];
} else {
return 'Custom Chain';
}
}
@@ -1,41 +0,0 @@
import Hash from '../hash';
import { getExternalExplorerLink } from './external-chain';
import { ExternalChainIcon } from './external-chain-icon';
export enum EthExplorerLinkTypes {
block = 'block',
address = 'address',
tx = 'tx',
}
export type ExternalExplorerLinkProps = Partial<typeof HTMLAnchorElement> & {
id: string;
type: EthExplorerLinkTypes;
// Defaults to Ethereum Mainnet, as chain support was added late
chain?: string;
code?: boolean;
};
export const ExternalExplorerLink = ({
id,
type,
chain = '1',
code = false,
...props
}: ExternalExplorerLinkProps) => {
const link = `${getExternalExplorerLink(chain, type)}/${type}/${id}${
code ? '#code' : ''
}`;
return (
<a
className="underline external font-mono"
target="_blank"
rel="noopener noreferrer"
{...props}
href={link}
>
<ExternalChainIcon chainId={chain} />
<Hash text={id} />
</a>
);
};
@@ -1,6 +1,5 @@
export type HashProps = React.HTMLProps<HTMLSpanElement> & { export type HashProps = {
text: string; text: string;
truncate?: boolean;
}; };
/** /**
@@ -8,16 +7,10 @@ export type HashProps = React.HTMLProps<HTMLSpanElement> & {
* are broken when they need to wrap. This will remove the need * are broken when they need to wrap. This will remove the need
* for a lot of the overflow scrolling that currently exists. * for a lot of the overflow scrolling that currently exists.
*/ */
const Hash = ({ text, truncate = false }: HashProps) => { const Hash = ({ text }: HashProps) => {
const h = truncate ? text.slice(0, 6) : text;
return ( return (
<code <code className="break-all font-mono" style={{ wordWrap: 'break-word' }}>
title={text} {text}
className="break-all font-mono"
style={{ wordWrap: 'break-word' }}
>
{h}
</code> </code>
); );
}; };
@@ -2,5 +2,4 @@ export { default as BlockLink } from './block-link/block-link';
export { default as PartyLink } from './party-link/party-link'; export { default as PartyLink } from './party-link/party-link';
export { default as NodeLink } from './node-link/node-link'; export { default as NodeLink } from './node-link/node-link';
export { default as MarketLink } from './market-link/market-link'; export { default as MarketLink } from './market-link/market-link';
export { default as NetworkParameterLink } from './network-parameter-link/network-parameter-link';
export * from './asset-link/asset-link'; export * from './asset-link/asset-link';
@@ -3,7 +3,6 @@ query ExplorerMarket($id: ID!) {
id id
decimalPlaces decimalPlaces
positionDecimalPlaces positionDecimalPlaces
state
tradableInstrument { tradableInstrument {
instrument { instrument {
name name
@@ -14,14 +13,9 @@ query ExplorerMarket($id: ID!) {
decimals decimals
} }
} }
... on Perpetual {
quoteName
settlementAsset {
decimals
}
}
} }
} }
} }
state
} }
} }
@@ -8,7 +8,7 @@ export type ExplorerMarketQueryVariables = Types.Exact<{
}>; }>;
export type ExplorerMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', decimals: number } } | { __typename?: 'Perpetual', quoteName: string, settlementAsset: { __typename?: 'Asset', decimals: number } } | { __typename?: 'Spot' } } } } | null }; export type ExplorerMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', decimals: number } } } } } | null };
export const ExplorerMarketDocument = gql` export const ExplorerMarketDocument = gql`
@@ -17,7 +17,6 @@ export const ExplorerMarketDocument = gql`
id id
decimalPlaces decimalPlaces
positionDecimalPlaces positionDecimalPlaces
state
tradableInstrument { tradableInstrument {
instrument { instrument {
name name
@@ -28,15 +27,10 @@ export const ExplorerMarketDocument = gql`
decimals decimals
} }
} }
... on Perpetual {
quoteName
settlementAsset {
decimals
}
}
} }
} }
} }
state
} }
} }
`; `;
@@ -61,11 +61,7 @@ describe('Market link component', () => {
instrument: { instrument: {
name: 'test-label', name: 'test-label',
product: { product: {
__typename: 'Future',
quoteName: 'dai', quoteName: 'dai',
settlementAsset: {
decimals: 8,
},
}, },
}, },
}, },
@@ -91,6 +87,6 @@ describe('Market link component', () => {
}; };
const res = render(renderComponent('123', [mock])); const res = render(renderComponent('123', [mock]));
expect(await res.findByTitle('123')).toBeInTheDocument(); expect(await res.findByText('123')).toBeInTheDocument();
}); });
}); });
@@ -23,7 +23,6 @@ const MarketLink = ({
}: MarketLinkProps) => { }: MarketLinkProps) => {
const { data, error, loading } = useExplorerMarketQuery({ const { data, error, loading } = useExplorerMarketQuery({
variables: { id }, variables: { id },
fetchPolicy: 'cache-first',
}); });
let label = <span>{id}</span>; let label = <span>{id}</span>;
@@ -1,30 +0,0 @@
import React from 'react';
import { Routes } from '../../../routes/route-names';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
import Hash from '../hash';
export type NetworkParameterLinkProps = Partial<ComponentProps<typeof Link>> & {
parameter: string;
};
/**
* Links a given network parameter to the relevant page and anchor on the page
*/
const NetworkParameterLink = ({
parameter,
...props
}: NetworkParameterLinkProps) => {
return (
<Link
className="underline"
{...props}
to={`/${Routes.NETWORK_PARAMETERS}#${parameter}`}
>
<Hash text={parameter} />
</Link>
);
};
export default NetworkParameterLink;
@@ -1,57 +0,0 @@
import OracleLink, { getStatusString } from './oracle-link';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
describe('getStatusString', () => {
it("returns 'Unknown' for undefined status", () => {
expect(getStatusString(undefined)).toBe('Unknown');
});
it('returns the correct string for a known status', () => {
expect(getStatusString('STATUS_ACTIVE')).toBe('Active');
expect(getStatusString('STATUS_DEACTIVATED')).toBe('Deactivated');
});
});
describe('OracleLink', () => {
it('renders the truncated Oracle ID', () => {
const id = '123456789';
render(
<MockedProvider>
<MemoryRouter>
<OracleLink id={id} />
</MemoryRouter>
</MockedProvider>
);
const idElement = screen.getByText(id.slice(0, 6));
expect(idElement).toBeInTheDocument();
expect(idElement).toHaveAttribute('title', id);
});
it('renders the Oracle status', () => {
const id = '123';
const status = 'STATUS_ACTIVE';
render(
<MockedProvider>
<MemoryRouter>
<OracleLink id={id} status={status} data-testid="link" />
</MemoryRouter>
</MockedProvider>
);
expect(screen.getByTestId('link')).toHaveAttribute('data-status', 'Active');
});
it('renders the Oracle data indicator', () => {
const id = '123';
const hasSeenOracleReports = true;
render(
<MockedProvider>
<MemoryRouter>
<OracleLink id={id} hasSeenOracleReports={hasSeenOracleReports} />
</MemoryRouter>
</MockedProvider>
);
expect(screen.getByTestId('oracle-data-indicator')).toBeInTheDocument();
});
});
@@ -3,93 +3,20 @@ import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react'; import type { ComponentProps } from 'react';
import Hash from '../hash'; import Hash from '../hash';
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import {
DataSourceSpecStatus,
DataSourceSpecStatusMapping,
} from '@vegaprotocol/types';
import { t } from '@vegaprotocol/i18n';
/**
* Returns a human-readable string for the given status, or a meaningful
* default if the status is unrecognised
* @param status string status
*/
export function getStatusString(status: string | undefined): string {
if (status && status in DataSourceSpecStatus) {
return DataSourceSpecStatusMapping[status as DataSourceSpecStatus];
}
return t('Unknown');
}
export type OracleLinkProps = Partial<ComponentProps<typeof Link>> & { export type OracleLinkProps = Partial<ComponentProps<typeof Link>> & {
// The Oracle ID
id: string; id: string;
// If available, the oracle status
status?: string;
// If the oracle has corresponding data in the OracleDataConnection
hasSeenOracleReports?: boolean;
}; };
/** const OracleLink = ({ id, ...props }: OracleLinkProps) => {
* Given an Oracle ID, renders a data-dense link to the Oracle page. Data density is achieved by:
* - Colour coding the link based on the Oracle's status
* - Showing a small indicator if the Oracle has matched data
* - Showing a tooltip with the Oracle's status and whether it has matched data
*
* @returns
*/
export const OracleLink = ({
id,
status,
hasSeenOracleReports = false,
...props
}: OracleLinkProps) => {
const bgColour =
status === 'STATUS_ACTIVE'
? 'bg-yellow-100 hover:bg-yellow-200 border-yellow-200 dark:bg-yellow-200 dark:border-yellow-200 dark:text-gray-900 dark:border-yellow-300'
: 'bg-gray-100 hover:bg-gray-200 border-gray-200';
const indicatorColour =
status === 'STATUS_ACTIVE'
? 'bg-yellow-300 hover:bg-yellow-500 dark:bg-yellow-500'
: 'bg-gray-300 hover:bg-gray-500';
const description = (
<div>
<p>
<strong>{`Status: `}</strong>
{getStatusString(status)}
</p>
<p>
<strong>{`Matched data: `}</strong>
{hasSeenOracleReports ? (
<VegaIcon name={VegaIconNames.TICK} />
) : (
<VegaIcon name={VegaIconNames.CROSS} />
)}
</p>
</div>
);
return ( return (
<Tooltip description={description}> <Link
<Link className="underline font-mono"
className={`pl-2 pr-2 font-mono dark:text-black ${bgColour} rounded-sm border-solid border-2 relative`} {...props}
{...props} to={`/${Routes.ORACLES}/${id}`}
to={`/${Routes.ORACLES}/${id}`} >
data-status={getStatusString(status)} <Hash text={id} />
> </Link>
<Hash text={id} truncate={true} />
{hasSeenOracleReports ? (
<strong
data-testid="oracle-data-indicator"
className={`absolute top-0 right-0 w-1 h-full font-thin ${indicatorColour}`}
title="Oracle has matched data"
></strong>
) : null}
</Link>
</Tooltip>
); );
}; };
@@ -32,17 +32,9 @@ export function getNameForParty(id: string, data?: ExplorerNodeNamesQuery) {
export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & { export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string; id: string;
truncate?: boolean; truncate?: boolean;
networkLabel?: string;
truncateLength?: number;
}; };
const PartyLink = ({ const PartyLink = ({ id, truncate = false, ...props }: PartyLinkProps) => {
id,
truncate = false,
truncateLength = 4,
networkLabel = t('Network'),
...props
}: PartyLinkProps) => {
const { data } = useExplorerNodeNamesQuery(); const { data } = useExplorerNodeNamesQuery();
const name = useMemo(() => getNameForParty(id, data), [data, id]); const name = useMemo(() => getNameForParty(id, data), [data, id]);
const useName = name !== id; const useName = name !== id;
@@ -52,7 +44,7 @@ const PartyLink = ({
if (id === SPECIAL_CASE_NETWORK || id === SPECIAL_CASE_NETWORK_ID) { if (id === SPECIAL_CASE_NETWORK || id === SPECIAL_CASE_NETWORK_ID) {
return ( return (
<span className="font-mono" data-testid="network"> <span className="font-mono" data-testid="network">
{networkLabel} {t('Network')}
</span> </span>
); );
} }
@@ -68,7 +60,7 @@ const PartyLink = ({
} }
return ( return (
<span> <span className="whitespace-nowrap">
{useName && <Icon size={4} name="cube" className="mr-2" />} {useName && <Icon size={4} name="cube" className="mr-2" />}
<Link <Link
className="underline font-mono" className="underline font-mono"
@@ -78,11 +70,7 @@ const PartyLink = ({
{useName ? ( {useName ? (
name name
) : ( ) : (
<Hash <Hash text={truncate ? truncateMiddle(id, 4, 4) : id} />
text={
truncate ? truncateMiddle(id, truncateLength, truncateLength) : id
}
/>
)} )}
</Link> </Link>
</span> </span>
@@ -1,11 +1,9 @@
query ExplorerProposal($id: ID!) { query ExplorerProposal($id: ID!) {
proposal(id: $id) { proposal(id: $id) {
... on Proposal { id
id rationale {
rationale { title
title description
description
}
} }
} }
} }
@@ -8,18 +8,16 @@ export type ExplorerProposalQueryVariables = Types.Exact<{
}>; }>;
export type ExplorerProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null }; export type ExplorerProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null };
export const ExplorerProposalDocument = gql` export const ExplorerProposalDocument = gql`
query ExplorerProposal($id: ID!) { query ExplorerProposal($id: ID!) {
proposal(id: $id) { proposal(id: $id) {
... on Proposal { id
id rationale {
rationale { title
title description
description
}
} }
} }
} }
@@ -3,11 +3,7 @@ import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing'; import type { MockedResponse } from '@apollo/client/testing';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import ProposalLink from './proposal-link'; import ProposalLink from './proposal-link';
import { import { ExplorerProposalDocument } from './__generated__/Proposal';
ExplorerProposalDocument,
type ExplorerProposalQuery,
type ExplorerProposalQueryVariables,
} from './__generated__/Proposal';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
function renderComponent(id: string, mocks: MockedResponse[]) { function renderComponent(id: string, mocks: MockedResponse[]) {
@@ -27,10 +23,7 @@ describe('Proposal link component', () => {
}); });
it('Renders the ID on error', async () => { it('Renders the ID on error', async () => {
const mock: MockedResponse< const mock = {
ExplorerProposalQuery,
ExplorerProposalQueryVariables
> = {
request: { request: {
query: ExplorerProposalDocument, query: ExplorerProposalDocument,
variables: { variables: {
@@ -47,22 +40,17 @@ describe('Proposal link component', () => {
}); });
it('Renders the proposal title when the query returns a result', async () => { it('Renders the proposal title when the query returns a result', async () => {
const proposalId = '123'; const mock = {
const mock: MockedResponse<
ExplorerProposalQuery,
ExplorerProposalQueryVariables
> = {
request: { request: {
query: ExplorerProposalDocument, query: ExplorerProposalDocument,
variables: { variables: {
id: proposalId, id: '123',
}, },
}, },
result: { result: {
data: { data: {
proposal: { proposal: {
__typename: 'Proposal', id: '123',
id: proposalId,
rationale: { rationale: {
title: 'test-title', title: 'test-title',
description: 'test description', description: 'test description',
@@ -72,16 +60,13 @@ describe('Proposal link component', () => {
}, },
}; };
const res = render(renderComponent(proposalId, [mock])); const res = render(renderComponent('123', [mock]));
expect(res.getByText(proposalId)).toBeInTheDocument(); expect(res.getByText('123')).toBeInTheDocument();
expect(await res.findByText('test-title')).toBeInTheDocument(); expect(await res.findByText('test-title')).toBeInTheDocument();
}); });
it('Leaves the proposal id when the market is not found', async () => { it('Leaves the proposal id when the market is not found', async () => {
const mock: MockedResponse< const mock = {
ExplorerProposalQuery,
ExplorerProposalQueryVariables
> = {
request: { request: {
query: ExplorerProposalDocument, query: ExplorerProposalDocument,
variables: { variables: {
@@ -1,11 +1,7 @@
import { import { useExplorerProposalQuery } from './__generated__/Proposal';
useExplorerProposalQuery,
type ExplorerProposalQuery,
} from './__generated__/Proposal';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { ENV } from '../../../config/env'; import { ENV } from '../../../config/env';
import Hash from '../hash'; import Hash from '../hash';
export type ProposalLinkProps = { export type ProposalLinkProps = {
id: string; id: string;
text?: string; text?: string;
@@ -20,13 +16,8 @@ const ProposalLink = ({ id, text }: ProposalLinkProps) => {
variables: { id }, variables: { id },
}); });
const proposal = data?.proposal as Extract<
ExplorerProposalQuery['proposal'],
{ __typename?: 'Proposal' }
>;
const base = ENV.dataSources.governanceUrl; const base = ENV.dataSources.governanceUrl;
const label = proposal?.rationale?.title || id; const label = data?.proposal?.rationale.title || id;
return ( return (
<ExternalLink href={`${base}/proposals/${id}`}> <ExternalLink href={`${base}/proposals/${id}`}>
@@ -1,42 +1,34 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/markets'; import type { MarketInfoWithData } from '@vegaprotocol/markets';
import { import { PriceMonitoringBoundsInfoPanel } from '@vegaprotocol/markets';
LiquidationStrategyInfoPanel,
LiquidityPriceRangeInfoPanel,
LiquiditySLAParametersInfoPanel,
MarginScalingFactorsPanel,
PriceMonitoringBoundsInfoPanel,
SuccessionLineInfoPanel,
getDataSourceSpecForSettlementData,
getDataSourceSpecForTradingTermination,
} from '@vegaprotocol/markets';
import { import {
LiquidityInfoPanel, LiquidityInfoPanel,
LiquidityMonitoringParametersInfoPanel, LiquidityMonitoringParametersInfoPanel,
InstrumentInfoPanel, InstrumentInfoPanel,
KeyDetailsInfoPanel, KeyDetailsInfoPanel,
LiquidityPriceRangeInfoPanel,
MetadataInfoPanel, MetadataInfoPanel,
OracleInfoPanel, OracleInfoPanel,
RiskFactorsInfoPanel, RiskFactorsInfoPanel,
RiskModelInfoPanel, RiskModelInfoPanel,
RiskParametersInfoPanel,
SettlementAssetInfoPanel, SettlementAssetInfoPanel,
} from '@vegaprotocol/markets'; } from '@vegaprotocol/markets';
import { MarketInfoTable } from '@vegaprotocol/markets'; import { MarketInfoTable } from '@vegaprotocol/markets';
import type { DataSourceFragment } from '@vegaprotocol/markets'; import type { DataSourceDefinition } from '@vegaprotocol/types';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => { export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
if (!market) return null; if (!market) return null;
const { product } = market.tradableInstrument.instrument;
const settlementDataSource = getDataSourceSpecForSettlementData(product);
const terminationDataSource = getDataSourceSpecForTradingTermination(product);
const getSigners = ({ data }: DataSourceFragment) => { const settlementData = market.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
const terminationData = market.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
const getSigners = (data: DataSourceDefinition) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') { if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
const signers = const signers = data.sourceType.sourceType.signers || [];
('signers' in data.sourceType.sourceType &&
data.sourceType.sourceType.signers) ||
[];
return signers.map(({ signer }, i) => { return signers.map(({ signer }, i) => {
return ( return (
@@ -48,13 +40,10 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
return []; return [];
}; };
const showTwoOracles = const showTwoOracles = isEqual(
settlementDataSource && getSigners(settlementData),
terminationDataSource && getSigners(terminationData)
isEqual( );
getSigners(settlementDataSource),
getSigners(terminationDataSource)
);
const headerClassName = 'font-alpha calt text-xl mt-4 border-b-2 pb-2'; const headerClassName = 'font-alpha calt text-xl mt-4 border-b-2 pb-2';
@@ -70,8 +59,8 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
<MetadataInfoPanel market={market} /> <MetadataInfoPanel market={market} />
<h2 className={headerClassName}>{t('Risk model')}</h2> <h2 className={headerClassName}>{t('Risk model')}</h2>
<RiskModelInfoPanel market={market} /> <RiskModelInfoPanel market={market} />
<h2 className={headerClassName}>{t('Margin scaling factors')}</h2> <h2 className={headerClassName}>{t('Risk parameters')}</h2>
<MarginScalingFactorsPanel market={market} /> <RiskParametersInfoPanel market={market} />
<h2 className={headerClassName}>{t('Risk factors')}</h2> <h2 className={headerClassName}>{t('Risk factors')}</h2>
<RiskFactorsInfoPanel market={market} /> <RiskFactorsInfoPanel market={market} />
{(market.data?.priceMonitoringBounds || []).map((trigger, i) => ( {(market.data?.priceMonitoringBounds || []).map((trigger, i) => (
@@ -95,16 +84,12 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
</> </>
) )
)} )}
<h2 className={headerClassName}>{t('Liquidation strategy')}</h2>
<LiquidationStrategyInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity monitoring')}</h2> <h2 className={headerClassName}>{t('Liquidity monitoring')}</h2>
<LiquidityMonitoringParametersInfoPanel market={market} /> <LiquidityMonitoringParametersInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity price range')}</h2>
<LiquidityPriceRangeInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity SLA protocol')}</h2>
<LiquiditySLAParametersInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity')}</h2> <h2 className={headerClassName}>{t('Liquidity')}</h2>
<LiquidityInfoPanel market={market} /> <LiquidityInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity price range')}</h2>
<LiquidityPriceRangeInfoPanel market={market} />
{showTwoOracles ? ( {showTwoOracles ? (
<> <>
<h2 className={headerClassName}>{t('Settlement oracle')}</h2> <h2 className={headerClassName}>{t('Settlement oracle')}</h2>
@@ -118,8 +103,6 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
<OracleInfoPanel market={market} type="settlementData" /> <OracleInfoPanel market={market} type="settlementData" />
</> </>
)} )}
<h2 className={`${headerClassName} mb-4`}>{t('Succession line')}</h2>
<SuccessionLineInfoPanel market={market} />
</div> </div>
); );
}; };
@@ -1,19 +1,19 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { getAsset, type MarketFieldsFragment } from '@vegaprotocol/markets'; import type { MarketFieldsFragment } from '@vegaprotocol/markets';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { ButtonLink } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { type AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import { type ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import { AgGrid } from '@vegaprotocol/datagrid'; import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { import type {
type VegaICellRendererParams, VegaICellRendererParams,
type VegaValueGetterParams, VegaValueGetterParams,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import { useRef, useLayoutEffect } from 'react'; import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints'; import { BREAKPOINT_MD } from '../../config/breakpoints';
import { MarketStateMapping } from '@vegaprotocol/types'; import { MarketStateMapping } from '@vegaprotocol/types';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { type RowClickedEvent } from 'ag-grid-community'; import type { RowClickedEvent } from 'ag-grid-community';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
type MarketsTableProps = { type MarketsTableProps = {
@@ -29,7 +29,7 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
const gridRef = useRef<AgGridReact>(null); const gridRef = useRef<AgGridReact>(null);
useLayoutEffect(() => { useLayoutEffect(() => {
const showColumnsOnDesktop = () => { const showColumnsOnDesktop = () => {
gridRef.current?.api.setColumnsVisible( gridRef.current?.columnApi.setColumnsVisible(
['id', 'state', 'asset'], ['id', 'state', 'asset'],
window.innerWidth > BREAKPOINT_MD window.innerWidth > BREAKPOINT_MD
); );
@@ -73,7 +73,8 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
MarketFieldsFragment, MarketFieldsFragment,
'tradableInstrument.instrument.product.settlementAsset.symbol' 'tradableInstrument.instrument.product.settlementAsset.symbol'
>) => { >) => {
const value = data && getAsset(data); const value =
data?.tradableInstrument.instrument.product.settlementAsset;
return value ? ( return value ? (
<ButtonLink <ButtonLink
onClick={(e) => { onClick={(e) => {
@@ -1,5 +1,9 @@
import { render, waitFor } from '@testing-library/react'; import { render, waitFor } from '@testing-library/react';
import { NestedDataList, sortNestedDataByChildren } from './nested-data-list'; import {
BORDER_COLOURS,
NestedDataList,
sortNestedDataByChildren,
} from './nested-data-list';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
const mockData = { const mockData = {
@@ -57,6 +61,38 @@ describe('NestedDataList', () => {
expect(parent[0].querySelector('li')).toHaveClass('pl-4 border-l-4 pt-2'); expect(parent[0].querySelector('li')).toHaveClass('pl-4 border-l-4 pt-2');
}); });
it('should repeat the border colours in the correct order', () => {
const colourMockData = {
t0: {
t1: {
t2: {
t3: {
t4: {
t5: {
t6: {
t7: {
t8: {
hello: 'world',
},
},
},
},
},
},
},
},
},
};
const tree = render(<NestedDataList data={colourMockData} />);
const { getByTestId } = tree;
for (let i = 0; i < 8; i++) {
const item = getByTestId(`T${i}`);
const expected = BORDER_COLOURS.light[i % 5];
expect(item.style.borderColor.toUpperCase()).toBe(expected);
}
});
it('should sort the data by values with children', () => { it('should sort the data by values with children', () => {
const mockData = { const mockData = {
nonce: '5980890939790185837', nonce: '5980890939790185837',
@@ -31,9 +31,6 @@ fragment ExplorerDeterministicOrderFields on Order {
... on Future { ... on Future {
quoteName quoteName
} }
... on Perpetual {
quoteName
}
} }
} }
} }
@@ -1,25 +0,0 @@
fragment ExplorerStopOrderFields on StopOrder {
id
status
createdAt
trigger {
... on StopOrderPrice {
price
}
... on StopOrderTrailingPercentOffset {
trailingPercentOffset
}
}
createdAt
ocoLinkId
triggerDirection
order {
id
}
}
query ExplorerStopOrder($stopOrderId: ID!) {
stopOrder(id: $stopOrderId) {
...ExplorerStopOrderFields
}
}
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client'; import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; const defaultOptions = {} as const;
export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } | { __typename?: 'Perpetual', quoteName: string } | { __typename?: 'Spot' } } } } }; export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } };
export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{ export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{
orderId: Types.Scalars['ID']; orderId: Types.Scalars['ID'];
@@ -11,7 +11,7 @@ export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{
}>; }>;
export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } | { __typename?: 'Perpetual', quoteName: string } | { __typename?: 'Spot' } } } } } }; export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } } };
export const ExplorerDeterministicOrderFieldsFragmentDoc = gql` export const ExplorerDeterministicOrderFieldsFragmentDoc = gql`
fragment ExplorerDeterministicOrderFields on Order { fragment ExplorerDeterministicOrderFields on Order {
@@ -47,9 +47,6 @@ export const ExplorerDeterministicOrderFieldsFragmentDoc = gql`
... on Future { ... on Future {
quoteName quoteName
} }
... on Perpetual {
quoteName
}
} }
} }
} }
@@ -1,70 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerStopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, status: Types.StopOrderStatus, createdAt: any, ocoLinkId?: string | null, triggerDirection: Types.StopOrderTriggerDirection, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, order?: { __typename?: 'Order', id: string } | null };
export type ExplorerStopOrderQueryVariables = Types.Exact<{
stopOrderId: Types.Scalars['ID'];
}>;
export type ExplorerStopOrderQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, status: Types.StopOrderStatus, createdAt: any, ocoLinkId?: string | null, triggerDirection: Types.StopOrderTriggerDirection, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, order?: { __typename?: 'Order', id: string } | null } | null };
export const ExplorerStopOrderFieldsFragmentDoc = gql`
fragment ExplorerStopOrderFields on StopOrder {
id
status
createdAt
trigger {
... on StopOrderPrice {
price
}
... on StopOrderTrailingPercentOffset {
trailingPercentOffset
}
}
createdAt
ocoLinkId
triggerDirection
order {
id
}
}
`;
export const ExplorerStopOrderDocument = gql`
query ExplorerStopOrder($stopOrderId: ID!) {
stopOrder(id: $stopOrderId) {
...ExplorerStopOrderFields
}
}
${ExplorerStopOrderFieldsFragmentDoc}`;
/**
* __useExplorerStopOrderQuery__
*
* To run a query within a React component, call `useExplorerStopOrderQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerStopOrderQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerStopOrderQuery({
* variables: {
* stopOrderId: // value for 'stopOrderId'
* },
* });
*/
export function useExplorerStopOrderQuery(baseOptions: Apollo.QueryHookOptions<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>(ExplorerStopOrderDocument, options);
}
export function useExplorerStopOrderLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>(ExplorerStopOrderDocument, options);
}
export type ExplorerStopOrderQueryHookResult = ReturnType<typeof useExplorerStopOrderQuery>;
export type ExplorerStopOrderLazyQueryHookResult = ReturnType<typeof useExplorerStopOrderLazyQuery>;
export type ExplorerStopOrderQueryResult = Apollo.QueryResult<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>;
@@ -17,7 +17,7 @@ function renderAmendOrderDetails(
mocks: MockedResponse[] mocks: MockedResponse[]
) { ) {
return render( return render(
<MockedProvider mocks={mocks}> <MockedProvider mocks={mocks} addTypename={false}>
<MemoryRouter> <MemoryRouter>
<AmendOrderDetails version={version} id={id} amend={amend} /> <AmendOrderDetails version={version} id={id} amend={amend} />
</MemoryRouter> </MemoryRouter>
@@ -44,18 +44,18 @@ function renderExistingAmend(
orderByID: { orderByID: {
__typename: 'Order', __typename: 'Order',
id: '123', id: '123',
type: Schema.OrderType.TYPE_LIMIT, type: 'GTT',
status: Schema.OrderStatus.STATUS_ACTIVE, status: Schema.OrderStatus.STATUS_ACTIVE,
version: version ? version.toString() : '', version: version,
createdAt: '123', createdAt: '123',
updatedAt: '456', updatedAt: '456',
expiresAt: '789', expiresAt: '789',
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC, timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
price: '200', price: '200',
side: Schema.Side.SIDE_BUY, side: 'BUY',
peggedOrder: null, peggedOrder: null,
remaining: '99', remaining: '99',
rejectionReason: Schema.OrderRejectionReason.ORDER_ERROR_NOT_FOUND, rejectionReason: 'rejection',
reference: '123', reference: '123',
size: '100', size: '100',
party: { party: {
@@ -65,9 +65,9 @@ function renderExistingAmend(
market: { market: {
__typename: 'Market', __typename: 'Market',
id: '789', id: '789',
state: Schema.MarketState.STATE_ACTIVE, state: 'STATUS_ACTIVE',
positionDecimalPlaces: 2, positionDecimalPlaces: 2,
decimalPlaces: 0, decimalPlaces: '5',
tradableInstrument: { tradableInstrument: {
instrument: { instrument: {
name: 'test', name: 'test',
@@ -94,18 +94,18 @@ function renderExistingAmend(
orderByID: { orderByID: {
__typename: 'Order', __typename: 'Order',
id: '123', id: '123',
type: Schema.OrderType.TYPE_LIMIT, type: 'GTT',
status: Schema.OrderStatus.STATUS_ACTIVE, status: Schema.OrderStatus.STATUS_ACTIVE,
version: '100', version: 100,
createdAt: '123', createdAt: '123',
updatedAt: '456', updatedAt: '456',
expiresAt: '789', expiresAt: '789',
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC, timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
peggedOrder: null, peggedOrder: null,
price: '200', price: '200',
side: Schema.Side.SIDE_BUY, side: 'BUY',
remaining: '99', remaining: '99',
rejectionReason: Schema.OrderRejectionReason.ORDER_ERROR_NOT_FOUND, rejectionReason: 'rejection',
reference: '123', reference: '123',
size: '200', size: '200',
party: { party: {
@@ -114,10 +114,10 @@ function renderExistingAmend(
}, },
market: { market: {
__typename: 'Market', __typename: 'Market',
id: '8888', id: 'amend-to-order-latest-version',
state: Schema.MarketState.STATE_ACTIVE, state: 'STATUS_ACTIVE',
positionDecimalPlaces: 2, positionDecimalPlaces: 2,
decimalPlaces: 0, decimalPlaces: '5',
tradableInstrument: { tradableInstrument: {
instrument: { instrument: {
name: 'amend-to-order-latest-version-test', name: 'amend-to-order-latest-version-test',
@@ -136,13 +136,13 @@ function renderExistingAmend(
request: { request: {
query: ExplorerMarketDocument, query: ExplorerMarketDocument,
variables: { variables: {
id: '8888', id: '789',
}, },
}, },
result: { result: {
data: { data: {
market: { market: {
id: '8888', id: '789',
decimalPlaces: 5, decimalPlaces: 5,
positionDecimalPlaces: 2, positionDecimalPlaces: 2,
state: 'irrelevant-test-data', state: 'irrelevant-test-data',
@@ -150,11 +150,7 @@ function renderExistingAmend(
instrument: { instrument: {
name: 'test-label', name: 'test-label',
product: { product: {
__typename: 'Future',
quoteName: 'dai', quoteName: 'dai',
settlementAsset: {
decimals: 8,
},
}, },
}, },
}, },
@@ -175,7 +171,6 @@ describe('Amend order details', () => {
const res = renderExistingAmend('123', 1, amend); const res = renderExistingAmend('123', 1, amend);
expect(await res.findByText('New size')).toBeInTheDocument(); expect(await res.findByText('New size')).toBeInTheDocument();
expect(await res.findByText('Size ±')).toBeInTheDocument();
}); });
it('Renders Reference if provided', async () => { it('Renders Reference if provided', async () => {
@@ -220,10 +215,12 @@ describe('Amend order details', () => {
it('Fetches latest version when version is not specified', async () => { it('Fetches latest version when version is not specified', async () => {
const amend: Amend = { const amend: Amend = {
price: '123', price: '-7879',
}; };
const res = renderExistingAmend('123', undefined, amend); const res = renderExistingAmend('123', undefined, amend);
expect(await res.findByText('test-label')).toBeInTheDocument(); expect(
await res.findByText('amend-to-order-latest-version')
).toBeInTheDocument();
}); });
}); });
@@ -46,10 +46,10 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
<div className={wrapperClasses}> <div className={wrapperClasses}>
<div className="mb-12 lg:mb-0"> <div className="mb-12 lg:mb-0">
<div className="relative block rounded-lg px-3 py-6 md:px-6 lg:-mr-7"> <div className="relative block rounded-lg px-3 py-6 md:px-6 lg:-mr-7">
<h2 className="display-5 mb-4 text-3xl font-bold"> <h2 className="text-3xl font-bold mb-4 display-5">
{t('Order not found')} {t('Order not found')}
</h2> </h2>
<p className="mb-12 text-gray-500"> <p className="text-gray-500 mb-12">
{t('No order created from this transaction')} {t('No order created from this transaction')}
</p> </p>
</div> </div>
@@ -68,24 +68,24 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
<div className={wrapperClasses}> <div className={wrapperClasses}>
<div className="mb-12 lg:mb-0"> <div className="mb-12 lg:mb-0">
<div className="relative block px-3 py-6 md:px-6 lg:-mr-7"> <div className="relative block px-3 py-6 md:px-6 lg:-mr-7">
<h2 className="display-5 mb-4 text-3xl font-bold"> <h2 className="text-3xl font-bold mb-4 display-5">
{t('Edits to ')} {t('Edits to ')}
{sideText[o.side]} {sideText[o.side]}
{t(' order')} {t(' order')}
</h2> </h2>
<div className="mb-4 text-gray-500"> <p className="text-gray-500 mb-4">
In <MarketLink id={o.market.id} />, updated at{' '} In <MarketLink id={o.market.id} />, updated at{' '}
<Time date={o.updatedAt} />. <Time date={o.updatedAt} />.
</div> </p>
<div className="grid gap-x-6 md:grid-cols-4"> <div className="grid md:grid-cols-4 gap-x-6">
{amend.sizeDelta && amend.sizeDelta !== '0' ? ( {amend.sizeDelta && amend.sizeDelta !== '0' ? (
<div className="mb-12 md:mb-0"> <div className="mb-12 md:mb-0">
<h2 className="text-dark mb-4 text-2xl font-bold"> <h2 className="text-2xl font-bold text-dark mb-4">
{t('Size ±')} {t('New size')}
</h2> </h2>
<h5 <h5
className={`mb-0 text-lg font-medium capitalize text-gray-500 ${getSideDeltaColour( className={`text-lg font-medium text-gray-500 mb-0 capitalize ${getSideDeltaColour(
amend.sizeDelta amend.sizeDelta
)}`} )}`}
> >
@@ -93,23 +93,13 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
</h5> </h5>
</div> </div>
) : null} ) : null}
{o && (
<div className="">
<h2 className="text-dark mb-4 text-2xl font-bold">
{t('New size')}
</h2>
<h5 className="mb-0 text-lg font-medium text-gray-500">
{o ? o.size : null}
</h5>
</div>
)}
{amend.price && amend.price !== '0' ? ( {amend.price && amend.price !== '0' ? (
<div className=""> <div className="">
<h2 className="text-dark mb-4 text-2xl font-bold"> <h2 className="text-2xl font-bold text-dark mb-4">
{t('New price')} {t('New price')}
</h2> </h2>
<h5 className="mb-0 text-lg font-medium text-gray-500"> <h5 className="text-lg font-medium text-gray-500 mb-0">
<PriceInMarket price={amend.price} marketId={o.market.id} /> <PriceInMarket price={amend.price} marketId={o.market.id} />
</h5> </h5>
</div> </div>
@@ -118,10 +108,10 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
{amend.peggedReference && {amend.peggedReference &&
amend.peggedReference !== 'PEGGED_REFERENCE_UNSPECIFIED' ? ( amend.peggedReference !== 'PEGGED_REFERENCE_UNSPECIFIED' ? (
<div className=""> <div className="">
<h2 className="text-dark mb-4 text-2xl font-bold"> <h2 className="text-2xl font-bold text-dark mb-4">
{t('New reference')} {t('New reference')}
</h2> </h2>
<h5 className="mb-0 text-lg font-medium text-gray-500"> <h5 className="text-lg font-medium text-gray-500 mb-0">
{peggedReference[amend.peggedReference]} {peggedReference[amend.peggedReference]}
</h5> </h5>
</div> </div>
@@ -129,10 +119,10 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
{amend.peggedOffset ? ( {amend.peggedOffset ? (
<div className=""> <div className="">
<h2 className="text-dark mb-4 text-2xl font-bold"> <h2 className="text-2xl font-bold text-dark mb-4">
{t('New offset')} {t('New offset')}
</h2> </h2>
<h5 className="mb-0 text-lg font-medium text-gray-500"> <h5 className="text-lg font-medium text-gray-500 mb-0">
{amend.peggedOffset} {amend.peggedOffset}
</h5> </h5>
</div> </div>
@@ -6,7 +6,6 @@ import { Time } from '../time';
import { sideText, statusText, tifFull, tifShort } from './lib/order-labels'; import { sideText, statusText, tifFull, tifShort } from './lib/order-labels';
import SizeInMarket from '../size-in-market/size-in-market'; import SizeInMarket from '../size-in-market/size-in-market';
import { TxOrderPeggedReference } from '../txs/details/order/tx-order-peg'; import { TxOrderPeggedReference } from '../txs/details/order/tx-order-peg';
import { OrderTypeMapping } from '@vegaprotocol/types';
export interface DeterministicOrderDetailsProps { export interface DeterministicOrderDetailsProps {
id: string; id: string;
@@ -60,7 +59,7 @@ const DeterministicOrderDetails = ({
const o = data.orderByID; const o = data.orderByID;
return ( return (
<div className={wrapperClasses}> <div className={wrapperClasses}>
<div className="mb-0"> <div className="mb-12 lg:mb-0">
<div className="relative block px-3 py-6 md:px-6 lg:-mr-7"> <div className="relative block px-3 py-6 md:px-6 lg:-mr-7">
<h2 className="text-3xl font-bold mb-4 display-5"> <h2 className="text-3xl font-bold mb-4 display-5">
<abbr title={tifFull[o.timeInForce]} className="bb-dotted mr-2"> <abbr title={tifFull[o.timeInForce]} className="bb-dotted mr-2">
@@ -70,7 +69,7 @@ const DeterministicOrderDetails = ({
<span className="mx-5 text-base">@</span> <span className="mx-5 text-base">@</span>
<PriceInMarket price={o.price} marketId={o.market.id} /> <PriceInMarket price={o.price} marketId={o.market.id} />
</h2> </h2>
<p className="text-gray-400 dark:text-gray-600"> <p className="text-gray-200">
In <MarketLink id={o.market.id} /> at <Time date={o.createdAt} />. In <MarketLink id={o.market.id} /> at <Time date={o.createdAt} />.
</p> </p>
{o.peggedOrder ? ( {o.peggedOrder ? (
@@ -84,14 +83,15 @@ const DeterministicOrderDetails = ({
/> />
</p> </p>
) : null} ) : null}
{o.reference ? ( {o.reference ? (
<p className="text-gray-500 mt-4"> <p className="text-gray-500 mt-4">
<span>{t('Reference')}</span>: {o.reference} <span>{t('Reference')}</span>: {o.reference}
</p> </p>
) : null} ) : null}
<div className="grid grid-cols-2 md:grid-cols-5 gap-x-6 mt-4"> <div className="grid md:grid-cols-4 gap-x-6 mt-4">
<div className="mb-6 md:mb-0"> <div className="mb-12 md:mb-0">
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4"> <h2 className="text-2xl font-bold text-dark mb-4">
{t('Status')} {t('Status')}
</h2> </h2>
<h5 className="text-lg font-medium text-gray-500 mb-0 capitalize"> <h5 className="text-lg font-medium text-gray-500 mb-0 capitalize">
@@ -99,33 +99,21 @@ const DeterministicOrderDetails = ({
</h5> </h5>
</div> </div>
<div className="mb-6 md:mb-0"> <div className="mb-12 md:mb-0">
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4"> <h2 className="text-2xl font-bold text-dark mb-4">{t('Size')}</h2>
{t('Size')}
</h2>
<h5 className="text-lg font-medium text-gray-500 mb-0"> <h5 className="text-lg font-medium text-gray-500 mb-0">
<SizeInMarket size={o.size} marketId={o.market.id} /> <SizeInMarket size={o.size} marketId={o.market.id} />
</h5> </h5>
</div> </div>
<div className="mb-6 md:mb-0"> <div className="">
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4"> <h2 className="text-2xl font-bold text-dark mb-4">
{t('Version')} {t('Version')}
</h2> </h2>
<h5 className="text-lg font-medium text-gray-500 mb-0"> <h5 className="text-lg font-medium text-gray-500 mb-0">
{o.version} {o.version}
</h5> </h5>
</div> </div>
{o.type ? (
<div className="mb-6 md:mb-0">
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4">
{t('Type')}
</h2>
<h5 className="text-lg font-medium text-gray-500 mb-0">
{OrderTypeMapping[o.type]}
</h5>
</div>
) : null}
</div> </div>
</div> </div>
</div> </div>
@@ -33,7 +33,7 @@ describe('Order TX Summary component', () => {
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument(); expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
}); });
it('Renders "Market Price" if the order passed lacks a price', () => { it('Renders nothing if the order passed lacks a price', () => {
const o: Order = { const o: Order = {
marketId: '123', marketId: '123',
side: 'SIDE_BUY', side: 'SIDE_BUY',
@@ -41,7 +41,7 @@ describe('Order TX Summary component', () => {
size: '10', size: '10',
}; };
const res = renderComponent(o); const res = renderComponent(o);
expect(res.queryByText('Market Price')).toBeInTheDocument(); expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
}); });
it('Renders nothing if the order has an unspecified side', () => { it('Renders nothing if the order has an unspecified side', () => {
@@ -3,7 +3,6 @@ import type { components } from '../../../types/explorer';
import PriceInMarket from '../price-in-market/price-in-market'; import PriceInMarket from '../price-in-market/price-in-market';
import { sideText } from '../order-details/lib/order-labels'; import { sideText } from '../order-details/lib/order-labels';
import SizeInMarket from '../size-in-market/size-in-market'; import SizeInMarket from '../size-in-market/size-in-market';
import { t } from '@vegaprotocol/i18n';
export type OrderSummaryProps = { export type OrderSummaryProps = {
order: components['schemas']['v1OrderSubmission']; order: components['schemas']['v1OrderSubmission'];
@@ -21,6 +20,7 @@ const OrderTxSummary = ({ order }: OrderSummaryProps) => {
if ( if (
!order || !order ||
!order.marketId || !order.marketId ||
!order.price ||
!order.side || !order.side ||
order.side === 'SIDE_UNSPECIFIED' order.side === 'SIDE_UNSPECIFIED'
) { ) {
@@ -36,14 +36,10 @@ const OrderTxSummary = ({ order }: OrderSummaryProps) => {
'-' '-'
)} )}
&nbsp;<i className="text-xs">@</i>&nbsp; &nbsp;<i className="text-xs">@</i>&nbsp;
{order.price ? ( <PriceInMarket
<PriceInMarket marketId={order.marketId}
marketId={order.marketId} price={order.price}
price={order.price} ></PriceInMarket>
></PriceInMarket>
) : (
t('Market Price')
)}
</div> </div>
); );
}; };
@@ -33,8 +33,6 @@ const PriceInMarket = ({
label = addDecimalsFormatNumber(price, data.market.decimalPlaces); label = addDecimalsFormatNumber(price, data.market.decimalPlaces);
} else if ( } else if (
decimalSource === 'SETTLEMENT_ASSET' && decimalSource === 'SETTLEMENT_ASSET' &&
data.market &&
'settlementAsset' in data.market.tradableInstrument.instrument.product &&
data.market?.tradableInstrument.instrument.product.settlementAsset data.market?.tradableInstrument.instrument.product.settlementAsset
) { ) {
label = addDecimalsFormatNumber( label = addDecimalsFormatNumber(
@@ -1,17 +1,22 @@
import { type ProposalListFieldsFragment } from '@vegaprotocol/proposals'; import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
import { type AgGridReact } from 'ag-grid-react'; import { VoteProgress } from '@vegaprotocol/proposals';
import type { AgGridReact } from 'ag-grid-react';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { AgGrid } from '@vegaprotocol/datagrid'; import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { import type {
type VegaICellRendererParams, VegaICellRendererParams,
type VegaValueFormatterParams, VegaValueFormatterParams,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import { useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useLayoutEffect, useMemo, useRef, useState } from 'react';
import { type ColDef } from 'ag-grid-community'; import type { RowClickedEvent, ColDef } from 'ag-grid-community';
import type { RowClickedEvent } from 'ag-grid-community';
import { getDateTimeFormat } from '@vegaprotocol/utils'; import { getDateTimeFormat } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { ProposalStateMapping } from '@vegaprotocol/types'; import { ProposalStateMapping } from '@vegaprotocol/types';
import BigNumber from 'bignumber.js';
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment'; import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
import { BREAKPOINT_MD } from '../../config/breakpoints'; import { BREAKPOINT_MD } from '../../config/breakpoints';
import { JsonViewerDialog } from '../dialogs/json-viewer-dialog'; import { JsonViewerDialog } from '../dialogs/json-viewer-dialog';
@@ -25,16 +30,24 @@ type ProposalsTableProps = {
data: ProposalListFieldsFragment[] | null; data: ProposalListFieldsFragment[] | null;
}; };
export const ProposalsTable = ({ data }: ProposalsTableProps) => { export const ProposalsTable = ({ data }: ProposalsTableProps) => {
const tokenLink = useLinks(DApp.Governance); const { params } = useNetworkParams([
NetworkParams.governance_proposal_market_requiredMajority,
]);
const tokenLink = useLinks(DApp.Token);
const requiredMajorityPercentage = useMemo(() => {
const requiredMajority =
params?.governance_proposal_market_requiredMajority ?? 1;
return new BigNumber(requiredMajority).times(100);
}, [params?.governance_proposal_market_requiredMajority]);
const gridRef = useRef<AgGridReact>(null); const gridRef = useRef<AgGridReact>(null);
useLayoutEffect(() => { useLayoutEffect(() => {
const showColumnsOnDesktop = () => { const showColumnsOnDesktop = () => {
gridRef.current?.api.setColumnsVisible( gridRef.current?.columnApi.setColumnsVisible(
['voting', 'cDate', 'eDate', 'type'], ['voting', 'cDate', 'eDate', 'type'],
window.innerWidth > BREAKPOINT_MD window.innerWidth > BREAKPOINT_MD
); );
gridRef.current?.api.setColumnWidth( gridRef.current?.columnApi.setColumnWidth(
'actions', 'actions',
window.innerWidth > BREAKPOINT_MD ? 221 : 80 window.innerWidth > BREAKPOINT_MD ? 221 : 80
); );
@@ -76,6 +89,33 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
return value ? ProposalStateMapping[value] : '-'; return value ? ProposalStateMapping[value] : '-';
}, },
}, },
{
colId: 'voting',
maxWidth: 100,
hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Voting'),
cellRenderer: ({
data,
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
if (data) {
const yesTokens = new BigNumber(data.votes.yes.totalTokens);
const noTokens = new BigNumber(data.votes.no.totalTokens);
const totalTokensVoted = yesTokens.plus(noTokens);
const yesPercentage = totalTokensVoted.isZero()
? new BigNumber(0)
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
return (
<div className="uppercase flex h-full items-center justify-center pt-2">
<VoteProgress
threshold={requiredMajorityPercentage}
progress={yesPercentage}
/>
</div>
);
}
return '-';
},
},
{ {
colId: 'cDate', colId: 'cDate',
maxWidth: 150, maxWidth: 150,
@@ -97,7 +137,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
hide: window.innerWidth <= BREAKPOINT_MD, hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Enactment date'), headerName: t('Enactment date'),
field: 'terms.enactmentDatetime', field: 'terms.enactmentDatetime',
valueFormatter: ({ valueFormatte: ({
value, value,
}: VegaValueFormatterParams< }: VegaValueFormatterParams<
ProposalListFieldsFragment, ProposalListFieldsFragment,
@@ -143,7 +183,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
}, },
}, },
], ],
[tokenLink] [requiredMajorityPercentage, tokenLink]
); );
return ( return (
<> <>
@@ -1,59 +0,0 @@
import { useState } from 'react';
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
import {
CopyWithTooltip,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
interface SignatureProps {
signature: BlockExplorerTransactionResult['signature'];
}
const valueClass =
'font-mono px-2.5 py-0.5 text-xs max-w-[200px] cursor-pointer';
const valueClassClosed = 'text-ellipsis overflow-hidden';
const valueClassOpen = 'break-words text-left';
/**
* Viewer component for a vega signature. Featuers copy and pasting, truncation
*
* @param signature
*/
export const Signature = ({ signature }: SignatureProps) => {
const [isOpen, setIsOpen] = useState(false);
if (!signature || !signature.value || !signature.version || !signature.algo) {
return null;
}
return (
<div className="inline-flex border rounded signature-component relative pr-[20px]">
<div
className="bg-gray-100 px-2.5 py-0.5 text-xs text-gray-500 select-none cursor-default"
title={`${signature.algo}`}
>
<span>v{signature.version}</span>
</div>
<div
className={
isOpen
? `${valueClass} ${valueClassOpen}`
: `${valueClass} ${valueClassClosed}`
}
>
<CopyWithTooltip text={signature.value}>
<span title={signature.value}>{signature.value}</span>
</CopyWithTooltip>
</div>
<button
onClick={() => setIsOpen(!isOpen)}
className="absolute top-[-3px] right-0 pr-2"
title={t('Show full signature')}
>
<VegaIcon name={isOpen ? VegaIconNames.EYE_OFF : VegaIconNames.EYE} />
</button>
</div>
);
};
@@ -22,9 +22,5 @@ export const TimeAgo = ({ date, ...props }: TimeAgoProps) => {
return <>{t('Date unknown')}</>; return <>{t('Date unknown')}</>;
} }
return ( return <span {...props}>{t(`${distanceToNow} ago`)}</span>;
<span {...props} title={date} className="underline decoration-dotted">
{t(`${distanceToNow} ago`)}
</span>
);
}; };
@@ -15,7 +15,6 @@ import isUndefined from 'lodash/isUndefined';
import type { BlockExplorerTransactionResult } from '../../../../routes/types/block-explorer-response'; import type { BlockExplorerTransactionResult } from '../../../../routes/types/block-explorer-response';
import { TxDetailsChainEventWithdrawal } from './tx-erc20-withdrawal'; import { TxDetailsChainEventWithdrawal } from './tx-erc20-withdrawal';
import { TxDetailsChainEventErc20AssetDelist } from './tx-erc20-asset-delist'; import { TxDetailsChainEventErc20AssetDelist } from './tx-erc20-asset-delist';
import { TxDetailsContractCall } from './tx-contract-call';
interface ChainEventProps { interface ChainEventProps {
txData: BlockExplorerTransactionResult | undefined; txData: BlockExplorerTransactionResult | undefined;
@@ -39,7 +38,7 @@ export const ChainEvent = ({ txData }: ChainEventProps) => {
return null; return null;
} }
const { builtin, erc20, erc20Multisig, stakingEvent, contractCall } = const { builtin, erc20, erc20Multisig, stakingEvent } =
txData.command.chainEvent; txData.command.chainEvent;
// Builtin Asset events // Builtin Asset events
@@ -141,10 +140,6 @@ export const ChainEvent = ({ txData }: ChainEventProps) => {
} }
} }
if (contractCall) {
return <TxDetailsContractCall contractCall={contractCall} />;
}
// If we hit this return, tx-shared-details should give a basic overview // If we hit this return, tx-shared-details should give a basic overview
return null; return null;
}; };
@@ -1,26 +0,0 @@
import { decodeEthCallResult } from './tx-contract-call';
import { base64 } from 'ethers/lib/utils';
import { defaultAbiCoder } from '@ethersproject/abi';
import { BigNumber } from '@ethersproject/bignumber';
describe('decodeEthCallResult', () => {
it('should decode contractData correctly (mocked)', () => {
const mockContractData = base64.encode(
defaultAbiCoder.encode(['int256'], [BigNumber.from(123)])
);
const result = decodeEthCallResult(mockContractData);
expect(result).toBe('123');
});
it('should decode contractData correctly (known data)', () => {
const mockContractData = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADH8cueyY=';
const result = decodeEthCallResult(mockContractData);
expect(result).toBe('3435020581670');
});
it('should return "-" when an error occurs', () => {
const mockContractData = 'invalid_data';
const result = decodeEthCallResult(mockContractData);
expect(result).toBe('-');
});
});
@@ -1,102 +0,0 @@
import { TableCell, TableRow } from '../../../table';
import { t } from '@vegaprotocol/i18n';
import {
ExternalExplorerLink,
EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link';
import { getExternalChainLabel } from '../../../links/external-explorer-link/external-chain';
import type { components } from '../../../../../types/explorer';
import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
import { BigNumber } from 'ethers';
import OracleLink from '../../../links/oracle-link/oracle-link';
import { useExplorerOracleSpecByIdQuery } from '../../../../routes/oracles/__generated__/Oracles';
import { OracleEthSource } from '../../../../routes/oracles/components/oracle-eth-source';
/**
* Decodes the b64/ABIcoded result from an eth cal
* @param data
* @returns
*/
export function decodeEthCallResult(contractData: string): string {
try {
const rawResult = defaultAbiCoder.decode(
['int256'],
base64.decode(contractData)
);
// Finally, convert the resulting BigNumber in to a string
const res = BigNumber.from(rawResult[0]).toString();
return res;
} catch (e) {
return '-';
}
}
interface TxDetailsContractCallProps {
contractCall: components['schemas']['vegaEthContractCallEvent'];
}
export const TxDetailsContractCall = ({
contractCall,
}: TxDetailsContractCallProps) => {
const { data } = useExplorerOracleSpecByIdQuery({
variables: {
id: contractCall.specId || '1',
},
});
if (!contractCall) {
return null;
}
const chainLabel = getExternalChainLabel(contractCall.sourceChainId);
return (
<>
{contractCall.specId && (
<TableRow modifier="bordered">
<TableCell>{t('Oracle')}</TableCell>
<TableCell>
<OracleLink
id={contractCall.specId}
hasSeenOracleReports={true}
status={data?.oracleSpec?.dataSourceSpec.spec.status || '-'}
/>
</TableCell>
</TableRow>
)}
{contractCall.blockHeight && (
<TableRow modifier="bordered">
<TableCell>
{chainLabel} {t('block')}
</TableCell>
<TableCell>
<ExternalExplorerLink
chain={contractCall.sourceChainId}
id={contractCall.blockHeight}
type={EthExplorerLinkTypes.block}
/>
</TableCell>
</TableRow>
)}
{data?.oracleSpec?.dataSourceSpec && (
<OracleEthSource
chain={contractCall.sourceChainId}
sourceType={data.oracleSpec.dataSourceSpec.spec.data.sourceType}
/>
)}
{contractCall.error && (
<TableRow modifier="bordered">
<TableCell>{t('Call error')}</TableCell>
<TableCell>{contractCall.error}</TableCell>
</TableRow>
)}
{contractCall.result && (
<TableRow modifier="bordered">
<TableCell>{t('Result')}</TableCell>
<TableCell>{decodeEthCallResult(contractCall.result)}</TableCell>
</TableRow>
)}
</>
);
};
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { AssetLink } from '../../../links'; import { AssetLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventErc20AssetLimitsUpdatedProps { interface TxDetailsChainEventErc20AssetLimitsUpdatedProps {
assetLimitsUpdated: components['schemas']['vegaERC20AssetLimitsUpdated']; assetLimitsUpdated: components['schemas']['vegaERC20AssetLimitsUpdated'];
@@ -42,7 +42,7 @@ export const TxDetailsChainEventErc20AssetLimitsUpdated = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('ERC20 asset')}</TableCell> <TableCell>{t('ERC20 asset')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={assetLimitsUpdated.sourceEthereumAddress} id={assetLimitsUpdated.sourceEthereumAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { AssetLink } from '../../../links'; import { AssetLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventErc20AssetListProps { interface TxDetailsChainEventErc20AssetListProps {
assetList: components['schemas']['vegaERC20AssetList']; assetList: components['schemas']['vegaERC20AssetList'];
@@ -32,7 +32,7 @@ export const TxDetailsChainEventErc20AssetList = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Source')}</TableCell> <TableCell>{t('Source')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={assetList.assetSource} id={assetList.assetSource}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { AssetLink, PartyLink } from '../../../links'; import { AssetLink, PartyLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventProps { interface TxDetailsChainEventProps {
deposit: components['schemas']['vegaERC20Deposit']; deposit: components['schemas']['vegaERC20Deposit'];
@@ -36,7 +36,7 @@ export const TxDetailsChainEventDeposit = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Source')}</TableCell> <TableCell>{t('Source')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={deposit.sourceEthereumAddress} id={deposit.sourceEthereumAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { AssetLink } from '../../../links'; import { AssetLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventWithdrawalProps { interface TxDetailsChainEventWithdrawalProps {
withdrawal: components['schemas']['vegaERC20Withdrawal']; withdrawal: components['schemas']['vegaERC20Withdrawal'];
@@ -35,7 +35,7 @@ export const TxDetailsChainEventWithdrawal = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Recipient')}</TableCell> <TableCell>{t('Recipient')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={withdrawal.targetEthereumAddress} id={withdrawal.targetEthereumAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />

Some files were not shown because too many files have changed in this diff Show More