Compare commits
163
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fc6e10d64 | ||
|
|
c221d0772c | ||
|
|
efcd128240 | ||
|
|
9a88c33458 | ||
|
|
d2a5adf6ce | ||
|
|
ab0065bd9a | ||
|
|
5600c56da1 | ||
|
|
3d7267f496 | ||
|
|
c87beb4050 | ||
|
|
907a4e256e | ||
|
|
835bf36393 | ||
|
|
57863a7167 | ||
|
|
4272fefb0b | ||
|
|
8fee3ca080 | ||
|
|
98abe2910d | ||
|
|
22f4edff82 | ||
|
|
28aa5cd060 | ||
|
|
7b0d41b4a6 | ||
|
|
83ea12b4cb | ||
|
|
a7768f154b | ||
|
|
65af6bc0a8 | ||
|
|
93c1835cd6 | ||
|
|
446af23d9c | ||
|
|
2cfe8608fb | ||
|
|
032805b5b9 | ||
|
|
7a7c4ad452 | ||
|
|
0c5b2f5c29 | ||
|
|
a8667eea31 | ||
|
|
8dd63d7cac | ||
|
|
8bffc05be2 | ||
|
|
26e3596c5a | ||
|
|
6ad68f87cc | ||
|
|
86fdd75d6c | ||
|
|
f688b28b29 | ||
|
|
6ddceaf6bd | ||
|
|
a9e8b6915e | ||
|
|
9318dab698 | ||
|
|
ae0424e21a | ||
|
|
5e484dfab0 | ||
|
|
e5b1390302 | ||
|
|
6a6779ee72 | ||
|
|
414b8ced13 | ||
|
|
e310f04034 | ||
|
|
adf6059701 | ||
|
|
5dda58334e | ||
|
|
3b03b16938 | ||
|
|
0d8c052ec6 | ||
|
|
9836f29e15 | ||
|
|
4d69dceaa7 | ||
|
|
6af189150b | ||
|
|
6f20c9125d | ||
|
|
7c6b14f73a | ||
|
|
0fa1f47ad5 | ||
|
|
c4468d507b | ||
|
|
191e73335e | ||
|
|
dae8949b46 | ||
|
|
3aca9207ed | ||
|
|
69db3fdecb | ||
|
|
d9a747102d | ||
|
|
3f6bd066f1 | ||
|
|
195e234f16 | ||
|
|
b505a0598d | ||
|
|
f87fd676f6 | ||
|
|
b885696659 | ||
|
|
5d553271a5 | ||
|
|
87f7efd960 | ||
|
|
d9e3b9de99 | ||
|
|
b5e2513d53 | ||
|
|
ba27f9e738 | ||
|
|
22787bd4c4 | ||
|
|
c5eeead356 | ||
|
|
612b906cb4 | ||
|
|
94a2de859f | ||
|
|
f287dced18 | ||
|
|
3beb43a719 | ||
|
|
da807c883d | ||
|
|
5a842c9be9 | ||
|
|
3bec65ac67 | ||
|
|
f124275ce5 | ||
|
|
d271f28b82 | ||
|
|
483cf05050 | ||
|
|
a452a6e831 | ||
|
|
ebf2bfc7a9 | ||
|
|
d60cf3e5e6 | ||
|
|
2650329a31 | ||
|
|
4ef8218267 | ||
|
|
e3254564ae | ||
|
|
7effa0791c | ||
|
|
93108c5b48 | ||
|
|
06a2fc4d12 | ||
|
|
c274a4694a | ||
|
|
756d4d00de | ||
|
|
ac16ca9ee5 | ||
|
|
c3cdcede9f | ||
|
|
186a624466 | ||
|
|
4d9c43cf19 | ||
|
|
8f20216b39 | ||
|
|
5c4af868a7 | ||
|
|
d95bfb60ea | ||
|
|
792459e589 | ||
|
|
4ca244a975 | ||
|
|
010d7e5e31 | ||
|
|
9b04310ab8 | ||
|
|
832327c918 | ||
|
|
c59aa19bd2 | ||
|
|
efc1d909b3 | ||
|
|
cc7a4f2ddd | ||
|
|
ddbaea5e3f | ||
|
|
1fd325a44f | ||
|
|
2ad248b6d7 | ||
|
|
10e1315afa | ||
|
|
4ddacb5269 | ||
|
|
597a1fa878 | ||
|
|
3bf5d823ee | ||
|
|
df3b79478e | ||
|
|
f383dedb24 | ||
|
|
592ec14b26 | ||
|
|
76126ae386 | ||
|
|
c211c179b6 | ||
|
|
4818b57fb2 | ||
|
|
70df56f745 | ||
|
|
2b76e7d5c8 | ||
|
|
947180a6a6 | ||
|
|
d25755474f | ||
|
|
888cbbec68 | ||
|
|
46884e3fec | ||
|
|
589c15de9d | ||
|
|
0271e62439 | ||
|
|
84a5cbd44b | ||
|
|
a386ea5cad | ||
|
|
599ee1a360 | ||
|
|
d176285021 | ||
|
|
40337ee698 | ||
|
|
d47d754ac9 | ||
|
|
666fe9aebb | ||
|
|
b8d5680011 | ||
|
|
4aa9bbaa23 | ||
|
|
9385e0f4c3 | ||
|
|
03df9e9c5d | ||
|
|
e8d110d207 | ||
|
|
6c41c0b14d | ||
|
|
e4f175b691 | ||
|
|
4b6c7f062d | ||
|
|
2fa80f65d0 | ||
|
|
7fb3a23a5a | ||
|
|
9169165a03 | ||
|
|
608ecf36af | ||
|
|
df29c9ee72 | ||
|
|
e3809f5bc3 | ||
|
|
ff317f1c3a | ||
|
|
e6b7f73be2 | ||
|
|
d65bdb1482 | ||
|
|
ff7c501621 | ||
|
|
60f286c8af | ||
|
|
a2c8c5a9c4 | ||
|
|
2eea3e2e34 | ||
|
|
040cf0b83b | ||
|
|
9d2f2fe3aa | ||
|
|
a08f63c7d8 | ||
|
|
3ff5bbb5a7 | ||
|
|
535bdb5183 | ||
|
|
4505e86363 | ||
|
|
7b2e86f32e |
+3
-3
@@ -51,8 +51,7 @@
|
|||||||
"ul": ["list"],
|
"ul": ["list"],
|
||||||
"ol": ["list"]
|
"ol": ["list"]
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"curly": "warn"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -75,7 +74,8 @@
|
|||||||
"prefer": "type-imports",
|
"prefer": "type-imports",
|
||||||
"disallowTypeAnnotations": true
|
"disallowTypeAnnotations": true
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"curly": ["error", "multi-line"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
name: "\U0001F41B Bug"
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: "\U0001F41B bug"
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
## Steps to Reproduce
|
||||||
|
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
## Expected behavior
|
||||||
|
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
## Device details
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Smartphone (please complete the following information):**
|
||||||
|
|
||||||
|
- Device: [e.g. iPhone6]
|
||||||
|
- OS: [e.g. iOS8.1]
|
||||||
|
- Browser [e.g. stock browser, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
## Additional context
|
||||||
|
|
||||||
|
Add any other context about the problem here.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: Chore
|
||||||
|
about:
|
||||||
|
A template to capture non feature / enhancement work we need to do like work
|
||||||
|
to support other functions or housekeeping etc.
|
||||||
|
title: ''
|
||||||
|
labels: chore
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Chore
|
||||||
|
|
||||||
|
What we need to achieve and who for
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] What do we need to do first
|
||||||
|
- [ ] and then what?
|
||||||
|
- [ ] Etc.
|
||||||
|
|
||||||
|
## Additional details / background info
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
name: User Story
|
||||||
|
about:
|
||||||
|
A template to describe a features or enhancements we want to make from a users
|
||||||
|
perspective to ensure we understand what is needed and the value it would add
|
||||||
|
title: ''
|
||||||
|
labels: feature
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## Story
|
||||||
|
|
||||||
|
As a
|
||||||
|
I want
|
||||||
|
So that
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] I can
|
||||||
|
- [ ] I can
|
||||||
|
- [ ] I can
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Explore and sketch
|
||||||
|
- [ ] Team and stakeholder review
|
||||||
|
- [ ] Visual Design
|
||||||
|
- [ ] Team review
|
||||||
|
- [ ] Etc.
|
||||||
|
|
||||||
|
## Sketch
|
||||||
|
|
||||||
|
## Additional details / background info
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
inputs:
|
||||||
|
all:
|
||||||
|
description: 'Install all binaries'
|
||||||
|
default: false
|
||||||
|
version:
|
||||||
|
description: 'Vega version'
|
||||||
|
gobin:
|
||||||
|
description: 'GOBIN path'
|
||||||
|
default: '/home/runner/go/bin'
|
||||||
|
runs:
|
||||||
|
using: 'composite'
|
||||||
|
steps:
|
||||||
|
- name: Install Vega binaries
|
||||||
|
if: ${{ inputs.all }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/vega-linux-amd64.zip' -q
|
||||||
|
unzip vega-linux-amd64.zip -d ${{ inputs.gobin }}
|
||||||
|
|
||||||
|
- name: Install date-node binaries
|
||||||
|
if: ${{ inputs.all }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/data-node-linux-amd64.zip' -q
|
||||||
|
unzip data-node-linux-amd64.zip -d ${{ inputs.gobin }}
|
||||||
|
|
||||||
|
- name: Install Vega wallet binaries
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/vegawallet-linux-amd64.zip' -q
|
||||||
|
unzip vegawallet-linux-amd64.zip -d ${{ inputs.gobin }}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
inputs:
|
||||||
|
github-token:
|
||||||
|
description: 'github token'
|
||||||
|
runs:
|
||||||
|
using: 'composite'
|
||||||
|
steps:
|
||||||
|
- name: Checkout capsule
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: vegaprotocol/vegacapsule
|
||||||
|
ref: main
|
||||||
|
token: ${{ inputs.github-token }}
|
||||||
|
path: './capsule'
|
||||||
|
|
||||||
|
- name: Build capsule
|
||||||
|
run: go install
|
||||||
|
shell: bash
|
||||||
|
working-directory: capsule
|
||||||
|
|
||||||
|
- name: Login to docker
|
||||||
|
shell: bash
|
||||||
|
run: echo -n ${{ inputs.github-token }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
|
||||||
|
|
||||||
|
- name: Start nomad
|
||||||
|
shell: bash
|
||||||
|
run: vegacapsule nomad &
|
||||||
|
|
||||||
|
- name: Bootstrap network
|
||||||
|
shell: bash
|
||||||
|
run: vegacapsule network bootstrap --config-path=./frontend-monorepo/vegacapsule/config.hcl --force
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
inputs:
|
||||||
|
recovery:
|
||||||
|
description: 'Recovery phrase'
|
||||||
|
passphrase:
|
||||||
|
description: 'Wallet password'
|
||||||
|
capsule:
|
||||||
|
description: 'Is Capsule network used'
|
||||||
|
default: false
|
||||||
|
runs:
|
||||||
|
using: 'composite'
|
||||||
|
steps:
|
||||||
|
- name: Create passphrase
|
||||||
|
shell: bash
|
||||||
|
run: echo "${{ inputs.passphrase }}" > ./passphrase
|
||||||
|
|
||||||
|
- name: Create recovery
|
||||||
|
shell: bash
|
||||||
|
run: echo "${{ inputs.recovery }}" > ./recovery
|
||||||
|
|
||||||
|
- name: Initialize wallet
|
||||||
|
shell: bash
|
||||||
|
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
|
||||||
|
|
||||||
|
- name: Import wallet
|
||||||
|
shell: bash
|
||||||
|
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
||||||
|
|
||||||
|
- name: Create public key 2
|
||||||
|
shell: bash
|
||||||
|
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Capsule tests -- manual trigger
|
name: Cypress tests -- manual trigger
|
||||||
|
|
||||||
# This workflow runs the frontend tests against chosen branch
|
# This workflow runs the frontend tests against chosen branch
|
||||||
|
|
||||||
@@ -15,20 +15,43 @@ on:
|
|||||||
- stats-e2e
|
- stats-e2e
|
||||||
- token-e2e
|
- token-e2e
|
||||||
- trading-e2e
|
- trading-e2e
|
||||||
includeFlows:
|
smokeOnly:
|
||||||
description: 'Include long flows (if any)?'
|
description: 'Run only smoke tests?'
|
||||||
required: true
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: false
|
||||||
|
skip-nx-cache:
|
||||||
|
description: 'Add --skip-nx-cache to cypress test'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
manual:
|
manual:
|
||||||
name: Run capsule tests -- manual trigger
|
name: Run Cypress tests -- manual trigger
|
||||||
runs-on: ubuntu-latest
|
runs-on: self-hosted
|
||||||
env:
|
env:
|
||||||
GO111MODULE: 'on'
|
GO111MODULE: 'on'
|
||||||
GOPROXY: ${{ secrets.GO_PROXY }}
|
GOBIN: /home/runner/go/bin
|
||||||
|
VEGA_VERSION: 'v0.57.0'
|
||||||
steps:
|
steps:
|
||||||
|
#######
|
||||||
|
## Setup flags
|
||||||
|
#######
|
||||||
|
|
||||||
|
- name: Set smoke tag
|
||||||
|
if: ${{ github.event.inputs.smokeOnly == 'true' }}
|
||||||
|
run: echo TAGS="--env.grepTags @smoke" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set --skip-nx-cache flag
|
||||||
|
if: ${{ github.event.inputs.skip-nx-cache == 'true' }}
|
||||||
|
run: echo SKIP_NX_CACHE="--skip-nx-cache" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# See if we capsule is needed for this project
|
||||||
|
- name: Set capsule flag
|
||||||
|
if: ${{ github.event.inputs.project == 'explorer-e2e' || github.event.inputs.project == 'token-e2e' }}
|
||||||
|
run: echo RUN_CAPSULE=true >> $GITHUB_ENV
|
||||||
|
|
||||||
#######
|
#######
|
||||||
## Setup langs
|
## Setup langs
|
||||||
#######
|
#######
|
||||||
@@ -37,6 +60,7 @@ jobs:
|
|||||||
id: go
|
id: go
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
|
|
||||||
- name: Set up Node 16
|
- name: Set up Node 16
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
id: npm
|
id: npm
|
||||||
@@ -44,112 +68,92 @@ jobs:
|
|||||||
node-version: 16
|
node-version: 16
|
||||||
|
|
||||||
#######
|
#######
|
||||||
## Checkout capsule
|
## Install Yarn
|
||||||
|
#######
|
||||||
|
- name: Setup yarn
|
||||||
|
run: npm install -g yarn
|
||||||
|
|
||||||
|
#######
|
||||||
|
## Checkout repos
|
||||||
#######
|
#######
|
||||||
|
|
||||||
# Checkout front ends
|
# Checkout front ends
|
||||||
- name: Checkout frontend mono repo
|
- name: Checkout frontend mono repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
path: './frontend-monorepo'
|
path: './frontend-monorepo'
|
||||||
|
|
||||||
# Checkout capsule to build local network
|
# Restore node_modules from cache if possible
|
||||||
- name: Checkout capsule
|
- name: Restore node_modules from cache
|
||||||
uses: actions/checkout@v2
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
repository: vegaprotocol/vegacapsule
|
path: |
|
||||||
ref: main
|
frontend-monorepo/node_modules
|
||||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
/home/runner/.cache/Cypress
|
||||||
path: './capsule'
|
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
|
||||||
|
|
||||||
|
# Install frontend dependencies
|
||||||
|
- name: Install root dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
working-directory: frontend-monorepo
|
||||||
|
|
||||||
#######
|
#######
|
||||||
## Build binaries
|
## Build and run Vegacapsule network
|
||||||
#######
|
#######
|
||||||
- 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
|
- name: Install Vega binaries
|
||||||
run: go install -v ./cmd/vega
|
uses: ./frontend-monorepo/.github/actions/install-vega-binaries
|
||||||
working-directory: vega
|
with:
|
||||||
|
all: ${{ env.RUN_CAPSULE }}
|
||||||
|
version: ${{ env.VEGA_VERSION }}
|
||||||
|
gobin: ${{ env.GOBIN }}
|
||||||
|
|
||||||
- name: Install Vega wallet binaries
|
- name: Build and run Vegacapsule network
|
||||||
run: go install ./cmd/vegawallet
|
if: ${{ env.RUN_CAPSULE }}
|
||||||
working-directory: vega
|
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
|
||||||
|
with:
|
||||||
- name: Install date-node binaries
|
github-token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||||
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
|
## 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
|
- name: Set up Vegawallet
|
||||||
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
|
uses: ./frontend-monorepo/.github/actions/setup-vegawallet
|
||||||
- name: Import wallet
|
with:
|
||||||
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
recovery: ${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}
|
||||||
- name: Create public key 2
|
passphrase: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||||
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
capsule: ${{ env.RUN_CAPSULE }}
|
||||||
- name: Start service
|
|
||||||
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
|
|
||||||
|
|
||||||
######
|
######
|
||||||
## Run some tests
|
## Run some tests
|
||||||
######
|
######
|
||||||
|
|
||||||
- name: Install root dependencies
|
# To make sure that all Cypress binaries are installed properly
|
||||||
run: yarn install
|
- name: Install cypress bins
|
||||||
|
run: yarn cypress install
|
||||||
working-directory: frontend-monorepo
|
working-directory: frontend-monorepo
|
||||||
|
|
||||||
- name: Run Cypress tests
|
- name: Run Cypress tests
|
||||||
run: yarn nx run ${{ github.event.inputs.project }}:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
|
run: yarn nx run ${{ github.event.inputs.project }}:e2e ${{ env.SKIP_NX_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome ${{ env.TAGS }}
|
||||||
working-directory: frontend-monorepo
|
working-directory: frontend-monorepo
|
||||||
env:
|
env:
|
||||||
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||||
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
|
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPRESS_ETH_WALLET_MNEMONIC }}
|
||||||
CYPRESS_INCLUDE_FLOWS: ${{ github.event.inputs.includeFlows }}
|
CYPRESS_TEARDOWN_NETWORK_AFTER_FLOWS: true
|
||||||
|
|
||||||
######
|
######
|
||||||
## Upload logs
|
## Upload logs
|
||||||
######
|
######
|
||||||
|
|
||||||
- name: Logs
|
- name: Logs
|
||||||
if: ${{ always() }}
|
if: ${{ env.RUN_CAPSULE }}
|
||||||
run: vegacapsule network logs > vega-capsule-logs.txt
|
run: vegacapsule network logs > vega-capsule-logs.txt
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
|
if: ${{ env.RUN_CAPSULE }}
|
||||||
with:
|
with:
|
||||||
name: logs
|
name: logs
|
||||||
path: ./vega-capsule-logs.txt
|
path: ./vega-capsule-logs.txt
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Capsule tests -- night run
|
name: Cypress tests -- night run
|
||||||
|
|
||||||
# This workflow runs the frontend tests against latest develop of the core to preempt breaking changes
|
# This workflow runs the frontend tests against latest develop of the core to preempt breaking changes
|
||||||
|
|
||||||
@@ -8,10 +8,14 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
nightly:
|
nightly:
|
||||||
name: Run capsule tests -- nightly
|
name: Run Cypress tests -- nightly
|
||||||
runs-on: ubuntu-latest
|
runs-on: self-hosted
|
||||||
|
timeout-minutes: 60
|
||||||
env:
|
env:
|
||||||
GO111MODULE: 'on'
|
GO111MODULE: 'on'
|
||||||
|
GOBIN: /home/runner/go/bin
|
||||||
|
VEGA_VERSION: 'v0.57.0'
|
||||||
|
RUN_CAPSULE: true
|
||||||
steps:
|
steps:
|
||||||
#######
|
#######
|
||||||
## Setup langs
|
## Setup langs
|
||||||
@@ -28,110 +32,88 @@ jobs:
|
|||||||
node-version: 16
|
node-version: 16
|
||||||
|
|
||||||
#######
|
#######
|
||||||
## Checkout capsule
|
## Install Yarn
|
||||||
|
#######
|
||||||
|
- name: Setup yarn
|
||||||
|
run: npm install -g yarn
|
||||||
|
|
||||||
|
#######
|
||||||
|
## Checkout repos
|
||||||
#######
|
#######
|
||||||
|
|
||||||
# Checkout front ends
|
# Checkout front ends
|
||||||
- name: Checkout frontend mono repo
|
- name: Checkout frontend mono repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
path: './frontend-monorepo'
|
path: './frontend-monorepo'
|
||||||
|
|
||||||
# Checkout capsule to build local network
|
# Restore node_modules from cache if possible
|
||||||
- name: Checkout capsule
|
- name: Restore node_modules from cache
|
||||||
uses: actions/checkout@v2
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
repository: vegaprotocol/vegacapsule
|
path: |
|
||||||
ref: main
|
frontend-monorepo/node_modules
|
||||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
/home/runner/.cache/Cypress
|
||||||
path: './capsule'
|
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
|
||||||
|
|
||||||
|
# Install frontend dependencies
|
||||||
|
- name: Install root dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
working-directory: frontend-monorepo
|
||||||
|
|
||||||
#######
|
#######
|
||||||
## Build binaries
|
## Build and run Vegacapsule network
|
||||||
#######
|
#######
|
||||||
- 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
|
- name: Install Vega binaries
|
||||||
run: go install -v ./cmd/vega
|
uses: ./frontend-monorepo/.github/actions/install-vega-binaries
|
||||||
working-directory: vega
|
with:
|
||||||
|
all: ${{ env.RUN_CAPSULE }}
|
||||||
|
version: ${{ env.VEGA_VERSION }}
|
||||||
|
gobin: ${{ env.GOBIN }}
|
||||||
|
|
||||||
- name: Install Vega wallet binaries
|
- name: Build and run Vegacapsule network
|
||||||
run: go install ./cmd/vegawallet
|
if: ${{ env.RUN_CAPSULE }}
|
||||||
working-directory: vega
|
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
|
||||||
|
with:
|
||||||
- name: Install date-node binaries
|
github-token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||||
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
|
## 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
|
- name: Set up Vegawallet
|
||||||
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
|
uses: ./frontend-monorepo/.github/actions/setup-vegawallet
|
||||||
- name: Import wallet
|
with:
|
||||||
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
recovery: ${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}
|
||||||
- name: Create public key 2
|
passphrase: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||||
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
capsule: ${{ env.RUN_CAPSULE }}
|
||||||
- name: Start service
|
|
||||||
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
|
|
||||||
|
|
||||||
######
|
######
|
||||||
## Run some tests
|
## Run some tests
|
||||||
######
|
######
|
||||||
|
|
||||||
- name: Install root dependencies
|
# To make sure that all Cypress binaries are installed properly
|
||||||
run: yarn install
|
- name: Install cypress bins
|
||||||
|
run: yarn cypress install
|
||||||
working-directory: frontend-monorepo
|
working-directory: frontend-monorepo
|
||||||
|
|
||||||
- name: Run Cypress tests
|
- name: Run Cypress tests
|
||||||
run: yarn nx run-many --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
|
run: yarn nx run-many --skip-nx-cache --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
|
||||||
working-directory: frontend-monorepo
|
working-directory: frontend-monorepo
|
||||||
env:
|
env:
|
||||||
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||||
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
|
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPRESS_ETH_WALLET_MNEMONIC }}
|
||||||
CYPRESS_NIGHTLY_RUN: true
|
CYPRESS_NIGHTLY_RUN: true
|
||||||
CYPRESS_INCLUDE_FLOWS: true
|
CYPRESS_TEARDOWN_NETWORK_AFTER_FLOWS: true
|
||||||
|
|
||||||
######
|
######
|
||||||
## Upload logs
|
## Upload logs
|
||||||
######
|
######
|
||||||
|
|
||||||
- name: Logs
|
- name: Logs
|
||||||
if: ${{ always() }}
|
|
||||||
run: vegacapsule network logs > vega-capsule-logs.txt
|
run: vegacapsule network logs > vega-capsule-logs.txt
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
name: Capsule tests
|
name: Capsule Cypress tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
|
||||||
- develop
|
- develop
|
||||||
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
@@ -14,11 +14,13 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pr:
|
pr:
|
||||||
name: Run capsule tests - PR
|
name: Run Cypress tests - PR
|
||||||
runs-on: ubuntu-latest
|
runs-on: self-hosted
|
||||||
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == false }}
|
timeout-minutes: 30
|
||||||
env:
|
env:
|
||||||
GO111MODULE: 'on'
|
GO111MODULE: 'on'
|
||||||
|
GOBIN: /home/runner/go/bin
|
||||||
|
VEGA_VERSION: 'v0.57.0'
|
||||||
steps:
|
steps:
|
||||||
#######
|
#######
|
||||||
## Setup langs
|
## Setup langs
|
||||||
@@ -28,6 +30,7 @@ jobs:
|
|||||||
id: go
|
id: go
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
|
|
||||||
- name: Set up Node 16
|
- name: Set up Node 16
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
id: npm
|
id: npm
|
||||||
@@ -35,119 +38,101 @@ jobs:
|
|||||||
node-version: 16
|
node-version: 16
|
||||||
|
|
||||||
#######
|
#######
|
||||||
## Checkout capsule
|
## Install Yarn
|
||||||
|
#######
|
||||||
|
- name: Setup yarn
|
||||||
|
run: npm install -g yarn
|
||||||
|
|
||||||
|
#######
|
||||||
|
## Checkout repos
|
||||||
#######
|
#######
|
||||||
|
|
||||||
# Checkout front ends
|
# Checkout front ends
|
||||||
- name: Checkout frontend mono repo
|
- name: Checkout frontend mono repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
path: './frontend-monorepo'
|
path: './frontend-monorepo'
|
||||||
|
|
||||||
# Checkout capsule to build local network
|
# Restore node_modules from cache if possible
|
||||||
- name: Checkout capsule
|
- name: Restore node_modules from cache
|
||||||
uses: actions/checkout@v2
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
repository: vegaprotocol/vegacapsule
|
path: |
|
||||||
ref: main
|
frontend-monorepo/node_modules
|
||||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
/home/runner/.cache/Cypress
|
||||||
path: './capsule'
|
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
|
||||||
|
|
||||||
|
# Install frontend dependencies
|
||||||
|
- name: Install root dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
working-directory: frontend-monorepo
|
||||||
|
|
||||||
|
# Check SHAs
|
||||||
|
- 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: ${{ github.base_ref || github.ref_name }}
|
||||||
|
set-environment-variables-for-job: true
|
||||||
|
|
||||||
#######
|
#######
|
||||||
## Build binaries
|
## Build and run Vegacapsule network
|
||||||
#######
|
#######
|
||||||
- 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
|
- name: Install Vega binaries
|
||||||
run: go install -v ./cmd/vega
|
uses: ./frontend-monorepo/.github/actions/install-vega-binaries
|
||||||
working-directory: vega
|
with:
|
||||||
|
version: ${{ env.VEGA_VERSION }}
|
||||||
|
gobin: ${{ env.GOBIN }}
|
||||||
|
|
||||||
- name: Install Vega wallet binaries
|
- name: Build and run Vegacapsule network
|
||||||
run: go install ./cmd/vegawallet
|
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
|
||||||
working-directory: vega
|
with:
|
||||||
|
github-token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||||
- 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
|
## 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
|
- name: Set up Vegawallet
|
||||||
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
|
uses: ./frontend-monorepo/.github/actions/setup-vegawallet
|
||||||
- name: Import wallet
|
with:
|
||||||
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
recovery: ${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}
|
||||||
- name: Create public key 2
|
passphrase: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||||
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
capsule: true
|
||||||
- name: Start service
|
|
||||||
|
- name: Start service using capsule network
|
||||||
|
shell: bash
|
||||||
|
if: ${{ inputs.capsule }}
|
||||||
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
|
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
|
||||||
|
|
||||||
######
|
######
|
||||||
## Run some tests
|
## Run some tests
|
||||||
######
|
######
|
||||||
|
|
||||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
# To make sure that all Cypress binaries are installed properly
|
||||||
uses: nrwl/nx-set-shas@v2
|
- name: Install cypress bins
|
||||||
with:
|
run: yarn cypress install
|
||||||
working-directory: frontend-monorepo
|
|
||||||
main-branch-name: master
|
|
||||||
|
|
||||||
- name: Install root dependencies
|
|
||||||
run: yarn install
|
|
||||||
working-directory: frontend-monorepo
|
working-directory: frontend-monorepo
|
||||||
|
|
||||||
- name: Run Cypress tests
|
- name: Run Cypress tests
|
||||||
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
|
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --env.grepTags='@smoke' --browser chrome --exclude=trading,trading-e2e,console-lite,console-lite-e2e
|
||||||
working-directory: frontend-monorepo
|
working-directory: frontend-monorepo
|
||||||
env:
|
env:
|
||||||
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||||
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
|
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPRESS_ETH_WALLET_MNEMONIC }}
|
||||||
CYPRESS_INCLUDE_FLOWS: false
|
CYPRESS_TEARDOWN_NETWORK_AFTER_FLOWS: true
|
||||||
|
|
||||||
######
|
######
|
||||||
## Upload logs
|
## Upload logs
|
||||||
######
|
######
|
||||||
|
|
||||||
- name: Logs
|
- name: Logs
|
||||||
if: ${{ always() }}
|
|
||||||
run: vegacapsule network logs > vega-capsule-logs.txt
|
run: vegacapsule network logs > vega-capsule-logs.txt
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
if: ${{ always() }}
|
|
||||||
with:
|
with:
|
||||||
name: logs
|
name: logs
|
||||||
path: ./vega-capsule-logs.txt
|
path: ./vega-capsule-logs.txt
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
name: Cypress tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
- ready_for_review
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pr:
|
||||||
|
name: Run Cypress tests - PR
|
||||||
|
runs-on: self-hosted
|
||||||
|
timeout-minutes: 30
|
||||||
|
env:
|
||||||
|
GO111MODULE: 'on'
|
||||||
|
GOBIN: /home/runner/go/bin
|
||||||
|
VEGA_VERSION: 'v0.57.0'
|
||||||
|
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
|
||||||
|
|
||||||
|
#######
|
||||||
|
## Install Yarn
|
||||||
|
#######
|
||||||
|
- name: Setup yarn
|
||||||
|
run: npm install -g yarn
|
||||||
|
|
||||||
|
#######
|
||||||
|
## Checkout repos
|
||||||
|
#######
|
||||||
|
|
||||||
|
# Checkout front ends
|
||||||
|
- name: Checkout frontend mono repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
path: './frontend-monorepo'
|
||||||
|
|
||||||
|
# Restore node_modules from cache if possible
|
||||||
|
- name: Restore node_modules from cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
frontend-monorepo/node_modules
|
||||||
|
/home/runner/.cache/Cypress
|
||||||
|
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
|
||||||
|
|
||||||
|
# Install frontend dependencies
|
||||||
|
- name: Install root dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
working-directory: frontend-monorepo
|
||||||
|
|
||||||
|
# Check SHAs
|
||||||
|
- 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: ${{ github.base_ref || github.ref_name }}
|
||||||
|
set-environment-variables-for-job: true
|
||||||
|
|
||||||
|
#######
|
||||||
|
## Install Vega wallet
|
||||||
|
#######
|
||||||
|
|
||||||
|
- name: Install Vega wallet binaries
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/vegawallet-linux-amd64.zip' -q
|
||||||
|
unzip vegawallet-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||||
|
|
||||||
|
######
|
||||||
|
## Setup a Vega wallet for our user
|
||||||
|
######
|
||||||
|
|
||||||
|
- name: Set up Vegawallet
|
||||||
|
uses: ./frontend-monorepo/.github/actions/setup-vegawallet
|
||||||
|
with:
|
||||||
|
recovery: ${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}
|
||||||
|
passphrase: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||||
|
capsule: false
|
||||||
|
|
||||||
|
- name: Import fairground network
|
||||||
|
shell: bash
|
||||||
|
run: vegawallet network import --from-url="https://raw.githubusercontent.com/vegaprotocol/networks/master/fairground/fairground.toml" --force --home ~/.vegacapsule/testnet/wallet
|
||||||
|
|
||||||
|
- name: Start service using fairground network
|
||||||
|
shell: bash
|
||||||
|
run: vegawallet service run --network fairground --automatic-consent --home ~/.vegacapsule/testnet/wallet &
|
||||||
|
|
||||||
|
######
|
||||||
|
## Run some tests
|
||||||
|
######
|
||||||
|
|
||||||
|
# To make sure that all Cypress binaries are installed properly
|
||||||
|
- name: Install cypress bins
|
||||||
|
run: yarn cypress install
|
||||||
|
working-directory: frontend-monorepo
|
||||||
|
|
||||||
|
- name: Run Cypress tests
|
||||||
|
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --env.grepTags='@smoke' --browser chrome --exclude=explorer,explorer-e2e,token,token-e2e
|
||||||
|
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.CYPRESS_ETH_WALLET_MNEMONIC }}
|
||||||
|
CYPRESS_TEARDOWN_NETWORK_AFTER_FLOWS: true
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
name: Deploy affected projects to IPFS
|
|
||||||
|
|
||||||
'on':
|
|
||||||
# Triggers the workflow on push to main branch
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
|
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
|
||||||
workflow_dispatch: null
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
master:
|
|
||||||
name: Publish NX Affected
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Use Node.js 16
|
|
||||||
id: Node
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: 16.14.0
|
|
||||||
- name: Install root dependencies
|
|
||||||
run: yarn install
|
|
||||||
- name: Run deploy for affected sites
|
|
||||||
env:
|
|
||||||
FLEEK_API_KEY: ${{ secrets.FLEEK_API_KEY }}
|
|
||||||
run: ./tools/ipfs-deploy.js
|
|
||||||
@@ -3,50 +3,16 @@ name: Unit tests & build
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
|
||||||
- develop
|
- develop
|
||||||
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
master:
|
|
||||||
name: Test and lint - main
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: 'read'
|
|
||||||
actions: 'read'
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
|
||||||
uses: nrwl/nx-set-shas@v2
|
|
||||||
with:
|
|
||||||
main-branch-name: master
|
|
||||||
- name: Use Node.js 16
|
|
||||||
id: Node
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 16.14.0
|
|
||||||
- name: Install root dependencies
|
|
||||||
run: yarn install
|
|
||||||
- name: Check formatting
|
|
||||||
run: yarn nx format:check
|
|
||||||
- name: Lint affected
|
|
||||||
run: yarn nx affected:lint --max-warnings=0
|
|
||||||
- name: Test affected
|
|
||||||
run: yarn nx affected:test
|
|
||||||
- name: Build affected
|
|
||||||
run: yarn nx affected:build
|
|
||||||
pr:
|
pr:
|
||||||
name: Test and lint - PR
|
name: Test and lint - PR
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: 'read'
|
contents: 'read'
|
||||||
actions: 'read'
|
actions: 'read'
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -55,14 +21,19 @@ jobs:
|
|||||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||||
uses: nrwl/nx-set-shas@v2
|
uses: nrwl/nx-set-shas@v2
|
||||||
with:
|
with:
|
||||||
main-branch-name: master
|
main-branch-name: ${{ github.base_ref }}
|
||||||
- name: Use Node.js 16
|
- name: Use Node.js 16
|
||||||
id: Node
|
id: Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16.14.0
|
node-version: 16.14.0
|
||||||
|
- name: Restore node_modules from cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: '**/node_modules'
|
||||||
|
key: node_modules-${{ hashFiles('**/yarn.lock') }}
|
||||||
- name: Install root dependencies
|
- name: Install root dependencies
|
||||||
run: yarn install
|
run: yarn install --frozen-lockfile
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: yarn nx format:check
|
run: yarn nx format:check
|
||||||
- name: Lint affected
|
- name: Lint affected
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ An application for the status of the Vega network. Showing block height and othe
|
|||||||
|
|
||||||
Hosting for static content being shared across apps, for example fonts.
|
Hosting for static content being shared across apps, for example fonts.
|
||||||
|
|
||||||
|
### [Multisig-signer](./apps/multisig-signer)
|
||||||
|
|
||||||
|
The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract.
|
||||||
|
|
||||||
# 🧱 Libraries in this repo
|
# 🧱 Libraries in this repo
|
||||||
|
|
||||||
### [UI toolkit](./libs/ui-toolkit)
|
### [UI toolkit](./libs/ui-toolkit)
|
||||||
|
|||||||
@@ -23,5 +23,4 @@ NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
|||||||
|
|
||||||
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
|
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
|
||||||
NX_VEGA_ENV=TESTNET
|
NX_VEGA_ENV=TESTNET
|
||||||
NX_VEGA_REST=https://api.n11.testnet.vega.xyz
|
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789/api/v1
|
NX_VEGA_WALLET_URL=http://localhost:1789/api/v1
|
||||||
|
|||||||
@@ -3,14 +3,18 @@ module.exports = defineConfig({
|
|||||||
projectId: 'et4snf',
|
projectId: 'et4snf',
|
||||||
|
|
||||||
e2e: {
|
e2e: {
|
||||||
|
setupNodeEvents(on, config) {
|
||||||
|
require('cypress-grep/src/plugin')(config);
|
||||||
|
return config;
|
||||||
|
},
|
||||||
baseUrl: 'http://localhost:4200',
|
baseUrl: 'http://localhost:4200',
|
||||||
fileServerFolder: '.',
|
fileServerFolder: '.',
|
||||||
fixturesFolder: false,
|
fixturesFolder: false,
|
||||||
specPattern: './src/integration/*.ts',
|
specPattern: './src/integration/*.ts',
|
||||||
excludeSpecPattern: '**/*.js',
|
excludeSpecPattern: '**/*.js',
|
||||||
modifyObstructiveCode: false,
|
modifyObstructiveCode: false,
|
||||||
supportFile: './src/support/index.ts',
|
supportFile: './src/support/index.js',
|
||||||
video: true,
|
video: false,
|
||||||
videoUploadOnPasses: false,
|
videoUploadOnPasses: false,
|
||||||
videosFolder: '../../dist/cypress/apps/console-lite-e2e/videos',
|
videosFolder: '../../dist/cypress/apps/console-lite-e2e/videos',
|
||||||
screenshotsFolder: '../../dist/cypress/apps/console-lite-e2e/screenshots',
|
screenshotsFolder: '../../dist/cypress/apps/console-lite-e2e/screenshots',
|
||||||
@@ -35,5 +39,8 @@ module.exports = defineConfig({
|
|||||||
TRADING_TEST_VEGA_WALLET_PASSPHRASE: '123',
|
TRADING_TEST_VEGA_WALLET_PASSPHRASE: '123',
|
||||||
ETH_WALLET_MNEMONIC:
|
ETH_WALLET_MNEMONIC:
|
||||||
'ugly gallery notice network true range brave clarify flat logic someone chunk',
|
'ugly gallery notice network true range brave clarify flat logic someone chunk',
|
||||||
|
grepTags: '@regression @smoke @slow',
|
||||||
|
grepFilterSpecs: true,
|
||||||
|
grepOmitFiltered: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
describe('simple trading app', () => {
|
describe('simple trading app', { tags: '@smoke' }, () => {
|
||||||
beforeEach(() => cy.visit('/'));
|
beforeEach(() => cy.visit('/'));
|
||||||
|
|
||||||
it('render', () => {
|
it('render', () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
describe('console lite header', () => {
|
describe('console lite header', { tags: '@smoke' }, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
window.localStorage.setItem('theme', 'dark');
|
window.localStorage.setItem('theme', 'dark');
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||||
|
import type { Markets } from '@vegaprotocol/market-list';
|
||||||
import {
|
import {
|
||||||
generateLongListMarkets,
|
generateLongListMarkets,
|
||||||
generateSimpleMarkets,
|
generateSimpleMarkets,
|
||||||
|
generateMarketsData,
|
||||||
|
generateMarketsCandles,
|
||||||
} from '../support/mocks/generate-markets';
|
} from '../support/mocks/generate-markets';
|
||||||
|
|
||||||
describe('market list', () => {
|
describe('market list', { tags: '@smoke' }, () => {
|
||||||
describe('simple url', () => {
|
describe('simple url', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasQuery(req, 'SimpleMarkets', generateSimpleMarkets());
|
aliasQuery(req, 'Markets', generateSimpleMarkets());
|
||||||
|
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||||
|
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||||
});
|
});
|
||||||
cy.visit('/markets');
|
cy.visit('/markets');
|
||||||
});
|
});
|
||||||
@@ -62,7 +67,9 @@ describe('market list', () => {
|
|||||||
describe('url params should select filters', () => {
|
describe('url params should select filters', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasQuery(req, 'SimpleMarkets', generateSimpleMarkets());
|
aliasQuery(req, 'Markets', generateSimpleMarkets());
|
||||||
|
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||||
|
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,10 +80,11 @@ describe('market list', () => {
|
|||||||
|
|
||||||
it('last asset (if exists)', () => {
|
it('last asset (if exists)', () => {
|
||||||
cy.visit('/markets');
|
cy.visit('/markets');
|
||||||
cy.wait('@SimpleMarkets').then((filters) => {
|
cy.wait('@Markets').then((filters) => {
|
||||||
if (filters?.response?.body?.data?.markets?.length) {
|
const data: Markets | undefined = filters?.response?.body?.data;
|
||||||
|
if (data.marketsConnection.edges.length) {
|
||||||
const asset =
|
const asset =
|
||||||
filters.response.body.data.markets[0].tradableInstrument.instrument
|
data.marketsConnection.edges[0].node.tradableInstrument.instrument
|
||||||
.product.settlementAsset.symbol;
|
.product.settlementAsset.symbol;
|
||||||
cy.visit(`/markets/Suspended/Future/${asset}`);
|
cy.visit(`/markets/Suspended/Future/${asset}`);
|
||||||
cy.getByTestId('market-assets-menu')
|
cy.getByTestId('market-assets-menu')
|
||||||
@@ -98,7 +106,9 @@ describe('market list', () => {
|
|||||||
it('handles 1000 markets', () => {
|
it('handles 1000 markets', () => {
|
||||||
cy.viewport(1440, 900);
|
cy.viewport(1440, 900);
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasQuery(req, 'SimpleMarkets', generateLongListMarkets(1000));
|
aliasQuery(req, 'Markets', generateLongListMarkets(1000));
|
||||||
|
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||||
|
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||||
});
|
});
|
||||||
performance.mark('start-1k');
|
performance.mark('start-1k');
|
||||||
cy.visit('/markets');
|
cy.visit('/markets');
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { connectVegaWallet } from '../support/connect-wallet';
|
import { connectVegaWallet } from '../support/connect-wallet';
|
||||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||||
import { generateSimpleMarkets } from '../support/mocks/generate-markets';
|
import {
|
||||||
|
generateMarketsCandles,
|
||||||
|
generateMarketsData,
|
||||||
|
generateSimpleMarkets,
|
||||||
|
} from '../support/mocks/generate-markets';
|
||||||
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
|
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
|
||||||
import { generateMarketTags } from '../support/mocks/generate-market-tags';
|
import { generateMarketTags } from '../support/mocks/generate-market-tags';
|
||||||
import { generateMarketPositions } from '../support/mocks/generate-market-positions';
|
import { generateMarketPositions } from '../support/mocks/generate-market-positions';
|
||||||
@@ -9,13 +13,17 @@ import { generatePartyBalance } from '../support/mocks/generate-party-balance';
|
|||||||
import { generatePartyMarketData } from '../support/mocks/generate-party-market-data';
|
import { generatePartyMarketData } from '../support/mocks/generate-party-market-data';
|
||||||
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
|
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
|
||||||
import { generateMarketNames } from '../support/mocks/generate-market-names';
|
import { generateMarketNames } from '../support/mocks/generate-market-names';
|
||||||
|
import { generateMarketDepth } from '../support/mocks/generate-market-depth';
|
||||||
|
import type { Market, Markets } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
describe('market selector', () => {
|
describe('market selector', { tags: '@smoke' }, () => {
|
||||||
let markets;
|
let markets: Market[];
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasQuery(req, 'SimpleMarkets', generateSimpleMarkets());
|
aliasQuery(req, 'Markets', generateSimpleMarkets());
|
||||||
aliasQuery(req, 'DealTicketQuery', generateDealTicket());
|
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||||
|
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||||
|
aliasQuery(req, 'DealTicket', generateDealTicket());
|
||||||
aliasQuery(req, 'MarketTags', generateMarketTags());
|
aliasQuery(req, 'MarketTags', generateMarketTags());
|
||||||
aliasQuery(req, 'MarketPositions', generateMarketPositions());
|
aliasQuery(req, 'MarketPositions', generateMarketPositions());
|
||||||
aliasQuery(req, 'EstimateOrder', generateEstimateOrder());
|
aliasQuery(req, 'EstimateOrder', generateEstimateOrder());
|
||||||
@@ -23,12 +31,14 @@ describe('market selector', () => {
|
|||||||
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
|
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
|
||||||
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
|
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
|
||||||
aliasQuery(req, 'MarketNames', generateMarketNames());
|
aliasQuery(req, 'MarketNames', generateMarketNames());
|
||||||
|
aliasQuery(req, 'MarketDepth', generateMarketDepth());
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.visit('/markets');
|
cy.visit('/markets');
|
||||||
cy.wait('@SimpleMarkets').then((response) => {
|
cy.wait('@Markets').then((response) => {
|
||||||
if (response.response.body.data?.markets?.length) {
|
const data: Markets | undefined = response?.response?.body?.data;
|
||||||
markets = response.response.body.data.markets;
|
if (data.marketsConnection.edges.length) {
|
||||||
|
markets = data.marketsConnection.edges.map((edge) => edge.node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -126,7 +136,7 @@ describe('market selector', () => {
|
|||||||
cy.getByTestId('market-pane')
|
cy.getByTestId('market-pane')
|
||||||
.children()
|
.children()
|
||||||
.find('[role="button"]')
|
.find('[role="button"]')
|
||||||
.should('have.length', 3);
|
.should('have.length', 9);
|
||||||
cy.get('div[role="dialog"]').should('have.class', 'w-screen');
|
cy.get('div[role="dialog"]').should('have.class', 'w-screen');
|
||||||
cy.getByTestId('dialog-close').click();
|
cy.getByTestId('dialog-close').click();
|
||||||
cy.get('input[placeholder="Search"]').should(
|
cy.get('input[placeholder="Search"]').should(
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||||
import { generateSimpleMarkets } from '../support/mocks/generate-markets';
|
import {
|
||||||
|
generateSimpleMarkets,
|
||||||
|
generateMarketsCandles,
|
||||||
|
generateMarketsData,
|
||||||
|
} from '../support/mocks/generate-markets';
|
||||||
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
|
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
|
||||||
import { generateMarketTags } from '../support/mocks/generate-market-tags';
|
import { generateMarketTags } from '../support/mocks/generate-market-tags';
|
||||||
import { generateMarketPositions } from '../support/mocks/generate-market-positions';
|
import { generateMarketPositions } from '../support/mocks/generate-market-positions';
|
||||||
@@ -9,13 +13,17 @@ import { generatePartyMarketData } from '../support/mocks/generate-party-market-
|
|||||||
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
|
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
|
||||||
import { generateMarketDepth } from '../support/mocks/generate-market-depth';
|
import { generateMarketDepth } from '../support/mocks/generate-market-depth';
|
||||||
import { connectVegaWallet } from '../support/connect-wallet';
|
import { connectVegaWallet } from '../support/connect-wallet';
|
||||||
|
import type { Markets, Market } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
describe('Market trade', () => {
|
describe('Market trade', { tags: '@smoke' }, () => {
|
||||||
let markets;
|
let markets: Market[];
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
|
aliasQuery(req, 'Markets', generateSimpleMarkets());
|
||||||
|
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||||
|
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||||
aliasQuery(req, 'SimpleMarkets', generateSimpleMarkets());
|
aliasQuery(req, 'SimpleMarkets', generateSimpleMarkets());
|
||||||
aliasQuery(req, 'DealTicketQuery', generateDealTicket());
|
aliasQuery(req, 'DealTicket', generateDealTicket());
|
||||||
aliasQuery(req, 'MarketTags', generateMarketTags());
|
aliasQuery(req, 'MarketTags', generateMarketTags());
|
||||||
aliasQuery(req, 'MarketPositions', generateMarketPositions());
|
aliasQuery(req, 'MarketPositions', generateMarketPositions());
|
||||||
aliasQuery(req, 'EstimateOrder', generateEstimateOrder());
|
aliasQuery(req, 'EstimateOrder', generateEstimateOrder());
|
||||||
@@ -25,9 +33,10 @@ describe('Market trade', () => {
|
|||||||
aliasQuery(req, 'MarketDepth', generateMarketDepth());
|
aliasQuery(req, 'MarketDepth', generateMarketDepth());
|
||||||
});
|
});
|
||||||
cy.visit('/markets');
|
cy.visit('/markets');
|
||||||
cy.wait('@SimpleMarkets').then((response) => {
|
cy.wait('@Markets').then((response) => {
|
||||||
if (response.response.body.data?.markets?.length) {
|
const data: Markets | undefined = response?.response?.body?.data;
|
||||||
markets = response.response.body.data.markets;
|
if (data.marketsConnection.edges.length) {
|
||||||
|
markets = data.marketsConnection.edges.map((edge) => edge.node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -290,7 +299,7 @@ describe('Market trade', () => {
|
|||||||
cy.getByTestId('place-order').click();
|
cy.getByTestId('place-order').click();
|
||||||
cy.getByTestId('dialog-title').should(
|
cy.getByTestId('dialog-title').should(
|
||||||
'have.text',
|
'have.text',
|
||||||
'Confirm transaction in wallet'
|
'Awaiting network confirmation'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,8 +2,17 @@ import {
|
|||||||
connectVegaWallet,
|
connectVegaWallet,
|
||||||
disconnectVegaWallet,
|
disconnectVegaWallet,
|
||||||
} from '../support/connect-wallet';
|
} from '../support/connect-wallet';
|
||||||
|
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||||
|
import {
|
||||||
|
generatePositions,
|
||||||
|
emptyPositions,
|
||||||
|
} from '../support/mocks/generate-positions';
|
||||||
|
import { generateAccounts } from '../support/mocks/generate-accounts';
|
||||||
|
import { generateOrders } from '../support/mocks/generate-orders';
|
||||||
|
import { generateFills } from '../support/mocks/generate-fills';
|
||||||
|
import { generateFillsMarkets } from '../support/mocks/generate-markets';
|
||||||
|
|
||||||
describe('Portfolio page', () => {
|
describe('Portfolio page', { tags: '@smoke' }, () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
disconnectVegaWallet();
|
disconnectVegaWallet();
|
||||||
});
|
});
|
||||||
@@ -19,19 +28,129 @@ describe('Portfolio page', () => {
|
|||||||
it('certain tabs should exist', () => {
|
it('certain tabs should exist', () => {
|
||||||
cy.visit('/portfolio');
|
cy.visit('/portfolio');
|
||||||
connectVegaWallet();
|
connectVegaWallet();
|
||||||
cy.getByTestId('Assets').should('exist');
|
|
||||||
cy.getByTestId('tab-assets').should('exist');
|
|
||||||
|
|
||||||
cy.getByTestId('Positions').click();
|
cy.getByTestId('assets').click();
|
||||||
cy.getByTestId('tab-positions').should('exist');
|
cy.location('pathname').should('eq', '/portfolio/assets');
|
||||||
|
|
||||||
cy.getByTestId('Orders').click();
|
cy.getByTestId('positions').click();
|
||||||
cy.getByTestId('tab-orders').should('exist');
|
cy.location('pathname').should('eq', '/portfolio/positions');
|
||||||
|
|
||||||
cy.getByTestId('Fills').click();
|
cy.getByTestId('orders').click();
|
||||||
cy.getByTestId('tab-fills').should('exist');
|
cy.location('pathname').should('eq', '/portfolio/orders');
|
||||||
|
|
||||||
cy.getByTestId('Deposits').click();
|
cy.getByTestId('fills').click();
|
||||||
cy.getByTestId('tab-deposits').should('exist');
|
cy.location('pathname').should('eq', '/portfolio/fills');
|
||||||
|
|
||||||
|
cy.getByTestId('deposits').click();
|
||||||
|
cy.location('pathname').should('eq', '/portfolio/deposits');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Assets view', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasQuery(req, 'Positions', generatePositions());
|
||||||
|
aliasQuery(req, 'Accounts', generateAccounts());
|
||||||
|
});
|
||||||
|
cy.visit('/portfolio/assets');
|
||||||
|
connectVegaWallet();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('data should be properly rendered', () => {
|
||||||
|
cy.get('.ag-center-cols-container .ag-row').should('have.length', 3);
|
||||||
|
cy.get(
|
||||||
|
'.ag-center-cols-container [row-id="ACCOUNT_TYPE_GENERAL-asset-id-null"]'
|
||||||
|
)
|
||||||
|
.find('button')
|
||||||
|
.click();
|
||||||
|
cy.getByTestId('dialog-title').should(
|
||||||
|
'have.text',
|
||||||
|
'Asset details - tEURO'
|
||||||
|
);
|
||||||
|
cy.getByTestId('dialog-close').click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Positions view', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasQuery(req, 'Positions', generatePositions());
|
||||||
|
aliasQuery(req, 'Accounts', generateAccounts());
|
||||||
|
});
|
||||||
|
cy.visit('/portfolio/positions');
|
||||||
|
connectVegaWallet();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('data should be properly rendered', () => {
|
||||||
|
cy.getByTestId('positions-asset-tDAI').should('exist');
|
||||||
|
cy.getByTestId('positions-asset-tEURO').should('exist');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Orders view', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasQuery(req, 'Orders', generateOrders());
|
||||||
|
aliasQuery(req, 'Markets', generateFillsMarkets());
|
||||||
|
});
|
||||||
|
cy.visit('/portfolio/orders');
|
||||||
|
connectVegaWallet();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('data should be properly rendered', () => {
|
||||||
|
cy.get('.ag-center-cols-container .ag-row').should('have.length', 5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Fills view', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasQuery(req, 'Fills', generateFills());
|
||||||
|
aliasQuery(req, 'Markets', generateFillsMarkets());
|
||||||
|
});
|
||||||
|
cy.visit('/portfolio/fills');
|
||||||
|
connectVegaWallet();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('data should be properly rendered', () => {
|
||||||
|
cy.get('.ag-center-cols-container .ag-row').should('have.length', 4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Empty views', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.mockGQL((req) => {
|
||||||
|
aliasQuery(req, 'Positions', emptyPositions());
|
||||||
|
aliasQuery(req, 'Accounts', { party: null });
|
||||||
|
aliasQuery(req, 'Orders', { party: null });
|
||||||
|
aliasQuery(req, 'Fills', { party: null });
|
||||||
|
aliasQuery(req, 'Markets', {
|
||||||
|
marketsConnection: { edges: [], __typename: 'MarketConnection' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cy.visit('/portfolio');
|
||||||
|
connectVegaWallet();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('"No data to display" should be always displayed', () => {
|
||||||
|
cy.getByTestId('assets').click();
|
||||||
|
cy.get('div.flex.items-center.justify-center').contains(
|
||||||
|
'No data to display'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.getByTestId('positions').click();
|
||||||
|
cy.get('div.flex.items-center.justify-center').contains(
|
||||||
|
'No data to display'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.getByTestId('orders').click();
|
||||||
|
cy.get('div.flex.items-center.justify-center').contains(
|
||||||
|
'No data to display'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.getByTestId('fills').click();
|
||||||
|
cy.get('div.flex.items-center.justify-center').contains(
|
||||||
|
'No data to display'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+2
@@ -17,3 +17,5 @@ import '@vegaprotocol/cypress';
|
|||||||
import 'cypress-real-events/support';
|
import 'cypress-real-events/support';
|
||||||
// Import commands.js using ES2015 syntax:
|
// Import commands.js using ES2015 syntax:
|
||||||
import './commands';
|
import './commands';
|
||||||
|
import registerCypressGrep from 'cypress-grep';
|
||||||
|
registerCypressGrep();
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
const protoCandles = [
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
|
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
export const protoCandles = [
|
||||||
{ open: '9556163', close: '9587028', __typename: 'Candle' },
|
{ open: '9556163', close: '9587028', __typename: 'Candle' },
|
||||||
{
|
{
|
||||||
open: '9587028',
|
open: '9587028',
|
||||||
@@ -73,11 +76,29 @@ const protoCandles = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const protoMarket = {
|
export const protoMarket: Market = {
|
||||||
id: 'first-btcusd-id',
|
id: 'ca7768f6de84bf86a21bbb6b0109d9659c81917b0e0339b2c262566c9b581a15',
|
||||||
state: 'STATE_ACTIVE',
|
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||||
|
state: MarketState.STATE_ACTIVE,
|
||||||
|
decimalPlaces: 5,
|
||||||
|
positionDecimalPlaces: 0,
|
||||||
|
marketTimestamps: {
|
||||||
|
__typename: 'MarketTimestamps',
|
||||||
|
close: '',
|
||||||
|
open: '',
|
||||||
|
},
|
||||||
|
fees: {
|
||||||
|
__typename: 'Fees',
|
||||||
|
factors: {
|
||||||
|
__typename: 'FeeFactors',
|
||||||
|
makerFee: '',
|
||||||
|
infrastructureFee: '',
|
||||||
|
liquidityFee: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
tradableInstrument: {
|
tradableInstrument: {
|
||||||
instrument: {
|
instrument: {
|
||||||
|
id: '',
|
||||||
code: 'AAVEDAI.MF21',
|
code: 'AAVEDAI.MF21',
|
||||||
name: 'AAVEDAI Monthly (30 Jun 2022)',
|
name: 'AAVEDAI Monthly (30 Jun 2022)',
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -94,12 +115,11 @@ export const protoMarket = {
|
|||||||
product: {
|
product: {
|
||||||
__typename: 'Future',
|
__typename: 'Future',
|
||||||
quoteName: 'DAI',
|
quoteName: 'DAI',
|
||||||
settlementAsset: { symbol: 'tDAI', __typename: 'Asset' },
|
settlementAsset: { symbol: 'tDAI', __typename: 'Asset', decimals: 5 },
|
||||||
},
|
},
|
||||||
__typename: 'Instrument',
|
__typename: 'Instrument',
|
||||||
},
|
},
|
||||||
__typename: 'TradableInstrument',
|
__typename: 'TradableInstrument',
|
||||||
},
|
},
|
||||||
candles: protoCandles,
|
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import merge from 'lodash/merge';
|
||||||
|
import type { AccountsQuery } from '@vegaprotocol/accounts';
|
||||||
|
import { AccountType } from '@vegaprotocol/types';
|
||||||
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
|
||||||
|
export const generateAccounts = (
|
||||||
|
override?: PartialDeep<AccountsQuery>
|
||||||
|
): AccountsQuery => {
|
||||||
|
const defaultAccounts: AccountsQuery = {
|
||||||
|
party: {
|
||||||
|
__typename: 'Party',
|
||||||
|
id: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
__typename: 'Account',
|
||||||
|
type: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||||
|
balance: '100000000',
|
||||||
|
market: null,
|
||||||
|
asset: {
|
||||||
|
__typename: 'Asset',
|
||||||
|
id: 'asset-id',
|
||||||
|
symbol: 'tEURO',
|
||||||
|
decimals: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'Account',
|
||||||
|
type: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||||
|
balance: '100000000',
|
||||||
|
market: {
|
||||||
|
id: '0604e8c918655474525e1a95367902266ade70d318c2c908f0cca6e3d11dcb13',
|
||||||
|
tradableInstrument: {
|
||||||
|
__typename: 'TradableInstrument',
|
||||||
|
instrument: {
|
||||||
|
__typename: 'Instrument',
|
||||||
|
name: 'AAVEDAI Monthly (30 Jun 2022)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
__typename: 'Market',
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
__typename: 'Asset',
|
||||||
|
id: 'asset-id-2',
|
||||||
|
symbol: 'tDAI',
|
||||||
|
decimals: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'Account',
|
||||||
|
type: AccountType.ACCOUNT_TYPE_MARGIN,
|
||||||
|
balance: '1000',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
tradableInstrument: {
|
||||||
|
__typename: 'TradableInstrument',
|
||||||
|
instrument: {
|
||||||
|
__typename: 'Instrument',
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376',
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
__typename: 'Asset',
|
||||||
|
id: 'asset-id',
|
||||||
|
symbol: 'tEURO',
|
||||||
|
decimals: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'Account',
|
||||||
|
type: AccountType.ACCOUNT_TYPE_MARGIN,
|
||||||
|
balance: '1000',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
tradableInstrument: {
|
||||||
|
__typename: 'TradableInstrument',
|
||||||
|
instrument: {
|
||||||
|
__typename: 'Instrument',
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d',
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
__typename: 'Asset',
|
||||||
|
id: 'asset-id-2',
|
||||||
|
symbol: 'tDAI',
|
||||||
|
decimals: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'Account',
|
||||||
|
type: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||||
|
balance: '100000000',
|
||||||
|
market: null,
|
||||||
|
asset: {
|
||||||
|
__typename: 'Asset',
|
||||||
|
id: 'asset-0',
|
||||||
|
symbol: 'AST0',
|
||||||
|
decimals: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return merge(defaultAccounts, override);
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
export const generateDealTicket = () => {
|
export const generateDealTicket = () => {
|
||||||
return {
|
return {
|
||||||
market: {
|
market: {
|
||||||
id: 'first-btcusd-id',
|
id: 'ca7768f6de84bf86a21bbb6b0109d9659c81917b0e0339b2c262566c9b581a15',
|
||||||
decimalPlaces: 5,
|
decimalPlaces: 5,
|
||||||
positionDecimalPlaces: 0,
|
positionDecimalPlaces: 0,
|
||||||
state: 'STATE_ACTIVE',
|
state: 'STATE_ACTIVE',
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import type { FillsQuery, FillFieldsFragment } from '@vegaprotocol/fills';
|
||||||
|
import { Side } from '@vegaprotocol/types';
|
||||||
|
import merge from 'lodash/merge';
|
||||||
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
|
||||||
|
export const generateFills = (
|
||||||
|
override?: PartialDeep<FillsQuery>
|
||||||
|
): FillsQuery => {
|
||||||
|
const fills: FillFieldsFragment[] = [
|
||||||
|
generateFill({
|
||||||
|
buyer: {
|
||||||
|
id: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
generateFill({
|
||||||
|
id: '1',
|
||||||
|
seller: {
|
||||||
|
id: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||||
|
},
|
||||||
|
aggressor: Side.SIDE_SELL,
|
||||||
|
buyerFee: {
|
||||||
|
infrastructureFee: '5000',
|
||||||
|
},
|
||||||
|
market: {
|
||||||
|
id: 'market-1',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
generateFill({
|
||||||
|
id: '2',
|
||||||
|
seller: {
|
||||||
|
id: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||||
|
},
|
||||||
|
aggressor: Side.SIDE_BUY,
|
||||||
|
}),
|
||||||
|
generateFill({
|
||||||
|
id: '3',
|
||||||
|
aggressor: Side.SIDE_SELL,
|
||||||
|
market: {
|
||||||
|
id: 'market-2',
|
||||||
|
},
|
||||||
|
buyer: {
|
||||||
|
id: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultResult: FillsQuery = {
|
||||||
|
party: {
|
||||||
|
id: 'buyer-id',
|
||||||
|
tradesConnection: {
|
||||||
|
__typename: 'TradeConnection',
|
||||||
|
edges: fills.map((f) => {
|
||||||
|
return {
|
||||||
|
__typename: 'TradeEdge',
|
||||||
|
node: f,
|
||||||
|
cursor: '3',
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
pageInfo: {
|
||||||
|
__typename: 'PageInfo',
|
||||||
|
startCursor: '1',
|
||||||
|
endCursor: '2',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
__typename: 'Party',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return merge(defaultResult, override);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateFill = (override?: PartialDeep<FillFieldsFragment>) => {
|
||||||
|
const defaultFill: FillFieldsFragment = {
|
||||||
|
__typename: 'Trade',
|
||||||
|
id: '0',
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
price: '10000000',
|
||||||
|
size: '50000',
|
||||||
|
buyOrder: 'buy-order',
|
||||||
|
sellOrder: 'sell-order',
|
||||||
|
aggressor: Side.SIDE_BUY,
|
||||||
|
buyer: {
|
||||||
|
__typename: 'Party',
|
||||||
|
id: 'buyer-id',
|
||||||
|
},
|
||||||
|
seller: {
|
||||||
|
__typename: 'Party',
|
||||||
|
id: 'seller-id',
|
||||||
|
},
|
||||||
|
buyerFee: {
|
||||||
|
__typename: 'TradeFee',
|
||||||
|
makerFee: '100',
|
||||||
|
infrastructureFee: '100',
|
||||||
|
liquidityFee: '100',
|
||||||
|
},
|
||||||
|
sellerFee: {
|
||||||
|
__typename: 'TradeFee',
|
||||||
|
makerFee: '200',
|
||||||
|
infrastructureFee: '200',
|
||||||
|
liquidityFee: '200',
|
||||||
|
},
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: 'market-0',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return merge(defaultFill, override);
|
||||||
|
};
|
||||||
@@ -1,24 +1,15 @@
|
|||||||
export const generateMarketDepth = () => {
|
import merge from 'lodash/merge';
|
||||||
return {
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
|
||||||
|
import type { MarketDepth } from '../../../../../libs/market-depth/src/lib/__generated__/MarketDepth';
|
||||||
|
|
||||||
|
export const generateMarketDepth = (
|
||||||
|
override?: PartialDeep<MarketDepth>
|
||||||
|
): MarketDepth => {
|
||||||
|
const defaultResult: MarketDepth = {
|
||||||
market: {
|
market: {
|
||||||
id: 'a46bd7e5277087723b7ab835844dec3cef8b4445738101269624bf5537d5d423',
|
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: {
|
depth: {
|
||||||
lastTrade: { price: '9893006', __typename: 'Trade' },
|
|
||||||
sell: [
|
sell: [
|
||||||
{
|
{
|
||||||
price: '9893007',
|
price: '9893007',
|
||||||
@@ -215,4 +206,5 @@ export const generateMarketDepth = () => {
|
|||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
return merge(defaultResult, override);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const generateMarketPositions = () => {
|
|||||||
},
|
},
|
||||||
balance: '265329',
|
balance: '265329',
|
||||||
market: {
|
market: {
|
||||||
id: 'first-btcusd-id',
|
id: 'ca7768f6de84bf86a21bbb6b0109d9659c81917b0e0339b2c262566c9b581a15',
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -43,7 +43,7 @@ export const generateMarketPositions = () => {
|
|||||||
node: {
|
node: {
|
||||||
openVolume: '12',
|
openVolume: '12',
|
||||||
market: {
|
market: {
|
||||||
id: 'first-btcusd-id',
|
id: 'ca7768f6de84bf86a21bbb6b0109d9659c81917b0e0339b2c262566c9b581a15',
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
},
|
},
|
||||||
__typename: 'Position',
|
__typename: 'Position',
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,149 @@
|
|||||||
|
import merge from 'lodash/merge';
|
||||||
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
import type {
|
||||||
|
Orders,
|
||||||
|
Orders_party_ordersConnection_edges_node,
|
||||||
|
} from '@vegaprotocol/orders';
|
||||||
|
import {
|
||||||
|
OrderStatus,
|
||||||
|
OrderTimeInForce,
|
||||||
|
OrderType,
|
||||||
|
Side,
|
||||||
|
} from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
|
||||||
|
const orders: Orders_party_ordersConnection_edges_node[] = [
|
||||||
|
{
|
||||||
|
__typename: 'Order',
|
||||||
|
id: '066468C06549101DAF7BC51099E1412A0067DC08C246B7D8013C9D0CBF1E8EE7',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: 'market-0',
|
||||||
|
},
|
||||||
|
size: '10',
|
||||||
|
type: OrderType.TYPE_LIMIT,
|
||||||
|
status: OrderStatus.STATUS_FILLED,
|
||||||
|
side: Side.SIDE_BUY,
|
||||||
|
remaining: '0',
|
||||||
|
price: '20000000',
|
||||||
|
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
|
||||||
|
createdAt: new Date(2020, 1, 30).toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
expiresAt: null,
|
||||||
|
rejectionReason: null,
|
||||||
|
liquidityProvision: null,
|
||||||
|
peggedOrder: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'Order',
|
||||||
|
id: '48DB6767E4E4E0F649C5A13ABFADE39F8451C27DA828DAF14B7A1E8E5EBDAD99',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: 'market-1',
|
||||||
|
},
|
||||||
|
size: '1',
|
||||||
|
type: OrderType.TYPE_LIMIT,
|
||||||
|
status: OrderStatus.STATUS_FILLED,
|
||||||
|
side: Side.SIDE_BUY,
|
||||||
|
remaining: '0',
|
||||||
|
price: '100',
|
||||||
|
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
|
||||||
|
createdAt: new Date(2020, 1, 29).toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
expiresAt: null,
|
||||||
|
rejectionReason: null,
|
||||||
|
liquidityProvision: null,
|
||||||
|
peggedOrder: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'Order',
|
||||||
|
id: '4e93702990712c41f6995fcbbd94f60bb372ad12d64dfa7d96d205c49f790336',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: 'market-2',
|
||||||
|
},
|
||||||
|
size: '1',
|
||||||
|
type: OrderType.TYPE_LIMIT,
|
||||||
|
status: OrderStatus.STATUS_FILLED,
|
||||||
|
side: Side.SIDE_BUY,
|
||||||
|
remaining: '0',
|
||||||
|
price: '20000',
|
||||||
|
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
|
||||||
|
createdAt: new Date(2020, 1, 28).toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
expiresAt: null,
|
||||||
|
rejectionReason: null,
|
||||||
|
liquidityProvision: null,
|
||||||
|
peggedOrder: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'Order',
|
||||||
|
id: '94737d2bafafa4bc3b80a56ef084ae52a983b91aa067c31e243c61a0f962a836',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: 'market-3',
|
||||||
|
},
|
||||||
|
size: '1',
|
||||||
|
type: OrderType.TYPE_LIMIT,
|
||||||
|
status: OrderStatus.STATUS_ACTIVE,
|
||||||
|
side: Side.SIDE_BUY,
|
||||||
|
remaining: '0',
|
||||||
|
price: '100000',
|
||||||
|
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
|
||||||
|
createdAt: new Date(2020, 1, 27).toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
expiresAt: null,
|
||||||
|
rejectionReason: null,
|
||||||
|
liquidityProvision: null,
|
||||||
|
peggedOrder: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'Order',
|
||||||
|
id: '94aead3ca92dc932efcb503631b03a410e2a5d4606cae6083e2406dc38e52f78',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: 'market-3',
|
||||||
|
},
|
||||||
|
size: '10',
|
||||||
|
type: OrderType.TYPE_LIMIT,
|
||||||
|
status: OrderStatus.STATUS_PARTIALLY_FILLED,
|
||||||
|
side: Side.SIDE_SELL,
|
||||||
|
remaining: '3',
|
||||||
|
price: '100000',
|
||||||
|
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
|
||||||
|
createdAt: new Date(2020, 1, 27).toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
expiresAt: null,
|
||||||
|
liquidityProvision: null,
|
||||||
|
peggedOrder: null,
|
||||||
|
rejectionReason: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultResult: Orders = {
|
||||||
|
party: {
|
||||||
|
id: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||||
|
ordersConnection: {
|
||||||
|
__typename: 'OrderConnection',
|
||||||
|
edges: orders.map((f) => {
|
||||||
|
return {
|
||||||
|
__typename: 'OrderEdge',
|
||||||
|
node: f,
|
||||||
|
cursor: f.id,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
pageInfo: {
|
||||||
|
__typename: 'PageInfo',
|
||||||
|
startCursor:
|
||||||
|
'066468C06549101DAF7BC51099E1412A0067DC08C246B7D8013C9D0CBF1E8EE7',
|
||||||
|
endCursor:
|
||||||
|
'94737d2bafafa4bc3b80a56ef084ae52a983b91aa067c31e243c61a0f962a836',
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
__typename: 'Party',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return merge(defaultResult, override);
|
||||||
|
};
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
import merge from 'lodash/merge';
|
||||||
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
import type {
|
||||||
|
Positions,
|
||||||
|
Positions_party_positionsConnection_edges_node,
|
||||||
|
} from '@vegaprotocol/positions';
|
||||||
|
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
export const generatePositions = (
|
||||||
|
override?: PartialDeep<Positions>
|
||||||
|
): Positions => {
|
||||||
|
const nodes: Positions_party_positionsConnection_edges_node[] = [
|
||||||
|
{
|
||||||
|
__typename: 'Position',
|
||||||
|
realisedPNL: '0',
|
||||||
|
openVolume: '6',
|
||||||
|
unrealisedPNL: '895000',
|
||||||
|
averageEntryPrice: '1129935',
|
||||||
|
updatedAt: '2022-07-28T15:09:34.441143Z',
|
||||||
|
marginsConnection: {
|
||||||
|
__typename: 'MarginConnection',
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
__typename: 'MarginEdge',
|
||||||
|
node: {
|
||||||
|
__typename: 'MarginLevels',
|
||||||
|
maintenanceLevel: '0',
|
||||||
|
searchLevel: '0',
|
||||||
|
initialLevel: '0',
|
||||||
|
collateralReleaseLevel: '0',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d',
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
__typename: 'Asset',
|
||||||
|
symbol: 'tDAI',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
market: {
|
||||||
|
id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d',
|
||||||
|
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||||
|
data: {
|
||||||
|
markPrice: '17588787',
|
||||||
|
__typename: 'MarketData',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decimalPlaces: 5,
|
||||||
|
positionDecimalPlaces: 0,
|
||||||
|
tradableInstrument: {
|
||||||
|
instrument: {
|
||||||
|
name: 'UNIDAI Monthly (30 Jun 2022)',
|
||||||
|
__typename: 'Instrument',
|
||||||
|
},
|
||||||
|
__typename: 'TradableInstrument',
|
||||||
|
},
|
||||||
|
__typename: 'Market',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'Position',
|
||||||
|
realisedPNL: '100',
|
||||||
|
openVolume: '20',
|
||||||
|
unrealisedPNL: '895000',
|
||||||
|
averageEntryPrice: '8509338',
|
||||||
|
updatedAt: '2022-07-28T15:09:34.441143Z',
|
||||||
|
marginsConnection: {
|
||||||
|
__typename: 'MarginConnection',
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
__typename: 'MarginEdge',
|
||||||
|
node: {
|
||||||
|
__typename: 'MarginLevels',
|
||||||
|
maintenanceLevel: '0',
|
||||||
|
searchLevel: '0',
|
||||||
|
initialLevel: '0',
|
||||||
|
collateralReleaseLevel: '0',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: '0604e8c918655474525e1a95367902266ade70d318c2c908f0cca6e3d11dcb13',
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
__typename: 'Asset',
|
||||||
|
symbol: 'tDAI',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
market: {
|
||||||
|
id: '0604e8c918655474525e1a95367902266ade70d318c2c908f0cca6e3d11dcb13',
|
||||||
|
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||||
|
data: {
|
||||||
|
markPrice: '8649338',
|
||||||
|
__typename: 'MarketData',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: '0604e8c918655474525e1a95367902266ade70d318c2c908f0cca6e3d11dcb13',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decimalPlaces: 5,
|
||||||
|
positionDecimalPlaces: 0,
|
||||||
|
tradableInstrument: {
|
||||||
|
instrument: {
|
||||||
|
name: 'AAVEDAI Monthly (30 Jun 2022)',
|
||||||
|
__typename: 'Instrument',
|
||||||
|
},
|
||||||
|
__typename: 'TradableInstrument',
|
||||||
|
},
|
||||||
|
__typename: 'Market',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
realisedPNL: '0',
|
||||||
|
openVolume: '1',
|
||||||
|
unrealisedPNL: '-22519',
|
||||||
|
averageEntryPrice: '84400088',
|
||||||
|
updatedAt: '2022-07-28T14:53:54.725477Z',
|
||||||
|
marginsConnection: {
|
||||||
|
__typename: 'MarginConnection',
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
__typename: 'MarginEdge',
|
||||||
|
node: {
|
||||||
|
__typename: 'MarginLevels',
|
||||||
|
maintenanceLevel: '0',
|
||||||
|
searchLevel: '0',
|
||||||
|
initialLevel: '0',
|
||||||
|
collateralReleaseLevel: '0',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376',
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
__typename: 'Asset',
|
||||||
|
symbol: 'tEURO',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
market: {
|
||||||
|
id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376',
|
||||||
|
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||||
|
data: {
|
||||||
|
markPrice: '84377569',
|
||||||
|
__typename: 'MarketData',
|
||||||
|
market: {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decimalPlaces: 5,
|
||||||
|
positionDecimalPlaces: 0,
|
||||||
|
tradableInstrument: {
|
||||||
|
instrument: {
|
||||||
|
name: 'Tesla Quarterly (30 Jun 2022)',
|
||||||
|
__typename: 'Instrument',
|
||||||
|
},
|
||||||
|
__typename: 'TradableInstrument',
|
||||||
|
},
|
||||||
|
__typename: 'Market',
|
||||||
|
},
|
||||||
|
__typename: 'Position',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultResult: Positions = {
|
||||||
|
party: {
|
||||||
|
__typename: 'Party',
|
||||||
|
id: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||||
|
positionsConnection: {
|
||||||
|
__typename: 'PositionConnection',
|
||||||
|
edges: nodes.map((node) => {
|
||||||
|
return {
|
||||||
|
__typename: 'PositionEdge',
|
||||||
|
node,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return merge(defaultResult, override);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const emptyPositions = () => {
|
||||||
|
return {
|
||||||
|
party: {
|
||||||
|
id: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||||
|
positionsConnection: { edges: null, __typename: 'PositionConnection' },
|
||||||
|
__typename: 'Party',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"outDir": "../../dist/out-tsc",
|
"outDir": "../../dist/out-tsc",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"types": ["cypress", "node", "cypress-real-events"]
|
"types": ["cypress", "node", "cypress-real-events", "cypress-grep"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.js"]
|
"include": ["src/**/*.ts", "src/**/*.js"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_VEGA_URL=http://localhost:3028/query
|
NX_VEGA_URL=http://localhost:3028/query
|
||||||
NX_VEGA_ENV=LOCAL
|
NX_VEGA_ENV=LOCAL
|
||||||
NX_VEGA_REST=http://localhost:3029
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
|
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
|
||||||
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
||||||
NX_VEGA_ENV=DEVNET
|
NX_VEGA_ENV=DEVNET
|
||||||
NX_VEGA_REST=https://api.n04.d.vega.xyz
|
|
||||||
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
|
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
|
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
|
||||||
NX_VEGA_URL=https://api.token.vega.xyz/query
|
NX_VEGA_URL=https://api.vega.xyz/query
|
||||||
NX_VEGA_ENV=MAINNET
|
NX_VEGA_ENV=MAINNET
|
||||||
NX_VEGA_REST=https://api.token.vega.xyz
|
|
||||||
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
|
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_ETHERSCAN_URL=https://etherscan.io
|
NX_ETHERSCAN_URL=https://etherscan.io
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
|
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
|
||||||
NX_VEGA_URL=https://api.n01.stagnet3.vega.xyz/graphql
|
NX_VEGA_URL=https://api.n01.stagnet3.vega.xyz/graphql
|
||||||
NX_VEGA_ENV=STAGNET3
|
NX_VEGA_ENV=STAGNET3
|
||||||
NX_VEGA_REST=https://api.n01.stagnet3.vega.xyz
|
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||||
NX_VEGA_EXPLORER_URL=https://staging2.explorer.vega.xyz
|
NX_VEGA_EXPLORER_URL=https://staging2.explorer.vega.xyz
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
|
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
|
||||||
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
|
NX_VEGA_URL=https://api.n12.testnet.vega.xyz/graphql
|
||||||
NX_VEGA_ENV=TESTNET
|
NX_VEGA_ENV=TESTNET
|
||||||
NX_VEGA_REST=https://api.n11.testnet.vega.xyz
|
|
||||||
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
|
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
|
import 'jest-canvas-mock';
|
||||||
import { defaultFallbackInView } from 'react-intersection-observer';
|
import { defaultFallbackInView } from 'react-intersection-observer';
|
||||||
import ResizeObserver from 'resize-observer-polyfill';
|
import ResizeObserver from 'resize-observer-polyfill';
|
||||||
|
|
||||||
@@ -38,3 +39,5 @@ Object.defineProperty(window, 'matchMedia', {
|
|||||||
dispatchEvent: jest.fn(),
|
dispatchEvent: jest.fn(),
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.setTimeout(30000);
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import React, {
|
||||||
|
forwardRef,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { ThemeContext, useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
|
import type {
|
||||||
|
GridOptions,
|
||||||
|
GetRowIdParams,
|
||||||
|
TabToNextCellParams,
|
||||||
|
CellKeyDownEvent,
|
||||||
|
FullWidthCellKeyDownEvent,
|
||||||
|
IGetRowsParams,
|
||||||
|
IDatasource,
|
||||||
|
} from 'ag-grid-community';
|
||||||
|
import { NO_DATA_MESSAGE } from '../../constants';
|
||||||
|
import * as constants from '../simple-market-list/constants';
|
||||||
|
|
||||||
|
export interface GetRowsParams<T>
|
||||||
|
extends Omit<IGetRowsParams, 'successCallback'> {
|
||||||
|
successCallback(rowsThisBlock: T[], lastRow?: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Datasource<T> extends IDatasource {
|
||||||
|
getRows(params: GetRowsParams<T>): void;
|
||||||
|
}
|
||||||
|
interface Props<T> extends GridOptions {
|
||||||
|
rowData?: T[];
|
||||||
|
datasource?: Datasource<T>;
|
||||||
|
handleRowClicked?: (event: { data: T }) => void;
|
||||||
|
components?: Record<string, unknown>;
|
||||||
|
classNamesParam?: string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConsoleLiteGrid = <T extends { id?: string }>(
|
||||||
|
{ handleRowClicked, getRowId, classNamesParam, ...props }: Props<T>,
|
||||||
|
ref?: React.Ref<AgGridReact>
|
||||||
|
) => {
|
||||||
|
const { isMobile, screenSize } = useScreenDimensions();
|
||||||
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
const theme = useContext(ThemeContext);
|
||||||
|
const handleOnGridReady = useCallback(() => {
|
||||||
|
(
|
||||||
|
(ref as React.RefObject<AgGridReact>) || gridRef
|
||||||
|
).current?.api?.sizeColumnsToFit();
|
||||||
|
}, [gridRef, ref]);
|
||||||
|
const onTabToNextCell = useCallback((params: TabToNextCellParams) => {
|
||||||
|
const {
|
||||||
|
api,
|
||||||
|
previousCellPosition: { rowIndex },
|
||||||
|
} = params;
|
||||||
|
const rowCount = api.getDisplayedRowCount();
|
||||||
|
if (rowCount <= rowIndex + 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { ...params.previousCellPosition, rowIndex: rowIndex + 1 };
|
||||||
|
}, []);
|
||||||
|
const getRowIdLocal = useCallback(({ data }: GetRowIdParams) => data.id, []);
|
||||||
|
const onCellKeyDown = useCallback(
|
||||||
|
(
|
||||||
|
params: (CellKeyDownEvent | FullWidthCellKeyDownEvent) & {
|
||||||
|
event: KeyboardEvent;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const { event: { key = '' } = {}, data } = params;
|
||||||
|
if (key === 'Enter') {
|
||||||
|
handleRowClicked?.({ data });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[handleRowClicked]
|
||||||
|
);
|
||||||
|
const shouldSuppressHorizontalScroll = useMemo(() => {
|
||||||
|
return !isMobile && constants.LARGE_SCREENS.includes(screenSize);
|
||||||
|
}, [isMobile, screenSize]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AgGrid
|
||||||
|
className={classNames(classNamesParam)}
|
||||||
|
rowHeight={60}
|
||||||
|
customThemeParams={
|
||||||
|
theme === 'dark'
|
||||||
|
? constants.agGridDarkVariables
|
||||||
|
: constants.agGridLightVariables
|
||||||
|
}
|
||||||
|
onGridReady={handleOnGridReady}
|
||||||
|
onRowClicked={handleRowClicked}
|
||||||
|
rowClass={isMobile ? 'mobile' : ''}
|
||||||
|
rowClassRules={constants.ROW_CLASS_RULES}
|
||||||
|
ref={ref || gridRef}
|
||||||
|
overlayNoRowsTemplate={NO_DATA_MESSAGE}
|
||||||
|
suppressContextMenu
|
||||||
|
getRowId={getRowId || getRowIdLocal}
|
||||||
|
suppressMovableColumns
|
||||||
|
suppressRowTransform
|
||||||
|
onCellKeyDown={onCellKeyDown}
|
||||||
|
tabToNextCell={onTabToNextCell}
|
||||||
|
suppressHorizontalScroll={shouldSuppressHorizontalScroll}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConsoleLiteGridForwarder = forwardRef(ConsoleLiteGrid) as <T>(
|
||||||
|
p: Props<T> & { ref?: React.Ref<AgGridReact> }
|
||||||
|
) => React.ReactElement;
|
||||||
|
|
||||||
|
export default ConsoleLiteGridForwarder;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as ConsoleLiteGrid } from './console-lite-grid';
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { DealTicketQuery_market_tradableInstrument_instrument_product_settlementAsset } from '@vegaprotocol/deal-ticket';
|
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||||
import type { PartyBalanceQuery_party_accounts } from './__generated__/PartyBalanceQuery';
|
import type { PartyBalanceQuery_party_accounts } from './__generated__/PartyBalanceQuery';
|
||||||
import { useSettlementAccount } from '../../hooks/use-settlement-account';
|
import { useSettlementAccount } from '../../hooks/use-settlement-account';
|
||||||
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
|
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
|
||||||
import { AccountType } from '@vegaprotocol/types';
|
import { AccountType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
interface DealTicketBalanceProps {
|
interface DealTicketBalanceProps {
|
||||||
settlementAsset: DealTicketQuery_market_tradableInstrument_instrument_product_settlementAsset;
|
settlementAsset: DealTicketMarketFragment['tradableInstrument']['instrument']['product']['settlementAsset'];
|
||||||
accounts: PartyBalanceQuery_party_accounts[];
|
accounts: PartyBalanceQuery_party_accounts[];
|
||||||
isWalletConnected: boolean;
|
isWalletConnected: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ export const DealTicketContainer = () => {
|
|||||||
const container = marketId ? (
|
const container = marketId ? (
|
||||||
<Container marketId={marketId}>
|
<Container marketId={marketId}>
|
||||||
{(data) => {
|
{(data) => {
|
||||||
|
if (!data.market) {
|
||||||
|
return null as unknown as JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
const balance = (
|
const balance = (
|
||||||
<DealTicketBalance
|
<DealTicketBalance
|
||||||
className="mb-4"
|
className="mb-4"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useForm, Controller } from 'react-hook-form';
|
import { useForm, Controller } from 'react-hook-form';
|
||||||
import { Stepper } from '../stepper';
|
import { Stepper } from '../stepper';
|
||||||
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
|
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||||
import { InputError } from '@vegaprotocol/ui-toolkit';
|
import { InputError } from '@vegaprotocol/ui-toolkit';
|
||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
import { MarketSelector } from '@vegaprotocol/deal-ticket';
|
import { MarketSelector } from '@vegaprotocol/deal-ticket';
|
||||||
@@ -37,7 +37,7 @@ import { Side, OrderType } from '@vegaprotocol/types';
|
|||||||
import { DealTicketSlippage } from './deal-ticket-slippage';
|
import { DealTicketSlippage } from './deal-ticket-slippage';
|
||||||
|
|
||||||
interface DealTicketMarketProps {
|
interface DealTicketMarketProps {
|
||||||
market: DealTicketQuery_market;
|
market: DealTicketMarketFragment;
|
||||||
partyData?: PartyBalanceQuery;
|
partyData?: PartyBalanceQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
|
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||||
import type { Order } from '@vegaprotocol/orders';
|
import type { Order } from '@vegaprotocol/orders';
|
||||||
import { SIDE_NAMES } from './side-selector';
|
import { SIDE_NAMES } from './side-selector';
|
||||||
import SimpleMarketExpires from '../simple-market-list/simple-market-expires';
|
import SimpleMarketExpires from '../simple-market-list/simple-market-expires';
|
||||||
@@ -33,7 +33,7 @@ export const MARKET_TAGS_QUERY = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
market: DealTicketQuery_market;
|
market: DealTicketMarketFragment;
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
transactionStatus?: string;
|
transactionStatus?: string;
|
||||||
order: Order;
|
order: Order;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
query Deposits {
|
query DepositAssets {
|
||||||
assetsConnection {
|
assetsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
|||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @generated
|
||||||
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
import { AssetStatus } from "@vegaprotocol/types";
|
||||||
|
|
||||||
|
// ====================================================
|
||||||
|
// GraphQL query operation: DepositAssets
|
||||||
|
// ====================================================
|
||||||
|
|
||||||
|
export interface DepositAssets_assetsConnection_edges_node_source_BuiltinAsset {
|
||||||
|
__typename: "BuiltinAsset";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DepositAssets_assetsConnection_edges_node_source_ERC20 {
|
||||||
|
__typename: "ERC20";
|
||||||
|
/**
|
||||||
|
* The address of the ERC20 contract
|
||||||
|
*/
|
||||||
|
contractAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DepositAssets_assetsConnection_edges_node_source = DepositAssets_assetsConnection_edges_node_source_BuiltinAsset | DepositAssets_assetsConnection_edges_node_source_ERC20;
|
||||||
|
|
||||||
|
export interface DepositAssets_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: DepositAssets_assetsConnection_edges_node_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DepositAssets_assetsConnection_edges {
|
||||||
|
__typename: "AssetEdge";
|
||||||
|
/**
|
||||||
|
* The asset information
|
||||||
|
*/
|
||||||
|
node: DepositAssets_assetsConnection_edges_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DepositAssets_assetsConnection {
|
||||||
|
__typename: "AssetsConnection";
|
||||||
|
/**
|
||||||
|
* The assets
|
||||||
|
*/
|
||||||
|
edges: (DepositAssets_assetsConnection_edges | null)[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DepositAssets {
|
||||||
|
/**
|
||||||
|
* The list of all assets in use in the Vega network or the specified asset if ID is provided
|
||||||
|
*/
|
||||||
|
assetsConnection: DepositAssets_assetsConnection | null;
|
||||||
|
}
|
||||||
@@ -3,14 +3,14 @@ import { Schema as Types } from '@vegaprotocol/types';
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
import * as Apollo from '@apollo/client';
|
import * as Apollo from '@apollo/client';
|
||||||
const defaultOptions = {} as const;
|
const defaultOptions = {} as const;
|
||||||
export type DepositsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
export type DepositAssetsQueryVariables = 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 } };
|
export type DepositAssetsQuery = { __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`
|
export const DepositAssetsDocument = gql`
|
||||||
query Deposits {
|
query DepositAssets {
|
||||||
assetsConnection {
|
assetsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
@@ -30,28 +30,28 @@ export const DepositsDocument = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useDepositsQuery__
|
* __useDepositAssetsQuery__
|
||||||
*
|
*
|
||||||
* To run a query within a React component, call `useDepositsQuery` and pass it any options that fit your needs.
|
* To run a query within a React component, call `useDepositAssetsQuery` 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
|
* When your component renders, `useDepositAssetsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
* you can use to render your UI.
|
* 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;
|
* @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
|
* @example
|
||||||
* const { data, loading, error } = useDepositsQuery({
|
* const { data, loading, error } = useDepositAssetsQuery({
|
||||||
* variables: {
|
* variables: {
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
export function useDepositsQuery(baseOptions?: Apollo.QueryHookOptions<DepositsQuery, DepositsQueryVariables>) {
|
export function useDepositAssetsQuery(baseOptions?: Apollo.QueryHookOptions<DepositAssetsQuery, DepositAssetsQueryVariables>) {
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
return Apollo.useQuery<DepositsQuery, DepositsQueryVariables>(DepositsDocument, options);
|
return Apollo.useQuery<DepositAssetsQuery, DepositAssetsQueryVariables>(DepositAssetsDocument, options);
|
||||||
}
|
}
|
||||||
export function useDepositsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<DepositsQuery, DepositsQueryVariables>) {
|
export function useDepositAssetsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<DepositAssetsQuery, DepositAssetsQueryVariables>) {
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
return Apollo.useLazyQuery<DepositsQuery, DepositsQueryVariables>(DepositsDocument, options);
|
return Apollo.useLazyQuery<DepositAssetsQuery, DepositAssetsQueryVariables>(DepositAssetsDocument, options);
|
||||||
}
|
}
|
||||||
export type DepositsQueryHookResult = ReturnType<typeof useDepositsQuery>;
|
export type DepositAssetsQueryHookResult = ReturnType<typeof useDepositAssetsQuery>;
|
||||||
export type DepositsLazyQueryHookResult = ReturnType<typeof useDepositsLazyQuery>;
|
export type DepositAssetsLazyQueryHookResult = ReturnType<typeof useDepositAssetsLazyQuery>;
|
||||||
export type DepositsQueryResult = Apollo.QueryResult<DepositsQuery, DepositsQueryVariables>;
|
export type DepositAssetsQueryResult = Apollo.QueryResult<DepositAssetsQuery, DepositAssetsQueryVariables>;
|
||||||
@@ -5,10 +5,10 @@ import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
|||||||
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { Web3Container } from '@vegaprotocol/web3';
|
import { Web3Container } from '@vegaprotocol/web3';
|
||||||
import type { Deposits } from './__generated__/Deposits';
|
import type { DepositAssets } from './__generated__/DepositAssets';
|
||||||
|
|
||||||
const DEPOSITS_QUERY = gql`
|
const DEPOSITS_QUERY = gql`
|
||||||
query Deposits {
|
query DepositAssets {
|
||||||
assetsConnection {
|
assetsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
@@ -35,7 +35,7 @@ export const DepositContainer = () => {
|
|||||||
const { VEGA_ENV } = useEnvironment();
|
const { VEGA_ENV } = useEnvironment();
|
||||||
const { keypair } = useVegaWallet();
|
const { keypair } = useVegaWallet();
|
||||||
|
|
||||||
const { data, loading, error } = useQuery<Deposits>(DEPOSITS_QUERY, {
|
const { data, loading, error } = useQuery<DepositAssets>(DEPOSITS_QUERY, {
|
||||||
variables: { partyId: keypair?.pub },
|
variables: { partyId: keypair?.pub },
|
||||||
skip: !keypair?.pub,
|
skip: !keypair?.pub,
|
||||||
});
|
});
|
||||||
@@ -43,7 +43,7 @@ export const DepositContainer = () => {
|
|||||||
const assets = getEnabledAssets(data);
|
const assets = getEnabledAssets(data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer<Deposits> data={data} loading={loading} error={error}>
|
<AsyncRenderer<DepositAssets> data={data} loading={loading} error={error}>
|
||||||
{assets.length ? (
|
{assets.length ? (
|
||||||
<Web3Container>
|
<Web3Container>
|
||||||
<DepositManager
|
<DepositManager
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const Header = () => {
|
|||||||
} = useContext(LocalContext);
|
} = useContext(LocalContext);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex items-stretch pr-6 py-6 bg-black text-neutral-400"
|
className="dark flex items-stretch pr-6 py-6 bg-black text-neutral-400"
|
||||||
data-testid="header"
|
data-testid="header"
|
||||||
>
|
>
|
||||||
<Logo />
|
<Logo />
|
||||||
@@ -21,12 +21,7 @@ const Header = () => {
|
|||||||
setConnectDialog={setConnect}
|
setConnectDialog={setConnect}
|
||||||
setManageDialog={setManage}
|
setManageDialog={setManage}
|
||||||
/>
|
/>
|
||||||
<ThemeSwitcher
|
<ThemeSwitcher theme={theme} onToggle={toggleTheme} className="-my-4" />
|
||||||
theme={theme}
|
|
||||||
onToggle={toggleTheme}
|
|
||||||
className="-my-4"
|
|
||||||
fixedBg="dark"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
export interface Item {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
url?: string;
|
||||||
|
isActive?: boolean;
|
||||||
|
color?: string;
|
||||||
|
cssClass?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
active?: string;
|
||||||
|
cssClass?: string[];
|
||||||
|
items: Item[];
|
||||||
|
'data-testid'?: string;
|
||||||
|
'aria-label'?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MenuItem = ({ id, name, url, isActive, cssClass }: Item): JSX.Element => {
|
||||||
|
if (!url) {
|
||||||
|
return <span data-testid={id}>{name}</span>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={url}
|
||||||
|
aria-label={name}
|
||||||
|
className={classNames('pl-0 hover:opacity-75', cssClass, {
|
||||||
|
active: isActive,
|
||||||
|
})}
|
||||||
|
data-testid={id}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const findActive = (active?: string, items?: Item[]) => {
|
||||||
|
if (!active && items?.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return items?.length
|
||||||
|
? items.findIndex((item) => item.id === active) || 0
|
||||||
|
: -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HorizontalMenu = ({
|
||||||
|
items,
|
||||||
|
active,
|
||||||
|
'data-testid': dataTestId,
|
||||||
|
'aria-label': ariaLabel,
|
||||||
|
}: Props) => {
|
||||||
|
const [activeNumber, setActiveNumber] = useState<number>(
|
||||||
|
findActive(active, items)
|
||||||
|
);
|
||||||
|
const slideContRef = useRef<HTMLUListElement | null>(null);
|
||||||
|
const [sliderStyles, setSliderStyles] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setActiveNumber(findActive(active, items));
|
||||||
|
}, [active, items]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const contStyles = (
|
||||||
|
slideContRef.current as HTMLUListElement
|
||||||
|
).getBoundingClientRect();
|
||||||
|
const selectedStyles = (slideContRef.current as HTMLUListElement).children[
|
||||||
|
activeNumber
|
||||||
|
]?.getBoundingClientRect();
|
||||||
|
const styles: Record<string, string> = selectedStyles
|
||||||
|
? {
|
||||||
|
backgroundColor: items[activeNumber].color || '',
|
||||||
|
width: `${selectedStyles.width}px`,
|
||||||
|
left: `${selectedStyles.left - contStyles.left}px`,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
setSliderStyles(styles);
|
||||||
|
}, [activeNumber, slideContRef, items]);
|
||||||
|
|
||||||
|
return items.length ? (
|
||||||
|
<ul
|
||||||
|
ref={slideContRef}
|
||||||
|
className="grid grid-flow-col auto-cols-min gap-4 relative pb-2 mb-2"
|
||||||
|
data-testid={dataTestId}
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
>
|
||||||
|
{items.map((item, i) => (
|
||||||
|
<li key={item.id} className="md:mr-2 whitespace-nowrap">
|
||||||
|
<MenuItem {...item} isActive={i === activeNumber} />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
<li
|
||||||
|
className="absolute bottom-0 h-[2px] transition-left duration-300 dark:bg-white bg-black"
|
||||||
|
key="slider"
|
||||||
|
style={sliderStyles}
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HorizontalMenu;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as HorizontalMenu } from './horizontal-menu';
|
||||||
|
export type { Item as HorizontalMenuItem } from './horizontal-menu';
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { useMemo, useRef, useCallback } from 'react';
|
||||||
|
import { useOutletContext } from 'react-router-dom';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import { PriceCell, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
|
import type { AccountFields } from '@vegaprotocol/accounts';
|
||||||
|
import { aggregatedAccountsDataProvider, getId } from '@vegaprotocol/accounts';
|
||||||
|
import type { IGetRowsParams } from 'ag-grid-community';
|
||||||
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
AssetDetailsDialog,
|
||||||
|
useAssetDetailsDialogStore,
|
||||||
|
} from '@vegaprotocol/assets';
|
||||||
|
import { NO_DATA_MESSAGE } from '../../../constants';
|
||||||
|
import { ConsoleLiteGrid } from '../../console-lite-grid';
|
||||||
|
import { useAccountColumnDefinitions } from '.';
|
||||||
|
|
||||||
|
const AccountsManager = () => {
|
||||||
|
const { partyId = '' } = useOutletContext<{ partyId: string }>();
|
||||||
|
const { isOpen, symbol, setOpen } = useAssetDetailsDialogStore();
|
||||||
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
const dataRef = useRef<AccountFields[] | null>(null);
|
||||||
|
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||||
|
const update = useCallback(
|
||||||
|
({ data }: { data: AccountFields[] | null }) => {
|
||||||
|
if (!gridRef.current?.api) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dataRef.current?.length) {
|
||||||
|
dataRef.current = data;
|
||||||
|
gridRef.current.api.refreshInfiniteCache();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
[gridRef]
|
||||||
|
);
|
||||||
|
const { data, error, loading } = useDataProvider<AccountFields[], never>({
|
||||||
|
dataProvider: aggregatedAccountsDataProvider,
|
||||||
|
update,
|
||||||
|
variables,
|
||||||
|
});
|
||||||
|
dataRef.current = data;
|
||||||
|
const getRows = async ({
|
||||||
|
successCallback,
|
||||||
|
startRow,
|
||||||
|
endRow,
|
||||||
|
}: IGetRowsParams) => {
|
||||||
|
const rowsThisBlock = dataRef.current
|
||||||
|
? dataRef.current.slice(startRow, endRow)
|
||||||
|
: [];
|
||||||
|
const lastRow = dataRef.current?.length ?? -1;
|
||||||
|
successCallback(rowsThisBlock, lastRow);
|
||||||
|
};
|
||||||
|
const { columnDefs, defaultColDef } = useAccountColumnDefinitions();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AsyncRenderer
|
||||||
|
loading={loading}
|
||||||
|
error={error}
|
||||||
|
data={data}
|
||||||
|
noDataMessage={NO_DATA_MESSAGE}
|
||||||
|
>
|
||||||
|
<ConsoleLiteGrid<AccountFields>
|
||||||
|
rowData={data?.length ? undefined : []}
|
||||||
|
rowModelType={data?.length ? 'infinite' : 'clientSide'}
|
||||||
|
ref={gridRef}
|
||||||
|
datasource={{ getRows }}
|
||||||
|
columnDefs={columnDefs}
|
||||||
|
defaultColDef={defaultColDef}
|
||||||
|
components={{ PriceCell }}
|
||||||
|
getRowId={({ data }) => getId(data)}
|
||||||
|
/>
|
||||||
|
</AsyncRenderer>
|
||||||
|
<AssetDetailsDialog
|
||||||
|
assetSymbol={symbol}
|
||||||
|
open={isOpen}
|
||||||
|
onChange={(open) => setOpen(open)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AccountsManager;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as AccountManager } from './accounts';
|
||||||
|
export { default as useAccountColumnDefinitions } from './use-column-definitions';
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
|
||||||
|
import type { AccountFields } from '@vegaprotocol/accounts';
|
||||||
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
|
import type { ColDef, GroupCellRendererParams } from 'ag-grid-community';
|
||||||
|
import type { VegaValueFormatterParams } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
const useAccountColumnDefinitions = () => {
|
||||||
|
const { open } = useAssetDetailsDialogStore();
|
||||||
|
const columnDefs: ColDef[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
colId: 'account-asset',
|
||||||
|
headerName: t('Asset'),
|
||||||
|
field: 'asset.symbol',
|
||||||
|
headerClass: 'uppercase justify-start',
|
||||||
|
cellClass: 'uppercase flex h-full items-center md:pl-4',
|
||||||
|
cellRenderer: ({ value }: GroupCellRendererParams) =>
|
||||||
|
value && value.length > 0 ? (
|
||||||
|
<div className="md:pl-4 grid h-full items-center" title={value}>
|
||||||
|
<div className="truncate min-w-0">
|
||||||
|
<button
|
||||||
|
className="hover:underline"
|
||||||
|
onClick={() => {
|
||||||
|
open(value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'deposited',
|
||||||
|
headerName: t('Deposited'),
|
||||||
|
field: 'deposited',
|
||||||
|
cellRenderer: 'PriceCell',
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}: VegaValueFormatterParams<AccountFields, 'deposited'>) => {
|
||||||
|
if (value && data) {
|
||||||
|
return addDecimalsFormatNumber(value, data.asset.decimals);
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'used',
|
||||||
|
headerName: t('Used'),
|
||||||
|
field: 'used',
|
||||||
|
cellRenderer: 'PriceCell',
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}: VegaValueFormatterParams<AccountFields, 'used'>) => {
|
||||||
|
if (value && data) {
|
||||||
|
return addDecimalsFormatNumber(value, data.asset.decimals);
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
const defaultColDef = useMemo(() => {
|
||||||
|
return {
|
||||||
|
sortable: true,
|
||||||
|
unSortIcon: true,
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
editable: false,
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return { columnDefs, defaultColDef };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useAccountColumnDefinitions;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
|
export const PORTFOLIO_ASSETS = 'assets';
|
||||||
|
export const PORTFOLIO_POSITIONS = 'positions';
|
||||||
|
export const PORTFOLIO_ORDERS = 'orders';
|
||||||
|
export const PORTFOLIO_FILLS = 'fills';
|
||||||
|
export const PORTFOLIO_DEPOSITS = 'deposits';
|
||||||
|
|
||||||
|
export const PORTFOLIO_ITEMS = [
|
||||||
|
{
|
||||||
|
name: t('Assets'),
|
||||||
|
id: PORTFOLIO_ASSETS,
|
||||||
|
url: `/portfolio/${PORTFOLIO_ASSETS}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('Positions'),
|
||||||
|
id: PORTFOLIO_POSITIONS,
|
||||||
|
url: `/portfolio/${PORTFOLIO_POSITIONS}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('Orders'),
|
||||||
|
id: PORTFOLIO_ORDERS,
|
||||||
|
url: `/portfolio/${PORTFOLIO_ORDERS}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('Fills'),
|
||||||
|
id: PORTFOLIO_FILLS,
|
||||||
|
url: `/portfolio/${PORTFOLIO_FILLS}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('Deposits'),
|
||||||
|
id: PORTFOLIO_DEPOSITS,
|
||||||
|
url: `/portfolio/${PORTFOLIO_DEPOSITS}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { useRef } from 'react';
|
||||||
|
import { useOutletContext } from 'react-router-dom';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import type { TradeWithMarket } from '@vegaprotocol/fills';
|
||||||
|
import { useFillsList } from '@vegaprotocol/fills';
|
||||||
|
import type { BodyScrollEndEvent, BodyScrollEvent } from 'ag-grid-community';
|
||||||
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { ConsoleLiteGrid } from '../../console-lite-grid';
|
||||||
|
import { NO_DATA_MESSAGE } from '../../../constants';
|
||||||
|
import useColumnDefinitions from './use-column-definitions';
|
||||||
|
|
||||||
|
const FillsManager = () => {
|
||||||
|
const { partyId } = useOutletContext<{ partyId: string }>();
|
||||||
|
const { columnDefs, defaultColDef } = useColumnDefinitions({ partyId });
|
||||||
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
const scrolledToTop = useRef(true);
|
||||||
|
const { data, error, loading, addNewRows, getRows } = useFillsList({
|
||||||
|
partyId,
|
||||||
|
gridRef,
|
||||||
|
scrolledToTop,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onBodyScrollEnd = (event: BodyScrollEndEvent) => {
|
||||||
|
if (event.top === 0) {
|
||||||
|
addNewRows();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBodyScroll = (event: BodyScrollEvent) => {
|
||||||
|
scrolledToTop.current = event.top <= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AsyncRenderer
|
||||||
|
loading={loading}
|
||||||
|
error={error}
|
||||||
|
data={data?.length ? data : null}
|
||||||
|
noDataMessage={NO_DATA_MESSAGE}
|
||||||
|
>
|
||||||
|
<ConsoleLiteGrid<TradeWithMarket>
|
||||||
|
ref={gridRef}
|
||||||
|
rowModelType="infinite"
|
||||||
|
datasource={{ getRows }}
|
||||||
|
onBodyScrollEnd={onBodyScrollEnd}
|
||||||
|
onBodyScroll={onBodyScroll}
|
||||||
|
columnDefs={columnDefs}
|
||||||
|
defaultColDef={defaultColDef}
|
||||||
|
/>
|
||||||
|
</AsyncRenderer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FillsManager;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as FillsManager } from './fills';
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import type { ColDef, ValueFormatterParams } from 'ag-grid-community';
|
||||||
|
import type { TradeWithMarket } from '@vegaprotocol/fills';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import {
|
||||||
|
addDecimal,
|
||||||
|
addDecimalsFormatNumber,
|
||||||
|
formatNumber,
|
||||||
|
getDateTimeFormat,
|
||||||
|
negativeClassNames,
|
||||||
|
positiveClassNames,
|
||||||
|
t,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { Side } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
partyId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useColumnDefinitions = ({ partyId }: Props) => {
|
||||||
|
const columnDefs: ColDef[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
colId: 'market',
|
||||||
|
headerName: t('Market'),
|
||||||
|
field: 'market.tradableInstrument.instrument.name',
|
||||||
|
cellClass: '!flex h-full items-center !md:pl-4',
|
||||||
|
minWidth: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'size',
|
||||||
|
headerName: t('Size'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
field: 'size',
|
||||||
|
width: 100,
|
||||||
|
cellClass: ({ data }: { data: TradeWithMarket }) => {
|
||||||
|
return classNames('!flex h-full items-center justify-center', {
|
||||||
|
[positiveClassNames]: data?.buyer.id === partyId,
|
||||||
|
[negativeClassNames]: data?.seller.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
valueFormatter: ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value && data?.market) {
|
||||||
|
let prefix;
|
||||||
|
if (data.buyer.id === partyId) {
|
||||||
|
prefix = '+';
|
||||||
|
} else if (data.seller.id) {
|
||||||
|
prefix = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = addDecimalsFormatNumber(
|
||||||
|
value,
|
||||||
|
data.market.positionDecimalPlaces
|
||||||
|
);
|
||||||
|
return `${prefix}${size}`;
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'value',
|
||||||
|
headerName: t('Value'),
|
||||||
|
headerClass: 'uppercase text-center',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
field: 'price',
|
||||||
|
valueFormatter: ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value && data?.market) {
|
||||||
|
const asset =
|
||||||
|
data.market.tradableInstrument.instrument.product.settlementAsset
|
||||||
|
.symbol;
|
||||||
|
const valueFormatted = addDecimalsFormatNumber(
|
||||||
|
value,
|
||||||
|
data.market.decimalPlaces
|
||||||
|
);
|
||||||
|
return `${valueFormatted} ${asset}`;
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
type: 'rightAligned',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'filledvalue',
|
||||||
|
headerName: t('Filled value'),
|
||||||
|
headerClass: 'uppercase text-center',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
field: 'price',
|
||||||
|
valueFormatter: ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value && data?.market) {
|
||||||
|
const asset =
|
||||||
|
data.market.tradableInstrument.instrument.product.settlementAsset
|
||||||
|
.symbol;
|
||||||
|
const size = new BigNumber(
|
||||||
|
addDecimal(data.size, data.market.positionDecimalPlaces)
|
||||||
|
);
|
||||||
|
const price = new BigNumber(
|
||||||
|
addDecimal(value, data.market.decimalPlaces)
|
||||||
|
);
|
||||||
|
|
||||||
|
const total = size.times(price).toString();
|
||||||
|
const valueFormatted = formatNumber(
|
||||||
|
total,
|
||||||
|
data?.market.decimalPlaces
|
||||||
|
);
|
||||||
|
return `${valueFormatted} ${asset}`;
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
type: 'rightAligned',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'role',
|
||||||
|
headerName: t('Role'),
|
||||||
|
field: 'aggressor',
|
||||||
|
width: 100,
|
||||||
|
valueFormatter: ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value && data) {
|
||||||
|
const taker = t('Taker');
|
||||||
|
const maker = t('Maker');
|
||||||
|
if (data?.buyer.id === partyId) {
|
||||||
|
if (value === Side.SIDE_BUY) {
|
||||||
|
return taker;
|
||||||
|
} else {
|
||||||
|
return maker;
|
||||||
|
}
|
||||||
|
} else if (data?.seller.id === partyId) {
|
||||||
|
if (value === Side.SIDE_SELL) {
|
||||||
|
return taker;
|
||||||
|
} else {
|
||||||
|
return maker;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'fee',
|
||||||
|
headerName: t('Fee'),
|
||||||
|
headerClass: 'uppercase text-center',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
field: 'market.tradableInstrument.instrument.product',
|
||||||
|
valueFormatter: ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value && data) {
|
||||||
|
const asset = value.settlementAsset;
|
||||||
|
let feesObj;
|
||||||
|
if (data.buyer.id === partyId) {
|
||||||
|
feesObj = data.buyerFee;
|
||||||
|
} else if (data?.seller.id === partyId) {
|
||||||
|
feesObj = data?.sellerFee;
|
||||||
|
} else {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
const fee = new BigNumber(feesObj.makerFee)
|
||||||
|
.plus(feesObj.infrastructureFee)
|
||||||
|
.plus(feesObj.liquidityFee);
|
||||||
|
const totalFees = addDecimalsFormatNumber(
|
||||||
|
fee.toString(),
|
||||||
|
asset.decimals
|
||||||
|
);
|
||||||
|
return `${totalFees} ${asset.symbol}`;
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
type: 'rightAligned',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'date',
|
||||||
|
headerName: t('Date'),
|
||||||
|
field: 'createdAt',
|
||||||
|
valueFormatter: ({ value }: ValueFormatterParams) => {
|
||||||
|
return value ? getDateTimeFormat().format(new Date(value)) : '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, [partyId]);
|
||||||
|
|
||||||
|
const defaultColDef = useMemo(() => {
|
||||||
|
return {
|
||||||
|
sortable: true,
|
||||||
|
unSortIcon: true,
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return { columnDefs, defaultColDef };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useColumnDefinitions;
|
||||||
@@ -1 +1,6 @@
|
|||||||
export * from './portfolio';
|
export * from './portfolio';
|
||||||
|
export * from './accounts';
|
||||||
|
export * from './fills';
|
||||||
|
export * from './orders';
|
||||||
|
export * from './positions';
|
||||||
|
export * as constants from './constants';
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as OrdersManager } from './orders';
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { useRef, useState } from 'react';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import type { BodyScrollEndEvent, BodyScrollEvent } from 'ag-grid-community';
|
||||||
|
import { useOutletContext } from 'react-router-dom';
|
||||||
|
import type { OrderWithMarket } from '@vegaprotocol/orders';
|
||||||
|
import {
|
||||||
|
useOrderCancel,
|
||||||
|
useOrderListData,
|
||||||
|
useOrderEdit,
|
||||||
|
OrderFeedback,
|
||||||
|
getCancelDialogTitle,
|
||||||
|
getCancelDialogIntent,
|
||||||
|
getEditDialogTitle,
|
||||||
|
OrderEditDialog,
|
||||||
|
} from '@vegaprotocol/orders';
|
||||||
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { ConsoleLiteGrid } from '../../console-lite-grid';
|
||||||
|
import { NO_DATA_MESSAGE } from '../../../constants';
|
||||||
|
import useColumnDefinitions from './use-column-definitions';
|
||||||
|
|
||||||
|
const OrdersManager = () => {
|
||||||
|
const { partyId } = useOutletContext<{ partyId: string }>();
|
||||||
|
const [editOrder, setEditOrder] = useState<OrderWithMarket | null>(null);
|
||||||
|
const orderCancel = useOrderCancel();
|
||||||
|
const orderEdit = useOrderEdit(editOrder);
|
||||||
|
const { columnDefs, defaultColDef } = useColumnDefinitions({
|
||||||
|
setEditOrder,
|
||||||
|
orderCancel,
|
||||||
|
});
|
||||||
|
|
||||||
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
const scrolledToTop = useRef(true);
|
||||||
|
|
||||||
|
const { data, error, loading, addNewRows, getRows } = useOrderListData({
|
||||||
|
partyId,
|
||||||
|
gridRef,
|
||||||
|
scrolledToTop,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onBodyScrollEnd = (event: BodyScrollEndEvent) => {
|
||||||
|
if (event.top === 0) {
|
||||||
|
addNewRows();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBodyScroll = (event: BodyScrollEvent) => {
|
||||||
|
scrolledToTop.current = event.top <= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AsyncRenderer
|
||||||
|
loading={loading}
|
||||||
|
error={error}
|
||||||
|
data={data?.length ? data : null}
|
||||||
|
noDataMessage={NO_DATA_MESSAGE}
|
||||||
|
>
|
||||||
|
<ConsoleLiteGrid<OrderWithMarket>
|
||||||
|
ref={gridRef}
|
||||||
|
rowModelType={data?.length ? 'infinite' : 'clientSide'}
|
||||||
|
rowData={data?.length ? undefined : []}
|
||||||
|
datasource={{ getRows }}
|
||||||
|
onBodyScrollEnd={onBodyScrollEnd}
|
||||||
|
onBodyScroll={onBodyScroll}
|
||||||
|
columnDefs={columnDefs}
|
||||||
|
defaultColDef={defaultColDef}
|
||||||
|
/>
|
||||||
|
<orderCancel.Dialog
|
||||||
|
title={getCancelDialogTitle(orderCancel.cancelledOrder?.status)}
|
||||||
|
intent={getCancelDialogIntent(orderCancel.cancelledOrder?.status)}
|
||||||
|
>
|
||||||
|
<OrderFeedback
|
||||||
|
transaction={orderCancel.transaction}
|
||||||
|
order={orderCancel.cancelledOrder}
|
||||||
|
/>
|
||||||
|
</orderCancel.Dialog>
|
||||||
|
<orderEdit.Dialog
|
||||||
|
title={getEditDialogTitle(orderEdit.updatedOrder?.status)}
|
||||||
|
>
|
||||||
|
<OrderFeedback
|
||||||
|
transaction={orderEdit.transaction}
|
||||||
|
order={orderEdit.updatedOrder}
|
||||||
|
/>
|
||||||
|
</orderEdit.Dialog>
|
||||||
|
{editOrder && (
|
||||||
|
<OrderEditDialog
|
||||||
|
isOpen={Boolean(editOrder)}
|
||||||
|
onChange={(isOpen) => {
|
||||||
|
if (!isOpen) setEditOrder(null);
|
||||||
|
}}
|
||||||
|
order={editOrder}
|
||||||
|
onSubmit={(fields) => {
|
||||||
|
setEditOrder(null);
|
||||||
|
orderEdit.edit({ price: fields.entryPrice });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AsyncRenderer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OrdersManager;
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import type {
|
||||||
|
ColDef,
|
||||||
|
ValueFormatterParams,
|
||||||
|
ICellRendererParams,
|
||||||
|
} from 'ag-grid-community';
|
||||||
|
import {
|
||||||
|
addDecimal,
|
||||||
|
getDateTimeFormat,
|
||||||
|
negativeClassNames,
|
||||||
|
positiveClassNames,
|
||||||
|
t,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
|
import type {
|
||||||
|
Orders_party_ordersConnection_edges_node,
|
||||||
|
OrderWithMarket,
|
||||||
|
CancelOrderArgs,
|
||||||
|
} from '@vegaprotocol/orders';
|
||||||
|
import { isOrderActive } from '@vegaprotocol/orders';
|
||||||
|
import {
|
||||||
|
OrderRejectionReasonMapping,
|
||||||
|
OrderStatus,
|
||||||
|
OrderType,
|
||||||
|
OrderStatusMapping,
|
||||||
|
OrderTypeMapping,
|
||||||
|
Side,
|
||||||
|
OrderTimeInForce,
|
||||||
|
OrderTimeInForceMapping,
|
||||||
|
} from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
type StatusKey = keyof typeof OrderStatusMapping;
|
||||||
|
type RejectReasonKey = keyof typeof OrderRejectionReasonMapping;
|
||||||
|
type OrderTimeKey = keyof typeof OrderTimeInForceMapping;
|
||||||
|
interface Props {
|
||||||
|
setEditOrder: (order: OrderWithMarket) => void;
|
||||||
|
orderCancel: {
|
||||||
|
cancel: (args: CancelOrderArgs) => void;
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const useColumnDefinitions = ({ setEditOrder, orderCancel }: Props) => {
|
||||||
|
const columnDefs: ColDef[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
colId: 'market',
|
||||||
|
headerName: t('Market'),
|
||||||
|
field: 'market.tradableInstrument.instrument.code',
|
||||||
|
headerClass: 'uppercase justify-start',
|
||||||
|
cellClass: '!flex h-full items-center !md:pl-4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'size',
|
||||||
|
headerName: t('Size'),
|
||||||
|
field: 'size',
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: 'font-mono !flex h-full items-center',
|
||||||
|
width: 80,
|
||||||
|
cellClassRules: {
|
||||||
|
[positiveClassNames]: ({
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
data: Orders_party_ordersConnection_edges_node;
|
||||||
|
}) => data?.side === Side.SIDE_BUY,
|
||||||
|
[negativeClassNames]: ({
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
data: Orders_party_ordersConnection_edges_node;
|
||||||
|
}) => data?.side === Side.SIDE_SELL,
|
||||||
|
},
|
||||||
|
valueFormatter: ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value && data && data.market) {
|
||||||
|
const prefix = data
|
||||||
|
? data.side === Side.SIDE_BUY
|
||||||
|
? '+'
|
||||||
|
: '-'
|
||||||
|
: '';
|
||||||
|
return (
|
||||||
|
prefix + addDecimal(value, data.market.positionDecimalPlaces)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'type',
|
||||||
|
field: 'type',
|
||||||
|
width: 80,
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
}: ValueFormatterParams & {
|
||||||
|
value?: Orders_party_ordersConnection_edges_node['type'];
|
||||||
|
}) => OrderTypeMapping[value as OrderType],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'status',
|
||||||
|
field: 'status',
|
||||||
|
cellClass: 'text-center font-mono !flex h-full items-center',
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}: ValueFormatterParams & {
|
||||||
|
value?: StatusKey;
|
||||||
|
}) => {
|
||||||
|
if (value && data && data.market) {
|
||||||
|
if (value === OrderStatus.STATUS_REJECTED) {
|
||||||
|
return `${OrderStatusMapping[value as StatusKey]}: ${
|
||||||
|
data.rejectionReason &&
|
||||||
|
OrderRejectionReasonMapping[
|
||||||
|
data.rejectionReason as RejectReasonKey
|
||||||
|
]
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
return OrderStatusMapping[value as StatusKey] as string;
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'filled',
|
||||||
|
headerName: t('Filled'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
field: 'remaining',
|
||||||
|
cellClass: 'font-mono text-center !flex h-full items-center',
|
||||||
|
width: 80,
|
||||||
|
valueFormatter: ({
|
||||||
|
data,
|
||||||
|
value,
|
||||||
|
}: ValueFormatterParams & {
|
||||||
|
value?: Orders_party_ordersConnection_edges_node['remaining'];
|
||||||
|
}) => {
|
||||||
|
if (value && data && data.market) {
|
||||||
|
const dps = data.market.positionDecimalPlaces;
|
||||||
|
const size = new BigNumber(data.size);
|
||||||
|
const remaining = new BigNumber(value);
|
||||||
|
const fills = size.minus(remaining);
|
||||||
|
return `${addDecimal(fills.toString(), dps)}/${addDecimal(
|
||||||
|
size.toString(),
|
||||||
|
dps
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'price',
|
||||||
|
field: 'price',
|
||||||
|
type: 'rightAligned',
|
||||||
|
width: 125,
|
||||||
|
headerClass: 'uppercase text-right',
|
||||||
|
cellClass: 'font-mono text-right !flex h-full items-center',
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}: ValueFormatterParams & {
|
||||||
|
value?: Orders_party_ordersConnection_edges_node['price'];
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
value === undefined ||
|
||||||
|
!data ||
|
||||||
|
!data.market ||
|
||||||
|
data.type === OrderType.TYPE_MARKET
|
||||||
|
) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
return addDecimal(value, data.market.decimalPlaces);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'timeInForce',
|
||||||
|
field: 'timeInForce',
|
||||||
|
minWidth: 120,
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}: ValueFormatterParams & {
|
||||||
|
value?: OrderTimeKey;
|
||||||
|
}) => {
|
||||||
|
if (value && data?.market) {
|
||||||
|
if (
|
||||||
|
value === OrderTimeInForce.TIME_IN_FORCE_GTT &&
|
||||||
|
data.expiresAt
|
||||||
|
) {
|
||||||
|
const expiry = getDateTimeFormat().format(
|
||||||
|
new Date(data.expiresAt)
|
||||||
|
);
|
||||||
|
return `${
|
||||||
|
OrderTimeInForceMapping[value as OrderTimeKey]
|
||||||
|
}: ${expiry}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OrderTimeInForceMapping[value as OrderTimeKey];
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'createdat',
|
||||||
|
field: 'createdAt',
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
}: ValueFormatterParams & {
|
||||||
|
value?: Orders_party_ordersConnection_edges_node['createdAt'];
|
||||||
|
}) => {
|
||||||
|
return value ? getDateTimeFormat().format(new Date(value)) : value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'updated',
|
||||||
|
field: 'updatedAt',
|
||||||
|
cellClass: '!flex h-full items-center justify-center',
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
}: ValueFormatterParams & {
|
||||||
|
value?: Orders_party_ordersConnection_edges_node['updatedAt'];
|
||||||
|
}) => {
|
||||||
|
return value ? getDateTimeFormat().format(new Date(value)) : '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'edit',
|
||||||
|
field: 'edit',
|
||||||
|
width: 75,
|
||||||
|
cellRenderer: ({ data }: ICellRendererParams) => {
|
||||||
|
if (!data) return null;
|
||||||
|
if (isOrderActive(data.status)) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
data-testid="edit"
|
||||||
|
onClick={() => {
|
||||||
|
setEditOrder(data);
|
||||||
|
}}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
{t('Edit')}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'cancel',
|
||||||
|
field: 'cancel',
|
||||||
|
minWidth: 130,
|
||||||
|
cellRenderer: ({ data }: ICellRendererParams) => {
|
||||||
|
if (!data) return null;
|
||||||
|
if (isOrderActive(data.status)) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
data-testid="cancel"
|
||||||
|
onClick={() => {
|
||||||
|
if (data.market) {
|
||||||
|
orderCancel.cancel({
|
||||||
|
orderId: data.id,
|
||||||
|
marketId: data.market.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, [orderCancel, setEditOrder]);
|
||||||
|
const defaultColDef = useMemo(() => {
|
||||||
|
return {
|
||||||
|
sortable: true,
|
||||||
|
unSortIcon: true,
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return { columnDefs, defaultColDef };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useColumnDefinitions;
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
import * as React from 'react';
|
import React from 'react';
|
||||||
import { AccountsContainer } from '@vegaprotocol/accounts';
|
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
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 ConnectWallet from '../wallet-connector';
|
||||||
import { DepositContainer } from '../deposits';
|
import { useOutlet, useLocation } from 'react-router-dom';
|
||||||
|
import { HorizontalMenu } from '../horizontal-menu';
|
||||||
|
import * as constants from './constants';
|
||||||
|
|
||||||
export const Portfolio = () => {
|
export const Portfolio = () => {
|
||||||
const { keypair } = useVegaWallet();
|
const { keypair } = useVegaWallet();
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const module = pathname.split('/portfolio/')?.[1] ?? '';
|
||||||
|
const outlet = useOutlet({ partyId: keypair?.pub || '' });
|
||||||
if (!keypair) {
|
if (!keypair) {
|
||||||
return (
|
return (
|
||||||
<section className="xl:w-1/2">
|
<section className="xl:w-1/2">
|
||||||
@@ -18,23 +17,11 @@ export const Portfolio = () => {
|
|||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs>
|
<div className="h-full p-4 md:p-6 grid grid-rows-[min-content_1fr]">
|
||||||
<Tab id="assets" name={t('Assets')}>
|
<HorizontalMenu active={module} items={constants.PORTFOLIO_ITEMS} />
|
||||||
<AccountsContainer />
|
{outlet}
|
||||||
</Tab>
|
</div>
|
||||||
<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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as PositionsManager } from './positions';
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import type { Position } from '@vegaprotocol/positions';
|
||||||
|
import { PriceFlashCell, t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { AssetBalance } from '@vegaprotocol/accounts';
|
||||||
|
import { usePositionsData } from '@vegaprotocol/positions';
|
||||||
|
import { ConsoleLiteGrid } from '../../console-lite-grid';
|
||||||
|
import useColumnDefinitions from './use-column-definitions';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
partyId: string;
|
||||||
|
assetSymbol: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRowId = ({ data }: { data: Position }) => data.marketId;
|
||||||
|
|
||||||
|
const PositionsAsset = ({ partyId, assetSymbol }: Props) => {
|
||||||
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
const { data, error, loading, getRows } = usePositionsData(
|
||||||
|
partyId,
|
||||||
|
gridRef,
|
||||||
|
assetSymbol
|
||||||
|
);
|
||||||
|
const { columnDefs, defaultColDef } = useColumnDefinitions();
|
||||||
|
return (
|
||||||
|
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||||
|
<div
|
||||||
|
data-testid={`positions-asset-${assetSymbol}`}
|
||||||
|
className="flex justify-between items-center px-4 pt-3 pb-1"
|
||||||
|
>
|
||||||
|
<h4>
|
||||||
|
{assetSymbol} {t('markets')}
|
||||||
|
</h4>
|
||||||
|
<div className="text-sm text-neutral-500 dark:text-neutral-300">
|
||||||
|
{assetSymbol} {t('balance')}:
|
||||||
|
<span data-testid="balance" className="pl-1 font-mono">
|
||||||
|
<AssetBalance partyId={partyId} assetSymbol={assetSymbol} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ConsoleLiteGrid<Position & { id: undefined }>
|
||||||
|
ref={gridRef}
|
||||||
|
domLayout="autoHeight"
|
||||||
|
classNamesParam="h-auto"
|
||||||
|
columnDefs={columnDefs}
|
||||||
|
defaultColDef={defaultColDef}
|
||||||
|
getRowId={getRowId}
|
||||||
|
rowModelType={data?.length ? 'infinite' : 'clientSide'}
|
||||||
|
rowData={data?.length ? undefined : []}
|
||||||
|
datasource={{ getRows }}
|
||||||
|
components={{ PriceFlashCell }}
|
||||||
|
/>
|
||||||
|
</AsyncRenderer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PositionsAsset;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { useOutletContext } from 'react-router-dom';
|
||||||
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { usePositionsAssets } from '@vegaprotocol/positions';
|
||||||
|
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import PositionsAsset from './positions-asset';
|
||||||
|
|
||||||
|
const Positions = () => {
|
||||||
|
const { partyId } = useOutletContext<{ partyId: string }>();
|
||||||
|
const { data, error, loading, assetSymbols } = usePositionsAssets(partyId);
|
||||||
|
return (
|
||||||
|
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||||
|
{assetSymbols && assetSymbols.length > 0 && (
|
||||||
|
<div className="w-full, h-max">
|
||||||
|
{assetSymbols?.map((assetSymbol) => (
|
||||||
|
<PositionsAsset
|
||||||
|
key={assetSymbol}
|
||||||
|
partyId={partyId}
|
||||||
|
assetSymbol={assetSymbol}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{assetSymbols?.length === 0 && <Splash>{t('No data to display')}</Splash>}
|
||||||
|
</AsyncRenderer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Positions;
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
addDecimalsFormatNumber,
|
||||||
|
formatNumber,
|
||||||
|
getDateTimeFormat,
|
||||||
|
PriceFlashCell,
|
||||||
|
signedNumberCssClassRules,
|
||||||
|
t,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
|
import type {
|
||||||
|
PositionsTableValueFormatterParams,
|
||||||
|
Position,
|
||||||
|
} from '@vegaprotocol/positions';
|
||||||
|
import { AmountCell } from '@vegaprotocol/positions';
|
||||||
|
import type {
|
||||||
|
CellRendererSelectorResult,
|
||||||
|
ICellRendererParams,
|
||||||
|
ValueGetterParams,
|
||||||
|
GroupCellRendererParams,
|
||||||
|
ColDef,
|
||||||
|
} from 'ag-grid-community';
|
||||||
|
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
import { Intent, ProgressBarCell } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
const EmptyCell = () => '';
|
||||||
|
|
||||||
|
const useColumnDefinitions = () => {
|
||||||
|
const columnDefs: ColDef[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
colId: 'market',
|
||||||
|
headerName: t('Market'),
|
||||||
|
headerClass: 'uppercase justify-start',
|
||||||
|
cellClass: '!flex h-full items-center !md:pl-4',
|
||||||
|
field: 'marketName',
|
||||||
|
cellRenderer: ({ value }: GroupCellRendererParams) => {
|
||||||
|
if (!value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const valueFormatted: [string, string?] = (() => {
|
||||||
|
// split market name into two parts, 'Part1 (Part2)' or 'Part1 - Part2'
|
||||||
|
const matches = value.match(/^(.*)(\((.*)\)| - (.*))\s*$/);
|
||||||
|
if (matches && matches[1] && matches[3]) {
|
||||||
|
return [matches[1].trim(), matches[3].trim()];
|
||||||
|
}
|
||||||
|
return [value];
|
||||||
|
})();
|
||||||
|
if (valueFormatted && valueFormatted[1]) {
|
||||||
|
return (
|
||||||
|
<div className="leading-tight">
|
||||||
|
<div>{valueFormatted[0]}</div>
|
||||||
|
<div>{valueFormatted[1]}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return valueFormatted ? valueFormatted[0] : null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'amount',
|
||||||
|
headerName: t('Amount'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
field: 'openVolume',
|
||||||
|
valueGetter: ({ node, data }: ValueGetterParams) => {
|
||||||
|
return node?.rowPinned ? data?.notional : data?.openVolume;
|
||||||
|
},
|
||||||
|
type: 'rightAligned',
|
||||||
|
cellRendererSelector: (
|
||||||
|
params: ICellRendererParams
|
||||||
|
): CellRendererSelectorResult => {
|
||||||
|
return {
|
||||||
|
component: params.node.rowPinned ? PriceFlashCell : AmountCell,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
node,
|
||||||
|
}: PositionsTableValueFormatterParams & {
|
||||||
|
value: Position['openVolume'];
|
||||||
|
}) => {
|
||||||
|
let ret;
|
||||||
|
if (value && data) {
|
||||||
|
ret = node?.rowPinned
|
||||||
|
? addDecimalsFormatNumber(value, data.decimals)
|
||||||
|
: data;
|
||||||
|
}
|
||||||
|
// FIXME this column needs refactoring
|
||||||
|
return ret as unknown as string;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'markprice',
|
||||||
|
headerName: t('Mark price'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center justify-center',
|
||||||
|
field: 'markPrice',
|
||||||
|
cellRendererSelector: (
|
||||||
|
params: ICellRendererParams
|
||||||
|
): CellRendererSelectorResult => {
|
||||||
|
return {
|
||||||
|
component: params.node.rowPinned ? EmptyCell : PriceFlashCell,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
node,
|
||||||
|
}: PositionsTableValueFormatterParams & {
|
||||||
|
value: Position['markPrice'];
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
data &&
|
||||||
|
value &&
|
||||||
|
node?.rowPinned &&
|
||||||
|
data.marketTradingMode ===
|
||||||
|
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||||
|
) {
|
||||||
|
return addDecimalsFormatNumber(
|
||||||
|
value.toString(),
|
||||||
|
data.marketDecimalPlaces
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'entryprice',
|
||||||
|
headerName: t('Entry price'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
field: 'averageEntryPrice',
|
||||||
|
headerComponentParams: {
|
||||||
|
template:
|
||||||
|
'<div class="ag-cell-label-container" role="presentation">' +
|
||||||
|
` <span>${t('Liquidation price (est)')}</span>` +
|
||||||
|
' <span ref="eText" class="ag-header-cell-text"></span>' +
|
||||||
|
'</div>',
|
||||||
|
},
|
||||||
|
cellRenderer: ({ node, data }: GroupCellRendererParams) => {
|
||||||
|
const valueFormatted =
|
||||||
|
data && !node?.rowPinned
|
||||||
|
? (() => {
|
||||||
|
const min = BigInt(data.averageEntryPrice);
|
||||||
|
const max = BigInt(data.liquidationPrice);
|
||||||
|
const mid = BigInt(data.markPrice);
|
||||||
|
const range = max - min;
|
||||||
|
return {
|
||||||
|
low: addDecimalsFormatNumber(
|
||||||
|
min.toString(),
|
||||||
|
data.marketDecimalPlaces
|
||||||
|
),
|
||||||
|
high: addDecimalsFormatNumber(
|
||||||
|
max.toString(),
|
||||||
|
data.marketDecimalPlaces
|
||||||
|
),
|
||||||
|
value: range
|
||||||
|
? Number(((mid - min) * BigInt(100)) / range)
|
||||||
|
: 0,
|
||||||
|
intent: data.lowMarginLevel ? Intent.Warning : undefined,
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
: undefined;
|
||||||
|
return node.rowPinned ? (
|
||||||
|
''
|
||||||
|
) : (
|
||||||
|
<ProgressBarCell valueFormatted={valueFormatted} />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'leverage',
|
||||||
|
headerName: t('Leverage'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center justify-center',
|
||||||
|
field: 'currentLeverage',
|
||||||
|
type: 'rightAligned',
|
||||||
|
cellRendererSelector: (
|
||||||
|
params: ICellRendererParams
|
||||||
|
): CellRendererSelectorResult => {
|
||||||
|
return {
|
||||||
|
component: params.node.rowPinned ? EmptyCell : PriceFlashCell,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
node,
|
||||||
|
}: PositionsTableValueFormatterParams & {
|
||||||
|
value: Position['currentLeverage'];
|
||||||
|
}) => (value === undefined ? '' : formatNumber(value.toString(), 1)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'marginallocated',
|
||||||
|
headerName: t('Margin allocated'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full flex-col justify-center',
|
||||||
|
field: 'capitalUtilisation',
|
||||||
|
type: 'rightAligned',
|
||||||
|
cellRenderer: ({ value, node, data }: GroupCellRendererParams) => {
|
||||||
|
const valueFormatted =
|
||||||
|
data && value
|
||||||
|
? (() => {
|
||||||
|
return {
|
||||||
|
low: `${formatNumber(value, 2)}%`,
|
||||||
|
high: addDecimalsFormatNumber(
|
||||||
|
data.totalBalance,
|
||||||
|
data.decimals
|
||||||
|
),
|
||||||
|
value: Number(value),
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
: undefined;
|
||||||
|
return node.rowPinned ? (
|
||||||
|
''
|
||||||
|
) : (
|
||||||
|
<ProgressBarCell valueFormatted={valueFormatted} />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'realisedpnl',
|
||||||
|
headerName: t('Realised PNL'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
field: 'realisedPNL',
|
||||||
|
type: 'rightAligned',
|
||||||
|
cellClassRules: signedNumberCssClassRules,
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}: PositionsTableValueFormatterParams & {
|
||||||
|
value: Position['realisedPNL'];
|
||||||
|
}) =>
|
||||||
|
value === undefined
|
||||||
|
? ''
|
||||||
|
: addDecimalsFormatNumber(value.toString(), data.decimals),
|
||||||
|
cellRenderer: 'PriceFlashCell',
|
||||||
|
headerTooltip: t('P&L excludes any fees paid.'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'unrealisedpnl',
|
||||||
|
headerName: t('Unrealised PNL'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
field: 'unrealisedPNL',
|
||||||
|
type: 'rightAligned',
|
||||||
|
cellClassRules: signedNumberCssClassRules,
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}: PositionsTableValueFormatterParams & {
|
||||||
|
value: Position['unrealisedPNL'];
|
||||||
|
}) =>
|
||||||
|
value === undefined
|
||||||
|
? ''
|
||||||
|
: addDecimalsFormatNumber(value.toString(), data.decimals),
|
||||||
|
cellRenderer: 'PriceFlashCell',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'updated',
|
||||||
|
headerName: t('Updated'),
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
field: 'updatedAt',
|
||||||
|
type: 'rightAligned',
|
||||||
|
valueFormatter: ({
|
||||||
|
value,
|
||||||
|
}: PositionsTableValueFormatterParams & {
|
||||||
|
value: Position['updatedAt'];
|
||||||
|
}) => {
|
||||||
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return getDateTimeFormat().format(new Date(value));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
const defaultColDef = useMemo(() => {
|
||||||
|
return {
|
||||||
|
sortable: true,
|
||||||
|
unSortIcon: true,
|
||||||
|
headerClass: 'uppercase',
|
||||||
|
cellClass: '!flex h-full items-center',
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return { columnDefs, defaultColDef };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useColumnDefinitions;
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
subscription CandleLive($marketId: ID!) {
|
|
||||||
candles(marketId: $marketId, interval: INTERVAL_I1H) {
|
|
||||||
close
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
fragment SimpleMarketDataFields on MarketData {
|
|
||||||
market {
|
|
||||||
id
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query SimpleMarkets($CandleSince: String!) {
|
|
||||||
markets {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
state
|
|
||||||
data {
|
|
||||||
...SimpleMarketDataFields
|
|
||||||
}
|
|
||||||
tradableInstrument {
|
|
||||||
instrument {
|
|
||||||
code
|
|
||||||
metadata {
|
|
||||||
tags
|
|
||||||
}
|
|
||||||
product {
|
|
||||||
__typename
|
|
||||||
... on Future {
|
|
||||||
quoteName
|
|
||||||
settlementAsset {
|
|
||||||
symbol
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
candles(interval: INTERVAL_I1H, since: $CandleSince) {
|
|
||||||
open
|
|
||||||
close
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subscription SimpleMarketDataSub {
|
|
||||||
marketData {
|
|
||||||
...SimpleMarketDataFields
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Generated
-30
@@ -1,30 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
// @generated
|
|
||||||
// This file was automatically generated and should not be edited.
|
|
||||||
|
|
||||||
import { MarketState } from "@vegaprotocol/types";
|
|
||||||
|
|
||||||
// ====================================================
|
|
||||||
// GraphQL fragment: SimpleMarketDataFields
|
|
||||||
// ====================================================
|
|
||||||
|
|
||||||
export interface SimpleMarketDataFields_market {
|
|
||||||
__typename: "Market";
|
|
||||||
/**
|
|
||||||
* Market ID
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* Current state of the market
|
|
||||||
*/
|
|
||||||
state: MarketState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarketDataFields {
|
|
||||||
__typename: "MarketData";
|
|
||||||
/**
|
|
||||||
* market ID of the associated mark price
|
|
||||||
*/
|
|
||||||
market: SimpleMarketDataFields_market;
|
|
||||||
}
|
|
||||||
Generated
-37
@@ -1,37 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
// @generated
|
|
||||||
// This file was automatically generated and should not be edited.
|
|
||||||
|
|
||||||
import { MarketState } from "@vegaprotocol/types";
|
|
||||||
|
|
||||||
// ====================================================
|
|
||||||
// GraphQL subscription operation: SimpleMarketDataSub
|
|
||||||
// ====================================================
|
|
||||||
|
|
||||||
export interface SimpleMarketDataSub_marketData_market {
|
|
||||||
__typename: "Market";
|
|
||||||
/**
|
|
||||||
* Market ID
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* Current state of the market
|
|
||||||
*/
|
|
||||||
state: MarketState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarketDataSub_marketData {
|
|
||||||
__typename: "MarketData";
|
|
||||||
/**
|
|
||||||
* market ID of the associated mark price
|
|
||||||
*/
|
|
||||||
market: SimpleMarketDataSub_marketData_market;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarketDataSub {
|
|
||||||
/**
|
|
||||||
* Subscribe to the mark price changes
|
|
||||||
*/
|
|
||||||
marketData: SimpleMarketDataSub_marketData;
|
|
||||||
}
|
|
||||||
-109
@@ -1,109 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
// @generated
|
|
||||||
// This file was automatically generated and should not be edited.
|
|
||||||
|
|
||||||
import { MarketState } from "@vegaprotocol/types";
|
|
||||||
|
|
||||||
// ====================================================
|
|
||||||
// GraphQL query operation: SimpleMarkets
|
|
||||||
// ====================================================
|
|
||||||
|
|
||||||
export interface SimpleMarkets_markets_tradableInstrument_instrument_metadata {
|
|
||||||
__typename: "InstrumentMetadata";
|
|
||||||
/**
|
|
||||||
* An arbitrary list of tags to associated to associate to the Instrument (string list)
|
|
||||||
*/
|
|
||||||
tags: string[] | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarkets_markets_tradableInstrument_instrument_product_settlementAsset {
|
|
||||||
__typename: "Asset";
|
|
||||||
/**
|
|
||||||
* The symbol of the asset (e.g: GBP)
|
|
||||||
*/
|
|
||||||
symbol: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarkets_markets_tradableInstrument_instrument_product {
|
|
||||||
__typename: "Future";
|
|
||||||
/**
|
|
||||||
* String representing the quote (e.g. BTCUSD -> USD is quote)
|
|
||||||
*/
|
|
||||||
quoteName: string;
|
|
||||||
/**
|
|
||||||
* The name of the asset (string)
|
|
||||||
*/
|
|
||||||
settlementAsset: SimpleMarkets_markets_tradableInstrument_instrument_product_settlementAsset;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarkets_markets_tradableInstrument_instrument {
|
|
||||||
__typename: "Instrument";
|
|
||||||
/**
|
|
||||||
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
|
|
||||||
*/
|
|
||||||
code: string;
|
|
||||||
/**
|
|
||||||
* Full and fairly descriptive name for the instrument
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* Metadata for this instrument
|
|
||||||
*/
|
|
||||||
metadata: SimpleMarkets_markets_tradableInstrument_instrument_metadata;
|
|
||||||
/**
|
|
||||||
* A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
|
|
||||||
*/
|
|
||||||
product: SimpleMarkets_markets_tradableInstrument_instrument_product;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarkets_markets_tradableInstrument {
|
|
||||||
__typename: "TradableInstrument";
|
|
||||||
/**
|
|
||||||
* An instance of, or reference to, a fully specified instrument.
|
|
||||||
*/
|
|
||||||
instrument: SimpleMarkets_markets_tradableInstrument_instrument;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarkets_markets_candles {
|
|
||||||
__typename: "Candle";
|
|
||||||
/**
|
|
||||||
* Open price (uint64)
|
|
||||||
*/
|
|
||||||
open: string;
|
|
||||||
/**
|
|
||||||
* Close price (uint64)
|
|
||||||
*/
|
|
||||||
close: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarkets_markets {
|
|
||||||
__typename: "Market";
|
|
||||||
/**
|
|
||||||
* Market ID
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* Current state of the market
|
|
||||||
*/
|
|
||||||
state: MarketState;
|
|
||||||
/**
|
|
||||||
* An instance of, or reference to, a tradable instrument.
|
|
||||||
*/
|
|
||||||
tradableInstrument: SimpleMarkets_markets_tradableInstrument;
|
|
||||||
/**
|
|
||||||
* Candles on a market, for the 'last' n candles, at 'interval' seconds as specified by parameters
|
|
||||||
*/
|
|
||||||
candles: (SimpleMarkets_markets_candles | null)[] | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarkets {
|
|
||||||
/**
|
|
||||||
* One or more instruments that are trading on the VEGA network
|
|
||||||
*/
|
|
||||||
markets: SimpleMarkets_markets[] | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleMarketsVariables {
|
|
||||||
CandleSince: string;
|
|
||||||
}
|
|
||||||
-117
@@ -1,117 +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?: 'MarketData', market: { __typename?: 'Market', id: string, state: Types.MarketState } };
|
|
||||||
|
|
||||||
export type SimpleMarketsQueryVariables = Types.Exact<{
|
|
||||||
CandleSince: Types.Scalars['String'];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type SimpleMarketsQuery = { __typename?: 'Query', markets?: Array<{ __typename?: 'Market', id: string, name: string, state: Types.MarketState, data?: { __typename?: 'MarketData', market: { __typename?: 'Market', id: string, state: Types.MarketState } } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', 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<{ [key: string]: never; }>;
|
|
||||||
|
|
||||||
|
|
||||||
export type SimpleMarketDataSubSubscription = { __typename?: 'Subscription', marketData: { __typename?: 'MarketData', market: { __typename?: 'Market', id: string, state: Types.MarketState } } };
|
|
||||||
|
|
||||||
export const SimpleMarketDataFieldsFragmentDoc = gql`
|
|
||||||
fragment SimpleMarketDataFields on MarketData {
|
|
||||||
market {
|
|
||||||
id
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export const SimpleMarketsDocument = gql`
|
|
||||||
query SimpleMarkets($CandleSince: String!) {
|
|
||||||
markets {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
state
|
|
||||||
data {
|
|
||||||
...SimpleMarketDataFields
|
|
||||||
}
|
|
||||||
tradableInstrument {
|
|
||||||
instrument {
|
|
||||||
code
|
|
||||||
metadata {
|
|
||||||
tags
|
|
||||||
}
|
|
||||||
product {
|
|
||||||
__typename
|
|
||||||
... on Future {
|
|
||||||
quoteName
|
|
||||||
settlementAsset {
|
|
||||||
symbol
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
candles(interval: INTERVAL_I1H, since: $CandleSince) {
|
|
||||||
open
|
|
||||||
close
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${SimpleMarketDataFieldsFragmentDoc}`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __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 {
|
|
||||||
marketData {
|
|
||||||
...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: {
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
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,9 +1,9 @@
|
|||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
|
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
|
||||||
import { MarketState } from '@vegaprotocol/types';
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
import { IS_MARKET_TRADABLE } from '../../constants';
|
|
||||||
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
|
||||||
import colors from 'tailwindcss/colors';
|
import colors from 'tailwindcss/colors';
|
||||||
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
|
import { IS_MARKET_TRADABLE } from '../../constants';
|
||||||
|
|
||||||
export const STATES_FILTER = [
|
export const STATES_FILTER = [
|
||||||
{ value: 'all', text: t('All') },
|
{ value: 'all', text: t('All') },
|
||||||
@@ -192,8 +192,17 @@ export const agGridDarkVariables = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const ROW_CLASS_RULES = {
|
export const ROW_CLASS_RULES = {
|
||||||
'cursor-pointer': ({ data }: { data: SimpleMarkets_markets }) =>
|
'cursor-pointer': ({ data }: { data: Market }) =>
|
||||||
IS_MARKET_TRADABLE(data || {}),
|
IS_MARKET_TRADABLE(data || {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LARGE_SCREENS = ['xl', 'xxl'];
|
export const LARGE_SCREENS = ['xl', 'xxl'];
|
||||||
|
|
||||||
|
export const ALL_PRODUCTS_ITEM = {
|
||||||
|
name: t('All Markets'),
|
||||||
|
id: 'allmarkets',
|
||||||
|
cssClass: 'text-pink',
|
||||||
|
color: theme.colors.pink,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AG_GRID_CONTAINER_STYLES = { width: '100%', height: '100%' };
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
import produce from 'immer';
|
|
||||||
import { gql } from '@apollo/client';
|
|
||||||
import { makeDataProvider } from '@vegaprotocol/react-helpers';
|
|
||||||
import type {
|
|
||||||
SimpleMarkets,
|
|
||||||
SimpleMarkets_markets,
|
|
||||||
} from './__generated__/SimpleMarkets';
|
|
||||||
import type {
|
|
||||||
SimpleMarketDataSub,
|
|
||||||
SimpleMarketDataSub_marketData,
|
|
||||||
} from './__generated__/SimpleMarketDataSub';
|
|
||||||
|
|
||||||
export const MARKETS_QUERY = gql`
|
|
||||||
query SimpleMarkets($CandleSince: String!) {
|
|
||||||
markets {
|
|
||||||
id
|
|
||||||
state
|
|
||||||
tradableInstrument {
|
|
||||||
instrument {
|
|
||||||
code
|
|
||||||
name
|
|
||||||
metadata {
|
|
||||||
tags
|
|
||||||
}
|
|
||||||
product {
|
|
||||||
__typename
|
|
||||||
... on Future {
|
|
||||||
quoteName
|
|
||||||
settlementAsset {
|
|
||||||
symbol
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
candles(interval: INTERVAL_I1H, since: $CandleSince) {
|
|
||||||
open
|
|
||||||
close
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const MARKET_DATA_SUB = gql`
|
|
||||||
subscription SimpleMarketDataSub {
|
|
||||||
marketData {
|
|
||||||
market {
|
|
||||||
id
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const CANDLE_SUB = gql`
|
|
||||||
subscription CandleLive($marketId: ID!) {
|
|
||||||
candles(marketId: $marketId, interval: INTERVAL_I1H) {
|
|
||||||
close
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const update = (
|
|
||||||
data: SimpleMarkets_markets[],
|
|
||||||
delta: SimpleMarketDataSub_marketData
|
|
||||||
) => {
|
|
||||||
return produce(data, (draft) => {
|
|
||||||
const index = draft.findIndex((m) => m.id === delta.market.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
draft[index].state = delta.market.state;
|
|
||||||
}
|
|
||||||
// @TODO - else push new market to draft
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getData = (responseData: SimpleMarkets) => responseData.markets;
|
|
||||||
const getDelta = (
|
|
||||||
subscriptionData: SimpleMarketDataSub
|
|
||||||
): SimpleMarketDataSub_marketData => subscriptionData.marketData;
|
|
||||||
|
|
||||||
export const dataProvider = makeDataProvider<
|
|
||||||
SimpleMarkets,
|
|
||||||
SimpleMarkets_markets[],
|
|
||||||
SimpleMarketDataSub,
|
|
||||||
SimpleMarketDataSub_marketData
|
|
||||||
>({
|
|
||||||
query: MARKETS_QUERY,
|
|
||||||
subscriptionQuery: MARKET_DATA_SUB,
|
|
||||||
update,
|
|
||||||
getData,
|
|
||||||
getDelta,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default dataProvider;
|
|
||||||
+37
-107
@@ -4,21 +4,14 @@ import {
|
|||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
waitFor,
|
waitFor,
|
||||||
cleanup,
|
|
||||||
getAllByRole,
|
getAllByRole,
|
||||||
fireEvent,
|
fireEvent,
|
||||||
} from '@testing-library/react';
|
} from '@testing-library/react';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { MarketState } from '@vegaprotocol/types';
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
import SimpleMarketList from './simple-market-list';
|
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();
|
const mockedNavigate = jest.fn();
|
||||||
|
|
||||||
@@ -28,20 +21,11 @@ jest.mock('react-router-dom', () => ({
|
|||||||
useParams: () => ({}),
|
useParams: () => ({}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('date-fns', () => ({
|
jest.mock('./simple-market-percent-change', () => jest.fn());
|
||||||
subDays: () => new Date('2022-06-02T11:11:21.721Z'),
|
|
||||||
}));
|
|
||||||
|
|
||||||
let updateMock: ({
|
let marketsMock = [
|
||||||
delta,
|
|
||||||
}: {
|
|
||||||
delta: SimpleMarketDataSub_marketData;
|
|
||||||
}) => boolean;
|
|
||||||
|
|
||||||
let mockData = [
|
|
||||||
{
|
{
|
||||||
id: '1',
|
id: 'MARKET_A',
|
||||||
name: 'Market 1',
|
|
||||||
state: MarketState.STATE_ACTIVE,
|
state: MarketState.STATE_ACTIVE,
|
||||||
tradableInstrument: {
|
tradableInstrument: {
|
||||||
instrument: {
|
instrument: {
|
||||||
@@ -57,8 +41,7 @@ let mockData = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: 'MARKET_B',
|
||||||
name: 'Market 2',
|
|
||||||
state: MarketState.STATE_ACTIVE,
|
state: MarketState.STATE_ACTIVE,
|
||||||
tradableInstrument: {
|
tradableInstrument: {
|
||||||
instrument: {
|
instrument: {
|
||||||
@@ -73,20 +56,26 @@ let mockData = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as unknown as SimpleMarkets_markets[];
|
] as unknown as Market[];
|
||||||
|
|
||||||
const mockUseDataProvider = ({ update }: { update: () => boolean }) => {
|
const LIB = '@vegaprotocol/market-list';
|
||||||
updateMock = update;
|
const useMarketList = () => {
|
||||||
return { data: mockData, loading: false, error: false };
|
return {
|
||||||
|
data: {
|
||||||
|
markets: marketsMock,
|
||||||
|
marketsData: [],
|
||||||
|
marketsCandles: [],
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
error: false,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
jest.mock(LIB, () => ({
|
||||||
jest.mock('@vegaprotocol/react-helpers', () => ({
|
...jest.requireActual(LIB),
|
||||||
...jest.requireActual('@vegaprotocol/react-helpers'),
|
useMarketList: jest.fn(() => useMarketList()),
|
||||||
useDataProvider: jest.fn((args) => mockUseDataProvider(args)),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockIsTradable = jest.fn((_arg) => true);
|
const mockIsTradable = jest.fn((_arg) => true);
|
||||||
|
|
||||||
jest.mock('../../constants', () => ({
|
jest.mock('../../constants', () => ({
|
||||||
...jest.requireActual('../../constants'),
|
...jest.requireActual('../../constants'),
|
||||||
IS_MARKET_TRADABLE: jest.fn((arg) => mockIsTradable(arg)),
|
IS_MARKET_TRADABLE: jest.fn((arg) => mockIsTradable(arg)),
|
||||||
@@ -95,25 +84,12 @@ jest.mock('../../constants', () => ({
|
|||||||
describe('SimpleMarketList', () => {
|
describe('SimpleMarketList', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
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 () => {
|
it('should be properly rendered with some data', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(
|
render(
|
||||||
<MockedProvider mocks={[mocks]}>
|
<MockedProvider mocks={[]}>
|
||||||
<SimpleMarketList />
|
<SimpleMarketList />
|
||||||
</MockedProvider>,
|
</MockedProvider>,
|
||||||
{ wrapper: BrowserRouter }
|
{ wrapper: BrowserRouter }
|
||||||
@@ -124,95 +100,49 @@ describe('SimpleMarketList', () => {
|
|||||||
expect(
|
expect(
|
||||||
document.querySelector('.ag-center-cols-container')
|
document.querySelector('.ag-center-cols-container')
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
|
||||||
await waitFor(() => {
|
|
||||||
const container = document.querySelector('.ag-center-cols-container');
|
const container = document.querySelector('.ag-center-cols-container');
|
||||||
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
|
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 () => {
|
it('click on row should be properly handled', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(
|
render(
|
||||||
<MockedProvider mocks={[mocks]}>
|
<MockedProvider mocks={[]}>
|
||||||
<SimpleMarketList />
|
<SimpleMarketList />
|
||||||
</MockedProvider>,
|
</MockedProvider>,
|
||||||
{ wrapper: BrowserRouter }
|
{ wrapper: BrowserRouter }
|
||||||
);
|
);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
||||||
});
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(
|
|
||||||
document.querySelector('.ag-center-cols-container')
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
mockIsTradable.mockClear();
|
mockIsTradable.mockClear();
|
||||||
const container = document.querySelector('.ag-center-cols-container');
|
const container = document.querySelector('.ag-center-cols-container');
|
||||||
const firstRow = getAllByRole(container as HTMLDivElement, 'row')[0];
|
const firstRow = getAllByRole(container as HTMLDivElement, 'row')[0];
|
||||||
expect(firstRow).toHaveAttribute('row-id', '1');
|
expect(firstRow).toHaveAttribute('row-id', marketsMock[0].id);
|
||||||
fireEvent.click(firstRow);
|
await act(async () => {
|
||||||
|
fireEvent.click(firstRow);
|
||||||
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockIsTradable).toHaveBeenCalledWith({
|
expect(mockIsTradable).toHaveBeenCalledWith(
|
||||||
...mockData[0],
|
expect.objectContaining({
|
||||||
percentChange: '-',
|
id: marketsMock[0].id,
|
||||||
});
|
state: MarketState.STATE_ACTIVE,
|
||||||
expect(mockedNavigate).toHaveBeenCalledWith(`/trading/${mockData[0].id}`);
|
})
|
||||||
|
);
|
||||||
|
expect(mockedNavigate).toHaveBeenCalledWith(
|
||||||
|
`/trading/${marketsMock[0].id}`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be properly renderer as empty', async () => {
|
it('should be properly renderer as empty', async () => {
|
||||||
mockData = [];
|
marketsMock = [];
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(
|
render(
|
||||||
<MockedProvider mocks={[mocks]}>
|
<MockedProvider mocks={[]}>
|
||||||
<SimpleMarketList />
|
<SimpleMarketList />
|
||||||
</MockedProvider>,
|
</MockedProvider>,
|
||||||
{ wrapper: BrowserRouter }
|
{ wrapper: BrowserRouter }
|
||||||
);
|
);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByText('No data to display')).toBeInTheDocument();
|
expect(screen.getByText('No data to display')).toBeInTheDocument();
|
||||||
|
|||||||
@@ -1,40 +1,18 @@
|
|||||||
import React, {
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
} from 'react';
|
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { subDays } from 'date-fns';
|
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
import {
|
|
||||||
useDataProvider,
|
|
||||||
useScreenDimensions,
|
|
||||||
} from '@vegaprotocol/react-helpers';
|
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
|
||||||
import type { MarketState } from '@vegaprotocol/types';
|
import type { MarketState } from '@vegaprotocol/types';
|
||||||
import useMarketsFilterData from '../../hooks/use-markets-filter-data';
|
import useMarketsFilterData from './use-markets-filter-data';
|
||||||
import useColumnDefinitions from '../../hooks/use-column-definitions';
|
import useColumnDefinitions from './use-column-definitions';
|
||||||
import dataProvider from './data-provider';
|
|
||||||
import * as constants from './constants';
|
|
||||||
import SimpleMarketToolbar from './simple-market-toolbar';
|
import SimpleMarketToolbar from './simple-market-toolbar';
|
||||||
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
|
||||||
import type { SimpleMarketDataSub_marketData } from './__generated__/SimpleMarketDataSub';
|
|
||||||
import { IS_MARKET_TRADABLE } from '../../constants';
|
import { IS_MARKET_TRADABLE } from '../../constants';
|
||||||
import type {
|
import { ConsoleLiteGrid } from '../console-lite-grid';
|
||||||
CellKeyDownEvent,
|
import type { Market, MarketsListData } from '@vegaprotocol/market-list';
|
||||||
FullWidthCellKeyDownEvent,
|
import { useMarketList } from '@vegaprotocol/market-list';
|
||||||
} from 'ag-grid-community/dist/lib/events';
|
|
||||||
import type {
|
|
||||||
GetRowIdParams,
|
|
||||||
TabToNextCellParams,
|
|
||||||
} from 'ag-grid-community/dist/lib/entities/iCallbackParams';
|
|
||||||
|
|
||||||
export type SimpleMarketsType = SimpleMarkets_markets & {
|
export type MarketWithPercentChange = Market & {
|
||||||
percentChange?: number | '-';
|
percentChange?: number | '-';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,33 +23,14 @@ export type RouterParams = Partial<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
const SimpleMarketList = () => {
|
const SimpleMarketList = () => {
|
||||||
const { isMobile, screenSize } = useScreenDimensions();
|
const { isMobile } = useScreenDimensions();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const params = useParams<RouterParams>();
|
const params = useParams<RouterParams>();
|
||||||
const theme = useContext(ThemeContext);
|
|
||||||
const statusesRef = useRef<Record<string, MarketState | ''>>({});
|
const statusesRef = useRef<Record<string, MarketState | ''>>({});
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const variables = useMemo(
|
|
||||||
() => ({
|
|
||||||
CandleSince: subDays(Date.now(), 1).toJSON(),
|
|
||||||
}),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const update = useCallback(
|
|
||||||
({ delta }: { delta: SimpleMarketDataSub_marketData }) =>
|
|
||||||
statusesRef.current[delta.market.id] === delta.market.state,
|
|
||||||
[statusesRef]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data, error, loading } = useDataProvider({
|
const { data, error, loading } = useMarketList();
|
||||||
dataProvider,
|
const localData = useMarketsFilterData(data as MarketsListData, params);
|
||||||
update,
|
|
||||||
variables,
|
|
||||||
});
|
|
||||||
const localData: Array<SimpleMarketsType> = useMarketsFilterData(
|
|
||||||
data || [],
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnGridReady = useCallback(() => {
|
const handleOnGridReady = useCallback(() => {
|
||||||
gridRef.current?.api?.sizeColumnsToFit();
|
gridRef.current?.api?.sizeColumnsToFit();
|
||||||
@@ -79,7 +38,7 @@ const SimpleMarketList = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const statuses: Record<string, MarketState | ''> = {};
|
const statuses: Record<string, MarketState | ''> = {};
|
||||||
data?.forEach((market) => {
|
data?.markets?.forEach((market) => {
|
||||||
statuses[market.id] = market.state || '';
|
statuses[market.id] = market.state || '';
|
||||||
});
|
});
|
||||||
statusesRef.current = statuses;
|
statusesRef.current = statuses;
|
||||||
@@ -92,10 +51,8 @@ const SimpleMarketList = () => {
|
|||||||
|
|
||||||
const { columnDefs, defaultColDef } = useColumnDefinitions({ isMobile });
|
const { columnDefs, defaultColDef } = useColumnDefinitions({ isMobile });
|
||||||
|
|
||||||
const getRowId = useCallback(({ data }: GetRowIdParams) => data.id, []);
|
|
||||||
|
|
||||||
const handleRowClicked = useCallback(
|
const handleRowClicked = useCallback(
|
||||||
({ data }: { data: SimpleMarketsType }) => {
|
({ data }: { data: Market }) => {
|
||||||
if (IS_MARKET_TRADABLE(data)) {
|
if (IS_MARKET_TRADABLE(data)) {
|
||||||
navigate(`/trading/${data.id}`);
|
navigate(`/trading/${data.id}`);
|
||||||
}
|
}
|
||||||
@@ -103,64 +60,17 @@ const SimpleMarketList = () => {
|
|||||||
[navigate]
|
[navigate]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onTabToNextCell = useCallback((params: TabToNextCellParams) => {
|
|
||||||
const {
|
|
||||||
api,
|
|
||||||
previousCellPosition: { rowIndex },
|
|
||||||
} = params;
|
|
||||||
const rowCount = api.getDisplayedRowCount();
|
|
||||||
if (rowCount <= rowIndex + 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { ...params.previousCellPosition, rowIndex: rowIndex + 1 };
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onCellKeyDown = useCallback(
|
|
||||||
(
|
|
||||||
params: (CellKeyDownEvent | FullWidthCellKeyDownEvent) & {
|
|
||||||
event: KeyboardEvent;
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
const { event: { key = '' } = {}, data } = params;
|
|
||||||
if (key === 'Enter') {
|
|
||||||
handleRowClicked({ data });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[handleRowClicked]
|
|
||||||
);
|
|
||||||
|
|
||||||
const shouldSuppressHorizontalScroll = useMemo(() => {
|
|
||||||
return !isMobile && constants.LARGE_SCREENS.includes(screenSize);
|
|
||||||
}, [isMobile, screenSize]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full p-4 md:p-6 grid grid-rows-[min-content,1fr]">
|
<div className="h-full p-4 md:p-6 grid grid-rows-[min-content,1fr]">
|
||||||
<SimpleMarketToolbar data={data || []} />
|
<SimpleMarketToolbar data={data?.markets || []} />
|
||||||
<AsyncRenderer loading={loading} error={error} data={localData}>
|
<AsyncRenderer loading={loading} error={error} data={localData}>
|
||||||
<AgGrid
|
<ConsoleLiteGrid<MarketWithPercentChange>
|
||||||
className="mb-32 min-h-[300px]"
|
classNamesParam="mb-32 min-h-[300px]"
|
||||||
defaultColDef={defaultColDef}
|
|
||||||
columnDefs={columnDefs}
|
columnDefs={columnDefs}
|
||||||
rowData={localData}
|
rowData={localData}
|
||||||
rowHeight={60}
|
defaultColDef={defaultColDef}
|
||||||
customThemeParams={
|
handleRowClicked={handleRowClicked}
|
||||||
theme === 'dark'
|
getRowId={({ data }) => data.id}
|
||||||
? constants.agGridDarkVariables
|
|
||||||
: constants.agGridLightVariables
|
|
||||||
}
|
|
||||||
onGridReady={handleOnGridReady}
|
|
||||||
onRowClicked={handleRowClicked}
|
|
||||||
rowClass={isMobile ? 'mobile' : ''}
|
|
||||||
rowClassRules={constants.ROW_CLASS_RULES}
|
|
||||||
ref={gridRef}
|
|
||||||
overlayNoRowsTemplate={t('No data to display')}
|
|
||||||
suppressContextMenu
|
|
||||||
getRowId={getRowId}
|
|
||||||
suppressMovableColumns
|
|
||||||
suppressRowTransform
|
|
||||||
onCellKeyDown={onCellKeyDown}
|
|
||||||
tabToNextCell={onTabToNextCell}
|
|
||||||
suppressHorizontalScroll={shouldSuppressHorizontalScroll}
|
|
||||||
/>
|
/>
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+4
-12
@@ -2,10 +2,10 @@ import React from 'react';
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { MockedProvider } from '@apollo/react-testing';
|
import { MockedProvider } from '@apollo/react-testing';
|
||||||
import SimpleMarketPercentChange from './simple-market-percent-change';
|
import SimpleMarketPercentChange from './simple-market-percent-change';
|
||||||
import type { SimpleMarkets_markets_candles } from './__generated__/SimpleMarkets';
|
import type { Candle } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
describe('SimpleMarketPercentChange should parse proper change', () => {
|
describe('SimpleMarketPercentChange should parse proper change', () => {
|
||||||
let candles: (SimpleMarkets_markets_candles | null)[] | null;
|
let candles: (Candle | null)[] | null;
|
||||||
const setValue = () => undefined;
|
const setValue = () => undefined;
|
||||||
it('empty array', () => {
|
it('empty array', () => {
|
||||||
candles = [];
|
candles = [];
|
||||||
@@ -34,11 +34,7 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
|
|||||||
expect(screen.getByText('-')).toBeInTheDocument();
|
expect(screen.getByText('-')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
it('an appreciated one', () => {
|
it('an appreciated one', () => {
|
||||||
candles = [
|
candles = [{ open: '50' } as Candle, { close: '100' } as Candle, null];
|
||||||
{ open: '50' } as SimpleMarkets_markets_candles,
|
|
||||||
{ close: '100' } as SimpleMarkets_markets_candles,
|
|
||||||
null,
|
|
||||||
];
|
|
||||||
render(
|
render(
|
||||||
<MockedProvider>
|
<MockedProvider>
|
||||||
<SimpleMarketPercentChange
|
<SimpleMarketPercentChange
|
||||||
@@ -54,11 +50,7 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('a depreciated one', () => {
|
it('a depreciated one', () => {
|
||||||
candles = [
|
candles = [{ open: '100' } as Candle, { close: '50' } as Candle, null];
|
||||||
{ open: '100' } as SimpleMarkets_markets_candles,
|
|
||||||
{ close: '50' } as SimpleMarkets_markets_candles,
|
|
||||||
null,
|
|
||||||
];
|
|
||||||
render(
|
render(
|
||||||
<MockedProvider>
|
<MockedProvider>
|
||||||
<SimpleMarketPercentChange
|
<SimpleMarketPercentChange
|
||||||
|
|||||||
+20
-18
@@ -1,26 +1,20 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { InView } from 'react-intersection-observer';
|
import { InView } from 'react-intersection-observer';
|
||||||
import { useSubscription } from '@apollo/client';
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
import type { SimpleMarkets_markets_candles } from './__generated__/SimpleMarkets';
|
import type { Candle } from '@vegaprotocol/market-list';
|
||||||
import type {
|
import { marketCandlesProvider } from '@vegaprotocol/market-list';
|
||||||
CandleLive,
|
import { Interval } from '@vegaprotocol/types';
|
||||||
CandleLiveVariables,
|
|
||||||
} from './__generated__/CandleLive';
|
|
||||||
import { CANDLE_SUB } from './data-provider';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
candles: (SimpleMarkets_markets_candles | null)[] | null;
|
candles: (Candle | null)[] | null;
|
||||||
marketId: string;
|
marketId: string;
|
||||||
setValue: (arg: unknown) => void;
|
setValue: (arg: unknown) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMPTY_VALUE = ' - ';
|
const EMPTY_VALUE = ' - ';
|
||||||
|
|
||||||
const getChange = (
|
const getChange = (candles: (Candle | null)[] | null, lastClose?: string) => {
|
||||||
candles: (SimpleMarkets_markets_candles | null)[] | null,
|
|
||||||
lastClose?: string
|
|
||||||
) => {
|
|
||||||
if (candles) {
|
if (candles) {
|
||||||
const first = parseInt(candles.find((item) => item?.open)?.open || '-1');
|
const first = parseInt(candles.find((item) => item?.open)?.open || '-1');
|
||||||
const last =
|
const last =
|
||||||
@@ -74,11 +68,19 @@ const SimpleMarketPercentChangeWrapper = (props: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SimpleMarketPercentChange = ({ candles, marketId, setValue }: Props) => {
|
const SimpleMarketPercentChange = ({ candles, marketId, setValue }: Props) => {
|
||||||
const { data: { candles: { close = undefined } = {} } = {} } =
|
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||||
useSubscription<CandleLive, CandleLiveVariables>(CANDLE_SUB, {
|
const { data } = useDataProvider({
|
||||||
variables: { marketId },
|
dataProvider: marketCandlesProvider,
|
||||||
});
|
variables: {
|
||||||
const change = getChange(candles, close);
|
marketId,
|
||||||
|
interval: Interval.INTERVAL_I1D,
|
||||||
|
since: new Date(yesterday * 1000).toISOString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const close = data?.map((m) => m.close);
|
||||||
|
|
||||||
|
const change = getChange(candles, close?.[0]);
|
||||||
const colorClasses = getClassColor(change);
|
const colorClasses = getClassColor(change);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const value = parseFloat(change);
|
const value = parseFloat(change);
|
||||||
|
|||||||
+10
-10
@@ -1,36 +1,36 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import type { MarketNames_markets } from '@vegaprotocol/deal-ticket';
|
|
||||||
import { MarketState } from '@vegaprotocol/types';
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
import MarketNameRenderer from './simple-market-renderer';
|
import MarketNameRenderer from './simple-market-renderer';
|
||||||
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
describe('SimpleMarketRenderer', () => {
|
describe('SimpleMarketRenderer', () => {
|
||||||
const market = {
|
const market = {
|
||||||
id: 'marketId',
|
id: 'MARKET_A',
|
||||||
state: MarketState.STATE_ACTIVE,
|
state: MarketState.STATE_ACTIVE,
|
||||||
tradableInstrument: {
|
tradableInstrument: {
|
||||||
instrument: {
|
instrument: {
|
||||||
code: 'Market code',
|
code: 'MARKET_A_CODE',
|
||||||
name: 'Market Name',
|
name: 'MARKET_A_NAME',
|
||||||
product: {
|
product: {
|
||||||
quoteName: 'Quote name',
|
quoteName: 'MARKET_A_QUOTE_NAME',
|
||||||
},
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: null,
|
tags: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as MarketNames_markets;
|
} as Market;
|
||||||
|
|
||||||
it('should properly render not mobile', () => {
|
it('should properly render not mobile', () => {
|
||||||
render(<MarketNameRenderer market={market} isMobile={false} />);
|
render(<MarketNameRenderer market={market} isMobile={false} />);
|
||||||
expect(screen.getByText('Market Name')).toBeInTheDocument();
|
expect(screen.getByText('MARKET_A_NAME')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Quote name')).toBeInTheDocument();
|
expect(screen.getByText('MARKET_A_QUOTE_NAME')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly render mobile', () => {
|
it('should properly render mobile', () => {
|
||||||
render(<MarketNameRenderer market={market} isMobile={true} />);
|
render(<MarketNameRenderer market={market} isMobile={true} />);
|
||||||
expect(screen.getByText('Market code')).toBeInTheDocument();
|
expect(screen.getByText('MARKET_A_CODE')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Quote name')).toBeInTheDocument();
|
expect(screen.getByText('MARKET_A_QUOTE_NAME')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React from 'react';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { MarketNames_markets } from '@vegaprotocol/deal-ticket';
|
|
||||||
import SimpleMarketExpires from './simple-market-expires';
|
import SimpleMarketExpires from './simple-market-expires';
|
||||||
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
market: MarketNames_markets;
|
market: Market;
|
||||||
isMobile?: boolean;
|
isMobile?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useNavigate, useParams, Link } from 'react-router-dom';
|
import { useNavigate, useParams, Link } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
} from '@radix-ui/react-dropdown-menu';
|
} from '@radix-ui/react-dropdown-menu';
|
||||||
import { IconNames } from '@blueprintjs/icons';
|
import { IconNames } from '@blueprintjs/icons';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
|
|
||||||
import {
|
import {
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -16,11 +15,13 @@ import {
|
|||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { MarketState } from '@vegaprotocol/types';
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
import useMarketFiltersData from '../../hooks/use-markets-filter';
|
import useMarketFiltersData from '../../hooks/use-markets-filter';
|
||||||
import { STATES_FILTER } from './constants';
|
import type { Markets_marketsConnection_edges_node } from '@vegaprotocol/market-list';
|
||||||
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
import { HorizontalMenu } from '../horizontal-menu';
|
||||||
|
import type { HorizontalMenuItem } from '../horizontal-menu';
|
||||||
|
import * as constants from './constants';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: SimpleMarkets_markets[];
|
data: Markets_marketsConnection_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const SimpleMarketToolbar = ({ data }: Props) => {
|
const SimpleMarketToolbar = ({ data }: Props) => {
|
||||||
@@ -28,37 +29,6 @@ const SimpleMarketToolbar = ({ data }: Props) => {
|
|||||||
const params = useParams();
|
const params = useParams();
|
||||||
const { products, assetsPerProduct } = useMarketFiltersData(data);
|
const { products, assetsPerProduct } = useMarketFiltersData(data);
|
||||||
const [isOpen, setOpen] = useState(false);
|
const [isOpen, setOpen] = useState(false);
|
||||||
const [activeNumber, setActiveNumber] = useState(
|
|
||||||
products?.length ? products.indexOf(params.product || '') + 1 : -1
|
|
||||||
);
|
|
||||||
|
|
||||||
const [sliderStyles, setSliderStyles] = useState<Record<string, string>>({});
|
|
||||||
const slideContRef = useRef<HTMLUListElement | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (products.length) {
|
|
||||||
setActiveNumber(products.indexOf(params.product || '') + 1);
|
|
||||||
} else {
|
|
||||||
setActiveNumber(-1);
|
|
||||||
}
|
|
||||||
}, [params, products, setActiveNumber]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const contStyles = (
|
|
||||||
slideContRef.current as HTMLUListElement
|
|
||||||
).getBoundingClientRect();
|
|
||||||
const selectedStyles = (slideContRef.current as HTMLUListElement).children[
|
|
||||||
activeNumber
|
|
||||||
]?.getBoundingClientRect();
|
|
||||||
const styles: Record<string, string> = selectedStyles
|
|
||||||
? {
|
|
||||||
backgroundColor: activeNumber ? '' : theme.colors.pink,
|
|
||||||
width: `${selectedStyles.width}px`,
|
|
||||||
left: `${selectedStyles.left - contStyles.left}px`,
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
setSliderStyles(styles);
|
|
||||||
}, [activeNumber, slideContRef]);
|
|
||||||
|
|
||||||
const onStateChange = useCallback(
|
const onStateChange = useCallback(
|
||||||
(activeState: string) => {
|
(activeState: string) => {
|
||||||
@@ -74,53 +44,33 @@ const SimpleMarketToolbar = ({ data }: Props) => {
|
|||||||
[params, navigate]
|
[params, navigate]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const productItems = useMemo(() => {
|
||||||
|
const currentState = params.state || MarketState.STATE_ACTIVE;
|
||||||
|
const noStateSkip = currentState !== MarketState.STATE_ACTIVE;
|
||||||
|
const items: HorizontalMenuItem[] = [
|
||||||
|
{
|
||||||
|
...constants.ALL_PRODUCTS_ITEM,
|
||||||
|
url: `/markets${noStateSkip ? '/' + currentState : ''}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
products.forEach((product) =>
|
||||||
|
items.push({
|
||||||
|
name: product,
|
||||||
|
id: product,
|
||||||
|
url: `/markets/${currentState}/${product}`,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return items;
|
||||||
|
}, [params, products]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-full mb-2 md:mb-8 font-alpha">
|
<div className="w-full max-w-full mb-2 md:mb-8 font-alpha">
|
||||||
<ul
|
<HorizontalMenu
|
||||||
ref={slideContRef}
|
active={params.product || constants.ALL_PRODUCTS_ITEM.id}
|
||||||
className="grid grid-flow-col auto-cols-min gap-4 relative pb-2 mb-2"
|
items={productItems}
|
||||||
data-testid="market-products-menu"
|
data-testid="market-products-menu"
|
||||||
aria-label={t('Product type')}
|
aria-label={t('Product type')}
|
||||||
>
|
/>
|
||||||
<li key="all-markets" className="md:mr-2 whitespace-nowrap">
|
|
||||||
<Link
|
|
||||||
to={`/markets${
|
|
||||||
params.state && params.state !== MarketState.STATE_ACTIVE
|
|
||||||
? '/' + params.state
|
|
||||||
: ''
|
|
||||||
}`}
|
|
||||||
aria-label={t('All markets')}
|
|
||||||
className={classNames('pl-0 text-pink hover:opacity-75', {
|
|
||||||
active: !activeNumber,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{t('All Markets')}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
{products.map((product, i) => (
|
|
||||||
<li key={product} className="mx-2 whitespace-nowrap">
|
|
||||||
<Link
|
|
||||||
to={`/markets/${
|
|
||||||
params.state || MarketState.STATE_ACTIVE
|
|
||||||
}/${product}`}
|
|
||||||
className={classNames(
|
|
||||||
'hover:opacity-75 text-black dark:text-white',
|
|
||||||
{
|
|
||||||
active: activeNumber - 1 === i,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
aria-label={product}
|
|
||||||
>
|
|
||||||
{product}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
<li
|
|
||||||
className="absolute bottom-0 h-[2px] transition-left duration-300 dark:bg-white bg-black"
|
|
||||||
key="slider"
|
|
||||||
style={sliderStyles}
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
<div className="grid gap-4 pb-2 mt-2 md:mt-6 md:grid-cols-[min-content,min-content,1fr]">
|
<div className="grid gap-4 pb-2 mt-2 md:mt-6 md:grid-cols-[min-content,min-content,1fr]">
|
||||||
<div className="pb-2">
|
<div className="pb-2">
|
||||||
<DropdownMenu open={isOpen} onOpenChange={(open) => setOpen(open)}>
|
<DropdownMenu open={isOpen} onOpenChange={(open) => setOpen(open)}>
|
||||||
@@ -130,7 +80,7 @@ const SimpleMarketToolbar = ({ data }: Props) => {
|
|||||||
onClick={() => setOpen(!isOpen)}
|
onClick={() => setOpen(!isOpen)}
|
||||||
>
|
>
|
||||||
<div className="w-full justify-between uppercase inline-flex items-center justify-center box-border">
|
<div className="w-full justify-between uppercase inline-flex items-center justify-center box-border">
|
||||||
{STATES_FILTER.find(
|
{constants.STATES_FILTER.find(
|
||||||
(state) =>
|
(state) =>
|
||||||
state.value === params.state ||
|
state.value === params.state ||
|
||||||
(!params.state && state.value === MarketState.STATE_ACTIVE)
|
(!params.state && state.value === MarketState.STATE_ACTIVE)
|
||||||
@@ -147,7 +97,7 @@ const SimpleMarketToolbar = ({ data }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
{STATES_FILTER.map(({ value, text }) => (
|
{constants.STATES_FILTER.map(({ value, text }) => (
|
||||||
<DropdownMenuCheckboxItem
|
<DropdownMenuCheckboxItem
|
||||||
className="uppercase text-ui"
|
className="uppercase text-ui"
|
||||||
key={value}
|
key={value}
|
||||||
@@ -169,7 +119,7 @@ const SimpleMarketToolbar = ({ data }: Props) => {
|
|||||||
<div className="hidden md:block text-deemphasise dark:text-midGrey">
|
<div className="hidden md:block text-deemphasise dark:text-midGrey">
|
||||||
|
|
|
|
||||||
</div>
|
</div>
|
||||||
{activeNumber > 0 && (
|
{params.product && (
|
||||||
<ul
|
<ul
|
||||||
className="md:gap-x-6 gap-x-4 gap-y-1 pb-2 md:ml-2 flex flex-wrap"
|
className="md:gap-x-6 gap-x-4 gap-y-1 pb-2 md:ml-2 flex flex-wrap"
|
||||||
data-testid="market-assets-menu"
|
data-testid="market-assets-menu"
|
||||||
@@ -189,7 +139,7 @@ const SimpleMarketToolbar = ({ data }: Props) => {
|
|||||||
{t('All')}
|
{t('All')}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
{assetsPerProduct[products[activeNumber - 1]]?.map((asset) => (
|
{assetsPerProduct[params?.product]?.map((asset) => (
|
||||||
<li key={asset}>
|
<li key={asset}>
|
||||||
<Link
|
<Link
|
||||||
to={`/markets/${params.state}/${params.product}/${asset}`}
|
to={`/markets/${params.state}/${params.product}/${asset}`}
|
||||||
|
|||||||
+10
-11
@@ -1,14 +1,13 @@
|
|||||||
import React, { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import type { SimpleMarkets_markets } from '../components/simple-market-list/__generated__/SimpleMarkets';
|
import MarketNameRenderer from './simple-market-renderer';
|
||||||
import MarketNameRenderer from '../components/simple-market-list/simple-market-renderer';
|
import SimpleMarketPercentChange from './simple-market-percent-change';
|
||||||
import SimpleMarketPercentChange from '../components/simple-market-list/simple-market-percent-change';
|
|
||||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { ValueSetterParams } from 'ag-grid-community';
|
import type { ValueSetterParams } from 'ag-grid-community';
|
||||||
import type { SimpleMarketsType } from '../components/simple-market-list/simple-market-list';
|
|
||||||
import { IconNames } from '@blueprintjs/icons';
|
import { IconNames } from '@blueprintjs/icons';
|
||||||
import { IS_MARKET_TRADABLE, MARKET_STATES_MAP } from '../constants';
|
import { IS_MARKET_TRADABLE, MARKET_STATES_MAP } from '../../constants';
|
||||||
|
import type { Candle, Market } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
@@ -24,7 +23,7 @@ const useColumnDefinitions = ({ isMobile }: Props) => {
|
|||||||
minWidth: isMobile ? 160 : 350,
|
minWidth: isMobile ? 160 : 350,
|
||||||
field: 'tradableInstrument.instrument.name',
|
field: 'tradableInstrument.instrument.name',
|
||||||
cellClass: 'overflow-visible',
|
cellClass: 'overflow-visible',
|
||||||
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
|
cellRenderer: ({ data }: { data: Market }) => (
|
||||||
<MarketNameRenderer market={data} isMobile={isMobile} />
|
<MarketNameRenderer market={data} isMobile={isMobile} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -35,7 +34,7 @@ const useColumnDefinitions = ({ isMobile }: Props) => {
|
|||||||
minWidth: isMobile ? 50 : 80,
|
minWidth: isMobile ? 50 : 80,
|
||||||
cellClass: 'uppercase flex h-full items-center',
|
cellClass: 'uppercase flex h-full items-center',
|
||||||
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
|
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
|
||||||
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
|
cellRenderer: ({ data }: { data: Market }) => (
|
||||||
<div
|
<div
|
||||||
className="grid h-full items-center text-center"
|
className="grid h-full items-center text-center"
|
||||||
title={
|
title={
|
||||||
@@ -70,7 +69,7 @@ const useColumnDefinitions = ({ isMobile }: Props) => {
|
|||||||
data,
|
data,
|
||||||
setValue,
|
setValue,
|
||||||
}: {
|
}: {
|
||||||
data: SimpleMarketsType;
|
data: { id: string; candles: Candle[] };
|
||||||
setValue: (arg: unknown) => void;
|
setValue: (arg: unknown) => void;
|
||||||
}) => (
|
}) => (
|
||||||
<SimpleMarketPercentChange
|
<SimpleMarketPercentChange
|
||||||
@@ -96,7 +95,7 @@ const useColumnDefinitions = ({ isMobile }: Props) => {
|
|||||||
field: 'state',
|
field: 'state',
|
||||||
headerClass: 'uppercase',
|
headerClass: 'uppercase',
|
||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
cellRenderer: ({ data }: { data: SimpleMarkets_markets }) => (
|
cellRenderer: ({ data }: { data: Market }) => (
|
||||||
<div className="uppercase flex h-full items-center justify-center">
|
<div className="uppercase flex h-full items-center justify-center">
|
||||||
<div className="border text-center px-2 md:px-6 leading-4 md:leading-6">
|
<div className="border text-center px-2 md:px-6 leading-4 md:leading-6">
|
||||||
{MARKET_STATES_MAP[data.state || '']}
|
{MARKET_STATES_MAP[data.state || '']}
|
||||||
@@ -110,7 +109,7 @@ const useColumnDefinitions = ({ isMobile }: Props) => {
|
|||||||
headerClass: 'uppercase',
|
headerClass: 'uppercase',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
width: isMobile ? 35 : 100,
|
width: isMobile ? 35 : 100,
|
||||||
cellRenderer: ({ data }: { data: SimpleMarkets_markets }) => (
|
cellRenderer: ({ data }: { data: Market }) => (
|
||||||
<div className="h-full flex h-full items-center justify-end">
|
<div className="h-full flex h-full items-center justify-end">
|
||||||
<div className="uppercase text-center pr-2">
|
<div className="uppercase text-center pr-2">
|
||||||
{!isMobile && t('Trade')}
|
{!isMobile && t('Trade')}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
|
import type { MarketsListData } from '@vegaprotocol/market-list';
|
||||||
|
import type { RouterParams } from './simple-market-list';
|
||||||
|
|
||||||
|
const useMarketsFilterData = (data: MarketsListData, params: RouterParams) => {
|
||||||
|
return useMemo(() => {
|
||||||
|
const markets =
|
||||||
|
data?.markets?.filter((item) => {
|
||||||
|
if (
|
||||||
|
params.product &&
|
||||||
|
params.product !==
|
||||||
|
item.tradableInstrument.instrument.product.__typename
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
params.asset &&
|
||||||
|
params.asset !== 'all' &&
|
||||||
|
params.asset !==
|
||||||
|
item.tradableInstrument.instrument.product.settlementAsset.symbol
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const state =
|
||||||
|
params.state === 'all'
|
||||||
|
? ''
|
||||||
|
: params.state
|
||||||
|
? params.state
|
||||||
|
: MarketState.STATE_ACTIVE;
|
||||||
|
if (state && state !== item.state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
return markets.map((market) => ({
|
||||||
|
...market,
|
||||||
|
candles: (data?.marketsCandles || [])
|
||||||
|
.filter((c) => c.marketId === market.id)
|
||||||
|
.map((c) => c.candles),
|
||||||
|
}));
|
||||||
|
}, [
|
||||||
|
data?.marketsCandles,
|
||||||
|
data?.markets,
|
||||||
|
params.product,
|
||||||
|
params.asset,
|
||||||
|
params.state,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useMarketsFilterData;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { SimpleMarkets_markets } from '../components/simple-market-list/__generated__/SimpleMarkets';
|
|
||||||
import { MarketState } from '@vegaprotocol/types';
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
export const DATE_FORMAT = 'dd MMMM yyyy HH:mm';
|
export const DATE_FORMAT = 'dd MMMM yyyy HH:mm';
|
||||||
export const EXPIRE_DATE_FORMAT = 'MMM dd';
|
export const EXPIRE_DATE_FORMAT = 'MMM dd';
|
||||||
@@ -9,7 +9,7 @@ export const TRADABLE_STATES = {
|
|||||||
[MarketState.STATE_ACTIVE]: true,
|
[MarketState.STATE_ACTIVE]: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IS_MARKET_TRADABLE = (market: SimpleMarkets_markets) =>
|
export const IS_MARKET_TRADABLE = (market: Market) =>
|
||||||
Boolean((market.state ?? '') in TRADABLE_STATES && market?.id);
|
Boolean((market.state ?? '') in TRADABLE_STATES && market?.id);
|
||||||
|
|
||||||
export const MARKET_STATES_MAP: Record<MarketState | '', string> = {
|
export const MARKET_STATES_MAP: Record<MarketState | '', string> = {
|
||||||
@@ -24,3 +24,5 @@ export const MARKET_STATES_MAP: Record<MarketState | '', string> = {
|
|||||||
[MarketState.STATE_TRADING_TERMINATED]: t('TradingTerminated'),
|
[MarketState.STATE_TRADING_TERMINATED]: t('TradingTerminated'),
|
||||||
'': t('Unknown'),
|
'': t('Unknown'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const NO_DATA_MESSAGE = t('No data to display');
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
export interface MarketMarkPrice_market_data {
|
export interface MarketMarkPrice_market_data {
|
||||||
__typename: "MarketData";
|
__typename: "MarketData";
|
||||||
/**
|
/**
|
||||||
* the mark price (an unsigned integer)
|
* The mark price (an unsigned integer)
|
||||||
*/
|
*/
|
||||||
markPrice: string;
|
markPrice: string;
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ export interface MarketMarkPrice_market_data {
|
|||||||
export interface MarketMarkPrice_market {
|
export interface MarketMarkPrice_market {
|
||||||
__typename: "Market";
|
__typename: "Market";
|
||||||
/**
|
/**
|
||||||
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
|
* 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)
|
* number denominated in the currency of the market. (uint64)
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
|
|||||||
@@ -60,19 +60,19 @@ export interface PartyMarketData_party_marginsConnection_edges_node_market {
|
|||||||
export interface PartyMarketData_party_marginsConnection_edges_node {
|
export interface PartyMarketData_party_marginsConnection_edges_node {
|
||||||
__typename: "MarginLevels";
|
__typename: "MarginLevels";
|
||||||
/**
|
/**
|
||||||
* market in which the margin is required for this party
|
* Market in which the margin is required for this party
|
||||||
*/
|
*/
|
||||||
market: PartyMarketData_party_marginsConnection_edges_node_market;
|
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)
|
* This is the minimum margin required for a party to place a new order on the network (unsigned integer)
|
||||||
*/
|
*/
|
||||||
initialLevel: string;
|
initialLevel: string;
|
||||||
/**
|
/**
|
||||||
* minimal margin for the position to be maintained in the network (unsigned integer)
|
* Minimal margin for the position to be maintained in the network (unsigned integer)
|
||||||
*/
|
*/
|
||||||
maintenanceLevel: string;
|
maintenanceLevel: string;
|
||||||
/**
|
/**
|
||||||
* if the margin is between maintenance and search, the network will initiate a collateral search (unsigned integer)
|
* If the margin is between maintenance and search, the network will initiate a collateral search (unsigned integer)
|
||||||
*/
|
*/
|
||||||
searchLevel: string;
|
searchLevel: string;
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ export interface PartyMarketData_party {
|
|||||||
/**
|
/**
|
||||||
* Margin levels for a market
|
* Margin levels for a market
|
||||||
*/
|
*/
|
||||||
marginsConnection: PartyMarketData_party_marginsConnection;
|
marginsConnection: PartyMarketData_party_marginsConnection | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PartyMarketData {
|
export interface PartyMarketData {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export interface EstimateOrder_estimateOrder_fee {
|
|||||||
export interface EstimateOrder_estimateOrder_marginLevels {
|
export interface EstimateOrder_estimateOrder_marginLevels {
|
||||||
__typename: "MarginLevels";
|
__typename: "MarginLevels";
|
||||||
/**
|
/**
|
||||||
* this is the minimum margin required for a party to place a new order on the network (unsigned integer)
|
* This is the minimum margin required for a party to place a new order on the network (unsigned integer)
|
||||||
*/
|
*/
|
||||||
initialLevel: string;
|
initialLevel: string;
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ export interface EstimateOrder_estimateOrder {
|
|||||||
|
|
||||||
export interface EstimateOrder {
|
export interface EstimateOrder {
|
||||||
/**
|
/**
|
||||||
* return an estimation of the potential cost for a new order
|
* Return an estimation of the potential cost for a new order
|
||||||
*/
|
*/
|
||||||
estimateOrder: EstimateOrder_estimateOrder;
|
estimateOrder: EstimateOrder_estimateOrder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ export interface MarketPositions_party_positionsConnection_edges_node {
|
|||||||
|
|
||||||
export interface MarketPositions_party_positionsConnection_edges {
|
export interface MarketPositions_party_positionsConnection_edges {
|
||||||
__typename: "PositionEdge";
|
__typename: "PositionEdge";
|
||||||
|
/**
|
||||||
|
* The position
|
||||||
|
*/
|
||||||
node: MarketPositions_party_positionsConnection_edges_node;
|
node: MarketPositions_party_positionsConnection_edges_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +94,7 @@ export interface MarketPositions_party {
|
|||||||
/**
|
/**
|
||||||
* Trading positions relating to a party
|
* Trading positions relating to a party
|
||||||
*/
|
*/
|
||||||
positionsConnection: MarketPositions_party_positionsConnection;
|
positionsConnection: MarketPositions_party_positionsConnection | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarketPositions {
|
export interface MarketPositions {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export type MarketPositionsQueryVariables = Types.Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
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 };
|
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`
|
export const MarketPositionsDocument = gql`
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export type PartyMarketDataQueryVariables = Types.Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
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 };
|
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`
|
export const PartyMarketDataDocument = gql`
|
||||||
|
|||||||
@@ -64,6 +64,15 @@ jest.mock('@vegaprotocol/market-depth', () => ({
|
|||||||
useOrderBookData: jest.fn(() => mockOrderBookData),
|
useOrderBookData: jest.fn(() => mockOrderBookData),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('@vegaprotocol/react-helpers', () => ({
|
||||||
|
...jest.requireActual('@vegaprotocol/react-helpers'),
|
||||||
|
useDataProvider: jest.fn(() => ({
|
||||||
|
data: {
|
||||||
|
marketsConnection: [],
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('useCalculateSlippage Hook', () => {
|
describe('useCalculateSlippage Hook', () => {
|
||||||
describe('calculate proper result', () => {
|
describe('calculate proper result', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { Side } from '@vegaprotocol/types';
|
import { Side } from '@vegaprotocol/types';
|
||||||
import { useOrderBookData } from '@vegaprotocol/market-depth';
|
import { useOrderBookData } from '@vegaprotocol/market-depth';
|
||||||
|
import { marketProvider } from '@vegaprotocol/market-list';
|
||||||
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
import type { Order } from '@vegaprotocol/orders';
|
import type { Order } from '@vegaprotocol/orders';
|
||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
import { formatNumber, toBigNum } from '@vegaprotocol/react-helpers';
|
import {
|
||||||
|
formatNumber,
|
||||||
|
toBigNum,
|
||||||
|
useDataProvider,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
marketId: string;
|
marketId: string;
|
||||||
@@ -16,11 +22,16 @@ const useCalculateSlippage = ({ marketId, order }: Props) => {
|
|||||||
variables,
|
variables,
|
||||||
throttleMilliseconds: 5000,
|
throttleMilliseconds: 5000,
|
||||||
});
|
});
|
||||||
|
const { data: market } = useDataProvider<Market, never>({
|
||||||
|
dataProvider: marketProvider,
|
||||||
|
noUpdate: true,
|
||||||
|
variables,
|
||||||
|
});
|
||||||
const volPriceArr =
|
const volPriceArr =
|
||||||
data?.depth[order.side === Side.SIDE_BUY ? 'sell' : 'buy'] || [];
|
data?.depth[order.side === Side.SIDE_BUY ? 'sell' : 'buy'] || [];
|
||||||
if (volPriceArr.length) {
|
if (volPriceArr.length && market) {
|
||||||
const decimals = data?.decimalPlaces ?? 0;
|
const decimals = market.decimalPlaces ?? 0;
|
||||||
const positionDecimals = data?.positionDecimalPlaces ?? 0;
|
const positionDecimals = market.positionDecimalPlaces ?? 0;
|
||||||
const bestPrice = toBigNum(volPriceArr[0].price, decimals);
|
const bestPrice = toBigNum(volPriceArr[0].price, decimals);
|
||||||
const { size } = order;
|
const { size } = order;
|
||||||
let descSize = new BigNumber(size);
|
let descSize = new BigNumber(size);
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import type { SimpleMarkets_markets } from '../components/simple-market-list/__generated__/SimpleMarkets';
|
|
||||||
import type { RouterParams } from '../components/simple-market-list/simple-market-list';
|
|
||||||
import { MarketState } from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
const useMarketsFilterData = (
|
|
||||||
data: SimpleMarkets_markets[] = [],
|
|
||||||
params: RouterParams
|
|
||||||
) => {
|
|
||||||
return data.filter((item) => {
|
|
||||||
if (
|
|
||||||
params.product &&
|
|
||||||
params.product !== item.tradableInstrument.instrument.product.__typename
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
params.asset &&
|
|
||||||
params.asset !== 'all' &&
|
|
||||||
params.asset !==
|
|
||||||
item.tradableInstrument.instrument.product.settlementAsset.symbol
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const state =
|
|
||||||
params.state === 'all'
|
|
||||||
? ''
|
|
||||||
: params.state
|
|
||||||
? params.state
|
|
||||||
: MarketState.STATE_ACTIVE;
|
|
||||||
if (state && state !== item.state) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useMarketsFilterData;
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import type { SimpleMarkets_markets } from '../components/simple-market-list/__generated__/SimpleMarkets';
|
|
||||||
|
|
||||||
const useMarketFilters = (data: SimpleMarkets_markets[]) => {
|
const useMarketFilters = (data: Market[]) => {
|
||||||
const [products, setProducts] = useState<string[]>([]);
|
const [products, setProducts] = useState<string[]>([]);
|
||||||
const [assetsPerProduct, setAssetsPerProduct] = useState<
|
const [assetsPerProduct, setAssetsPerProduct] = useState<
|
||||||
Record<string, string[]>
|
Record<string, string[]>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { renderHook } from '@testing-library/react';
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import useOrderCloseOut from './use-order-closeout';
|
import useOrderCloseOut from './use-order-closeout';
|
||||||
import type { Order } from '@vegaprotocol/orders';
|
import type { Order } from '@vegaprotocol/orders';
|
||||||
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
|
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||||
import type { PartyBalanceQuery } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
|
import type { PartyBalanceQuery } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/wallet', () => ({
|
jest.mock('@vegaprotocol/wallet', () => ({
|
||||||
@@ -49,7 +49,7 @@ describe('useOrderCloseOut Hook', () => {
|
|||||||
() =>
|
() =>
|
||||||
useOrderCloseOut({
|
useOrderCloseOut({
|
||||||
order: order as Order,
|
order: order as Order,
|
||||||
market: market as DealTicketQuery_market,
|
market: market as DealTicketMarketFragment,
|
||||||
partyData: partyData as PartyBalanceQuery,
|
partyData: partyData as PartyBalanceQuery,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -66,7 +66,7 @@ describe('useOrderCloseOut Hook', () => {
|
|||||||
() =>
|
() =>
|
||||||
useOrderCloseOut({
|
useOrderCloseOut({
|
||||||
order: { ...order, side: 'SIDE_SELL' } as Order,
|
order: { ...order, side: 'SIDE_SELL' } as Order,
|
||||||
market: market as DealTicketQuery_market,
|
market: market as DealTicketMarketFragment,
|
||||||
partyData: partyData as PartyBalanceQuery,
|
partyData: partyData as PartyBalanceQuery,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -83,7 +83,7 @@ describe('useOrderCloseOut Hook', () => {
|
|||||||
() =>
|
() =>
|
||||||
useOrderCloseOut({
|
useOrderCloseOut({
|
||||||
order: { ...order, side: 'SIDE_SELL' } as Order,
|
order: { ...order, side: 'SIDE_SELL' } as Order,
|
||||||
market: market as DealTicketQuery_market,
|
market: market as DealTicketMarketFragment,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
wrapper: ({ children }: { children: React.ReactNode }) => (
|
wrapper: ({ children }: { children: React.ReactNode }) => (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
import type { Order } from '@vegaprotocol/orders';
|
import type { Order } from '@vegaprotocol/orders';
|
||||||
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
|
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||||
import type { PartyBalanceQuery } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
|
import type { PartyBalanceQuery } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
|
||||||
import { useSettlementAccount } from './use-settlement-account';
|
import { useSettlementAccount } from './use-settlement-account';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
@@ -47,7 +47,7 @@ const CLOSEOUT_PRICE_QUERY = gql`
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
order: Order;
|
order: Order;
|
||||||
market: DealTicketQuery_market;
|
market: DealTicketMarketFragment;
|
||||||
partyData?: PartyBalanceQuery;
|
partyData?: PartyBalanceQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ const useOrderCloseOut = ({ order, market, partyData }: Props): string => {
|
|||||||
|
|
||||||
const marginMaintenanceLevel = new BigNumber(
|
const marginMaintenanceLevel = new BigNumber(
|
||||||
addDecimal(
|
addDecimal(
|
||||||
data?.party?.marginsConnection.edges?.find(
|
data?.party?.marginsConnection?.edges?.find(
|
||||||
(nodes) => nodes.node.market.id === market.id
|
(nodes) => nodes.node.market.id === market.id
|
||||||
)?.node.maintenanceLevel || 0,
|
)?.node.maintenanceLevel || 0,
|
||||||
market.decimalPlaces
|
market.decimalPlaces
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { renderHook } from '@testing-library/react';
|
|||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from '@apollo/client';
|
||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
import type { Order } from '@vegaprotocol/orders';
|
import type { Order } from '@vegaprotocol/orders';
|
||||||
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
|
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||||
import type { PositionMargin } from './use-market-positions';
|
import type { PositionMargin } from './use-market-positions';
|
||||||
import useOrderMargin from './use-order-margin';
|
import useOrderMargin from './use-order-margin';
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ describe('useOrderMargin Hook', () => {
|
|||||||
const { result } = renderHook(() =>
|
const { result } = renderHook(() =>
|
||||||
useOrderMargin({
|
useOrderMargin({
|
||||||
order: order as Order,
|
order: order as Order,
|
||||||
market: market as DealTicketQuery_market,
|
market: market as DealTicketMarketFragment,
|
||||||
partyId,
|
partyId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -72,7 +72,7 @@ describe('useOrderMargin Hook', () => {
|
|||||||
const { result } = renderHook(() =>
|
const { result } = renderHook(() =>
|
||||||
useOrderMargin({
|
useOrderMargin({
|
||||||
order: order as Order,
|
order: order as Order,
|
||||||
market: market as DealTicketQuery_market,
|
market: market as DealTicketMarketFragment,
|
||||||
partyId,
|
partyId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -84,7 +84,7 @@ describe('useOrderMargin Hook', () => {
|
|||||||
const { result } = renderHook(() =>
|
const { result } = renderHook(() =>
|
||||||
useOrderMargin({
|
useOrderMargin({
|
||||||
order: order as Order,
|
order: order as Order,
|
||||||
market: market as DealTicketQuery_market,
|
market: market as DealTicketMarketFragment,
|
||||||
partyId,
|
partyId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -111,7 +111,7 @@ describe('useOrderMargin Hook', () => {
|
|||||||
const { result } = renderHook(() =>
|
const { result } = renderHook(() =>
|
||||||
useOrderMargin({
|
useOrderMargin({
|
||||||
order: order as Order,
|
order: order as Order,
|
||||||
market: market as DealTicketQuery_market,
|
market: market as DealTicketMarketFragment,
|
||||||
partyId,
|
partyId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
EstimateOrderVariables,
|
EstimateOrderVariables,
|
||||||
EstimateOrder_estimateOrder_fee,
|
EstimateOrder_estimateOrder_fee,
|
||||||
} from './__generated__/estimateOrder';
|
} from './__generated__/estimateOrder';
|
||||||
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
|
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||||
import { Side } from '@vegaprotocol/types';
|
import { Side } from '@vegaprotocol/types';
|
||||||
import { addDecimal, removeDecimal } from '@vegaprotocol/react-helpers';
|
import { addDecimal, removeDecimal } from '@vegaprotocol/react-helpers';
|
||||||
import useMarketPositions from './use-market-positions';
|
import useMarketPositions from './use-market-positions';
|
||||||
@@ -47,7 +47,7 @@ export const ESTIMATE_ORDER_QUERY = gql`
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
order: Order;
|
order: Order;
|
||||||
market: DealTicketQuery_market;
|
market: DealTicketMarketFragment;
|
||||||
partyId: string;
|
partyId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user