Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e05e81be86 | ||
|
|
1d251251b3 |
+3
-3
@@ -1,5 +1,5 @@
|
||||
apps/**/dist/*
|
||||
apps/**/node_modules/*
|
||||
dist/*
|
||||
node_modules/*
|
||||
tmp/*
|
||||
.dockerignore
|
||||
dockerfiles
|
||||
Dockerfile
|
||||
|
||||
+3
-15
@@ -2,21 +2,9 @@
|
||||
"root": true,
|
||||
"ignorePatterns": ["**/*"],
|
||||
"plugins": ["@nrwl/nx", "eslint-plugin-unicorn", "jsx-a11y", "jest"],
|
||||
"settings": {
|
||||
"jsx-a11y": {
|
||||
"components": {
|
||||
"Button": "button",
|
||||
"Input": "input",
|
||||
"Select": "select",
|
||||
"Radio": "radio",
|
||||
"TextArea": "textarea"
|
||||
}
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"extends": ["plugin:jsx-a11y/strict"],
|
||||
"rules": {
|
||||
"@nrwl/nx/enforce-module-boundaries": [
|
||||
"error",
|
||||
@@ -51,7 +39,8 @@
|
||||
"ul": ["list"],
|
||||
"ol": ["list"]
|
||||
}
|
||||
]
|
||||
],
|
||||
"curly": "warn"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -74,8 +63,7 @@
|
||||
"prefer": "type-imports",
|
||||
"disallowTypeAnnotations": true
|
||||
}
|
||||
],
|
||||
"curly": ["error", "multi-line"]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,205 +0,0 @@
|
||||
name: Capsule tests -- manual trigger
|
||||
|
||||
# This workflow runs the frontend tests against chosen branch
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
project:
|
||||
description: 'Project'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- explorer-e2e
|
||||
- console-lite-e2e
|
||||
- stats-e2e
|
||||
- token-e2e
|
||||
- trading-e2e
|
||||
runAlltests:
|
||||
description: 'Run only smoke tests?'
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
manual:
|
||||
name: Run capsule tests -- manual trigger
|
||||
runs-on: self-hosted
|
||||
env:
|
||||
GO111MODULE: 'on'
|
||||
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:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
path: './frontend-monorepo'
|
||||
|
||||
# See if we capsule is needed for this project
|
||||
- name: See if capsule is necessary
|
||||
if: ${{ github.event.inputs.project == 'explorer-e2e' || github.event.inputs.project == 'token-e2e' }}
|
||||
run: echo RUN_CAPSULE=true >> $GITHUB_ENV
|
||||
|
||||
# Checkout capsule to build local network
|
||||
- name: Checkout capsule
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: vegaprotocol/vegacapsule
|
||||
ref: main
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
path: './capsule'
|
||||
|
||||
# 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
|
||||
|
||||
#######
|
||||
## Build binaries
|
||||
#######
|
||||
- name: Build capsule
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: go install
|
||||
working-directory: capsule
|
||||
|
||||
- name: Set GOBIN
|
||||
run: echo GOBIN=$(go env GOPATH)/bin >> $GITHUB_ENV
|
||||
|
||||
- name: Install Vega binaries
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: |
|
||||
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/vega-linux-amd64.zip'
|
||||
unzip vega-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||
|
||||
- name: Install date-node binaries
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: |
|
||||
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/data-node-linux-amd64.zip'
|
||||
unzip data-node-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||
|
||||
- name: Install Vega wallet binaries
|
||||
run: |
|
||||
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/vegawallet-linux-amd64.zip'
|
||||
unzip vegawallet-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||
|
||||
######
|
||||
## Start capsule
|
||||
######
|
||||
- name: Login to docker
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: echo -n ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
|
||||
|
||||
- name: Start nomad
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: vegacapsule nomad &
|
||||
|
||||
- name: Bootstrap network
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
|
||||
working-directory: capsule
|
||||
|
||||
######
|
||||
## Setup a Vega wallet for our user
|
||||
######
|
||||
- name: Create passphrase
|
||||
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
|
||||
- name: Create recovery
|
||||
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
|
||||
|
||||
- name: Initialize wallet
|
||||
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
|
||||
- name: Import wallet
|
||||
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
||||
- name: Create public key 2
|
||||
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
||||
|
||||
- name: Import fairground network
|
||||
if: ${{ env.RUN_CAPSULE==false }}
|
||||
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
|
||||
if: ${{ env.RUN_CAPSULE==false }}
|
||||
run: vegawallet service run --network fairground --automatic-consent --home ~/.vegacapsule/testnet/wallet &
|
||||
|
||||
- name: Start service using capsule network
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: vegawallet service run --network DV --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 smoke Cypress tests
|
||||
if: ${{ github.event.inputs.runAlltests == 'true' }}
|
||||
run: yarn nx run ${{ github.event.inputs.project }}:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --env.grepTags='@smoke' --browser chrome
|
||||
working-directory: frontend-monorepo
|
||||
env:
|
||||
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
|
||||
CYPRESS_TEARDOWN_NETWORK_AFTER_FLOWS: true
|
||||
|
||||
- name: Run Cypress tests
|
||||
if: ${{ github.event.inputs.runAlltests == 'false' }}
|
||||
run: yarn nx run ${{ github.event.inputs.project }}:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
|
||||
working-directory: frontend-monorepo
|
||||
env:
|
||||
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
|
||||
CYPRESS_TEARDOWN_NETWORK_AFTER_FLOWS: true
|
||||
|
||||
######
|
||||
## Upload logs
|
||||
######
|
||||
|
||||
- name: Logs
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: vegacapsule network logs > vega-capsule-logs.txt
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
with:
|
||||
name: logs
|
||||
path: ./vega-capsule-logs.txt
|
||||
+62
-53
@@ -1,4 +1,4 @@
|
||||
name: Capsule tests -- night run
|
||||
name: Capsule tests -- nightly
|
||||
|
||||
# This workflow runs the frontend tests against latest develop of the core to preempt breaking changes
|
||||
|
||||
@@ -9,10 +9,10 @@ on:
|
||||
jobs:
|
||||
nightly:
|
||||
name: Run capsule tests -- nightly
|
||||
runs-on: self-hosted
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GO111MODULE: 'on'
|
||||
VEGA_VERSION: 'v0.57.0'
|
||||
GOPROXY: ${{ secrets.GO_PROXY }}
|
||||
steps:
|
||||
#######
|
||||
## Setup langs
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
uses: actions/setup-go@v3
|
||||
id: go
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.18
|
||||
- name: Set up Node 16
|
||||
uses: actions/setup-node@v2
|
||||
id: npm
|
||||
@@ -29,22 +29,48 @@ jobs:
|
||||
node-version: 16
|
||||
|
||||
#######
|
||||
## Install Yarn
|
||||
#######
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
#######
|
||||
## Checkout repos
|
||||
## Checkout all the things
|
||||
#######
|
||||
|
||||
# Checkout front ends
|
||||
- name: Checkout frontend mono repo
|
||||
# Checkout repos required to build vega capsule
|
||||
- name: Checkout vega
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
path: './frontend-monorepo'
|
||||
repository: vegaprotocol/vega
|
||||
ref: master
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
path: './vega'
|
||||
|
||||
- name: Checkout data node
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: vegaprotocol/data-node
|
||||
ref: main
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
path: './data-node'
|
||||
|
||||
- name: Checkout protos
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: vegaprotocol/protos
|
||||
ref: master
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
path: './protos'
|
||||
|
||||
- name: Checkout vegatools
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: vegaprotocol/vegatools
|
||||
ref: main
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
path: './vegatools'
|
||||
|
||||
- name: Checkout vega wallet
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: vegaprotocol/vegawallet
|
||||
ref: master
|
||||
path: './vegawallet'
|
||||
|
||||
# Checkout capsule to build local network
|
||||
- name: Checkout capsule
|
||||
@@ -55,20 +81,6 @@ jobs:
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
path: './capsule'
|
||||
|
||||
# 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
|
||||
|
||||
#######
|
||||
## Build binaries
|
||||
#######
|
||||
@@ -76,35 +88,28 @@ jobs:
|
||||
run: go install
|
||||
working-directory: capsule
|
||||
|
||||
- name: Set GOBIN
|
||||
run: echo GOBIN=$(go env GOPATH)/bin >> $GITHUB_ENV
|
||||
- name: Build vega
|
||||
run: go install ./cmd/vega/
|
||||
working-directory: vega
|
||||
|
||||
- name: Install Vega binaries
|
||||
run: |
|
||||
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/vega-linux-amd64.zip'
|
||||
unzip vega-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||
- name: Build vegawallet
|
||||
run: go install
|
||||
working-directory: vegawallet
|
||||
|
||||
- name: Install date-node binaries
|
||||
run: |
|
||||
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/data-node-linux-amd64.zip'
|
||||
unzip data-node-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||
|
||||
- name: Install Vega wallet binaries
|
||||
run: |
|
||||
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/vegawallet-linux-amd64.zip'
|
||||
unzip vegawallet-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||
- name: Build data-node
|
||||
run: go install ./cmd/data-node/
|
||||
working-directory: data-node
|
||||
|
||||
######
|
||||
## Start capsule
|
||||
######
|
||||
- name: Login to docker
|
||||
run: echo -n ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
|
||||
|
||||
- name: Start nomad
|
||||
run: vegacapsule nomad &
|
||||
|
||||
- name: Bootstrap network
|
||||
run: vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
|
||||
run: vegacapsule network bootstrap --config-path=./net_confs/config.hcl
|
||||
working-directory: capsule
|
||||
|
||||
######
|
||||
@@ -128,26 +133,30 @@ jobs:
|
||||
## 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
|
||||
# Checkout front ends
|
||||
- name: Checkout frontend mono repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install root dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Run Cypress tests
|
||||
run: yarn nx run-many --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
|
||||
working-directory: frontend-monorepo
|
||||
env:
|
||||
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
|
||||
CYPRESS_NIGHTLY_RUN: true
|
||||
CYPRESS_TEARDOWN_NETWORK_AFTER_FLOWS: true
|
||||
|
||||
######
|
||||
## Upload logs
|
||||
######
|
||||
|
||||
- name: Logs
|
||||
if: ${{ always() }}
|
||||
run: vegacapsule network logs > vega-capsule-logs.txt
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
@@ -3,22 +3,18 @@ name: Capsule tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- main
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
pr:
|
||||
name: Run capsule tests - PR
|
||||
runs-on: self-hosted
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
GO111MODULE: 'on'
|
||||
VEGA_VERSION: 'v0.57.0'
|
||||
GOPROXY: ${{ secrets.GO_PROXY }}
|
||||
steps:
|
||||
#######
|
||||
## Setup langs
|
||||
@@ -27,7 +23,7 @@ jobs:
|
||||
uses: actions/setup-go@v3
|
||||
id: go
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.18
|
||||
- name: Set up Node 16
|
||||
uses: actions/setup-node@v2
|
||||
id: npm
|
||||
@@ -35,49 +31,11 @@ jobs:
|
||||
node-version: 16
|
||||
|
||||
#######
|
||||
## Install Yarn
|
||||
## Checkout capsule
|
||||
#######
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
#######
|
||||
## Checkout repos
|
||||
#######
|
||||
|
||||
# Checkout front ends
|
||||
- name: Checkout frontend mono repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
path: './frontend-monorepo'
|
||||
|
||||
# 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
|
||||
|
||||
# See affected apps to see if building all binaries is necessary
|
||||
- name: See affected apps
|
||||
run: echo AFFECTED=$(yarn nx print-affected --base=origin/${{github.base_ref}} --head=${{github.head_ref}} --select=projects) >> $GITHUB_ENV
|
||||
working-directory: frontend-monorepo
|
||||
|
||||
- name: See if capsule is necessary
|
||||
if: ${{ contains(env.AFFECTED, 'token') || contains(env.AFFECTED, 'token-e2e') || contains(env.AFFECTED, 'explorer') || contains(env.AFFECTED, 'explorer-e2e') }}
|
||||
run: echo RUN_CAPSULE=true >> $GITHUB_ENV
|
||||
|
||||
# Checkout capsule to build local network
|
||||
- name: Checkout capsule
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: vegaprotocol/vegacapsule
|
||||
@@ -89,44 +47,29 @@ jobs:
|
||||
## Build binaries
|
||||
#######
|
||||
- name: Build capsule
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: go install
|
||||
working-directory: capsule
|
||||
|
||||
- name: Set GOBIN
|
||||
run: echo GOBIN=$(go env GOPATH)/bin >> $GITHUB_ENV
|
||||
|
||||
- name: Install Vega binaries
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: |
|
||||
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/vega-linux-amd64.zip'
|
||||
unzip vega-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||
|
||||
- name: Install date-node binaries
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: |
|
||||
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/data-node-linux-amd64.zip'
|
||||
unzip data-node-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||
|
||||
- name: Install Vega wallet binaries
|
||||
run: |
|
||||
wget 'https://github.com/vegaprotocol/vega/releases/download/${{ env.VEGA_VERSION }}/vegawallet-linux-amd64.zip'
|
||||
unzip vegawallet-linux-amd64.zip -d ${{ env.GOBIN }}
|
||||
- name: Install binaries
|
||||
run: vegacapsule install-bins
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
GOBIN: ${{ env.GOBIN }}
|
||||
|
||||
######
|
||||
## Start capsule
|
||||
######
|
||||
- name: Login to docker
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: echo -n ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
|
||||
|
||||
- name: Start nomad
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: vegacapsule nomad &
|
||||
|
||||
- name: Bootstrap network
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
run: vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
|
||||
run: vegacapsule network bootstrap --config-path=./net_confs/config.hcl --force
|
||||
working-directory: capsule
|
||||
|
||||
######
|
||||
@@ -143,53 +86,45 @@ jobs:
|
||||
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
||||
- name: Create public key 2
|
||||
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
|
||||
|
||||
- name: Import fairground network
|
||||
if: ${{ env.RUN_CAPSULE==false }}
|
||||
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
|
||||
if: ${{ env.RUN_CAPSULE==false }}
|
||||
run: vegawallet service run --network fairground --automatic-consent --home ~/.vegacapsule/testnet/wallet &
|
||||
|
||||
- name: Start service using capsule network
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
- name: Start service
|
||||
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
|
||||
|
||||
######
|
||||
## Run some tests
|
||||
######
|
||||
|
||||
# Checkout front ends
|
||||
- name: Checkout frontend mono repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||
uses: nrwl/nx-set-shas@v2
|
||||
with:
|
||||
working-directory: frontend-monorepo
|
||||
main-branch-name: ${{github.base_ref}}
|
||||
main-branch-name: master
|
||||
|
||||
# To make sure that all Cypress binaries are installed properly
|
||||
- name: Install cypress bins
|
||||
run: yarn cypress install
|
||||
working-directory: frontend-monorepo
|
||||
- name: Install root dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Run Cypress tests
|
||||
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --env.grepTags='@smoke' --browser chrome
|
||||
working-directory: frontend-monorepo
|
||||
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
|
||||
env:
|
||||
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
|
||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
|
||||
CYPRESS_TEARDOWN_NETWORK_AFTER_FLOWS: true
|
||||
|
||||
######
|
||||
## Upload logs
|
||||
######
|
||||
|
||||
- name: Logs
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
if: ${{ always() }}
|
||||
run: vegacapsule network logs > vega-capsule-logs.txt
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ env.RUN_CAPSULE }}
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: logs
|
||||
path: ./vega-capsule-logs.txt
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
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
|
||||
@@ -1,126 +0,0 @@
|
||||
name: Publish docker containers
|
||||
|
||||
'on':
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+'
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
publish:
|
||||
description: 'Publish tag to Docker Hub & GitHub Registry'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
tag:
|
||||
description: 'Git Tag to build and publish'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
apps:
|
||||
description: 'Applications to build and publish'
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- '["explorer", "token", "trading"]'
|
||||
- '["explorer"]'
|
||||
- '["token"]'
|
||||
- '["trading"]'
|
||||
archs:
|
||||
description: 'Architecture to build and publish'
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- linux/amd64, linux/arm64
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
|
||||
jobs:
|
||||
master:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
app: ${{ fromJson(inputs.apps || '["explorer", "token", "trading"]') }}
|
||||
name: Build the ${{ matrix.app }} image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.tag }}
|
||||
|
||||
- name: Set up QEMU
|
||||
id: quemu
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Available platforms
|
||||
run: echo ${{ steps.qemu.outputs.platforms }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Determine Docker Image tag
|
||||
id: tags
|
||||
run: |
|
||||
hash=$(git rev-parse HEAD|cut -b1-8)
|
||||
versionTag=${{ inputs.tag || startsWith(github.ref, 'refs/tags/') && github.ref_name || '${hash}' }}
|
||||
echo ::set-output name=version::${versionTag}
|
||||
echo ::set-output name=npmVersion::$(cat dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }} | grep FROM | head -n 1 | awk '{print $2}' | cut -d ':' -f 2 | cut -d '-' -f 1 )
|
||||
|
||||
- name: Print config
|
||||
run: |
|
||||
git rev-parse --verify HEAD
|
||||
git status
|
||||
echo "inputs.tag=${{ inputs.tag }}"
|
||||
echo "inputs.publish=${{ inputs.publish }}"
|
||||
echo "inputs.apps=${{ inputs.apps }}"
|
||||
echo "inputs.archs=${{ inputs.archs }}"
|
||||
echo "steps.tags.outputs.version=${{ steps.tags.outputs.version }}"
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ steps.tags.outputs.npmVersion }}
|
||||
|
||||
- name: Build frontend dists
|
||||
run: |
|
||||
yarn --verbose --pure-lockfile
|
||||
yarn nx ${{ matrix.app =='trading' && 'export' || 'build' }} ${{ matrix.app }} --pure-lockfile
|
||||
|
||||
- name: Build and export to local Docker
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
|
||||
build-args: APP=${{ matrix.app }}
|
||||
load: true
|
||||
tags: vegaprotocol/${{ matrix.app }}:local
|
||||
|
||||
- name: Sanity check docker image
|
||||
run: |
|
||||
docker run --rm vegaprotocol/${{ matrix.app }}:local cat .env
|
||||
docker run --rm vegaprotocol/${{ matrix.app }}:local ls -lah
|
||||
|
||||
- name: Build and push to DockerHub
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
|
||||
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
|
||||
build-args: APP=${{ matrix.app }}
|
||||
platforms: ${{ inputs.archs || 'linux/amd64, linux/arm64' }}
|
||||
tags: |
|
||||
vegaprotocol/${{ matrix.app }}:latest
|
||||
vegaprotocol/${{ matrix.app }}:${{ steps.tags.outputs.version }}
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
+38
-15
@@ -3,37 +3,60 @@ name: Unit tests & build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
pr:
|
||||
name: Test and lint - PR
|
||||
master:
|
||||
name: Test and lint - main
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: 'read'
|
||||
actions: 'read'
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||
uses: nrwl/nx-set-shas@v2
|
||||
with:
|
||||
main-branch-name: ${{ github.base_ref }}
|
||||
main-branch-name: master
|
||||
- name: Use Node.js 16
|
||||
id: Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.14.0
|
||||
- name: Restore node_modules from cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: node_modules-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Install root dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
run: yarn install
|
||||
- name: Check formatting
|
||||
run: yarn nx format:check
|
||||
- name: Lint affected
|
||||
run: yarn nx affected:lint --max-warnings=0
|
||||
- name: Test affected
|
||||
run: yarn nx affected:test
|
||||
- name: Build affected
|
||||
run: yarn nx affected:build
|
||||
pr:
|
||||
name: Test and lint - PR
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||
uses: nrwl/nx-set-shas@v2
|
||||
with:
|
||||
main-branch-name: master
|
||||
- name: Use Node.js 16
|
||||
id: Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.14.0
|
||||
- name: Install root dependencies
|
||||
run: yarn install
|
||||
- name: Check formatting
|
||||
run: yarn nx format:check
|
||||
- name: Lint affected
|
||||
|
||||
+1
-5
@@ -5,7 +5,6 @@
|
||||
/tmp
|
||||
/out-tsc
|
||||
/tools/executors/**/*.js
|
||||
/tools/utils/*.js
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
@@ -21,7 +20,7 @@
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
.vscode/settings.json
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
@@ -43,6 +42,3 @@ Thumbs.db
|
||||
.local.env
|
||||
.env.local
|
||||
cypress.env.json
|
||||
|
||||
# Next.js
|
||||
.next
|
||||
|
||||
@@ -6,3 +6,7 @@ yarn commitlint --edit "${1}"
|
||||
|
||||
# Lint all staged files
|
||||
yarn lint-staged
|
||||
|
||||
# Build & test affected apps
|
||||
yarn nx affected:build --uncommitted
|
||||
yarn nx affected:test --uncommitted
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@
|
||||
/dist
|
||||
/coverage
|
||||
__generated__
|
||||
__generated___
|
||||
|
||||
apps/static/src/assets/devnet-tranches.json
|
||||
apps/static/src/assets/mainnet-tranches.json
|
||||
apps/static/src/assets/stagnet3-tranches.json
|
||||
apps/static/src/assets/stagnet1-tranches.json
|
||||
apps/static/src/assets/stagnet2-tranches.json
|
||||
apps/static/src/assets/testnet-tranches.json
|
||||
|
||||
Vendored
+1
-2
@@ -4,7 +4,6 @@
|
||||
"esbenp.prettier-vscode",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"stevejpurves.cucumber",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
"stevejpurves.cucumber"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,9 +7,8 @@ ENV PATH /app/node_modules/.bin:$PATH
|
||||
COPY package.json ./
|
||||
COPY yarn.lock ./
|
||||
COPY . ./
|
||||
RUN apk add python3 make gcc g++
|
||||
RUN yarn --network-timeout 100000 --verbose --pure-lockfile
|
||||
RUN yarn nx build $APP --network-timeout 100000 --verbose --pure-lockfile
|
||||
RUN yarn
|
||||
RUN yarn nx build $APP
|
||||
|
||||
# Production environment
|
||||
FROM nginx:stable-alpine
|
||||
@@ -16,7 +16,7 @@ The trading interface built based on a component toolkit. It will provide a way
|
||||
|
||||
### [Token](./apps/token)
|
||||
|
||||
The utility dApp for interacting with the Vega token and using its' utility. This includes; delegation, nomination, governance and redemption of tokens.
|
||||
The utlity dApp for interacting with the Vega token and using its' utility. This includes; delegation, nomination, governance and redemption of tokens.
|
||||
|
||||
### [Explorer](./apps/explorer)
|
||||
|
||||
@@ -51,7 +51,7 @@ A utility library for connecting to the Ethereum network and interacting with Ve
|
||||
|
||||
### [React Helpers](./libs/react-helpers)
|
||||
|
||||
Generic react helpers that can be used across multiple applications, along with other utilities.
|
||||
Generic react helpers that can be used across multilpe applications, along with other utilities.
|
||||
|
||||
# 💻 Develop
|
||||
|
||||
@@ -69,10 +69,11 @@ Run `nx serve my-app` for a dev server. Navigate to the port specified in `app/<
|
||||
|
||||
### Using Apollo GraphQL and Generate Types
|
||||
|
||||
In order to generate the schemas for your GraphQL queries, you can run `GRAPHQL_SCHEMA_PATH=[YOUR SCHEMA FILE / API URL HERE] nx run types:generate`.
|
||||
In order to generate the schemas for your GraphQL queries, you can run `nx run types:generate`.
|
||||
If it is the first time you are running the command, make sure you are setting up the environment variable from `apollo.config.js`.
|
||||
|
||||
```bash
|
||||
export GRAPHQL_SCHEMA_PATH=https://api.n11.testnet.vega.xyz/graphql
|
||||
export NX_VEGA_URL=https://lb.testnet.vega.xyz/query
|
||||
yarn nx run types:generate
|
||||
```
|
||||
|
||||
@@ -106,12 +107,12 @@ Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more
|
||||
|
||||
## Docker
|
||||
|
||||
The [Dockerfile](./dockerfiles) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this.
|
||||
The [Dockerfile](./Dockerfile) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this.
|
||||
|
||||
You can build any of the containers locally with the following command:
|
||||
|
||||
```bash
|
||||
docker build --dockerfile dockerfiles/Dockerfile.cra . --build-arg APP=[YOUR APP] --tag=[TAG]
|
||||
docker build . --build-arg APP=[YOUR APP] --tag=[TAG]
|
||||
```
|
||||
|
||||
In order to run a container:
|
||||
@@ -120,8 +121,6 @@ In order to run a container:
|
||||
docker run -p 3000:80 [TAG]
|
||||
```
|
||||
|
||||
Images ending with `.dist` are to pack locally created transpiled HTML files into nginx container for non-compatible with yarn architectures like M1 Mac
|
||||
|
||||
## Config
|
||||
|
||||
As environment variables are build time and not run time in frontend applications. We have built a system which allows for passing run time environment variables, this generates a JSON file that will override the default environment variables that the container was built with (which is always testnet, using the default .env files).
|
||||
@@ -129,7 +128,7 @@ As environment variables are build time and not run time in frontend application
|
||||
In order to override specific environment variables you can pass these to the container like this:
|
||||
|
||||
```bash
|
||||
docker run -e NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql -p 3000:80 [TAG]
|
||||
docker run -e NX_VEGA_URL=https://n04.d.vega.xyz/query -p 3000:80 [TAG]
|
||||
```
|
||||
|
||||
Which will now point the app to use a devnet data node. To see a list of all possible config properties see the readme.md for each app in the app directory.
|
||||
@@ -140,17 +139,6 @@ Coming soon! You will be able to run the containers within Vega Capsule.
|
||||
|
||||
You can run against a local instance of Vega Capsule today by using the .env.capsule present in the apps.
|
||||
|
||||
If you wish to run E2E tests for Token and Block Explorer (other areas to be added soon)
|
||||
|
||||
- Vegacapsule must be used in order for these tests to succeed, the vegacapsule repo README.md file contains the steps required to set this up, it must be installed globally.
|
||||
- However we start the capsule network a little differently to how it is laid out in those instructions:
|
||||
|
||||
In order to run the bootstrap command to generate and start a new network, we must do so using the following:
|
||||
|
||||
```bash
|
||||
vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl
|
||||
```
|
||||
|
||||
# 📑 License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
module.exports = defineConfig({
|
||||
projectId: 'et4snf',
|
||||
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
require('cypress-grep/src/plugin')(config);
|
||||
return config;
|
||||
},
|
||||
baseUrl: 'http://localhost:4200',
|
||||
fileServerFolder: '.',
|
||||
fixturesFolder: false,
|
||||
specPattern: './src/integration/*.ts',
|
||||
excludeSpecPattern: '**/*.js',
|
||||
modifyObstructiveCode: false,
|
||||
supportFile: './src/support/index.js',
|
||||
video: false,
|
||||
videoUploadOnPasses: false,
|
||||
videosFolder: '../../dist/cypress/apps/console-lite-e2e/videos',
|
||||
screenshotsFolder: '../../dist/cypress/apps/console-lite-e2e/screenshots',
|
||||
chromeWebSecurity: false,
|
||||
viewportWidth: 1440,
|
||||
viewportHeight: 900,
|
||||
},
|
||||
env: {
|
||||
TRADING_TEST_VEGA_WALLET_NAME: 'UI_Trading_Test',
|
||||
ETHEREUM_PROVIDER_URL:
|
||||
'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
|
||||
VEGA_PUBLIC_KEY:
|
||||
'47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278',
|
||||
VEGA_PUBLIC_KEY2:
|
||||
'1a18cdcaaa4f44a57b35a4e9b77e0701c17a476f2b407620f8c17371740cf2e4',
|
||||
TRUNCATED_VEGA_PUBLIC_KEY: '47836c…c7d278',
|
||||
TRUNCATED_VEGA_PUBLIC_KEY2: '1a18cd…0cf2e4',
|
||||
ETHEREUM_WALLET_ADDRESS: '0x265Cc6d39a1B53d0d92068443009eE7410807158',
|
||||
ETHERSCAN_URL: 'https://ropsten.etherscan.io',
|
||||
tsConfig: 'tsconfig.json',
|
||||
TAGS: 'not @todo and not @ignore and not @manual',
|
||||
TRADING_TEST_VEGA_WALLET_PASSPHRASE: '123',
|
||||
ETH_WALLET_MNEMONIC:
|
||||
'ugly gallery notice network true range brave clarify flat logic someone chunk',
|
||||
grepTags: '@regression @smoke @slow',
|
||||
grepFilterSpecs: true,
|
||||
grepOmitFiltered: true,
|
||||
},
|
||||
});
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "apps/console-lite-e2e/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"executor": "@nrwl/cypress:cypress",
|
||||
"options": {
|
||||
"cypressConfig": "apps/console-lite-e2e/cypress.config.js",
|
||||
"devServerTarget": "console-lite:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "console-lite:serve:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/console-lite-e2e/**/*.{js,ts}"]
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"executor": "@nrwl/workspace:run-commands",
|
||||
"outputs": [],
|
||||
"options": {
|
||||
"command": "yarn tsc --project ./apps/console-lite-e2e/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [],
|
||||
"implicitDependencies": ["console-lite"]
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
describe('console lite header', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
window.localStorage.setItem('theme', 'dark');
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('logo should linked home', () => {
|
||||
cy.get('span').contains('Markets').click();
|
||||
cy.location('pathname').should('eq', '/markets');
|
||||
cy.getByTestId('header').find('a').click();
|
||||
cy.location('pathname').should('eq', '/');
|
||||
});
|
||||
|
||||
it('theme switcher should switch theme', () => {
|
||||
cy.get('#root').children().eq(0).as('Container');
|
||||
cy.get('@Container').should('have.css', 'background-color', 'rgb(8, 8, 8)');
|
||||
cy.getByTestId('theme-switcher').click();
|
||||
cy.get('@Container').should(
|
||||
'have.css',
|
||||
'background-color',
|
||||
'rgb(255, 255, 255)'
|
||||
);
|
||||
});
|
||||
|
||||
it('wallet connector should open a dialog', () => {
|
||||
cy.get('[role="dialog"]').should('not.exist');
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.get('[role="dialog"]').should('be.visible');
|
||||
});
|
||||
});
|
||||
@@ -1,141 +0,0 @@
|
||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||
import type { Markets } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
generateLongListMarkets,
|
||||
generateSimpleMarkets,
|
||||
generateMarketsData,
|
||||
generateMarketsCandles,
|
||||
} from '../support/mocks/generate-markets';
|
||||
|
||||
describe('market list', { tags: '@smoke' }, () => {
|
||||
describe('simple url', () => {
|
||||
beforeEach(() => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(req, 'Markets', generateSimpleMarkets());
|
||||
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||
});
|
||||
cy.visit('/markets');
|
||||
});
|
||||
|
||||
it('selects menus', () => {
|
||||
cy.get('[aria-label="Sidebar Navigation Menu"] [aria-current]').should(
|
||||
'have.text',
|
||||
'Markets'
|
||||
);
|
||||
cy.getByTestId('state-trigger').should('have.text', 'Active');
|
||||
cy.get('[aria-label="Future"]').click();
|
||||
cy.get('[data-testid="market-assets-menu"] a.active').should(
|
||||
'have.text',
|
||||
'All'
|
||||
);
|
||||
});
|
||||
|
||||
it('navigation should make possibly shortest url', () => {
|
||||
cy.location('pathname').should('equal', '/markets');
|
||||
|
||||
cy.getByTestId('state-trigger').click();
|
||||
cy.get('[role=menuitemcheckbox]').contains('All').click();
|
||||
cy.location('pathname').should('equal', '/markets/all');
|
||||
|
||||
cy.get('[aria-label="Future"]').click();
|
||||
cy.location('pathname').should('eq', '/markets/all/Future');
|
||||
let asset = '';
|
||||
cy.getByTestId('market-assets-menu')
|
||||
.children()
|
||||
.then((children) => {
|
||||
if (children.length > 1) {
|
||||
asset = children[1].innerText;
|
||||
if (asset) {
|
||||
cy.wrap(children[1]).click();
|
||||
cy.location('pathname').should(
|
||||
'match',
|
||||
new RegExp(`/markets/all/Future/${asset}`, 'i')
|
||||
);
|
||||
cy.get('a').contains('All Markets').click();
|
||||
cy.location('pathname').should('eq', '/markets/all');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cy.getByTestId('state-trigger').click();
|
||||
cy.get('[role=menuitemcheckbox]').contains('Active').click();
|
||||
cy.location('pathname').should('equal', '/markets');
|
||||
});
|
||||
});
|
||||
|
||||
describe('url params should select filters', () => {
|
||||
beforeEach(() => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(req, 'Markets', generateSimpleMarkets());
|
||||
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||
});
|
||||
});
|
||||
|
||||
it('suspended status', () => {
|
||||
cy.visit('/markets/Suspended');
|
||||
cy.getByTestId('state-trigger').should('have.text', 'Suspended');
|
||||
});
|
||||
|
||||
it('last asset (if exists)', () => {
|
||||
cy.visit('/markets');
|
||||
cy.wait('@Markets').then((filters) => {
|
||||
const data: Markets | undefined = filters?.response?.body?.data;
|
||||
if (data.marketsConnection.edges.length) {
|
||||
const asset =
|
||||
data.marketsConnection.edges[0].node.tradableInstrument.instrument
|
||||
.product.settlementAsset.symbol;
|
||||
cy.visit(`/markets/Suspended/Future/${asset}`);
|
||||
cy.getByTestId('market-assets-menu')
|
||||
.find('a.active')
|
||||
.should('have.text', asset);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Future product', () => {
|
||||
cy.visit('/markets/Suspended/Future');
|
||||
cy.getByTestId('market-products-menu')
|
||||
.find('a.active')
|
||||
.should('have.text', 'Future');
|
||||
});
|
||||
});
|
||||
|
||||
describe('long list of results should be handled properly', () => {
|
||||
it('handles 1000 markets', () => {
|
||||
cy.viewport(1440, 900);
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(req, 'Markets', generateLongListMarkets(1000));
|
||||
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||
});
|
||||
performance.mark('start-1k');
|
||||
cy.visit('/markets');
|
||||
cy.get('.ag-center-cols-container', { timeout: 50000 }).then(() => {
|
||||
performance.mark('end-1k');
|
||||
performance.measure('load-1k', 'start-1k', 'end-1k');
|
||||
const measure = performance.getEntriesByName('load-1k')[0];
|
||||
expect(measure.duration).lte(20000);
|
||||
cy.log(`Ag-grid 1k load took ${measure.duration} milliseconds.`);
|
||||
|
||||
cy.get('.ag-root').should('have.attr', 'aria-rowcount', '1001');
|
||||
cy.get('.ag-center-cols-container')
|
||||
.find('[role="row"]')
|
||||
.its('length')
|
||||
.then((length) => expect(length).to.be.closeTo(20, 3));
|
||||
cy.get('.ag-cell-label-container').eq(4).click();
|
||||
cy.get('body').then(($body) => {
|
||||
for (let i = 0; i < 15; i++) {
|
||||
cy.wrap($body).realPress('Tab', { pressDelay: 100 });
|
||||
}
|
||||
});
|
||||
cy.focused().parent('.ag-row').should('have.attr', 'row-index', '14');
|
||||
cy.get('.ag-center-cols-container')
|
||||
.find('[role="row"]')
|
||||
.its('length')
|
||||
.then((length) => expect(length).to.be.closeTo(26, 2));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,148 +0,0 @@
|
||||
import { connectVegaWallet } from '../support/connect-wallet';
|
||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||
import {
|
||||
generateMarketsCandles,
|
||||
generateMarketsData,
|
||||
generateSimpleMarkets,
|
||||
} from '../support/mocks/generate-markets';
|
||||
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
|
||||
import { generateMarketTags } from '../support/mocks/generate-market-tags';
|
||||
import { generateMarketPositions } from '../support/mocks/generate-market-positions';
|
||||
import { generateEstimateOrder } from '../support/mocks/generate-estimate-order';
|
||||
import { generatePartyBalance } from '../support/mocks/generate-party-balance';
|
||||
import { generatePartyMarketData } from '../support/mocks/generate-party-market-data';
|
||||
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
|
||||
import { generateMarketNames } from '../support/mocks/generate-market-names';
|
||||
import { generateMarketDepth } from '../support/mocks/generate-market-depth';
|
||||
import type { Market, Markets } from '@vegaprotocol/market-list';
|
||||
|
||||
describe('market selector', { tags: '@smoke' }, () => {
|
||||
let markets: Market[];
|
||||
beforeEach(() => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(req, 'Markets', generateSimpleMarkets());
|
||||
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||
aliasQuery(req, 'DealTicket', generateDealTicket());
|
||||
aliasQuery(req, 'MarketTags', generateMarketTags());
|
||||
aliasQuery(req, 'MarketPositions', generateMarketPositions());
|
||||
aliasQuery(req, 'EstimateOrder', generateEstimateOrder());
|
||||
aliasQuery(req, 'PartyBalanceQuery', generatePartyBalance());
|
||||
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
|
||||
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
|
||||
aliasQuery(req, 'MarketNames', generateMarketNames());
|
||||
aliasQuery(req, 'MarketDepth', generateMarketDepth());
|
||||
});
|
||||
|
||||
cy.visit('/markets');
|
||||
cy.wait('@Markets').then((response) => {
|
||||
const data: Markets | undefined = response?.response?.body?.data;
|
||||
if (data.marketsConnection.edges.length) {
|
||||
markets = data.marketsConnection.edges.map((edge) => edge.node);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should be properly rendered', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('input[placeholder="Search"]').should(
|
||||
'have.value',
|
||||
markets[0].tradableInstrument.instrument.name
|
||||
);
|
||||
cy.getByTestId('arrow-button').click();
|
||||
cy.getByTestId('market-pane').should('be.visible');
|
||||
cy.getByTestId('market-pane')
|
||||
.children()
|
||||
.find('[role="button"]')
|
||||
.first()
|
||||
.should('contain.text', markets[0].tradableInstrument.instrument.name);
|
||||
cy.getByTestId('market-pane')
|
||||
.children()
|
||||
.find('[role="button"]')
|
||||
.first()
|
||||
.click();
|
||||
cy.getByTestId('market-pane').should('not.be.visible');
|
||||
}
|
||||
});
|
||||
|
||||
it('typing should change list', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('input[placeholder="Search"]').type('{backspace}');
|
||||
cy.getByTestId('market-pane')
|
||||
.children()
|
||||
.find('[role="button"]')
|
||||
.should('have.length.at.least', 1);
|
||||
cy.get('input[placeholder="Search"]').clear();
|
||||
cy.get('input[placeholder="Search"]').type('aa');
|
||||
const filtered = markets.filter(
|
||||
(market) =>
|
||||
market.state === 'STATE_ACTIVE' &&
|
||||
market.tradableInstrument.instrument.name.match(/aa/i)
|
||||
);
|
||||
cy.getByTestId('market-pane')
|
||||
.children()
|
||||
.find('[role="button"]')
|
||||
.should('have.length', filtered.length);
|
||||
cy.getByTestId('market-pane')
|
||||
.children()
|
||||
.find('[role="button"]')
|
||||
.last()
|
||||
.click();
|
||||
cy.location('pathname').should(
|
||||
'eq',
|
||||
`/trading/${filtered[filtered.length - 1].id}`
|
||||
);
|
||||
cy.get('input[placeholder="Search"]').should(
|
||||
'have.value',
|
||||
filtered[filtered.length - 1].tradableInstrument.instrument.name
|
||||
);
|
||||
}
|
||||
});
|
||||
// constantly failing on ci
|
||||
it.skip('keyboard navigation should work well', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('input[placeholder="Search"]').type('{backspace}');
|
||||
cy.get('input[placeholder="Search"]').clear();
|
||||
cy.focused().realPress('ArrowDown');
|
||||
cy.focused().should('contain.text', 'AAVEDAI Monthly');
|
||||
cy.focused().realPress('ArrowDown');
|
||||
cy.focused().should('contain.text', 'ETHBTC').realPress('Enter');
|
||||
cy.location('pathname').should('eq', '/trading/ethbtc-quaterly');
|
||||
|
||||
cy.get('input[placeholder="Search"]').type('{backspace}');
|
||||
cy.get('input[placeholder="Search"]').clear();
|
||||
cy.getByTestId('market-pane').should('be.visible');
|
||||
cy.get('body').realPress('ArrowDown');
|
||||
cy.get('body').realPress('Tab');
|
||||
cy.getByTestId('market-pane').should('not.be.visible');
|
||||
}
|
||||
});
|
||||
|
||||
it('mobile view', () => {
|
||||
if (markets?.length) {
|
||||
cy.viewport('iphone-xr');
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('[role="dialog"]').should('not.exist');
|
||||
cy.getByTestId('arrow-button').click();
|
||||
cy.get('[role="dialog"]').should('be.visible');
|
||||
cy.get('input[placeholder="Search"]').clear();
|
||||
cy.getByTestId('market-pane')
|
||||
.children()
|
||||
.find('[role="button"]')
|
||||
.should('have.length', 9);
|
||||
cy.get('div[role="dialog"]').should('have.class', 'w-screen');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.get('input[placeholder="Search"]').should(
|
||||
'have.value',
|
||||
markets[0].tradableInstrument.instrument.name
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,329 +0,0 @@
|
||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||
import {
|
||||
generateSimpleMarkets,
|
||||
generateMarketsCandles,
|
||||
generateMarketsData,
|
||||
} from '../support/mocks/generate-markets';
|
||||
import { generateDealTicket } from '../support/mocks/generate-deal-ticket';
|
||||
import { generateMarketTags } from '../support/mocks/generate-market-tags';
|
||||
import { generateMarketPositions } from '../support/mocks/generate-market-positions';
|
||||
import { generateEstimateOrder } from '../support/mocks/generate-estimate-order';
|
||||
import { generatePartyBalance } from '../support/mocks/generate-party-balance';
|
||||
import { generatePartyMarketData } from '../support/mocks/generate-party-market-data';
|
||||
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
|
||||
import { generateMarketDepth } from '../support/mocks/generate-market-depth';
|
||||
import { connectVegaWallet } from '../support/connect-wallet';
|
||||
import type { Markets, Market } from '@vegaprotocol/market-list';
|
||||
|
||||
describe('Market trade', { tags: '@smoke' }, () => {
|
||||
let markets: Market[];
|
||||
beforeEach(() => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(req, 'Markets', generateSimpleMarkets());
|
||||
aliasQuery(req, 'MarketsCandlesQuery', generateMarketsCandles());
|
||||
aliasQuery(req, 'MarketsDataQuery', generateMarketsData());
|
||||
aliasQuery(req, 'SimpleMarkets', generateSimpleMarkets());
|
||||
aliasQuery(req, 'DealTicket', generateDealTicket());
|
||||
aliasQuery(req, 'MarketTags', generateMarketTags());
|
||||
aliasQuery(req, 'MarketPositions', generateMarketPositions());
|
||||
aliasQuery(req, 'EstimateOrder', generateEstimateOrder());
|
||||
aliasQuery(req, 'PartyBalanceQuery', generatePartyBalance());
|
||||
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
|
||||
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
|
||||
aliasQuery(req, 'MarketDepth', generateMarketDepth());
|
||||
});
|
||||
cy.visit('/markets');
|
||||
cy.wait('@Markets').then((response) => {
|
||||
const data: Markets | undefined = response?.response?.body?.data;
|
||||
if (data.marketsConnection.edges.length) {
|
||||
markets = data.marketsConnection.edges.map((edge) => edge.node);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not display steps if wallet is disconnected', () => {
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
cy.getByTestId('trading-connect-wallet')
|
||||
.find('h3')
|
||||
.should('have.text', 'Please connect your Vega wallet to make a trade');
|
||||
cy.getByTestId('trading-connect-wallet')
|
||||
.find('button')
|
||||
.should('have.text', 'Connect Vega wallet');
|
||||
cy.getByTestId('trading-connect-wallet')
|
||||
.find('a')
|
||||
.should('have.text', 'https://vega.xyz/wallet');
|
||||
});
|
||||
|
||||
it('side selector should work well', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').should(
|
||||
'have.text',
|
||||
'Long'
|
||||
);
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').click();
|
||||
cy.get('button[aria-label="Open short position"]').click();
|
||||
cy.get('#step-2-control').click();
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').should(
|
||||
'have.text',
|
||||
'Short'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('side selector mobile view should work well', () => {
|
||||
if (markets?.length) {
|
||||
cy.viewport('iphone-xr');
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
connectVegaWallet();
|
||||
cy.getByTestId('next-button').scrollIntoView().click();
|
||||
|
||||
cy.get('button[aria-label="Open long position"]').should(
|
||||
'have.class',
|
||||
'selected'
|
||||
);
|
||||
cy.get('button[aria-label="Open short position"]').should(
|
||||
'not.have.class',
|
||||
'selected'
|
||||
);
|
||||
|
||||
cy.get('button[aria-label="Open short position"]').click();
|
||||
cy.get('button[aria-label="Open long position"]').should(
|
||||
'not.have.class',
|
||||
'selected'
|
||||
);
|
||||
cy.get('button[aria-label="Open short position"]').should(
|
||||
'have.class',
|
||||
'selected'
|
||||
);
|
||||
cy.getByTestId('next-button').scrollIntoView().click();
|
||||
cy.get('#step-1-control').should(
|
||||
'contain.html',
|
||||
'aria-label="Selected value Short"'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('size slider should work well', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[1].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').click();
|
||||
cy.get('button[aria-label="Open short position"]').click();
|
||||
cy.get('#step-2-control').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(0)
|
||||
.find('button')
|
||||
.should('have.text', '1');
|
||||
cy.get('#step-2-panel').find('[role="slider"]').type('{rightarrow}');
|
||||
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(0)
|
||||
.find('button')
|
||||
.should('have.text', '2');
|
||||
}
|
||||
});
|
||||
|
||||
it('percentage selection should work well', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[1].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').click();
|
||||
cy.get('button[aria-label="Open short position"]').click();
|
||||
cy.get('#step-2-control').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(0)
|
||||
.find('button')
|
||||
.should('have.text', '1');
|
||||
|
||||
cy.getByTestId('max-label').should('have.text', '21');
|
||||
|
||||
cy.getByTestId('percentage-selector')
|
||||
.find('button')
|
||||
.contains('Max')
|
||||
.click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(0)
|
||||
.find('button')
|
||||
.should('have.text', '21');
|
||||
}
|
||||
});
|
||||
|
||||
it('size input should work well', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[1].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').click();
|
||||
cy.get('button[aria-label="Open short position"]').click();
|
||||
cy.get('#step-2-control').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(0)
|
||||
.find('button')
|
||||
.should('have.text', '1');
|
||||
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(0)
|
||||
.find('input')
|
||||
.type('{backspace}2');
|
||||
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(0)
|
||||
.find('button')
|
||||
.should('have.text', '2');
|
||||
cy.get('button').contains('Max').click();
|
||||
}
|
||||
});
|
||||
|
||||
it('slippage value should be displayed', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[1].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').click();
|
||||
cy.get('button[aria-label="Open short position"]').click();
|
||||
cy.get('#step-2-control').click();
|
||||
cy.get('button').contains('Max').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dl')
|
||||
.eq(2)
|
||||
.find('dd')
|
||||
.should('have.text', '0.02%');
|
||||
}
|
||||
});
|
||||
|
||||
it('allow slippage value to be adjusted', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[1].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').click();
|
||||
cy.get('button[aria-label="Open short position"]').click();
|
||||
cy.get('#step-2-control').click();
|
||||
cy.get('button').contains('Max').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dl')
|
||||
.eq(2)
|
||||
.find('dd')
|
||||
.should('have.text', '0.02%');
|
||||
cy.get('#step-2-panel').find('dl').eq(2).find('button').click();
|
||||
cy.get('#input-order-slippage')
|
||||
.focus()
|
||||
.type('{backspace}{backspace}{backspace}1');
|
||||
|
||||
cy.getByTestId('slippage-dialog').find('button').click();
|
||||
|
||||
cy.get('#step-2-panel')
|
||||
.find('dl')
|
||||
.eq(2)
|
||||
.find('dd')
|
||||
.should('have.text', '1%');
|
||||
}
|
||||
});
|
||||
|
||||
it('notional position size should be present', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[1].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').click();
|
||||
cy.get('button[aria-label="Open short position"]').click();
|
||||
cy.get('#step-2-control').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(0)
|
||||
.find('button')
|
||||
.should('have.text', '1');
|
||||
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(0)
|
||||
.find('input')
|
||||
.type('{backspace}2');
|
||||
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dt')
|
||||
.eq(2)
|
||||
.should('have.text', 'Est. Position Size (tDAI)');
|
||||
cy.get('#step-2-panel').find('dd').eq(2).should('have.text', '197.86012');
|
||||
}
|
||||
});
|
||||
|
||||
it('total fees should be displayed', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[1].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-1-control [aria-label^="Selected value"]').click();
|
||||
cy.get('button[aria-label="Open short position"]').click();
|
||||
cy.get('#step-2-control').click();
|
||||
cy.get('#step-2-panel')
|
||||
.find('dt')
|
||||
.eq(3)
|
||||
.should('have.text', 'Est. Fees (tDAI)');
|
||||
cy.get('#step-2-panel')
|
||||
.find('dd')
|
||||
.eq(3)
|
||||
.should('have.text', '3.00000 (3.03%)');
|
||||
}
|
||||
});
|
||||
|
||||
it('order review should display proper calculations', () => {
|
||||
if (markets?.length) {
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-3-control').click();
|
||||
|
||||
cy.getByTestId('review-trade')
|
||||
.get('#contracts_tooltip_trigger')
|
||||
.trigger('click')
|
||||
.realTouch();
|
||||
|
||||
cy.get('[data-radix-popper-content-wrapper]').contains(
|
||||
'The number of contracts determines'
|
||||
);
|
||||
cy.get('#step-3-panel').find('dd').eq(1).should('have.text', '1');
|
||||
|
||||
cy.get('#step-3-panel').find('dd').eq(2).should('have.text', '98.93006');
|
||||
|
||||
cy.get('#step-3-panel')
|
||||
.find('dd')
|
||||
.eq(3)
|
||||
.should('have.text', '3.00000 (3.03%)');
|
||||
|
||||
cy.get('#step-3-panel').find('dd').eq(4).should('have.text', ' - ');
|
||||
|
||||
cy.getByTestId('place-order').click();
|
||||
cy.getByTestId('dialog-title').should(
|
||||
'have.text',
|
||||
'Confirm transaction in wallet'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('info tooltip on mobile view should work well', () => {
|
||||
if (markets?.length) {
|
||||
cy.viewport('iphone-xr');
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
connectVegaWallet();
|
||||
cy.get('#step-3-control').click();
|
||||
|
||||
// Start from the bottom tooltip to ensure the tooltip above
|
||||
// can be interacted with
|
||||
cy.getByTestId('review-trade').get('div.cursor-help').eq(1).realTouch();
|
||||
cy.get('[data-radix-popper-content-wrapper]').contains(
|
||||
'The notional size represents the position size'
|
||||
);
|
||||
|
||||
cy.getByTestId('review-trade')
|
||||
.get('#contracts_tooltip_trigger')
|
||||
.realTouch();
|
||||
cy.get('[data-radix-popper-content-wrapper]').contains(
|
||||
'The number of contracts determines'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,156 +0,0 @@
|
||||
import {
|
||||
connectVegaWallet,
|
||||
disconnectVegaWallet,
|
||||
} 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', { tags: '@smoke' }, () => {
|
||||
afterEach(() => {
|
||||
disconnectVegaWallet();
|
||||
});
|
||||
|
||||
it('button for wallet connect should work', () => {
|
||||
cy.visit('/');
|
||||
cy.get('[href="/portfolio"]').eq(0).click();
|
||||
cy.getByTestId('trading-connect-wallet').should('be.visible');
|
||||
connectVegaWallet();
|
||||
cy.getByTestId('trading-connect-wallet').should('not.exist');
|
||||
});
|
||||
|
||||
it('certain tabs should exist', () => {
|
||||
cy.visit('/portfolio');
|
||||
connectVegaWallet();
|
||||
|
||||
cy.getByTestId('assets').click();
|
||||
cy.location('pathname').should('eq', '/portfolio/assets');
|
||||
|
||||
cy.getByTestId('positions').click();
|
||||
cy.location('pathname').should('eq', '/portfolio/positions');
|
||||
|
||||
cy.getByTestId('orders').click();
|
||||
cy.location('pathname').should('eq', '/portfolio/orders');
|
||||
|
||||
cy.getByTestId('fills').click();
|
||||
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', 5);
|
||||
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'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
export const connectVegaWallet = () => {
|
||||
const form = 'rest-connector-form';
|
||||
const walletName = Cypress.env('TRADING_TEST_VEGA_WALLET_NAME');
|
||||
const walletPassphrase = Cypress.env('TRADING_TEST_VEGA_WALLET_PASSPHRASE');
|
||||
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.getByTestId('connectors-list').find('button').click();
|
||||
cy.getByTestId(form).find('#wallet').click().type(walletName);
|
||||
cy.getByTestId(form).find('#passphrase').click().type(walletPassphrase);
|
||||
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
|
||||
};
|
||||
|
||||
export const disconnectVegaWallet = () => {
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.getByTestId('disconnect').click();
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
import '@vegaprotocol/cypress';
|
||||
import 'cypress-real-events/support';
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
import registerCypressGrep from 'cypress-grep';
|
||||
registerCypressGrep();
|
||||
@@ -1,125 +0,0 @@
|
||||
import type { Market } from '@vegaprotocol/market-list';
|
||||
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
||||
|
||||
export const protoCandles = [
|
||||
{ open: '9556163', close: '9587028', __typename: 'Candle' },
|
||||
{
|
||||
open: '9587028',
|
||||
close: '9769899',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9769899', close: '9586292', __typename: 'Candle' },
|
||||
{
|
||||
open: '9586292',
|
||||
close: '9261774',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9261773', close: '9236369', __typename: 'Candle' },
|
||||
{
|
||||
open: '9236369',
|
||||
close: '9226070',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9226077', close: '9233252', __typename: 'Candle' },
|
||||
{
|
||||
open: '9249854',
|
||||
close: '9333038',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9333038', close: '9410371', __typename: 'Candle' },
|
||||
{
|
||||
open: '9410371',
|
||||
close: '9626249',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9626247', close: '9493253', __typename: 'Candle' },
|
||||
{
|
||||
open: '9493253',
|
||||
close: '9309054',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9309054', close: '9378428', __typename: 'Candle' },
|
||||
{
|
||||
open: '9378428',
|
||||
close: '9352996',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9352996', close: '9451142', __typename: 'Candle' },
|
||||
{
|
||||
open: '9451142',
|
||||
close: '9691070',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9691071', close: '9622031', __typename: 'Candle' },
|
||||
{
|
||||
open: '9622034',
|
||||
close: '9519285',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9528904', close: '9671275', __typename: 'Candle' },
|
||||
{
|
||||
open: '9671275',
|
||||
close: '9988454',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9982457', close: '10085537', __typename: 'Candle' },
|
||||
{
|
||||
open: '10085537',
|
||||
close: '9967390',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
{ open: '9967390', close: '9974844', __typename: 'Candle' },
|
||||
{
|
||||
open: '9974844',
|
||||
close: '9940706',
|
||||
__typename: 'Candle',
|
||||
},
|
||||
];
|
||||
|
||||
export const protoMarket: Market = {
|
||||
id: 'first-btcusd-id',
|
||||
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: {
|
||||
instrument: {
|
||||
id: '',
|
||||
code: 'AAVEDAI.MF21',
|
||||
name: 'AAVEDAI Monthly (30 Jun 2022)',
|
||||
metadata: {
|
||||
tags: [
|
||||
'formerly:2839D9B2329C9E70',
|
||||
'base:AAVE',
|
||||
'quote:DAI',
|
||||
'class:fx/crypto',
|
||||
'monthly',
|
||||
'sector:defi',
|
||||
],
|
||||
__typename: 'InstrumentMetadata',
|
||||
},
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: 'DAI',
|
||||
settlementAsset: { symbol: 'tDAI', __typename: 'Asset', decimals: 5 },
|
||||
},
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
__typename: 'Market',
|
||||
};
|
||||
@@ -1,108 +0,0 @@
|
||||
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,33 +0,0 @@
|
||||
export const generateDealTicket = () => {
|
||||
return {
|
||||
market: {
|
||||
id: 'first-btcusd-id',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 0,
|
||||
state: 'STATE_ACTIVE',
|
||||
tradingMode: 'Continuous',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'AAVEDAI Monthly (30 Jun 2022)',
|
||||
product: {
|
||||
quoteName: 'DAI',
|
||||
settlementAsset: {
|
||||
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
|
||||
symbol: 'tDAI',
|
||||
name: 'tDAI TEST',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'Future',
|
||||
},
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
depth: {
|
||||
lastTrade: { price: '9893006', __typename: 'Trade' },
|
||||
__typename: 'MarketDepth',
|
||||
},
|
||||
__typename: 'Market',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
export const generateEstimateOrder = () => {
|
||||
return {
|
||||
estimateOrder: {
|
||||
fee: {
|
||||
__typename: 'TradeFee',
|
||||
makerFee: '100000',
|
||||
liquidityFee: '100000',
|
||||
infrastructureFee: '100000',
|
||||
},
|
||||
marginLevels: {
|
||||
initialLevel: '2844054.80937741220203',
|
||||
__typename: 'MarginLevels',
|
||||
},
|
||||
__typename: 'OrderEstimate',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,111 +0,0 @@
|
||||
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,210 +0,0 @@
|
||||
import merge from 'lodash/merge';
|
||||
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: {
|
||||
id: 'a46bd7e5277087723b7ab835844dec3cef8b4445738101269624bf5537d5d423',
|
||||
depth: {
|
||||
sell: [
|
||||
{
|
||||
price: '9893007',
|
||||
volume: '3',
|
||||
numberOfOrders: '3',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893010',
|
||||
volume: '4',
|
||||
numberOfOrders: '4',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893012',
|
||||
volume: '1',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893015',
|
||||
volume: '1',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893017',
|
||||
volume: '2',
|
||||
numberOfOrders: '2',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893021',
|
||||
volume: '4',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893025',
|
||||
volume: '5',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893125',
|
||||
volume: '4',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893135',
|
||||
volume: '2',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893165',
|
||||
volume: '5',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893175',
|
||||
volume: '3',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893185',
|
||||
volume: '3',
|
||||
numberOfOrders: '3',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9894185',
|
||||
volume: '1',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9894585',
|
||||
volume: '1',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9895585',
|
||||
volume: '4',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9896585',
|
||||
volume: '2',
|
||||
numberOfOrders: '2',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
],
|
||||
buy: [
|
||||
{
|
||||
price: '9893005',
|
||||
volume: '4',
|
||||
numberOfOrders: '3',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893003',
|
||||
volume: '2',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9893001',
|
||||
volume: '1',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9892006',
|
||||
volume: '3',
|
||||
numberOfOrders: '2',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9891006',
|
||||
volume: '2',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9891001',
|
||||
volume: '1',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9890101',
|
||||
volume: '2',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9890091',
|
||||
volume: '5',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9890081',
|
||||
volume: '4',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9890050',
|
||||
volume: '2',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9890040',
|
||||
volume: '6',
|
||||
numberOfOrders: '3',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9890030',
|
||||
volume: '6',
|
||||
numberOfOrders: '2',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9890021',
|
||||
volume: '3',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9890011',
|
||||
volume: '1',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
{
|
||||
price: '9890001',
|
||||
volume: '11',
|
||||
numberOfOrders: '1',
|
||||
__typename: 'PriceLevel',
|
||||
},
|
||||
],
|
||||
sequenceNumber: '1661773865550746910',
|
||||
__typename: 'MarketDepth',
|
||||
},
|
||||
__typename: 'Market',
|
||||
},
|
||||
};
|
||||
return merge(defaultResult, override);
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
export const generateMarketMarkPrice = () => {
|
||||
return {
|
||||
market: {
|
||||
decimalPlaces: 5,
|
||||
data: { markPrice: '692748', __typename: 'MarketData' },
|
||||
__typename: 'Market',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,136 +0,0 @@
|
||||
import { protoMarket } from './commons';
|
||||
|
||||
export const generateMarketNames = () => {
|
||||
return {
|
||||
markets: [
|
||||
{ ...protoMarket },
|
||||
{
|
||||
id: '1d7ddf67dac4924db03f5bf58571a7bcb1908d70c66580467717aabc5345b68a',
|
||||
state: 'STATE_SUSPENDED',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
code: 'AAPL.MF21',
|
||||
name: 'Apple Monthly (30 Jun 2022)',
|
||||
metadata: {
|
||||
tags: [
|
||||
'formerly:4899E01009F1A721',
|
||||
'quote:USD',
|
||||
'ticker:AAPL',
|
||||
'class:equities/single-stock-futures',
|
||||
'sector:tech',
|
||||
'listing_venue:NASDAQ',
|
||||
'country:US',
|
||||
],
|
||||
__typename: 'InstrumentMetadata',
|
||||
},
|
||||
product: { quoteName: 'USD', __typename: 'Future' },
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
__typename: 'Market',
|
||||
},
|
||||
{
|
||||
id: '87ae87cd3244fc1fab4b0e2dad2437879864192bb969f3109b69293421644c8b',
|
||||
state: 'STATE_SUSPENDED',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
code: 'TSLA.QM21',
|
||||
name: 'Tesla Quarterly (30 Jun 2022)',
|
||||
metadata: {
|
||||
tags: [
|
||||
'formerly:5A86B190C384997F',
|
||||
'quote:EURO',
|
||||
'ticker:TSLA',
|
||||
'class:equities/single-stock-futures',
|
||||
'sector:tech',
|
||||
'listing_venue:NASDAQ',
|
||||
'country:US',
|
||||
],
|
||||
__typename: 'InstrumentMetadata',
|
||||
},
|
||||
product: { quoteName: 'EURO', __typename: 'Future' },
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
__typename: 'Market',
|
||||
},
|
||||
{
|
||||
id: '69205712a854f1bbfb69fa3d11b60e01a1e249bafb5ece88115e7451e8ef07b3',
|
||||
state: 'STATE_SUSPENDED',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
code: 'BTCUSD.MF21',
|
||||
name: 'BTCUSD Monthly (30 Jun 2022)',
|
||||
metadata: {
|
||||
tags: [
|
||||
'formerly:076BB86A5AA41E3E',
|
||||
'base:BTC',
|
||||
'quote:USD',
|
||||
'class:fx/crypto',
|
||||
'monthly',
|
||||
'sector:crypto',
|
||||
],
|
||||
__typename: 'InstrumentMetadata',
|
||||
},
|
||||
product: { quoteName: 'USD', __typename: 'Future' },
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
__typename: 'Market',
|
||||
},
|
||||
{
|
||||
id: 'ethbtc-quaterly',
|
||||
state: 'STATE_ACTIVE',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
code: 'ETHBTC.QM21',
|
||||
name: 'ETHBTC Quarterly (30 Jun 2022)',
|
||||
metadata: {
|
||||
tags: [
|
||||
'formerly:1F0BB6EB5703B099',
|
||||
'base:ETH',
|
||||
'quote:BTC',
|
||||
'class:fx/crypto',
|
||||
'quarterly',
|
||||
'sector:crypto',
|
||||
],
|
||||
__typename: 'InstrumentMetadata',
|
||||
},
|
||||
product: { quoteName: 'BTC', __typename: 'Future' },
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
__typename: 'Market',
|
||||
},
|
||||
{
|
||||
id: '3c62b2714c4332d1a689a5352bff090e6aabccfd6bd87ce018936b38372530c9',
|
||||
state: 'STATE_ACTIVE',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
code: 'UNIDAI.MF21',
|
||||
name: 'UNIDAI Monthly (30 Jun 2022)',
|
||||
metadata: {
|
||||
tags: [
|
||||
'formerly:3C58ED2A4A6C5D7E',
|
||||
'base:UNI',
|
||||
'quote:DAI',
|
||||
'class:fx/crypto',
|
||||
'monthly',
|
||||
'sector:defi',
|
||||
],
|
||||
__typename: 'InstrumentMetadata',
|
||||
},
|
||||
product: { quoteName: 'DAI', __typename: 'Future' },
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
__typename: 'Market',
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -1,59 +0,0 @@
|
||||
export const generateMarketPositions = () => {
|
||||
return {
|
||||
party: {
|
||||
id: '2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
|
||||
accounts: [
|
||||
{
|
||||
type: 'General',
|
||||
asset: {
|
||||
decimals: 5,
|
||||
},
|
||||
balance: '400000000000000000000',
|
||||
market: {
|
||||
id: '2751c508f9759761f912890f37fb3f97a00300bf7685c02a56a86e05facfe221',
|
||||
__typename: 'Market',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Margin',
|
||||
asset: {
|
||||
decimals: 5,
|
||||
},
|
||||
balance: '265329',
|
||||
market: {
|
||||
id: 'first-btcusd-id',
|
||||
__typename: 'Market',
|
||||
},
|
||||
},
|
||||
],
|
||||
positionsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
openVolume: '3',
|
||||
market: {
|
||||
id: '2751c508f9759761f912890f37fb3f97a00300bf7685c02a56a86e05facfe221',
|
||||
__typename: 'Market',
|
||||
},
|
||||
__typename: 'Position',
|
||||
},
|
||||
__typename: 'PositionEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
openVolume: '12',
|
||||
market: {
|
||||
id: 'first-btcusd-id',
|
||||
__typename: 'Market',
|
||||
},
|
||||
__typename: 'Position',
|
||||
},
|
||||
__typename: 'PositionEdge',
|
||||
},
|
||||
],
|
||||
__typename: 'PositionConnection',
|
||||
},
|
||||
__typename: 'Party',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
export const generateMarketTags = () => {
|
||||
return {
|
||||
market: {
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
metadata: {
|
||||
tags: [
|
||||
'formerly:2839D9B2329C9E70',
|
||||
'base:AAVE',
|
||||
'quote:DAI',
|
||||
'class:fx/crypto',
|
||||
'monthly',
|
||||
'sector:defi',
|
||||
'settlement:2022-08-01',
|
||||
],
|
||||
__typename: 'InstrumentMetadata',
|
||||
},
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
__typename: 'Market',
|
||||
},
|
||||
};
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,139 +0,0 @@
|
||||
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,
|
||||
},
|
||||
{
|
||||
__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,
|
||||
},
|
||||
{
|
||||
__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,
|
||||
},
|
||||
{
|
||||
__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,
|
||||
},
|
||||
{
|
||||
__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,
|
||||
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);
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
export const generatePartyBalance = () => {
|
||||
return {
|
||||
party: {
|
||||
accounts: [
|
||||
{
|
||||
balance: '88474051',
|
||||
type: 'ACCOUNT_TYPE_GENERAL',
|
||||
asset: {
|
||||
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
|
||||
symbol: 'tDAI',
|
||||
name: 'tDAI TEST',
|
||||
decimals: 5,
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'Account',
|
||||
},
|
||||
{
|
||||
balance: '100000000',
|
||||
type: 'ACCOUNT_TYPE_GENERAL',
|
||||
asset: {
|
||||
id: '8b52d4a3a4b0ffe733cddbc2b67be273816cfeb6ca4c8b339bac03ffba08e4e4',
|
||||
symbol: 'tEURO',
|
||||
name: 'tEURO TEST',
|
||||
decimals: 5,
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'Account',
|
||||
},
|
||||
{
|
||||
balance: '3412867',
|
||||
type: 'ACCOUNT_TYPE_GENERAL',
|
||||
asset: {
|
||||
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
|
||||
symbol: 'tDAI',
|
||||
name: 'tDAI TEST',
|
||||
decimals: 5,
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'Account',
|
||||
},
|
||||
{
|
||||
balance: '70007',
|
||||
type: 'ACCOUNT_TYPE_GENERAL',
|
||||
asset: {
|
||||
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
|
||||
symbol: 'tDAI',
|
||||
name: 'tDAI TEST',
|
||||
decimals: 5,
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'Account',
|
||||
},
|
||||
],
|
||||
__typename: 'Party',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
export const generatePartyMarketData = () => {
|
||||
return {
|
||||
party: {
|
||||
id: '2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
|
||||
accounts: [
|
||||
{
|
||||
type: 'General',
|
||||
balance: '1200000',
|
||||
asset: { id: 'fBTC', decimals: 5, __typename: 'Asset' },
|
||||
market: null,
|
||||
__typename: 'Account',
|
||||
},
|
||||
],
|
||||
marginsConnection: { edges: null, __typename: 'MarginConnection' },
|
||||
positionsConnection: { edges: null, __typename: 'PositionConnection' },
|
||||
__typename: 'Party',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,201 +0,0 @@
|
||||
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',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
|
||||
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
||||
NX_VEGA_ENV=DEVNET
|
||||
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
|
||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||
@@ -1,8 +0,0 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
|
||||
NX_VEGA_URL=https://api.vega.xyz/query
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
|
||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
||||
@@ -1,7 +0,0 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
|
||||
NX_VEGA_URL=https://api.n01.stagnet3.vega.xyz/graphql
|
||||
NX_VEGA_ENV=STAGNET3
|
||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||
NX_VEGA_EXPLORER_URL=https://staging2.explorer.vega.xyz
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "apps/console-lite/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "./tools/executors/webpack:build",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"compiler": "babel",
|
||||
"outputPath": "dist/apps/console-lite",
|
||||
"index": "apps/console-lite/src/index.html",
|
||||
"baseHref": "/",
|
||||
"main": "apps/console-lite/src/main.tsx",
|
||||
"polyfills": "apps/console-lite/src/polyfills.ts",
|
||||
"tsConfig": "apps/console-lite/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/console-lite/src/favicon.ico",
|
||||
"apps/console-lite/src/assets"
|
||||
],
|
||||
"styles": ["apps/console-lite/src/styles.scss"],
|
||||
"scripts": [],
|
||||
"webpackConfig": "@nrwl/react/plugins/webpack"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "apps/console-lite/src/environments/environment.ts",
|
||||
"with": "apps/console-lite/src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "./tools/executors/webpack:serve",
|
||||
"options": {
|
||||
"buildTarget": "console-lite:build:development",
|
||||
"hmr": true,
|
||||
"port": 4001
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "console-lite:build:production",
|
||||
"hmr": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/console-lite/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/apps/console-lite"],
|
||||
"options": {
|
||||
"jestConfig": "apps/console-lite/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
},
|
||||
"build-netlify": {
|
||||
"executor": "@nrwl/workspace:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"cp apps/console-lite/netlify.toml netlify.toml",
|
||||
"nx build console-lite"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import 'jest-canvas-mock';
|
||||
import { defaultFallbackInView } from 'react-intersection-observer';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
defaultFallbackInView(true);
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
||||
global.DOMRect = class DOMRect {
|
||||
bottom = 0;
|
||||
left = 0;
|
||||
right = 0;
|
||||
top = 0;
|
||||
|
||||
constructor(
|
||||
public x = 0,
|
||||
public y = 0,
|
||||
public width = 0,
|
||||
public height = 0
|
||||
) {}
|
||||
static fromRect(other?: DOMRectInit): DOMRect {
|
||||
return new DOMRect(other?.x, other?.y, other?.width, other?.height);
|
||||
}
|
||||
toJSON() {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
};
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // Deprecated
|
||||
removeListener: jest.fn(), // Deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
|
||||
jest.setTimeout(30000);
|
||||
@@ -1,57 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { createClient } from './lib/apollo-client';
|
||||
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
|
||||
import {
|
||||
VegaConnectDialog,
|
||||
VegaManageDialog,
|
||||
VegaWalletProvider,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { Connectors } from './lib/vega-connectors';
|
||||
import '../styles.scss';
|
||||
import { AppLoader } from './components/app-loader';
|
||||
import Header from './components/header';
|
||||
import { Main } from './components/main';
|
||||
import LocalContext from './context/local-context';
|
||||
import useLocalValues from './hooks/use-local-values';
|
||||
|
||||
function App() {
|
||||
const localValues = useLocalValues();
|
||||
const {
|
||||
vegaWalletDialog,
|
||||
menu: { setMenuOpen },
|
||||
} = localValues;
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
setMenuOpen(false);
|
||||
}, [location, setMenuOpen]);
|
||||
|
||||
return (
|
||||
<EnvironmentProvider>
|
||||
<NetworkLoader createClient={createClient}>
|
||||
<VegaWalletProvider>
|
||||
<LocalContext.Provider value={localValues}>
|
||||
<AppLoader>
|
||||
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-neutral-200 bg-white text-neutral-800 grid grid-rows-[min-content,1fr]">
|
||||
<Header />
|
||||
<Main />
|
||||
<VegaConnectDialog
|
||||
connectors={Connectors}
|
||||
dialogOpen={vegaWalletDialog.connect}
|
||||
setDialogOpen={vegaWalletDialog.setConnect}
|
||||
/>
|
||||
<VegaManageDialog
|
||||
dialogOpen={vegaWalletDialog.manage}
|
||||
setDialogOpen={vegaWalletDialog.setManage}
|
||||
/>
|
||||
</div>
|
||||
</AppLoader>
|
||||
</LocalContext.Provider>
|
||||
</VegaWalletProvider>
|
||||
</NetworkLoader>
|
||||
</EnvironmentProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,108 +0,0 @@
|
||||
import React, { forwardRef, useCallback, useMemo, useRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||
import { useScreenDimensions, useTheme } 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 } = useTheme();
|
||||
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 extends { id?: string }
|
||||
>(
|
||||
p: Props<T> & { ref?: React.Ref<AgGridReact> }
|
||||
) => React.ReactElement;
|
||||
|
||||
export default ConsoleLiteGridForwarder;
|
||||
@@ -1 +0,0 @@
|
||||
export { default as ConsoleLiteGrid } from './console-lite-grid';
|
||||
@@ -1,11 +0,0 @@
|
||||
query MarketTags($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
metadata {
|
||||
tags
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
query PartyBalanceQuery($partyId: ID!) {
|
||||
party(id: $partyId) {
|
||||
accounts {
|
||||
type
|
||||
balance
|
||||
asset {
|
||||
id
|
||||
symbol
|
||||
name
|
||||
decimals
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: MarketTags
|
||||
// ====================================================
|
||||
|
||||
export interface MarketTags_market_tradableInstrument_instrument_metadata {
|
||||
__typename: "InstrumentMetadata";
|
||||
/**
|
||||
* An arbitrary list of tags to associated to associate to the Instrument (string list)
|
||||
*/
|
||||
tags: string[] | null;
|
||||
}
|
||||
|
||||
export interface MarketTags_market_tradableInstrument_instrument {
|
||||
__typename: "Instrument";
|
||||
/**
|
||||
* Metadata for this instrument
|
||||
*/
|
||||
metadata: MarketTags_market_tradableInstrument_instrument_metadata;
|
||||
}
|
||||
|
||||
export interface MarketTags_market_tradableInstrument {
|
||||
__typename: "TradableInstrument";
|
||||
/**
|
||||
* An instance of, or reference to, a fully specified instrument.
|
||||
*/
|
||||
instrument: MarketTags_market_tradableInstrument_instrument;
|
||||
}
|
||||
|
||||
export interface MarketTags_market {
|
||||
__typename: "Market";
|
||||
/**
|
||||
* An instance of, or reference to, a tradable instrument.
|
||||
*/
|
||||
tradableInstrument: MarketTags_market_tradableInstrument;
|
||||
}
|
||||
|
||||
export interface MarketTags {
|
||||
/**
|
||||
* An instrument that is trading on the Vega network
|
||||
*/
|
||||
market: MarketTags_market | null;
|
||||
}
|
||||
|
||||
export interface MarketTagsVariables {
|
||||
marketId: string;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import { Schema as Types } from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type MarketTagsQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarketTagsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null } } } } | null };
|
||||
|
||||
|
||||
export const MarketTagsDocument = gql`
|
||||
query MarketTags($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
metadata {
|
||||
tags
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useMarketTagsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useMarketTagsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useMarketTagsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useMarketTagsQuery({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useMarketTagsQuery(baseOptions: Apollo.QueryHookOptions<MarketTagsQuery, MarketTagsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<MarketTagsQuery, MarketTagsQueryVariables>(MarketTagsDocument, options);
|
||||
}
|
||||
export function useMarketTagsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketTagsQuery, MarketTagsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<MarketTagsQuery, MarketTagsQueryVariables>(MarketTagsDocument, options);
|
||||
}
|
||||
export type MarketTagsQueryHookResult = ReturnType<typeof useMarketTagsQuery>;
|
||||
export type MarketTagsLazyQueryHookResult = ReturnType<typeof useMarketTagsLazyQuery>;
|
||||
export type MarketTagsQueryResult = Apollo.QueryResult<MarketTagsQuery, MarketTagsQueryVariables>;
|
||||
@@ -1,57 +0,0 @@
|
||||
import { Schema as Types } from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type PartyBalanceQueryQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type PartyBalanceQueryQuery = { __typename?: 'Query', party?: { __typename?: 'Party', accounts?: Array<{ __typename?: 'Account', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number } }> | null } | null };
|
||||
|
||||
|
||||
export const PartyBalanceQueryDocument = gql`
|
||||
query PartyBalanceQuery($partyId: ID!) {
|
||||
party(id: $partyId) {
|
||||
accounts {
|
||||
type
|
||||
balance
|
||||
asset {
|
||||
id
|
||||
symbol
|
||||
name
|
||||
decimals
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __usePartyBalanceQueryQuery__
|
||||
*
|
||||
* To run a query within a React component, call `usePartyBalanceQueryQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `usePartyBalanceQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = usePartyBalanceQueryQuery({
|
||||
* variables: {
|
||||
* partyId: // value for 'partyId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function usePartyBalanceQueryQuery(baseOptions: Apollo.QueryHookOptions<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>(PartyBalanceQueryDocument, options);
|
||||
}
|
||||
export function usePartyBalanceQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>(PartyBalanceQueryDocument, options);
|
||||
}
|
||||
export type PartyBalanceQueryQueryHookResult = ReturnType<typeof usePartyBalanceQueryQuery>;
|
||||
export type PartyBalanceQueryLazyQueryHookResult = ReturnType<typeof usePartyBalanceQueryLazyQuery>;
|
||||
export type PartyBalanceQueryQueryResult = Apollo.QueryResult<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>;
|
||||
@@ -1,36 +0,0 @@
|
||||
import React from 'react';
|
||||
import Video from '../header/video';
|
||||
import Comet from '../header/comet';
|
||||
import Star from '../icons/star';
|
||||
|
||||
const Baubles = () => {
|
||||
return (
|
||||
<aside className="relative right-0 top-0 h-[700px] hidden md:block md:w-1/2 overflow-hidden">
|
||||
<div className="absolute top-[100px] w-[393px] left-[19%] h-[517px]">
|
||||
<div className="absolute top-[82px] right-[34px] w-[100px] h-[100px] clip-path-rounded">
|
||||
<Video />
|
||||
</div>
|
||||
<div className="absolute bottom-[100px] left-[59px] w-[200px] h-[200px] clip-path-rounded">
|
||||
<Video />
|
||||
</div>
|
||||
<div className="absolute w-[118px] h-[85px] right-0 bottom-[178px]">
|
||||
<Comet />
|
||||
</div>
|
||||
<div className="absolute w-[118px] h-[82px] left-0 bottom-[120px]">
|
||||
<Comet />
|
||||
</div>
|
||||
<div className="absolute w-[20px] h-[20px] top-0 left-[49px]">
|
||||
<Star />
|
||||
</div>
|
||||
<div className="absolute w-[20px] h-[20px] top-[89px] left-[184px]">
|
||||
<Star />
|
||||
</div>
|
||||
<div className="absolute w-[10px] h-[10px] bottom-0 right-[137px]">
|
||||
<Star />
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
export default Baubles;
|
||||
@@ -1,21 +0,0 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const EST_MARGIN_TOOLTIP_TEXT = t(
|
||||
'When opening a position on a futures market, you must post margin to cover any potential losses that you may incur. The margin is typically a fraction of the notional position size. For example, for a notional position size of $500, if the margin requirement is 10%, then the estimated margin would be approximately $50.'
|
||||
);
|
||||
export const CONTRACTS_MARGIN_TOOLTIP_TEXT = t(
|
||||
'The number of contracts determines how many units of the futures contract to buy or sell. For example, this is similar to buying one share of a listed company. The value of 1 contract is equivalent to the price of the contract. For example, if the current price is $50, then one contract is worth $50.'
|
||||
);
|
||||
export const EST_CLOSEOUT_TOOLTIP_TEXT = t(
|
||||
'Because you only need to post a fraction of your position size as margin when trading futures, it is possible to obtain leverage meaning your notional position size exceeds your account balance. In this scenario, if the market moves against your position, it will sometimes be necessary to force close your position due to insufficient funds. The estimated close out tells you the price at which that would happen based on current position and account balance.'
|
||||
);
|
||||
export const NOTIONAL_SIZE_TOOLTIP_TEXT = t(
|
||||
'The notional size represents the position size in the settlement asset of the futures contract. The notional size is calculated by multiplying the number of contracts by the price of the contract. For example, ten contracts traded at a price of $50 has a notional size of $500.'
|
||||
);
|
||||
export const EST_FEES_TOOLTIP_TEXT = t(
|
||||
'When you execute a new buy or sell order, you must pay a small amount of commission to the network for doing so. This fee is used to provide income to the node operates of the network and market makers who make prices on the futures market you are trading.'
|
||||
);
|
||||
|
||||
export const EST_SLIPPAGE = t(
|
||||
'When you execute a trade on Vega, the price obtained in the market may differ from the best available price displayed at the time of placing the trade. The estimated slippage shows the difference between the best available price and the estimated execution price, determined by market liquidity and your chosen order size.'
|
||||
);
|
||||
@@ -1,67 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import type {
|
||||
PartyBalanceQuery_party_accounts,
|
||||
PartyBalanceQuery_party_accounts_asset,
|
||||
} from './__generated__/PartyBalanceQuery';
|
||||
import { DealTicketBalance } from './deal-ticket-balance';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
const tDAI: PartyBalanceQuery_party_accounts_asset = {
|
||||
__typename: 'Asset',
|
||||
id: '1',
|
||||
symbol: 'tDAI',
|
||||
name: 'TDAI',
|
||||
decimals: 2,
|
||||
};
|
||||
|
||||
const accounts: PartyBalanceQuery_party_accounts[] = [
|
||||
{
|
||||
__typename: 'Account',
|
||||
type: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
balance: '1000000',
|
||||
asset: tDAI,
|
||||
},
|
||||
];
|
||||
|
||||
describe('DealTicketBalance', function () {
|
||||
it('should render the balance', () => {
|
||||
const { getByText, getByRole } = render(
|
||||
<DealTicketBalance
|
||||
settlementAsset={tDAI}
|
||||
accounts={accounts}
|
||||
isWalletConnected
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getByRole('complementary')).toHaveAccessibleName('tDAI Balance');
|
||||
expect(getByText('10,000.00')).toBeInTheDocument();
|
||||
expect(getByText('tDAI')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should prompt to connect wallet', () => {
|
||||
const { getByText } = render(
|
||||
<DealTicketBalance
|
||||
settlementAsset={tDAI}
|
||||
accounts={accounts}
|
||||
isWalletConnected={false}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
getByText('Please connect your Vega wallet to see your balance')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display zero balance', () => {
|
||||
const { getByText } = render(
|
||||
<DealTicketBalance
|
||||
settlementAsset={tDAI}
|
||||
accounts={[]}
|
||||
isWalletConnected={true}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getByText('No tDAI left to trade')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||
import type { PartyBalanceQuery_party_accounts } from './__generated__/PartyBalanceQuery';
|
||||
import { useSettlementAccount } from '../../hooks/use-settlement-account';
|
||||
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
interface DealTicketBalanceProps {
|
||||
settlementAsset: DealTicketMarketFragment['tradableInstrument']['instrument']['product']['settlementAsset'];
|
||||
accounts: PartyBalanceQuery_party_accounts[];
|
||||
isWalletConnected: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const DealTicketBalance = ({
|
||||
settlementAsset,
|
||||
accounts,
|
||||
isWalletConnected,
|
||||
className = '',
|
||||
}: DealTicketBalanceProps) => {
|
||||
const settlementAssetId = settlementAsset?.id;
|
||||
const settlementAssetSymbol = settlementAsset?.symbol;
|
||||
const settlementAccount = useSettlementAccount(
|
||||
settlementAssetId,
|
||||
accounts,
|
||||
AccountType.ACCOUNT_TYPE_GENERAL
|
||||
);
|
||||
const formatedNumber =
|
||||
settlementAccount?.balance &&
|
||||
settlementAccount.asset.decimals &&
|
||||
addDecimalsFormatNumber(
|
||||
settlementAccount.balance,
|
||||
settlementAccount.asset.decimals
|
||||
);
|
||||
|
||||
const balance = (
|
||||
<p className="text-blue text-lg font-semibold">
|
||||
{settlementAccount
|
||||
? t(`${formatedNumber}`)
|
||||
: `No ${settlementAssetSymbol} left to trade`}
|
||||
</p>
|
||||
);
|
||||
|
||||
const connectWallet = (
|
||||
<p>{t('Please connect your Vega wallet to see your balance')}</p>
|
||||
);
|
||||
|
||||
const ariaLabel = t(`${settlementAssetSymbol} Balance`);
|
||||
|
||||
return (
|
||||
<aside
|
||||
aria-label={ariaLabel}
|
||||
className={classNames('text-right', className)}
|
||||
>
|
||||
<div className="inline-block">
|
||||
<span className="text-blue">{settlementAssetSymbol}</span>
|
||||
{isWalletConnected ? balance : connectWallet}
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
@@ -1,90 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import {
|
||||
DealTicketManager,
|
||||
DealTicketContainer as Container,
|
||||
} from '@vegaprotocol/deal-ticket';
|
||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { DealTicketSteps } from './deal-ticket-steps';
|
||||
import { DealTicketBalance } from './deal-ticket-balance';
|
||||
import Baubles from './baubles-decor';
|
||||
import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery';
|
||||
import ConnectWallet from '../wallet-connector';
|
||||
|
||||
const tempEmptyText = (
|
||||
<p>{t('Please select a market from the markets page')}</p>
|
||||
);
|
||||
|
||||
const PARTY_BALANCE_QUERY = gql`
|
||||
query PartyBalanceQuery($partyId: ID!) {
|
||||
party(id: $partyId) {
|
||||
accounts {
|
||||
type
|
||||
balance
|
||||
asset {
|
||||
id
|
||||
symbol
|
||||
name
|
||||
decimals
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DealTicketContainer = () => {
|
||||
const { marketId } = useParams<{ marketId: string }>();
|
||||
const { keypair } = useVegaWallet();
|
||||
|
||||
const { data: partyData, loading } = useQuery<PartyBalanceQuery>(
|
||||
PARTY_BALANCE_QUERY,
|
||||
{
|
||||
variables: { partyId: keypair?.pub },
|
||||
skip: !keypair?.pub,
|
||||
}
|
||||
);
|
||||
|
||||
const loader = <Loader />;
|
||||
|
||||
const container = marketId ? (
|
||||
<Container marketId={marketId}>
|
||||
{(data) => {
|
||||
if (!data.market) {
|
||||
return null as unknown as JSX.Element;
|
||||
}
|
||||
|
||||
const balance = (
|
||||
<DealTicketBalance
|
||||
className="mb-4"
|
||||
settlementAsset={
|
||||
data.market.tradableInstrument.instrument.product?.settlementAsset
|
||||
}
|
||||
accounts={partyData?.party?.accounts || []}
|
||||
isWalletConnected={!!keypair?.pub}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<DealTicketManager market={data.market}>
|
||||
{loading ? loader : balance}
|
||||
<DealTicketSteps market={data.market} partyData={partyData} />
|
||||
</DealTicketManager>
|
||||
);
|
||||
}}
|
||||
</Container>
|
||||
) : (
|
||||
tempEmptyText
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="flex p-4 md:p-6">
|
||||
<section className="w-full md:w-1/2 md:min-w-[500px]">
|
||||
{keypair ? container : <ConnectWallet />}
|
||||
</section>
|
||||
<Baubles />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -1,133 +0,0 @@
|
||||
import React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import * as constants from './constants';
|
||||
import { TrafficLight } from '../traffic-light';
|
||||
|
||||
interface DealTicketEstimatesProps {
|
||||
quoteName?: string;
|
||||
price?: string;
|
||||
estCloseOut?: string;
|
||||
estMargin?: string;
|
||||
fees?: string;
|
||||
notionalSize?: string;
|
||||
size?: string;
|
||||
slippage?: string;
|
||||
}
|
||||
|
||||
interface DataTitleProps {
|
||||
children: ReactNode;
|
||||
quoteName?: string;
|
||||
}
|
||||
|
||||
export const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
|
||||
<dt>
|
||||
{children}
|
||||
{quoteName && <small> ({quoteName})</small>}
|
||||
</dt>
|
||||
);
|
||||
|
||||
interface ValueTooltipProps {
|
||||
value?: string;
|
||||
children?: ReactNode;
|
||||
description: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export const ValueTooltipRow = ({
|
||||
value,
|
||||
children,
|
||||
description,
|
||||
id,
|
||||
}: ValueTooltipProps) => (
|
||||
<dd className="flex gap-x-2 items-center">
|
||||
{value || children}
|
||||
<Tooltip align="center" description={description}>
|
||||
<div className="cursor-help" id={id || ''} tabIndex={-1}>
|
||||
<Icon
|
||||
name={IconNames.ISSUE}
|
||||
className="block rotate-180"
|
||||
ariaLabel={description}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</dd>
|
||||
);
|
||||
|
||||
export const DealTicketEstimates = ({
|
||||
price,
|
||||
quoteName,
|
||||
estCloseOut,
|
||||
estMargin,
|
||||
fees,
|
||||
notionalSize,
|
||||
size,
|
||||
slippage,
|
||||
}: DealTicketEstimatesProps) => (
|
||||
<dl className="text-black dark:text-white">
|
||||
{size && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Contracts')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={size}
|
||||
description={constants.CONTRACTS_MARGIN_TOOLTIP_TEXT}
|
||||
id="contracts_tooltip_trigger"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{price && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Est. Price')}</DataTitle>
|
||||
<dd>{price}</dd>
|
||||
</div>
|
||||
)}
|
||||
{notionalSize && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Position Size')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={notionalSize}
|
||||
description={constants.NOTIONAL_SIZE_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{fees && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Fees')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={fees}
|
||||
description={constants.EST_FEES_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{estMargin && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Margin')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={estMargin}
|
||||
description={constants.EST_MARGIN_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{estCloseOut && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Close out')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={estCloseOut}
|
||||
description={constants.EST_CLOSEOUT_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{slippage && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
|
||||
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
|
||||
<TrafficLight value={parseFloat(slippage)} q1={1} q2={5}>
|
||||
{slippage}%
|
||||
</TrafficLight>
|
||||
</ValueTooltipRow>
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
);
|
||||
@@ -1,149 +0,0 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
SliderRoot,
|
||||
SliderThumb,
|
||||
SliderTrack,
|
||||
SliderRange,
|
||||
FormGroup,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { InputSetter } from '../input-setter';
|
||||
|
||||
interface DealTicketSizeInputProps {
|
||||
step: number;
|
||||
min: number;
|
||||
max: number;
|
||||
value: number;
|
||||
onValueChange: (value: number) => void;
|
||||
positionDecimalPlaces: number;
|
||||
}
|
||||
|
||||
const getSizeLabel = (value: number): string => {
|
||||
const MIN_LABEL = 'Min';
|
||||
const MAX_LABEL = 'Max';
|
||||
if (value === 0) {
|
||||
return MIN_LABEL;
|
||||
} else if (value === 100) {
|
||||
return MAX_LABEL;
|
||||
}
|
||||
|
||||
return `${value}%`;
|
||||
};
|
||||
|
||||
export const DealTicketSizeInput = ({
|
||||
value,
|
||||
step,
|
||||
min,
|
||||
max,
|
||||
onValueChange,
|
||||
positionDecimalPlaces,
|
||||
}: DealTicketSizeInputProps) => {
|
||||
const sizeRatios = [0, 25, 50, 75, 100];
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
|
||||
const onInputValueChange = useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
let value = parseFloat(event.target.value);
|
||||
const isLessThanMin = value < min;
|
||||
const isMoreThanMax = value > max;
|
||||
if (isLessThanMin) {
|
||||
value = min;
|
||||
} else if (isMoreThanMax) {
|
||||
value = max;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
onValueChange(value);
|
||||
}
|
||||
|
||||
setInputValue(value);
|
||||
},
|
||||
[min, max, onValueChange, setInputValue]
|
||||
);
|
||||
|
||||
const onButtonValueChange = (size: number) => {
|
||||
const newVal = new BigNumber(size)
|
||||
.decimalPlaces(positionDecimalPlaces)
|
||||
.toNumber();
|
||||
onValueChange(newVal);
|
||||
setInputValue(newVal);
|
||||
};
|
||||
|
||||
const onSliderValueChange = useCallback(
|
||||
(value: number[]) => {
|
||||
const val = value[0];
|
||||
setInputValue(val);
|
||||
onValueChange(val);
|
||||
},
|
||||
[onValueChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between text-black dark:text-white mb-2">
|
||||
<span data-testid="min-label">{min}</span>
|
||||
<span data-testid="max-label">{max}</span>
|
||||
</div>
|
||||
<SliderRoot
|
||||
className="mb-2"
|
||||
value={[value]}
|
||||
onValueChange={onSliderValueChange}
|
||||
step={step}
|
||||
min={min}
|
||||
max={max}
|
||||
>
|
||||
<SliderTrack className="bg-lightGrey dark:bg-offBlack">
|
||||
<SliderRange className="!bg-black dark:!bg-white" />
|
||||
</SliderTrack>
|
||||
<SliderThumb />
|
||||
</SliderRoot>
|
||||
|
||||
<div
|
||||
data-testid="percentage-selector"
|
||||
className="flex w-full justify-between text-black dark:text-white mb-6"
|
||||
>
|
||||
{sizeRatios.map((size, index) => {
|
||||
const proportionalSize = size ? (size / 100) * max : min;
|
||||
return (
|
||||
<button
|
||||
className="no-underline hover:underline text-blue"
|
||||
onClick={() => onButtonValueChange(proportionalSize)}
|
||||
type="button"
|
||||
key={index}
|
||||
>
|
||||
{getSizeLabel(size)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<dl className="text-black dark:text-white">
|
||||
<div className="flex items-center justify-between">
|
||||
<dt>{t('Contracts')}</dt>
|
||||
<dd className="flex justify-end w-full">
|
||||
<FormGroup
|
||||
hideLabel={true}
|
||||
label="Enter Size"
|
||||
labelFor="trade-size-input"
|
||||
className="mb-1"
|
||||
>
|
||||
<InputSetter
|
||||
id="input-order-size-market"
|
||||
type="number"
|
||||
step={step}
|
||||
min={min}
|
||||
max={max}
|
||||
className="w-full"
|
||||
value={inputValue}
|
||||
onChange={onInputValueChange}
|
||||
>
|
||||
{inputValue}
|
||||
</InputSetter>
|
||||
</FormGroup>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,55 +0,0 @@
|
||||
import React from 'react';
|
||||
import { DealTicketEstimates } from './deal-ticket-estimates';
|
||||
import { DealTicketSizeInput } from './deal-ticket-size-input';
|
||||
|
||||
interface DealTicketSizeProps {
|
||||
step: number;
|
||||
min: number;
|
||||
max: number;
|
||||
size: number;
|
||||
onSizeChange: (value: number) => void;
|
||||
name: string;
|
||||
quoteName: string;
|
||||
price: string;
|
||||
estCloseOut: string;
|
||||
estMargin: string;
|
||||
fees: string;
|
||||
positionDecimalPlaces: number;
|
||||
notionalSize: string;
|
||||
}
|
||||
|
||||
export const DealTicketSize = ({
|
||||
step,
|
||||
min,
|
||||
max,
|
||||
price,
|
||||
quoteName,
|
||||
size,
|
||||
onSizeChange,
|
||||
estCloseOut,
|
||||
positionDecimalPlaces,
|
||||
fees,
|
||||
notionalSize,
|
||||
}: DealTicketSizeProps) => {
|
||||
return max === 0 ? (
|
||||
<p>Not enough balance to trade</p>
|
||||
) : (
|
||||
<div>
|
||||
<DealTicketSizeInput
|
||||
step={step}
|
||||
min={min}
|
||||
max={max}
|
||||
value={size}
|
||||
onValueChange={onSizeChange}
|
||||
positionDecimalPlaces={positionDecimalPlaces}
|
||||
/>
|
||||
<DealTicketEstimates
|
||||
quoteName={quoteName}
|
||||
fees={fees}
|
||||
estCloseOut={estCloseOut}
|
||||
price={price}
|
||||
notionalSize={notionalSize}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,104 +0,0 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import * as constants from './constants';
|
||||
import { TrafficLight } from '../traffic-light';
|
||||
import { Dialog, Icon, Intent, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { InputSetter } from '../../components/input-setter';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { DataTitle, ValueTooltipRow } from './deal-ticket-estimates';
|
||||
|
||||
interface DealTicketSlippageProps {
|
||||
step?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
value: number;
|
||||
onValueChange(value: number): void;
|
||||
}
|
||||
|
||||
export const DealTicketSlippage = ({
|
||||
value,
|
||||
step = 0.01,
|
||||
min = 0,
|
||||
max = 50,
|
||||
onValueChange,
|
||||
}: DealTicketSlippageProps) => {
|
||||
const [isDialogVisible, setIsDialogVisible] = useState(false);
|
||||
|
||||
const onChange = useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value;
|
||||
const numericValue = parseFloat(value);
|
||||
onValueChange(numericValue);
|
||||
},
|
||||
[onValueChange]
|
||||
);
|
||||
|
||||
const toggleDialog = useCallback(() => {
|
||||
setIsDialogVisible(!isDialogVisible);
|
||||
}, [isDialogVisible]);
|
||||
|
||||
const formLabel = (
|
||||
<label className="flex items-center mb-1">
|
||||
<span className="mr-1">{t('Adjust slippage tolerance')}</span>
|
||||
<Tooltip align="center" description={constants.EST_SLIPPAGE}>
|
||||
<div className="cursor-help" tabIndex={-1}>
|
||||
<Icon
|
||||
name={IconNames.ISSUE}
|
||||
className="block rotate-180"
|
||||
ariaLabel={constants.EST_SLIPPAGE}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
open={isDialogVisible}
|
||||
onChange={toggleDialog}
|
||||
intent={Intent.None}
|
||||
title={t('Transaction Settings')}
|
||||
>
|
||||
<div data-testid="slippage-dialog">
|
||||
{formLabel}
|
||||
<InputSetter
|
||||
id="input-order-slippage"
|
||||
isInputVisible
|
||||
hasError={!value}
|
||||
type="number"
|
||||
step={step}
|
||||
min={min}
|
||||
max={max}
|
||||
className="w-full"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
>
|
||||
{value}%
|
||||
</InputSetter>
|
||||
</div>
|
||||
</Dialog>
|
||||
<dl className="text-black dark:text-white">
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
|
||||
<div className="flex">
|
||||
<div className="mr-1">
|
||||
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
|
||||
<TrafficLight value={value} q1={1} q2={5}>
|
||||
{value}%
|
||||
</TrafficLight>
|
||||
</ValueTooltipRow>
|
||||
</div>
|
||||
<button type="button" onClick={toggleDialog}>
|
||||
<Icon
|
||||
name={IconNames.COG}
|
||||
className="block rotate-180"
|
||||
ariaLabel={t('Override slippage value')}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</dl>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,327 +0,0 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { Stepper } from '../stepper';
|
||||
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||
import { InputError } from '@vegaprotocol/ui-toolkit';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import { MarketSelector } from '@vegaprotocol/deal-ticket';
|
||||
import type { Order } from '@vegaprotocol/orders';
|
||||
import { useVegaWallet, VegaTxStatus } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
t,
|
||||
addDecimalsFormatNumber,
|
||||
toDecimal,
|
||||
removeDecimal,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
getDefaultOrder,
|
||||
useOrderValidation,
|
||||
useOrderSubmit,
|
||||
getOrderDialogTitle,
|
||||
getOrderDialogIntent,
|
||||
getOrderDialogIcon,
|
||||
OrderFeedback,
|
||||
validateSize,
|
||||
} from '@vegaprotocol/orders';
|
||||
import { DealTicketSize } from './deal-ticket-size';
|
||||
import MarketNameRenderer from '../simple-market-list/simple-market-renderer';
|
||||
import SideSelector, { SIDE_NAMES } from './side-selector';
|
||||
import ReviewTrade from './review-trade';
|
||||
import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery';
|
||||
import useOrderCloseOut from '../../hooks/use-order-closeout';
|
||||
import useOrderMargin from '../../hooks/use-order-margin';
|
||||
import useMaximumPositionSize from '../../hooks/use-maximum-position-size';
|
||||
import useCalculateSlippage from '../../hooks/use-calculate-slippage';
|
||||
import { Side, OrderType } from '@vegaprotocol/types';
|
||||
import { DealTicketSlippage } from './deal-ticket-slippage';
|
||||
|
||||
interface DealTicketMarketProps {
|
||||
market: DealTicketMarketFragment;
|
||||
partyData?: PartyBalanceQuery;
|
||||
}
|
||||
|
||||
export const DealTicketSteps = ({
|
||||
market,
|
||||
partyData,
|
||||
}: DealTicketMarketProps) => {
|
||||
const navigate = useNavigate();
|
||||
const setMarket = useCallback(
|
||||
(marketId: string) => {
|
||||
navigate(`/trading/${marketId}`);
|
||||
},
|
||||
[navigate]
|
||||
);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<Order>({
|
||||
mode: 'onChange',
|
||||
defaultValues: getDefaultOrder(market),
|
||||
});
|
||||
|
||||
const emptyString = ' - ';
|
||||
const step = toDecimal(market.positionDecimalPlaces);
|
||||
const orderType = watch('type');
|
||||
const orderTimeInForce = watch('timeInForce');
|
||||
const orderSide = watch('side');
|
||||
const orderSize = watch('size');
|
||||
const order = watch();
|
||||
const { message: invalidText, isDisabled } = useOrderValidation({
|
||||
market,
|
||||
orderType,
|
||||
orderTimeInForce,
|
||||
fieldErrors: errors,
|
||||
});
|
||||
const { submit, transaction, finalizedOrder, Dialog } = useOrderSubmit();
|
||||
const { keypair } = useVegaWallet();
|
||||
const estMargin = useOrderMargin({
|
||||
order,
|
||||
market,
|
||||
partyId: keypair?.pub || '',
|
||||
});
|
||||
|
||||
const maxTrade = useMaximumPositionSize({
|
||||
partyId: keypair?.pub || '',
|
||||
accounts: partyData?.party?.accounts || [],
|
||||
marketId: market.id,
|
||||
settlementAssetId:
|
||||
market.tradableInstrument.instrument.product.settlementAsset.id,
|
||||
price: market?.depth?.lastTrade?.price,
|
||||
order,
|
||||
});
|
||||
|
||||
const estCloseOut = useOrderCloseOut({ order, market, partyData });
|
||||
const slippage = useCalculateSlippage({ marketId: market.id, order });
|
||||
const [slippageValue, setSlippageValue] = useState(
|
||||
slippage ? parseFloat(slippage) : 0
|
||||
);
|
||||
const transactionStatus =
|
||||
transaction.status === VegaTxStatus.Requested ||
|
||||
transaction.status === VegaTxStatus.Pending
|
||||
? 'pending'
|
||||
: 'default';
|
||||
|
||||
useEffect(() => {
|
||||
setSlippageValue(slippage ? parseFloat(slippage) : 0);
|
||||
}, [slippage]);
|
||||
|
||||
const price = useMemo(() => {
|
||||
if (slippage && market?.depth?.lastTrade?.price) {
|
||||
const isLong = order.side === Side.SIDE_BUY;
|
||||
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
|
||||
parseFloat(slippage) / 100
|
||||
);
|
||||
return new BigNumber(market?.depth?.lastTrade?.price)
|
||||
.multipliedBy(multiplier)
|
||||
.toNumber();
|
||||
}
|
||||
return null;
|
||||
}, [market?.depth?.lastTrade?.price, order.side, slippage]);
|
||||
|
||||
const formattedPrice =
|
||||
price && addDecimalsFormatNumber(price, market.decimalPlaces);
|
||||
|
||||
const notionalSize = useMemo(() => {
|
||||
if (price) {
|
||||
const size = new BigNumber(price).multipliedBy(orderSize).toNumber();
|
||||
|
||||
return addDecimalsFormatNumber(size, market.decimalPlaces);
|
||||
}
|
||||
return null;
|
||||
}, [market.decimalPlaces, orderSize, price]);
|
||||
|
||||
const fees = useMemo(() => {
|
||||
if (estMargin?.fees && notionalSize) {
|
||||
const percentage = new BigNumber(estMargin?.fees)
|
||||
.dividedBy(notionalSize)
|
||||
.multipliedBy(100)
|
||||
.decimalPlaces(2)
|
||||
.toString();
|
||||
|
||||
return `${estMargin.fees} (${percentage}%)`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [estMargin?.fees, notionalSize]);
|
||||
|
||||
const max = useMemo(() => {
|
||||
return new BigNumber(maxTrade)
|
||||
.decimalPlaces(market.positionDecimalPlaces)
|
||||
.toNumber();
|
||||
}, [market.positionDecimalPlaces, maxTrade]);
|
||||
|
||||
const onSizeChange = useCallback(
|
||||
(value: number) => {
|
||||
const newVal = new BigNumber(value)
|
||||
.decimalPlaces(market.positionDecimalPlaces)
|
||||
.toString();
|
||||
const isValid = validateSize(step)(newVal);
|
||||
if (isValid !== 'step') {
|
||||
setValue('size', newVal);
|
||||
}
|
||||
},
|
||||
[market.positionDecimalPlaces, setValue, step]
|
||||
);
|
||||
|
||||
const onSlippageChange = useCallback(
|
||||
(value: number) => {
|
||||
if (market?.depth?.lastTrade?.price) {
|
||||
if (value) {
|
||||
const isLong = order.side === Side.SIDE_BUY;
|
||||
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
|
||||
value / 100
|
||||
);
|
||||
const bestAskPrice = new BigNumber(market?.depth?.lastTrade?.price)
|
||||
.multipliedBy(multiplier)
|
||||
.decimalPlaces(market.decimalPlaces)
|
||||
.toString();
|
||||
|
||||
setValue('price', bestAskPrice);
|
||||
|
||||
if (orderType === OrderType.TYPE_MARKET) {
|
||||
setValue('type', OrderType.TYPE_LIMIT);
|
||||
}
|
||||
} else {
|
||||
setValue('type', OrderType.TYPE_MARKET);
|
||||
setValue('price', market?.depth?.lastTrade?.price);
|
||||
}
|
||||
}
|
||||
setSlippageValue(value);
|
||||
},
|
||||
[
|
||||
market.decimalPlaces,
|
||||
market?.depth?.lastTrade?.price,
|
||||
order.side,
|
||||
orderType,
|
||||
setValue,
|
||||
]
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(order: Order) => {
|
||||
if (transactionStatus !== 'pending') {
|
||||
submit({
|
||||
...order,
|
||||
price:
|
||||
order.price && removeDecimal(order.price, market.decimalPlaces),
|
||||
size: removeDecimal(order.size, market.positionDecimalPlaces),
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
transactionStatus,
|
||||
submit,
|
||||
market.decimalPlaces,
|
||||
market.positionDecimalPlaces,
|
||||
]
|
||||
);
|
||||
|
||||
const steps = [
|
||||
{
|
||||
label: t('Select Market'),
|
||||
component: (
|
||||
<MarketSelector
|
||||
market={market}
|
||||
setMarket={setMarket}
|
||||
ItemRenderer={MarketNameRenderer}
|
||||
/>
|
||||
),
|
||||
value: market.tradableInstrument.instrument.name,
|
||||
},
|
||||
{
|
||||
label: t('Select Direction'),
|
||||
component: (
|
||||
<Controller
|
||||
name="side"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<SideSelector value={field.value} onSelect={field.onChange} />
|
||||
)}
|
||||
/>
|
||||
),
|
||||
value: SIDE_NAMES[orderSide] || '',
|
||||
},
|
||||
{
|
||||
label: t('Choose Position Size'),
|
||||
component:
|
||||
max !== null ? (
|
||||
<>
|
||||
<DealTicketSize
|
||||
step={step}
|
||||
min={step}
|
||||
max={max}
|
||||
onSizeChange={onSizeChange}
|
||||
size={new BigNumber(orderSize).toNumber()}
|
||||
name="size"
|
||||
price={formattedPrice || emptyString}
|
||||
positionDecimalPlaces={market.positionDecimalPlaces}
|
||||
quoteName={
|
||||
market.tradableInstrument.instrument.product.settlementAsset
|
||||
.symbol
|
||||
}
|
||||
notionalSize={notionalSize || emptyString}
|
||||
estCloseOut={estCloseOut}
|
||||
fees={fees || emptyString}
|
||||
estMargin={estMargin?.margin || emptyString}
|
||||
/>
|
||||
<DealTicketSlippage
|
||||
value={slippageValue}
|
||||
onValueChange={onSlippageChange}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
'loading...'
|
||||
),
|
||||
value: orderSize,
|
||||
},
|
||||
{
|
||||
label: t('Review Trade'),
|
||||
component: (
|
||||
<div className="mb-8">
|
||||
{invalidText && (
|
||||
<div className="mb-2">
|
||||
<InputError data-testid="dealticket-error-message">
|
||||
{invalidText}
|
||||
</InputError>
|
||||
</div>
|
||||
)}
|
||||
<ReviewTrade
|
||||
market={market}
|
||||
isDisabled={isDisabled}
|
||||
transactionStatus={transactionStatus}
|
||||
order={order}
|
||||
estCloseOut={estCloseOut}
|
||||
estMargin={estMargin?.margin || emptyString}
|
||||
price={formattedPrice || emptyString}
|
||||
quoteName={
|
||||
market.tradableInstrument.instrument.product.settlementAsset
|
||||
.symbol
|
||||
}
|
||||
notionalSize={notionalSize || emptyString}
|
||||
fees={fees || emptyString}
|
||||
slippage={slippageValue}
|
||||
/>
|
||||
<Dialog
|
||||
title={getOrderDialogTitle(finalizedOrder?.status)}
|
||||
intent={getOrderDialogIntent(finalizedOrder?.status)}
|
||||
icon={getOrderDialogIcon(finalizedOrder?.status)}
|
||||
>
|
||||
<OrderFeedback transaction={transaction} order={finalizedOrder} />
|
||||
</Dialog>
|
||||
</div>
|
||||
),
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="px-2 py-4">
|
||||
<Stepper steps={steps} />
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './deal-ticket-container';
|
||||
export * from './deal-ticket-size';
|
||||
@@ -1,126 +0,0 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
Button,
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { DealTicketMarketFragment } from '@vegaprotocol/deal-ticket';
|
||||
import type { Order } from '@vegaprotocol/orders';
|
||||
import { SIDE_NAMES } from './side-selector';
|
||||
import SimpleMarketExpires from '../simple-market-list/simple-market-expires';
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import type {
|
||||
MarketTags,
|
||||
MarketTagsVariables,
|
||||
} from './__generated__/MarketTags';
|
||||
import { DealTicketEstimates } from './deal-ticket-estimates';
|
||||
import { Side } from '@vegaprotocol/types';
|
||||
|
||||
export const MARKET_TAGS_QUERY = gql`
|
||||
query MarketTags($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
metadata {
|
||||
tags
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
market: DealTicketMarketFragment;
|
||||
isDisabled: boolean;
|
||||
transactionStatus?: string;
|
||||
order: Order;
|
||||
estCloseOut: string;
|
||||
estMargin: string;
|
||||
quoteName: string;
|
||||
price: string;
|
||||
fees: string;
|
||||
notionalSize: string;
|
||||
slippage: number;
|
||||
}
|
||||
|
||||
export default ({
|
||||
isDisabled,
|
||||
market,
|
||||
order,
|
||||
transactionStatus,
|
||||
estCloseOut,
|
||||
quoteName,
|
||||
fees,
|
||||
price,
|
||||
notionalSize,
|
||||
slippage,
|
||||
}: Props) => {
|
||||
const { data: tagsData } = useQuery<MarketTags, MarketTagsVariables>(
|
||||
MARKET_TAGS_QUERY,
|
||||
{
|
||||
variables: { marketId: market.id },
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mb-8 text-black dark:text-white" data-testid="review-trade">
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow noBorder>
|
||||
<div className="flex flex-none gap-x-2 items-center">
|
||||
<div
|
||||
className={classNames(
|
||||
{
|
||||
'buyButton dark:buyButtonDark': order.side === Side.SIDE_BUY,
|
||||
'sellButton dark:sellButtonDark':
|
||||
order.side === Side.SIDE_SELL,
|
||||
},
|
||||
'px-2 py-1 inline text-ui-small'
|
||||
)}
|
||||
>
|
||||
{SIDE_NAMES[order.side]}
|
||||
</div>
|
||||
<div>{market.tradableInstrument.instrument.product.quoteName}</div>
|
||||
<div>
|
||||
{tagsData?.market?.tradableInstrument.instrument.metadata
|
||||
.tags && (
|
||||
<SimpleMarketExpires
|
||||
tags={
|
||||
tagsData?.market.tradableInstrument.instrument.metadata.tags
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-blue">
|
||||
{`@ ${price} `}
|
||||
<span className="text-ui-small inline">(EST)</span>
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
|
||||
<DealTicketEstimates
|
||||
size={order.size}
|
||||
quoteName={quoteName}
|
||||
fees={fees}
|
||||
estCloseOut={estCloseOut}
|
||||
notionalSize={notionalSize}
|
||||
slippage={slippage.toString()}
|
||||
/>
|
||||
|
||||
<div className="mt-12 max-w-sm">
|
||||
<Button
|
||||
fill={true}
|
||||
type="submit"
|
||||
disabled={transactionStatus === 'pending' || isDisabled}
|
||||
data-testid="place-order"
|
||||
rightIcon="arrow-top-right"
|
||||
>
|
||||
{transactionStatus === 'pending' ? t('Pending...') : t('Submit')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { FormGroup } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Side } from '@vegaprotocol/types';
|
||||
|
||||
interface SideSelectorProps {
|
||||
value: Side;
|
||||
onSelect: (side: Side) => void;
|
||||
}
|
||||
|
||||
export const SIDE_NAMES: Record<Side, string> = {
|
||||
[Side.SIDE_BUY]: t('Long'),
|
||||
[Side.SIDE_SELL]: t('Short'),
|
||||
};
|
||||
|
||||
export default ({ value, onSelect }: SideSelectorProps) => {
|
||||
return (
|
||||
<FormGroup
|
||||
label={t('Direction')}
|
||||
labelFor="order-side-toggle"
|
||||
hideLabel={true}
|
||||
>
|
||||
<fieldset
|
||||
className="w-full grid md:grid-cols-2 gap-4"
|
||||
id="order-side-toggle"
|
||||
>
|
||||
<button
|
||||
aria-label={t('Open long position')}
|
||||
type="button"
|
||||
className={classNames(
|
||||
'px-8 py-2',
|
||||
'buyButton hover:buyButton dark:buyButtonDark dark:hover:buyButtonDark',
|
||||
{ selected: value === Side.SIDE_BUY }
|
||||
)}
|
||||
onClick={() => onSelect(Side.SIDE_BUY)}
|
||||
>
|
||||
{t('Long')}
|
||||
</button>
|
||||
<button
|
||||
aria-label={t('Open short position')}
|
||||
type="button"
|
||||
className={classNames(
|
||||
'px-8 py-2',
|
||||
'sellButton hover:sellButton dark:sellButtonDark dark:hover:sellButtonDark',
|
||||
{ selected: value === Side.SIDE_SELL }
|
||||
)}
|
||||
onClick={() => onSelect(Side.SIDE_SELL)}
|
||||
>
|
||||
{t('Short')}
|
||||
</button>
|
||||
<div className="md:col-span-2 text-black dark:text-white text-ui-small">
|
||||
{t(
|
||||
'Trading derivatives allows you to make a profit or loss regardless of whether the market you are trading goes up or down. If you open a "long" position, you will make a profit if the price of your chosen market goes up, and you will make a profit for "short" positions when the price goes down.'
|
||||
)}
|
||||
</div>
|
||||
</fieldset>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
query DepositAssets {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
source {
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AssetStatus } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: 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;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { Schema as Types } from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type DepositAssetsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
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 DepositAssetsDocument = gql`
|
||||
query DepositAssets {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
source {
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useDepositAssetsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useDepositAssetsQuery` and pass it any options that fit your needs.
|
||||
* 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.
|
||||
*
|
||||
* @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 } = useDepositAssetsQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useDepositAssetsQuery(baseOptions?: Apollo.QueryHookOptions<DepositAssetsQuery, DepositAssetsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<DepositAssetsQuery, DepositAssetsQueryVariables>(DepositAssetsDocument, options);
|
||||
}
|
||||
export function useDepositAssetsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<DepositAssetsQuery, DepositAssetsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<DepositAssetsQuery, DepositAssetsQueryVariables>(DepositAssetsDocument, options);
|
||||
}
|
||||
export type DepositAssetsQueryHookResult = ReturnType<typeof useDepositAssetsQuery>;
|
||||
export type DepositAssetsLazyQueryHookResult = ReturnType<typeof useDepositAssetsLazyQuery>;
|
||||
export type DepositAssetsQueryResult = Apollo.QueryResult<DepositAssetsQuery, DepositAssetsQueryVariables>;
|
||||
@@ -1,61 +0,0 @@
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import { DepositManager } from '@vegaprotocol/deposits';
|
||||
import { getEnabledAssets, t } from '@vegaprotocol/react-helpers';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { Web3Container } from '@vegaprotocol/web3';
|
||||
import type { DepositAssets } from './__generated__/DepositAssets';
|
||||
|
||||
const DEPOSITS_QUERY = gql`
|
||||
query DepositAssets {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
status
|
||||
source {
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Fetches data required for the Deposit page
|
||||
*/
|
||||
export const DepositContainer = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const { keypair } = useVegaWallet();
|
||||
|
||||
const { data, loading, error } = useQuery<DepositAssets>(DEPOSITS_QUERY, {
|
||||
variables: { partyId: keypair?.pub },
|
||||
skip: !keypair?.pub,
|
||||
});
|
||||
|
||||
const assets = getEnabledAssets(data);
|
||||
|
||||
return (
|
||||
<AsyncRenderer<DepositAssets> data={data} loading={loading} error={error}>
|
||||
{assets.length ? (
|
||||
<Web3Container>
|
||||
<DepositManager
|
||||
assets={assets}
|
||||
isFaucetable={VEGA_ENV !== Networks.MAINNET}
|
||||
/>
|
||||
</Web3Container>
|
||||
) : (
|
||||
<Splash>
|
||||
<p>{t('No assets on this network')}</p>
|
||||
</Splash>
|
||||
)}
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './deposits';
|
||||
@@ -1,48 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const Comet = () => {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 118 82"
|
||||
fill="currentColor"
|
||||
className="max-w-[7.5rem] mx-auto"
|
||||
>
|
||||
<g className="fill-black dark:fill-white">
|
||||
<path d="M44.3791 67.5864H41.5859V70.3796H44.3791V67.5864Z"></path>
|
||||
<path d="M47.173 64.7932V62H44.3799V64.7932V67.5863H47.173V64.7932Z"></path>
|
||||
<path d="M41.5863 70.3794H36V73.1726H41.5863V70.3794Z"></path>
|
||||
<path d="M44.3791 73.1719H41.5859V75.965H44.3791V73.1719Z"></path>
|
||||
<path d="M47.173 75.9658H44.3799V81.5521H47.173V75.9658Z"></path>
|
||||
<path d="M49.966 73.1719H47.1729V75.965H49.966V73.1719Z"></path>
|
||||
<path d="M55.5521 70.3794H49.9658V73.1726H55.5521V70.3794Z"></path>
|
||||
<path d="M49.966 67.5864H47.1729V70.3796H49.966V67.5864Z"></path>
|
||||
<path d="M57.1876 60.564H54V63.7515H57.1876V60.564Z"></path>
|
||||
<path d="M60.3751 57.3765H57.1875V60.564H60.3751V57.3765Z"></path>
|
||||
<path d="M63.5626 54.1885H60.375V57.3761H63.5626V54.1885Z"></path>
|
||||
<path d="M66.7501 51.001H63.5625V54.1886H66.7501V51.001Z"></path>
|
||||
<path d="M69.9376 47.8135H66.75V51.0011H69.9376V47.8135Z"></path>
|
||||
<path d="M73.1251 44.626H69.9375V47.8136H73.1251V44.626Z"></path>
|
||||
<path d="M76.3126 41.4385H73.125V44.6261H76.3126V41.4385Z"></path>
|
||||
<path d="M79.5001 38.251H76.3125V41.4386H79.5001V38.251Z"></path>
|
||||
<path d="M82.6886 35.0635H79.501V38.2511H82.6886V35.0635Z"></path>
|
||||
<path d="M85.8761 31.876H82.6885V35.0636H85.8761V31.876Z"></path>
|
||||
<path d="M89.0636 28.688H85.876V31.8756H89.0636V28.688Z"></path>
|
||||
<path d="M92.2511 25.5005H89.0635V28.6881H92.2511V25.5005Z"></path>
|
||||
<path d="M95.4386 22.313H92.251V25.5006H95.4386V22.313Z"></path>
|
||||
<path d="M98.6261 19.1255H95.4385V22.3131H98.6261V19.1255Z"></path>
|
||||
<path d="M101.814 15.938H98.626V19.1256H101.814V15.938Z"></path>
|
||||
<path d="M105.001 12.7505H101.813V15.9381H105.001V12.7505Z"></path>
|
||||
<path d="M108.189 9.5625H105.001V12.7501H108.189V9.5625Z"></path>
|
||||
<path d="M111.376 6.375H108.188V9.56258H111.376V6.375Z"></path>
|
||||
<path d="M114.565 3.1875H111.377V6.37508H114.565V3.1875Z"></path>
|
||||
<path d="M117.752 0H114.564V3.18758H117.752V0Z"></path>
|
||||
<path d="M2.79316 20.793H0V23.5861H2.79316V20.793Z"></path>
|
||||
<path d="M5.58612 18H2.79297V20.7932H5.58612V18Z"></path>
|
||||
<path d="M5.58612 23.5859H2.79297V26.3791H5.58612V23.5859Z"></path>
|
||||
<path d="M8.37909 20.793H5.58594V23.5861H8.37909V20.793Z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Comet;
|
||||
@@ -1,30 +0,0 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||
import Logo from './logo';
|
||||
import { VegaWalletConnectButton } from '../vega-wallet-connect-button';
|
||||
import LocalContext from '../../context/local-context';
|
||||
|
||||
const Header = () => {
|
||||
const {
|
||||
vegaWalletDialog: { setConnect, setManage },
|
||||
theme,
|
||||
toggleTheme,
|
||||
} = useContext(LocalContext);
|
||||
return (
|
||||
<div
|
||||
className="dark flex items-stretch pr-6 py-6 bg-black text-neutral-400"
|
||||
data-testid="header"
|
||||
>
|
||||
<Logo />
|
||||
<div className="flex items-center gap-2 ml-auto relative z-10">
|
||||
<VegaWalletConnectButton
|
||||
setConnectDialog={setConnect}
|
||||
setManageDialog={setManage}
|
||||
/>
|
||||
<ThemeSwitcher theme={theme} onToggle={toggleTheme} className="-my-4" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './header';
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { VLogo } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
const Logo = () => {
|
||||
return (
|
||||
<NavLink
|
||||
className="mx-6 text-white"
|
||||
aria-label={t('Go to home page')}
|
||||
to="/"
|
||||
>
|
||||
<VLogo className="mx-6 my-2" />
|
||||
</NavLink>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logo;
|
||||
@@ -1,33 +0,0 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface Props {
|
||||
width: number;
|
||||
height: number;
|
||||
className: string;
|
||||
}
|
||||
|
||||
const Video = ({ width = 60, height = 60, className = '' }: Partial<Props>) => {
|
||||
return (
|
||||
<video
|
||||
width={width}
|
||||
height={height}
|
||||
autoPlay
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
className={classNames(
|
||||
'absolute left-0 top-0 w-full h-full object-cover',
|
||||
className
|
||||
)}
|
||||
poster="https://vega.xyz/static/poster-image.jpg"
|
||||
>
|
||||
<source
|
||||
src="https://d33wubrfki0l68.cloudfront.net/2500bc5ef1b96927e0220eeb2bef0b22b87bcda1/3e0d3/static/moshed-aa65f0933af9abe9afb5e5663c9b3f68.mp4"
|
||||
type="video/mp4"
|
||||
/>
|
||||
</video>
|
||||
);
|
||||
};
|
||||
|
||||
export default Video;
|
||||
@@ -1,103 +0,0 @@
|
||||
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;
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default as HorizontalMenu } from './horizontal-menu';
|
||||
export type { Item as HorizontalMenuItem } from './horizontal-menu';
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const Star = () => {
|
||||
return (
|
||||
<svg width="100%" height="100%" viewBox="0 0 21 21" fill="currentColor">
|
||||
<g className="fill-black dark:fill-white">
|
||||
<path d="M8.99996 5.99976H6V8.99972H8.99996V5.99976Z" />
|
||||
<path d="M12.0009 2.99996V0H9.00098V2.99996V5.99993H12.0009V2.99996Z" />
|
||||
<path d="M5.99993 9H0V12H5.99993V9Z" />
|
||||
<path d="M8.99996 11.9998H6V14.9997H8.99996V11.9998Z" />
|
||||
<path d="M12.0009 15H9.00098V20.9999H12.0009V15Z" />
|
||||
<path d="M15 11.9998H12V14.9997H15V11.9998Z" />
|
||||
<path d="M20.9999 9H15V12H20.9999V9Z" />
|
||||
<path d="M15 5.99976H12V8.99972H15V5.99976Z" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Star;
|
||||
@@ -1 +0,0 @@
|
||||
export * from './input-setter';
|
||||
@@ -1,64 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, waitFor, fireEvent } from '@testing-library/react';
|
||||
import { InputSetter } from './index';
|
||||
|
||||
describe('InputSetter Component', () => {
|
||||
it('should show the correct value and visibility based on props', async () => {
|
||||
const value = 'Hello';
|
||||
const isInputVisible = true;
|
||||
const { getByRole } = await render(
|
||||
<InputSetter
|
||||
id="input-order-size-market"
|
||||
type="number"
|
||||
className="w-full"
|
||||
value={value}
|
||||
isInputVisible={isInputVisible}
|
||||
onChange={() => false}
|
||||
/>
|
||||
);
|
||||
const input = getByRole('spinbutton');
|
||||
const btn = getByRole('button');
|
||||
expect(input).toHaveAttribute('value', value);
|
||||
expect(btn).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should toggle the visibility of the input when the button is clicked', async () => {
|
||||
const value = 'Hello';
|
||||
const isInputVisible = true;
|
||||
const { getByRole } = await render(
|
||||
<InputSetter
|
||||
id="input-order-size-market"
|
||||
type="number"
|
||||
className="w-full"
|
||||
value={value}
|
||||
isInputVisible={isInputVisible}
|
||||
onChange={() => false}
|
||||
/>
|
||||
);
|
||||
const btn = getByRole('button');
|
||||
fireEvent.click(btn);
|
||||
await waitFor(() => getByRole('button'));
|
||||
expect(getByRole('button')).toHaveTextContent(value);
|
||||
});
|
||||
|
||||
it('should toggle the visibility of the input when the enter key is pressed', async () => {
|
||||
const value = 'Hello';
|
||||
const isInputVisible = true;
|
||||
const { getByRole } = await render(
|
||||
<InputSetter
|
||||
id="input-order-size-market"
|
||||
name="input-order-size-market"
|
||||
type="number"
|
||||
className="w-full"
|
||||
value={value}
|
||||
isInputVisible={isInputVisible}
|
||||
onChange={() => false}
|
||||
/>
|
||||
);
|
||||
fireEvent.keyDown(getByRole('spinbutton'), {
|
||||
key: 'Enter',
|
||||
});
|
||||
await waitFor(() => getByRole('button'));
|
||||
expect(getByRole('button')).toHaveTextContent(value);
|
||||
});
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Input } from '@vegaprotocol/ui-toolkit';
|
||||
import type { InputProps } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface InputSetterProps {
|
||||
buttonLabel?: string;
|
||||
isInputVisible?: boolean;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export const InputSetter = ({
|
||||
buttonLabel = t('set'),
|
||||
isInputVisible = false,
|
||||
children,
|
||||
...props
|
||||
}: InputSetterProps & InputProps) => {
|
||||
const [isInputToggled, setIsInputToggled] = useState(isInputVisible);
|
||||
|
||||
const toggleInput = useCallback(() => {
|
||||
setIsInputToggled(!isInputToggled);
|
||||
}, [isInputToggled]);
|
||||
|
||||
const onInputEnter = useCallback(
|
||||
(event: React.KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.stopPropagation();
|
||||
toggleInput();
|
||||
}
|
||||
},
|
||||
[toggleInput]
|
||||
);
|
||||
|
||||
return isInputToggled ? (
|
||||
<div className="flex items-center">
|
||||
<Input {...props} onKeyDown={onInputEnter} />
|
||||
<button
|
||||
type="button"
|
||||
className="no-underline hover:underline text-blue ml-2"
|
||||
onClick={toggleInput}
|
||||
>
|
||||
{buttonLabel}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="no-underline hover:underline text-blue py-1.5"
|
||||
onClick={toggleInput}
|
||||
>
|
||||
{children || props.value}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
import { routerConfig } from '../../routes';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { NavItem } from './nav-item';
|
||||
import React from 'react';
|
||||
|
||||
interface NavProps {
|
||||
className?: string;
|
||||
tabs?: boolean;
|
||||
}
|
||||
|
||||
export const Nav = ({ className, tabs = false }: NavProps) => {
|
||||
return (
|
||||
<nav role={tabs ? 'tablist' : 'menu'} className={className}>
|
||||
{routerConfig
|
||||
.filter((r) => r.isNavItem)
|
||||
.map((r) => (
|
||||
<NavLink
|
||||
role={tabs ? 'tab' : 'menuitem'}
|
||||
key={r.name}
|
||||
to={r.path}
|
||||
className={({ isActive }) =>
|
||||
`text-base block md:mb-10 px-12 md:text-black md:dark:text-white ${
|
||||
isActive && 'text-white md:text-blue md:dark:text-blue'
|
||||
}`
|
||||
}
|
||||
>
|
||||
<NavItem iconName={r.icon} label={r.text} />
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { PriceCell, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import type {
|
||||
AccountFieldsFragment,
|
||||
AccountEventsSubscription,
|
||||
} from '@vegaprotocol/accounts';
|
||||
import {
|
||||
accountsDataProvider,
|
||||
accountsManagerUpdate,
|
||||
getId,
|
||||
} from '@vegaprotocol/accounts';
|
||||
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 '.';
|
||||
|
||||
interface AccountObj extends AccountFieldsFragment {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const AccountsManager = () => {
|
||||
const { partyId = '' } = useOutletContext<{ partyId: string }>();
|
||||
const { isOpen, symbol, setOpen } = useAssetDetailsDialogStore();
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||
const update = useMemo(() => accountsManagerUpdate(gridRef), []);
|
||||
const { data, error, loading } = useDataProvider<
|
||||
AccountFieldsFragment[],
|
||||
AccountEventsSubscription['accounts']
|
||||
>({ dataProvider: accountsDataProvider, update, variables });
|
||||
const { columnDefs, defaultColDef } = useAccountColumnDefinitions();
|
||||
return (
|
||||
<>
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={data}
|
||||
noDataMessage={NO_DATA_MESSAGE}
|
||||
>
|
||||
<ConsoleLiteGrid<AccountObj>
|
||||
rowData={data as AccountObj[]}
|
||||
columnDefs={columnDefs}
|
||||
defaultColDef={defaultColDef}
|
||||
components={{ PriceCell }}
|
||||
getRowId={({ data }) => getId(data)}
|
||||
/>
|
||||
</AsyncRenderer>
|
||||
<AssetDetailsDialog
|
||||
assetSymbol={symbol}
|
||||
open={isOpen}
|
||||
onChange={(open) => setOpen(open)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccountsManager;
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default as AccountManager } from './accounts';
|
||||
export { default as useAccountColumnDefinitions } from './use-column-definitions';
|
||||
@@ -1,111 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
|
||||
import type { SummaryRow } from '@vegaprotocol/react-helpers';
|
||||
import type { AccountFieldsFragment } from '@vegaprotocol/accounts';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import type {
|
||||
ColDef,
|
||||
GroupCellRendererParams,
|
||||
ValueFormatterParams,
|
||||
} from 'ag-grid-community';
|
||||
import type { AccountType } from '@vegaprotocol/types';
|
||||
import { AccountTypeMapping } from '@vegaprotocol/types';
|
||||
|
||||
interface AccountsTableValueFormatterParams extends ValueFormatterParams {
|
||||
data: AccountFieldsFragment;
|
||||
}
|
||||
|
||||
const comparator = (
|
||||
valueA: string,
|
||||
valueB: string,
|
||||
nodeA: { data: AccountFieldsFragment & SummaryRow },
|
||||
nodeB: { data: AccountFieldsFragment & SummaryRow },
|
||||
isInverted: boolean
|
||||
) => {
|
||||
if (valueA < valueB) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (valueA > valueB) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nodeA.data.__summaryRow) {
|
||||
return isInverted ? -1 : 1;
|
||||
}
|
||||
|
||||
if (nodeB.data.__summaryRow) {
|
||||
return isInverted ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
const useAccountColumnDefinitions = () => {
|
||||
const { open } = useAssetDetailsDialogStore();
|
||||
const columnDefs: ColDef[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
colId: 'account-asset',
|
||||
headerName: t('Asset'),
|
||||
field: 'asset.symbol',
|
||||
comparator,
|
||||
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: 'type',
|
||||
headerName: t('Type'),
|
||||
field: 'type',
|
||||
cellClass: 'uppercase !flex h-full items-center',
|
||||
valueFormatter: ({ value }: ValueFormatterParams) =>
|
||||
value ? AccountTypeMapping[value as AccountType] : '-',
|
||||
},
|
||||
{
|
||||
colId: 'market',
|
||||
headerName: t('Market'),
|
||||
cellClass: 'uppercase !flex h-full items-center',
|
||||
field: 'market.tradableInstrument.instrument.name',
|
||||
valueFormatter: "value || '—'",
|
||||
},
|
||||
{
|
||||
colId: 'balance',
|
||||
headerName: t('Balance'),
|
||||
field: 'balance',
|
||||
cellClass: 'uppercase !flex h-full items-center',
|
||||
cellRenderer: 'PriceCell',
|
||||
valueFormatter: ({ value, data }: AccountsTableValueFormatterParams) =>
|
||||
addDecimalsFormatNumber(value, data.asset.decimals),
|
||||
},
|
||||
];
|
||||
}, [open]);
|
||||
|
||||
const defaultColDef = useMemo(() => {
|
||||
return {
|
||||
sortable: true,
|
||||
unSortIcon: true,
|
||||
headerClass: 'uppercase',
|
||||
editable: false,
|
||||
};
|
||||
}, []);
|
||||
return { columnDefs, defaultColDef };
|
||||
};
|
||||
|
||||
export default useAccountColumnDefinitions;
|
||||
@@ -1,35 +0,0 @@
|
||||
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}`,
|
||||
},
|
||||
];
|
||||
@@ -1,53 +0,0 @@
|
||||
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;
|
||||
@@ -1 +0,0 @@
|
||||
export { default as FillsManager } from './fills';
|
||||
@@ -1,190 +0,0 @@
|
||||
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,6 +0,0 @@
|
||||
export * from './portfolio';
|
||||
export * from './accounts';
|
||||
export * from './fills';
|
||||
export * from './orders';
|
||||
export * from './positions';
|
||||
export * as constants from './constants';
|
||||
@@ -1 +0,0 @@
|
||||
export { default as OrdersManager } from './orders';
|
||||
@@ -1,101 +0,0 @@
|
||||
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;
|
||||
@@ -1,287 +0,0 @@
|
||||
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,27 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import ConnectWallet from '../wallet-connector';
|
||||
import { useOutlet, useLocation } from 'react-router-dom';
|
||||
import { HorizontalMenu } from '../horizontal-menu';
|
||||
import * as constants from './constants';
|
||||
|
||||
export const Portfolio = () => {
|
||||
const { keypair } = useVegaWallet();
|
||||
const { pathname } = useLocation();
|
||||
const module = pathname.split('/portfolio/')?.[1] ?? '';
|
||||
const outlet = useOutlet({ partyId: keypair?.pub || '' });
|
||||
if (!keypair) {
|
||||
return (
|
||||
<section className="xl:w-1/2">
|
||||
<ConnectWallet />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full p-4 md:p-6 grid grid-rows-[min-content_1fr]">
|
||||
<HorizontalMenu active={module} items={constants.PORTFOLIO_ITEMS} />
|
||||
{outlet}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { default as PositionsManager } from './positions';
|
||||
@@ -1,58 +0,0 @@
|
||||
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;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user