Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3b240ef48 | ||
|
|
24f21dbd2e | ||
|
|
0473821c17 | ||
|
|
24347eaf18 | ||
|
|
5145c2a558 |
@@ -1,6 +1,6 @@
|
||||
# Related issues 🔗
|
||||
|
||||
Closes #[Issue number here]
|
||||
Issue: #[Issue number here]
|
||||
|
||||
# Description ℹ️
|
||||
|
||||
|
||||
@@ -82,38 +82,3 @@ jobs:
|
||||
https://${{ env.IPFS_V1 }}.ipfs.dweb.link/
|
||||
https://${{ env.IPFS_V1 }}.ipfs.cf-ipfs.com/
|
||||
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
|
||||
|
||||
@@ -5,17 +5,15 @@ on:
|
||||
branches:
|
||||
- release/*
|
||||
- develop
|
||||
- main
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- ready_for_review
|
||||
- reopened
|
||||
- edited
|
||||
- synchronize
|
||||
jobs:
|
||||
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
|
||||
name: 'Cache yarn modules'
|
||||
steps:
|
||||
@@ -44,6 +42,13 @@ jobs:
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: yarn install --pure-lockfile
|
||||
|
||||
lint-pr-title:
|
||||
needs: node-modules
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
name: Verify PR title
|
||||
uses: ./.github/workflows/lint-pr.yml
|
||||
secrets: inherit
|
||||
|
||||
lint-format:
|
||||
timeout-minutes: 20
|
||||
needs: node-modules
|
||||
@@ -170,48 +175,25 @@ jobs:
|
||||
preview_explorer: ${{ env.PREVIEW_EXPLORER }}
|
||||
preview_tools: ${{ env.PREVIEW_TOOLS }}
|
||||
|
||||
check-e2e-needed:
|
||||
runs-on: ubuntu-latest
|
||||
console-e2e:
|
||||
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 }}"
|
||||
name: '(CI) console python'
|
||||
uses: ./.github/workflows/console-test-run.yml
|
||||
secrets: inherit
|
||||
if: ${{ contains(fromJSON(needs.build-sources.outputs.projects), 'trading') && github.event_name == 'pull_request' }}
|
||||
with:
|
||||
github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
cypress:
|
||||
needs: [build-sources, check-e2e-needed]
|
||||
needs: build-sources
|
||||
name: '(CI) cypress'
|
||||
if: ${{ needs.check-e2e-needed.outputs.run-tests == 'true' }}
|
||||
# if: ${{ needs.build-sources.outputs.projects-e2e != '[]' }}
|
||||
uses: ./.github/workflows/cypress-run.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
projects: ${{ needs.build-sources.outputs.projects-e2e }}
|
||||
tags: '@smoke'
|
||||
|
||||
console-e2e:
|
||||
needs: [build-sources, check-e2e-needed]
|
||||
name: '(CI) console 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:
|
||||
needs: build-sources
|
||||
name: '(CD) publish dist'
|
||||
|
||||
@@ -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
|
||||
@@ -6,37 +6,23 @@ on:
|
||||
github-sha:
|
||||
required: true
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
console-test-branch:
|
||||
type: choice
|
||||
description: 'main: v0.72.14, develop: v0.73.0-preview7'
|
||||
options:
|
||||
- main
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
create-docker-image:
|
||||
name: Create docker image for console-test
|
||||
runs-on: ubuntu-22.04
|
||||
run-tests:
|
||||
name: run-tests
|
||||
runs-on: console-test
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
#----------------------------------------------
|
||||
# check-out frontend-monorepo
|
||||
#----------------------------------------------
|
||||
- name: Checkout frontend-monorepo
|
||||
- name: Checkout console test repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.github-sha || github.sha }}
|
||||
ref: ${{ inputs.github-sha }}
|
||||
#----------------------------------------------
|
||||
# cache node modules
|
||||
#----------------------------------------------
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
cache: yarn
|
||||
|
||||
- name: Cache node modules
|
||||
id: cache
|
||||
uses: actions/cache@v3
|
||||
@@ -55,105 +41,23 @@ jobs:
|
||||
#----------------------------------------------
|
||||
# build trading
|
||||
#----------------------------------------------
|
||||
- name: Build trading app
|
||||
- name: Build affected spec
|
||||
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
|
||||
# run trading server
|
||||
#----------------------------------------------
|
||||
|
||||
- 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
|
||||
- name: Run trading server
|
||||
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
|
||||
outputs:
|
||||
console-branch: ${{ steps.output-step.outputs.branch }}
|
||||
steps:
|
||||
- name: Workflow dispatch input
|
||||
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: 20
|
||||
steps:
|
||||
docker run -d -p 80:4200 -v $PWD/docker/nginx.conf:/etc/nginx/conf.d/default.conf -v $PWD/dist/apps/trading/exported:/usr/share/nginx/html nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
|
||||
sleep 5
|
||||
docker ps
|
||||
#----------------------------------------------
|
||||
# load docker image
|
||||
# check if container persists between runs
|
||||
#----------------------------------------------
|
||||
- name: Download docker image from previous job
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: console-image
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker image
|
||||
- name: Check server
|
||||
run: |
|
||||
docker load --input /tmp/console-image.tar
|
||||
docker image ls -a
|
||||
|
||||
docker ps
|
||||
#----------------------------------------------
|
||||
# check-out tests repo
|
||||
#----------------------------------------------
|
||||
@@ -161,55 +65,36 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: vegaprotocol/console-test
|
||||
ref: ${{ needs.console-test-branch.outputs.console-branch }}
|
||||
|
||||
- name: Load console test envs
|
||||
id: console-test-env
|
||||
uses: falti/dotenv-action@v1.0.4
|
||||
with:
|
||||
path: '.env.${{ needs.console-test-branch.outputs.console-branch }}'
|
||||
export-variables: true
|
||||
keys-case: upper
|
||||
log-variables: true
|
||||
|
||||
path: './console-test'
|
||||
#----------------------------------------------
|
||||
# ----- Setup python -----
|
||||
#----------------------------------------------
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
#----------------------------------------------
|
||||
# ----- install & configure poetry -----
|
||||
#----------------------------------------------
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
virtualenvs-create: true
|
||||
virtualenvs-in-project: true
|
||||
virtualenvs-path: .venv
|
||||
|
||||
#----------------------------------------------
|
||||
# install python dependencies
|
||||
# install dependencies if cache does not exist
|
||||
#----------------------------------------------
|
||||
- name: Install dependencies
|
||||
working-directory: ./console-test
|
||||
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
|
||||
run: poetry install --no-interaction --no-root
|
||||
#----------------------------------------------
|
||||
# install vega binaries
|
||||
#----------------------------------------------
|
||||
- name: Install vega binaries
|
||||
run: poetry run python -m vega_sim.tools.load_binaries --force --version ${{ env.VEGA_VERSION }}
|
||||
working-directory: ./console-test
|
||||
run: poetry run python -m vega_sim.tools.load_binaries --force
|
||||
#----------------------------------------------
|
||||
# install playwright
|
||||
#----------------------------------------------
|
||||
- name: install playwright
|
||||
run: poetry run playwright install --with-deps chromium
|
||||
run: poetry run playwright install
|
||||
working-directory: ./console-test
|
||||
#----------------------------------------------
|
||||
# run tests
|
||||
#----------------------------------------------
|
||||
- name: Run tests
|
||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
|
||||
|
||||
working-directory: ./console-test
|
||||
run: poetry run pytest -v -s --numprocesses 2 --dist loadfile --durations=20
|
||||
- name: Check files
|
||||
run: |
|
||||
ls -al .
|
||||
ls -al console-test
|
||||
#----------------------------------------------
|
||||
# upload traces
|
||||
#----------------------------------------------
|
||||
@@ -220,13 +105,3 @@ jobs:
|
||||
name: playwright-trace
|
||||
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
|
||||
|
||||
@@ -13,35 +13,13 @@ on:
|
||||
type: string
|
||||
|
||||
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:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
project: ${{ fromJSON(inputs.projects) }}
|
||||
name: ${{ matrix.project }}
|
||||
needs: runner-choice
|
||||
runs-on: ${{ needs.runner-choice.outputs.runner }}
|
||||
runs-on: self-hosted-runner
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
# Checks if skip cache was requested
|
||||
@@ -85,7 +63,6 @@ jobs:
|
||||
- name: Run Vegacapsule network and Vega wallet
|
||||
id: setup-vega
|
||||
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
|
||||
timeout-minutes: 10
|
||||
|
||||
######
|
||||
## Run some tests
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
name: Verify PR title
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- reopened
|
||||
- synchronize
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
lint_pr:
|
||||
@@ -16,16 +11,21 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version-file: '.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm package.json
|
||||
npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
- name: Check PR title
|
||||
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
|
||||
|
||||
@@ -30,18 +30,12 @@ jobs:
|
||||
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
|
||||
echo IS_S3_RELEASE=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' }}
|
||||
if: ${{ contains(github.ref, '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
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
@@ -63,7 +57,7 @@ jobs:
|
||||
echo IS_IPFS_RELEASE=true >> $GITHUB_ENV
|
||||
|
||||
- name: Is S3 Release
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'false' && github.event_name == 'push' && github.ref_name != 'main'}}
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'false' && github.event_name == 'push' }}
|
||||
run: |
|
||||
echo IS_S3_RELEASE=true >> $GITHUB_ENV
|
||||
|
||||
@@ -87,7 +81,7 @@ jobs:
|
||||
|
||||
- name: Log in to the Container registry (docker hub)
|
||||
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' || env.IS_DEV_IMAGE == 'true' }}
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -161,10 +155,10 @@ jobs:
|
||||
- name: Sanity check docker image
|
||||
run: |
|
||||
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 /bin/sh -c 'cat /ipfs-hash' > ${{ matrix.app }}-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 cat /ipfs-hash > ${{ matrix.app }}-ipfs-hash
|
||||
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)
|
||||
uses: docker/build-push-action@v3
|
||||
@@ -185,7 +179,7 @@ jobs:
|
||||
uses: docker/build-push-action@v3
|
||||
continue-on-error: true
|
||||
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' || env.IS_DEV_IMAGE == 'true' }}
|
||||
with:
|
||||
context: .
|
||||
file: docker/node-outside-docker.Dockerfile
|
||||
@@ -195,7 +189,7 @@ jobs:
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
tags: |
|
||||
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' || env.IS_DEV_IMAGE == 'true' && 'develop' || '' }}
|
||||
|
||||
- name: Publish dist as docker image (ghcr - retry)
|
||||
uses: docker/build-push-action@v3
|
||||
@@ -222,7 +216,7 @@ jobs:
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
tags: |
|
||||
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
|
||||
- name: Publish dist to s3
|
||||
@@ -335,9 +329,7 @@ jobs:
|
||||
fi
|
||||
|
||||
# create commit
|
||||
if ! git diff --cached --exit-code; then
|
||||
commit_msg="Automated hash update from ${{ github.ref }}"
|
||||
git commit -m "$commit_msg"
|
||||
git push -u origin "main"
|
||||
fi
|
||||
commit_msg="Automated hash update from ${{ github.ref }}"
|
||||
git commit -m "$commit_msg"
|
||||
git push -u origin "main"
|
||||
)
|
||||
|
||||
@@ -113,20 +113,6 @@ In order to run a container on port 3000:
|
||||
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
|
||||
|
||||
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.
|
||||
@@ -164,7 +150,7 @@ 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:
|
||||
|
||||
```bash
|
||||
docker build -f docker/node-outside-docker.Dockerfile . --tag=[TAG]
|
||||
docker build --dockerfile docker/node-outside-docker.Dockerfile . --tag=[TAG]
|
||||
```
|
||||
|
||||
### Verifying ipfs-hash of existing current application version
|
||||
|
||||
@@ -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_ENV=DEVNET
|
||||
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
|
||||
NX_EXPLORER_ASSETS=1
|
||||
|
||||
@@ -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_ENV=MAINNET
|
||||
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
|
||||
NX_EXPLORER_ASSETS=1
|
||||
|
||||
@@ -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_ENV=TESTNET
|
||||
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
|
||||
NX_EXPLORER_ASSETS=1
|
||||
|
||||
@@ -36,9 +36,10 @@ context('Market page', { tags: '@regression' }, function () {
|
||||
|
||||
it('Able to go to market details page', function () {
|
||||
cy.navigate_to('markets');
|
||||
cy.contains('Test market 1').click();
|
||||
cy.get_element_by_col_id('actions').eq(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('Market ID', this.createdMarketId);
|
||||
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');
|
||||
@@ -86,13 +87,15 @@ context('Market page', { tags: '@regression' }, function () {
|
||||
// Liquidity
|
||||
cy.validate_element_from_table('Target Stake', '0.00 fUSDC');
|
||||
cy.validate_element_from_table('Supplied Stake', '0.00 fUSDC');
|
||||
cy.validate_element_from_table('Market Value Proxy', '0.00 fUSDC');
|
||||
// Liquidity price range
|
||||
cy.validate_element_from_table(
|
||||
'Liquidity Price Range',
|
||||
'95.00% of mid price'
|
||||
'1,000.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(
|
||||
@@ -141,8 +144,11 @@ context('Market page', { tags: '@regression' }, function () {
|
||||
.as('successorMarketId');
|
||||
cy.contains('Token test market').click();
|
||||
cy.getByTestId(marketHeaders).should('have.text', 'Token test market');
|
||||
cy.validate_proposal_change_type('Triggering Ratio', 'Added');
|
||||
cy.validate_element_from_table('Triggering Ratio', '0.7');
|
||||
cy.validate_proposal_change_type('Time Window', 'Added');
|
||||
cy.validate_element_from_table('Time Window', '3,600');
|
||||
cy.validate_proposal_change_type('Scaling Factor', 'Added');
|
||||
cy.validate_element_from_table('Scaling Factor', '10');
|
||||
|
||||
cy.getByTestId(successionLineItem)
|
||||
|
||||
@@ -40,7 +40,7 @@ context.skip('Node switcher', { tags: '@regression' }, function () {
|
||||
const errorTypeTxt = 'Error: invalid 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.get('input').clear().type('fakeUrl');
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
context('Oracle page', { tags: '@smoke' }, () => {
|
||||
describe('Verify elements on page', () => {
|
||||
before('create market and navigate to oracle page', () => {
|
||||
cy.createMarket();
|
||||
cy.visit('/oracles');
|
||||
});
|
||||
it('should see oracle data', () => {
|
||||
cy.getByTestId('oracle-details').should('have.length.at.least', 2);
|
||||
cy.getByTestId('oracle-details')
|
||||
.should('exist')
|
||||
.eq(0)
|
||||
.within(() => {
|
||||
cy.get('tr')
|
||||
.eq(0)
|
||||
.within(() => {
|
||||
cy.get('th').should('have.text', 'ID');
|
||||
cy.get('a').invoke('text').should('have.length', 64);
|
||||
cy.get('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/oracles/');
|
||||
});
|
||||
cy.get('tr')
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.get('th').should('have.text', 'Type');
|
||||
cy.get('td').should('have.text', 'External data');
|
||||
});
|
||||
cy.get('tr')
|
||||
.eq(2)
|
||||
.within(() => {
|
||||
cy.get('th').should('have.text', 'Signer');
|
||||
cy.getByTestId('keytype').should('have.text', 'Vega');
|
||||
cy.get('a').invoke('text').should('have.length', 64);
|
||||
cy.get('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/parties/');
|
||||
});
|
||||
cy.get('tr')
|
||||
.eq(3)
|
||||
.within(() => {
|
||||
cy.get('th').should('have.text', 'Settlement for');
|
||||
cy.get('a').invoke('text').should('have.length', 64);
|
||||
cy.get('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/markets/');
|
||||
});
|
||||
cy.get('tr')
|
||||
.eq(4)
|
||||
.within(() => {
|
||||
cy.get('th').should('have.text', 'Matched data');
|
||||
cy.get('td').should('have.text', '❌');
|
||||
});
|
||||
cy.get('details')
|
||||
.eq(0)
|
||||
.within(() => {
|
||||
cy.contains('Filter').click();
|
||||
cy.get('.language-json').should('exist');
|
||||
});
|
||||
cy.get('details')
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.contains('JSON').click();
|
||||
cy.get('.language-json').should('exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -26,6 +26,7 @@ function getSuccessorTxBody(parentMarketId) {
|
||||
positionDecimalPlaces: '5',
|
||||
linearSlippageFactor: '0.001',
|
||||
quadraticSlippageFactor: '0',
|
||||
lpPriceRange: '10',
|
||||
instrument: {
|
||||
name: 'Token test market',
|
||||
code: 'TEST.24h',
|
||||
@@ -129,12 +130,6 @@ function getSuccessorTxBody(parentMarketId) {
|
||||
parentMarketId: parentMarketId,
|
||||
insurancePoolFraction: '0.75',
|
||||
},
|
||||
liquiditySlaParameters: {
|
||||
priceRange: '0.95',
|
||||
commitmentMinTimeFraction: '0.5',
|
||||
performanceHysteresisEpochs: 2,
|
||||
slaCompetitionFactor: '0.75',
|
||||
},
|
||||
},
|
||||
},
|
||||
closingTimestamp,
|
||||
|
||||
@@ -3,7 +3,6 @@ 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_VEGA_EXPLORER_URL=/
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_TXS_LIST=0
|
||||
|
||||
@@ -8,7 +8,6 @@ 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_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
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
|
||||
|
||||
@@ -9,7 +9,6 @@ NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.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
|
||||
|
||||
@@ -9,7 +9,6 @@ NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
|
||||
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_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,3 +1,2 @@
|
||||
# .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_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
@@ -9,7 +9,6 @@ NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.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
|
||||
|
||||
@@ -11,7 +11,6 @@ NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.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_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
|
||||
|
||||
@@ -5,5 +5,4 @@ NX_VEGA_ENV=CUSTOM
|
||||
NX_BLOCK_EXPLORER=
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
NX_VEGA_EXPLORER_URL=/
|
||||
@@ -77,7 +77,7 @@
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"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/main/specs/v0.72.3/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
AppFailure,
|
||||
NetworkLoader,
|
||||
NodeFailure,
|
||||
NodeGuard,
|
||||
NodeSwitcherDialog,
|
||||
useEnvironment,
|
||||
@@ -31,7 +31,7 @@ function App() {
|
||||
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
||||
<NodeGuard
|
||||
skeleton={<div>{t('Loading')}</div>}
|
||||
failure={<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||
>
|
||||
<Suspense fallback={splashLoading}>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
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 { useRef, useLayoutEffect } from 'react';
|
||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export type HashProps = {
|
||||
text: string;
|
||||
truncate?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -8,16 +7,10 @@ export type HashProps = {
|
||||
* are broken when they need to wrap. This will remove the need
|
||||
* for a lot of the overflow scrolling that currently exists.
|
||||
*/
|
||||
const Hash = ({ text, truncate = false }: HashProps) => {
|
||||
const h = truncate ? text.slice(0, 6) : text;
|
||||
|
||||
const Hash = ({ text }: HashProps) => {
|
||||
return (
|
||||
<code
|
||||
title={text}
|
||||
className="break-all font-mono"
|
||||
style={{ wordWrap: 'break-word' }}
|
||||
>
|
||||
{h}
|
||||
<code className="break-all font-mono" style={{ wordWrap: 'break-word' }}>
|
||||
{text}
|
||||
</code>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ query ExplorerMarket($id: ID!) {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
state
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
name
|
||||
@@ -14,14 +13,9 @@ query ExplorerMarket($id: ID!) {
|
||||
decimals
|
||||
}
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
settlementAsset {
|
||||
decimals
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
+2
-8
@@ -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`
|
||||
@@ -17,7 +17,6 @@ export const ExplorerMarketDocument = gql`
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
state
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
name
|
||||
@@ -28,15 +27,10 @@ export const ExplorerMarketDocument = gql`
|
||||
decimals
|
||||
}
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
settlementAsset {
|
||||
decimals
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
state
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -61,11 +61,7 @@ describe('Market link component', () => {
|
||||
instrument: {
|
||||
name: 'test-label',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: 'dai',
|
||||
settlementAsset: {
|
||||
decimals: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -23,7 +23,6 @@ const MarketLink = ({
|
||||
}: MarketLinkProps) => {
|
||||
const { data, error, loading } = useExplorerMarketQuery({
|
||||
variables: { id },
|
||||
fetchPolicy: 'cache-first',
|
||||
});
|
||||
|
||||
let label = <span>{id}</span>;
|
||||
|
||||
@@ -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 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>> & {
|
||||
// The Oracle ID
|
||||
id: string;
|
||||
// If available, the oracle status
|
||||
status?: string;
|
||||
// If the oracle has corresponding data in the OracleDataConnection
|
||||
hasSeenOracleReports?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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>
|
||||
);
|
||||
|
||||
const OracleLink = ({ id, ...props }: OracleLinkProps) => {
|
||||
return (
|
||||
<Tooltip description={description}>
|
||||
<Link
|
||||
className={`pl-2 pr-2 font-mono dark:text-black ${bgColour} rounded-sm border-solid border-2 relative`}
|
||||
{...props}
|
||||
to={`/${Routes.ORACLES}/${id}`}
|
||||
data-status={getStatusString(status)}
|
||||
>
|
||||
<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>
|
||||
<Link
|
||||
className="underline font-mono"
|
||||
{...props}
|
||||
to={`/${Routes.ORACLES}/${id}`}
|
||||
>
|
||||
<Hash text={id} />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,41 +1,37 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import {
|
||||
LiquidityPriceRangeInfoPanel,
|
||||
LiquiditySLAParametersInfoPanel,
|
||||
MarginScalingFactorsPanel,
|
||||
PriceMonitoringBoundsInfoPanel,
|
||||
SuccessionLineInfoPanel,
|
||||
getDataSourceSpecForSettlementData,
|
||||
getDataSourceSpecForTradingTermination,
|
||||
} from '@vegaprotocol/markets';
|
||||
import {
|
||||
LiquidityInfoPanel,
|
||||
LiquidityMonitoringParametersInfoPanel,
|
||||
InstrumentInfoPanel,
|
||||
KeyDetailsInfoPanel,
|
||||
LiquidityPriceRangeInfoPanel,
|
||||
MetadataInfoPanel,
|
||||
OracleInfoPanel,
|
||||
RiskFactorsInfoPanel,
|
||||
RiskModelInfoPanel,
|
||||
RiskParametersInfoPanel,
|
||||
SettlementAssetInfoPanel,
|
||||
} 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';
|
||||
|
||||
export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
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') {
|
||||
const signers =
|
||||
('signers' in data.sourceType.sourceType &&
|
||||
data.sourceType.sourceType.signers) ||
|
||||
[];
|
||||
const signers = data.sourceType.sourceType.signers || [];
|
||||
|
||||
return signers.map(({ signer }, i) => {
|
||||
return (
|
||||
@@ -47,13 +43,10 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
return [];
|
||||
};
|
||||
|
||||
const showTwoOracles =
|
||||
settlementDataSource &&
|
||||
terminationDataSource &&
|
||||
isEqual(
|
||||
getSigners(settlementDataSource),
|
||||
getSigners(terminationDataSource)
|
||||
);
|
||||
const showTwoOracles = isEqual(
|
||||
getSigners(settlementData),
|
||||
getSigners(terminationData)
|
||||
);
|
||||
|
||||
const headerClassName = 'font-alpha calt text-xl mt-4 border-b-2 pb-2';
|
||||
|
||||
@@ -69,8 +62,8 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
<MetadataInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Risk model')}</h2>
|
||||
<RiskModelInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Margin scaling factors')}</h2>
|
||||
<MarginScalingFactorsPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Risk parameters')}</h2>
|
||||
<RiskParametersInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Risk factors')}</h2>
|
||||
<RiskFactorsInfoPanel market={market} />
|
||||
{(market.data?.priceMonitoringBounds || []).map((trigger, i) => (
|
||||
@@ -96,12 +89,10 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
)}
|
||||
<h2 className={headerClassName}>{t('Liquidity monitoring')}</h2>
|
||||
<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>
|
||||
<LiquidityInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Liquidity price range')}</h2>
|
||||
<LiquidityPriceRangeInfoPanel market={market} />
|
||||
{showTwoOracles ? (
|
||||
<>
|
||||
<h2 className={headerClassName}>{t('Settlement oracle')}</h2>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMemo } from 'react';
|
||||
import { getAsset, type MarketFieldsFragment } from '@vegaprotocol/markets';
|
||||
import type { MarketFieldsFragment } from '@vegaprotocol/markets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueGetterParams,
|
||||
@@ -73,7 +73,8 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
|
||||
MarketFieldsFragment,
|
||||
'tradableInstrument.instrument.product.settlementAsset.symbol'
|
||||
>) => {
|
||||
const value = data && getAsset(data);
|
||||
const value =
|
||||
data?.tradableInstrument.instrument.product.settlementAsset;
|
||||
return value ? (
|
||||
<ButtonLink
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -31,9 +31,6 @@ fragment ExplorerDeterministicOrderFields on Order {
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
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<{
|
||||
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`
|
||||
fragment ExplorerDeterministicOrderFields on Order {
|
||||
@@ -47,9 +47,6 @@ export const ExplorerDeterministicOrderFieldsFragmentDoc = gql`
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +74,6 @@ function renderExistingAmend(
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: '123',
|
||||
settlementAsset: {
|
||||
decimals: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -127,9 +124,6 @@ function renderExistingAmend(
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: '123',
|
||||
settlementAsset: {
|
||||
decimals: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -156,11 +150,7 @@ function renderExistingAmend(
|
||||
instrument: {
|
||||
name: 'test-label',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: 'dai',
|
||||
settlementAsset: {
|
||||
decimals: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -33,8 +33,6 @@ const PriceInMarket = ({
|
||||
label = addDecimalsFormatNumber(price, data.market.decimalPlaces);
|
||||
} else if (
|
||||
decimalSource === 'SETTLEMENT_ASSET' &&
|
||||
data.market &&
|
||||
'settlementAsset' in data.market.tradableInstrument.instrument.product &&
|
||||
data.market?.tradableInstrument.instrument.product.settlementAsset
|
||||
) {
|
||||
label = addDecimalsFormatNumber(
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { VoteProgress } from '@vegaprotocol/proposals';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
@@ -33,7 +33,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.governance_proposal_market_requiredMajority,
|
||||
]);
|
||||
const tokenLink = useLinks(DApp.Governance);
|
||||
const tokenLink = useLinks(DApp.Token);
|
||||
const requiredMajorityPercentage = useMemo(() => {
|
||||
const requiredMajority =
|
||||
params?.governance_proposal_market_requiredMajority ?? 1;
|
||||
@@ -105,7 +105,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||
? new BigNumber(0)
|
||||
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full pt-2 uppercase">
|
||||
<div className="uppercase flex h-full items-center justify-center pt-2">
|
||||
<VoteProgress
|
||||
threshold={requiredMajorityPercentage}
|
||||
progress={yesPercentage}
|
||||
|
||||
@@ -22,9 +22,5 @@ export const TimeAgo = ({ date, ...props }: TimeAgoProps) => {
|
||||
return <>{t('Date unknown')}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span {...props} title={date} className="underline decoration-dotted">
|
||||
{t(`${distanceToNow} ago`)}
|
||||
</span>
|
||||
);
|
||||
return <span {...props}>{t(`${distanceToNow} ago`)}</span>;
|
||||
};
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
query ExplorerSettlementAssetForMarket($id: ID!) {
|
||||
market(id: $id) {
|
||||
id
|
||||
decimalPlaces
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerSettlementAssetForMarketQueryVariables = Types.Exact<{
|
||||
id: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerSettlementAssetForMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number } | null };
|
||||
|
||||
|
||||
export const ExplorerSettlementAssetForMarketDocument = gql`
|
||||
query ExplorerSettlementAssetForMarket($id: ID!) {
|
||||
market(id: $id) {
|
||||
id
|
||||
decimalPlaces
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useExplorerSettlementAssetForMarketQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerSettlementAssetForMarketQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerSettlementAssetForMarketQuery` 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 } = useExplorerSettlementAssetForMarketQuery({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerSettlementAssetForMarketQuery(baseOptions: Apollo.QueryHookOptions<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>(ExplorerSettlementAssetForMarketDocument, options);
|
||||
}
|
||||
export function useExplorerSettlementAssetForMarketLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>(ExplorerSettlementAssetForMarketDocument, options);
|
||||
}
|
||||
export type ExplorerSettlementAssetForMarketQueryHookResult = ReturnType<typeof useExplorerSettlementAssetForMarketQuery>;
|
||||
export type ExplorerSettlementAssetForMarketLazyQueryHookResult = ReturnType<typeof useExplorerSettlementAssetForMarketLazyQuery>;
|
||||
export type ExplorerSettlementAssetForMarketQueryResult = Apollo.QueryResult<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>;
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { render } from '@testing-library/react';
|
||||
import { Side } from '@vegaprotocol/types';
|
||||
import type { LiquidityOrder } from '@vegaprotocol/types';
|
||||
import { PeggedReference } from '@vegaprotocol/types';
|
||||
import { LiquidityProvisionDetailsRow } from './liquidity-provision-details-row';
|
||||
import type { VegaSide } from './liquidity-provision-details-row';
|
||||
|
||||
describe('LiquidityProvisionDetails component', () => {
|
||||
function renderComponent(
|
||||
order: LiquidityOrder,
|
||||
side: VegaSide,
|
||||
normaliseProportionsTo: number,
|
||||
marketId: string
|
||||
) {
|
||||
return render(
|
||||
<MockedProvider>
|
||||
<table>
|
||||
<tbody data-testid="container">
|
||||
<LiquidityProvisionDetailsRow
|
||||
order={order}
|
||||
marketId={marketId}
|
||||
normaliseProportionsTo={normaliseProportionsTo}
|
||||
side={side}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</MockedProvider>
|
||||
);
|
||||
}
|
||||
|
||||
it('renders null for an order with no proportion', () => {
|
||||
const mockOrder = {
|
||||
offset: '1',
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
};
|
||||
|
||||
const res = renderComponent(
|
||||
mockOrder as LiquidityOrder,
|
||||
Side.SIDE_BUY,
|
||||
100,
|
||||
'123'
|
||||
);
|
||||
expect(res.getByTestId('container')).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('renders null for a null order', () => {
|
||||
const res = renderComponent(
|
||||
null as unknown as LiquidityOrder,
|
||||
Side.SIDE_BUY,
|
||||
100,
|
||||
'123'
|
||||
);
|
||||
expect(res.getByTestId('container')).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('renders a row when the order is as expected', () => {
|
||||
const mockOrder = {
|
||||
offset: '1',
|
||||
proportion: 20,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
};
|
||||
|
||||
const res = renderComponent(
|
||||
mockOrder as LiquidityOrder,
|
||||
Side.SIDE_BUY,
|
||||
100,
|
||||
'123'
|
||||
);
|
||||
// Row test ids and keys are based on the side, reference and proportion
|
||||
expect(res.getByTestId('SIDE_BUY-20-1')).toBeInTheDocument();
|
||||
expect(res.getByText('+1')).toBeInTheDocument();
|
||||
expect(res.getByText('Mid')).toBeInTheDocument();
|
||||
expect(res.getByText('20%')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('normalises offsets when normaliseToProportion is not 100', () => {
|
||||
const mockOrder = {
|
||||
offset: '1',
|
||||
proportion: 20,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_BEST_BID,
|
||||
};
|
||||
|
||||
const res = renderComponent(
|
||||
mockOrder as LiquidityOrder,
|
||||
Side.SIDE_SELL,
|
||||
50,
|
||||
'123'
|
||||
);
|
||||
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
|
||||
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
|
||||
expect(res.getByText('-1')).toBeInTheDocument();
|
||||
expect(res.getByText('Best Bid')).toBeInTheDocument();
|
||||
expect(res.getByText('40%')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles a missing offset gracefully (should not happen)', () => {
|
||||
const mockOrder = {
|
||||
proportion: 20,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_BEST_BID,
|
||||
};
|
||||
|
||||
const res = renderComponent(
|
||||
mockOrder as LiquidityOrder,
|
||||
Side.SIDE_SELL,
|
||||
50,
|
||||
'123'
|
||||
);
|
||||
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
|
||||
expect(res.getByTestId('SIDE_SELL-40-')).toBeInTheDocument();
|
||||
expect(res.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles a missing reference gracefully (should not happen)', () => {
|
||||
const mockOrder = {
|
||||
offset: '1',
|
||||
proportion: 20,
|
||||
};
|
||||
|
||||
const res = renderComponent(
|
||||
mockOrder as LiquidityOrder,
|
||||
Side.SIDE_SELL,
|
||||
50,
|
||||
'123'
|
||||
);
|
||||
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
|
||||
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
|
||||
expect(res.getByText('40%')).toBeInTheDocument();
|
||||
expect(res.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
import type { components } from '../../../../../../types/explorer';
|
||||
import { TableRow } from '../../../../table';
|
||||
import { LiquidityProvisionOffset } from './liquidity-provision-offset';
|
||||
|
||||
export type VegaPeggedReference = components['schemas']['vegaPeggedReference'];
|
||||
export type VegaSide = components['schemas']['vegaSide'];
|
||||
|
||||
export type LiquidityProvisionOrder =
|
||||
components['schemas']['vegaLiquidityOrder'];
|
||||
|
||||
export const LiquidityReferenceLabel: Record<VegaPeggedReference, string> = {
|
||||
PEGGED_REFERENCE_BEST_ASK: t('Best Ask'),
|
||||
PEGGED_REFERENCE_BEST_BID: t('Best Bid'),
|
||||
PEGGED_REFERENCE_MID: t('Mid'),
|
||||
PEGGED_REFERENCE_UNSPECIFIED: '-',
|
||||
};
|
||||
|
||||
export type LiquidityProvisionDetailsRowProps = {
|
||||
order?: LiquidityProvisionOrder;
|
||||
marketId?: string;
|
||||
side: VegaSide;
|
||||
// If this is
|
||||
normaliseProportionsTo: number;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Note: offset is formatted by settlement asset on the market, assuming that is available
|
||||
* Note: Due to the mix of references (MID vs BEST_X), it's not possible to correctly order
|
||||
* the orders by their actual distance from a midpoint. This would require us knowing
|
||||
* the best bid (now or at placement) and the mid. Getting the data for *now* would be
|
||||
* misleading for LP submissions in the past. There is no API for getting <mid />
|
||||
* at the time of a transaction.
|
||||
*/
|
||||
export function LiquidityProvisionDetailsRow({
|
||||
normaliseProportionsTo,
|
||||
order,
|
||||
side,
|
||||
marketId,
|
||||
}: LiquidityProvisionDetailsRowProps) {
|
||||
if (!order || !order.proportion) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const proportion =
|
||||
normaliseProportionsTo === 100
|
||||
? order.proportion
|
||||
: Math.round((order.proportion / normaliseProportionsTo) * 100);
|
||||
|
||||
const key = `${side}-${proportion}-${order.offset ? order.offset : ''}`;
|
||||
|
||||
return (
|
||||
<TableRow modifier="bordered" key={key} data-testid={key}>
|
||||
<td className="text-right px-2">
|
||||
{order.offset && marketId ? (
|
||||
<LiquidityProvisionOffset
|
||||
offset={order.offset}
|
||||
side={side}
|
||||
marketId={marketId}
|
||||
/>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</td>
|
||||
<td className="text-center">
|
||||
{order.reference ? LiquidityReferenceLabel[order.reference] : '-'}
|
||||
</td>
|
||||
<td className="text-center">{proportion}%</td>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { LiquidityProvisionMid } from './liquidity-provision-mid';
|
||||
|
||||
describe('LiquidityProvisionMid component', () => {
|
||||
function renderComponent() {
|
||||
return render(
|
||||
<table>
|
||||
<tbody data-testid="container">
|
||||
<LiquidityProvisionMid />
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
it('renders a basic row that spans the whole table', () => {
|
||||
const res = renderComponent();
|
||||
const display = res.getByTestId('mid-display');
|
||||
expect(res.getByTestId('mid')).toBeInTheDocument();
|
||||
expect(display).toBeInTheDocument();
|
||||
expect(display).toHaveAttribute('colspan', '3');
|
||||
});
|
||||
});
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { TableRow } from '../../../../table';
|
||||
|
||||
/**
|
||||
* In a LiquidityProvision table, this row is the midpoint. Above our LP orders on the
|
||||
* buy side, below are LP orders on the sell side. This component simply divides them.
|
||||
*
|
||||
* There is no API that can give us the mid price when the order was created, and even
|
||||
* if there was it isn't clear that would be appropriate for this centre row. So instead
|
||||
* it's a simple divider.
|
||||
*/
|
||||
export function LiquidityProvisionMid() {
|
||||
return (
|
||||
<TableRow modifier="bordered" data-testid="mid">
|
||||
<td data-testid="mid-display" colSpan={3} className="bg-white"></td>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { render } from '@testing-library/react';
|
||||
import { ExplorerSettlementAssetForMarketDocument } from '../__generated__/Explorer-settlement-asset';
|
||||
import type { ExplorerSettlementAssetForMarketQuery } from '../__generated__/Explorer-settlement-asset';
|
||||
import type { VegaSide } from './liquidity-provision-details-row';
|
||||
import {
|
||||
getFormattedOffset,
|
||||
LiquidityProvisionOffset,
|
||||
} from './liquidity-provision-offset';
|
||||
const decimalsMock: ExplorerSettlementAssetForMarketQuery = {
|
||||
market: {
|
||||
id: '123',
|
||||
__typename: 'Market',
|
||||
decimalPlaces: 5,
|
||||
},
|
||||
};
|
||||
|
||||
describe('LiquidityProvisionOffset component', () => {
|
||||
function renderComponent(
|
||||
offset: string,
|
||||
side: VegaSide,
|
||||
marketId: string,
|
||||
mocks: MockedResponse[]
|
||||
) {
|
||||
return render(
|
||||
<MockedProvider mocks={mocks}>
|
||||
<LiquidityProvisionOffset
|
||||
offset={offset}
|
||||
side={side}
|
||||
marketId={marketId}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
}
|
||||
|
||||
it('renders a simple row before market data comes in', () => {
|
||||
const res = renderComponent('1', 'SIDE_BUY', '123', []);
|
||||
expect(res.getByText('+1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('replaces unformatted with formatted if the market data comes in', () => {
|
||||
const mock = {
|
||||
request: {
|
||||
query: ExplorerSettlementAssetForMarketDocument,
|
||||
variables: {
|
||||
id: '123',
|
||||
},
|
||||
result: {
|
||||
data: decimalsMock,
|
||||
},
|
||||
},
|
||||
};
|
||||
const res = renderComponent('1', 'SIDE_BUY', '123', [mock]);
|
||||
expect(res.getByText('+1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('getFormattedOffset returns the unformatted offset if there is not enough data', () => {
|
||||
const res = getFormattedOffset('1', {});
|
||||
expect(res).toEqual('1');
|
||||
});
|
||||
|
||||
it('getFormattedOffset decimal formats a number if it comes in with market data', () => {
|
||||
const res = getFormattedOffset('1', decimalsMock);
|
||||
expect(res).toEqual('0.00001');
|
||||
});
|
||||
});
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { useExplorerSettlementAssetForMarketQuery } from '../__generated__/Explorer-settlement-asset';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import type { ExplorerSettlementAssetForMarketQuery } from '../__generated__/Explorer-settlement-asset';
|
||||
import type { VegaSide } from './liquidity-provision-details-row';
|
||||
|
||||
export type LiquidityProvisionOffsetProps = {
|
||||
side: VegaSide;
|
||||
offset: string;
|
||||
marketId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Correctly formats an LP's offset according to the market settlement decimal places.
|
||||
* Initially this will appear unformatted, then when the query loads in the proper formatted
|
||||
* value will be displayed
|
||||
*
|
||||
* @see getFormattedOffset
|
||||
*/
|
||||
export function LiquidityProvisionOffset({
|
||||
side,
|
||||
offset,
|
||||
marketId,
|
||||
}: LiquidityProvisionOffsetProps) {
|
||||
const { data } = useExplorerSettlementAssetForMarketQuery({
|
||||
variables: {
|
||||
id: marketId,
|
||||
},
|
||||
});
|
||||
|
||||
// getFormattedOffset handles missing results/loading states
|
||||
const formattedOffset = getFormattedOffset(offset, data);
|
||||
|
||||
const label = side === 'SIDE_BUY' ? '+' : '-';
|
||||
const className = side === 'SIDE_BUY' ? 'text-vega-green' : 'text-vega-pink';
|
||||
return <span className={className}>{`${label}${formattedOffset}`}</span>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the work of formatting the number now we have the market decimal places.
|
||||
* If no market data is assigned (i.e. during loading, or if the market doesn't exist)
|
||||
* this function will return the unformatted number
|
||||
*
|
||||
* @see LiquidityProvisionOffset
|
||||
* @param data the result of a ExplorerSettlementAssetForMarketQuery
|
||||
* @param offset the unformatted offset
|
||||
* @returns string the offset of this lp order formatted with the settlement decimal places
|
||||
*/
|
||||
export function getFormattedOffset(
|
||||
offset: string,
|
||||
data?: ExplorerSettlementAssetForMarketQuery
|
||||
) {
|
||||
const decimals = data?.market?.decimalPlaces;
|
||||
|
||||
if (!decimals) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
return addDecimalsFormatNumber(offset, decimals);
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { render } from '@testing-library/react';
|
||||
import type { LiquidityOrder } from '@vegaprotocol/types';
|
||||
import { PeggedReference } from '@vegaprotocol/types';
|
||||
import type { LiquiditySubmission } from '../tx-liquidity-submission';
|
||||
import {
|
||||
LiquidityProvisionDetails,
|
||||
sumProportions,
|
||||
} from './liquidity-provision-details';
|
||||
|
||||
function mockProportion(proportion: number): LiquidityOrder {
|
||||
return {
|
||||
proportion,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
offset: '1',
|
||||
};
|
||||
}
|
||||
describe('sumProportions function', () => {
|
||||
it('returns 0 if the side is undefined', () => {
|
||||
const side: LiquidityOrder[] = undefined as unknown as LiquidityOrder[];
|
||||
const res = sumProportions(side);
|
||||
|
||||
expect(res).toEqual(0);
|
||||
});
|
||||
|
||||
it('returns 0 if the side is empty', () => {
|
||||
const side: LiquidityOrder[] = [];
|
||||
const res = sumProportions(side);
|
||||
|
||||
expect(res).toEqual(0);
|
||||
});
|
||||
|
||||
it('sums 1 item correctly (under 100%)', () => {
|
||||
const side: LiquidityOrder[] = [mockProportion(10)];
|
||||
const res = sumProportions(side);
|
||||
|
||||
expect(res).toEqual(10);
|
||||
});
|
||||
|
||||
it('sums 2 item correctly (exactly 100%)', () => {
|
||||
const side: LiquidityOrder[] = [mockProportion(50), mockProportion(50)];
|
||||
const res = sumProportions(side);
|
||||
|
||||
expect(res).toEqual(100);
|
||||
});
|
||||
|
||||
it('sums 3 item correctly to over 100%', () => {
|
||||
const side: LiquidityOrder[] = [
|
||||
mockProportion(20),
|
||||
mockProportion(40),
|
||||
mockProportion(50),
|
||||
];
|
||||
const res = sumProportions(side);
|
||||
|
||||
expect(res).toEqual(110);
|
||||
});
|
||||
});
|
||||
|
||||
describe('LiquidityProvisionDetails component', () => {
|
||||
function renderComponent(provision: LiquiditySubmission) {
|
||||
return render(
|
||||
<MockedProvider>
|
||||
<LiquidityProvisionDetails provision={provision} />
|
||||
</MockedProvider>
|
||||
);
|
||||
}
|
||||
it('handles an LP with no buys or sells by returning empty (should never happen)', () => {
|
||||
const mock: LiquiditySubmission = {};
|
||||
|
||||
const res = renderComponent(mock);
|
||||
expect(res.container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('handles an LP with no sells by just rendering buys', () => {
|
||||
const mock: LiquiditySubmission = {
|
||||
marketId: '123',
|
||||
buys: [
|
||||
{
|
||||
offset: '1',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
{
|
||||
offset: '2',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const res = renderComponent(mock);
|
||||
expect(res.getByText('Price offset')).toBeInTheDocument();
|
||||
expect(res.getByText('Price reference')).toBeInTheDocument();
|
||||
expect(res.getByText('Proportion')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_BUY-50-1')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_BUY-50-2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles an LP with no buys by just rendering sells', () => {
|
||||
const mock: LiquiditySubmission = {
|
||||
marketId: '123',
|
||||
sells: [
|
||||
{
|
||||
offset: '1',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
{
|
||||
offset: '2',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const res = renderComponent(mock);
|
||||
expect(res.getByText('Price offset')).toBeInTheDocument();
|
||||
expect(res.getByText('Price reference')).toBeInTheDocument();
|
||||
expect(res.getByText('Proportion')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_SELL-50-1')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_SELL-50-2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles an LP with sells by just rendering buys', () => {
|
||||
const mock: LiquiditySubmission = {
|
||||
marketId: '123',
|
||||
buys: [
|
||||
{
|
||||
offset: '1',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
{
|
||||
offset: '2',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const res = renderComponent(mock);
|
||||
expect(res.getByText('Price offset')).toBeInTheDocument();
|
||||
expect(res.getByText('Price reference')).toBeInTheDocument();
|
||||
expect(res.getByText('Proportion')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_BUY-50-1')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_BUY-50-2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles an LP with both sides', () => {
|
||||
const mock: LiquiditySubmission = {
|
||||
marketId: '123',
|
||||
buys: [
|
||||
{
|
||||
offset: '1',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
{
|
||||
offset: '2',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
],
|
||||
sells: [
|
||||
{
|
||||
offset: '4',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
{
|
||||
offset: '2',
|
||||
proportion: 50,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const res = renderComponent(mock);
|
||||
expect(res.getByText('Price offset')).toBeInTheDocument();
|
||||
expect(res.getByText('Price reference')).toBeInTheDocument();
|
||||
expect(res.getByText('Proportion')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_BUY-50-1')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_BUY-50-2')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_SELL-50-4')).toBeInTheDocument();
|
||||
expect(res.getByTestId('SIDE_SELL-50-2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('normalises proportions when they do not total 100%', () => {
|
||||
const mock: LiquiditySubmission = {
|
||||
marketId: '123',
|
||||
buys: [
|
||||
{
|
||||
offset: '1',
|
||||
proportion: 25,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
{
|
||||
offset: '2',
|
||||
proportion: 30,
|
||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const res = renderComponent(mock);
|
||||
expect(res.getByText('45%')).toBeInTheDocument();
|
||||
expect(res.getByText('55%')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import type { LiquiditySubmission } from '../tx-liquidity-submission';
|
||||
import { TableRow } from '../../../table';
|
||||
import { LiquidityProvisionMid } from './components/liquidity-provision-mid';
|
||||
import { LiquidityProvisionDetailsRow } from './components/liquidity-provision-details-row';
|
||||
import { Side } from '@vegaprotocol/types';
|
||||
|
||||
export type VegaPeggedReference = components['schemas']['vegaPeggedReference'];
|
||||
|
||||
export type LiquidityProvisionOrder =
|
||||
components['schemas']['vegaLiquidityOrder'];
|
||||
|
||||
export const LiquidityReferenceLabel: Record<VegaPeggedReference, string> = {
|
||||
PEGGED_REFERENCE_BEST_ASK: t('Best Ask'),
|
||||
PEGGED_REFERENCE_BEST_BID: t('Best Bid'),
|
||||
PEGGED_REFERENCE_MID: t('Mid'),
|
||||
PEGGED_REFERENCE_UNSPECIFIED: '-',
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a side of a liquidity provision order, returns the total
|
||||
* It should be 100%, but it isn't always and if it isn't the proportion
|
||||
* reported for each order should be scaled
|
||||
*
|
||||
* @returns number
|
||||
*/
|
||||
export function sumProportions(
|
||||
side: LiquiditySubmission['buys'] | LiquiditySubmission['sells']
|
||||
): number {
|
||||
if (!side || side.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return side.reduce((total, o) => total + (o.proportion || 0), 0);
|
||||
}
|
||||
|
||||
export type LiquidityProvisionDetailsProps = {
|
||||
provision: LiquiditySubmission;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a table displaying all buys and sells in this LP. It is valid for there
|
||||
* to be no buys or sells.
|
||||
*
|
||||
* It might seem logical to turn proportions in to values based on the total commitment
|
||||
* but based on the current API structure it is awkward, and given that non-LP orders
|
||||
* will change the amount that is actually deployed vs assigned to a level, we decided
|
||||
* not to bother going down that route.
|
||||
*/
|
||||
export function LiquidityProvisionDetails({
|
||||
provision,
|
||||
}: LiquidityProvisionDetailsProps) {
|
||||
if (!provision.buys?.length && !provision.sells?.length) {
|
||||
return null;
|
||||
}
|
||||
// We need to do some additional calcs if these aren't both 100
|
||||
const buyTotal = sumProportions(provision.buys);
|
||||
const sellTotal = sumProportions(provision.sells);
|
||||
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<TableRow modifier="bordered">
|
||||
<th className="px-2 pb-1">{t('Price offset')}</th>
|
||||
<th className="px-2 pb-1">{t('Price reference')}</th>
|
||||
<th className="px-2 pb-1">{t('Proportion')}</th>
|
||||
</TableRow>
|
||||
</thead>
|
||||
<tbody>
|
||||
{provision.buys?.map((b, i) => (
|
||||
<LiquidityProvisionDetailsRow
|
||||
order={b}
|
||||
marketId={provision.marketId}
|
||||
side={Side.SIDE_BUY}
|
||||
key={`SIDE_BUY-${i}`}
|
||||
normaliseProportionsTo={buyTotal}
|
||||
/>
|
||||
))}
|
||||
<LiquidityProvisionMid />
|
||||
{provision.sells?.map((s, i) => (
|
||||
<LiquidityProvisionDetailsRow
|
||||
order={s}
|
||||
marketId={provision.marketId}
|
||||
side={Side.SIDE_SELL}
|
||||
key={`SIDE_SELL-${i}`}
|
||||
normaliseProportionsTo={sellTotal}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +1,11 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import { TableCell, TableRow } from '../../../table';
|
||||
import type { VegaPeggedReference } from '../liquidity-provision/liquidity-provision-details';
|
||||
import { Side, PeggedReferenceMapping } from '@vegaprotocol/types';
|
||||
import { useExplorerMarketQuery } from '../../../links/market-link/__generated__/Market';
|
||||
import type { ExplorerMarketQuery } from '../../../links/market-link/__generated__/Market';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
|
||||
export type VegaPeggedReference = components['schemas']['vegaPeggedReference'];
|
||||
|
||||
export const LiquidityReferenceLabel: Record<VegaPeggedReference, string> = {
|
||||
PEGGED_REFERENCE_BEST_ASK: t('Best Ask'),
|
||||
PEGGED_REFERENCE_BEST_BID: t('Best Bid'),
|
||||
PEGGED_REFERENCE_MID: t('Mid'),
|
||||
PEGGED_REFERENCE_UNSPECIFIED: '-',
|
||||
};
|
||||
|
||||
export interface TxDetailsOrderProps {
|
||||
offset: string;
|
||||
reference: VegaPeggedReference;
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ export const BundleSigners = ({
|
||||
tx,
|
||||
id,
|
||||
}: BundleSignersProps) => {
|
||||
const tokenLink = useLinks(DApp.Governance);
|
||||
const tokenLink = useLinks(DApp.Token);
|
||||
|
||||
const bridgeFunction: BridgeFunction =
|
||||
tx?.changes?.erc20 && 'contractAddress' in tx.changes.erc20
|
||||
|
||||
@@ -33,14 +33,6 @@ const AccountType: Record<AccountTypes, string> = {
|
||||
ACCOUNT_TYPE_HOLDING: 'Holding',
|
||||
ACCOUNT_TYPE_LIQUIDITY_FEES_BONUS_DISTRIBUTION: 'Bonus Distribution',
|
||||
ACCOUNT_TYPE_LP_LIQUIDITY_FEES: 'LP Liquidity Fees',
|
||||
ACCOUNT_TYPE_NETWORK_TREASURY: 'Network Treasury',
|
||||
ACCOUNT_TYPE_VESTING_REWARDS: 'Vesting Rewards',
|
||||
ACCOUNT_TYPE_VESTED_REWARDS: 'Vested Rewards',
|
||||
ACCOUNT_TYPE_REWARD_AVERAGE_POSITION: 'Reward Average Position',
|
||||
ACCOUNT_TYPE_REWARD_RELATIVE_RETURN: 'Reward Relative Return',
|
||||
ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY: 'Reward Return Volatility',
|
||||
ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING: 'Reward Validator Ranking',
|
||||
ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD: 'Pending Fee Referral Reward',
|
||||
};
|
||||
|
||||
interface TransferParticipantsProps {
|
||||
|
||||
@@ -1,50 +1,10 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableWithTbody } from '../../table';
|
||||
import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
|
||||
import { ChainEvent } from './chain-events';
|
||||
import { BigNumber } from 'ethers';
|
||||
|
||||
import type { AbiType } from '../../../lib/encoders/abis/abi-types';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
|
||||
interface AbiOutput {
|
||||
type: AbiType;
|
||||
internalType: AbiType;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the b64/ABIcoded result from an eth cal
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export function decodeEthCallResult(
|
||||
data: BlockExplorerTransactionResult
|
||||
): string {
|
||||
const ethResult = data.command.chainEvent?.contractCall.result;
|
||||
|
||||
try {
|
||||
// Decode the result string: base64 => uint8array
|
||||
const data = base64.decode(ethResult);
|
||||
|
||||
// Parse the escaped ABI in to an object
|
||||
const abi = JSON.parse(
|
||||
'[{"inputs":[],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"}]'
|
||||
);
|
||||
// Pull the expected types out of the Oracles ABI
|
||||
const types: AbiType[] = abi[0].outputs.map((o: AbiOutput) => o.type);
|
||||
|
||||
const rawResult = defaultAbiCoder.decode(types, data);
|
||||
|
||||
// Finally, convert the resulting BigNumber in to a string
|
||||
const res = BigNumber.from(rawResult[0]).toString();
|
||||
return res;
|
||||
} catch (e) {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
import { ChainEvent } from './chain-events';
|
||||
|
||||
interface TxDetailsChainEventProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
|
||||
@@ -16,6 +16,7 @@ import { TxDetailsOrderAmend } from './tx-order-amend';
|
||||
import { TxDetailsWithdrawSubmission } from './tx-withdraw-submission';
|
||||
import { TxDetailsDelegate } from './tx-delegation';
|
||||
import { TxDetailsUndelegate } from './tx-undelegation';
|
||||
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
|
||||
import { TxDetailsLiquidityAmendment } from './tx-liquidity-amend';
|
||||
import { TxDetailsLiquidityCancellation } from './tx-liquidity-cancel';
|
||||
import { TxDetailsDataSubmission } from './tx-data-submission';
|
||||
@@ -27,7 +28,6 @@ import { TxDetailsStateVariable } from './tx-state-variable-proposal';
|
||||
import { TxProposal } from './tx-proposal';
|
||||
import { TxDetailsTransfer } from './tx-transfer';
|
||||
import { TxDetailsStopOrderSubmission } from './tx-stop-order-submission';
|
||||
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
|
||||
|
||||
interface TxDetailsWrapperProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -107,7 +107,7 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
||||
return TxDetailsWithdrawSubmission;
|
||||
case 'Liquidity Provision Order':
|
||||
return TxDetailsLiquiditySubmission;
|
||||
case 'Amend Liquidity Provision Order':
|
||||
case 'Amend LiquidityProvision Order':
|
||||
return TxDetailsLiquidityAmendment;
|
||||
case 'Cancel LiquidityProvision Order':
|
||||
return TxDetailsLiquidityCancellation;
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
|
||||
import PriceInMarket from '../../price-in-market/price-in-market';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
@@ -39,32 +40,40 @@ export const TxDetailsLiquidityAmendment = ({
|
||||
: '-';
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Market')}</TableCell>
|
||||
<TableCell>
|
||||
<MarketLink id={marketId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{amendment.commitmentAmount ? (
|
||||
<>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Commitment amount')}</TableCell>
|
||||
<TableCell>{t('Market')}</TableCell>
|
||||
<TableCell>
|
||||
<PriceInMarket
|
||||
price={amendment.commitmentAmount}
|
||||
marketId={marketId}
|
||||
decimalSource="SETTLEMENT_ASSET"
|
||||
/>
|
||||
<MarketLink id={marketId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{amendment.fee ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Fee')}</TableCell>
|
||||
<TableCell>{fee}%</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
{amendment.commitmentAmount ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Commitment amount')}</TableCell>
|
||||
<TableCell>
|
||||
<PriceInMarket
|
||||
price={amendment.commitmentAmount}
|
||||
marketId={marketId}
|
||||
decimalSource="SETTLEMENT_ASSET"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{amendment.fee ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Fee')}</TableCell>
|
||||
<TableCell>{fee}%</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
|
||||
<LiquidityProvisionDetails provision={amendment} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
describe('TxDetailsLiquiditySubmission', () => {
|
||||
const mockTxData = {
|
||||
hash: 'test',
|
||||
command: {
|
||||
liquidityProvisionSubmission: {
|
||||
marketId: 'BTC-USD',
|
||||
commitmentAmount: 100,
|
||||
fee: '0.01',
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockPubKey = '123';
|
||||
const mockBlockData = {
|
||||
result: {
|
||||
block: {
|
||||
header: {
|
||||
height: '123',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should render the component with correct data', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsLiquiditySubmission
|
||||
txData={mockTxData as BlockExplorerTransactionResult}
|
||||
pubKey={mockPubKey}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(getByText('Market')).toBeInTheDocument();
|
||||
expect(getByText('BTC-USD')).toBeInTheDocument();
|
||||
expect(getByText('Commitment amount')).toBeInTheDocument();
|
||||
expect(getByText('100')).toBeInTheDocument();
|
||||
expect(getByText('Fee')).toBeInTheDocument();
|
||||
expect(getByText('1%')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display awaiting message when tx data is undefined', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsLiquiditySubmission
|
||||
txData={undefined}
|
||||
pubKey={mockPubKey}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
getByText('Awaiting Block Explorer transaction details')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display awaiting message when liquidityProvisionSubmission is undefined', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsLiquiditySubmission
|
||||
txData={{ command: {} } as BlockExplorerTransactionResult}
|
||||
pubKey={mockPubKey}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
getByText('Awaiting Block Explorer transaction details')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,30 +1,31 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import { MarketLink } from '../../links';
|
||||
import { MarketLink } from '../../links/';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
|
||||
import PriceInMarket from '../../price-in-market/price-in-market';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export type LiquiditySubmission =
|
||||
components['schemas']['v1LiquidityProvisionSubmission'];
|
||||
|
||||
interface TxDetailsLiquidityAmendmentProps {
|
||||
interface TxDetailsLiquiditySubmissionProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* An existing liquidity order is being created.
|
||||
* Someone cancelled an order
|
||||
*/
|
||||
export const TxDetailsLiquiditySubmission = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsLiquidityAmendmentProps) => {
|
||||
}: TxDetailsLiquiditySubmissionProps) => {
|
||||
if (!txData || !txData.command.liquidityProvisionSubmission) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
@@ -38,38 +39,40 @@ export const TxDetailsLiquiditySubmission = ({
|
||||
: '-';
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Market')}</TableCell>
|
||||
<TableCell>
|
||||
<MarketLink id={marketId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{submission.commitmentAmount ? (
|
||||
<>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Commitment amount')}</TableCell>
|
||||
<TableCell>{t('Market')}</TableCell>
|
||||
<TableCell>
|
||||
<PriceInMarket
|
||||
price={submission.commitmentAmount}
|
||||
marketId={marketId}
|
||||
decimalSource="SETTLEMENT_ASSET"
|
||||
/>
|
||||
<MarketLink id={marketId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{submission.fee ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Fee')}</TableCell>
|
||||
<TableCell>{fee}%</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{submission.reference ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Reference')}</TableCell>
|
||||
<TableCell>{submission.reference}</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
{submission.commitmentAmount ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Commitment amount')}</TableCell>
|
||||
<TableCell>
|
||||
<PriceInMarket
|
||||
price={submission.commitmentAmount}
|
||||
marketId={marketId}
|
||||
decimalSource="SETTLEMENT_ASSET"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{submission.fee ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Fee')}</TableCell>
|
||||
<TableCell>{fee}%</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
|
||||
<LiquidityProvisionDetails provision={submission} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
SPECIAL_CASE_NETWORK,
|
||||
SPECIAL_CASE_NETWORK_ID,
|
||||
} from '../../links/party-link/party-link';
|
||||
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
||||
|
||||
type Transfer = components['schemas']['commandsv1Transfer'];
|
||||
|
||||
@@ -64,16 +63,10 @@ export const TxDetailsTransfer = ({
|
||||
return (
|
||||
<>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TableRow modifier="bordered" data-testid="type">
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
|
||||
<TableCell>{getTypeLabelForTransfer(transfer)}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered" data-testid="id">
|
||||
<TableCell {...sharedHeaderProps}>{t('Transfer ID')}</TableCell>
|
||||
<TableCell>
|
||||
{txSignatureToDeterministicId(txData.signature.value)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
@@ -81,7 +74,7 @@ export const TxDetailsTransfer = ({
|
||||
hideTypeRow={true}
|
||||
/>
|
||||
{from ? (
|
||||
<TableRow modifier="bordered" data-testid="from">
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('From')}</TableCell>
|
||||
<TableCell>
|
||||
<PartyLink id={from} />
|
||||
@@ -89,7 +82,7 @@ export const TxDetailsTransfer = ({
|
||||
</TableRow>
|
||||
) : null}
|
||||
{transfer.to ? (
|
||||
<TableRow modifier="bordered" data-testid="to">
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('To')}</TableCell>
|
||||
<TableCell>
|
||||
<PartyLink id={transfer.to} />
|
||||
@@ -97,7 +90,7 @@ export const TxDetailsTransfer = ({
|
||||
</TableRow>
|
||||
) : null}
|
||||
{transfer.asset && transfer.amount ? (
|
||||
<TableRow modifier="bordered" data-testid="amount">
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Amount')}</TableCell>
|
||||
<TableCell>
|
||||
<SizeInAsset assetId={transfer.asset} size={transfer.amount} />
|
||||
|
||||
@@ -18,13 +18,11 @@ import { FilterLabel } from './tx-filter-label';
|
||||
export type FilterOption =
|
||||
| 'Amend LiquidityProvision Order'
|
||||
| 'Amend Order'
|
||||
| 'Apply Referral Code'
|
||||
| 'Batch Market Instructions'
|
||||
| 'Cancel LiquidityProvision Order'
|
||||
| 'Cancel Order'
|
||||
| 'Cancel Transfer Funds'
|
||||
| 'Chain Event'
|
||||
| 'Create Referral Set'
|
||||
| 'Delegate'
|
||||
| 'Ethereum Key Rotate Submission'
|
||||
| 'Issue Signatures'
|
||||
@@ -42,7 +40,6 @@ export type FilterOption =
|
||||
| 'Submit Order'
|
||||
| 'Transfer Funds'
|
||||
| 'Undelegate'
|
||||
| 'Update Referral Set'
|
||||
| 'Validator Heartbeat'
|
||||
| 'Vote on Proposal'
|
||||
| 'Withdraw';
|
||||
|
||||
@@ -104,30 +104,10 @@ export function getLabelForProposal(
|
||||
}
|
||||
} else if (proposal.terms?.updateMarket) {
|
||||
return t('Proposal: Update market');
|
||||
} else if (proposal.terms?.updateSpotMarket) {
|
||||
return t('Proposal: Update spot');
|
||||
} else if (proposal.terms?.updateMarketState) {
|
||||
const type = proposal.terms.updateMarketState.changes?.updateType;
|
||||
if (type === 'MARKET_STATE_UPDATE_TYPE_TERMINATE') {
|
||||
return t('Proposal: Market terminate');
|
||||
} else if (type === 'MARKET_STATE_UPDATE_TYPE_SUSPEND') {
|
||||
return t('Proposal: Market suspend');
|
||||
} else if (type === 'MARKET_STATE_UPDATE_TYPE_RESUME') {
|
||||
return t('Proposal: Market resume');
|
||||
}
|
||||
return t('Proposal: Market state');
|
||||
} else if (proposal.terms?.updateNetworkParameter) {
|
||||
return t('Proposal: Network parameter');
|
||||
} else if (proposal.terms?.updateReferralProgram) {
|
||||
return t('Proposal: Referral program');
|
||||
} else if (proposal.terms?.updateVolumeDiscountProgram) {
|
||||
return t('Proposal: Discount program');
|
||||
} else if (proposal.terms?.newFreeform) {
|
||||
return t('Proposal: Freeform');
|
||||
} else if (proposal.terms?.newTransfer) {
|
||||
return t('Proposal: Transfer');
|
||||
} else if (proposal.terms?.cancelTransfer) {
|
||||
return t('Proposal: Transfer cancel');
|
||||
} else {
|
||||
return t('Proposal');
|
||||
}
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../routes/blocks/tendermint-blocks-response';
|
||||
import { getTypeLabelForTransfer } from './details/tx-transfer';
|
||||
import type { components } from '../../../types/explorer';
|
||||
|
||||
import {
|
||||
TxDetailsTransfer,
|
||||
getTypeLabelForTransfer,
|
||||
} from './details/tx-transfer';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
type Transfer = components['schemas']['commandsv1Transfer'];
|
||||
|
||||
describe('TX: Transfer: getLabelForTransfer', () => {
|
||||
@@ -65,70 +56,3 @@ describe('TX: Transfer: getLabelForTransfer', () => {
|
||||
expect(getTypeLabelForTransfer(mock)).toEqual('Transfer');
|
||||
});
|
||||
});
|
||||
|
||||
describe('TxDetailsTransfer', () => {
|
||||
const mockBlockData = {
|
||||
result: {
|
||||
block: {
|
||||
header: {
|
||||
height: '123',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mockTxData: Partial<BlockExplorerTransactionResult> = {
|
||||
hash: 'test',
|
||||
submitter:
|
||||
'e1943eea46fed576cf2be42972f3c5515ad3d0ac7ac013f56677c12a53a1b3ed',
|
||||
command: {
|
||||
nonce: '5188810881378065222',
|
||||
blockHeight: '14951513',
|
||||
transfer: {
|
||||
fromAccountType: 'ACCOUNT_TYPE_GENERAL',
|
||||
to: '78432a2808f20b18a46ccc6a917bdc4d63c2b9e7007f777bdcab5a9f462c5ba6',
|
||||
toAccountType: 'ACCOUNT_TYPE_GENERAL',
|
||||
asset:
|
||||
'dd20590509d30d20bdbbe64dc1090c1140c7690121a9b9940bc66f62dfa2e599',
|
||||
amount: '4800000000',
|
||||
reference: '',
|
||||
oneOff: {
|
||||
deliverOn: '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
signature: {
|
||||
value:
|
||||
'610c2e196a7d4fed4413b9e82af267b1ff3e30e943df3a3d28096fd60604d430d752fbaf6dd4f84d496be78885bb6118f40560bff7832c06bd7a3d67b718b700',
|
||||
},
|
||||
};
|
||||
|
||||
it('renders basic transfer details', () => {
|
||||
const { getByTestId } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsTransfer
|
||||
txData={mockTxData as BlockExplorerTransactionResult}
|
||||
pubKey={mockTxData.command.submitter}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
const id = getByTestId('id');
|
||||
expect(id.children[0].textContent).toEqual('Transfer ID');
|
||||
expect(id.children[1].textContent).toEqual(
|
||||
'51f3bab5eb2637651012507a64d497790a734248792c16e5cf36df8984074fbd'
|
||||
);
|
||||
|
||||
const type = getByTestId('type');
|
||||
expect(type.children[1].textContent).toEqual('Transfer');
|
||||
|
||||
const from = getByTestId('from');
|
||||
expect(from.children[1].textContent).toEqual(mockTxData.submitter);
|
||||
|
||||
const to = getByTestId('to');
|
||||
expect(to.children[1].textContent).toEqual(mockTxData.command.transfer.to);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,32 +82,6 @@ describe('Txs infinite list item', () => {
|
||||
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders data even with missing time', () => {
|
||||
render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<table>
|
||||
<tbody>
|
||||
<TxsInfiniteListItem
|
||||
type="testType"
|
||||
submitter="testPubKey"
|
||||
hash="testTxHash"
|
||||
block="1"
|
||||
code={0}
|
||||
command={{}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByTestId('tx-hash')).toHaveTextContent('testTxHash');
|
||||
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
||||
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
||||
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
||||
expect(screen.getByTestId('tx-time')).toHaveTextContent('-');
|
||||
});
|
||||
|
||||
it('renders data correctly', () => {
|
||||
render(
|
||||
<MockedProvider>
|
||||
@@ -121,7 +95,6 @@ describe('Txs infinite list item', () => {
|
||||
block="1"
|
||||
code={0}
|
||||
command={{}}
|
||||
createdAt="1970-11-01T18:07:15Z"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -132,6 +105,5 @@ describe('Txs infinite list item', () => {
|
||||
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
||||
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
||||
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
||||
expect(screen.getByTestId('tx-time').textContent).toMatch(/years ago/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,6 @@ import { PartyLink } from '../links';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import type { Screen } from '@vegaprotocol/react-helpers';
|
||||
import { useMemo } from 'react';
|
||||
import { TimeAgo } from '../time-ago';
|
||||
|
||||
const DEFAULT_TRUNCATE_LENGTH = 7;
|
||||
|
||||
@@ -33,7 +32,6 @@ export const TxsInfiniteListItem = ({
|
||||
type,
|
||||
block,
|
||||
command,
|
||||
createdAt,
|
||||
}: Partial<BlockExplorerTransactionResult>) => {
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const idTruncateLength = useMemo(
|
||||
@@ -87,11 +85,6 @@ export const TxsInfiniteListItem = ({
|
||||
endChars={5}
|
||||
/>
|
||||
</td>
|
||||
{['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize) && (
|
||||
<td className="text-sm items-center font-mono" data-testid="tx-time">
|
||||
{createdAt ? <TimeAgo date={createdAt} /> : '-'}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ import { TxsInfiniteListItem } from './txs-infinite-list-item';
|
||||
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
|
||||
import EmptyList from '../empty-list/empty-list';
|
||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
|
||||
interface TxsInfiniteListProps {
|
||||
hasMoreTxs: boolean;
|
||||
@@ -20,16 +19,7 @@ interface ItemProps {
|
||||
}
|
||||
|
||||
const Item = ({ tx }: ItemProps) => {
|
||||
const {
|
||||
hash,
|
||||
submitter,
|
||||
type,
|
||||
command,
|
||||
block,
|
||||
code,
|
||||
createdAt,
|
||||
index: blockIndex,
|
||||
} = tx;
|
||||
const { hash, submitter, type, command, block, code, index: blockIndex } = tx;
|
||||
return (
|
||||
<TxsInfiniteListItem
|
||||
type={type}
|
||||
@@ -39,7 +29,6 @@ const Item = ({ tx }: ItemProps) => {
|
||||
hash={hash}
|
||||
block={block}
|
||||
index={blockIndex}
|
||||
createdAt={createdAt}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -50,7 +39,6 @@ export const TxsInfiniteList = ({
|
||||
className,
|
||||
hasFilters = false,
|
||||
}: TxsInfiniteListProps) => {
|
||||
const { screenSize } = useScreenDimensions();
|
||||
if (!txs || txs.length === 0) {
|
||||
if (!areTxsLoading) {
|
||||
return (
|
||||
@@ -78,9 +66,6 @@ export const TxsInfiniteList = ({
|
||||
<th>{t('Type')}</th>
|
||||
<th className="text-left">{t('From')}</th>
|
||||
<th>{t('Block')}</th>
|
||||
{['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize) && (
|
||||
<th>{t('Time')}</th>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -43,7 +43,7 @@ export const getTxsDataUrl = (params: IGetTxsDataUrl) => {
|
||||
url.searchParams.append('first', count);
|
||||
url.searchParams.append('after', params.after);
|
||||
} else {
|
||||
url.searchParams.append('last', count);
|
||||
url.searchParams.append('first', count);
|
||||
}
|
||||
|
||||
// Hacky fix for param as array
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('getTxsDataUrl', () => {
|
||||
count: 10,
|
||||
baseUrl: 'https://example.com/transactions',
|
||||
};
|
||||
const expectedUrl = 'https://example.com/transactions?last=10';
|
||||
const expectedUrl = 'https://example.com/transactions?first=10';
|
||||
|
||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||
});
|
||||
@@ -41,7 +41,7 @@ describe('getTxsDataUrl', () => {
|
||||
baseUrl: 'https://example.com/transactions',
|
||||
};
|
||||
const expectedUrl =
|
||||
'https://example.com/transactions?last=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
|
||||
'https://example.com/transactions?first=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
|
||||
|
||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||
});
|
||||
|
||||
@@ -133,7 +133,7 @@ export const ErrorBoundary = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const GHOST = (
|
||||
const GHOST = (
|
||||
<svg
|
||||
width="56"
|
||||
height="85"
|
||||
|
||||
@@ -36,50 +36,30 @@ const PERCENTAGE_PARAMS = [
|
||||
'governance.proposal.updateNetParam.requiredMajority',
|
||||
'governance.proposal.updateNetParam.requiredParticipation',
|
||||
'governance.proposal.updateMarket.minProposerEquityLikeShare',
|
||||
'governance.proposal.VolumeDiscountProgram.requiredMajority',
|
||||
'governance.proposal.VolumeDiscountProgram.requiredParticipation',
|
||||
'governance.proposal.referralProgram.requiredMajority',
|
||||
'governance.proposal.transfer.requiredMajority',
|
||||
'governance.proposal.updateAsset.requiredMajority',
|
||||
'governance.proposal.updateAsset.requiredParticipation',
|
||||
'governance.proposal.transfer.requiredParticipation',
|
||||
'governance.proposal.referralProgram.requiredParticipation',
|
||||
'network.validators.ersatz.rewardFactor',
|
||||
'network.validators.ersatz.multipleOfTendermintValidators',
|
||||
'validators.vote.required',
|
||||
'referralProgram.maxReferralRewardFactor',
|
||||
'referralProgram.maxReferralDiscountFactor',
|
||||
'referralProgram.maxReferralRewardProportion',
|
||||
].map((p) => p.toLowerCase());
|
||||
];
|
||||
|
||||
const BIG_NUMBER_PARAMS = [
|
||||
'spam.protection.delegation.min.tokens',
|
||||
'validators.delegation.minAmount',
|
||||
'governance.proposal.transfer.maxAmount',
|
||||
'reward.staking.delegation.minimumValidatorStake',
|
||||
'reward.staking.delegation.maxPayoutPerParticipant',
|
||||
'reward.staking.delegation.maxPayoutPerEpoch',
|
||||
'spam.protection.voting.min.tokens',
|
||||
'spam.protection.proposal.min.tokens',
|
||||
'governance.proposal.transfer.minVoterBalance',
|
||||
'governance.proposal.freeform.minProposerBalance',
|
||||
'governance.proposal.updateNetParam.minVoterBalance',
|
||||
'governance.proposal.updateMarket.minVoterBalance',
|
||||
'governance.proposal.asset.minVoterBalance',
|
||||
'governance.proposal.updateNetParam.minProposerBalance',
|
||||
'governance.proposal.freeform.minVoterBalance',
|
||||
'spam.protection.proposal.min.tokens',
|
||||
'governance.proposal.updateMarket.minProposerBalance',
|
||||
'governance.proposal.asset.minProposerBalance',
|
||||
'governance.proposal.transfer.minProposerBalance',
|
||||
'governance.proposal.market.minProposerBalance',
|
||||
'governance.proposal.market.minVoterBalance',
|
||||
'governance.proposal.updateAsset.minProposerBalance',
|
||||
'governance.proposal.updateAsset.minVoterBalance',
|
||||
'governance.proposal.referralProgram.minProposerBalance',
|
||||
'governance.proposal.referralProgram.minVoterBalance',
|
||||
'governance.proposal.VolumeDiscountProgram.minProposerBalance',
|
||||
'governance.proposal.VolumeDiscountProgram.minVoterBalance',
|
||||
].map((p) => p.toLowerCase());
|
||||
];
|
||||
|
||||
export const renderGroupedParams = (
|
||||
group: GroupedParams,
|
||||
@@ -151,12 +131,12 @@ export const NetworkParameterRow = ({
|
||||
<div className="pb-2">
|
||||
<SyntaxHighlighter data={JSON.parse(value)} />
|
||||
</div>
|
||||
) : BIG_NUMBER_PARAMS.includes(key.toLowerCase()) ? (
|
||||
addDecimalsFormatNumber(Number(value), 18)
|
||||
) : PERCENTAGE_PARAMS.includes(key.toLowerCase()) ? (
|
||||
`${formatNumber(Number(value) * 100, 0)}%`
|
||||
) : isNaN(Number(value)) ? (
|
||||
value
|
||||
) : BIG_NUMBER_PARAMS.includes(key) ? (
|
||||
addDecimalsFormatNumber(Number(value), 18)
|
||||
) : PERCENTAGE_PARAMS.includes(key) ? (
|
||||
`${formatNumber(Number(value) * 100, 0)}%`
|
||||
) : (
|
||||
formatNumber(Number(value), 4)
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fragment ExplorerOracleDataConnection on OracleSpec {
|
||||
dataConnection(pagination: { first: 30 }) {
|
||||
dataConnection {
|
||||
edges {
|
||||
node {
|
||||
externalData {
|
||||
@@ -45,51 +45,10 @@ fragment ExplorerOracleDataSource on OracleSpec {
|
||||
operator
|
||||
}
|
||||
}
|
||||
... on DataSourceSpecConfigurationTimeTrigger {
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
triggers {
|
||||
initial
|
||||
every
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on EthCallSpec {
|
||||
abi
|
||||
args
|
||||
method
|
||||
requiredConfirmations
|
||||
address
|
||||
normalisers {
|
||||
name
|
||||
expression
|
||||
}
|
||||
trigger {
|
||||
trigger {
|
||||
... on EthTimeTrigger {
|
||||
initial
|
||||
every
|
||||
until
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
@@ -113,23 +72,6 @@ fragment ExplorerOracleDataSource on OracleSpec {
|
||||
}
|
||||
}
|
||||
}
|
||||
... on EthCallSpec {
|
||||
abi
|
||||
address
|
||||
requiredConfirmations
|
||||
method
|
||||
filters {
|
||||
key {
|
||||
type
|
||||
name
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +81,7 @@ fragment ExplorerOracleDataSource on OracleSpec {
|
||||
}
|
||||
|
||||
query ExplorerOracleSpecs {
|
||||
oracleSpecsConnection(pagination: { first: 30 }) {
|
||||
oracleSpecsConnection(pagination: { first: 50 }) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
|
||||
@@ -1,85 +1,14 @@
|
||||
fragment ExplorerOraclePerpetual on Perpetual {
|
||||
dataSourceSpecForSettlementData {
|
||||
id
|
||||
status
|
||||
}
|
||||
dataSourceSpecForSettlementSchedule {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
fragment ExplorerOracleFuture on Future {
|
||||
dataSourceSpecForSettlementData {
|
||||
id
|
||||
status
|
||||
}
|
||||
dataSourceSpecForTradingTermination {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
fragment ExplorerOracleForMarketsMarket on Market {
|
||||
id
|
||||
state
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
product {
|
||||
... on Future {
|
||||
...ExplorerOracleFuture
|
||||
}
|
||||
... on Perpetual {
|
||||
...ExplorerOraclePerpetual
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment ExplorerOracleDataSourceSpec on ExternalDataSourceSpec {
|
||||
spec {
|
||||
id
|
||||
status
|
||||
data {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
... on DataSourceSpecConfigurationTimeTrigger {
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
triggers {
|
||||
initial
|
||||
every
|
||||
}
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
id
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on EthCallSpec {
|
||||
address
|
||||
}
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSourceSpecForTradingTermination {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,27 +24,4 @@ query ExplorerOracleFormMarkets {
|
||||
}
|
||||
}
|
||||
}
|
||||
oracleSpecsConnection {
|
||||
edges {
|
||||
node {
|
||||
dataSourceSpec {
|
||||
...ExplorerOracleDataSourceSpec
|
||||
}
|
||||
dataConnection(pagination: { last: 1 }) {
|
||||
edges {
|
||||
node {
|
||||
externalData {
|
||||
data {
|
||||
data {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-63
@@ -5,23 +5,23 @@ import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
||||
|
||||
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
||||
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
||||
|
||||
export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
|
||||
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
|
||||
|
||||
export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{
|
||||
id: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
|
||||
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
|
||||
|
||||
export const ExplorerOracleDataConnectionFragmentDoc = gql`
|
||||
fragment ExplorerOracleDataConnection on OracleSpec {
|
||||
dataConnection(pagination: {first: 30}) {
|
||||
dataConnection {
|
||||
edges {
|
||||
node {
|
||||
externalData {
|
||||
@@ -68,51 +68,10 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
||||
operator
|
||||
}
|
||||
}
|
||||
... on DataSourceSpecConfigurationTimeTrigger {
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
triggers {
|
||||
initial
|
||||
every
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on EthCallSpec {
|
||||
abi
|
||||
args
|
||||
method
|
||||
requiredConfirmations
|
||||
address
|
||||
normalisers {
|
||||
name
|
||||
expression
|
||||
}
|
||||
trigger {
|
||||
trigger {
|
||||
... on EthTimeTrigger {
|
||||
initial
|
||||
every
|
||||
until
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
@@ -136,23 +95,6 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
... on EthCallSpec {
|
||||
abi
|
||||
address
|
||||
requiredConfirmations
|
||||
method
|
||||
filters {
|
||||
key {
|
||||
type
|
||||
name
|
||||
numberDecimalPlaces
|
||||
}
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,7 +105,7 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
||||
${ExplorerOracleDataConnectionFragmentDoc}`;
|
||||
export const ExplorerOracleSpecsDocument = gql`
|
||||
query ExplorerOracleSpecs {
|
||||
oracleSpecsConnection(pagination: {first: 30}) {
|
||||
oracleSpecsConnection(pagination: {first: 50}) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
|
||||
@@ -3,106 +3,25 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerOraclePerpetualFragment = { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } };
|
||||
|
||||
export type ExplorerOracleFutureFragment = { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } };
|
||||
|
||||
export type ExplorerOracleForMarketsMarketFragment = { __typename?: 'Market', id: string, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Spot' } } } };
|
||||
|
||||
export type ExplorerOracleDataSourceSpecFragment = { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } | { __typename?: 'EthCallSpec', address: string } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } };
|
||||
export type ExplorerOracleForMarketsMarketFragment = { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string } } } } };
|
||||
|
||||
export type ExplorerOracleFormMarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ExplorerOracleFormMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Spot' } } } } }> } | null, oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } | { __typename?: 'EthCallSpec', address: string } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
|
||||
export type ExplorerOracleFormMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string } } } } } }> } | null };
|
||||
|
||||
export const ExplorerOracleFutureFragmentDoc = gql`
|
||||
fragment ExplorerOracleFuture on Future {
|
||||
dataSourceSpecForSettlementData {
|
||||
id
|
||||
status
|
||||
}
|
||||
dataSourceSpecForTradingTermination {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const ExplorerOraclePerpetualFragmentDoc = gql`
|
||||
fragment ExplorerOraclePerpetual on Perpetual {
|
||||
dataSourceSpecForSettlementData {
|
||||
id
|
||||
status
|
||||
}
|
||||
dataSourceSpecForSettlementSchedule {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const ExplorerOracleForMarketsMarketFragmentDoc = gql`
|
||||
fragment ExplorerOracleForMarketsMarket on Market {
|
||||
id
|
||||
state
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
product {
|
||||
... on Future {
|
||||
...ExplorerOracleFuture
|
||||
}
|
||||
... on Perpetual {
|
||||
...ExplorerOraclePerpetual
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${ExplorerOracleFutureFragmentDoc}
|
||||
${ExplorerOraclePerpetualFragmentDoc}`;
|
||||
export const ExplorerOracleDataSourceSpecFragmentDoc = gql`
|
||||
fragment ExplorerOracleDataSourceSpec on ExternalDataSourceSpec {
|
||||
spec {
|
||||
id
|
||||
status
|
||||
data {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
}
|
||||
... on DataSourceSpecConfigurationTimeTrigger {
|
||||
conditions {
|
||||
value
|
||||
operator
|
||||
}
|
||||
triggers {
|
||||
initial
|
||||
every
|
||||
}
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
id
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on EthCallSpec {
|
||||
address
|
||||
}
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSourceSpecForTradingTermination {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,32 +38,8 @@ export const ExplorerOracleFormMarketsDocument = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
oracleSpecsConnection {
|
||||
edges {
|
||||
node {
|
||||
dataSourceSpec {
|
||||
...ExplorerOracleDataSourceSpec
|
||||
}
|
||||
dataConnection(pagination: {last: 1}) {
|
||||
edges {
|
||||
node {
|
||||
externalData {
|
||||
data {
|
||||
data {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${ExplorerOracleForMarketsMarketFragmentDoc}
|
||||
${ExplorerOracleDataSourceSpecFragmentDoc}`;
|
||||
${ExplorerOracleForMarketsMarketFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useExplorerOracleFormMarketsQuery__
|
||||
|
||||
@@ -51,26 +51,15 @@ describe('Oracle Data view', () => {
|
||||
{
|
||||
node: {
|
||||
externalData: {
|
||||
__typename: 'ExternalData',
|
||||
data: {
|
||||
__typename: 'Data',
|
||||
matchedSpecIds: ['123'],
|
||||
broadcastAt: '2023-01-01T00:00:00Z',
|
||||
data: [
|
||||
{
|
||||
__typename: 'Property',
|
||||
name: 'Test-name',
|
||||
value: 'Test-data',
|
||||
},
|
||||
],
|
||||
broadcastAt: '2022-01-01',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
} as ExplorerOracleDataConnectionFragment['dataConnection'])
|
||||
} as DataConnection)
|
||||
);
|
||||
expect(res.getByText('Test-name')).toBeInTheDocument();
|
||||
expect(res.getByText('Test-data')).toBeInTheDocument();
|
||||
expect(res.getByText('Broadcast data')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,55 +1,39 @@
|
||||
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
|
||||
import { TimeAgo } from '../../../components/time-ago';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
const cellSpacing = 'px-3';
|
||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import filter from 'recursive-key-filter';
|
||||
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
|
||||
|
||||
interface OracleDataTypeProps {
|
||||
data: ExplorerOracleDataConnectionFragment['dataConnection'];
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is data that has matched this oracle, this view will
|
||||
* render the data inside a collapsed element so that it can be viewed.
|
||||
* Currently the data is just rendered as a JSON view, because
|
||||
* that Does The Job, rather than because it's good.
|
||||
*/
|
||||
export function OracleData({ data }: OracleDataTypeProps) {
|
||||
if (!data || !data.edges?.length) {
|
||||
if (!data || !data.edges?.length || data.edges.length > 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="text-3xl font-bold mb-4 display-5 mt-5">
|
||||
{t('Recent data')}
|
||||
</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr className="text-left">
|
||||
<th className={cellSpacing}>Value</th>
|
||||
<th className={cellSpacing}>Key</th>
|
||||
<th className={cellSpacing}>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.edges.map((d) => {
|
||||
if (!d) {
|
||||
return null;
|
||||
}
|
||||
<details data-testid="oracle-data">
|
||||
<summary>{t('Broadcast data')}</summary>
|
||||
<ul>
|
||||
{data.edges.map((d) => {
|
||||
if (!d) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const broadcastAt = d.node.externalData.data.broadcastAt;
|
||||
const node = d.node.externalData.data.data?.at(0);
|
||||
if (!node || !node.value || !node.name || !node || !broadcastAt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<tr key={d.node.externalData.data.broadcastAt}>
|
||||
<td className={`${cellSpacing} font-mono`}>{node.value}</td>
|
||||
<td className={`${cellSpacing} font-mono`}>{node.name}</td>
|
||||
<td className={cellSpacing}>
|
||||
<TimeAgo date={broadcastAt} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
return (
|
||||
<li key={d.node.externalData.data.broadcastAt}>
|
||||
<SyntaxHighlighter data={filter(d, ['__typename'])} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,13 +55,13 @@ describe('Oracle type view', () => {
|
||||
const s = mock('prices.external.whatever');
|
||||
expect(isInternalSourceType(s)).toEqual(false);
|
||||
const res = render(renderWrappedComponent(s));
|
||||
expect(res.getByText('External Data')).toBeInTheDocument();
|
||||
expect(res.getByText('External data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders External data otherwise', () => {
|
||||
const s = mock('prices.external.vegaprotocol.builtin.');
|
||||
expect(isInternalSourceType(s)).toEqual(false);
|
||||
const res = render(renderWrappedComponent(s));
|
||||
expect(res.getByText('External Data')).toBeInTheDocument();
|
||||
expect(res.getByText('External data')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,19 +26,6 @@ export function isInternalSourceType(s: SourceType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getExternalType(s: SourceType) {
|
||||
if (s.sourceType.__typename === 'EthCallSpec') {
|
||||
return 'Ethereum Contract Call';
|
||||
} else {
|
||||
return 'External Data';
|
||||
}
|
||||
}
|
||||
|
||||
export function getTypeString(s: SourceType) {
|
||||
const isInternal = isInternalSourceType(s);
|
||||
return isInternal ? 'Internal data' : getExternalType(s);
|
||||
}
|
||||
|
||||
interface OracleDetailsTypeProps {
|
||||
sourceType: SourceType;
|
||||
}
|
||||
@@ -52,10 +39,14 @@ export function OracleDetailsType({ sourceType }: OracleDetailsTypeProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isInternal = isInternalSourceType(sourceType);
|
||||
|
||||
return (
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">Type</TableHeader>
|
||||
<TableCell modifier="bordered">{getTypeString(sourceType)}</TableCell>
|
||||
<TableCell modifier="bordered">
|
||||
{isInternal ? 'Internal data' : 'External data'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { TableRow, TableCell, TableHeader } from '../../../components/table';
|
||||
import type { SourceType } from './oracle';
|
||||
import {
|
||||
EthExplorerLink,
|
||||
EthExplorerLinkTypes,
|
||||
} from '../../../components/links/eth-explorer-link/eth-explorer-link';
|
||||
|
||||
interface OracleDetailsEthSourceProps {
|
||||
sourceType: SourceType;
|
||||
}
|
||||
/**
|
||||
* Given an Oracle that sources data from Ethereum, this component will render
|
||||
* a link to the smart contract and some basic details
|
||||
*/
|
||||
export function OracleEthSource({ sourceType }: OracleDetailsEthSourceProps) {
|
||||
if (
|
||||
sourceType.__typename !== 'DataSourceDefinitionExternal' ||
|
||||
sourceType.sourceType.__typename !== 'EthCallSpec'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const address = sourceType.sourceType.address;
|
||||
|
||||
if (!address) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">Ethereum Contract</TableHeader>
|
||||
<TableCell modifier="bordered">
|
||||
<EthExplorerLink id={address} type={EthExplorerLinkTypes.address} />
|
||||
<span className="mx-3">⇒</span>
|
||||
<code>{sourceType.sourceType.method}</code>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +1,14 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { OracleFilter } from './oracle-filter';
|
||||
import { getConditionsOrFilters, OracleFilter } from './oracle-filter';
|
||||
import type { Filter } from './oracle-filter';
|
||||
import type { ExplorerOracleDataSourceFragment } from '../__generated__/Oracles';
|
||||
import {
|
||||
ConditionOperator,
|
||||
DataSourceSpecStatus,
|
||||
PropertyKeyType,
|
||||
} from '@vegaprotocol/types';
|
||||
import type { Condition } from '@vegaprotocol/types';
|
||||
|
||||
type Spec =
|
||||
ExplorerOracleDataSourceFragment['dataSourceSpec']['spec']['data']['sourceType'];
|
||||
|
||||
const mockExternalSpec: Spec = {
|
||||
const mockExternalSpec = {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration',
|
||||
filters: [
|
||||
@@ -19,12 +16,12 @@ const mockExternalSpec: Spec = {
|
||||
__typename: 'Filter',
|
||||
key: {
|
||||
type: PropertyKeyType.TYPE_INTEGER,
|
||||
name: 'testKey',
|
||||
name: 'test',
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
__typename: 'Condition',
|
||||
value: 'testValue',
|
||||
value: 'test',
|
||||
operator: ConditionOperator.OPERATOR_EQUALS,
|
||||
},
|
||||
],
|
||||
@@ -33,6 +30,16 @@ const mockExternalSpec: Spec = {
|
||||
},
|
||||
};
|
||||
|
||||
const mockTimeSpec = {
|
||||
__typename: 'DataSourceSpecConfigurationTime',
|
||||
conditions: [
|
||||
{
|
||||
value: '123',
|
||||
operator: ConditionOperator.OPERATOR_EQUALS,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function renderComponent(data: ExplorerOracleDataSourceFragment) {
|
||||
return <OracleFilter data={data} />;
|
||||
}
|
||||
@@ -63,16 +70,11 @@ describe('Oracle Filter view', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
dataConnection: {
|
||||
edges: [],
|
||||
},
|
||||
})
|
||||
} as ExplorerOracleDataSourceFragment)
|
||||
);
|
||||
|
||||
// Renders a comprehensible summary of key = value
|
||||
expect(res.getByText('testKey')).toBeInTheDocument();
|
||||
expect(res.getByText('=')).toBeInTheDocument();
|
||||
expect(res.getByText('testValue')).toBeInTheDocument();
|
||||
expect(res.getByText('Filter')).toBeInTheDocument();
|
||||
// Avoids asserting on how the data is presented because it is very rudimentary
|
||||
});
|
||||
|
||||
it('Renders conditions if type is DataSourceSpecConfigurationTime', () => {
|
||||
@@ -86,59 +88,75 @@ describe('Oracle Filter view', () => {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionInternal',
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfigurationTime',
|
||||
conditions: [
|
||||
{
|
||||
value: '1',
|
||||
operator: ConditionOperator.OPERATOR_EQUALS,
|
||||
},
|
||||
],
|
||||
},
|
||||
sourceType: mockTimeSpec,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dataConnection: {
|
||||
edges: [],
|
||||
},
|
||||
})
|
||||
} as ExplorerOracleDataSourceFragment)
|
||||
);
|
||||
|
||||
expect(res.getByText('Time')).toBeInTheDocument();
|
||||
expect(res.getByText('=')).toBeInTheDocument();
|
||||
expect(res.getByTitle('1').textContent).toMatch(/1970/);
|
||||
expect(res.getByText('Filter')).toBeInTheDocument();
|
||||
// Avoids asserting on how the data is presented because it is very rudimentary
|
||||
});
|
||||
});
|
||||
|
||||
it('DataSourceSpecConfigurationTime handles empty conditions', () => {
|
||||
const res = render(
|
||||
renderComponent({
|
||||
dataSourceSpec: {
|
||||
spec: {
|
||||
id: 'irrelevant-test-data',
|
||||
createdAt: 'irrelevant-test-data',
|
||||
status: DataSourceSpecStatus.STATUS_ACTIVE,
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionInternal',
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfigurationTime',
|
||||
conditions: [undefined as unknown as Condition],
|
||||
},
|
||||
},
|
||||
},
|
||||
describe('getConditionsOrFilter', () => {
|
||||
it('Returns null if the type is undetermined (not DataSourceSpecConfiguration or DataSourceSpecConfigurationTime', () => {
|
||||
expect(getConditionsOrFilters({})).toBeNull();
|
||||
});
|
||||
|
||||
it('Returns the conditions object for time specs', () => {
|
||||
const mock: Filter = {
|
||||
__typename: 'DataSourceSpecConfigurationTime',
|
||||
conditions: [
|
||||
{
|
||||
__typename: 'Condition',
|
||||
value: '100',
|
||||
operator: ConditionOperator.OPERATOR_GREATER_THAN,
|
||||
},
|
||||
],
|
||||
};
|
||||
const res = getConditionsOrFilters(mock);
|
||||
// This ugly construction is due to lazy typing on getConditionsOrFilter
|
||||
if (!res || res.length !== 1 || !res[0] || 'key' in res[0]) {
|
||||
throw new Error(
|
||||
'getConditionsOrFilter did not return conditions on a time spec'
|
||||
);
|
||||
}
|
||||
|
||||
expect(res[0].__typename).toEqual('Condition');
|
||||
});
|
||||
|
||||
it('Returns the filters object for external specs', () => {
|
||||
const mock: Filter = {
|
||||
__typename: 'DataSourceSpecConfiguration',
|
||||
filters: [
|
||||
{
|
||||
__typename: 'Filter',
|
||||
key: {
|
||||
type: PropertyKeyType.TYPE_INTEGER,
|
||||
name: 'test',
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
__typename: 'Condition',
|
||||
value: 'test',
|
||||
operator: ConditionOperator.OPERATOR_EQUALS,
|
||||
},
|
||||
],
|
||||
},
|
||||
dataConnection: {
|
||||
edges: [],
|
||||
},
|
||||
})
|
||||
);
|
||||
],
|
||||
};
|
||||
|
||||
// This should never happen, but for coverage sake we test that it does this
|
||||
const ul = res.getByRole('list');
|
||||
expect(ul).toBeInTheDocument();
|
||||
expect(ul).toBeEmptyDOMElement();
|
||||
const res = getConditionsOrFilters(mock);
|
||||
// This ugly construction is due to lazy typing on getConditionsOrFilter
|
||||
if (!res || res.length !== 1 || !res[0] || 'value' in res[0]) {
|
||||
throw new Error(
|
||||
'getConditionsOrFilter did not return filters on a external spec'
|
||||
);
|
||||
}
|
||||
|
||||
expect(res[0].__typename).toEqual('Filter');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import filter from 'recursive-key-filter';
|
||||
import type { ExplorerOracleDataSourceFragment } from '../__generated__/Oracles';
|
||||
import { OracleSpecInternalTimeTrigger } from './oracle-spec/internal-time-trigger';
|
||||
import { OracleSpecCondition } from './oracle-spec/condition';
|
||||
import { getCharacterForOperator } from './oracle-spec/operator';
|
||||
|
||||
interface OracleFilterProps {
|
||||
data: ExplorerOracleDataSourceFragment;
|
||||
}
|
||||
|
||||
export type Filter =
|
||||
ExplorerOracleDataSourceFragment['dataSourceSpec']['spec']['data']['sourceType']['sourceType'];
|
||||
|
||||
/**
|
||||
* Given the main Filter view just uses a JSON dump view, this function
|
||||
* selects the correct filter to dump in to that view. Internal oracles
|
||||
* (i.e. the Time oracle) have conditions while external data sources
|
||||
* have filters
|
||||
*
|
||||
* @param s A data source
|
||||
* @returns Object an object containing conditions or filters
|
||||
*/
|
||||
export function getConditionsOrFilters(s: Filter) {
|
||||
if (s.__typename === 'DataSourceSpecConfiguration') {
|
||||
return s.filters;
|
||||
} else if (s.__typename === 'DataSourceSpecConfigurationTime') {
|
||||
return s.conditions;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the conditions that this oracle is using to filter
|
||||
* data sources, as a list.
|
||||
* data sources.
|
||||
*
|
||||
* Renders nothing if there is no data (which will frequently)
|
||||
* be the case) and if there is data, currently renders a simple
|
||||
@@ -21,53 +42,16 @@ export function OracleFilter({ data }: OracleFilterProps) {
|
||||
}
|
||||
|
||||
const s = data.dataSourceSpec.spec.data.sourceType.sourceType;
|
||||
if (s.__typename === 'DataSourceSpecConfigurationTime' && s.conditions) {
|
||||
return (
|
||||
<ul>
|
||||
{s.conditions
|
||||
.filter((c) => !!c)
|
||||
.map((c) => {
|
||||
if (!c) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<OracleSpecCondition key={c.value} data={c} type={s.__typename} />
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
} else if (
|
||||
s.__typename === 'DataSourceSpecConfigurationTimeTrigger' &&
|
||||
s.triggers
|
||||
) {
|
||||
return <OracleSpecInternalTimeTrigger data={s} />;
|
||||
} else if (
|
||||
s.__typename === 'EthCallSpec' ||
|
||||
s.__typename === 'DataSourceSpecConfiguration'
|
||||
) {
|
||||
if (s.filters !== null && s.filters && 'filters' in s) {
|
||||
return (
|
||||
<ul>
|
||||
{s.filters.map((f) => {
|
||||
const prop = <code title={f.key.type}>{f.key.name}</code>;
|
||||
const f = getConditionsOrFilters(s);
|
||||
|
||||
if (!f.conditions || f.conditions.length === 0) {
|
||||
return prop;
|
||||
} else {
|
||||
return f.conditions.map((c) => {
|
||||
return (
|
||||
<li key={`${prop}${c.value}`}>
|
||||
{prop} {getCharacterForOperator(c.operator)}{' '}
|
||||
<code>{c.value ? c.value : '-'}</code>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
if (!f) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return (
|
||||
<details>
|
||||
<summary>{t('Filter')}</summary>
|
||||
<SyntaxHighlighter data={filter(f, ['__typename'])} />
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ function renderComponent(id: string, mocks: MockedResponse[]) {
|
||||
<MemoryRouter>
|
||||
<MockedProvider mocks={mocks}>
|
||||
<Table>
|
||||
<tbody data-testid="wrapper">
|
||||
<tbody>
|
||||
<OracleMarkets id={id} />
|
||||
</tbody>
|
||||
</Table>
|
||||
@@ -23,7 +23,8 @@ function renderComponent(id: string, mocks: MockedResponse[]) {
|
||||
describe('Oracle Markets component', () => {
|
||||
it('Renders a row with the market ID initially', () => {
|
||||
const res = render(renderComponent('123', []));
|
||||
expect(res.getByTestId('wrapper')).toBeEmptyDOMElement();
|
||||
expect(res.getByText('Market')).toBeInTheDocument();
|
||||
expect(res.getByText('123')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders that this is a termination source for the right market', async () => {
|
||||
@@ -33,58 +34,21 @@ describe('Oracle Markets component', () => {
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
oracleSpecsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
dataConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
externalData: {
|
||||
data: {
|
||||
data: {
|
||||
name: '123',
|
||||
value: '456',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
dataSourceSpec: {
|
||||
spec: {
|
||||
id: '789',
|
||||
state: 'Active',
|
||||
status: 'Active',
|
||||
data: {
|
||||
sourceType: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: '123',
|
||||
state: 'Active',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
dataSourceSpecForSettlementData: {
|
||||
id: '456',
|
||||
status: 'Active',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '789',
|
||||
status: 'Active',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -95,18 +59,15 @@ describe('Oracle Markets component', () => {
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'abc',
|
||||
state: 'Active',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
dataSourceSpecForSettlementData: {
|
||||
id: 'def',
|
||||
status: 'Active',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: 'ghi',
|
||||
status: 'Active',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -130,58 +91,21 @@ describe('Oracle Markets component', () => {
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
oracleSpecsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
dataConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
externalData: {
|
||||
data: {
|
||||
data: {
|
||||
name: '123',
|
||||
value: '456',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
dataSourceSpec: {
|
||||
spec: {
|
||||
id: '789',
|
||||
state: 'Active',
|
||||
status: 'Active',
|
||||
data: {
|
||||
sourceType: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: '123',
|
||||
state: 'Active',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
dataSourceSpecForSettlementData: {
|
||||
id: '789',
|
||||
status: 'Active',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '123',
|
||||
status: 'Active',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -192,18 +116,15 @@ describe('Oracle Markets component', () => {
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'abc',
|
||||
state: 'Active',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
dataSourceSpecForSettlementData: {
|
||||
id: 'def',
|
||||
status: 'Active',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: 'ghi',
|
||||
status: 'Active',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getNodes } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { MarketLink } from '../../../components/links';
|
||||
import { TableRow, TableCell, TableHeader } from '../../../components/table';
|
||||
import type { ExplorerOracleForMarketsMarketFragment } from '../__generated__/OraclesForMarkets';
|
||||
@@ -9,7 +10,7 @@ interface OracleMarketsProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* Slightly misleading names, OracleMarkets lists the market (almost always singular)
|
||||
* Slightly misleadlingly names, OracleMarkets lists the market (almost always singular)
|
||||
* to which an oracle is attached. It also checks what it triggers, by checking on the
|
||||
* market whether it is attached to the dataSourceSpecForSettlementData or ..TradingTermination
|
||||
*/
|
||||
@@ -23,37 +24,36 @@ export function OracleMarkets({ id }: OracleMarketsProps) {
|
||||
);
|
||||
|
||||
if (markets) {
|
||||
const m = markets.filter((market) => {
|
||||
const p = market.tradableInstrument.instrument.product;
|
||||
const m = markets.find((m) => {
|
||||
const p = m.tradableInstrument.instrument.product;
|
||||
if (
|
||||
((p.__typename === 'Future' || p.__typename === 'Perpetual') &&
|
||||
p.dataSourceSpecForSettlementData.id === id) ||
|
||||
('dataSourceSpecForTradingTermination' in p &&
|
||||
p.dataSourceSpecForTradingTermination.id === id) ||
|
||||
(p.__typename === 'Perpetual' &&
|
||||
p.dataSourceSpecForSettlementSchedule.id === id)
|
||||
p?.dataSourceSpecForSettlementData?.id === id ||
|
||||
p?.dataSourceSpecForTradingTermination?.id === id
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (m && m.length > 0) {
|
||||
if (m && m.id) {
|
||||
return (
|
||||
<>
|
||||
{m.map((market) => (
|
||||
<TableRow modifier="bordered" key={`m-${market.id}`}>
|
||||
<TableHeader scope="row">{getLabel(id, market)}</TableHeader>
|
||||
<TableCell modifier="bordered" data-testid={`m-${market.id}`}>
|
||||
<MarketLink id={market.id} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</>
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">{getLabel(id, m)}</TableHeader>
|
||||
<TableCell modifier="bordered" data-testid={`m-${m.id}`}>
|
||||
<MarketLink id={m.id} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return (
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">{t('Market')}</TableHeader>
|
||||
<TableCell modifier="bordered">
|
||||
<span>{id}</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
export function getLabel(
|
||||
@@ -61,32 +61,8 @@ export function getLabel(
|
||||
m: ExplorerOracleForMarketsMarketFragment | null
|
||||
): string {
|
||||
const settlementId =
|
||||
((m?.tradableInstrument?.instrument?.product?.__typename === 'Future' ||
|
||||
m?.tradableInstrument?.instrument?.product?.__typename === 'Perpetual') &&
|
||||
m?.tradableInstrument?.instrument?.product
|
||||
?.dataSourceSpecForSettlementData?.id) ||
|
||||
null;
|
||||
m?.tradableInstrument?.instrument?.product?.dataSourceSpecForSettlementData
|
||||
?.id || null;
|
||||
|
||||
const terminationId =
|
||||
(m?.tradableInstrument?.instrument?.product?.__typename === 'Future' &&
|
||||
m?.tradableInstrument?.instrument?.product
|
||||
?.dataSourceSpecForTradingTermination?.id) ||
|
||||
null;
|
||||
|
||||
const settlementScheduleId =
|
||||
(m?.tradableInstrument?.instrument?.product?.__typename === 'Perpetual' &&
|
||||
m?.tradableInstrument?.instrument?.product
|
||||
?.dataSourceSpecForSettlementSchedule?.id) ||
|
||||
null;
|
||||
|
||||
switch (id) {
|
||||
case settlementId:
|
||||
return 'Settlement for';
|
||||
case terminationId:
|
||||
return 'Termination for';
|
||||
case settlementScheduleId:
|
||||
return 'Settlement schedule for';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
return id === settlementId ? 'Settlement for' : 'Termination for';
|
||||
}
|
||||
|
||||
@@ -67,9 +67,6 @@ export function OracleSigners({ sourceType }: OracleDetailsSignersProps) {
|
||||
if (sourceType.__typename !== 'DataSourceDefinitionExternal') {
|
||||
return null;
|
||||
}
|
||||
if (!('signers' in sourceType.sourceType)) {
|
||||
return null;
|
||||
}
|
||||
const signers = sourceType.sourceType.signers;
|
||||
|
||||
if (!signers || signers.length === 0) {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import fromUnixTime from 'date-fns/fromUnixTime';
|
||||
import { getCharacterForOperator } from './operator';
|
||||
import type { Condition } from '@vegaprotocol/types';
|
||||
|
||||
export interface OracleSpecCondition {
|
||||
data: Condition;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export function OracleSpecCondition({ data, type }: OracleSpecCondition) {
|
||||
const c = getCharacterForOperator(data.operator);
|
||||
const value =
|
||||
type === 'DataSourceSpecConfigurationTime' && data.value
|
||||
? fromUnixTime(parseInt(data.value)).toLocaleString()
|
||||
: data.value;
|
||||
const typeLabel =
|
||||
type === 'DataSourceSpecConfigurationTime' ? (
|
||||
<span>{t('Time')}</span>
|
||||
) : (
|
||||
type
|
||||
);
|
||||
|
||||
return (
|
||||
<li key={`${typeLabel}${c}${value}`}>
|
||||
{typeLabel} {c}{' '}
|
||||
{value && (
|
||||
<span
|
||||
title={data.value || value}
|
||||
className="underline decoration-dotted"
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { DataSourceSpecConfigurationTimeTrigger } from '@vegaprotocol/types';
|
||||
import secondsToMinutes from 'date-fns/secondsToMinutes';
|
||||
import fromUnixTime from 'date-fns/fromUnixTime';
|
||||
|
||||
export interface OracleSpecInternalTimeTriggerProps {
|
||||
data: DataSourceSpecConfigurationTimeTrigger;
|
||||
}
|
||||
|
||||
export function OracleSpecInternalTimeTrigger({
|
||||
data,
|
||||
}: OracleSpecInternalTimeTriggerProps) {
|
||||
return (
|
||||
<div>
|
||||
<span>{t('Time')}</span>,
|
||||
{data.triggers.map((tr) => {
|
||||
return (
|
||||
<span>
|
||||
{tr?.initial ? (
|
||||
<span title={`${tr.initial}`}>
|
||||
<strong>{t('starting at')}</strong>{' '}
|
||||
<em className="not-italic underline decoration-dotted">
|
||||
{fromUnixTime(tr.initial).toLocaleString()}
|
||||
</em>
|
||||
</span>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{tr?.every ? (
|
||||
<span title={`${tr.every} ${t('seconds')}`}>
|
||||
, <strong>{t('every')}</strong>{' '}
|
||||
<em className="not-italic underline decoration-dotted">
|
||||
{secondsToMinutes(tr.every)} {t('minutes')}
|
||||
</em>{' '}
|
||||
</span>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { ConditionOperator } from '@vegaprotocol/types';
|
||||
|
||||
export function getCharacterForOperator(
|
||||
operator: ConditionOperator
|
||||
): React.ReactElement {
|
||||
switch (operator) {
|
||||
case 'OPERATOR_EQUALS':
|
||||
return <span title={t('equals')}>=</span>;
|
||||
case 'OPERATOR_GREATER_THAN':
|
||||
return <span title={t('greater than')}>></span>;
|
||||
case 'OPERATOR_GREATER_THAN_OR_EQUAL':
|
||||
return <span title={t('greater than or equal')}>≥</span>;
|
||||
case 'OPERATOR_LESS_THAN':
|
||||
return <span title={t('less than')}><</span>;
|
||||
case 'OPERATOR_LESS_THAN_OR_EQUAL':
|
||||
return <span title={t('less than or equal')}>≤</span>;
|
||||
}
|
||||
|
||||
return <span>{operator}</span>;
|
||||
}
|
||||
@@ -14,9 +14,7 @@ import { OracleFilter } from './oracle-filter';
|
||||
import { OracleDetailsType } from './oracle-details-type';
|
||||
import { OracleMarkets } from './oracle-markets';
|
||||
import { OracleSigners } from './oracle-signers';
|
||||
import { OracleEthSource } from './oracle-eth-source';
|
||||
import Hash from '../../../components/links/hash';
|
||||
import { getStatusString } from '../../../components/links/oracle-link/oracle-link';
|
||||
import OracleLink from '../../../components/links/oracle-link/oracle-link';
|
||||
|
||||
export type SourceType =
|
||||
ExplorerOracleDataSourceFragment['dataSourceSpec']['spec']['data']['sourceType'];
|
||||
@@ -40,8 +38,10 @@ export const OracleDetails = ({
|
||||
id,
|
||||
dataSource,
|
||||
dataConnection,
|
||||
showBroadcasts = false,
|
||||
}: OracleDetailsProps) => {
|
||||
const sourceType = dataSource.dataSourceSpec.spec.data.sourceType;
|
||||
const reportsCount: number = dataConnection.edges?.length || 0;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -49,27 +49,23 @@ export const OracleDetails = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">{t('ID')}</TableHeader>
|
||||
<TableCell modifier="bordered">
|
||||
<Hash text={id} />
|
||||
<OracleLink id={id} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<OracleDetailsType sourceType={sourceType} />
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">{t('Status')}</TableHeader>
|
||||
<TableCell modifier="bordered">
|
||||
{getStatusString(dataSource.dataSourceSpec.spec.status)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<OracleSigners sourceType={sourceType} />
|
||||
<OracleEthSource sourceType={sourceType} />
|
||||
<OracleMarkets id={id} />
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">{t('Filter')}</TableHeader>
|
||||
<TableHeader scope="row">{t('Matched data')}</TableHeader>
|
||||
<TableCell modifier="bordered">
|
||||
<OracleFilter data={dataSource} />
|
||||
{showBroadcasts ? reportsCount : reportsCount > 0 ? '✅' : '❌'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
{dataConnection ? <OracleData data={dataConnection} /> : null}
|
||||
<OracleFilter data={dataSource} />
|
||||
{showBroadcasts && dataConnection ? (
|
||||
<OracleData data={dataConnection} />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
import compact from 'lodash/compact';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { RouteTitle } from '../../../components/route-title';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useExplorerOracleSpecsQuery } from '../__generated__/Oracles';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
import { OracleDetails } from '../components/oracle';
|
||||
import { useScrollToLocation } from '../../../hooks/scroll-to-location';
|
||||
import { useExplorerOracleFormMarketsQuery } from '../__generated__/OraclesForMarkets';
|
||||
import { MarketLink } from '../../../components/links';
|
||||
import { OracleLink } from '../../../components/links/oracle-link/oracle-link';
|
||||
import { useState } from 'react';
|
||||
import { MarketStateMapping } from '@vegaprotocol/types';
|
||||
import type { MarketState } from '@vegaprotocol/types';
|
||||
|
||||
const cellSpacing = 'px-3';
|
||||
import filter from 'recursive-key-filter';
|
||||
|
||||
const Oracles = () => {
|
||||
const { data, loading, error } = useExplorerOracleFormMarketsQuery({
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
const { data, loading, error } = useExplorerOracleSpecsQuery();
|
||||
|
||||
useDocumentTitle(['Oracles']);
|
||||
useScrollToLocation();
|
||||
|
||||
const [hoveredOracle, setHoveredOracle] = useState('');
|
||||
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
|
||||
@@ -38,148 +28,36 @@ const Oracles = () => {
|
||||
data.oracleSpecsConnection.edges?.length === 0
|
||||
}
|
||||
>
|
||||
<table className="text-left">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={cellSpacing}>Market</th>
|
||||
<th className={cellSpacing}>Type</th>
|
||||
<th className={cellSpacing}>State</th>
|
||||
<th className={cellSpacing}>Settlement</th>
|
||||
<th className={cellSpacing}>Termination</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.marketsConnection?.edges
|
||||
? data.marketsConnection.edges.map((o) => {
|
||||
let hasSeenOracleReports = false;
|
||||
let settlementOracle = '-';
|
||||
let settlementOracleStatus = '-';
|
||||
let terminationOracle = '-';
|
||||
let terminationOracleStatus = '-';
|
||||
{data?.oracleSpecsConnection?.edges
|
||||
? data.oracleSpecsConnection.edges.map((o) => {
|
||||
const id = o?.node.dataSourceSpec.spec.id;
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const id = o?.node.id;
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
const dataConnection = o?.node.dataConnection;
|
||||
|
||||
if (
|
||||
o.node.tradableInstrument.instrument.product.__typename ===
|
||||
'Future'
|
||||
) {
|
||||
settlementOracle =
|
||||
o.node.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementData.id;
|
||||
terminationOracle =
|
||||
o.node.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForTradingTermination.id;
|
||||
settlementOracleStatus =
|
||||
o.node.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementData.status;
|
||||
terminationOracleStatus =
|
||||
o.node.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForTradingTermination.status;
|
||||
} else if (
|
||||
o.node.tradableInstrument.instrument.product.__typename ===
|
||||
'Perpetual'
|
||||
) {
|
||||
settlementOracle =
|
||||
o.node.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementData.id;
|
||||
terminationOracle =
|
||||
o.node.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementSchedule.id;
|
||||
settlementOracleStatus =
|
||||
o.node.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementData.status;
|
||||
terminationOracleStatus =
|
||||
o.node.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementSchedule.status;
|
||||
}
|
||||
const oracleInformationUnfiltered =
|
||||
data?.oracleSpecsConnection?.edges?.map((e) =>
|
||||
e && e.node ? e.node : undefined
|
||||
) || [];
|
||||
|
||||
const oracleInformation = compact(oracleInformationUnfiltered)
|
||||
.filter(
|
||||
(o) =>
|
||||
o.dataConnection.edges &&
|
||||
o.dataConnection.edges.length > 0 &&
|
||||
(o.dataSourceSpec.spec.id === settlementOracle ||
|
||||
o.dataSourceSpec.spec.id === terminationOracle)
|
||||
)
|
||||
.at(0);
|
||||
if (oracleInformation) {
|
||||
hasSeenOracleReports = true;
|
||||
}
|
||||
|
||||
const oracleList = `${settlementOracle} ${terminationOracle}`;
|
||||
|
||||
return (
|
||||
<tr
|
||||
id={id}
|
||||
key={id}
|
||||
className={
|
||||
hoveredOracle.length > 0 &&
|
||||
oracleList.indexOf(hoveredOracle) > -1
|
||||
? 'bg-gray-100 dark:bg-gray-800'
|
||||
: ''
|
||||
}
|
||||
data-testid="oracle-details"
|
||||
data-oracles={oracleList}
|
||||
>
|
||||
<td className={cellSpacing}>
|
||||
<MarketLink id={id} />
|
||||
</td>
|
||||
<td className={cellSpacing}>
|
||||
{
|
||||
o.node.tradableInstrument.instrument.product
|
||||
.__typename
|
||||
}
|
||||
</td>
|
||||
<td className={cellSpacing}>
|
||||
{MarketStateMapping[o.node.state as MarketState]}
|
||||
</td>
|
||||
<td
|
||||
className={
|
||||
hoveredOracle.length > 0 &&
|
||||
hoveredOracle === settlementOracle
|
||||
? `indent-1 ${cellSpacing}`
|
||||
: cellSpacing
|
||||
}
|
||||
>
|
||||
<OracleLink
|
||||
id={settlementOracle}
|
||||
status={settlementOracleStatus}
|
||||
hasSeenOracleReports={hasSeenOracleReports}
|
||||
onMouseOver={() => setHoveredOracle(settlementOracle)}
|
||||
onMouseOut={() => setHoveredOracle('')}
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
className={
|
||||
hoveredOracle.length > 0 &&
|
||||
hoveredOracle === terminationOracle
|
||||
? `indent-1 ${cellSpacing}`
|
||||
: cellSpacing
|
||||
}
|
||||
>
|
||||
<OracleLink
|
||||
id={terminationOracle}
|
||||
status={terminationOracleStatus}
|
||||
hasSeenOracleReports={hasSeenOracleReports}
|
||||
onMouseOver={() =>
|
||||
setHoveredOracle(terminationOracle)
|
||||
}
|
||||
onMouseOut={() => setHoveredOracle('')}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</tbody>
|
||||
</table>
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
key={id}
|
||||
className="mb-10"
|
||||
data-testid="oracle-details"
|
||||
>
|
||||
<OracleDetails
|
||||
id={id}
|
||||
dataSource={o?.node}
|
||||
dataConnection={dataConnection}
|
||||
showBroadcasts={false}
|
||||
/>
|
||||
<details>
|
||||
<summary className="pointer">JSON</summary>
|
||||
<SyntaxHighlighter data={filter(o, ['__typename'])} />
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</AsyncRenderer>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -40,9 +40,10 @@ export const Oracle = () => {
|
||||
id={id || ''}
|
||||
dataSource={data?.oracleSpec}
|
||||
dataConnection={data?.oracleSpec.dataConnection}
|
||||
showBroadcasts={true}
|
||||
/>
|
||||
<details className="mt-5 cursor-pointer">
|
||||
<summary>JSON</summary>
|
||||
<details>
|
||||
<summary className="pointer">JSON</summary>
|
||||
<SyntaxHighlighter data={filter(data, ['__typename'])} />
|
||||
</details>
|
||||
</div>
|
||||
|
||||
@@ -23,9 +23,6 @@ fragment ExplorerPartyAssetsAccounts on AccountBalance {
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerPartyAssetsAccountsFragment = { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } | { __typename?: 'Perpetual', quoteName: string } | { __typename?: 'Spot' } } } } | null };
|
||||
export type ExplorerPartyAssetsAccountsFragment = { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null };
|
||||
|
||||
export type ExplorerPartyAssetsQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string, linkings: { __typename?: 'StakesConnection', edges?: Array<{ __typename?: 'StakeLinkingEdge', node: { __typename?: 'StakeLinking', type: Types.StakeLinkingType, status: Types.StakeLinkingStatus, amount: string } } | null> | null } }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } | { __typename?: 'Perpetual', quoteName: string } | { __typename?: 'Spot' } } } } | null } } | null> | null } | null } }> } | null };
|
||||
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string, linkings: { __typename?: 'StakesConnection', edges?: Array<{ __typename?: 'StakeLinkingEdge', node: { __typename?: 'StakeLinking', type: Types.StakeLinkingType, status: Types.StakeLinkingStatus, amount: string } } | null> | null } }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null } } | null> | null } | null } }> } | null };
|
||||
|
||||
export const ExplorerPartyAssetsAccountsFragmentDoc = gql`
|
||||
fragment ExplorerPartyAssetsAccounts on AccountBalance {
|
||||
@@ -38,9 +38,6 @@ export const ExplorerPartyAssetsAccountsFragmentDoc = gql`
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
... on Perpetual {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import { BackgroundVideo } from '@vegaprotocol/ui-toolkit';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const RestrictedPage = () => {
|
||||
const errorTitle = '451 Unavailable';
|
||||
const errorMessage =
|
||||
'Due to uncertainty about the legal and regulatory status of the content hosted on this site, it is not available to visitors in your jurisdiction.';
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BackgroundVideo className="brightness-50" />
|
||||
<div
|
||||
className={classNames(
|
||||
'max-w-[620px] p-2 mt-[10vh]',
|
||||
'mx-auto my-0',
|
||||
'antialiased text-white',
|
||||
'overflow-hidden relative',
|
||||
'flex flex-col gap-2'
|
||||
)}
|
||||
>
|
||||
<div className="flex gap-4">
|
||||
<div>{GHOST}</div>
|
||||
<h1 className="text-[2.7rem] font-alpha calt break-words uppercase">
|
||||
{errorTitle}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="text-sm mt-10 overflow-auto font-mono">
|
||||
{errorMessage}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const GHOST = (
|
||||
<svg
|
||||
width="56"
|
||||
height="85"
|
||||
viewBox="0 0 150 234"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.43 93.6821L0 104.097V172.435H5.13793V106.2L14.0779 97.3247L10.43 93.6821Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path d="M105.328 18.521H7.70703V172.435H105.328V18.521Z" fill="white" />
|
||||
<path d="M38.5364 64.6953H33.3984V69.8258H38.5364V64.6953Z" fill="black" />
|
||||
<path d="M43.6731 69.8257H38.5352V74.9561H43.6731V69.8257Z" fill="black" />
|
||||
<path d="M48.8098 74.9561H43.6719V80.0865H48.8098V74.9561Z" fill="black" />
|
||||
<path d="M38.5364 74.9561H33.3984V80.0865H38.5364V74.9561Z" fill="black" />
|
||||
<path d="M48.8098 64.6953H43.6719V69.8258H48.8098V64.6953Z" fill="black" />
|
||||
<path d="M69.3606 64.6953H64.2227V69.8258H69.3606V64.6953Z" fill="black" />
|
||||
<path d="M74.5012 69.8257H69.3633V74.9561H74.5012V69.8257Z" fill="black" />
|
||||
<path d="M79.6379 74.9561H74.5V80.0865H79.6379V74.9561Z" fill="black" />
|
||||
<path d="M69.3606 74.9561H64.2227V80.0865H69.3606V74.9561Z" fill="black" />
|
||||
<path d="M79.6379 64.6953H74.5V69.8258H79.6379V64.6953Z" fill="black" />
|
||||
<path d="M79.6398 90.3477H33.3984V95.4781H79.6398V90.3477Z" fill="black" />
|
||||
<path d="M48.8098 172.435H43.6719V234H48.8098V172.435Z" fill="black" />
|
||||
<path d="M69.3606 172.435H64.2227V234H69.3606V172.435Z" fill="black" />
|
||||
<path
|
||||
d="M129.87 0.00408026L92.0273 14.3682L111.195 64.7203L149.038 50.3562L129.87 0.00408026Z"
|
||||
fill="#FF0081"
|
||||
/>
|
||||
<path
|
||||
d="M108.565 21.7836L103.762 23.6064L105.587 28.4024L110.39 26.5795L108.565 21.7836Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M115.189 24.7596L110.387 26.583L112.213 31.3785L117.015 29.5551L115.189 24.7596Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M121.826 27.7348L117.023 29.5576L118.849 34.3536L123.652 32.5307L121.826 27.7348Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M128.447 30.7059L123.645 32.5293L125.471 37.3247L130.273 35.5014L128.447 30.7059Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M135.084 33.6854L130.281 35.5083L132.107 40.3043L136.91 38.4814L135.084 33.6854Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M124.799 21.1156L119.996 22.939L121.822 27.7344L126.625 25.911L124.799 21.1156Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M127.775 14.492L122.973 16.3154L124.799 21.1109L129.601 19.2875L127.775 14.492Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M118.838 34.3499L114.035 36.1733L115.861 40.9688L120.664 39.1454L118.838 34.3499Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M115.857 40.9691L111.055 42.7925L112.881 47.5879L117.683 45.7645L115.857 40.9691Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M129.938 52.3822L139.906 91.5276L100.703 101.429L101.988 106.406L146.174 95.2215L134.922 51.0996L129.938 52.3822Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default RestrictedPage;
|
||||
@@ -1,6 +1,5 @@
|
||||
export const Routes = {
|
||||
HOME: '/',
|
||||
RESTRICTED: '/restricted',
|
||||
TX: 'txs',
|
||||
BLOCKS: 'blocks',
|
||||
PARTIES: 'parties',
|
||||
|
||||
@@ -30,7 +30,6 @@ import { remove0x } from '@vegaprotocol/utils';
|
||||
import { PartyAccountsByAsset } from './parties/id/accounts';
|
||||
import { Disclaimer } from './pages/disclaimer';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import RestrictedPage from './restricted';
|
||||
|
||||
export type Navigable = {
|
||||
path: string;
|
||||
@@ -357,14 +356,6 @@ export const routerConfig: Route[] = [
|
||||
...validators,
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.RESTRICTED,
|
||||
element: <RestrictedPage />,
|
||||
handle: {
|
||||
name: t('Restricted'),
|
||||
text: t('Restricted'),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const router = createBrowserRouter(routerConfig);
|
||||
|
||||
@@ -14,10 +14,6 @@ export interface BlockExplorerTransactionResult {
|
||||
value: string;
|
||||
};
|
||||
error?: string;
|
||||
// These aren't strictly optional but are new in 0.73.0 so we need to make them optional
|
||||
createdAt?: string;
|
||||
version?: string;
|
||||
pow?: string;
|
||||
}
|
||||
|
||||
export interface BlockExplorerTransactions {
|
||||
|
||||
@@ -139,7 +139,7 @@ export const ValidatorsPage = () => {
|
||||
const [vegaDialog, setVegaDialog] = useState<boolean>(false);
|
||||
const [tmDialog, setTmDialog] = useState<boolean>(false);
|
||||
|
||||
const tokenLink = useLinks(DApp.Governance);
|
||||
const tokenLink = useLinks(DApp.Token);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
+251
-664
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user