Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
829d3112e1 | ||
|
|
73cda26e17 | ||
|
|
f34ddaa7b9 | ||
|
|
20371f56f7 | ||
|
|
261ec8c5e4 | ||
|
|
d577080a86 | ||
|
|
c5deddb6d8 | ||
|
|
4acf2a7f8c | ||
|
|
fdfae74524 | ||
|
|
7f9c1178c4 | ||
|
|
b633dbc049 | ||
|
|
92315181e1 | ||
|
|
9b7c26386b | ||
|
|
312db3ea0f | ||
|
|
4d9bcde560 | ||
|
|
e5fc3d1bc4 | ||
|
|
799cb04cae | ||
|
|
e03ba3256f | ||
|
|
0c6212e82b | ||
|
|
71ae12007b | ||
|
|
ccc4d39aec | ||
|
|
5d4ce7e2f2 | ||
|
|
3f77986885 |
+3
-3
@@ -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
|
||||
+58
-33
@@ -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
|
||||
@@ -6,19 +6,15 @@ on:
|
||||
- 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 }}
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
GO111MODULE: 'on'
|
||||
GOPROXY: ${{ secrets.GO_PROXY }}
|
||||
steps:
|
||||
#######
|
||||
## Setup langs
|
||||
@@ -27,7 +23,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
|
||||
@@ -38,14 +34,6 @@ jobs:
|
||||
## 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
|
||||
@@ -65,25 +53,11 @@ jobs:
|
||||
- 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
|
||||
- name: Install binaries
|
||||
run: vegacapsule install-bins
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
GOBIN: ${{ env.GOBIN }}
|
||||
|
||||
######
|
||||
## Start capsule
|
||||
@@ -95,7 +69,7 @@ jobs:
|
||||
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 --force
|
||||
working-directory: capsule
|
||||
|
||||
######
|
||||
@@ -119,24 +93,27 @@ 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: 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
|
||||
run: npx nx affected:e2e --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_INCLUDE_FLOWS: false
|
||||
|
||||
######
|
||||
## Upload logs
|
||||
|
||||
@@ -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 }}
|
||||
@@ -11,13 +11,10 @@ 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
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
@@ -27,7 +24,7 @@ jobs:
|
||||
main-branch-name: master
|
||||
- name: Use Node.js 16
|
||||
id: Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.14.0
|
||||
- name: Install root dependencies
|
||||
@@ -43,13 +40,10 @@ jobs:
|
||||
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
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||
@@ -58,7 +52,7 @@ jobs:
|
||||
main-branch-name: master
|
||||
- name: Use Node.js 16
|
||||
id: Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.14.0
|
||||
- name: Install root dependencies
|
||||
|
||||
+1
-4
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Vendored
+1
-2
@@ -4,7 +4,6 @@
|
||||
"esbenp.prettier-vscode",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"stevejpurves.cucumber",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
"stevejpurves.cucumber"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,9 +7,8 @@ ENV PATH /app/node_modules/.bin:$PATH
|
||||
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
|
||||
@@ -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)
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
});
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-51
@@ -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>;
|
||||
-115
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
-36
@@ -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;
|
||||
-228
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -1,78 +0,0 @@
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import type {
|
||||
MarketPositions,
|
||||
MarketPositionsVariables,
|
||||
} from './__generated__/marketPositions';
|
||||
|
||||
const MARKET_POSITIONS_QUERY = gql`
|
||||
query MarketPositions($partyId: ID!) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
accounts {
|
||||
type
|
||||
balance
|
||||
asset {
|
||||
decimals
|
||||
}
|
||||
market {
|
||||
id
|
||||
}
|
||||
}
|
||||
positionsConnection {
|
||||
edges {
|
||||
node {
|
||||
openVolume
|
||||
market {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
marketId: string;
|
||||
partyId: string;
|
||||
}
|
||||
|
||||
export type PositionMargin = {
|
||||
openVolume: BigNumber;
|
||||
balance: BigNumber;
|
||||
balanceDecimals?: number;
|
||||
} | null;
|
||||
|
||||
export default ({ marketId, partyId }: Props): PositionMargin => {
|
||||
const { data } = useQuery<MarketPositions, MarketPositionsVariables>(
|
||||
MARKET_POSITIONS_QUERY,
|
||||
{
|
||||
pollInterval: 5000,
|
||||
variables: { partyId },
|
||||
skip: !partyId,
|
||||
fetchPolicy: 'no-cache',
|
||||
}
|
||||
);
|
||||
|
||||
const account = data?.party?.accounts?.find(
|
||||
(nodes) => nodes.market?.id === marketId
|
||||
);
|
||||
|
||||
if (account) {
|
||||
const balance = new BigNumber(account.balance || 0);
|
||||
const openVolume = new BigNumber(
|
||||
data?.party?.positionsConnection?.edges?.find(
|
||||
(nodes) => nodes.node.market.id === marketId
|
||||
)?.node.openVolume || 0
|
||||
);
|
||||
if (!balance.isZero() && !openVolume.isZero()) {
|
||||
return {
|
||||
balance,
|
||||
balanceDecimals: account?.asset.decimals,
|
||||
openVolume,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -1,117 +0,0 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import useMaximumPositionSize from './use-maximum-position-size';
|
||||
import type { PartyBalanceQuery_party_accounts } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
|
||||
import {
|
||||
AccountType,
|
||||
OrderTimeInForce,
|
||||
OrderType,
|
||||
Side,
|
||||
} from '@vegaprotocol/types';
|
||||
import type { PositionMargin } from './use-market-positions';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
|
||||
const defaultMockMarketPositions = {
|
||||
openVolume: new BigNumber(1),
|
||||
balance: new BigNumber(100000),
|
||||
};
|
||||
|
||||
let mockMarketPositions: PositionMargin | null = defaultMockMarketPositions;
|
||||
|
||||
const mockAccount: PartyBalanceQuery_party_accounts = {
|
||||
__typename: 'Account',
|
||||
type: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
balance: '200000',
|
||||
asset: {
|
||||
__typename: 'Asset',
|
||||
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
|
||||
symbol: 'tBTC',
|
||||
name: 'tBTC TEST',
|
||||
decimals: 5,
|
||||
},
|
||||
};
|
||||
|
||||
const mockOrder = {
|
||||
type: OrderType.TYPE_MARKET,
|
||||
size: '1',
|
||||
side: Side.SIDE_BUY,
|
||||
timeInForce: OrderTimeInForce.TIME_IN_FORCE_IOC,
|
||||
};
|
||||
|
||||
jest.mock('./use-settlement-account', () => {
|
||||
return {
|
||||
useSettlementAccount: jest.fn(() => mockAccount),
|
||||
};
|
||||
});
|
||||
jest.mock('./use-market-positions', () => jest.fn(() => mockMarketPositions));
|
||||
|
||||
describe('useMaximumPositionSize Hook', () => {
|
||||
it('should return correct size when no open positions', () => {
|
||||
mockMarketPositions = null;
|
||||
const price = '50';
|
||||
const expected = 4000;
|
||||
const { result } = renderHook(() =>
|
||||
useMaximumPositionSize({
|
||||
marketId: '',
|
||||
partyId: '',
|
||||
price,
|
||||
settlementAssetId: '',
|
||||
order: mockOrder,
|
||||
accounts: [mockAccount],
|
||||
})
|
||||
);
|
||||
expect(result.current).toBe(expected);
|
||||
});
|
||||
|
||||
it('should return correct size when open positions and same side', () => {
|
||||
const price = '50';
|
||||
mockMarketPositions = defaultMockMarketPositions;
|
||||
const expected = 3999;
|
||||
const { result } = renderHook(() =>
|
||||
useMaximumPositionSize({
|
||||
marketId: '',
|
||||
partyId: '',
|
||||
price,
|
||||
settlementAssetId: '',
|
||||
order: mockOrder,
|
||||
accounts: [mockAccount],
|
||||
})
|
||||
);
|
||||
expect(result.current).toBe(expected);
|
||||
});
|
||||
|
||||
it('should return correct size when open positions and opposite side', () => {
|
||||
const price = '50';
|
||||
mockOrder.side = Side.SIDE_SELL;
|
||||
mockMarketPositions = defaultMockMarketPositions;
|
||||
const expected = 4001;
|
||||
const { result } = renderHook(() =>
|
||||
useMaximumPositionSize({
|
||||
marketId: '',
|
||||
partyId: '',
|
||||
price,
|
||||
settlementAssetId: '',
|
||||
order: mockOrder,
|
||||
accounts: [mockAccount],
|
||||
})
|
||||
);
|
||||
expect(result.current).toBe(expected);
|
||||
});
|
||||
|
||||
it('should return zero if no account balance', () => {
|
||||
mockAccount.balance = '0';
|
||||
const price = '50';
|
||||
mockMarketPositions = defaultMockMarketPositions;
|
||||
const expected = 0;
|
||||
const { result } = renderHook(() =>
|
||||
useMaximumPositionSize({
|
||||
marketId: '',
|
||||
partyId: '',
|
||||
price,
|
||||
settlementAssetId: '',
|
||||
order: mockOrder,
|
||||
accounts: [],
|
||||
})
|
||||
);
|
||||
expect(result.current).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
import useMarketPositions from './use-market-positions';
|
||||
import type { Order } from '@vegaprotocol/orders';
|
||||
import type { PartyBalanceQuery_party_accounts } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
|
||||
import { useSettlementAccount } from './use-settlement-account';
|
||||
import { AccountType, Side } from '@vegaprotocol/types';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
|
||||
interface Props {
|
||||
partyId: string;
|
||||
accounts: PartyBalanceQuery_party_accounts[];
|
||||
marketId: string;
|
||||
price?: string;
|
||||
settlementAssetId: string;
|
||||
order: Order;
|
||||
}
|
||||
|
||||
const getSize = (balance: string, price: string) =>
|
||||
new BigNumber(balance).dividedBy(new BigNumber(price));
|
||||
|
||||
export default ({
|
||||
marketId,
|
||||
accounts,
|
||||
partyId,
|
||||
price,
|
||||
settlementAssetId,
|
||||
order,
|
||||
}: Props): number => {
|
||||
const settlementAccount = useSettlementAccount(
|
||||
settlementAssetId,
|
||||
accounts,
|
||||
AccountType.ACCOUNT_TYPE_GENERAL
|
||||
);
|
||||
|
||||
const marketPositions = useMarketPositions({ marketId: marketId, partyId });
|
||||
|
||||
if (
|
||||
!settlementAccount?.balance ||
|
||||
new BigNumber(settlementAccount?.balance || 0).isZero()
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size = getSize(settlementAccount.balance, price || '');
|
||||
|
||||
if (!marketPositions) {
|
||||
return size.toNumber() || 0;
|
||||
}
|
||||
|
||||
const isSameSide =
|
||||
(marketPositions.openVolume.isPositive() && order.side === Side.SIDE_BUY) ||
|
||||
(marketPositions.openVolume.isNegative() && order.side === Side.SIDE_SELL);
|
||||
|
||||
const adjustedForVolume = new BigNumber(size)[isSameSide ? 'minus' : 'plus'](
|
||||
marketPositions.openVolume
|
||||
);
|
||||
|
||||
return adjustedForVolume.isNegative() ? 0 : adjustedForVolume.toNumber();
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user