Compare commits

..
Author SHA1 Message Date
Dexter 6c5f148bb5 ci: publish on tag only 2022-07-13 13:00:03 +01:00
Dexter 64d593c63f ci: rename 2022-07-11 12:20:19 +01:00
Dexter 80990aac32 ci: rename pipelines 2022-07-11 12:17:54 +01:00
Dexter 63f2e5b4fb ci: add missing deps 2022-07-11 12:14:29 +01:00
Dexter 96749a9af6 ci: publish 2022-07-11 12:01:34 +01:00
Dexter ddc8236c41 ci: build for both platforms 2022-07-08 15:40:52 +01:00
Dexter 32adf99ee9 fix: incorrect asset path 2022-07-08 15:40:52 +01:00
Dexter 9fec3d837b chore: correct path 2022-07-08 15:40:52 +01:00
Dexter 4dade0ab53 feat: separate dockerfiles for CRA based apps and next apps 2022-07-08 15:40:52 +01:00
Dexter 4c1c1b3f51 fix: use correct environement variable names 2022-07-08 15:40:52 +01:00
Dexter 415d520a7b ci: publish trading and token containers 2022-07-08 15:40:52 +01:00
Dexter ae75a85f67 ci: add push true to step 2022-07-08 15:40:52 +01:00
Dexter 4903bdbcd5 ci: only build amd as PoC 2022-07-08 15:40:52 +01:00
Dexter 9d0daf9630 ci: fix build platform 2022-07-08 15:40:52 +01:00
Dexter abef4785cf ci: use quotes 2022-07-08 15:40:52 +01:00
Dexter 44c1881dae ci: correct output syntax 2022-07-08 15:40:52 +01:00
Dexter 231ad4ff91 ci: correct tags 2022-07-08 15:40:52 +01:00
Dexter 6654704c36 ci: run all steps all the time 2022-07-08 15:40:52 +01:00
Dexter 7054367ba0 ci: remove workflows that take a while to run to not waste build mins 2022-07-08 15:40:52 +01:00
Dexter 2087aa7f41 ci: pass app version 2022-07-08 15:40:52 +01:00
Dexter a323dcee5a chore: first pass at config file 2022-07-08 15:40:52 +01:00
1420 changed files with 30911 additions and 76877 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
apps/**/dist/*
apps/**/node_modules/*
dist/*
node_modules/*
tmp/*
.dockerignore
dockerfiles
Dockerfile
@@ -1,155 +0,0 @@
name: Capsule tests -- manual trigger
# This workflow runs the frontend tests against chosen branch
on:
workflow_dispatch:
inputs:
project:
description: 'Project'
required: true
type: choice
options:
- explorer-e2e
- console-lite-e2e
- stats-e2e
- token-e2e
- trading-e2e
includeFlows:
description: 'Include long flows (if any)?'
required: true
type: boolean
default: true
jobs:
manual:
name: Run capsule tests -- manual trigger
runs-on: ubuntu-latest
env:
GO111MODULE: 'on'
GOPROXY: ${{ secrets.GO_PROXY }}
steps:
#######
## Setup langs
#######
- name: Set up Go
uses: actions/setup-go@v3
id: go
with:
go-version: 1.19
- name: Set up Node 16
uses: actions/setup-node@v2
id: npm
with:
node-version: 16
#######
## Checkout capsule
#######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
path: './frontend-monorepo'
# Checkout capsule to build local network
- name: Checkout capsule
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegacapsule
ref: main
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './capsule'
#######
## Build binaries
#######
- name: Build capsule
run: go install
working-directory: capsule
- name: Set GOBIN
run: echo GOBIN=$(go env GOPATH)/bin >> $GITHUB_ENV
- name: Checkout Vega
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: v0.54.0
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
- name: Install Vega binaries
run: go install -v ./cmd/vega
working-directory: vega
- name: Install Vega wallet binaries
run: go install ./cmd/vegawallet
working-directory: vega
- name: Install date-node binaries
run: go install ./cmd/data-node
working-directory: vega
######
## Start capsule
######
- name: Login to docker
run: echo -n ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
- name: Start nomad
run: vegacapsule nomad &
- name: Bootstrap network
run: vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
working-directory: capsule
######
## Setup a Vega wallet for our user
######
- name: Create passphrase
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
- name: Create recovery
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
- name: Initialize wallet
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
- name: Import wallet
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Create public key 2
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Start service
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
######
## Run some tests
######
- name: Install root dependencies
run: yarn install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: yarn nx run ${{ github.event.inputs.project }}:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
CYPRESS_INCLUDE_FLOWS: ${{ github.event.inputs.includeFlows }}
######
## Upload logs
######
- name: Logs
if: ${{ always() }}
run: vegacapsule network logs > vega-capsule-logs.txt
- uses: actions/upload-artifact@v2
with:
name: logs
path: ./vega-capsule-logs.txt
@@ -1,4 +1,4 @@
name: Capsule tests -- night run
name: Capsule tests -- nightly
# This workflow runs the frontend tests against latest develop of the core to preempt breaking changes
@@ -12,6 +12,7 @@ jobs:
runs-on: ubuntu-latest
env:
GO111MODULE: 'on'
GOPROXY: ${{ secrets.GO_PROXY }}
steps:
#######
## Setup langs
@@ -20,7 +21,7 @@ jobs:
uses: actions/setup-go@v3
id: go
with:
go-version: 1.19
go-version: 1.18
- name: Set up Node 16
uses: actions/setup-node@v2
id: npm
@@ -28,16 +29,48 @@ jobs:
node-version: 16
#######
## Checkout capsule
## Checkout all the things
#######
# Checkout front ends
- name: Checkout frontend mono repo
# Checkout repos required to build vega capsule
- name: Checkout vega
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
path: './frontend-monorepo'
repository: vegaprotocol/vega
ref: master
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
- name: Checkout data node
uses: actions/checkout@v2
with:
repository: vegaprotocol/data-node
ref: main
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './data-node'
- name: Checkout protos
uses: actions/checkout@v2
with:
repository: vegaprotocol/protos
ref: master
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './protos'
- name: Checkout vegatools
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegatools
ref: main
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vegatools'
- name: Checkout vega wallet
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegawallet
ref: master
path: './vegawallet'
# Checkout capsule to build local network
- name: Checkout capsule
@@ -55,40 +88,28 @@ jobs:
run: go install
working-directory: capsule
- name: Set GOBIN
run: echo GOBIN=$(go env GOPATH)/bin >> $GITHUB_ENV
- name: Checkout Vega
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: v0.54.0
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
- name: Install Vega binaries
run: go install -v ./cmd/vega
- name: Build vega
run: go install ./cmd/vega/
working-directory: vega
- name: Install Vega wallet binaries
run: go install ./cmd/vegawallet
working-directory: vega
- name: Build vegawallet
run: go install
working-directory: vegawallet
- name: Install date-node binaries
run: go install ./cmd/data-node
working-directory: vega
- name: Build data-node
run: go install ./cmd/data-node/
working-directory: data-node
######
## Start capsule
######
- name: Login to docker
run: echo -n ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
- name: Start nomad
run: vegacapsule nomad &
- name: Bootstrap network
run: vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
run: vegacapsule network bootstrap --config-path=./net_confs/config.hcl
working-directory: capsule
######
@@ -112,19 +133,23 @@ jobs:
## Run some tests
######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Install root dependencies
run: yarn install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: yarn nx run-many --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
working-directory: frontend-monorepo
run: yarn nx run-many --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
CYPRESS_NIGHTLY_RUN: true
CYPRESS_INCLUDE_FLOWS: true
######
## Upload logs
-153
View File
@@ -1,153 +0,0 @@
name: Capsule tests
on:
push:
branches:
- master
- develop
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review
jobs:
pr:
name: Run capsule tests - PR
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == false }}
env:
GO111MODULE: 'on'
steps:
#######
## Setup langs
#######
- name: Set up Go
uses: actions/setup-go@v3
id: go
with:
go-version: 1.19
- name: Set up Node 16
uses: actions/setup-node@v2
id: npm
with:
node-version: 16
#######
## Checkout capsule
#######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
path: './frontend-monorepo'
# Checkout capsule to build local network
- name: Checkout capsule
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegacapsule
ref: main
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './capsule'
#######
## Build binaries
#######
- name: Build capsule
run: go install
working-directory: capsule
- name: Set GOBIN
run: echo GOBIN=$(go env GOPATH)/bin >> $GITHUB_ENV
- name: Checkout Vega
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: v0.54.0
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
- name: Install Vega binaries
run: go install -v ./cmd/vega
working-directory: vega
- name: Install Vega wallet binaries
run: go install ./cmd/vegawallet
working-directory: vega
- name: Install date-node binaries
run: go install ./cmd/data-node
working-directory: vega
######
## Start capsule
######
- name: Login to docker
run: echo -n ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
- name: Start nomad
run: vegacapsule nomad &
- name: Bootstrap network
run: vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
working-directory: capsule
######
## Setup a Vega wallet for our user
######
- name: Create passphrase
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
- name: Create recovery
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
- name: Initialize wallet
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
- name: Import wallet
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Create public key 2
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Start service
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
######
## Run some tests
######
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
working-directory: frontend-monorepo
main-branch-name: master
- name: Install root dependencies
run: yarn install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
CYPRESS_INCLUDE_FLOWS: false
######
## Upload logs
######
- name: Logs
if: ${{ always() }}
run: vegacapsule network logs > vega-capsule-logs.txt
- uses: actions/upload-artifact@v2
if: ${{ always() }}
with:
name: logs
path: ./vega-capsule-logs.txt
@@ -0,0 +1,60 @@
name: Publish NextJs containers
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+'
jobs:
master:
strategy:
matrix:
build-platform: ['linux/amd64']
app: ['trading']
name: Build the ${{ inputs.image_name }} image
runs-on: ubuntu-latest
steps:
- name: Set output
id: vars
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
- name: Check output
env:
RELEASE_VERSION: ${{ steps.vars.outputs.tag }}
run: |
echo $RELEASE_VERSION
echo ${{ steps.vars.outputs.tag }}
- uses: actions/checkout@v3
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Collect images data
id: tags
run: |
echo '::set-output name=current::vegaprotocol/${{ matrix.app }}:v${{ steps.vars.outputs.tag }}'
echo '::set-output name=latest::vegaprotocol/${{ matrix.app }}:latest'
- name: Echo tags
run: |
echo ${{ steps.tags.outputs.latest }}
echo ${{ steps.tags.outputs.current }}
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker build
uses: docker/build-push-action@v3
with:
context: '.'
file: './Dockerfile.next'
build-args: 'APP=${{ matrix.app }}'
platforms: ${{ matrix.build-platform }}
tags: ${{ steps.tags.outputs.latest }},${{ steps.tags.outputs.current }}
push: true
@@ -0,0 +1,61 @@
name: Publish CRA containers
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+'
jobs:
master:
strategy:
matrix:
build-platform: ['linux/amd64']
app: ['explorer', 'token']
name: Build the ${{ inputs.image_name }} image
runs-on: ubuntu-latest
steps:
- name: Set output
id: vars
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
- name: Check output
env:
RELEASE_VERSION: ${{ steps.vars.outputs.tag }}
run: |
echo $RELEASE_VERSION
echo ${{ steps.vars.outputs.tag }}
- uses: actions/checkout@v3
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
# TODO take version from tag
- name: Collect images data
id: tags
run: |
echo '::set-output name=current::vegaprotocol/${{ matrix.app }}:v${{ steps.vars.outputs.tag }}'
echo '::set-output name=latest::vegaprotocol/${{ matrix.app }}:latest'
- name: Echo tags
run: |
echo ${{ steps.tags.outputs.latest }}
echo ${{ steps.tags.outputs.current }}
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker build
uses: docker/build-push-action@v3
with:
context: '.'
file: './Dockerfile.cra'
build-args: 'APP=${{ matrix.app }}'
platforms: ${{ matrix.build-platform }}
tags: ${{ steps.tags.outputs.latest }},${{ steps.tags.outputs.current }}
push: true
@@ -1,126 +0,0 @@
name: Publish docker containers
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+'
workflow_dispatch:
inputs:
publish:
description: 'Publish tag to Docker Hub & GitHub Registry'
required: false
type: boolean
default: false
tag:
description: 'Git Tag to build and publish'
required: false
type: string
default: ''
apps:
description: 'Applications to build and publish'
required: false
type: choice
options:
- '["explorer", "token", "trading"]'
- '["explorer"]'
- '["token"]'
- '["trading"]'
archs:
description: 'Architecture to build and publish'
required: false
type: choice
options:
- linux/amd64, linux/arm64
- linux/amd64
- linux/arm64
jobs:
master:
strategy:
fail-fast: false
matrix:
app: ${{ fromJson(inputs.apps || '["explorer", "token", "trading"]') }}
name: Build the ${{ matrix.app }} image
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
with:
ref: ${{ inputs.tag }}
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
- name: Available platforms
run: echo ${{ steps.qemu.outputs.platforms }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
if: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Determine Docker Image tag
id: tags
run: |
hash=$(git rev-parse HEAD|cut -b1-8)
versionTag=${{ inputs.tag || startsWith(github.ref, 'refs/tags/') && github.ref_name || '${hash}' }}
echo ::set-output name=version::${versionTag}
echo ::set-output name=npmVersion::$(cat dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }} | grep FROM | head -n 1 | awk '{print $2}' | cut -d ':' -f 2 | cut -d '-' -f 1 )
- name: Print config
run: |
git rev-parse --verify HEAD
git status
echo "inputs.tag=${{ inputs.tag }}"
echo "inputs.publish=${{ inputs.publish }}"
echo "inputs.apps=${{ inputs.apps }}"
echo "inputs.archs=${{ inputs.archs }}"
echo "steps.tags.outputs.version=${{ steps.tags.outputs.version }}"
- uses: actions/setup-node@v3
with:
node-version: ${{ steps.tags.outputs.npmVersion }}
- name: Build frontend dists
run: |
yarn --verbose --pure-lockfile
yarn nx ${{ matrix.app =='trading' && 'export' || 'build' }} ${{ matrix.app }} --pure-lockfile
- name: Build and export to local Docker
uses: docker/build-push-action@v3
with:
context: .
push: false
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
build-args: APP=${{ matrix.app }}
load: true
tags: vegaprotocol/${{ matrix.app }}:local
- name: Sanity check docker image
run: |
docker run --rm vegaprotocol/${{ matrix.app }}:local cat .env
docker run --rm vegaprotocol/${{ matrix.app }}:local ls -lah
- name: Build and push to DockerHub
id: docker_build
uses: docker/build-push-action@v3
with:
context: .
push: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
build-args: APP=${{ matrix.app }}
platforms: ${{ inputs.archs || 'linux/amd64, linux/arm64' }}
tags: |
vegaprotocol/${{ matrix.app }}:latest
vegaprotocol/${{ matrix.app }}:${{ steps.tags.outputs.version }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
-73
View File
@@ -1,73 +0,0 @@
name: Unit tests & build
on:
push:
branches:
- master
- develop
pull_request:
jobs:
master:
name: Test and lint - main
runs-on: ubuntu-latest
permissions:
contents: 'read'
actions: 'read'
if: ${{ github.event_name != 'pull_request' }}
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: master
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v3
with:
node-version: 16.14.0
- name: Install root dependencies
run: yarn install
- name: Check formatting
run: yarn nx format:check
- name: Lint affected
run: yarn nx affected:lint --max-warnings=0
- name: Test affected
run: yarn nx affected:test
- name: Build affected
run: yarn nx affected:build
pr:
name: Test and lint - PR
runs-on: ubuntu-latest
permissions:
contents: 'read'
actions: 'read'
if: ${{ github.event_name == 'pull_request' }}
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: master
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v3
with:
node-version: 16.14.0
- name: Install root dependencies
run: yarn install
- name: Check formatting
run: yarn nx format:check
- name: Lint affected
run: yarn nx affected:lint --max-warnings=0
- name: Test affected
run: yarn nx affected:test
- name: Build affected
run: yarn nx affected:build
+1 -4
View File
@@ -21,7 +21,7 @@
# IDE - VSCode
.vscode/*
.vscode/settings.json
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
@@ -43,6 +43,3 @@ Thumbs.db
.local.env
.env.local
cypress.env.json
# Next.js
.next
+4
View File
@@ -6,3 +6,7 @@ yarn commitlint --edit "${1}"
# Lint all staged files
yarn lint-staged
# Build & test affected apps
yarn nx affected:build --uncommitted
yarn nx affected:test --uncommitted
+2 -2
View File
@@ -3,9 +3,9 @@
/dist
/coverage
__generated__
__generated___
apps/static/src/assets/devnet-tranches.json
apps/static/src/assets/mainnet-tranches.json
apps/static/src/assets/stagnet3-tranches.json
apps/static/src/assets/stagnet1-tranches.json
apps/static/src/assets/stagnet2-tranches.json
apps/static/src/assets/testnet-tranches.json
+1 -2
View File
@@ -4,7 +4,6 @@
"esbenp.prettier-vscode",
"firsttris.vscode-jest-runner",
"dbaeumer.vscode-eslint",
"stevejpurves.cucumber",
"streetsidesoftware.code-spell-checker"
"stevejpurves.cucumber"
]
}
@@ -8,8 +8,8 @@ COPY package.json ./
COPY yarn.lock ./
COPY . ./
RUN apk add python3 make gcc g++
RUN yarn --network-timeout 100000 --verbose --pure-lockfile
RUN yarn nx build $APP --network-timeout 100000 --verbose --pure-lockfile
RUN yarn
RUN yarn nx build $APP
# Production environment
FROM nginx:stable-alpine
@@ -8,8 +8,8 @@ COPY package.json ./
COPY yarn.lock ./
COPY . ./
RUN apk add python3 make gcc g++
RUN yarn --network-timeout 100000 --verbose --pure-lockfile
RUN yarn nx export $APP --network-timeout 100000 --verbose --pure-lockfile
RUN yarn
RUN yarn nx export $APP
# Production environment
FROM nginx:stable-alpine
+6 -18
View File
@@ -69,10 +69,11 @@ Run `nx serve my-app` for a dev server. Navigate to the port specified in `app/<
### Using Apollo GraphQL and Generate Types
In order to generate the schemas for your GraphQL queries, you can run `GRAPHQL_SCHEMA_PATH=[YOUR SCHEMA FILE / API URL HERE] nx run types:generate`.
In order to generate the schemas for your GraphQL queries, you can run `nx run types:generate`.
If it is the first time you are running the command, make sure you are setting up the environment variable from `apollo.config.js`.
```bash
export GRAPHQL_SCHEMA_PATH=https://api.n11.testnet.vega.xyz/graphql
export NX_VEGA_URL=https://lb.testnet.vega.xyz/query
yarn nx run types:generate
```
@@ -106,12 +107,12 @@ Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more
## Docker
The [Dockerfile](./dockerfiles) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this.
The [Dockerfile](./Dockerfile) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this.
You can build any of the containers locally with the following command:
```bash
docker build --dockerfile dockerfiles/Dockerfile.cra . --build-arg APP=[YOUR APP] --tag=[TAG]
docker build . --build-arg APP=[YOUR APP] --tag=[TAG]
```
In order to run a container:
@@ -120,8 +121,6 @@ In order to run a container:
docker run -p 3000:80 [TAG]
```
Images ending with `.dist` are to pack locally created transpiled HTML files into nginx container for non-compatible with yarn architectures like M1 Mac
## Config
As environment variables are build time and not run time in frontend applications. We have built a system which allows for passing run time environment variables, this generates a JSON file that will override the default environment variables that the container was built with (which is always testnet, using the default .env files).
@@ -129,7 +128,7 @@ As environment variables are build time and not run time in frontend application
In order to override specific environment variables you can pass these to the container like this:
```bash
docker run -e NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql -p 3000:80 [TAG]
docker run -e NX_VEGA_URL=https://n04.d.vega.xyz/query -p 3000:80 [TAG]
```
Which will now point the app to use a devnet data node. To see a list of all possible config properties see the readme.md for each app in the app directory.
@@ -140,17 +139,6 @@ Coming soon! You will be able to run the containers within Vega Capsule.
You can run against a local instance of Vega Capsule today by using the .env.capsule present in the apps.
If you wish to run E2E tests for Token and Block Explorer (other areas to be added soon)
- Vegacapsule must be used in order for these tests to succeed, the vegacapsule repo README.md file contains the steps required to set this up, it must be installed globally.
- However we start the capsule network a little differently to how it is laid out in those instructions:
In order to run the bootstrap command to generate and start a new network, we must do so using the following:
```bash
vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl
```
# 📑 License
[MIT](./LICENSE)
-39
View File
@@ -1,39 +0,0 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
projectId: 'et4snf',
e2e: {
baseUrl: 'http://localhost:4200',
fileServerFolder: '.',
fixturesFolder: false,
specPattern: './src/integration/*.ts',
excludeSpecPattern: '**/*.js',
modifyObstructiveCode: false,
supportFile: './src/support/index.ts',
video: true,
videoUploadOnPasses: false,
videosFolder: '../../dist/cypress/apps/console-lite-e2e/videos',
screenshotsFolder: '../../dist/cypress/apps/console-lite-e2e/screenshots',
chromeWebSecurity: false,
viewportWidth: 1440,
viewportHeight: 900,
},
env: {
TRADING_TEST_VEGA_WALLET_NAME: 'UI_Trading_Test',
ETHEREUM_PROVIDER_URL:
'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
VEGA_PUBLIC_KEY:
'47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278',
VEGA_PUBLIC_KEY2:
'1a18cdcaaa4f44a57b35a4e9b77e0701c17a476f2b407620f8c17371740cf2e4',
TRUNCATED_VEGA_PUBLIC_KEY: '47836c…c7d278',
TRUNCATED_VEGA_PUBLIC_KEY2: '1a18cd…0cf2e4',
ETHEREUM_WALLET_ADDRESS: '0x265Cc6d39a1B53d0d92068443009eE7410807158',
ETHERSCAN_URL: 'https://ropsten.etherscan.io',
tsConfig: 'tsconfig.json',
TAGS: 'not @todo and not @ignore and not @manual',
TRADING_TEST_VEGA_WALLET_PASSPHRASE: '123',
ETH_WALLET_MNEMONIC:
'ugly gallery notice network true range brave clarify flat logic someone chunk',
},
});
-35
View File
@@ -1,35 +0,0 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/console-lite-e2e/src",
"projectType": "application",
"targets": {
"e2e": {
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/console-lite-e2e/cypress.config.js",
"devServerTarget": "console-lite:serve"
},
"configurations": {
"production": {
"devServerTarget": "console-lite:serve:production"
}
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/console-lite-e2e/**/*.{js,ts}"]
}
},
"build": {
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./apps/console-lite-e2e/"
}
}
},
"tags": [],
"implicitDependencies": ["console-lite"]
}
@@ -1,138 +0,0 @@
import { connectVegaWallet } from '../support/connect-wallet';
import { aliasQuery } from '@vegaprotocol/cypress';
import { generateSimpleMarkets } from '../support/mocks/generate-markets';
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
import { generateMarketTags } from '../support/mocks/generate-market-tags';
import { generateMarketPositions } from '../support/mocks/generate-market-positions';
import { generateEstimateOrder } from '../support/mocks/generate-estimate-order';
import { generatePartyBalance } from '../support/mocks/generate-party-balance';
import { generatePartyMarketData } from '../support/mocks/generate-party-market-data';
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
import { generateMarketNames } from '../support/mocks/generate-market-names';
describe('market selector', () => {
let markets;
beforeEach(() => {
cy.mockGQL((req) => {
aliasQuery(req, 'SimpleMarkets', generateSimpleMarkets());
aliasQuery(req, 'DealTicketQuery', generateDealTicket());
aliasQuery(req, 'MarketTags', generateMarketTags());
aliasQuery(req, 'MarketPositions', generateMarketPositions());
aliasQuery(req, 'EstimateOrder', generateEstimateOrder());
aliasQuery(req, 'PartyBalanceQuery', generatePartyBalance());
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
aliasQuery(req, 'MarketNames', generateMarketNames());
});
cy.visit('/markets');
cy.wait('@SimpleMarkets').then((response) => {
if (response.response.body.data?.markets?.length) {
markets = response.response.body.data.markets;
}
});
});
it('should be properly rendered', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[0].id}`);
connectVegaWallet();
cy.get('input[placeholder="Search"]').should(
'have.value',
markets[0].tradableInstrument.instrument.name
);
cy.getByTestId('arrow-button').click();
cy.getByTestId('market-pane').should('be.visible');
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.first()
.should('contain.text', markets[0].tradableInstrument.instrument.name);
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.first()
.click();
cy.getByTestId('market-pane').should('not.be.visible');
}
});
it('typing should change list', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[0].id}`);
connectVegaWallet();
cy.get('input[placeholder="Search"]').type('{backspace}');
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.should('have.length.at.least', 1);
cy.get('input[placeholder="Search"]').clear();
cy.get('input[placeholder="Search"]').type('aa');
const filtered = markets.filter(
(market) =>
market.state === 'STATE_ACTIVE' &&
market.tradableInstrument.instrument.name.match(/aa/i)
);
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.should('have.length', filtered.length);
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.last()
.click();
cy.location('pathname').should(
'eq',
`/trading/${filtered[filtered.length - 1].id}`
);
cy.get('input[placeholder="Search"]').should(
'have.value',
filtered[filtered.length - 1].tradableInstrument.instrument.name
);
}
});
// constantly failing on ci
it.skip('keyboard navigation should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[0].id}`);
connectVegaWallet();
cy.get('input[placeholder="Search"]').type('{backspace}');
cy.get('input[placeholder="Search"]').clear();
cy.focused().realPress('ArrowDown');
cy.focused().should('contain.text', 'AAVEDAI Monthly');
cy.focused().realPress('ArrowDown');
cy.focused().should('contain.text', 'ETHBTC').realPress('Enter');
cy.location('pathname').should('eq', '/trading/ethbtc-quaterly');
cy.get('input[placeholder="Search"]').type('{backspace}');
cy.get('input[placeholder="Search"]').clear();
cy.getByTestId('market-pane').should('be.visible');
cy.get('body').realPress('ArrowDown');
cy.get('body').realPress('Tab');
cy.getByTestId('market-pane').should('not.be.visible');
}
});
it('mobile view', () => {
if (markets?.length) {
cy.viewport('iphone-xr');
cy.visit(`/trading/${markets[0].id}`);
connectVegaWallet();
cy.get('[role="dialog"]').should('not.exist');
cy.getByTestId('arrow-button').click();
cy.get('[role="dialog"]').should('be.visible');
cy.get('input[placeholder="Search"]').clear();
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.should('have.length', 3);
cy.get('div[role="dialog"]').should('have.class', 'w-screen');
cy.getByTestId('dialog-close').click();
cy.get('input[placeholder="Search"]').should(
'have.value',
markets[0].tradableInstrument.instrument.name
);
}
});
});
@@ -1,320 +0,0 @@
import { aliasQuery } from '@vegaprotocol/cypress';
import { generateSimpleMarkets } from '../support/mocks/generate-markets';
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
import { generateMarketTags } from '../support/mocks/generate-market-tags';
import { generateMarketPositions } from '../support/mocks/generate-market-positions';
import { generateEstimateOrder } from '../support/mocks/generate-estimate-order';
import { generatePartyBalance } from '../support/mocks/generate-party-balance';
import { generatePartyMarketData } from '../support/mocks/generate-party-market-data';
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
import { generateMarketDepth } from '../support/mocks/generate-market-depth';
import { connectVegaWallet } from '../support/connect-wallet';
describe('Market trade', () => {
let markets;
beforeEach(() => {
cy.mockGQL((req) => {
aliasQuery(req, 'SimpleMarkets', generateSimpleMarkets());
aliasQuery(req, 'DealTicketQuery', generateDealTicket());
aliasQuery(req, 'MarketTags', generateMarketTags());
aliasQuery(req, 'MarketPositions', generateMarketPositions());
aliasQuery(req, 'EstimateOrder', generateEstimateOrder());
aliasQuery(req, 'PartyBalanceQuery', generatePartyBalance());
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
aliasQuery(req, 'MarketDepth', generateMarketDepth());
});
cy.visit('/markets');
cy.wait('@SimpleMarkets').then((response) => {
if (response.response.body.data?.markets?.length) {
markets = response.response.body.data.markets;
}
});
});
it('should not display steps if wallet is disconnected', () => {
cy.visit(`/trading/${markets[0].id}`);
cy.getByTestId('trading-connect-wallet')
.find('h3')
.should('have.text', 'Please connect your Vega wallet to make a trade');
cy.getByTestId('trading-connect-wallet')
.find('button')
.should('have.text', 'Connect Vega wallet');
cy.getByTestId('trading-connect-wallet')
.find('a')
.should('have.text', 'https://vega.xyz/wallet');
});
it('side selector should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[0].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').should(
'have.text',
'Long'
);
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('#step-1-control [aria-label^="Selected value"]').should(
'have.text',
'Short'
);
}
});
it('side selector mobile view should work well', () => {
if (markets?.length) {
cy.viewport('iphone-xr');
cy.visit(`/trading/${markets[0].id}`);
connectVegaWallet();
cy.getByTestId('next-button').scrollIntoView().click();
cy.get('button[aria-label="Open long position"]').should(
'have.class',
'selected'
);
cy.get('button[aria-label="Open short position"]').should(
'not.have.class',
'selected'
);
cy.get('button[aria-label="Open short position"]').click();
cy.get('button[aria-label="Open long position"]').should(
'not.have.class',
'selected'
);
cy.get('button[aria-label="Open short position"]').should(
'have.class',
'selected'
);
cy.getByTestId('next-button').scrollIntoView().click();
cy.get('#step-1-control').should(
'contain.html',
'aria-label="Selected value Short"'
);
}
});
it('size slider should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '1');
cy.get('#step-2-panel').find('[role="slider"]').type('{rightarrow}');
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '2');
}
});
it('percentage selection should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '1');
cy.getByTestId('max-label').should('have.text', '21');
cy.getByTestId('percentage-selector')
.find('button')
.contains('Max')
.click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '21');
}
});
it('size input should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '1');
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('input')
.type('{backspace}2');
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '2');
cy.get('button').contains('Max').click();
}
});
it('slippage value should be displayed', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('button').contains('Max').click();
cy.get('#step-2-panel')
.find('dl')
.eq(2)
.find('dd')
.should('have.text', '0.02%');
}
});
it('allow slippage value to be adjusted', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('button').contains('Max').click();
cy.get('#step-2-panel')
.find('dl')
.eq(2)
.find('dd')
.should('have.text', '0.02%');
cy.get('#step-2-panel').find('dl').eq(2).find('button').click();
cy.get('#input-order-slippage')
.focus()
.type('{backspace}{backspace}{backspace}1');
cy.getByTestId('slippage-dialog').find('button').click();
cy.get('#step-2-panel')
.find('dl')
.eq(2)
.find('dd')
.should('have.text', '1%');
}
});
it('notional position size should be present', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '1');
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('input')
.type('{backspace}2');
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
cy.get('#step-2-panel')
.find('dt')
.eq(2)
.should('have.text', 'Est. Position Size (tDAI)');
cy.get('#step-2-panel').find('dd').eq(2).should('have.text', '197.86012');
}
});
it('total fees should be displayed', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('#step-2-panel')
.find('dt')
.eq(3)
.should('have.text', 'Est. Fees (tDAI)');
cy.get('#step-2-panel')
.find('dd')
.eq(3)
.should('have.text', '3.00000 (3.03%)');
}
});
it('order review should display proper calculations', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[0].id}`);
connectVegaWallet();
cy.get('#step-3-control').click();
cy.getByTestId('review-trade')
.get('#contracts_tooltip_trigger')
.trigger('click')
.realTouch();
cy.get('[data-radix-popper-content-wrapper]').contains(
'The number of contracts determines'
);
cy.get('#step-3-panel').find('dd').eq(1).should('have.text', '1');
cy.get('#step-3-panel').find('dd').eq(2).should('have.text', '98.93006');
cy.get('#step-3-panel')
.find('dd')
.eq(3)
.should('have.text', '3.00000 (3.03%)');
cy.get('#step-3-panel').find('dd').eq(4).should('have.text', ' - ');
cy.getByTestId('place-order').click();
cy.getByTestId('dialog-title').should(
'have.text',
'Confirm transaction in wallet'
);
}
});
it('info tooltip on mobile view should work well', () => {
if (markets?.length) {
cy.viewport('iphone-xr');
cy.visit(`/trading/${markets[0].id}`);
connectVegaWallet();
cy.get('#step-3-control').click();
// Start from the bottom tooltip to ensure the tooltip above
// can be interacted with
cy.getByTestId('review-trade').get('div.cursor-help').eq(1).realTouch();
cy.get('[data-radix-popper-content-wrapper]').contains(
'The notional size represents the position size'
);
cy.getByTestId('review-trade')
.get('#contracts_tooltip_trigger')
.realTouch();
cy.get('[data-radix-popper-content-wrapper]').contains(
'The number of contracts determines'
);
}
});
});
@@ -1,37 +0,0 @@
import {
connectVegaWallet,
disconnectVegaWallet,
} from '../support/connect-wallet';
describe('Portfolio page', () => {
afterEach(() => {
disconnectVegaWallet();
});
it('button for wallet connect should work', () => {
cy.visit('/');
cy.get('[href="/portfolio"]').eq(0).click();
cy.getByTestId('trading-connect-wallet').should('be.visible');
connectVegaWallet();
cy.getByTestId('trading-connect-wallet').should('not.exist');
});
it('certain tabs should exist', () => {
cy.visit('/portfolio');
connectVegaWallet();
cy.getByTestId('Assets').should('exist');
cy.getByTestId('tab-assets').should('exist');
cy.getByTestId('Positions').click();
cy.getByTestId('tab-positions').should('exist');
cy.getByTestId('Orders').click();
cy.getByTestId('tab-orders').should('exist');
cy.getByTestId('Fills').click();
cy.getByTestId('tab-fills').should('exist');
cy.getByTestId('Deposits').click();
cy.getByTestId('tab-deposits').should('exist');
});
});
@@ -1,16 +0,0 @@
export const connectVegaWallet = () => {
const form = 'rest-connector-form';
const walletName = Cypress.env('TRADING_TEST_VEGA_WALLET_NAME');
const walletPassphrase = Cypress.env('TRADING_TEST_VEGA_WALLET_PASSPHRASE');
cy.getByTestId('connect-vega-wallet').click();
cy.getByTestId('connectors-list').find('button').click();
cy.getByTestId(form).find('#wallet').click().type(walletName);
cy.getByTestId(form).find('#passphrase').click().type(walletPassphrase);
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
};
export const disconnectVegaWallet = () => {
cy.getByTestId('connect-vega-wallet').click();
cy.getByTestId('disconnect').click();
};
@@ -1,105 +0,0 @@
const protoCandles = [
{ open: '9556163', close: '9587028', __typename: 'Candle' },
{
open: '9587028',
close: '9769899',
__typename: 'Candle',
},
{ open: '9769899', close: '9586292', __typename: 'Candle' },
{
open: '9586292',
close: '9261774',
__typename: 'Candle',
},
{ open: '9261773', close: '9236369', __typename: 'Candle' },
{
open: '9236369',
close: '9226070',
__typename: 'Candle',
},
{ open: '9226077', close: '9233252', __typename: 'Candle' },
{
open: '9249854',
close: '9333038',
__typename: 'Candle',
},
{ open: '9333038', close: '9410371', __typename: 'Candle' },
{
open: '9410371',
close: '9626249',
__typename: 'Candle',
},
{ open: '9626247', close: '9493253', __typename: 'Candle' },
{
open: '9493253',
close: '9309054',
__typename: 'Candle',
},
{ open: '9309054', close: '9378428', __typename: 'Candle' },
{
open: '9378428',
close: '9352996',
__typename: 'Candle',
},
{ open: '9352996', close: '9451142', __typename: 'Candle' },
{
open: '9451142',
close: '9691070',
__typename: 'Candle',
},
{ open: '9691071', close: '9622031', __typename: 'Candle' },
{
open: '9622034',
close: '9519285',
__typename: 'Candle',
},
{ open: '9528904', close: '9671275', __typename: 'Candle' },
{
open: '9671275',
close: '9988454',
__typename: 'Candle',
},
{ open: '9982457', close: '10085537', __typename: 'Candle' },
{
open: '10085537',
close: '9967390',
__typename: 'Candle',
},
{ open: '9967390', close: '9974844', __typename: 'Candle' },
{
open: '9974844',
close: '9940706',
__typename: 'Candle',
},
];
export const protoMarket = {
id: 'first-btcusd-id',
state: 'STATE_ACTIVE',
tradableInstrument: {
instrument: {
code: 'AAVEDAI.MF21',
name: 'AAVEDAI Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:2839D9B2329C9E70',
'base:AAVE',
'quote:DAI',
'class:fx/crypto',
'monthly',
'sector:defi',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'DAI',
settlementAsset: { symbol: 'tDAI', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: protoCandles,
__typename: 'Market',
};
@@ -1,33 +0,0 @@
export const generateDealTicket = () => {
return {
market: {
id: 'first-btcusd-id',
decimalPlaces: 5,
positionDecimalPlaces: 0,
state: 'STATE_ACTIVE',
tradingMode: 'Continuous',
tradableInstrument: {
instrument: {
name: 'AAVEDAI Monthly (30 Jun 2022)',
product: {
quoteName: 'DAI',
settlementAsset: {
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
symbol: 'tDAI',
name: 'tDAI TEST',
__typename: 'Asset',
},
__typename: 'Future',
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
depth: {
lastTrade: { price: '9893006', __typename: 'Trade' },
__typename: 'MarketDepth',
},
__typename: 'Market',
},
};
};
@@ -1,17 +0,0 @@
export const generateEstimateOrder = () => {
return {
estimateOrder: {
fee: {
__typename: 'TradeFee',
makerFee: '100000',
liquidityFee: '100000',
infrastructureFee: '100000',
},
marginLevels: {
initialLevel: '2844054.80937741220203',
__typename: 'MarginLevels',
},
__typename: 'OrderEstimate',
},
};
};
@@ -1,218 +0,0 @@
export const generateMarketDepth = () => {
return {
market: {
id: 'a46bd7e5277087723b7ab835844dec3cef8b4445738101269624bf5537d5d423',
decimalPlaces: 5,
positionDecimalPlaces: 0,
data: {
staticMidPrice: '9893006',
marketTradingMode: 'TRADING_MODE_CONTINUOUS',
indicativeVolume: '0',
indicativePrice: '0',
bestStaticBidPrice: '9893006',
bestStaticOfferPrice: '9893006',
market: {
id: 'a46bd7e5277087723b7ab835844dec3cef8b4445738101269624bf5537d5d423',
__typename: 'Market',
},
__typename: 'MarketData',
},
depth: {
lastTrade: { price: '9893006', __typename: 'Trade' },
sell: [
{
price: '9893007',
volume: '3',
numberOfOrders: '3',
__typename: 'PriceLevel',
},
{
price: '9893010',
volume: '4',
numberOfOrders: '4',
__typename: 'PriceLevel',
},
{
price: '9893012',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893015',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893017',
volume: '2',
numberOfOrders: '2',
__typename: 'PriceLevel',
},
{
price: '9893021',
volume: '4',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893025',
volume: '5',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893125',
volume: '4',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893135',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893165',
volume: '5',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893175',
volume: '3',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893185',
volume: '3',
numberOfOrders: '3',
__typename: 'PriceLevel',
},
{
price: '9894185',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9894585',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9895585',
volume: '4',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9896585',
volume: '2',
numberOfOrders: '2',
__typename: 'PriceLevel',
},
],
buy: [
{
price: '9893005',
volume: '4',
numberOfOrders: '3',
__typename: 'PriceLevel',
},
{
price: '9893003',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893001',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9892006',
volume: '3',
numberOfOrders: '2',
__typename: 'PriceLevel',
},
{
price: '9891006',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9891001',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890101',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890091',
volume: '5',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890081',
volume: '4',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890050',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890040',
volume: '6',
numberOfOrders: '3',
__typename: 'PriceLevel',
},
{
price: '9890030',
volume: '6',
numberOfOrders: '2',
__typename: 'PriceLevel',
},
{
price: '9890021',
volume: '3',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890011',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890001',
volume: '11',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
],
sequenceNumber: '1661773865550746910',
__typename: 'MarketDepth',
},
__typename: 'Market',
},
};
};
@@ -1,9 +0,0 @@
export const generateMarketMarkPrice = () => {
return {
market: {
decimalPlaces: 5,
data: { markPrice: '692748', __typename: 'MarketData' },
__typename: 'Market',
},
};
};
@@ -1,136 +0,0 @@
import { protoMarket } from './commons';
export const generateMarketNames = () => {
return {
markets: [
{ ...protoMarket },
{
id: '1d7ddf67dac4924db03f5bf58571a7bcb1908d70c66580467717aabc5345b68a',
state: 'STATE_SUSPENDED',
tradableInstrument: {
instrument: {
code: 'AAPL.MF21',
name: 'Apple Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:4899E01009F1A721',
'quote:USD',
'ticker:AAPL',
'class:equities/single-stock-futures',
'sector:tech',
'listing_venue:NASDAQ',
'country:US',
],
__typename: 'InstrumentMetadata',
},
product: { quoteName: 'USD', __typename: 'Future' },
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
__typename: 'Market',
},
{
id: '87ae87cd3244fc1fab4b0e2dad2437879864192bb969f3109b69293421644c8b',
state: 'STATE_SUSPENDED',
tradableInstrument: {
instrument: {
code: 'TSLA.QM21',
name: 'Tesla Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:5A86B190C384997F',
'quote:EURO',
'ticker:TSLA',
'class:equities/single-stock-futures',
'sector:tech',
'listing_venue:NASDAQ',
'country:US',
],
__typename: 'InstrumentMetadata',
},
product: { quoteName: 'EURO', __typename: 'Future' },
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
__typename: 'Market',
},
{
id: '69205712a854f1bbfb69fa3d11b60e01a1e249bafb5ece88115e7451e8ef07b3',
state: 'STATE_SUSPENDED',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
'base:BTC',
'quote:USD',
'class:fx/crypto',
'monthly',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: { quoteName: 'USD', __typename: 'Future' },
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
__typename: 'Market',
},
{
id: 'ethbtc-quaterly',
state: 'STATE_ACTIVE',
tradableInstrument: {
instrument: {
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:1F0BB6EB5703B099',
'base:ETH',
'quote:BTC',
'class:fx/crypto',
'quarterly',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: { quoteName: 'BTC', __typename: 'Future' },
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
__typename: 'Market',
},
{
id: '3c62b2714c4332d1a689a5352bff090e6aabccfd6bd87ce018936b38372530c9',
state: 'STATE_ACTIVE',
tradableInstrument: {
instrument: {
code: 'UNIDAI.MF21',
name: 'UNIDAI Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:3C58ED2A4A6C5D7E',
'base:UNI',
'quote:DAI',
'class:fx/crypto',
'monthly',
'sector:defi',
],
__typename: 'InstrumentMetadata',
},
product: { quoteName: 'DAI', __typename: 'Future' },
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
__typename: 'Market',
},
],
};
};
@@ -1,59 +0,0 @@
export const generateMarketPositions = () => {
return {
party: {
id: '2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
accounts: [
{
type: 'General',
asset: {
decimals: 5,
},
balance: '400000000000000000000',
market: {
id: '2751c508f9759761f912890f37fb3f97a00300bf7685c02a56a86e05facfe221',
__typename: 'Market',
},
},
{
type: 'Margin',
asset: {
decimals: 5,
},
balance: '265329',
market: {
id: 'first-btcusd-id',
__typename: 'Market',
},
},
],
positionsConnection: {
edges: [
{
node: {
openVolume: '3',
market: {
id: '2751c508f9759761f912890f37fb3f97a00300bf7685c02a56a86e05facfe221',
__typename: 'Market',
},
__typename: 'Position',
},
__typename: 'PositionEdge',
},
{
node: {
openVolume: '12',
market: {
id: 'first-btcusd-id',
__typename: 'Market',
},
__typename: 'Position',
},
__typename: 'PositionEdge',
},
],
__typename: 'PositionConnection',
},
__typename: 'Party',
},
};
};
@@ -1,25 +0,0 @@
export const generateMarketTags = () => {
return {
market: {
tradableInstrument: {
instrument: {
metadata: {
tags: [
'formerly:2839D9B2329C9E70',
'base:AAVE',
'quote:DAI',
'class:fx/crypto',
'monthly',
'sector:defi',
'settlement:2022-08-01',
],
__typename: 'InstrumentMetadata',
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
__typename: 'Market',
},
};
};
@@ -1,882 +0,0 @@
import { protoMarket } from './commons';
export const generateSimpleMarkets = () => {
return {
markets: [
{ ...protoMarket },
{
id: '57fbaa322e97cfc8bb5f1de048c37e033c41b1ac1906d3aed9960912a067ef5a',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'CELUSD',
name: 'CELUSD (June 2022)',
metadata: {
tags: [
'base:CEL',
'quote:USD',
'class:fx/crypto',
'ad-hoc',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'XYZalpha', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: null,
__typename: 'Market',
},
{
id: 'ccf2f04865e5951ac3405da6e16b7cbdb535a0ad32df4df2dbed4262cf473255',
state: 'Suspended',
tradableInstrument: {
instrument: {
code: 'XMRUSD',
name: 'XMRUSD market',
metadata: {
tags: [
'base:monero',
'quote:usd',
'class:fx/crypto',
'ad-hoc',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'XYZbeta', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: null,
__typename: 'Market',
},
{
id: '6030e5b4e0ca3297a26081e5af4d453f97f96baab2d74bf56f84efcffc4c382f',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'UNIDAI.MF21',
name: 'UNIDAI Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:3C58ED2A4A6C5D7E',
'base:UNI',
'quote:DAI',
'class:fx/crypto',
'monthly',
'sector:defi',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'DAI',
settlementAsset: { symbol: 'tDAI', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: [
{ open: '723082', close: '726800', __typename: 'Candle' },
{
open: '726800',
close: '733551',
__typename: 'Candle',
},
{ open: '733557', close: '719960', __typename: 'Candle' },
{
open: '719961',
close: '707711',
__typename: 'Candle',
},
{ open: '702459', close: '705275', __typename: 'Candle' },
{
open: '701609',
close: '698494',
__typename: 'Candle',
},
{ open: '698494', close: '702201', __typename: 'Candle' },
{
open: '704749',
close: '704248',
__typename: 'Candle',
},
{ open: '708562', close: '714712', __typename: 'Candle' },
{
open: '718434',
close: '732567',
__typename: 'Candle',
},
{ open: '732567', close: '750475', __typename: 'Candle' },
{
open: '768916',
close: '746371',
__typename: 'Candle',
},
{ open: '740240', close: '744910', __typename: 'Candle' },
{
open: '746902',
close: '731680',
__typename: 'Candle',
},
{ open: '730156', close: '729021', __typename: 'Candle' },
{
open: '730503',
close: '738096',
__typename: 'Candle',
},
{ open: '738096', close: '743087', __typename: 'Candle' },
{
open: '739244',
close: '732352',
__typename: 'Candle',
},
{ open: '732352', close: '751259', __typename: 'Candle' },
{
open: '751259',
close: '751455',
__typename: 'Candle',
},
{ open: '751455', close: '753990', __typename: 'Candle' },
{
open: '757174',
close: '761100',
__typename: 'Candle',
},
{ open: '758974', close: '759379', __typename: 'Candle' },
{
open: '762834',
close: '761777',
__typename: 'Candle',
},
],
__typename: 'Market',
},
{
id: 'de74a5572045b300e8ec50d136896912ec76e7d7ec135bc305dfd4854d9363a4',
state: 'Pending',
tradableInstrument: {
instrument: {
code: 'XMRUSD',
name: 'XMRUSD market',
metadata: {
tags: [
'base:monero',
'quote:usd',
'class:fx/crypto',
'ad-hoc',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'XYZbeta', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: [
{ open: '14602', close: '14596', __typename: 'Candle' },
{
open: '14596',
close: '14731',
__typename: 'Candle',
},
{ open: '14732', close: '14592', __typename: 'Candle' },
{
open: '14592',
close: '14516',
__typename: 'Candle',
},
{ open: '14516', close: '14179', __typename: 'Candle' },
{
open: '14179',
close: '14031',
__typename: 'Candle',
},
{ open: '14032', close: '14115', __typename: 'Candle' },
{
open: '14116',
close: '14166',
__typename: 'Candle',
},
{ open: '14166', close: '14377', __typename: 'Candle' },
{
open: '14377',
close: '14714',
__typename: 'Candle',
},
{ open: '14713', close: '14593', __typename: 'Candle' },
{
open: '14594',
close: '14273',
__typename: 'Candle',
},
{ open: '14272', close: '14245', __typename: 'Candle' },
{
open: '14244',
close: '14337',
__typename: 'Candle',
},
{ open: '14338', close: '14384', __typename: 'Candle' },
{
open: '14385',
close: '14257',
__typename: 'Candle',
},
{ open: '14256', close: '14105', __typename: 'Candle' },
{
open: '14106',
close: '14067',
__typename: 'Candle',
},
{ open: '14066', close: '14196', __typename: 'Candle' },
{
open: '14197',
close: '14316',
__typename: 'Candle',
},
{ open: '14315', close: '14377', __typename: 'Candle' },
{
open: '14378',
close: '14106',
__typename: 'Candle',
},
{ open: '14105', close: '14317', __typename: 'Candle' },
{
open: '14318',
close: '14467',
__typename: 'Candle',
},
],
__typename: 'Market',
},
{
id: '31ea96284611771e486c820acb26a325a99664f9854b5a7e7ad99023efa8f9e6',
state: 'Closed',
tradableInstrument: {
instrument: {
code: 'APE/USD',
name: 'ApeCoin (18 Jul 2022)',
metadata: {
tags: [
'quote:USD',
'ticker:APE',
'class:equities/single-stock-futures',
'sector:tech',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'tUSDC', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: null,
__typename: 'Market',
},
{
id: '34cff959cdc2ffdb0f167820d701fe8b51cc6b8588e650d93369aaa22d6f8b74',
state: 'Settled',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (18 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
'base:BTC',
'quote:USD',
'class:fx/crypto',
'monthly',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'tBTC', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: null,
__typename: 'Market',
},
{
id: '87b0bbb3c171baa5d97dfc3852332829c91e5c5dc9f7c8fb584c6d8ac75aaaf2',
state: 'Settled',
tradableInstrument: {
instrument: {
code: 'LTCUSD',
name: 'Builders Club x ETHcc',
metadata: {
tags: [
'base:litecoin',
'quote:usd',
'class:fx/crypto',
'ad-hoc',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'XYZgamma', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: [
{ open: '5700', close: '5688', __typename: 'Candle' },
{
open: '5688',
close: '5644',
__typename: 'Candle',
},
{ open: '5644', close: '5664', __typename: 'Candle' },
{
open: '5664',
close: '5694',
__typename: 'Candle',
},
{ open: '5694', close: '5751', __typename: 'Candle' },
{
open: '5751',
close: '5853',
__typename: 'Candle',
},
{ open: '5853', close: '5773', __typename: 'Candle' },
{
open: '5773',
close: '5697',
__typename: 'Candle',
},
{ open: '5697', close: '5683', __typename: 'Candle' },
{
open: '5683',
close: '5655',
__typename: 'Candle',
},
{ open: '5655', close: '5674', __typename: 'Candle' },
{
open: '5674',
close: '5705',
__typename: 'Candle',
},
{ open: '5705', close: '5648', __typename: 'Candle' },
{
open: '5648',
close: '5646',
__typename: 'Candle',
},
{ open: '5646', close: '5687', __typename: 'Candle' },
{
open: '5687',
close: '5675',
__typename: 'Candle',
},
{ open: '5675', close: '5696', __typename: 'Candle' },
{
open: '5696',
close: '5699',
__typename: 'Candle',
},
{ open: '5699', close: '5731', __typename: 'Candle' },
{
open: '5731',
close: '5768',
__typename: 'Candle',
},
],
__typename: 'Market',
},
{
id: '45266143c6e9b58f4cff9b8906e971c531bb29ea7af01066973f9b77e8134823',
state: 'Pending',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (18 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
'base:BTC',
'quote:USD',
'class:fx/crypto',
'monthly',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'tBTC', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: null,
__typename: 'Market',
},
{
id: '65663ebdc96161162769c4d5c5508137416748178d7cb28e2cb0d07a151a2bc6',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:1F0BB6EB5703B099',
'base:ETH',
'quote:BTC',
'class:fx/crypto',
'quarterly',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'BTC',
settlementAsset: { symbol: 'tBTC', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: [
{ open: '6680', close: '6600', __typename: 'Candle' },
{
open: '6600',
close: '6630',
__typename: 'Candle',
},
{ open: '6630', close: '6677', __typename: 'Candle' },
{
open: '6735',
close: '6720',
__typename: 'Candle',
},
{ open: '6720', close: '6691', __typename: 'Candle' },
{
open: '6681',
close: '6730',
__typename: 'Candle',
},
{ open: '6756', close: '6778', __typename: 'Candle' },
{
open: '6778',
close: '6768',
__typename: 'Candle',
},
{ open: '6768', close: '6834', __typename: 'Candle' },
{
open: '6834',
close: '7001',
__typename: 'Candle',
},
{ open: '7001', close: '6894', __typename: 'Candle' },
{
open: '6910',
close: '6867',
__typename: 'Candle',
},
{ open: '6867', close: '6827', __typename: 'Candle' },
{
open: '6899',
close: '6910',
__typename: 'Candle',
},
{ open: '6868', close: '6902', __typename: 'Candle' },
{
open: '6905',
close: '6947',
__typename: 'Candle',
},
{ open: '6932', close: '6900', __typename: 'Candle' },
{
open: '6900',
close: '6911',
__typename: 'Candle',
},
{ open: '6936', close: '6948', __typename: 'Candle' },
{
open: '6936',
close: '6946',
__typename: 'Candle',
},
{ open: '6946', close: '6906', __typename: 'Candle' },
{
open: '6947',
close: '6962',
__typename: 'Candle',
},
{ open: '6962', close: '7027', __typename: 'Candle' },
{
open: '6941',
close: '6921',
__typename: 'Candle',
},
],
__typename: 'Market',
},
{
id: '234afabd27e3bce1a879039c041f9f00f915d98459935ddafd0169d38dd13850',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'AAPL.MF21',
name: 'Apple Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:4899E01009F1A721',
'quote:USD',
'ticker:AAPL',
'class:equities/single-stock-futures',
'sector:tech',
'listing_venue:NASDAQ',
'country:US',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'tUSDC', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: [
{ open: '15126319', close: '15087482', __typename: 'Candle' },
{
open: '15087482',
close: '15119759',
__typename: 'Candle',
},
{ open: '15119759', close: '15061361', __typename: 'Candle' },
{
open: '15061361',
close: '14742622',
__typename: 'Candle',
},
{ open: '14742622', close: '14735571', __typename: 'Candle' },
{
open: '14735571',
close: '14694437',
__typename: 'Candle',
},
{ open: '14694437', close: '14670479', __typename: 'Candle' },
{
open: '14670479',
close: '14624796',
__typename: 'Candle',
},
{ open: '14621001', close: '14722835', __typename: 'Candle' },
{
open: '14722835',
close: '14705183',
__typename: 'Candle',
},
{ open: '14705183', close: '14710797', __typename: 'Candle' },
{
open: '14710796',
close: '14713874',
__typename: 'Candle',
},
{ open: '14713874', close: '14677982', __typename: 'Candle' },
{
open: '14677982',
close: '14710031',
__typename: 'Candle',
},
{ open: '14751685', close: '14737556', __typename: 'Candle' },
{
open: '14737556',
close: '14769139',
__typename: 'Candle',
},
{ open: '14760616', close: '14761691', __typename: 'Candle' },
{
open: '14761691',
close: '14714380',
__typename: 'Candle',
},
{ open: '14714380', close: '14740160', __typename: 'Candle' },
{
open: '14740160',
close: '14764832',
__typename: 'Candle',
},
{ open: '14764832', close: '14778059', __typename: 'Candle' },
{
open: '14761936',
close: '14707831',
__typename: 'Candle',
},
{ open: '14707831', close: '14753272', __typename: 'Candle' },
{
open: '14753272',
close: '14771411',
__typename: 'Candle',
},
],
__typename: 'Market',
},
{
id: '9f2a3c1caa67bb0773ec18d908d32b55b129b9ec2d106a8e9a87f6aa5c0375a6',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'TSLA.QM21',
name: 'Tesla Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:5A86B190C384997F',
'quote:EURO',
'ticker:TSLA',
'class:equities/single-stock-futures',
'sector:tech',
'listing_venue:NASDAQ',
'country:US',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'EURO',
settlementAsset: { symbol: 'tEURO', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: [
{ open: '71263667', close: '71173749', __typename: 'Candle' },
{
open: '71173749',
close: '71055959',
__typename: 'Candle',
},
{ open: '71055959', close: '71078605', __typename: 'Candle' },
{
open: '71078605',
close: '69838205',
__typename: 'Candle',
},
{ open: '69838205', close: '69166023', __typename: 'Candle' },
{
open: '69166023',
close: '68841759',
__typename: 'Candle',
},
{ open: '68841759', close: '68711290', __typename: 'Candle' },
{
open: '68711290',
close: '68650154',
__typename: 'Candle',
},
{ open: '68644289', close: '68638322', __typename: 'Candle' },
{
open: '68704232',
close: '68343581',
__typename: 'Candle',
},
{ open: '67345721', close: '67554701', __typename: 'Candle' },
{
open: '67533143',
close: '67551571',
__typename: 'Candle',
},
{ open: '67533286', close: '67543539', __typename: 'Candle' },
{
open: '67580208',
close: '67472337',
__typename: 'Candle',
},
{ open: '67416289', close: '67459697', __typename: 'Candle' },
{
open: '67396085',
close: '67528265',
__typename: 'Candle',
},
{ open: '67491799', close: '67538833', __typename: 'Candle' },
{
open: '67493210',
close: '67362908',
__typename: 'Candle',
},
{ open: '67362908', close: '67518182', __typename: 'Candle' },
{
open: '67518182',
close: '67635775',
__typename: 'Candle',
},
{ open: '67635775', close: '67734861', __typename: 'Candle' },
{
open: '67714943',
close: '67736742',
__typename: 'Candle',
},
{ open: '67716074', close: '67690826', __typename: 'Candle' },
{
open: '67714352',
close: '67817444',
__typename: 'Candle',
},
],
__typename: 'Market',
},
{
id: '8a45ee934d3ddac4b036f9884df1064a5353c620a56f775ba36597d0edef9a7a',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'LTCUSD',
name: 'Go big or Go home',
metadata: {
tags: [
'base:litecoin',
'quote:usd',
'class:fx/crypto',
'ad-hoc',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'XYZgamma', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: [
{ open: '5809', close: '5795', __typename: 'Candle' },
{
open: '5795',
close: '5845',
__typename: 'Candle',
},
{ open: '5845', close: '5779', __typename: 'Candle' },
{
open: '5779',
close: '5710',
__typename: 'Candle',
},
{ open: '5710', close: '5691', __typename: 'Candle' },
],
__typename: 'Market',
},
{
id: 'aede7b9ac0c3b225004929c5455160a00f59864aad32ec366e8a2bff1b30fd0f',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (08 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
'base:BTC',
'quote:USD',
'class:fx/crypto',
'monthly',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'tBTC', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: null,
__typename: 'Market',
},
{
id: '9398707e01daa1a1f1ca6ff87cf8d6c03fe7373ce31121ce81b99a129e6bda47',
state: 'Active',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (18 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
'base:BTC',
'quote:USD',
'class:fx/crypto',
'monthly',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
product: {
__typename: 'Future',
quoteName: 'USD',
settlementAsset: { symbol: 'tBTC', __typename: 'Asset' },
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
candles: null,
__typename: 'Market',
},
],
};
};
export const generateLongListMarkets = (count: number) => {
const markets = [];
for (let i = 0; i < count; i++) {
markets.push({
...protoMarket,
id: protoMarket.id + i,
tradableInstrument: {
...protoMarket.tradableInstrument,
instrument: {
...protoMarket.tradableInstrument.instrument,
name: protoMarket.tradableInstrument.instrument.name + i,
},
},
});
}
return { markets };
};
@@ -1,57 +0,0 @@
export const generatePartyBalance = () => {
return {
party: {
accounts: [
{
balance: '88474051',
type: 'ACCOUNT_TYPE_GENERAL',
asset: {
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
symbol: 'tDAI',
name: 'tDAI TEST',
decimals: 5,
__typename: 'Asset',
},
__typename: 'Account',
},
{
balance: '100000000',
type: 'ACCOUNT_TYPE_GENERAL',
asset: {
id: '8b52d4a3a4b0ffe733cddbc2b67be273816cfeb6ca4c8b339bac03ffba08e4e4',
symbol: 'tEURO',
name: 'tEURO TEST',
decimals: 5,
__typename: 'Asset',
},
__typename: 'Account',
},
{
balance: '3412867',
type: 'ACCOUNT_TYPE_GENERAL',
asset: {
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
symbol: 'tDAI',
name: 'tDAI TEST',
decimals: 5,
__typename: 'Asset',
},
__typename: 'Account',
},
{
balance: '70007',
type: 'ACCOUNT_TYPE_GENERAL',
asset: {
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
symbol: 'tDAI',
name: 'tDAI TEST',
decimals: 5,
__typename: 'Asset',
},
__typename: 'Account',
},
],
__typename: 'Party',
},
};
};
@@ -1,19 +0,0 @@
export const generatePartyMarketData = () => {
return {
party: {
id: '2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
accounts: [
{
type: 'General',
balance: '1200000',
asset: { id: 'fBTC', decimals: 5, __typename: 'Asset' },
market: null,
__typename: 'Account',
},
],
marginsConnection: { edges: null, __typename: 'MarginConnection' },
positionsConnection: { edges: null, __typename: 'PositionConnection' },
__typename: 'Party',
},
};
};
-9
View File
@@ -1,9 +0,0 @@
# App configuration variables
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
NX_VEGA_ENV=DEVNET
NX_VEGA_REST=https://api.n04.d.vega.xyz
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
-9
View File
@@ -1,9 +0,0 @@
# App configuration variables
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
NX_VEGA_URL=https://api.token.vega.xyz/query
NX_VEGA_ENV=MAINNET
NX_VEGA_REST=https://api.token.vega.xyz
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
-9
View File
@@ -1,9 +0,0 @@
# App configuration variables
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
NX_VEGA_ENV=TESTNET
NX_VEGA_REST=https://api.n11.testnet.vega.xyz
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
-18
View File
@@ -1,18 +0,0 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
-62
View File
@@ -1,62 +0,0 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { createClient } from './lib/apollo-client';
import { ThemeContext } from '@vegaprotocol/react-helpers';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
import {
VegaConnectDialog,
VegaManageDialog,
VegaWalletProvider,
} from '@vegaprotocol/wallet';
import { Connectors } from './lib/vega-connectors';
import '../styles.scss';
import { AppLoader } from './components/app-loader';
import Header from './components/header';
import { Main } from './components/main';
import LocalContext from './context/local-context';
import useLocalValues from './hooks/use-local-values';
function App() {
const [theme, toggleTheme] = useThemeSwitcher();
const localValues = useLocalValues(theme, toggleTheme);
const {
vegaWalletDialog,
menu: { setMenuOpen },
} = localValues;
const location = useLocation();
useEffect(() => {
setMenuOpen(false);
}, [location, setMenuOpen]);
return (
<EnvironmentProvider>
<ThemeContext.Provider value={theme}>
<NetworkLoader createClient={createClient}>
<VegaWalletProvider>
<LocalContext.Provider value={localValues}>
<AppLoader>
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-neutral-200 bg-white text-neutral-800 grid grid-rows-[min-content,1fr]">
<Header />
<Main />
<VegaConnectDialog
connectors={Connectors}
dialogOpen={vegaWalletDialog.connect}
setDialogOpen={vegaWalletDialog.setConnect}
/>
<VegaManageDialog
dialogOpen={vegaWalletDialog.manage}
setDialogOpen={vegaWalletDialog.setManage}
/>
</div>
</AppLoader>
</LocalContext.Provider>
</VegaWalletProvider>
</NetworkLoader>
</ThemeContext.Provider>
</EnvironmentProvider>
);
}
export default App;
@@ -1,11 +0,0 @@
query MarketTags($marketId: ID!) {
market(id: $marketId) {
tradableInstrument {
instrument {
metadata {
tags
}
}
}
}
}
@@ -1,14 +0,0 @@
query PartyBalanceQuery($partyId: ID!) {
party(id: $partyId) {
accounts {
type
balance
asset {
id
symbol
name
decimals
}
}
}
}
@@ -1,51 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: MarketTags
// ====================================================
export interface MarketTags_market_tradableInstrument_instrument_metadata {
__typename: "InstrumentMetadata";
/**
* An arbitrary list of tags to associated to associate to the Instrument (string list)
*/
tags: string[] | null;
}
export interface MarketTags_market_tradableInstrument_instrument {
__typename: "Instrument";
/**
* Metadata for this instrument
*/
metadata: MarketTags_market_tradableInstrument_instrument_metadata;
}
export interface MarketTags_market_tradableInstrument {
__typename: "TradableInstrument";
/**
* An instance of, or reference to, a fully specified instrument.
*/
instrument: MarketTags_market_tradableInstrument_instrument;
}
export interface MarketTags_market {
__typename: "Market";
/**
* An instance of, or reference to, a tradable instrument.
*/
tradableInstrument: MarketTags_market_tradableInstrument;
}
export interface MarketTags {
/**
* An instrument that is trading on the Vega network
*/
market: MarketTags_market | null;
}
export interface MarketTagsVariables {
marketId: string;
}
@@ -1,54 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketTagsQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketTagsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null } } } } | null };
export const MarketTagsDocument = gql`
query MarketTags($marketId: ID!) {
market(id: $marketId) {
tradableInstrument {
instrument {
metadata {
tags
}
}
}
}
}
`;
/**
* __useMarketTagsQuery__
*
* To run a query within a React component, call `useMarketTagsQuery` and pass it any options that fit your needs.
* When your component renders, `useMarketTagsQuery` 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 } = useMarketTagsQuery({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useMarketTagsQuery(baseOptions: Apollo.QueryHookOptions<MarketTagsQuery, MarketTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<MarketTagsQuery, MarketTagsQueryVariables>(MarketTagsDocument, options);
}
export function useMarketTagsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketTagsQuery, MarketTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<MarketTagsQuery, MarketTagsQueryVariables>(MarketTagsDocument, options);
}
export type MarketTagsQueryHookResult = ReturnType<typeof useMarketTagsQuery>;
export type MarketTagsLazyQueryHookResult = ReturnType<typeof useMarketTagsLazyQuery>;
export type MarketTagsQueryResult = Apollo.QueryResult<MarketTagsQuery, MarketTagsQueryVariables>;
@@ -1,57 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type PartyBalanceQueryQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type PartyBalanceQueryQuery = { __typename?: 'Query', party?: { __typename?: 'Party', accounts?: Array<{ __typename?: 'Account', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number } }> | null } | null };
export const PartyBalanceQueryDocument = gql`
query PartyBalanceQuery($partyId: ID!) {
party(id: $partyId) {
accounts {
type
balance
asset {
id
symbol
name
decimals
}
}
}
}
`;
/**
* __usePartyBalanceQueryQuery__
*
* To run a query within a React component, call `usePartyBalanceQueryQuery` and pass it any options that fit your needs.
* When your component renders, `usePartyBalanceQueryQuery` 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 } = usePartyBalanceQueryQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function usePartyBalanceQueryQuery(baseOptions: Apollo.QueryHookOptions<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>(PartyBalanceQueryDocument, options);
}
export function usePartyBalanceQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>(PartyBalanceQueryDocument, options);
}
export type PartyBalanceQueryQueryHookResult = ReturnType<typeof usePartyBalanceQueryQuery>;
export type PartyBalanceQueryLazyQueryHookResult = ReturnType<typeof usePartyBalanceQueryLazyQuery>;
export type PartyBalanceQueryQueryResult = Apollo.QueryResult<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>;
@@ -1,21 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
export const EST_MARGIN_TOOLTIP_TEXT = t(
'When opening a position on a futures market, you must post margin to cover any potential losses that you may incur. The margin is typically a fraction of the notional position size. For example, for a notional position size of $500, if the margin requirement is 10%, then the estimated margin would be approximately $50.'
);
export const CONTRACTS_MARGIN_TOOLTIP_TEXT = t(
'The number of contracts determines how many units of the futures contract to buy or sell. For example, this is similar to buying one share of a listed company. The value of 1 contract is equivalent to the price of the contract. For example, if the current price is $50, then one contract is worth $50.'
);
export const EST_CLOSEOUT_TOOLTIP_TEXT = t(
'Because you only need to post a fraction of your position size as margin when trading futures, it is possible to obtain leverage meaning your notional position size exceeds your account balance. In this scenario, if the market moves against your position, it will sometimes be necessary to force close your position due to insufficient funds. The estimated close out tells you the price at which that would happen based on current position and account balance.'
);
export const NOTIONAL_SIZE_TOOLTIP_TEXT = t(
'The notional size represents the position size in the settlement asset of the futures contract. The notional size is calculated by multiplying the number of contracts by the price of the contract. For example, ten contracts traded at a price of $50 has a notional size of $500.'
);
export const EST_FEES_TOOLTIP_TEXT = t(
'When you execute a new buy or sell order, you must pay a small amount of commission to the network for doing so. This fee is used to provide income to the node operates of the network and market makers who make prices on the futures market you are trading.'
);
export const EST_SLIPPAGE = t(
'When you execute a trade on Vega, the price obtained in the market may differ from the best available price displayed at the time of placing the trade. The estimated slippage shows the difference between the best available price and the estimated execution price, determined by market liquidity and your chosen order size.'
);
@@ -1,67 +0,0 @@
import React from 'react';
import { render } from '@testing-library/react';
import type {
PartyBalanceQuery_party_accounts,
PartyBalanceQuery_party_accounts_asset,
} from './__generated__/PartyBalanceQuery';
import { DealTicketBalance } from './deal-ticket-balance';
import { AccountType } from '@vegaprotocol/types';
const tDAI: PartyBalanceQuery_party_accounts_asset = {
__typename: 'Asset',
id: '1',
symbol: 'tDAI',
name: 'TDAI',
decimals: 2,
};
const accounts: PartyBalanceQuery_party_accounts[] = [
{
__typename: 'Account',
type: AccountType.ACCOUNT_TYPE_GENERAL,
balance: '1000000',
asset: tDAI,
},
];
describe('DealTicketBalance', function () {
it('should render the balance', () => {
const { getByText, getByRole } = render(
<DealTicketBalance
settlementAsset={tDAI}
accounts={accounts}
isWalletConnected
/>
);
expect(getByRole('complementary')).toHaveAccessibleName('tDAI Balance');
expect(getByText('10,000.00')).toBeInTheDocument();
expect(getByText('tDAI')).toBeInTheDocument();
});
it('should prompt to connect wallet', () => {
const { getByText } = render(
<DealTicketBalance
settlementAsset={tDAI}
accounts={accounts}
isWalletConnected={false}
/>
);
expect(
getByText('Please connect your Vega wallet to see your balance')
).toBeInTheDocument();
});
it('should display zero balance', () => {
const { getByText } = render(
<DealTicketBalance
settlementAsset={tDAI}
accounts={[]}
isWalletConnected={true}
/>
);
expect(getByText('No tDAI left to trade')).toBeInTheDocument();
});
});
@@ -1,86 +0,0 @@
import * as React from 'react';
import { useParams } from 'react-router-dom';
import { gql, useQuery } from '@apollo/client';
import {
DealTicketManager,
DealTicketContainer as Container,
} from '@vegaprotocol/deal-ticket';
import { Loader } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { DealTicketSteps } from './deal-ticket-steps';
import { DealTicketBalance } from './deal-ticket-balance';
import Baubles from './baubles-decor';
import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery';
import ConnectWallet from '../wallet-connector';
const tempEmptyText = (
<p>{t('Please select a market from the markets page')}</p>
);
const PARTY_BALANCE_QUERY = gql`
query PartyBalanceQuery($partyId: ID!) {
party(id: $partyId) {
accounts {
type
balance
asset {
id
symbol
name
decimals
}
}
}
}
`;
export const DealTicketContainer = () => {
const { marketId } = useParams<{ marketId: string }>();
const { keypair } = useVegaWallet();
const { data: partyData, loading } = useQuery<PartyBalanceQuery>(
PARTY_BALANCE_QUERY,
{
variables: { partyId: keypair?.pub },
skip: !keypair?.pub,
}
);
const loader = <Loader />;
const container = marketId ? (
<Container marketId={marketId}>
{(data) => {
const balance = (
<DealTicketBalance
className="mb-4"
settlementAsset={
data.market.tradableInstrument.instrument.product?.settlementAsset
}
accounts={partyData?.party?.accounts || []}
isWalletConnected={!!keypair?.pub}
/>
);
return (
<DealTicketManager market={data.market}>
{loading ? loader : balance}
<DealTicketSteps market={data.market} partyData={partyData} />
</DealTicketManager>
);
}}
</Container>
) : (
tempEmptyText
);
return (
<section className="flex p-4 md:p-6">
<section className="w-full md:w-1/2 md:min-w-[500px]">
{keypair ? container : <ConnectWallet />}
</section>
<Baubles />
</section>
);
};
@@ -1,133 +0,0 @@
import React from 'react';
import type { ReactNode } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
import { IconNames } from '@blueprintjs/icons';
import * as constants from './constants';
import { TrafficLight } from '../traffic-light';
interface DealTicketEstimatesProps {
quoteName?: string;
price?: string;
estCloseOut?: string;
estMargin?: string;
fees?: string;
notionalSize?: string;
size?: string;
slippage?: string;
}
interface DataTitleProps {
children: ReactNode;
quoteName?: string;
}
export const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
<dt>
{children}
{quoteName && <small> ({quoteName})</small>}
</dt>
);
interface ValueTooltipProps {
value?: string;
children?: ReactNode;
description: string;
id?: string;
}
export const ValueTooltipRow = ({
value,
children,
description,
id,
}: ValueTooltipProps) => (
<dd className="flex gap-x-2 items-center">
{value || children}
<Tooltip align="center" description={description}>
<div className="cursor-help" id={id || ''} tabIndex={-1}>
<Icon
name={IconNames.ISSUE}
className="block rotate-180"
ariaLabel={description}
/>
</div>
</Tooltip>
</dd>
);
export const DealTicketEstimates = ({
price,
quoteName,
estCloseOut,
estMargin,
fees,
notionalSize,
size,
slippage,
}: DealTicketEstimatesProps) => (
<dl className="text-black dark:text-white">
{size && (
<div className="flex justify-between mb-2">
<DataTitle>{t('Contracts')}</DataTitle>
<ValueTooltipRow
value={size}
description={constants.CONTRACTS_MARGIN_TOOLTIP_TEXT}
id="contracts_tooltip_trigger"
/>
</div>
)}
{price && (
<div className="flex justify-between mb-2">
<DataTitle>{t('Est. Price')}</DataTitle>
<dd>{price}</dd>
</div>
)}
{notionalSize && (
<div className="flex justify-between mb-2">
<DataTitle quoteName={quoteName}>{t('Est. Position Size')}</DataTitle>
<ValueTooltipRow
value={notionalSize}
description={constants.NOTIONAL_SIZE_TOOLTIP_TEXT}
/>
</div>
)}
{fees && (
<div className="flex justify-between mb-2">
<DataTitle quoteName={quoteName}>{t('Est. Fees')}</DataTitle>
<ValueTooltipRow
value={fees}
description={constants.EST_FEES_TOOLTIP_TEXT}
/>
</div>
)}
{estMargin && (
<div className="flex justify-between mb-2">
<DataTitle quoteName={quoteName}>{t('Est. Margin')}</DataTitle>
<ValueTooltipRow
value={estMargin}
description={constants.EST_MARGIN_TOOLTIP_TEXT}
/>
</div>
)}
{estCloseOut && (
<div className="flex justify-between mb-2">
<DataTitle quoteName={quoteName}>{t('Est. Close out')}</DataTitle>
<ValueTooltipRow
value={estCloseOut}
description={constants.EST_CLOSEOUT_TOOLTIP_TEXT}
/>
</div>
)}
{slippage && (
<div className="flex justify-between mb-2">
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
<TrafficLight value={parseFloat(slippage)} q1={1} q2={5}>
{slippage}%
</TrafficLight>
</ValueTooltipRow>
</div>
)}
</dl>
);
@@ -1,149 +0,0 @@
import React, { useCallback, useState } from 'react';
import { BigNumber } from 'bignumber.js';
import { t } from '@vegaprotocol/react-helpers';
import {
SliderRoot,
SliderThumb,
SliderTrack,
SliderRange,
FormGroup,
} from '@vegaprotocol/ui-toolkit';
import { InputSetter } from '../input-setter';
interface DealTicketSizeInputProps {
step: number;
min: number;
max: number;
value: number;
onValueChange: (value: number) => void;
positionDecimalPlaces: number;
}
const getSizeLabel = (value: number): string => {
const MIN_LABEL = 'Min';
const MAX_LABEL = 'Max';
if (value === 0) {
return MIN_LABEL;
} else if (value === 100) {
return MAX_LABEL;
}
return `${value}%`;
};
export const DealTicketSizeInput = ({
value,
step,
min,
max,
onValueChange,
positionDecimalPlaces,
}: DealTicketSizeInputProps) => {
const sizeRatios = [0, 25, 50, 75, 100];
const [inputValue, setInputValue] = useState(value);
const onInputValueChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
let value = parseFloat(event.target.value);
const isLessThanMin = value < min;
const isMoreThanMax = value > max;
if (isLessThanMin) {
value = min;
} else if (isMoreThanMax) {
value = max;
}
if (value) {
onValueChange(value);
}
setInputValue(value);
},
[min, max, onValueChange, setInputValue]
);
const onButtonValueChange = (size: number) => {
const newVal = new BigNumber(size)
.decimalPlaces(positionDecimalPlaces)
.toNumber();
onValueChange(newVal);
setInputValue(newVal);
};
const onSliderValueChange = useCallback(
(value: number[]) => {
const val = value[0];
setInputValue(val);
onValueChange(val);
},
[onValueChange]
);
return (
<div>
<div className="flex justify-between text-black dark:text-white mb-2">
<span data-testid="min-label">{min}</span>
<span data-testid="max-label">{max}</span>
</div>
<SliderRoot
className="mb-2"
value={[value]}
onValueChange={onSliderValueChange}
step={step}
min={min}
max={max}
>
<SliderTrack className="bg-lightGrey dark:bg-offBlack">
<SliderRange className="!bg-black dark:!bg-white" />
</SliderTrack>
<SliderThumb />
</SliderRoot>
<div
data-testid="percentage-selector"
className="flex w-full justify-between text-black dark:text-white mb-6"
>
{sizeRatios.map((size, index) => {
const proportionalSize = size ? (size / 100) * max : min;
return (
<button
className="no-underline hover:underline text-blue"
onClick={() => onButtonValueChange(proportionalSize)}
type="button"
key={index}
>
{getSizeLabel(size)}
</button>
);
})}
</div>
<dl className="text-black dark:text-white">
<div className="flex items-center justify-between">
<dt>{t('Contracts')}</dt>
<dd className="flex justify-end w-full">
<FormGroup
hideLabel={true}
label="Enter Size"
labelFor="trade-size-input"
className="mb-1"
>
<InputSetter
id="input-order-size-market"
type="number"
step={step}
min={min}
max={max}
className="w-full"
value={inputValue}
onChange={onInputValueChange}
>
{inputValue}
</InputSetter>
</FormGroup>
</dd>
</div>
</dl>
</div>
);
};
@@ -1,55 +0,0 @@
import React from 'react';
import { DealTicketEstimates } from './deal-ticket-estimates';
import { DealTicketSizeInput } from './deal-ticket-size-input';
interface DealTicketSizeProps {
step: number;
min: number;
max: number;
size: number;
onSizeChange: (value: number) => void;
name: string;
quoteName: string;
price: string;
estCloseOut: string;
estMargin: string;
fees: string;
positionDecimalPlaces: number;
notionalSize: string;
}
export const DealTicketSize = ({
step,
min,
max,
price,
quoteName,
size,
onSizeChange,
estCloseOut,
positionDecimalPlaces,
fees,
notionalSize,
}: DealTicketSizeProps) => {
return max === 0 ? (
<p>Not enough balance to trade</p>
) : (
<div>
<DealTicketSizeInput
step={step}
min={min}
max={max}
value={size}
onValueChange={onSizeChange}
positionDecimalPlaces={positionDecimalPlaces}
/>
<DealTicketEstimates
quoteName={quoteName}
fees={fees}
estCloseOut={estCloseOut}
price={price}
notionalSize={notionalSize}
/>
</div>
);
};
@@ -1,104 +0,0 @@
import React, { useCallback, useState } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import * as constants from './constants';
import { TrafficLight } from '../traffic-light';
import { Dialog, Icon, Intent, Tooltip } from '@vegaprotocol/ui-toolkit';
import { InputSetter } from '../../components/input-setter';
import { IconNames } from '@blueprintjs/icons';
import { DataTitle, ValueTooltipRow } from './deal-ticket-estimates';
interface DealTicketSlippageProps {
step?: number;
min?: number;
max?: number;
value: number;
onValueChange(value: number): void;
}
export const DealTicketSlippage = ({
value,
step = 0.01,
min = 0,
max = 50,
onValueChange,
}: DealTicketSlippageProps) => {
const [isDialogVisible, setIsDialogVisible] = useState(false);
const onChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
const numericValue = parseFloat(value);
onValueChange(numericValue);
},
[onValueChange]
);
const toggleDialog = useCallback(() => {
setIsDialogVisible(!isDialogVisible);
}, [isDialogVisible]);
const formLabel = (
<label className="flex items-center mb-1">
<span className="mr-1">{t('Adjust slippage tolerance')}</span>
<Tooltip align="center" description={constants.EST_SLIPPAGE}>
<div className="cursor-help" tabIndex={-1}>
<Icon
name={IconNames.ISSUE}
className="block rotate-180"
ariaLabel={constants.EST_SLIPPAGE}
/>
</div>
</Tooltip>
</label>
);
return (
<>
<Dialog
open={isDialogVisible}
onChange={toggleDialog}
intent={Intent.None}
title={t('Transaction Settings')}
>
<div data-testid="slippage-dialog">
{formLabel}
<InputSetter
id="input-order-slippage"
isInputVisible
hasError={!value}
type="number"
step={step}
min={min}
max={max}
className="w-full"
value={value}
onChange={onChange}
>
{value}%
</InputSetter>
</div>
</Dialog>
<dl className="text-black dark:text-white">
<div className="flex justify-between mb-2">
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
<div className="flex">
<div className="mr-1">
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
<TrafficLight value={value} q1={1} q2={5}>
{value}%
</TrafficLight>
</ValueTooltipRow>
</div>
<button type="button" onClick={toggleDialog}>
<Icon
name={IconNames.COG}
className="block rotate-180"
ariaLabel={t('Override slippage value')}
/>
</button>
</div>
</div>
</dl>
</>
);
};
@@ -1,327 +0,0 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm, Controller } from 'react-hook-form';
import { Stepper } from '../stepper';
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
import { InputError } from '@vegaprotocol/ui-toolkit';
import { BigNumber } from 'bignumber.js';
import { MarketSelector } from '@vegaprotocol/deal-ticket';
import type { Order } from '@vegaprotocol/orders';
import { useVegaWallet, VegaTxStatus } from '@vegaprotocol/wallet';
import {
t,
addDecimalsFormatNumber,
toDecimal,
removeDecimal,
} from '@vegaprotocol/react-helpers';
import {
getDefaultOrder,
useOrderValidation,
useOrderSubmit,
getOrderDialogTitle,
getOrderDialogIntent,
getOrderDialogIcon,
OrderFeedback,
validateSize,
} from '@vegaprotocol/orders';
import { DealTicketSize } from './deal-ticket-size';
import MarketNameRenderer from '../simple-market-list/simple-market-renderer';
import SideSelector, { SIDE_NAMES } from './side-selector';
import ReviewTrade from './review-trade';
import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery';
import useOrderCloseOut from '../../hooks/use-order-closeout';
import useOrderMargin from '../../hooks/use-order-margin';
import useMaximumPositionSize from '../../hooks/use-maximum-position-size';
import useCalculateSlippage from '../../hooks/use-calculate-slippage';
import { Side, OrderType } from '@vegaprotocol/types';
import { DealTicketSlippage } from './deal-ticket-slippage';
interface DealTicketMarketProps {
market: DealTicketQuery_market;
partyData?: PartyBalanceQuery;
}
export const DealTicketSteps = ({
market,
partyData,
}: DealTicketMarketProps) => {
const navigate = useNavigate();
const setMarket = useCallback(
(marketId: string) => {
navigate(`/trading/${marketId}`);
},
[navigate]
);
const {
control,
handleSubmit,
watch,
setValue,
formState: { errors },
} = useForm<Order>({
mode: 'onChange',
defaultValues: getDefaultOrder(market),
});
const emptyString = ' - ';
const step = toDecimal(market.positionDecimalPlaces);
const orderType = watch('type');
const orderTimeInForce = watch('timeInForce');
const orderSide = watch('side');
const orderSize = watch('size');
const order = watch();
const { message: invalidText, isDisabled } = useOrderValidation({
market,
orderType,
orderTimeInForce,
fieldErrors: errors,
});
const { submit, transaction, finalizedOrder, Dialog } = useOrderSubmit();
const { keypair } = useVegaWallet();
const estMargin = useOrderMargin({
order,
market,
partyId: keypair?.pub || '',
});
const maxTrade = useMaximumPositionSize({
partyId: keypair?.pub || '',
accounts: partyData?.party?.accounts || [],
marketId: market.id,
settlementAssetId:
market.tradableInstrument.instrument.product.settlementAsset.id,
price: market?.depth?.lastTrade?.price,
order,
});
const estCloseOut = useOrderCloseOut({ order, market, partyData });
const slippage = useCalculateSlippage({ marketId: market.id, order });
const [slippageValue, setSlippageValue] = useState(
slippage ? parseFloat(slippage) : 0
);
const transactionStatus =
transaction.status === VegaTxStatus.Requested ||
transaction.status === VegaTxStatus.Pending
? 'pending'
: 'default';
useEffect(() => {
setSlippageValue(slippage ? parseFloat(slippage) : 0);
}, [slippage]);
const price = useMemo(() => {
if (slippage && market?.depth?.lastTrade?.price) {
const isLong = order.side === Side.SIDE_BUY;
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
parseFloat(slippage) / 100
);
return new BigNumber(market?.depth?.lastTrade?.price)
.multipliedBy(multiplier)
.toNumber();
}
return null;
}, [market?.depth?.lastTrade?.price, order.side, slippage]);
const formattedPrice =
price && addDecimalsFormatNumber(price, market.decimalPlaces);
const notionalSize = useMemo(() => {
if (price) {
const size = new BigNumber(price).multipliedBy(orderSize).toNumber();
return addDecimalsFormatNumber(size, market.decimalPlaces);
}
return null;
}, [market.decimalPlaces, orderSize, price]);
const fees = useMemo(() => {
if (estMargin?.fees && notionalSize) {
const percentage = new BigNumber(estMargin?.fees)
.dividedBy(notionalSize)
.multipliedBy(100)
.decimalPlaces(2)
.toString();
return `${estMargin.fees} (${percentage}%)`;
}
return null;
}, [estMargin?.fees, notionalSize]);
const max = useMemo(() => {
return new BigNumber(maxTrade)
.decimalPlaces(market.positionDecimalPlaces)
.toNumber();
}, [market.positionDecimalPlaces, maxTrade]);
const onSizeChange = useCallback(
(value: number) => {
const newVal = new BigNumber(value)
.decimalPlaces(market.positionDecimalPlaces)
.toString();
const isValid = validateSize(step)(newVal);
if (isValid !== 'step') {
setValue('size', newVal);
}
},
[market.positionDecimalPlaces, setValue, step]
);
const onSlippageChange = useCallback(
(value: number) => {
if (market?.depth?.lastTrade?.price) {
if (value) {
const isLong = order.side === Side.SIDE_BUY;
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
value / 100
);
const bestAskPrice = new BigNumber(market?.depth?.lastTrade?.price)
.multipliedBy(multiplier)
.decimalPlaces(market.decimalPlaces)
.toString();
setValue('price', bestAskPrice);
if (orderType === OrderType.TYPE_MARKET) {
setValue('type', OrderType.TYPE_LIMIT);
}
} else {
setValue('type', OrderType.TYPE_MARKET);
setValue('price', market?.depth?.lastTrade?.price);
}
}
setSlippageValue(value);
},
[
market.decimalPlaces,
market?.depth?.lastTrade?.price,
order.side,
orderType,
setValue,
]
);
const onSubmit = useCallback(
(order: Order) => {
if (transactionStatus !== 'pending') {
submit({
...order,
price:
order.price && removeDecimal(order.price, market.decimalPlaces),
size: removeDecimal(order.size, market.positionDecimalPlaces),
});
}
},
[
transactionStatus,
submit,
market.decimalPlaces,
market.positionDecimalPlaces,
]
);
const steps = [
{
label: t('Select Market'),
component: (
<MarketSelector
market={market}
setMarket={setMarket}
ItemRenderer={MarketNameRenderer}
/>
),
value: market.tradableInstrument.instrument.name,
},
{
label: t('Select Direction'),
component: (
<Controller
name="side"
control={control}
render={({ field }) => (
<SideSelector value={field.value} onSelect={field.onChange} />
)}
/>
),
value: SIDE_NAMES[orderSide] || '',
},
{
label: t('Choose Position Size'),
component:
max !== null ? (
<>
<DealTicketSize
step={step}
min={step}
max={max}
onSizeChange={onSizeChange}
size={new BigNumber(orderSize).toNumber()}
name="size"
price={formattedPrice || emptyString}
positionDecimalPlaces={market.positionDecimalPlaces}
quoteName={
market.tradableInstrument.instrument.product.settlementAsset
.symbol
}
notionalSize={notionalSize || emptyString}
estCloseOut={estCloseOut}
fees={fees || emptyString}
estMargin={estMargin?.margin || emptyString}
/>
<DealTicketSlippage
value={slippageValue}
onValueChange={onSlippageChange}
/>
</>
) : (
'loading...'
),
value: orderSize,
},
{
label: t('Review Trade'),
component: (
<div className="mb-8">
{invalidText && (
<div className="mb-2">
<InputError data-testid="dealticket-error-message">
{invalidText}
</InputError>
</div>
)}
<ReviewTrade
market={market}
isDisabled={isDisabled}
transactionStatus={transactionStatus}
order={order}
estCloseOut={estCloseOut}
estMargin={estMargin?.margin || emptyString}
price={formattedPrice || emptyString}
quoteName={
market.tradableInstrument.instrument.product.settlementAsset
.symbol
}
notionalSize={notionalSize || emptyString}
fees={fees || emptyString}
slippage={slippageValue}
/>
<Dialog
title={getOrderDialogTitle(finalizedOrder?.status)}
intent={getOrderDialogIntent(finalizedOrder?.status)}
icon={getOrderDialogIcon(finalizedOrder?.status)}
>
<OrderFeedback transaction={transaction} order={finalizedOrder} />
</Dialog>
</div>
),
disabled: true,
},
];
return (
<form onSubmit={handleSubmit(onSubmit)} className="px-2 py-4">
<Stepper steps={steps} />
</form>
);
};
@@ -1,2 +0,0 @@
export * from './deal-ticket-container';
export * from './deal-ticket-size';
@@ -1,126 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import {
Button,
KeyValueTable,
KeyValueTableRow,
} from '@vegaprotocol/ui-toolkit';
import * as React from 'react';
import classNames from 'classnames';
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
import type { Order } from '@vegaprotocol/orders';
import { SIDE_NAMES } from './side-selector';
import SimpleMarketExpires from '../simple-market-list/simple-market-expires';
import { gql, useQuery } from '@apollo/client';
import type {
MarketTags,
MarketTagsVariables,
} from './__generated__/MarketTags';
import { DealTicketEstimates } from './deal-ticket-estimates';
import { Side } from '@vegaprotocol/types';
export const MARKET_TAGS_QUERY = gql`
query MarketTags($marketId: ID!) {
market(id: $marketId) {
tradableInstrument {
instrument {
metadata {
tags
}
}
}
}
}
`;
interface Props {
market: DealTicketQuery_market;
isDisabled: boolean;
transactionStatus?: string;
order: Order;
estCloseOut: string;
estMargin: string;
quoteName: string;
price: string;
fees: string;
notionalSize: string;
slippage: number;
}
export default ({
isDisabled,
market,
order,
transactionStatus,
estCloseOut,
quoteName,
fees,
price,
notionalSize,
slippage,
}: Props) => {
const { data: tagsData } = useQuery<MarketTags, MarketTagsVariables>(
MARKET_TAGS_QUERY,
{
variables: { marketId: market.id },
}
);
return (
<div className="mb-8 text-black dark:text-white" data-testid="review-trade">
<KeyValueTable>
<KeyValueTableRow noBorder>
<div className="flex flex-none gap-x-2 items-center">
<div
className={classNames(
{
'buyButton dark:buyButtonDark': order.side === Side.SIDE_BUY,
'sellButton dark:sellButtonDark':
order.side === Side.SIDE_SELL,
},
'px-2 py-1 inline text-ui-small'
)}
>
{SIDE_NAMES[order.side]}
</div>
<div>{market.tradableInstrument.instrument.product.quoteName}</div>
<div>
{tagsData?.market?.tradableInstrument.instrument.metadata
.tags && (
<SimpleMarketExpires
tags={
tagsData?.market.tradableInstrument.instrument.metadata.tags
}
/>
)}
</div>
</div>
<div className="text-blue">
{`@ ${price} `}
<span className="text-ui-small inline">(EST)</span>
</div>
</KeyValueTableRow>
</KeyValueTable>
<DealTicketEstimates
size={order.size}
quoteName={quoteName}
fees={fees}
estCloseOut={estCloseOut}
notionalSize={notionalSize}
slippage={slippage.toString()}
/>
<div className="mt-12 max-w-sm">
<Button
fill={true}
type="submit"
disabled={transactionStatus === 'pending' || isDisabled}
data-testid="place-order"
rightIcon="arrow-top-right"
>
{transactionStatus === 'pending' ? t('Pending...') : t('Submit')}
</Button>
</div>
</div>
);
};
@@ -1,60 +0,0 @@
import React from 'react';
import classNames from 'classnames';
import { FormGroup } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import { Side } from '@vegaprotocol/types';
interface SideSelectorProps {
value: Side;
onSelect: (side: Side) => void;
}
export const SIDE_NAMES: Record<Side, string> = {
[Side.SIDE_BUY]: t('Long'),
[Side.SIDE_SELL]: t('Short'),
};
export default ({ value, onSelect }: SideSelectorProps) => {
return (
<FormGroup
label={t('Direction')}
labelFor="order-side-toggle"
hideLabel={true}
>
<fieldset
className="w-full grid md:grid-cols-2 gap-4"
id="order-side-toggle"
>
<button
aria-label={t('Open long position')}
type="button"
className={classNames(
'px-8 py-2',
'buyButton hover:buyButton dark:buyButtonDark dark:hover:buyButtonDark',
{ selected: value === Side.SIDE_BUY }
)}
onClick={() => onSelect(Side.SIDE_BUY)}
>
{t('Long')}
</button>
<button
aria-label={t('Open short position')}
type="button"
className={classNames(
'px-8 py-2',
'sellButton hover:sellButton dark:sellButtonDark dark:hover:sellButtonDark',
{ selected: value === Side.SIDE_SELL }
)}
onClick={() => onSelect(Side.SIDE_SELL)}
>
{t('Short')}
</button>
<div className="md:col-span-2 text-black dark:text-white text-ui-small">
{t(
'Trading derivatives allows you to make a profit or loss regardless of whether the market you are trading goes up or down. If you open a "long" position, you will make a profit if the price of your chosen market goes up, and you will make a profit for "short" positions when the price goes down.'
)}
</div>
</fieldset>
</FormGroup>
);
};
@@ -1,17 +0,0 @@
query Deposits {
assetsConnection {
edges {
node {
id
name
symbol
decimals
source {
... on ERC20 {
contractAddress
}
}
}
}
}
}
@@ -1,72 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { AssetStatus } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: Deposits
// ====================================================
export interface Deposits_assetsConnection_edges_node_source_BuiltinAsset {
__typename: "BuiltinAsset";
}
export interface Deposits_assetsConnection_edges_node_source_ERC20 {
__typename: "ERC20";
/**
* The address of the ERC20 contract
*/
contractAddress: string;
}
export type Deposits_assetsConnection_edges_node_source = Deposits_assetsConnection_edges_node_source_BuiltinAsset | Deposits_assetsConnection_edges_node_source_ERC20;
export interface Deposits_assetsConnection_edges_node {
__typename: "Asset";
/**
* The ID of the asset
*/
id: string;
/**
* The full name of the asset (e.g: Great British Pound)
*/
name: string;
/**
* The symbol of the asset (e.g: GBP)
*/
symbol: string;
/**
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
*/
decimals: number;
/**
* The status of the asset in the Vega network
*/
status: AssetStatus;
/**
* The origin source of the asset (e.g: an ERC20 asset)
*/
source: Deposits_assetsConnection_edges_node_source;
}
export interface Deposits_assetsConnection_edges {
__typename: "AssetEdge";
node: Deposits_assetsConnection_edges_node;
}
export interface Deposits_assetsConnection {
__typename: "AssetsConnection";
/**
* The assets
*/
edges: (Deposits_assetsConnection_edges | null)[] | null;
}
export interface Deposits {
/**
* The list of all assets in use in the Vega network or the specified asset if ID is provided
*/
assetsConnection: Deposits_assetsConnection | null;
}
@@ -1,57 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type DepositsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type DepositsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } } } | null> | null } | null };
export const DepositsDocument = gql`
query Deposits {
assetsConnection {
edges {
node {
id
name
symbol
decimals
source {
... on ERC20 {
contractAddress
}
}
}
}
}
}
`;
/**
* __useDepositsQuery__
*
* To run a query within a React component, call `useDepositsQuery` and pass it any options that fit your needs.
* When your component renders, `useDepositsQuery` 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 } = useDepositsQuery({
* variables: {
* },
* });
*/
export function useDepositsQuery(baseOptions?: Apollo.QueryHookOptions<DepositsQuery, DepositsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<DepositsQuery, DepositsQueryVariables>(DepositsDocument, options);
}
export function useDepositsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<DepositsQuery, DepositsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<DepositsQuery, DepositsQueryVariables>(DepositsDocument, options);
}
export type DepositsQueryHookResult = ReturnType<typeof useDepositsQuery>;
export type DepositsLazyQueryHookResult = ReturnType<typeof useDepositsLazyQuery>;
export type DepositsQueryResult = Apollo.QueryResult<DepositsQuery, DepositsQueryVariables>;
@@ -1,61 +0,0 @@
import { gql, useQuery } from '@apollo/client';
import { DepositManager } from '@vegaprotocol/deposits';
import { getEnabledAssets, t } from '@vegaprotocol/react-helpers';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { Web3Container } from '@vegaprotocol/web3';
import type { Deposits } from './__generated__/Deposits';
const DEPOSITS_QUERY = gql`
query Deposits {
assetsConnection {
edges {
node {
id
name
symbol
decimals
status
source {
... on ERC20 {
contractAddress
}
}
}
}
}
}
`;
/**
* Fetches data required for the Deposit page
*/
export const DepositContainer = () => {
const { VEGA_ENV } = useEnvironment();
const { keypair } = useVegaWallet();
const { data, loading, error } = useQuery<Deposits>(DEPOSITS_QUERY, {
variables: { partyId: keypair?.pub },
skip: !keypair?.pub,
});
const assets = getEnabledAssets(data);
return (
<AsyncRenderer<Deposits> data={data} loading={loading} error={error}>
{assets.length ? (
<Web3Container>
<DepositManager
assets={assets}
isFaucetable={VEGA_ENV !== Networks.MAINNET}
/>
</Web3Container>
) : (
<Splash>
<p>{t('No assets on this network')}</p>
</Splash>
)}
</AsyncRenderer>
);
};
@@ -1 +0,0 @@
export * from './deposits';
@@ -1,35 +0,0 @@
import React, { useContext } from 'react';
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
import Logo from './logo';
import { VegaWalletConnectButton } from '../vega-wallet-connect-button';
import LocalContext from '../../context/local-context';
const Header = () => {
const {
vegaWalletDialog: { setConnect, setManage },
theme,
toggleTheme,
} = useContext(LocalContext);
return (
<div
className="flex items-stretch pr-6 py-6 bg-black text-neutral-400"
data-testid="header"
>
<Logo />
<div className="flex items-center gap-2 ml-auto relative z-10">
<VegaWalletConnectButton
setConnectDialog={setConnect}
setManageDialog={setManage}
/>
<ThemeSwitcher
theme={theme}
onToggle={toggleTheme}
className="-my-4"
fixedBg="dark"
/>
</div>
</div>
);
};
export default Header;
@@ -1 +0,0 @@
export * from './input-setter';
@@ -1,64 +0,0 @@
import React from 'react';
import { render, waitFor, fireEvent } from '@testing-library/react';
import { InputSetter } from './index';
describe('InputSetter Component', () => {
it('should show the correct value and visibility based on props', async () => {
const value = 'Hello';
const isInputVisible = true;
const { getByRole } = await render(
<InputSetter
id="input-order-size-market"
type="number"
className="w-full"
value={value}
isInputVisible={isInputVisible}
onChange={() => false}
/>
);
const input = getByRole('spinbutton');
const btn = getByRole('button');
expect(input).toHaveAttribute('value', value);
expect(btn).toBeTruthy();
});
it('should toggle the visibility of the input when the button is clicked', async () => {
const value = 'Hello';
const isInputVisible = true;
const { getByRole } = await render(
<InputSetter
id="input-order-size-market"
type="number"
className="w-full"
value={value}
isInputVisible={isInputVisible}
onChange={() => false}
/>
);
const btn = getByRole('button');
fireEvent.click(btn);
await waitFor(() => getByRole('button'));
expect(getByRole('button')).toHaveTextContent(value);
});
it('should toggle the visibility of the input when the enter key is pressed', async () => {
const value = 'Hello';
const isInputVisible = true;
const { getByRole } = await render(
<InputSetter
id="input-order-size-market"
name="input-order-size-market"
type="number"
className="w-full"
value={value}
isInputVisible={isInputVisible}
onChange={() => false}
/>
);
fireEvent.keyDown(getByRole('spinbutton'), {
key: 'Enter',
});
await waitFor(() => getByRole('button'));
expect(getByRole('button')).toHaveTextContent(value);
});
});
@@ -1,55 +0,0 @@
import React, { useCallback, useState } from 'react';
import type { ReactNode } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import { Input } from '@vegaprotocol/ui-toolkit';
import type { InputProps } from '@vegaprotocol/ui-toolkit';
interface InputSetterProps {
buttonLabel?: string;
isInputVisible?: boolean;
children?: ReactNode;
}
export const InputSetter = ({
buttonLabel = t('set'),
isInputVisible = false,
children,
...props
}: InputSetterProps & InputProps) => {
const [isInputToggled, setIsInputToggled] = useState(isInputVisible);
const toggleInput = useCallback(() => {
setIsInputToggled(!isInputToggled);
}, [isInputToggled]);
const onInputEnter = useCallback(
(event: React.KeyboardEvent) => {
if (event.key === 'Enter') {
event.stopPropagation();
toggleInput();
}
},
[toggleInput]
);
return isInputToggled ? (
<div className="flex items-center">
<Input {...props} onKeyDown={onInputEnter} />
<button
type="button"
className="no-underline hover:underline text-blue ml-2"
onClick={toggleInput}
>
{buttonLabel}
</button>
</div>
) : (
<button
type="button"
className="no-underline hover:underline text-blue py-1.5"
onClick={toggleInput}
>
{children || props.value}
</button>
);
};
@@ -1,32 +0,0 @@
import { routerConfig } from '../../routes';
import { NavLink } from 'react-router-dom';
import { NavItem } from './nav-item';
import React from 'react';
interface NavProps {
className?: string;
tabs?: boolean;
}
export const Nav = ({ className, tabs = false }: NavProps) => {
return (
<nav role={tabs ? 'tablist' : 'menu'} className={className}>
{routerConfig
.filter((r) => r.isNavItem)
.map((r) => (
<NavLink
role={tabs ? 'tab' : 'menuitem'}
key={r.name}
to={r.path}
className={({ isActive }) =>
`text-base block md:mb-10 px-12 md:text-black md:dark:text-white ${
isActive && 'text-white md:text-blue md:dark:text-blue'
}`
}
>
<NavItem iconName={r.icon} label={r.text} />
</NavLink>
))}
</nav>
);
};
@@ -1,40 +0,0 @@
import * as React from 'react';
import { AccountsContainer } from '@vegaprotocol/accounts';
import { t } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { Tabs, Tab } from '@vegaprotocol/ui-toolkit';
import { OrderListContainer } from '@vegaprotocol/orders';
import { PositionsContainer } from '@vegaprotocol/positions';
import { FillsContainer } from '@vegaprotocol/fills';
import ConnectWallet from '../wallet-connector';
import { DepositContainer } from '../deposits';
export const Portfolio = () => {
const { keypair } = useVegaWallet();
if (!keypair) {
return (
<section className="xl:w-1/2">
<ConnectWallet />
</section>
);
}
return (
<Tabs>
<Tab id="assets" name={t('Assets')}>
<AccountsContainer />
</Tab>
<Tab id="positions" name={t('Positions')}>
<PositionsContainer />
</Tab>
<Tab id="orders" name={t('Orders')}>
<OrderListContainer />
</Tab>
<Tab id="fills" name={t('Fills')}>
<FillsContainer />
</Tab>
<Tab id="deposits" name={t('Deposits')}>
<DepositContainer />
</Tab>
</Tabs>
);
};
@@ -1,5 +0,0 @@
subscription CandleLive($marketId: ID!) {
candles(marketId: $marketId, interval: INTERVAL_I1H) {
close
}
}
@@ -1,39 +0,0 @@
fragment SimpleMarketDataFields on ObservableMarketData {
marketId
marketState
}
query SimpleMarkets($CandleSince: String!) {
markets {
id
state
tradableInstrument {
instrument {
name
code
metadata {
tags
}
product {
__typename
... on Future {
quoteName
settlementAsset {
symbol
}
}
}
}
}
candles(interval: INTERVAL_I1H, since: $CandleSince) {
open
close
}
}
}
subscription SimpleMarketDataSub($marketIds: [ID!]!) {
marketsData(marketIds: $marketIds) {
...SimpleMarketDataFields
}
}
@@ -1,43 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type CandleLiveSubscriptionVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type CandleLiveSubscription = { __typename?: 'Subscription', candles: { __typename?: 'Candle', close: string } };
export const CandleLiveDocument = gql`
subscription CandleLive($marketId: ID!) {
candles(marketId: $marketId, interval: INTERVAL_I1H) {
close
}
}
`;
/**
* __useCandleLiveSubscription__
*
* To run a query within a React component, call `useCandleLiveSubscription` and pass it any options that fit your needs.
* When your component renders, `useCandleLiveSubscription` 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 subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useCandleLiveSubscription({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useCandleLiveSubscription(baseOptions: Apollo.SubscriptionHookOptions<CandleLiveSubscription, CandleLiveSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<CandleLiveSubscription, CandleLiveSubscriptionVariables>(CandleLiveDocument, options);
}
export type CandleLiveSubscriptionHookResult = ReturnType<typeof useCandleLiveSubscription>;
export type CandleLiveSubscriptionResult = Apollo.SubscriptionResult<CandleLiveSubscription>;
@@ -1,115 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type SimpleMarketDataFieldsFragment = { __typename?: 'ObservableMarketData', marketId: string, marketState: Types.MarketState };
export type SimpleMarketsQueryVariables = Types.Exact<{
CandleSince: Types.Scalars['String'];
}>;
export type SimpleMarketsQuery = { __typename?: 'Query', markets?: Array<{ __typename?: 'Market', id: string, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', symbol: string } } } }, candles?: Array<{ __typename?: 'Candle', open: string, close: string } | null> | null }> | null };
export type SimpleMarketDataSubSubscriptionVariables = Types.Exact<{
marketIds: Array<Types.Scalars['ID']> | Types.Scalars['ID'];
}>;
export type SimpleMarketDataSubSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', marketId: string, marketState: Types.MarketState }> };
export const SimpleMarketDataFieldsFragmentDoc = gql`
fragment SimpleMarketDataFields on ObservableMarketData {
marketId
marketState
}
`;
export const SimpleMarketsDocument = gql`
query SimpleMarkets($CandleSince: String!) {
markets {
id
state
tradableInstrument {
instrument {
name
code
metadata {
tags
}
product {
__typename
... on Future {
quoteName
settlementAsset {
symbol
}
}
}
}
}
candles(interval: INTERVAL_I1H, since: $CandleSince) {
open
close
}
}
}
`;
/**
* __useSimpleMarketsQuery__
*
* To run a query within a React component, call `useSimpleMarketsQuery` and pass it any options that fit your needs.
* When your component renders, `useSimpleMarketsQuery` 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 } = useSimpleMarketsQuery({
* variables: {
* CandleSince: // value for 'CandleSince'
* },
* });
*/
export function useSimpleMarketsQuery(baseOptions: Apollo.QueryHookOptions<SimpleMarketsQuery, SimpleMarketsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<SimpleMarketsQuery, SimpleMarketsQueryVariables>(SimpleMarketsDocument, options);
}
export function useSimpleMarketsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<SimpleMarketsQuery, SimpleMarketsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<SimpleMarketsQuery, SimpleMarketsQueryVariables>(SimpleMarketsDocument, options);
}
export type SimpleMarketsQueryHookResult = ReturnType<typeof useSimpleMarketsQuery>;
export type SimpleMarketsLazyQueryHookResult = ReturnType<typeof useSimpleMarketsLazyQuery>;
export type SimpleMarketsQueryResult = Apollo.QueryResult<SimpleMarketsQuery, SimpleMarketsQueryVariables>;
export const SimpleMarketDataSubDocument = gql`
subscription SimpleMarketDataSub($marketIds: [ID!]!) {
marketsData(marketIds: $marketIds) {
...SimpleMarketDataFields
}
}
${SimpleMarketDataFieldsFragmentDoc}`;
/**
* __useSimpleMarketDataSubSubscription__
*
* To run a query within a React component, call `useSimpleMarketDataSubSubscription` and pass it any options that fit your needs.
* When your component renders, `useSimpleMarketDataSubSubscription` 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 subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useSimpleMarketDataSubSubscription({
* variables: {
* marketIds: // value for 'marketIds'
* },
* });
*/
export function useSimpleMarketDataSubSubscription(baseOptions: Apollo.SubscriptionHookOptions<SimpleMarketDataSubSubscription, SimpleMarketDataSubSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<SimpleMarketDataSubSubscription, SimpleMarketDataSubSubscriptionVariables>(SimpleMarketDataSubDocument, options);
}
export type SimpleMarketDataSubSubscriptionHookResult = ReturnType<typeof useSimpleMarketDataSubSubscription>;
export type SimpleMarketDataSubSubscriptionResult = Apollo.SubscriptionResult<SimpleMarketDataSubSubscription>;
@@ -1,199 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
import { MarketState } from '@vegaprotocol/types';
import { IS_MARKET_TRADABLE } from '../../constants';
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
import colors from 'tailwindcss/colors';
export const STATES_FILTER = [
{ value: 'all', text: t('All') },
{ value: MarketState.STATE_ACTIVE, text: t('Active') },
{ value: MarketState.STATE_CANCELLED, text: t('Cancelled') },
{ value: MarketState.STATE_CLOSED, text: t('Closed') },
{ value: MarketState.STATE_PENDING, text: t('Pending') },
{ value: MarketState.STATE_PROPOSED, text: t('Proposed') },
{ value: MarketState.STATE_REJECTED, text: t('Rejected') },
{ value: MarketState.STATE_SETTLED, text: t('Settled') },
{ value: MarketState.STATE_SUSPENDED, text: t('Suspended') },
{ value: MarketState.STATE_TRADING_TERMINATED, text: t('TradingTerminated') },
];
export const agGridLightVariables = `
.ag-theme-balham {
--ag-row-border-color: ${theme.colors.transparent};
--ag-row-hover-color: ${theme.colors.transparent};
--ag-border-color: ${theme.colors.transparent};
--ag-font-size: 15px;
}
.ag-theme-balham .ag-header-cell{
padding-left: 0;
padding-right: 0;
}
.ag-theme-balham .ag-cell{
padding-left: 0.2rem;
padding-right: 0.2rem;
}
.ag-theme-balham .ag-cell.overflow-visible{
overflow: visible;
}
.ag-theme-balham .ag-row-hover:not(.mobile) {
--ag-row-border-color: ${colors.black};
}
.ag-theme-balham .ag-row-hover .icon-green-hover {
fill: ${theme.colors.darkerGreen};
}
.ag-theme-balham [col-id="status"] .ag-header-cell-label,
.ag-theme-balham [col-id="asset"] .ag-header-cell-label,
.ag-theme-balham [col-id="change"] .ag-header-cell-label{
justify-content: center;
}
.ag-theme-balham .ag-header-row .ag-header-cell:first-child,
.ag-theme-balham .ag-row.mobile .ag-cell:first-child{
padding-left: 0;
}
.ag-theme-balham .ag-ltr .ag-header-cell::after, .ag-theme-balham .ag-ltr .ag-header-group-cell::after {
right: 0;
}
.ag-theme-balham .ag-header-cell::after{
width: 0;
}
.ag-theme-balham .ag-header{
border-bottom-width: 0;
border-bottom: none;
}
.ag-theme-balham .ag-has-focus .ag-row.ag-row-focus{
border: 1px solid #0091ea;
}
.ag-theme-balham .ag-has-focus .ag-row.ag-row-focus .ag-cell-focus {
outline: none;
border-width: 0;
}
.ag-theme-balham .ag-header-label-icon .ag-icon{
font-family: unset;
font-size: 20px;
font-weight: 600;
position: relative;
height: 20px;
line-height: 20px;
-moz-osx-font-smoothing: unset;
}
.ag-theme-balham .ag-icon::before{
display: inline-block;
font-family: Arial;
font-size: 20px;
line-height: 20px;
height: 20px;
content: "⬥";
background: linear-gradient(0deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 49%, rgba(0,0,0,0) 49%, rgba(0,0,0,0) 60%, rgba(0,0,0,0.54) 60%, rgba(0,0,0,0.54) 100%);
background: -moz-linear-gradient(-90deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 54%, rgba(0,0,0,0) 54%, rgba(0,0,0,0) 66%, rgba(0,0,0,0.54) 66%, rgba(0,0,0,0.54) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.ag-theme-balham .ag-icon-desc::before{
background: linear-gradient(0deg, #000 0%, #000 49%, rgba(0,0,0,0) 49%, rgba(0,0,0,0) 60%, rgba(0,0,0,0.54) 60%, rgba(0,0,0,0.54) 100%);
background: -moz-linear-gradient(-90deg, #000 0%, #000 54%, rgba(0,0,0,0) 54%, rgba(0,0,0,0) 66%, rgba(0,0,0,0.54) 66%, rgba(0,0,0,0.54) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.ag-theme-balham .ag-icon-asc::before{
background: linear-gradient(0deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 49%, rgba(0,0,0,0) 49%, rgba(0,0,0,0) 60%, #000 60%, #000 100%);
background: -moz-linear-gradient(-90deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 54%, rgba(0,0,0,0) 54%, rgba(0,0,0,0) 66%, #000 66%, #000 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
`;
export const agGridDarkVariables = `
.ag-theme-balham-dark {
--ag-background-color: ${theme.colors.transparent};
--ag-row-border-color: ${theme.colors.transparent};
--ag-row-hover-color: ${theme.colors.transparent};
--ag-odd-row-background-color: ${theme.colors.transparent};
--ag-header-background-color: ${theme.colors.transparent};
--ag-font-size: 15px;
}
.ag-theme-balham-dark .ag-header-cell{
padding-left: 0;
padding-right: 0;
}
.ag-theme-balham-dark .ag-cell{
padding-left: 0.2rem;
padding-right: 0.2rem;
}
.ag-theme-balham-dark .ag-cell.overflow-visible{
overflow: visible;
}
.ag-theme-balham-dark .ag-row-hover:not(.mobile){
--ag-row-border-color: ${colors.white};
}
.ag-theme-balham-dark .ag-row-hover .icon-green-hover {
fill: ${theme.colors.lightGreen};
}
.ag-theme-balham-dark [col-id="status"] .ag-header-cell-label,
.ag-theme-balham-dark [col-id="asset"] .ag-header-cell-label,
.ag-theme-balham-dark [col-id="change"] .ag-header-cell-label{
justify-content: center;
}
.ag-theme-balham-dark .ag-header-row .ag-header-cell:first-child,
.ag-theme-balham-dark .ag-row.mobile .ag-cell:first-child{
padding-left: 0;
}
.ag-theme-balham-dark .ag-header-row .ag-header-cell:first-child{
padding-left: 0;
}
.ag-theme-balham-dark .ag-header-cell::after{
width: 0;
}
.ag-theme-balham-dark .ag-header{
border-bottom-width: 0;
border-bottom: none;
}
.ag-theme-balham-dark .ag-has-focus .ag-row.ag-row-focus{
border: 1px solid #0091ea;
}
.ag-theme-balham-dark .ag-has-focus .ag-row.ag-row-focus .ag-cell-focus {
outline: none;
border-width: 0;
}
.ag-theme-balham-dark .ag-header-label-icon .ag-icon{
font-family: unset;
font-size: 20px;
font-weight: 600;
position: relative;
height: 20px;
line-height: 20px;
-moz-osx-font-smoothing: unset;
}
.ag-theme-balham-dark .ag-icon::before{
display: inline-block;
font-family: Arial;
font-size: 20px;
line-height: 20px;
height: 20px;
content: "⬥";
background: linear-gradient(0deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 49%, rgba(0,0,0,0) 49%, rgba(0,0,0,0) 60%, rgba(245, 245, 245, 0.64) 60%, rgba(245, 245, 245, 0.64) 100%);
background: -moz-linear-gradient(-90deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 54%, rgba(0,0,0,0) 54%, rgba(0,0,0,0) 66%, rgba(245, 245, 245, 0.64) 66%, rgba(245, 245, 245, 0.64) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-webkit-font-smoothing: antialiased;
}
.ag-theme-balham-dark .ag-icon-desc::before{
background: linear-gradient(0deg, #fff 0%, #fff 49%, rgba(0,0,0,0) 49%, rgba(0,0,0,0) 60%, rgba(245, 245, 245, 0.64) 60%, rgba(245, 245, 245, 0.64) 100%);
background: -moz-linear-gradient(-90deg, #fff 0%, #fff 54%, rgba(0,0,0,0) 54%, rgba(0,0,0,0) 66%, rgba(245, 245, 245, 0.64) 66%, rgba(245, 245, 245, 0.64) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.ag-theme-balham-dark .ag-icon-asc::before{
background: linear-gradient(0deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 49%, rgba(0,0,0,0) 49%, rgba(0,0,0,0) 60%, #fff 60%, #fff 100%);
background: -moz-linear-gradient(-90deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 54%, rgba(0,0,0,0) 54%, rgba(0,0,0,0) 66%, #fff 66%, #fff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
`;
export const ROW_CLASS_RULES = {
'cursor-pointer': ({ data }: { data: SimpleMarkets_markets }) =>
IS_MARKET_TRADABLE(data || {}),
};
export const LARGE_SCREENS = ['xl', 'xxl'];
@@ -1,221 +0,0 @@
import * as React from 'react';
import { act } from 'react-dom/test-utils';
import {
render,
screen,
waitFor,
cleanup,
getAllByRole,
fireEvent,
} from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { BrowserRouter } from 'react-router-dom';
import { MarketState } from '@vegaprotocol/types';
import SimpleMarketList from './simple-market-list';
import { MARKETS_QUERY } from './data-provider';
import type {
SimpleMarkets_markets,
SimpleMarkets,
} from './__generated__/SimpleMarkets';
import type { SimpleMarketDataSub_marketData } from './__generated__/SimpleMarketDataSub';
const mockedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockedNavigate,
useParams: () => ({}),
}));
jest.mock('date-fns', () => ({
subDays: () => new Date('2022-06-02T11:11:21.721Z'),
}));
let updateMock: ({
delta,
}: {
delta: SimpleMarketDataSub_marketData;
}) => boolean;
let mockData = [
{
id: '1',
name: 'Market 1',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'tUSD',
},
},
metadata: {
tags: [],
},
},
},
},
{
id: '2',
name: 'Market 2',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'ETH',
},
},
metadata: {
tags: [],
},
},
},
},
] as unknown as SimpleMarkets_markets[];
const mockUseDataProvider = ({ update }: { update: () => boolean }) => {
updateMock = update;
return { data: mockData, loading: false, error: false };
};
jest.mock('@vegaprotocol/react-helpers', () => ({
...jest.requireActual('@vegaprotocol/react-helpers'),
useDataProvider: jest.fn((args) => mockUseDataProvider(args)),
}));
const mockIsTradable = jest.fn((_arg) => true);
jest.mock('../../constants', () => ({
...jest.requireActual('../../constants'),
IS_MARKET_TRADABLE: jest.fn((arg) => mockIsTradable(arg)),
}));
describe('SimpleMarketList', () => {
afterEach(() => {
jest.clearAllMocks();
cleanup();
});
const mocks: MockedResponse<SimpleMarkets> = {
request: {
query: MARKETS_QUERY,
variables: {
CandleSince: '2022-06-02T11:11:21.721Z',
},
},
result: {
data: { markets: mockData },
},
};
it('should be properly rendered with some data', async () => {
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(
document.querySelector('.ag-center-cols-container')
).toBeInTheDocument();
});
await waitFor(() => {
const container = document.querySelector('.ag-center-cols-container');
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
});
});
it('update should return proper boolean value', async () => {
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(
document.querySelector('.ag-center-cols-container')
).toBeInTheDocument();
});
await expect(
updateMock({
delta: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '2',
state: MarketState.STATE_ACTIVE,
},
},
})
).toEqual(true);
await expect(
updateMock({
delta: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '2',
state: MarketState.STATE_SUSPENDED,
},
},
})
).toEqual(false);
});
it('click on row should be properly handled', async () => {
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(
document.querySelector('.ag-center-cols-container')
).toBeInTheDocument();
});
mockIsTradable.mockClear();
const container = document.querySelector('.ag-center-cols-container');
const firstRow = getAllByRole(container as HTMLDivElement, 'row')[0];
expect(firstRow).toHaveAttribute('row-id', '1');
fireEvent.click(firstRow);
await waitFor(() => {
expect(mockIsTradable).toHaveBeenCalledWith({
...mockData[0],
percentChange: '-',
});
expect(mockedNavigate).toHaveBeenCalledWith(`/trading/${mockData[0].id}`);
});
});
it('should be properly renderer as empty', async () => {
mockData = [];
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(screen.getByText('No data to display')).toBeInTheDocument();
});
});
});
@@ -1,36 +0,0 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import type { MarketNames_markets } from '@vegaprotocol/deal-ticket';
import { MarketState } from '@vegaprotocol/types';
import MarketNameRenderer from './simple-market-renderer';
describe('SimpleMarketRenderer', () => {
const market = {
id: 'marketId',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
code: 'Market code',
name: 'Market Name',
product: {
quoteName: 'Quote name',
},
metadata: {
tags: null,
},
},
},
} as MarketNames_markets;
it('should properly render not mobile', () => {
render(<MarketNameRenderer market={market} isMobile={false} />);
expect(screen.getByText('Market Name')).toBeInTheDocument();
expect(screen.getByText('Quote name')).toBeInTheDocument();
});
it('should properly render mobile', () => {
render(<MarketNameRenderer market={market} isMobile={true} />);
expect(screen.getByText('Market code')).toBeInTheDocument();
expect(screen.getByText('Quote name')).toBeInTheDocument();
});
});
@@ -1,34 +0,0 @@
import React from 'react';
import classNames from 'classnames';
import type { MarketNames_markets } from '@vegaprotocol/deal-ticket';
import SimpleMarketExpires from './simple-market-expires';
interface Props {
market: MarketNames_markets;
isMobile?: boolean;
}
const MarketNameRenderer = ({ market, isMobile }: Props) => {
return (
<div className="flex h-full items-center grid grid-rows-2 grid-flow-col gap-x-2 md:gap-x-4 gap-y-0 grid-cols-[min-content,1fr,1fr] md:pl-4">
<div
className={classNames(
'row-span-2 bg-pink rounded-full bg-gradient-to-br from-white/40 to-white/80 opacity-30 w-6 h-6 md:w-10 md:h-10'
)}
/>
<div className="col-span-2 uppercase justify-start text-black dark:text-white text-ui-small md:text-market self-end">
{isMobile
? market.tradableInstrument.instrument.code
: market.tradableInstrument.instrument.name}{' '}
<SimpleMarketExpires
tags={market.tradableInstrument.instrument.metadata.tags}
/>
</div>
<div className="col-span-2 text-ui-tiny md:text-ui-small text-deemphasise dark:text-midGrey self-start leading-3">
{market.tradableInstrument.instrument.product.quoteName}
</div>
</div>
);
};
export default MarketNameRenderer;
@@ -1,228 +0,0 @@
import React from 'react';
import {
useLocation,
useRoutes,
BrowserRouter,
useParams,
} from 'react-router-dom';
import {
render,
screen,
fireEvent,
waitFor,
act,
getAllByText,
} from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { MarketState } from '@vegaprotocol/types';
import SimpleMarketToolbar from './simple-market-toolbar';
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
import { markets as filterData } from './mocks/market-filters.json';
const mockedNavigate = jest.fn();
jest.mock('react-router-dom', () => {
const actualRouter = jest.requireActual('react-router-dom');
return {
...actualRouter,
useNavigate: () => mockedNavigate,
useParams: jest.fn(() => actualRouter.useParams()),
};
});
describe('SimpleMarketToolbar', () => {
const WrappedCompForTest = () => {
const routes = useRoutes([
{
path: '/',
element: (
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
),
},
{
path: 'markets',
children: [
{
path: `:state`,
element: (
<SimpleMarketToolbar
data={filterData as SimpleMarkets_markets[]}
/>
),
children: [
{
path: `:product`,
element: (
<SimpleMarketToolbar
data={filterData as SimpleMarkets_markets[]}
/>
),
children: [
{
path: `:asset`,
element: (
<SimpleMarketToolbar
data={filterData as SimpleMarkets_markets[]}
/>
),
},
],
},
],
},
],
element: (
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
),
},
]);
const location = useLocation();
return (
<>
{routes}
<div data-testid="location-display">{location.pathname}</div>
</>
);
};
afterEach(() => {
jest.clearAllMocks();
});
it('should be properly rendered', async () => {
render(
<MockedProvider mocks={[]} addTypename={false}>
<WrappedCompForTest />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await waitFor(() => {
expect(screen.getByText('Future')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('Future'));
await waitFor(() => {
expect(screen.getByTestId('market-products-menu').children).toHaveLength(
3
);
expect(screen.getByTestId('market-assets-menu').children).toHaveLength(6);
});
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
expect(screen.getByRole('menu').children).toHaveLength(10);
});
});
it('navigation should work well', async () => {
render(
<MockedProvider mocks={[]} addTypename={false}>
<WrappedCompForTest />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await waitFor(() => {
expect(screen.getByText('Future')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('Future'));
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
`/markets/${MarketState.STATE_ACTIVE}/Future`
);
});
fireEvent.click(
screen
.getByTestId('market-assets-menu')
.children[5].querySelector('a') as HTMLAnchorElement
);
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
`/markets/${MarketState.STATE_ACTIVE}/Future/tEURO`
);
});
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const pending = getAllByText(menu, 'Pending')[0];
await act(() => {
fireEvent.click(pending);
});
await waitFor(() => {
expect(mockedNavigate).toHaveBeenCalledWith(
`/markets/${MarketState.STATE_PENDING}/Future/tEURO`
);
});
});
it('stateChange callback should work well', async () => {
(useParams as jest.Mock).mockImplementation(() => ({
asset: 'asset1',
product: 'product1',
state: 'state1',
}));
render(
<MockedProvider mocks={[]} addTypename={false}>
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const suspended = getAllByText(menu, 'Suspended')[0];
fireEvent.click(suspended);
expect(mockedNavigate).toHaveBeenCalledWith(
`/markets/${MarketState.STATE_SUSPENDED}/product1/asset1`
);
});
it('stateChange callback should call navigate with url with state only', async () => {
(useParams as jest.Mock).mockImplementation(() => ({}));
render(
<MockedProvider mocks={[]} addTypename={false}>
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const closed = getAllByText(menu, 'Closed')[0];
fireEvent.click(closed);
expect(mockedNavigate).toHaveBeenCalledWith(
`/markets/${MarketState.STATE_CLOSED}`
);
});
it('stateChange callback should call navigate with no asset', async () => {
(useParams as jest.Mock).mockImplementation(() => ({
asset: 'all',
state: 'state1',
}));
render(
<MockedProvider mocks={[]} addTypename={false}>
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const active = getAllByText(menu, 'Active')[0];
fireEvent.click(active);
expect(mockedNavigate).toHaveBeenCalledWith('/markets');
});
});
@@ -1,55 +0,0 @@
import * as React from 'react';
import classNames from 'classnames';
import Video from '../header/video';
interface CounterProps {
label: string;
className?: string;
isActive: boolean;
}
type TStyleMap = {
[key: string]: string;
};
const reduceStyles = (styleMap: TStyleMap) =>
Object.keys(styleMap).reduce((acc, curr) => acc + `${styleMap[curr]} `, '');
export const Counter = ({ label, isActive, className }: CounterProps) => {
const generalStyles: TStyleMap = {
shape: 'rounded-full relative aspect-square clip-path-rounded',
border: 'border-black dark:border-white',
colours: 'bg-white dark:bg-black text-black dark:text-white',
children: 'flex items-center justify-center text-center',
};
const unselected: TStyleMap = {
border: 'border',
size: 'w-[30px] md:w-10',
margin: 'mt-[5px] md:ml-[10px] md:mt-0',
};
const selected: TStyleMap = {
colours: 'text-xl text-white',
size: 'w-10 md:w-[60px]',
};
const classes = classNames(
reduceStyles(generalStyles),
isActive ? reduceStyles(selected) : reduceStyles(unselected),
className
).replace(/ +/g, ' '); // remove double space
return (
<div className={classes} aria-hidden>
{isActive && (
<div className="w-full h-full">
<Video />
</div>
)}
<span className="absolute inset-0 flex items-center justify-center">
{label}
</span>
</div>
);
};
@@ -1 +0,0 @@
export * from './stepper';
@@ -1,124 +0,0 @@
import React from 'react';
import { render, waitFor, fireEvent } from '@testing-library/react';
import { Stepper } from './index';
import type { TStep } from './index';
jest.mock('@vegaprotocol/react-helpers', () => {
return {
t: (a: string) => a,
useScreenDimensions: jest.fn(() => ({
isMobile: true,
})),
};
});
// Used to disable the error you get from muted video https://github.com/testing-library/react-testing-library/issues/470
Object.defineProperty(HTMLMediaElement.prototype, 'muted', {
// eslint-disable-next-line @typescript-eslint/no-empty-function
set: () => {},
});
const steps: TStep[] = [
{
label: 'Alpha',
component: <p>Alpha Content</p>,
},
{
label: 'Beta',
component: <p>Beta Content</p>,
},
{
label: 'Gamma',
component: <p>Gamma Content</p>,
},
];
describe('Stepper Component', () => {
it('should render it as a list', async () => {
const { getByLabelText } = await render(<Stepper steps={steps} />);
expect(getByLabelText('Step by step to make a trade')).toBeTruthy();
expect(getByLabelText('Step 1')).toBeTruthy();
expect(getByLabelText('Step 2')).toBeTruthy();
expect(getByLabelText('Step 3')).toBeTruthy();
});
it('should go to the correct tab on click', async () => {
const { getByRole } = await render(<Stepper steps={steps} />);
expect(getByRole('tab', { name: 'Alpha' })).toHaveAttribute(
'aria-selected',
'true'
);
fireEvent.click(getByRole('tab', { name: 'Beta' }));
await waitFor(() => getByRole('tab', { name: 'Gamma' }));
expect(getByRole('tab', { name: 'Alpha' })).toHaveAttribute(
'aria-selected',
'false'
);
expect(getByRole('tab', { name: 'Beta' })).toHaveAttribute(
'aria-selected',
'true'
);
expect(getByRole('tab', { name: 'Gamma' })).toHaveAttribute(
'aria-selected',
'false'
);
});
it('should go to the correct tab on right/left arrow press', async () => {
const { getByRole } = await render(<Stepper steps={steps} />);
expect(getByRole('tab', { name: 'Alpha' })).toHaveAttribute(
'aria-selected',
'true'
);
fireEvent.keyDown(getByRole('tab', { name: 'Beta' }), {
key: 'ArrowRight',
});
await waitFor(() => getByRole('tab', { name: 'Beta' }));
expect(getByRole('tab', { name: 'Alpha' })).toHaveAttribute(
'aria-selected',
'false'
);
expect(getByRole('tab', { name: 'Beta' })).toHaveAttribute(
'aria-selected',
'true'
);
fireEvent.keyDown(getByRole('tab', { name: 'Beta' }), { key: 'ArrowLeft' });
await waitFor(() => getByRole('tab', { name: 'Alpha' }));
expect(getByRole('tab', { name: 'Alpha' })).toHaveAttribute(
'aria-selected',
'true'
);
expect(getByRole('tab', { name: 'Beta' })).toHaveAttribute(
'aria-selected',
'false'
);
});
it('should go to next tab on next button click', async () => {
const { getByRole } = await render(<Stepper steps={steps} />);
expect(getByRole('tab', { name: 'Alpha' })).toHaveAttribute(
'aria-selected',
'true'
);
fireEvent.click(getByRole('button', { name: 'Next' }));
await waitFor(() => getByRole('tab', { name: 'Gamma' }));
expect(getByRole('tab', { name: 'Alpha' })).toHaveAttribute(
'aria-selected',
'false'
);
expect(getByRole('tab', { name: 'Beta' })).toHaveAttribute(
'aria-selected',
'true'
);
expect(getByRole('tab', { name: 'Gamma' })).toHaveAttribute(
'aria-selected',
'false'
);
});
afterEach(() => {
jest.clearAllMocks();
});
});
@@ -1,158 +0,0 @@
import * as React from 'react';
import type { ReactNode } from 'react';
import classNames from 'classnames';
import { Button } from '@vegaprotocol/ui-toolkit';
import { t, useScreenDimensions } from '@vegaprotocol/react-helpers';
import { Counter } from './counter';
export type TStep = {
label: string;
component: ReactNode;
disabled?: boolean;
value?: string;
};
export interface StepperProps {
steps: TStep[];
className?: string;
}
export const Stepper = ({ steps }: StepperProps) => {
const [activeStep, setActiveStep] = React.useState(0);
const lastStep = steps.length - 1;
const isLastStep = activeStep === lastStep;
const { isMobile } = useScreenDimensions();
const handleClick = (index: typeof activeStep) => {
setActiveStep(index);
};
const handleKeyPress = (event: React.KeyboardEvent<HTMLButtonElement>) => {
if (event.key === 'ArrowLeft' && activeStep > 0) {
return setActiveStep((prevActiveStep) => prevActiveStep - 1);
} else if (event.key === 'ArrowRight' && !isLastStep) {
return setActiveStep((prevActiveStep) => prevActiveStep + 1);
}
return false;
};
const handleNext = () => {
if (!isLastStep) {
setActiveStep((prevActiveStep) => prevActiveStep + 1);
}
};
return (
<div>
<ol
aria-label={t('Step by step to make a trade')}
className="relative flex md:flex-col justify-between list-none"
>
{steps.map((step, index) => {
const isActive = activeStep === index;
const isFirstStep = !index;
const isLastStep = lastStep === index;
return (
<li
className="flex-1"
key={`${index}-${step.label}`}
aria-label={t(`Step ${index + 1}`)}
>
<div className="flex relative md:pt-6">
{!isFirstStep ? (
<div
aria-hidden
className="flex-auto absolute top-[20px] -left-1/2 right-1/2 md:-top-1/2 md:bottom-1/2 md:left-[29.5px] md:right-auto"
>
<span className="h-full block border-t border-black dark:border-white w-full md:border-l md:border-t-0" />
</div>
) : undefined}
<button
role="tab"
aria-selected={isActive}
type="button"
id={`step-${index}-control`}
aria-controls={`step-${index}-panel`}
onKeyDown={(event) => handleKeyPress(event)}
onClick={() => handleClick(index)}
className="cursor-pointer z-10 flex w-full items-center"
>
<div className="flex-1 flex flex-col md:flex-row items-center w-full text-center">
<Counter
className="md:mr-4"
isActive={isActive}
label={(index + 1).toString()}
/>
<h3
className={classNames(
'md:mt-0 font-alpha uppercase text-black dark:text-white',
{
'mt-2 text-md md:text-2xl': isActive,
'mt-4 text-sm md:text-lg md:ml-2': !isActive,
}
)}
>
{step.label}
</h3>
</div>
{step.value && !isActive && (
<span
aria-label={`Selected value ${step.value}`}
className="text-blue hidden md:block"
>
{step.value}
</span>
)}
</button>
</div>
{!isMobile && (
<div
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
tabIndex={0}
id={`step-${index}-panel`}
aria-labelledby={`step-${index}-control`}
aria-hidden={!isActive}
role="tabpanel"
className={classNames(
'hidden md:block md:border-black md:dark:border-white md:ml-[29.5px] md:pl-[45px]',
{
'invisible h-0': !isActive,
'visible h-full': isActive,
'md:border-l': !isLastStep,
}
)}
>
{step.component}
</div>
)}
</li>
);
})}
</ol>
{isMobile && (
<div
id={`step-${activeStep}-panel`}
aria-labelledby={`step-${activeStep}-control`}
role="tabpanel"
className="md:hidden mt-10" // md:hidden as fallback
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
tabIndex={0}
>
{steps[activeStep].component}
{!isLastStep && (
<div className="mt-10 md:sr-only">
<Button
fill={true}
onClick={handleNext}
disabled={steps[activeStep].disabled}
data-testid="next-button"
>
{t('Next')}
</Button>
</div>
)}
</div>
)}
</div>
);
};
@@ -1 +0,0 @@
export * from './traffic-light';
@@ -1,25 +0,0 @@
import React from 'react';
import type { ReactNode } from 'react';
import classNames from 'classnames';
interface TrafficLightProps {
value: number;
q1: number;
q2: number;
children: ReactNode;
}
export const TrafficLight = ({
value,
q1,
q2,
children,
}: TrafficLightProps) => {
const slippageClassName = classNames({
'text-darkerGreen dark:text-lightGreen': value < q1,
'text-amber': value >= q1 && value < q2,
'text-vega-red': value >= q2,
});
return <div className={slippageClassName}>{children || value}</div>;
};
@@ -1,38 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import { Button } from '@vegaprotocol/ui-toolkit';
import * as React from 'react';
import { useContext } from 'react';
import LocalContext from '../../context/local-context';
const ConnectWallet = () => {
const {
vegaWalletDialog: { setConnect },
} = useContext(LocalContext);
return (
<section
className="p-8 bg-white-normal dark:bg-offBlack"
data-testid="trading-connect-wallet"
>
<h3 className="mb-4 text-2xl text-offBlack dark:text-white">
{t('Please connect your Vega wallet to make a trade')}
</h3>
<div className="mb-4">
<Button variant="primary" onClick={() => setConnect(true)} size="lg">
{t('Connect Vega wallet')}
</Button>
</div>
<h4 className="text-lg text-offBlack dark:text-white">
{t("Don't have a wallet?")}
</h4>
<p>
{t('Head over to ')}
<a className="text-blue" href="https://vega.xyz/wallet">
https://vega.xyz/wallet
</a>
{t(' and follow the steps to create one.')}
</p>
</section>
);
};
export default ConnectWallet;
@@ -1,26 +0,0 @@
import type { SimpleMarkets_markets } from '../components/simple-market-list/__generated__/SimpleMarkets';
import { MarketState } from '@vegaprotocol/types';
import { t } from '@vegaprotocol/react-helpers';
export const DATE_FORMAT = 'dd MMMM yyyy HH:mm';
export const EXPIRE_DATE_FORMAT = 'MMM dd';
export const TRADABLE_STATES = {
[MarketState.STATE_ACTIVE]: true,
};
export const IS_MARKET_TRADABLE = (market: SimpleMarkets_markets) =>
Boolean((market.state ?? '') in TRADABLE_STATES && market?.id);
export const MARKET_STATES_MAP: Record<MarketState | '', string> = {
[MarketState.STATE_ACTIVE]: t('Active'),
[MarketState.STATE_CANCELLED]: t('Cancelled'),
[MarketState.STATE_CLOSED]: t('Closed'),
[MarketState.STATE_PENDING]: t('Pending'),
[MarketState.STATE_PROPOSED]: t('Proposed'),
[MarketState.STATE_REJECTED]: t('Rejected'),
[MarketState.STATE_SETTLED]: t('Settled'),
[MarketState.STATE_SUSPENDED]: t('Suspended'),
[MarketState.STATE_TRADING_TERMINATED]: t('TradingTerminated'),
'': t('Unknown'),
};
@@ -1,25 +0,0 @@
import { createContext } from 'react';
export interface VegaWalletDialogState {
connect: boolean;
manage: boolean;
setConnect: (isOpen: boolean) => void;
setManage: (isOpen: boolean) => void;
}
interface MenuState {
menuOpen: boolean;
setMenuOpen: (value: boolean) => void;
onToggle: () => void;
}
export interface LocalValues {
vegaWalletDialog: VegaWalletDialogState;
menu: MenuState;
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const LocalContext = createContext<LocalValues>({} as LocalValues);
export default LocalContext;
@@ -1,30 +0,0 @@
query EstimateOrder(
$marketId: ID!
$partyId: ID!
$price: String
$size: String!
$side: Side!
$timeInForce: OrderTimeInForce!
$expiration: String
$type: OrderType!
) {
estimateOrder(
marketId: $marketId
partyId: $partyId
price: $price
size: $size
side: $side
timeInForce: $timeInForce
expiration: $expiration
type: $type
) {
fee {
makerFee
infrastructureFee
liquidityFee
}
marginLevels {
initialLevel
}
}
}
@@ -1,8 +0,0 @@
query MarketMarkPrice($marketId: ID!) {
market(id: $marketId) {
decimalPlaces
data {
markPrice
}
}
}
@@ -1,25 +0,0 @@
query MarketPositions($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
decimals
}
market {
id
}
}
positionsConnection {
edges {
node {
openVolume
market {
id
}
}
}
}
}
}
@@ -1,28 +0,0 @@
query PartyMarketData($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
id
decimals
}
market {
id
}
}
marginsConnection {
edges {
node {
market {
id
}
initialLevel
maintenanceLevel
searchLevel
}
}
}
}
}
@@ -1,52 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: MarketMarkPrice
// ====================================================
export interface MarketMarkPrice_market_data {
__typename: "MarketData";
/**
* the mark price (an unsigned integer)
*/
markPrice: string;
}
export interface MarketMarkPrice_market {
__typename: "Market";
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the market. (uint64)
*
* Examples:
* Currency Balance decimalPlaces Real Balance
* GBP 100 0 GBP 100
* GBP 100 2 GBP 1.00
* GBP 100 4 GBP 0.01
* GBP 1 4 GBP 0.0001 ( 0.01p )
*
* GBX (pence) 100 0 GBP 1.00 (100p )
* GBX (pence) 100 2 GBP 0.01 ( 1p )
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/
decimalPlaces: number;
/**
* marketData for the given market
*/
data: MarketMarkPrice_market_data | null;
}
export interface MarketMarkPrice {
/**
* An instrument that is trading on the Vega network
*/
market: MarketMarkPrice_market | null;
}
export interface MarketMarkPriceVariables {
marketId: string;
}
@@ -1,118 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: PartyMarketData
// ====================================================
export interface PartyMarketData_party_accounts_asset {
__typename: "Asset";
/**
* The ID of the asset
*/
id: string;
/**
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
*/
decimals: number;
}
export interface PartyMarketData_party_accounts_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface PartyMarketData_party_accounts {
__typename: "Account";
/**
* Account type (General, Margin, etc)
*/
type: AccountType;
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
balance: string;
/**
* Asset, the 'currency'
*/
asset: PartyMarketData_party_accounts_asset;
/**
* Market (only relevant to margin accounts)
*/
market: PartyMarketData_party_accounts_market | null;
}
export interface PartyMarketData_party_marginsConnection_edges_node_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface PartyMarketData_party_marginsConnection_edges_node {
__typename: "MarginLevels";
/**
* market in which the margin is required for this party
*/
market: PartyMarketData_party_marginsConnection_edges_node_market;
/**
* this is the minimum margin required for a party to place a new order on the network (unsigned integer)
*/
initialLevel: string;
/**
* minimal margin for the position to be maintained in the network (unsigned integer)
*/
maintenanceLevel: string;
/**
* if the margin is between maintenance and search, the network will initiate a collateral search (unsigned integer)
*/
searchLevel: string;
}
export interface PartyMarketData_party_marginsConnection_edges {
__typename: "MarginEdge";
node: PartyMarketData_party_marginsConnection_edges_node;
}
export interface PartyMarketData_party_marginsConnection {
__typename: "MarginConnection";
/**
* The margin levels in this connection
*/
edges: PartyMarketData_party_marginsConnection_edges[] | null;
}
export interface PartyMarketData_party {
__typename: "Party";
/**
* Party identifier
*/
id: string;
/**
* Collateral accounts relating to a party
*/
accounts: PartyMarketData_party_accounts[] | null;
/**
* Margin levels for a market
*/
marginsConnection: PartyMarketData_party_marginsConnection | null;
}
export interface PartyMarketData {
/**
* An entity that is trading on the Vega network
*/
party: PartyMarketData_party | null;
}
export interface PartyMarketDataVariables {
partyId: string;
}
@@ -1,64 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { Side, OrderTimeInForce, OrderType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: EstimateOrder
// ====================================================
export interface EstimateOrder_estimateOrder_fee {
__typename: "TradeFee";
/**
* The maker fee, paid by the aggressive party to the other party (the one who had an order in the book)
*/
makerFee: string;
/**
* The infrastructure fee, a fee paid to the validators to maintain the Vega network
*/
infrastructureFee: string;
/**
* The fee paid to the liquidity providers that committed liquidity to the market
*/
liquidityFee: string;
}
export interface EstimateOrder_estimateOrder_marginLevels {
__typename: "MarginLevels";
/**
* this is the minimum margin required for a party to place a new order on the network (unsigned integer)
*/
initialLevel: string;
}
export interface EstimateOrder_estimateOrder {
__typename: "OrderEstimate";
/**
* The estimated fee if the order was to trade
*/
fee: EstimateOrder_estimateOrder_fee;
/**
* The margin requirement for this order
*/
marginLevels: EstimateOrder_estimateOrder_marginLevels;
}
export interface EstimateOrder {
/**
* return an estimation of the potential cost for a new order
*/
estimateOrder: EstimateOrder_estimateOrder;
}
export interface EstimateOrderVariables {
marketId: string;
partyId: string;
price?: string | null;
size: string;
side: Side;
timeInForce: OrderTimeInForce;
expiration?: string | null;
type: OrderType;
}
@@ -1,106 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: MarketPositions
// ====================================================
export interface MarketPositions_party_accounts_asset {
__typename: "Asset";
/**
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
*/
decimals: number;
}
export interface MarketPositions_party_accounts_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface MarketPositions_party_accounts {
__typename: "Account";
/**
* Account type (General, Margin, etc)
*/
type: AccountType;
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
balance: string;
/**
* Asset, the 'currency'
*/
asset: MarketPositions_party_accounts_asset;
/**
* Market (only relevant to margin accounts)
*/
market: MarketPositions_party_accounts_market | null;
}
export interface MarketPositions_party_positionsConnection_edges_node_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface MarketPositions_party_positionsConnection_edges_node {
__typename: "Position";
/**
* Open volume (uint64)
*/
openVolume: string;
/**
* Market relating to this position
*/
market: MarketPositions_party_positionsConnection_edges_node_market;
}
export interface MarketPositions_party_positionsConnection_edges {
__typename: "PositionEdge";
node: MarketPositions_party_positionsConnection_edges_node;
}
export interface MarketPositions_party_positionsConnection {
__typename: "PositionConnection";
/**
* The positions in this connection
*/
edges: MarketPositions_party_positionsConnection_edges[] | null;
}
export interface MarketPositions_party {
__typename: "Party";
/**
* Party identifier
*/
id: string;
/**
* Collateral accounts relating to a party
*/
accounts: MarketPositions_party_accounts[] | null;
/**
* Trading positions relating to a party
*/
positionsConnection: MarketPositions_party_positionsConnection | null;
}
export interface MarketPositions {
/**
* An entity that is trading on the Vega network
*/
party: MarketPositions_party | null;
}
export interface MarketPositionsVariables {
partyId: string;
}
@@ -1,78 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type EstimateOrderQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
partyId: Types.Scalars['ID'];
price?: Types.InputMaybe<Types.Scalars['String']>;
size: Types.Scalars['String'];
side: Types.Side;
timeInForce: Types.OrderTimeInForce;
expiration?: Types.InputMaybe<Types.Scalars['String']>;
type: Types.OrderType;
}>;
export type EstimateOrderQuery = { __typename?: 'Query', estimateOrder: { __typename?: 'OrderEstimate', fee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string }, marginLevels: { __typename?: 'MarginLevels', initialLevel: string } } };
export const EstimateOrderDocument = gql`
query EstimateOrder($marketId: ID!, $partyId: ID!, $price: String, $size: String!, $side: Side!, $timeInForce: OrderTimeInForce!, $expiration: String, $type: OrderType!) {
estimateOrder(
marketId: $marketId
partyId: $partyId
price: $price
size: $size
side: $side
timeInForce: $timeInForce
expiration: $expiration
type: $type
) {
fee {
makerFee
infrastructureFee
liquidityFee
}
marginLevels {
initialLevel
}
}
}
`;
/**
* __useEstimateOrderQuery__
*
* To run a query within a React component, call `useEstimateOrderQuery` and pass it any options that fit your needs.
* When your component renders, `useEstimateOrderQuery` 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 } = useEstimateOrderQuery({
* variables: {
* marketId: // value for 'marketId'
* partyId: // value for 'partyId'
* price: // value for 'price'
* size: // value for 'size'
* side: // value for 'side'
* timeInForce: // value for 'timeInForce'
* expiration: // value for 'expiration'
* type: // value for 'type'
* },
* });
*/
export function useEstimateOrderQuery(baseOptions: Apollo.QueryHookOptions<EstimateOrderQuery, EstimateOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
}
export function useEstimateOrderLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimateOrderQuery, EstimateOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
}
export type EstimateOrderQueryHookResult = ReturnType<typeof useEstimateOrderQuery>;
export type EstimateOrderLazyQueryHookResult = ReturnType<typeof useEstimateOrderLazyQuery>;
export type EstimateOrderQueryResult = Apollo.QueryResult<EstimateOrderQuery, EstimateOrderQueryVariables>;
@@ -1,51 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketMarkPriceQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketMarkPriceQuery = { __typename?: 'Query', market?: { __typename?: 'Market', decimalPlaces: number, data?: { __typename?: 'MarketData', markPrice: string } | null } | null };
export const MarketMarkPriceDocument = gql`
query MarketMarkPrice($marketId: ID!) {
market(id: $marketId) {
decimalPlaces
data {
markPrice
}
}
}
`;
/**
* __useMarketMarkPriceQuery__
*
* To run a query within a React component, call `useMarketMarkPriceQuery` and pass it any options that fit your needs.
* When your component renders, `useMarketMarkPriceQuery` 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 } = useMarketMarkPriceQuery({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useMarketMarkPriceQuery(baseOptions: Apollo.QueryHookOptions<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>(MarketMarkPriceDocument, options);
}
export function useMarketMarkPriceLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>(MarketMarkPriceDocument, options);
}
export type MarketMarkPriceQueryHookResult = ReturnType<typeof useMarketMarkPriceQuery>;
export type MarketMarkPriceLazyQueryHookResult = ReturnType<typeof useMarketMarkPriceLazyQuery>;
export type MarketMarkPriceQueryResult = Apollo.QueryResult<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>;
@@ -1,68 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketPositionsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type MarketPositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, accounts?: Array<{ __typename?: 'Account', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', decimals: number }, market?: { __typename?: 'Market', id: string } | null }> | null, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', openVolume: string, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
export const MarketPositionsDocument = gql`
query MarketPositions($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
decimals
}
market {
id
}
}
positionsConnection {
edges {
node {
openVolume
market {
id
}
}
}
}
}
}
`;
/**
* __useMarketPositionsQuery__
*
* To run a query within a React component, call `useMarketPositionsQuery` and pass it any options that fit your needs.
* When your component renders, `useMarketPositionsQuery` 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 } = useMarketPositionsQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function useMarketPositionsQuery(baseOptions: Apollo.QueryHookOptions<MarketPositionsQuery, MarketPositionsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<MarketPositionsQuery, MarketPositionsQueryVariables>(MarketPositionsDocument, options);
}
export function useMarketPositionsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketPositionsQuery, MarketPositionsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<MarketPositionsQuery, MarketPositionsQueryVariables>(MarketPositionsDocument, options);
}
export type MarketPositionsQueryHookResult = ReturnType<typeof useMarketPositionsQuery>;
export type MarketPositionsLazyQueryHookResult = ReturnType<typeof useMarketPositionsLazyQuery>;
export type MarketPositionsQueryResult = Apollo.QueryResult<MarketPositionsQuery, MarketPositionsQueryVariables>;
@@ -1,71 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type PartyMarketDataQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type PartyMarketDataQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, accounts?: Array<{ __typename?: 'Account', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string, decimals: number }, market?: { __typename?: 'Market', id: string } | null }> | null, marginsConnection?: { __typename?: 'MarginConnection', edges?: Array<{ __typename?: 'MarginEdge', node: { __typename?: 'MarginLevels', initialLevel: string, maintenanceLevel: string, searchLevel: string, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
export const PartyMarketDataDocument = gql`
query PartyMarketData($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
id
decimals
}
market {
id
}
}
marginsConnection {
edges {
node {
market {
id
}
initialLevel
maintenanceLevel
searchLevel
}
}
}
}
}
`;
/**
* __usePartyMarketDataQuery__
*
* To run a query within a React component, call `usePartyMarketDataQuery` and pass it any options that fit your needs.
* When your component renders, `usePartyMarketDataQuery` 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 } = usePartyMarketDataQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function usePartyMarketDataQuery(baseOptions: Apollo.QueryHookOptions<PartyMarketDataQuery, PartyMarketDataQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<PartyMarketDataQuery, PartyMarketDataQueryVariables>(PartyMarketDataDocument, options);
}
export function usePartyMarketDataLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PartyMarketDataQuery, PartyMarketDataQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<PartyMarketDataQuery, PartyMarketDataQueryVariables>(PartyMarketDataDocument, options);
}
export type PartyMarketDataQueryHookResult = ReturnType<typeof usePartyMarketDataQuery>;
export type PartyMarketDataLazyQueryHookResult = ReturnType<typeof usePartyMarketDataLazyQuery>;
export type PartyMarketDataQueryResult = Apollo.QueryResult<PartyMarketDataQuery, PartyMarketDataQueryVariables>;
@@ -1,124 +0,0 @@
import { MockedProvider } from '@apollo/client/testing';
import { renderHook } from '@testing-library/react';
import { Side } from '@vegaprotocol/types';
import type { Order } from '@vegaprotocol/orders';
import useCalculateSlippage from './use-calculate-slippage';
const mockData = {
decimalPlaces: 0,
positionDecimalPlaces: 0,
depth: {
buy: [
{
price: '5',
volume: '2',
},
{
price: '4',
volume: '3',
},
{
price: '3',
volume: '2',
},
{
price: '2',
volume: '1',
},
{
price: '1',
volume: '1',
},
],
sell: [
{
price: '6',
volume: '1',
},
{
price: '7',
volume: '3',
},
{
price: '8',
volume: '2',
},
{
price: '9',
volume: '1',
},
{
price: '10',
volume: '2',
},
],
},
};
let mockOrderBookData = {
data: mockData,
};
jest.mock('@vegaprotocol/market-depth', () => ({
...jest.requireActual('@vegaprotocol/market-depth'),
useOrderBookData: jest.fn(() => mockOrderBookData),
}));
describe('useCalculateSlippage Hook', () => {
describe('calculate proper result', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('long order', () => {
const { result } = renderHook(
() =>
useCalculateSlippage({
marketId: 'marketId',
order: { size: '10', side: Side.SIDE_BUY } as Order,
}),
{
wrapper: MockedProvider,
}
);
expect(result.current).toEqual('33.33');
});
it('short order', () => {
const { result } = renderHook(
() =>
useCalculateSlippage({
marketId: 'marketId',
order: { size: '10', side: Side.SIDE_SELL } as Order,
}),
{
wrapper: MockedProvider,
}
);
expect(result.current).toEqual('31.11');
});
it('when no order book result should be null', () => {
mockOrderBookData = {
data: {
...mockData,
depth: {
...mockData.depth,
buy: [],
},
},
};
const { result } = renderHook(
() =>
useCalculateSlippage({
marketId: 'marketId',
order: { size: '10', side: Side.SIDE_SELL } as Order,
}),
{
wrapper: MockedProvider,
}
);
expect(result.current).toBeNull();
});
});
});
@@ -1,62 +0,0 @@
import { useMemo } from 'react';
import { Side } from '@vegaprotocol/types';
import { useOrderBookData } from '@vegaprotocol/market-depth';
import type { Order } from '@vegaprotocol/orders';
import { BigNumber } from 'bignumber.js';
import { formatNumber, toBigNum } from '@vegaprotocol/react-helpers';
interface Props {
marketId: string;
order: Order;
}
const useCalculateSlippage = ({ marketId, order }: Props) => {
const variables = useMemo(() => ({ marketId }), [marketId]);
const { data } = useOrderBookData({
variables,
throttleMilliseconds: 5000,
});
const volPriceArr =
data?.depth[order.side === Side.SIDE_BUY ? 'sell' : 'buy'] || [];
if (volPriceArr.length) {
const decimals = data?.decimalPlaces ?? 0;
const positionDecimals = data?.positionDecimalPlaces ?? 0;
const bestPrice = toBigNum(volPriceArr[0].price, decimals);
const { size } = order;
let descSize = new BigNumber(size);
let i = 0;
const volPricePairs: Array<[BigNumber, BigNumber]> = [];
while (!descSize.isZero() && i < volPriceArr.length) {
const price = toBigNum(volPriceArr[i].price, decimals);
const amount = BigNumber.min(
descSize,
toBigNum(volPriceArr[i].volume, positionDecimals)
);
volPricePairs.push([price, amount]);
descSize = BigNumber.max(0, descSize.minus(amount));
i++;
}
if (volPricePairs.length) {
const volWeightAvPricePair = volPricePairs.reduce(
(agg, item) => {
agg[0] = agg[0].plus(item[0].multipliedBy(item[1]));
agg[1] = agg[1].plus(item[1]);
return agg;
},
[new BigNumber(0), new BigNumber(0)]
);
const volWeightAvPrice = volWeightAvPricePair[0].dividedBy(
volWeightAvPricePair[1]
);
const slippage = volWeightAvPrice
.minus(bestPrice)
.absoluteValue()
.dividedBy(bestPrice)
.multipliedBy(100);
return formatNumber(slippage, 2);
}
}
return null;
};
export default useCalculateSlippage;
@@ -1,18 +0,0 @@
import { renderHook, act } from '@testing-library/react';
import useLocalValues from './use-local-values';
describe('local values hook', () => {
it('state of wallet dialog should be properly handled', () => {
const setTheme = jest.fn();
const { result } = renderHook(() => useLocalValues('light', setTheme));
expect(result.current.vegaWalletDialog).toBeDefined();
expect(result.current.vegaWalletDialog.manage).toBe(false);
expect(result.current.vegaWalletDialog.connect).toBe(false);
act(() => {
result.current.vegaWalletDialog.setConnect(true);
result.current.vegaWalletDialog.setManage(true);
});
expect(result.current.vegaWalletDialog.manage).toBe(true);
expect(result.current.vegaWalletDialog.connect).toBe(true);
});
});
@@ -1,19 +0,0 @@
import { useMemo, useState } from 'react';
import type { LocalValues } from '../context/local-context';
const useLocalValues = (theme: 'light' | 'dark', toggleTheme: () => void) => {
const [connect, setConnect] = useState<boolean>(false);
const [manage, setManage] = useState<boolean>(false);
const [menuOpen, setMenuOpen] = useState(false);
return useMemo<LocalValues>(
() => ({
vegaWalletDialog: { connect, manage, setConnect, setManage },
menu: { menuOpen, setMenuOpen, onToggle: () => setMenuOpen(!menuOpen) },
theme,
toggleTheme,
}),
[connect, manage, theme, toggleTheme, menuOpen]
);
};
export default useLocalValues;
@@ -1,38 +0,0 @@
import { gql, useQuery } from '@apollo/client';
import { useMemo, useRef } from 'react';
import type {
MarketMarkPrice,
MarketMarkPriceVariables,
} from './__generated__/MarketMarkPrice';
const MARKET_MARK_PRICE = gql`
query MarketMarkPrice($marketId: ID!) {
market(id: $marketId) {
decimalPlaces
data {
markPrice
}
}
}
`;
export default (marketId: string) => {
const memoRef = useRef<MarketMarkPrice | null>(null);
const { data } = useQuery<MarketMarkPrice, MarketMarkPriceVariables>(
MARKET_MARK_PRICE,
{
pollInterval: 5000,
variables: { marketId },
skip: !marketId,
}
);
return useMemo(() => {
if (
data &&
data.market?.data?.markPrice !== memoRef.current?.market?.data?.markPrice
) {
memoRef.current = data;
}
return memoRef.current;
}, [data, memoRef]);
};
@@ -1,133 +0,0 @@
import { renderHook } from '@testing-library/react';
import useMarketPositions from './use-market-positions';
let mockNotEmptyData = {
party: {
accounts: [
{
balance: '50001000000',
asset: {
decimals: 5,
},
market: {
id: 'marketId',
},
},
{
balance: '700000000000000000000000000000',
asset: {
decimals: 5,
},
market: {
id: 'someOtherMarketId',
},
},
],
positionsConnection: {
edges: [
{
node: {
openVolume: '100002',
market: {
id: 'marketId',
},
},
},
{
node: {
openVolume: '3',
market: {
id: 'someOtherMarketId',
},
},
},
],
},
},
};
jest.mock('@apollo/client', () => ({
...jest.requireActual('@apollo/client'),
useQuery: jest.fn(() => ({ data: mockNotEmptyData })),
}));
describe('useOrderPosition Hook', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('should return proper positive value', () => {
const { result } = renderHook(() =>
useMarketPositions({ marketId: 'marketId', partyId: 'partyId' })
);
expect(result.current?.openVolume.toNumber()).toEqual(100002);
expect(result.current?.balance.toString()).toEqual('50001000000');
});
it('if balance equal 0 return null', () => {
mockNotEmptyData = {
party: {
accounts: [
{
balance: '0',
asset: {
decimals: 5,
},
market: {
id: 'marketId',
},
},
],
positionsConnection: {
edges: [
{
node: {
openVolume: '2',
market: {
id: 'marketId',
},
},
},
],
},
},
};
const { result } = renderHook(() =>
useMarketPositions({ marketId: 'marketId', partyId: 'partyId' })
);
expect(result.current).toBeNull();
});
it('if no markets return null', () => {
mockNotEmptyData = {
party: {
accounts: [
{
balance: '33330',
asset: {
decimals: 5,
},
market: {
id: 'otherMarketId',
},
},
],
positionsConnection: {
edges: [
{
node: {
openVolume: '2',
market: {
id: 'otherMarketId',
},
},
},
],
},
},
};
const { result } = renderHook(() =>
useMarketPositions({ marketId: 'marketId', partyId: 'partyId' })
);
expect(result.current).toBeNull();
});
});

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