Compare commits

..
80 changed files with 663 additions and 2336 deletions
+120 -25
View File
@@ -81,22 +81,6 @@ jobs:
with:
main-branch-name: develop
# See affected apps
- name: See affected apps
run: |
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
python3 tools/ci/check-affected.py --github-ref="${{ github.ref }}" --branch-slug="$branch_slug" --event-name="${{ github.event_name }}"
- name: Verify script result
run: |
echo "Check outputs from script"
echo "projects: ${{ env.PROJECTS }}"
echo "projects-e2e: ${{ env.PROJECTS_E2E }}"
echo "preview_governance: ${{ env.PREVIEW_GOVERNANCE }}"
echo "preview_trading: ${{ env.PREVIEW_TRADING }}"
echo "preview_explorer: ${{ env.PREVIEW_EXPLORER }}"
echo "preview_tools: ${{ env.PREVIEW_TOOLS }}"
- name: Check formatting
run: yarn nx format:check
@@ -112,6 +96,126 @@ jobs:
- name: Build affected
run: yarn nx affected:build || (yarn install && yarn nx affected:build)
# See affected apps
- name: See affected apps
run: |
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
echo ">>>> debug"
echo "NX_BASE: ${{ env.NX_BASE }}"
echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo "Affected: ${affected}"
echo "Branch slug: ${branch_slug}"
echo "Current ref: ${{ github.ref }}"
echo ">>>> eof debug"
projects_array=()
preview_governance="not deployed"
preview_trading="not deployed"
preview_explorer="not deployed"
preview_tools="not deployed"
# parse if affected is any of three main applications, if none - use all of them
if echo "$affected" | grep -q governance; then
echo "Governance is affected"
projects_array+=("governance")
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi
if echo "$affected" | grep -q trading; then
echo "Trading is affected"
projects_array+=("trading")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
fi
if echo "$affected" | grep -q explorer; then
echo "Explorer is affected"
projects_array+=("explorer")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
if [[ ${#projects_array[@]} -eq 0 ]]; then
projects_array=("governance" "trading" "explorer")
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
# applications parsed before this loop are applicable for running e2e-tests
projects_e2e_array=()
for project in "${projects_array[@]}"; do
projects_e2e_array+=("${project}-e2e")
done
# all applications below this loop are not applicable for running e2e-test
# check if pull request event to deploy tools
if [[ "${{ github.event_name }}" = "pull_request" ]]; then
if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected"
echo "Deploying tools on preview"
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
projects_array+=("multisig-signer")
fi
# those apps deploy only from develop to mainnet
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected"
echo "Deploying tools on s3"
projects_array+=("multisig-signer")
fi
if echo "$affected" | grep -q static; then
echo "static is affected"
echo "Deploying static on s3"
projects_array+=("static")
fi
if echo "$affected" | grep -q ui-toolkit; then
echo "ui-toolkit is affected"
echo "Deploying ui-toolkit on s3"
projects_array+=("ui-toolkit")
fi
fi
# if branch starts with release/ and ends with trading / governance or explorer - overwrite the array of affected projects with fixed single application
if [[ "${{ github.ref }}" == *release* ]]; then
echo ">> This is a relase branch"
case "${{ github.ref }}" in
*trading)
echo ">> Only trading will be deployed"
projects_array=(trading)
projects_e2e_array=(trading)
;;
*governance)
echo ">> Only governance will be deployed"
projects_array=(governance)
projects_e2e_array=(governance)
;;
*explorer)
echo ">> Only explorer will be deployed"
projects_array=(explorer)
projects_e2e_array=(explorer)
;;
*)
echo ">> All apps will be deployed"
;;
esac
fi
echo "Projects: ${projects_array[@]}"
echo "Projects E2E: ${projects_e2e_array[@]}"
projects_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_array[@]}")
projects_e2e_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_e2e_array[@]}")
echo PROJECTS_E2E=$projects_e2e_json >> $GITHUB_ENV
echo PROJECTS=$projects_json >> $GITHUB_ENV
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
echo PREVIEW_TOOLS=$preview_tools >> $GITHUB_ENV
outputs:
projects: ${{ env.PROJECTS }}
projects-e2e: ${{ env.PROJECTS_E2E }}
@@ -120,15 +224,6 @@ jobs:
preview_explorer: ${{ env.PREVIEW_EXPLORER }}
preview_tools: ${{ env.PREVIEW_TOOLS }}
console-e2e:
needs: lint-test-build
name: '(CI) console python'
uses: ./.github/workflows/console-test-run.yml
secrets: inherit
if: ${{ contains(fromJSON(needs.lint-test-build.outputs.projects), 'trading') && github.event_name == 'pull_request' }}
with:
github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
cypress:
needs: lint-test-build
name: '(CI) cypress'
+27 -110
View File
@@ -1,138 +1,55 @@
name: (CI) Console tests
name: console-test-run
on:
workflow_call:
inputs:
github-sha:
required: true
type: string
jobs:
run-tests:
name: run-tests
runs-on: ubuntu-22.04
timeout-minutes: 25
console-test:
timeout-minutes: 5
runs-on: self-hosted-runner
steps:
#----------------------------------------------
# check-out frontend-monorepo
#----------------------------------------------
- name: Checkout console test repo
uses: actions/checkout@v3
with:
ref: ${{ inputs.github-sha }}
#----------------------------------------------
# cache node modules
#----------------------------------------------
- name: Cache node modules
id: cache
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
# comment out "restore-keys" if you need to rebuild yarn from 0
restore-keys: |
${{ runner.os }}-cache-node-modules-
#----------------------------------------------
# setup node
#----------------------------------------------
- name: Setup node
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
#----------------------------------------------
# install deps if cache missing
#----------------------------------------------
- name: yarn install
if: steps.cache.outputs.cache-hit != 'true'
run: yarn install --pure-lockfile
#----------------------------------------------
# build trading
#----------------------------------------------
- name: Build affected spec
run: |
yarn env-cmd -f ./apps/trading/.env.stagnet1 yarn nx export trading
#----------------------------------------------
# run trading server
#----------------------------------------------
- name: Run trading server
run: |
docker run -d -p 80:4200 -v $PWD/docker/nginx.conf:/etc/nginx/conf.d/default.conf -v $PWD/dist/apps/trading/exported:/usr/share/nginx/html nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
sleep 5
docker ps
#----------------------------------------------
# check if container persists between runs
#----------------------------------------------
- name: Check server
run: |
docker ps
#----------------------------------------------
# check-out tests repo
#----------------------------------------------
- name: Checkout console test repo
uses: actions/checkout@v3
with:
repository: vegaprotocol/console-test
path: './console-test'
#----------------------------------------------
# set-up python
#----------------------------------------------
- name: Set up python
id: setup-python
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10.11'
#----------------------------------------------
# ----- install & configure poetry -----
#----------------------------------------------
- name: Install Poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
virtualenvs-path: console-test/.venv
#----------------------------------------------
# load cached venv if cache exists
#----------------------------------------------
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v3
with:
path: console-test/.venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
#----------------------------------------------
# install dependencies if cache does not exist
#----------------------------------------------
- name: Install dependencies
run: |
poetry install --no-root
working-directory: ./console-test
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root
#----------------------------------------------
# install vega binaries
#----------------------------------------------
- name: Install vega binaries
- name: load Binaries
run: |
poetry run python -m vega_sim.tools.load_binaries
working-directory: ./console-test
run: poetry run python -m vega_sim.tools.load_binaries --force
#----------------------------------------------
# install playwright
#----------------------------------------------
- name: pull console
run: |
poetry run docker pull ghcr.io/vegaprotocol/frontend/trading:${{ inputs.github-sha }}
- name: Update container_name in config.py
run: |
sed -i "s/container_name = \".*\"/container_name = \"vegaprotocol\/frontend\/trading:${{ inputs.github-sha }}\"/g" config.py
- name: install playwright
run: poetry run playwright install
working-directory: ./console-test
#----------------------------------------------
# run tests
#----------------------------------------------
- name: Run tests
working-directory: ./console-test
- name: run tests
run: poetry run pytest --numprocesses auto
- name: Check files
run: |
ls -al .
ls -al console-test
#----------------------------------------------
# upload traces
#----------------------------------------------
working-directory: ./console-test
- name: Upload Playwright Trace
uses: actions/upload-artifact@v3
if: always()
+46 -11
View File
@@ -36,7 +36,7 @@ jobs:
echo IS_PR=true >> $GITHUB_ENV
- name: Is mainnet release
if: ${{ contains(github.ref, 'release/mainnet') && !contains(github.ref, 'mirror') }}
if: ${{ contains(github.ref, 'release/mainnnet') && !contains(github.ref, 'mirror') }}
run: |
echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV
@@ -97,13 +97,52 @@ jobs:
- name: Define dist variables
if: ${{ github.event_name == 'push' }}
run: |
python3 tools/ci/define-dist-variables.py --github-ref="${{ github.ref }}" --app="${{ matrix.app }}"
envName=''
domain="vega.rocks"
bucketName=''
- name: Verify script result
if: ${{ github.event_name == 'push' }}
run: |
echo "BUCKET_NAME=${{ env.BUCKET_NAME }}"
echo "ENV_NAME=${{ env.ENV_NAME }}"
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
# remove prefixing release/ and take the first string limited by - which is supposed to be name of the environment for releasing (format: release/testnet-trading)
envName="$(echo ${{ github.ref }} | sed -e "s|refs/heads/release/||" | cut -d '-' -f 1 )"
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then
envName="mainnet"
bucketName="tools.vega.xyz"
fi
if [[ "${{ matrix.app }}" = "static" ]]; then
envName="mainnet"
bucketName="static.vega.xyz"
fi
if [[ "${{ matrix.app }}" = "ui-toolkit" ]]; then
envName="mainnet"
bucketName="ui.vega.rocks"
fi
elif [[ "${{ github.ref }}" =~ .*mainnet$ ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${domain}"
fi
elif [[ "${envName}" = "testnet" ]]; then
domain="fairground.wtf"
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${domain}"
fi
fi
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${envName}.${domain}"
fi
echo "bucket name: ${bucketName}"
echo "env name: ${envName}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
echo ENV_NAME=${envName} >> $GITHUB_ENV
- name: Build local dist
run: |
@@ -118,12 +157,8 @@ jobs:
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
NODE_ENV=production yarn nx run ui-toolkit:build-storybook
DIST_LOCATION=dist/storybook/ui-toolkit
elif [ "${{ matrix.app }}" = "static" ]; then
yarn nx build static || (yarn install && yarn nx build static)
else
$envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }})
fi
if [[ -z "$DIST_LOCATION" ]]; then
DIST_LOCATION=dist/apps/${{ matrix.app }}
fi
mv $DIST_LOCATION dist-result
@@ -7,11 +7,7 @@ context('Validator page', { tags: '@smoke' }, function () {
it('should be able to see validator tiles', function () {
cy.getNodes().then((nodes) => {
nodes.forEach((node) => {
if (node.rankingScore.performanceScore > 0) {
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
} else {
cy.get(`[validator-id="${node.id}"]`).should('not.exist');
}
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
});
});
});
@@ -15,9 +15,6 @@ query ExplorerNodes {
stakedByDelegates
stakedTotal
pendingStake
rankingScore {
performanceScore
}
epochData {
total
offline
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
export type ExplorerNodesQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerNodesQuery = { __typename?: 'Query', nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, infoUrl: string, avatarUrl?: string | null, pubkey: string, tmPubkey: string, ethereumAddress: string, location: string, status: Types.NodeStatus, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, rankingScore: { __typename?: 'RankingScore', performanceScore: string }, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null } } | null> | null } };
export type ExplorerNodesQuery = { __typename?: 'Query', nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, infoUrl: string, avatarUrl?: string | null, pubkey: string, tmPubkey: string, ethereumAddress: string, location: string, status: Types.NodeStatus, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null } } | null> | null } };
export const ExplorerNodesDocument = gql`
@@ -27,9 +27,6 @@ export const ExplorerNodesDocument = gql`
stakedByDelegates
stakedTotal
pendingStake
rankingScore {
performanceScore
}
epochData {
total
offline
@@ -91,15 +91,7 @@ export const ValidatorsPage = () => {
const { data: tmData } = useTendermintValidators(5000);
const { data, loading, error, refetch } = useExplorerNodesQuery();
const validators = compact(
data?.nodesConnection.edges
?.map((e) => e?.node)
.filter(
(node) =>
node?.rankingScore?.performanceScore &&
new BigNumber(node.rankingScore.performanceScore).isGreaterThan(0)
)
);
const validators = compact(data?.nodesConnection.edges?.map((e) => e?.node));
// voting power
const powers = compact(tmData?.result.validators).map(
@@ -218,33 +218,17 @@ describe(
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
// 3002-PROP-021
cy.getByTestId('user-voted-yes').should('exist');
cy.getByTestId(viewProposalButton).click();
});
});
cy.getByTestId(changeVoteButton).should('be.visible').click();
voteForProposal('against');
cy.getByTestId(proposalVoteProgressAgainstPercentage)
.contains('100.00%')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Tokens against proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
getProposalInformationFromTable('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
cy.getByTestId('user-voted-no').should('exist');
cy.getByTestId(viewProposalButton).click();
});
});
});
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
@@ -26,8 +26,6 @@ const votesTable = 'votes-table';
const openProposals = 'open-proposals';
const proposalVoteProgressForPercentage =
'vote-progress-indicator-percentage-for';
const majorityVoteReached = 'majority-reached';
const minParticipationReached = 'participation-reached';
const proposalTimeout = { timeout: 8000 };
context(
@@ -74,7 +72,7 @@ context(
});
});
// 3001-VOTE-020 3001-VOTE-021 3001-VOTE-046 3001-VOTE-044 3001-VOTE-074 3001-VOTE-074
// 3001-VOTE-046 3001-VOTE-044 3001-VOTE-074 3001-VOTE-074
it('Able to enact proposal by voting', function () {
const proposalTitle = 'Add New proposal with short enactment';
const proposalTx = createUpdateNetworkProposalTxBody();
@@ -87,18 +85,10 @@ context(
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.last()
.within(() => {
// 3001-VOTE-019 time to vote is highlighted red
cy.getByTestId('vote-details')
.find('span')
.should('have.class', 'text-vega-pink');
cy.getByTestId(viewProposalButton).click();
});
.within(() => cy.getByTestId(viewProposalButton).click());
});
cy.getByTestId(proposalStatus).should('have.text', 'Open');
voteForProposal('for');
cy.getByTestId(majorityVoteReached).should('exist');
cy.getByTestId(minParticipationReached).should('exist');
cy.getByTestId(proposalStatus, proposalTimeout)
.should('have.text', 'Passed')
.then(() => {
@@ -114,14 +104,6 @@ context(
cy.getByTestId(proposalVoteProgressForPercentage)
.contains('100.00%')
.and('be.visible');
navigateTo(navigation.proposals);
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.last()
.within(() => {
cy.getByTestId(majorityVoteReached).should('exist');
cy.getByTestId(minParticipationReached).should('exist');
});
});
// 3001-VOTE-047
@@ -49,8 +49,8 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
navigateTo(navigation.proposals);
});
// 3001-VOTE-018
it('Newly created proposals list - proposals closest to closing date appear higher in list', function () {
// 3001-VOTE-005
const proposalDays = [364, 50, 2];
for (let index = 0; index < proposalDays.length; index++) {
goToMakeNewProposal(governanceProposalType.RAW);
@@ -120,24 +120,16 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
submitUniqueRawProposal({ proposalTitle: proposalTitle });
getProposalFromTitle(proposalTitle).within(() => {
// 3001-VOTE-039
cy.getByTestId('participation-not-reached').should(
cy.getByTestId(voteStatus).should(
'have.text',
'Min. participation not reached'
'Participation not reached'
);
cy.getByTestId(viewProposalButton).click();
});
voteForProposal('for');
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() => {
cy.getByTestId(voteStatus).should(
'have.text',
'Currently expected to pass'
);
cy.getByTestId('user-voted-yes').should('exist');
cy.getByTestId('participation-reached').should(
'have.text',
'Min. participation reached'
);
cy.getByTestId(voteStatus).should('have.text', 'Set to pass');
cy.getByTestId(viewProposalButton).click();
});
cy.getByTestId(voteBreakDownToggle).click();
@@ -351,12 +351,6 @@ context(
);
stakingPageDisassociateAllTokens();
});
it('Able to associate over 1 million tokens', function () {
cy.get(ethWalletAssociateButton).click();
stakingPageAssociateTokens('2000000', { approve: true });
stakingPageDisassociateAllTokens();
});
});
}
);
@@ -17,7 +17,6 @@ const governanceDocsUrl = 'https://vega.xyz/governance';
const networkUpgradeProposalListItem = 'protocol-upgrade-proposals-list-item';
const closedProposals = 'closed-proposals';
const closedProposalToggle = 'closed-proposals-toggle-networkUpgrades';
const protocolUpgradeTime = 'protocol-upgrade-time';
context(
'Governance Page - verify elements on page',
@@ -175,7 +174,6 @@ context(
});
});
// 3009-NTWU-003 3009-NTWU-004 3009-NTWU-007
it('should see details of network upgrade proposal', function () {
mockNetworkUpgradeProposal();
navigateTo(navigation.proposals);
@@ -230,7 +228,6 @@ context(
cy.getByTestId(closedProposalToggle).should('not.exist');
});
// 3009-NTWU-001 3009-NTWU-002 3009-NTWU-006 3009-NTWU-009
it('should display network upgrade banner with estimate', function () {
mockNetworkUpgradeProposal();
cy.visit('/');
@@ -262,18 +259,10 @@ context(
.as('displayedEstimate');
cy.get('@displayedEstimate').then((estimateText) => {
// Estimated time should automatically update every second
cy.getByTestId(protocolUpgradeTime)
cy.getByTestId('protocol-upgrade-time')
.invoke('text')
.should('not.eq', estimateText);
});
// time estimate on proposal detail
cy.getByTestId('protocol-upgrade-proposal').within(() => {
cy.get('@displayedEstimate').then((estimateText) => {
cy.getByTestId(protocolUpgradeTime)
.invoke('text')
.should('not.eq', estimateText);
});
});
});
}
);
-2
View File
@@ -25,5 +25,3 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
CYPRESS_FAIRGROUND=false
LC_ALL="en_US.UTF-8"
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
-2
View File
@@ -25,5 +25,3 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
#Test configuration variables
CYPRESS_FAIRGROUND=false
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false
-3
View File
@@ -16,6 +16,3 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
-3
View File
@@ -17,6 +17,3 @@ NX_VEGA_REST_URL=https://api.vega.community/api/v2/
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false
-3
View File
@@ -16,6 +16,3 @@ NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false
+1 -4
View File
@@ -11,7 +11,4 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
+1 -4
View File
@@ -16,7 +16,4 @@ NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
+1 -4
View File
@@ -13,7 +13,4 @@ NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
@@ -1 +0,0 @@
export * from './json-diff';
@@ -1,30 +0,0 @@
import { render, screen, cleanup } from '@testing-library/react';
import { JsonDiff } from './json-diff';
describe('JsonDiff', () => {
afterEach(cleanup);
it('renders without crashing', () => {
render(<JsonDiff left={{}} right={{}} />);
expect(screen.getByTestId('json-diff')).toBeInTheDocument();
});
it('shows the correct message when both objects are identical', () => {
render(<JsonDiff left={{}} right={{}} />);
expect(screen.getByText('Data is identical')).toBeInTheDocument();
});
it('does not show the "identical" message when both objects are not identical', () => {
render(
<JsonDiff
left={{
name: 'test',
}}
right={{
name: 'test2',
}}
/>
);
expect(screen.queryByText('Data is identical')).not.toBeInTheDocument();
});
});
@@ -1,46 +0,0 @@
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { formatters, create } from 'jsondiffpatch';
import 'jsondiffpatch/dist/formatters-styles/html.css';
import 'jsondiffpatch/dist/formatters-styles/annotated.css';
export type JsonValue =
| string
| number
| boolean
| null
| JsonValue[]
| { [key: string]: JsonValue };
interface JsonDiffProps {
left: JsonValue;
right: JsonValue;
objectHash?: (obj: unknown) => string | undefined;
}
export const JsonDiff = ({ right, left, objectHash }: JsonDiffProps) => {
const { t } = useTranslation();
const [html, setHtml] = useState<string | undefined>();
useEffect(() => {
const delta = create({
objectHash,
}).diff(left, right);
if (delta) {
const deltaHtml = formatters.html.format(delta, left);
formatters.html.hideUnchanged();
setHtml(deltaHtml);
} else {
setHtml(undefined);
}
}, [right, left, objectHash]);
return html ? (
<div data-testid="json-diff" dangerouslySetInnerHTML={{ __html: html }} />
) : (
<p data-testid="json-diff">{t('dataIsIdentical')}</p>
);
};
@@ -853,7 +853,5 @@
"consensusNodes": "consensus nodes",
"activeNodes": "active nodes",
"Estimated time to upgrade": "Estimated time to upgrade",
"Upgraded at": "Upgraded at",
"dataIsIdentical": "Data is identical",
"updatesToMarket": "Updates to market"
"Upgraded at": "Upgraded at"
}
@@ -22,16 +22,6 @@ import {
} from '../../test-helpers/mocks';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { MockedResponse } from '@apollo/client/testing';
import { FLAGS } from '@vegaprotocol/environment';
import { BrowserRouter } from 'react-router-dom';
jest.mock('@vegaprotocol/proposals', () => ({
...jest.requireActual('@vegaprotocol/proposals'),
useSuccessorMarketProposalDetails: () => ({
code: 'PARENT_CODE',
parentMarketId: 'PARENT_ID',
}),
}));
const renderComponent = (
proposal: ProposalQuery['proposal'],
@@ -40,27 +30,20 @@ const renderComponent = (
) =>
render(
<AppStateProvider>
<BrowserRouter>
<MockedProvider mocks={mocks}>
<VegaWalletContext.Provider value={mockWalletContext}>
<ProposalHeader
proposal={proposal}
isListItem={isListItem}
networkParams={mockNetworkParams}
/>
</VegaWalletContext.Provider>
</MockedProvider>
</BrowserRouter>
<MockedProvider mocks={mocks}>
<VegaWalletContext.Provider value={mockWalletContext}>
<ProposalHeader
proposal={proposal}
isListItem={isListItem}
networkParams={mockNetworkParams}
/>
</VegaWalletContext.Provider>
</MockedProvider>
</AppStateProvider>
);
describe('Proposal header', () => {
afterAll(() => {
jest.clearAllMocks();
});
it('Renders New market proposal', () => {
const mockedFlags = jest.mocked(FLAGS);
mockedFlags.SUCCESSOR_MARKETS = true;
renderComponent(
generateProposal({
rationale: {
@@ -93,9 +76,6 @@ describe('Proposal header', () => {
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
'tGBP settled future.'
);
expect(screen.getByTestId('proposal-successor-info')).toHaveTextContent(
'PARENT_CODE'
);
});
it('Renders Update market proposal', () => {
@@ -11,10 +11,6 @@ import { ProposalInfoLabel } from '../proposal-info-label';
import { useUserVote } from '../vote-details/use-user-vote';
import { ProposalVotingStatus } from '../proposal-voting-status';
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
import { FLAGS } from '@vegaprotocol/environment';
import Routes from '../../../routes';
import { Link } from 'react-router-dom';
export const ProposalHeader = ({
proposal,
@@ -43,9 +39,6 @@ export const ProposalHeader = ({
fallbackTitle = t('NewMarketProposal');
details = (
<>
{FLAGS.SUCCESSOR_MARKETS && (
<SuccessorCode proposalId={proposal?.id} />
)}
<span>
{t('Code')}: {change.instrument.code}.
</span>{' '}
@@ -145,7 +138,7 @@ export const ProposalHeader = ({
className="flex items-center gap-2"
data-testid={`user-voted-${voteState.toLowerCase()}`}
>
<div className="text-vega-green" data-testid="you-voted-icon">
<div className="text-vega-green">
<VegaIcon name={VegaIconNames.VOTE} size={24} />
</div>
<div>
@@ -188,20 +181,3 @@ export const ProposalHeader = ({
</>
);
};
const SuccessorCode = ({ proposalId }: { proposalId?: string | null }) => {
const { t } = useTranslation();
const successor = useSuccessorMarketProposalDetails(proposalId);
return successor.parentMarketId || successor.code ? (
<span className="block" data-testid="proposal-successor-info">
{t('Successor market to')}:{' '}
<Link
to={`${Routes.PROPOSALS}/${successor.parentMarketId}`}
className="hover:underline"
>
{successor.code || successor.parentMarketId}
</Link>
</span>
) : null;
};
@@ -1 +0,0 @@
export * from './proposal-market-changes';
@@ -1,91 +0,0 @@
import { render, fireEvent } from '@testing-library/react';
import {
ProposalMarketChanges,
applyImmutableKeysFromEarlierVersion,
} from './proposal-market-changes';
import type { JsonValue } from '../../../../components/json-diff';
describe('applyImmutableKeysFromEarlierVersion', () => {
it('returns an empty object if any argument is not an object or null', () => {
const earlierVersion: JsonValue = null;
const updatedVersion: JsonValue = null;
expect(
applyImmutableKeysFromEarlierVersion(earlierVersion, updatedVersion)
).toEqual({});
});
it('overrides updatedVersion with values from earlierVersion for immutable keys', () => {
const earlierVersion: JsonValue = {
decimalPlaces: 2,
positionDecimalPlaces: 3,
instrument: {
name: 'Instrument1',
future: {
settlementAsset: 'Asset1',
},
},
};
const updatedVersion: JsonValue = {
decimalPlaces: 3, // should be overridden by 2
positionDecimalPlaces: 4, // should be overridden by 3
instrument: {
name: 'Instrument2', // should be overridden by 'Instrument1'
future: {
settlementAsset: 'Asset2', // should be overridden by 'Asset1'
},
},
};
const expected: JsonValue = {
decimalPlaces: 2,
positionDecimalPlaces: 3,
instrument: {
name: 'Instrument1',
future: {
settlementAsset: 'Asset1',
},
},
};
expect(
applyImmutableKeysFromEarlierVersion(earlierVersion, updatedVersion)
).toEqual(expected);
});
});
describe('ProposalMarketChanges', () => {
it('renders correctly', () => {
const { getByTestId } = render(
<ProposalMarketChanges
originalProposal={{}}
latestEnactedProposal={{}}
updatedProposal={{}}
/>
);
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
});
it('JsonDiff is not visible when showChanges is false', () => {
const { queryByTestId } = render(
<ProposalMarketChanges
originalProposal={{}}
latestEnactedProposal={{}}
updatedProposal={{}}
/>
);
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
});
it('JsonDiff is visible when showChanges is true', async () => {
const { getByTestId } = render(
<ProposalMarketChanges
originalProposal={{}}
latestEnactedProposal={{}}
updatedProposal={{}}
/>
);
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
expect(getByTestId('json-diff')).toBeInTheDocument();
});
});
@@ -1,86 +0,0 @@
import cloneDeep from 'lodash/cloneDeep';
import set from 'lodash/set';
import get from 'lodash/get';
import { JsonDiff } from '../../../../components/json-diff';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { SubHeading } from '../../../../components/heading';
import type { JsonValue } from '../../../../components/json-diff';
const immutableKeys = [
'decimalPlaces',
'positionDecimalPlaces',
'instrument.name',
'instrument.future.settlementAsset',
];
export const applyImmutableKeysFromEarlierVersion = (
earlierVersion: JsonValue,
updatedVersion: JsonValue
) => {
if (
typeof earlierVersion !== 'object' ||
earlierVersion === null ||
typeof updatedVersion !== 'object' ||
updatedVersion === null
) {
// If either version is not an object or is null, return null or throw an error
return {};
}
const updatedVersionCopy = cloneDeep(updatedVersion);
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
immutableKeys.forEach((key) => {
set(updatedVersionCopy, key, get(earlierVersion, key));
});
return updatedVersionCopy;
};
interface ProposalMarketChangesProps {
originalProposal: JsonValue;
latestEnactedProposal: JsonValue | undefined;
updatedProposal: JsonValue;
}
export const ProposalMarketChanges = ({
originalProposal,
latestEnactedProposal,
updatedProposal,
}: ProposalMarketChangesProps) => {
const { t } = useTranslation();
const [showChanges, setShowChanges] = useState(false);
return (
<section data-testid="proposal-market-changes">
<CollapsibleToggle
toggleState={showChanges}
setToggleState={setShowChanges}
dataTestId={'proposal-market-changes-toggle'}
>
<SubHeading title={t('updatesToMarket')} />
</CollapsibleToggle>
{showChanges && (
<div className="mb-6">
<JsonDiff
left={latestEnactedProposal || originalProposal}
right={
latestEnactedProposal
? applyImmutableKeysFromEarlierVersion(
latestEnactedProposal,
updatedProposal
)
: applyImmutableKeysFromEarlierVersion(
originalProposal,
updatedProposal
)
}
/>
</div>
)}
</section>
);
};
@@ -17,7 +17,6 @@ import type { MarketInfoWithData } from '@vegaprotocol/markets';
import type { AssetQuery } from '@vegaprotocol/assets';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { ProposalState } from '@vegaprotocol/types';
import { ProposalMarketChanges } from '../proposal-market-changes';
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
export enum ProposalType {
@@ -35,10 +34,6 @@ export interface ProposalProps {
assetData?: AssetQuery | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
originalMarketProposalRestData?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mostRecentlyEnactedAssociatedMarketProposal?: any;
}
export const Proposal = ({
@@ -47,8 +42,6 @@ export const Proposal = ({
restData,
newMarketData,
assetData,
originalMarketProposalRestData,
mostRecentlyEnactedAssociatedMarketProposal,
}: ProposalProps) => {
const { t } = useTranslation();
@@ -161,24 +154,6 @@ export const Proposal = ({
</div>
)}
{proposal.terms.change.__typename === 'UpdateMarket' && (
<div className="mb-4">
<ProposalMarketChanges
originalProposal={
originalMarketProposalRestData?.data?.proposal?.terms?.newMarket
?.changes || {}
}
latestEnactedProposal={
mostRecentlyEnactedAssociatedMarketProposal?.node?.proposal
?.terms?.updateMarket?.changes || {}
}
updatedProposal={
restData?.data?.proposal?.terms?.updateMarket?.changes || {}
}
/>
</div>
)}
{(proposal.terms.change.__typename === 'NewAsset' ||
proposal.terms.change.__typename === 'UpdateAsset') &&
asset && (
@@ -1,5 +1,5 @@
import { Link } from 'react-router-dom';
import { Button } from '@vegaprotocol/ui-toolkit';
import { Button, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
import { useTranslation } from 'react-i18next';
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
@@ -115,6 +115,10 @@ export const ProposalsListItemDetails = ({
return (
<div className="mt-4 items-start text-sm">
<div className="text-vega-green">
<VegaIcon size={16} name={VegaIconNames.VOTE} />
<VegaIcon size={16} name={VegaIconNames.TICKET} />
</div>
<div className="flex items-center gap-2 text-vega-light-300 mb-2">
{voteDetails && <span data-testid="vote-details">{voteDetails}</span>}
{voteDetails && voteStatus && <span>&middot;</span>}
@@ -1,5 +1,5 @@
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { Proposal } from '../components/proposal';
@@ -16,12 +16,7 @@ import {
} from '@vegaprotocol/network-parameters';
export const ProposalContainer = () => {
const [
mostRecentlyEnactedAssociatedMarketProposal,
setMostRecentlyEnactedAssociatedMarketProposal,
] = useState(undefined);
const params = useParams<{ proposalId: string }>();
const {
params: networkParams,
loading: networkParamsLoading,
@@ -42,11 +37,9 @@ export const ProposalContainer = () => {
NetworkParams.governance_proposal_updateNetParam_requiredMajority,
NetworkParams.governance_proposal_freeform_requiredMajority,
]);
const {
state: { data: restData, loading: restLoading, error: restError },
state: { data: restData },
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
const { data, loading, error, refetch } = useProposalQuery({
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
@@ -54,35 +47,6 @@ export const ProposalContainer = () => {
skip: !params.proposalId,
});
const {
state: {
data: originalMarketProposalRestData,
loading: originalMarketProposalRestLoading,
error: originalMarketProposalRestError,
},
} = useFetch(
`${ENV.rest}governance?proposalId=${
data?.proposal?.terms.change.__typename === 'UpdateMarket' &&
data?.proposal.terms.change.marketId
}`,
undefined,
true,
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
);
const {
state: {
data: previouslyEnactedMarketProposalsRestData,
loading: previouslyEnactedMarketProposalsRestLoading,
error: previouslyEnactedMarketProposalsRestError,
},
} = useFetch(
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
undefined,
true,
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
);
const {
data: newMarketData,
loading: newMarketLoading,
@@ -115,39 +79,6 @@ export const ProposalContainer = () => {
),
});
useEffect(() => {
if (
previouslyEnactedMarketProposalsRestData &&
data?.proposal?.terms.change.__typename === 'UpdateMarket'
) {
const change = data?.proposal?.terms?.change as { marketId: string };
const filteredProposals =
// @ts-ignore rest data is not typed
previouslyEnactedMarketProposalsRestData.connection.edges.filter(
// @ts-ignore rest data is not typed
({ node }) =>
node?.proposal?.terms?.updateMarket?.marketId === change.marketId
);
const sortedProposals = filteredProposals.sort(
// @ts-ignore rest data is not typed
(a, b) =>
new Date(a?.node?.terms?.enactmentTimestamp).getTime() -
new Date(b?.node?.terms?.enactmentTimestamp).getTime()
);
setMostRecentlyEnactedAssociatedMarketProposal(
sortedProposals[sortedProposals.length - 1]
);
}
}, [
previouslyEnactedMarketProposalsRestData,
params.proposalId,
data?.proposal?.terms.change.__typename,
data?.proposal?.terms.change,
]);
useEffect(() => {
const interval = setInterval(refetch, 2000);
return () => clearInterval(interval);
@@ -156,39 +87,14 @@ export const ProposalContainer = () => {
return (
<AsyncRenderer
loading={
loading ||
newMarketLoading ||
assetLoading ||
networkParamsLoading ||
(restLoading ? (restLoading as boolean) : false) ||
(originalMarketProposalRestLoading
? (originalMarketProposalRestLoading as boolean)
: false) ||
(previouslyEnactedMarketProposalsRestLoading
? (previouslyEnactedMarketProposalsRestLoading as boolean)
: false)
}
error={
error ||
newMarketError ||
assetError ||
restError ||
originalMarketProposalRestError ||
previouslyEnactedMarketProposalsRestError ||
networkParamsError
loading || newMarketLoading || assetLoading || networkParamsLoading
}
error={error || newMarketError || assetError || networkParamsError}
data={{
...data,
...networkParams,
...(newMarketData ? { newMarketData } : {}),
...(assetData ? { assetData } : {}),
...(restData ? { restData } : {}),
...(originalMarketProposalRestData
? { originalMarketProposalRestData }
: {}),
...(previouslyEnactedMarketProposalsRestData
? { previouslyEnactedMarketProposalsRestData }
: {}),
}}
>
{data?.proposal ? (
@@ -198,10 +104,6 @@ export const ProposalContainer = () => {
restData={restData}
newMarketData={newMarketData}
assetData={assetData}
originalMarketProposalRestData={originalMarketProposalRestData}
mostRecentlyEnactedAssociatedMarketProposal={
mostRecentlyEnactedAssociatedMarketProposal
}
/>
) : (
<ProposalNotFound />
@@ -8,10 +8,9 @@ import { TxState } from '../../../hooks/transaction-reducer';
import { useTransaction } from '../../../hooks/use-transaction';
import { BigNumber } from '../../../lib/bignumber';
import { AssociateInfo } from './associate-info';
import { toBigNum } from '@vegaprotocol/utils';
import { removeDecimal, toBigNum } from '@vegaprotocol/utils';
import type { EthereumConfig } from '@vegaprotocol/web3';
import { useBalances } from '../../../lib/balances/balances-store';
import { MaxUint256 } from '@ethersproject/constants';
export const WalletAssociate = ({
perform,
@@ -43,7 +42,7 @@ export const WalletAssociate = ({
} = useTransaction(() => {
return token.approve(
ethereumConfig.staking_bridge_contract.address,
MaxUint256.toString()
removeDecimal('1000000', decimals).toString()
);
});
+1 -31
View File
@@ -62,7 +62,7 @@
cursor: pointer;
}
/* Styles required to (effectively) un-override the
/* Styles required to (effectively) un-override the
* reset styles so that the Proposal description fields
* render as you'd expect them to.
*
@@ -97,33 +97,3 @@
.react-markdown-container ul li {
list-style: circle;
}
.jsondiffpatch-delta,
.jsondiffpatch-delta pre {
font-family: 'Roboto Mono', monospace !important;
}
.jsondiffpatch-delta pre {
padding: 0 0.25em !important;
}
.jsondiffpatch-added .jsondiffpatch-property-name,
.jsondiffpatch-added .jsondiffpatch-value pre,
.jsondiffpatch-modified .jsondiffpatch-right-value pre,
.jsondiffpatch-textdiff-added {
background: theme(colors.vega.green[650]) !important;
color: theme(colors.white) !important;
}
.jsondiffpatch-deleted .jsondiffpatch-property-name,
.jsondiffpatch-deleted pre,
.jsondiffpatch-modified .jsondiffpatch-left-value pre,
.jsondiffpatch-textdiff-deleted {
background: theme(colors.vega.pink[650]) !important;
color: theme(colors.white) !important;
}
.jsondiffpatch-moved .jsondiffpatch-moved-destination {
background: theme(colors.vega.yellow[350]) !important;
color: theme(colors.vega.dark[200]) !important;
}
@@ -363,11 +363,12 @@ describe('Closed markets', { tags: '@smoke' }, () => {
.first()
.find('button svg')
.should('exist');
if (Cypress.env('NX_SUCCESSOR_MARKETS')) {
cy.get(rowSelector)
.find('[col-id="successorMarket"]')
.find('[col-id="successorMarketID"]')
.first()
.should('have.text', '-');
.should('have.text', ' - ');
}
});
@@ -27,7 +27,6 @@ describe('markets all table', { tags: '@smoke' }, () => {
'Description',
'Trading mode',
'Status',
'Successor market',
'Best bid',
'Best offer',
'Mark price',
@@ -1,6 +1,5 @@
import { MarketTradingModeMapping } from '@vegaprotocol/types';
import { MarketState } from '@vegaprotocol/types';
import compact from 'lodash/compact';
const accordionContent = 'accordion-content';
const blockExplorerLink = 'block-explorer-link';
@@ -67,24 +66,20 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
// 6002-MDET-201
cy.getByTestId(marketTitle).contains('Key details').click();
const rows: [string, string][] = compact([
['Name', 'BTCUSD Monthly (30 Jun 2022)'],
['Market ID', 'market-0'],
Cypress.env('NX_SUCCESSOR_MARKETS') && ['Parent Market ID', 'PARENT-A'],
Cypress.env('NX_SUCCESSOR_MARKETS') && [
'Insurance Pool Fraction',
'0.75',
],
['Trading Mode', MarketTradingModeMapping.TRADING_MODE_CONTINUOUS],
['Market Decimal Places', '5'],
['Position Decimal Places', '0'],
['Settlement Asset Decimal Places', '5'],
]);
validateMarketDataRow(0, 'Name', 'BTCUSD Monthly (30 Jun 2022)');
validateMarketDataRow(1, 'Market ID', 'market-0');
for (const rowNumber in rows) {
const [name, value] = rows[rowNumber];
validateMarketDataRow(Number(rowNumber), name, value);
if (Cypress.env('NX_SUCCESSOR_MARKETS')) {
validateMarketDataRow(2, 'Parent Market ID', 'PARENT-A');
}
validateMarketDataRow(
3,
'Trading Mode',
MarketTradingModeMapping.TRADING_MODE_CONTINUOUS
);
validateMarketDataRow(4, 'Market Decimal Places', '5');
validateMarketDataRow(5, 'Position Decimal Places', '0');
validateMarketDataRow(6, 'Settlement Asset Decimal Places', '5');
});
it('instrument displayed', () => {
@@ -19,7 +19,6 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
'Description',
'Settlement asset',
'State',
'Parent market',
'Voting',
'Closing date',
'Enactment date',
@@ -127,7 +127,7 @@ describe(
{
name: 'Price monitoring bounds',
infoText:
'Price Monitoring Bounds 1: Min 162.56291Max 182.96869Reference 172.47489',
'Price Monitoring Bounds: Min 162.56291Max 182.96869Reference 172.47489',
},
];
-6
View File
@@ -36,7 +36,6 @@ import {
successorMarketQuery,
parentMarketIdQuery,
successorMarketIdsQuery,
successorMarketProposalDetailsQuery,
} from '@vegaprotocol/mock';
import type { PartialDeep } from 'type-fest';
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/markets';
@@ -187,11 +186,6 @@ const mockTradingPage = (
aliasGQLQuery(req, 'SuccessorMarket', successorMarketQuery());
aliasGQLQuery(req, 'ParentMarketId', parentMarketIdQuery());
aliasGQLQuery(req, 'SuccessorMarketIds', successorMarketIdsQuery());
aliasGQLQuery(
req,
'SuccessorMarketProposalDetails',
successorMarketProposalDetailsQuery()
);
};
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
@@ -17,10 +17,7 @@ import {
usePaneLayout,
} from '../../components/resizable-grid';
import { TradingViews } from './trade-views';
import {
MarketSuccessorBanner,
MarketSuccessorProposalBanner,
} from '../../components/market-banner';
import { MarketSuccessorBanner } from '../../components/market-banner';
import { FLAGS } from '@vegaprotocol/environment';
interface TradeGridProps {
@@ -165,12 +162,7 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
return (
<div className={wrapperClasses}>
<div>
{FLAGS.SUCCESSOR_MARKETS && (
<>
<MarketSuccessorBanner market={market} />
<MarketSuccessorProposalBanner marketId={market?.id} />
</>
)}
{FLAGS.SUCCESSOR_MARKETS && <MarketSuccessorBanner market={market} />}
<OracleBanner marketId={market?.id || ''} />
</div>
<div className="min-h-0 p-0.5">
@@ -12,10 +12,7 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
import { NO_MARKET } from './constants';
import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames';
import {
MarketSuccessorBanner,
MarketSuccessorProposalBanner,
} from '../../components/market-banner';
import { MarketSuccessorBanner } from '../../components/market-banner';
import { FLAGS } from '@vegaprotocol/environment';
interface TradePanelsProps {
@@ -68,12 +65,7 @@ export const TradePanels = ({
return (
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
<div>
{FLAGS.SUCCESSOR_MARKETS && (
<>
<MarketSuccessorBanner market={market} />
<MarketSuccessorProposalBanner marketId={market?.id} />
</>
)}
{FLAGS.SUCCESSOR_MARKETS && <MarketSuccessorBanner market={market} />}
<OracleBanner marketId={market?.id || ''} />
</div>
<div className="h-full">
@@ -10,18 +10,15 @@ import type {
OracleSpecDataConnectionQuery,
MarketsDataQuery,
MarketsQuery,
SuccessorMarketIdsQuery,
} from '@vegaprotocol/markets';
import {
OracleSpecDataConnectionDocument,
MarketsDataDocument,
MarketsDocument,
SuccessorMarketIdsDocument,
} from '@vegaprotocol/markets';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { FLAGS } from '@vegaprotocol/environment';
import {
createMarketFragment,
marketsQuery,
@@ -49,16 +46,10 @@ jest.mock('@vegaprotocol/markets', () => ({
: { data: undefined },
}));
jest.mock('@vegaprotocol/environment', () => {
const actual = jest.requireActual('@vegaprotocol/environment');
return {
...actual,
FLAGS: {
...actual.FLAGS,
SUCCESSOR_MARKETS: true,
} as FeatureFlags,
};
});
jest.mock('@vegaprotocol/environment', () => ({
...jest.requireActual('@vegaprotocol/environment'),
FLAGS: { SUCCESSOR_MARKETS: true } as Partial<FeatureFlags>,
}));
describe('Closed', () => {
let originalNow: typeof Date.now;
@@ -358,25 +349,8 @@ describe('Closed', () => {
state: MarketState.STATE_SETTLED,
}),
},
{
__typename: 'MarketEdge' as const,
node: {
...createMarketFragment({
id: 'successorMarketID',
state: MarketState.STATE_ACTIVE,
}),
tradableInstrument: {
...createMarketFragment().tradableInstrument,
instrument: {
...createMarketFragment().tradableInstrument.instrument,
id: 'successorAssset',
name: 'Successor Market Name',
code: 'SuccessorCode',
},
},
},
},
];
const mixedMarketsMock: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
@@ -390,36 +364,11 @@ describe('Closed', () => {
},
},
};
const successorMarketsMock: MockedResponse<SuccessorMarketIdsQuery> = {
request: {
query: SuccessorMarketIdsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: [
{
node: {
id: 'include-0',
successorMarketID: 'successorMarketID',
parentMarketID: '',
},
},
],
},
},
},
};
render(
<MemoryRouter>
<MockedProvider
mocks={[
mixedMarketsMock,
marketsDataMock,
oracleDataMock,
successorMarketsMock,
]}
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
@@ -435,107 +384,5 @@ describe('Closed', () => {
screen.getByRole('button', { name: 'SuccessorCode' })
).toBeInTheDocument();
});
expect(
screen.getByRole('columnheader', {
name: (_name, element) =>
element.getAttribute('col-id') === 'successorMarket',
})
).toBeInTheDocument();
});
it('feature flag should hide successors', async () => {
const mockedFlags = jest.mocked(FLAGS);
mockedFlags.SUCCESSOR_MARKETS = false;
const mixedMarkets = [
{
__typename: 'MarketEdge' as const,
node: createMarketFragment({
id: 'include-0',
state: MarketState.STATE_SETTLED,
}),
},
{
__typename: 'MarketEdge' as const,
node: {
...createMarketFragment({
id: 'successorMarketID',
state: MarketState.STATE_ACTIVE,
}),
tradableInstrument: {
...createMarketFragment().tradableInstrument,
instrument: {
...createMarketFragment().tradableInstrument.instrument,
id: 'successorAssset',
name: 'Successor Market Name',
code: 'SuccessorCode',
},
},
},
},
];
const mixedMarketsMock: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: mixedMarkets,
},
},
},
};
const successorMarketsMock: MockedResponse<SuccessorMarketIdsQuery> = {
request: {
query: SuccessorMarketIdsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: [
{
node: {
id: 'include-0',
successorMarketID: 'successorMarketID',
parentMarketID: '',
},
},
],
},
},
},
};
render(
<MemoryRouter>
<MockedProvider
mocks={[
mixedMarketsMock,
marketsDataMock,
oracleDataMock,
successorMarketsMock,
]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
await waitFor(() => {
expect(
screen.getByRole('columnheader', {
name: (_name, element) =>
element.getAttribute('col-id') === 'settlementDate',
})
).toBeInTheDocument();
});
screen.getAllByRole('columnheader').forEach((element) => {
expect(element.getAttribute('col-id')).not.toEqual('successorMarket');
});
});
});
+25 -4
View File
@@ -4,7 +4,11 @@ import type {
VegaICellRendererParams,
VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
import {
AgGridLazy as AgGrid,
COL_DEFS,
MarketNameCell,
} from '@vegaprotocol/datagrid';
import { useMemo } from 'react';
import { t } from '@vegaprotocol/i18n';
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
@@ -19,14 +23,15 @@ import type {
import {
MarketActionsDropdown,
closedMarketsWithDataProvider,
useSuccessorMarket,
} from '@vegaprotocol/markets';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import type { ColDef } from 'ag-grid-community';
import { FLAGS } from '@vegaprotocol/environment';
import { SettlementDateCell } from './settlement-date-cell';
import { SettlementPriceCell } from './settlement-price-cell';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { SuccessorMarketRenderer } from './successor-market-cell';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { FLAGS } from '@vegaprotocol/environment';
type SettlementAsset =
MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset'];
@@ -101,6 +106,22 @@ export const Closed = () => {
);
};
export const SuccessorMarketRenderer = ({
value,
}: VegaICellRendererParams<Row, 'id'>) => {
const { data } = useSuccessorMarket(value);
const onMarketClick = useMarketClickHandler();
return data ? (
<MarketNameCell
value={data.tradableInstrument.instrument.code}
data={data}
onMarketClick={onMarketClick}
/>
) : (
' - '
);
};
const ClosedMarketsDataGrid = ({
rowData,
error,
@@ -181,8 +202,8 @@ const ClosedMarketsDataGrid = ({
},
FLAGS.SUCCESSOR_MARKETS && {
headerName: t('Successor market'),
colId: 'successorMarketID',
field: 'id',
colId: 'successorMarket',
cellRenderer: 'SuccessorMarketRenderer',
},
{
@@ -1,13 +1,7 @@
import { MarketsContainer } from '@vegaprotocol/markets';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { SuccessorMarketRenderer } from './successor-market-cell';
export const Markets = () => {
const handleOnSelect = useMarketClickHandler();
return (
<MarketsContainer
onSelect={handleOnSelect}
SuccessorMarketRenderer={SuccessorMarketRenderer}
/>
);
return <MarketsContainer onSelect={handleOnSelect} />;
};
@@ -6,7 +6,6 @@ import {
} from '@vegaprotocol/environment';
import { ProposalsList } from '@vegaprotocol/proposals';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { SuccessorMarketRenderer } from './successor-market-cell';
export const Proposed = () => {
const tokenLink = useLinks(DApp.Token);
@@ -14,7 +13,7 @@ export const Proposed = () => {
return (
<>
<div className="h-[400px]">
<ProposalsList SuccessorMarketRenderer={SuccessorMarketRenderer} />
<ProposalsList />
</div>
<ExternalLink className="py-4 px-[11px] text-sm" href={externalLink}>
{t('Propose a new market')}
@@ -1,40 +0,0 @@
import { MarketNameCell } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketProvider, useSuccessorMarketIds } from '@vegaprotocol/markets';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import React from 'react';
export const SuccessorMarketRenderer = ({
value,
parent,
}: {
value: string;
parent?: boolean;
}) => {
const successors = useSuccessorMarketIds(value);
const onMarketClick = useMarketClickHandler();
const lookupValue = successors
? parent
? successors.parentMarketID
: successors.successorMarketID
: '';
const { data } = useDataProvider({
dataProvider: marketProvider,
variables: {
marketId: lookupValue || '',
},
skip: !lookupValue,
});
return data ? (
<MarketNameCell
value={data.tradableInstrument.instrument.code}
data={data}
onMarketClick={onMarketClick}
/>
) : (
'-'
);
};
@@ -12,9 +12,10 @@ import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import type { AgGridReact } from 'ag-grid-react';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
@@ -25,6 +26,8 @@ export const LiquidityContainer = ({
marketId: string | undefined;
filter?: Filter;
}) => {
const gridRef = useRef<AgGridReact | null>(null);
const gridStore = useLiquidityStore((store) => store.gridStore);
const updateGridStore = useLiquidityStore((store) => store.updateGridStore);
@@ -57,6 +60,7 @@ export const LiquidityContainer = ({
return (
<div className="h-full relative">
<LiquidityTable
ref={gridRef}
rowData={data}
symbol={symbol}
assetDecimalPlaces={assetDecimalPlaces}
@@ -1,2 +1 @@
export * from './market-successor-banner';
export * from './market-successor-proposal-banner';
@@ -1,155 +0,0 @@
import { act, render, screen, waitFor } from '@testing-library/react';
import type { SingleExecutionResult } from '@apollo/client';
import type { MockedResponse } from '@apollo/react-testing';
import { MockedProvider } from '@apollo/react-testing';
import { MarketSuccessorProposalBanner } from './market-successor-proposal-banner';
import type { SuccessorProposalsListQuery } from '@vegaprotocol/proposals';
import { SuccessorProposalsListDocument } from '@vegaprotocol/proposals';
const marketProposalMock: MockedResponse<SuccessorProposalsListQuery> = {
request: {
query: SuccessorProposalsListDocument,
},
result: {
data: {
proposalsConnection: {
edges: [
{
node: {
__typename: 'Proposal',
id: 'proposal-1',
terms: {
__typename: 'ProposalTerms',
change: {
__typename: 'NewMarket',
instrument: {
name: 'New proposal of the market successor',
},
successorConfiguration: {
parentMarketId: 'marketId',
},
},
},
},
},
],
},
},
},
};
describe('MarketSuccessorProposalBanner', () => {
it('should display single proposal', async () => {
render(
<MockedProvider mocks={[marketProposalMock]}>
<MarketSuccessorProposalBanner marketId="marketId" />
</MockedProvider>
);
await waitFor(() => {
expect(
screen.getByText('A successors to this market has been proposed')
).toBeInTheDocument();
});
expect(
screen
.getByRole('link')
.getAttribute('href')
?.endsWith('/proposals/proposal-1') ?? false
).toBe(true);
});
it('should display plural proposals', async () => {
const dualProposalMock = {
...marketProposalMock,
result: {
...marketProposalMock.result,
data: {
proposalsConnection: {
edges: [
...((
marketProposalMock?.result as SingleExecutionResult<SuccessorProposalsListQuery>
)?.data?.proposalsConnection?.edges ?? []),
{
node: {
__typename: 'Proposal',
id: 'proposal-2',
terms: {
__typename: 'ProposalTerms',
change: {
__typename: 'NewMarket',
instrument: {
name: 'New second proposal of the market successor',
},
successorConfiguration: {
parentMarketId: 'marketId',
},
},
},
},
},
],
},
},
},
};
render(
<MockedProvider mocks={[dualProposalMock]}>
<MarketSuccessorProposalBanner marketId="marketId" />
</MockedProvider>
);
await waitFor(() => {
expect(
screen.getByText('Successors to this market have been proposed')
).toBeInTheDocument();
});
expect(
screen
.getAllByRole('link')[0]
.getAttribute('href')
?.endsWith('/proposals/proposal-1') ?? false
).toBe(true);
expect(
screen
.getAllByRole('link')[1]
.getAttribute('href')
?.endsWith('/proposals/proposal-2') ?? false
).toBe(true);
});
it('banner should be hidden because no proposals', () => {
const { container } = render(
<MockedProvider>
<MarketSuccessorProposalBanner marketId="marketId" />
</MockedProvider>
);
expect(container).toBeEmptyDOMElement();
});
it('banner should be hidden because no proposals for the market', () => {
const { container } = render(
<MockedProvider mocks={[marketProposalMock]}>
<MarketSuccessorProposalBanner marketId="otherMarketId" />
</MockedProvider>
);
expect(container).toBeEmptyDOMElement();
});
it('banner should be hidden after user close click', async () => {
const { container } = render(
<MockedProvider mocks={[marketProposalMock]}>
<MarketSuccessorProposalBanner marketId="marketId" />
</MockedProvider>
);
await waitFor(() => {
expect(
screen.getByText('A successors to this market has been proposed')
).toBeInTheDocument();
});
await act(() => {
screen.getByTestId('notification-banner-close').click();
});
await waitFor(() => {
expect(container).toBeEmptyDOMElement();
});
});
});
@@ -1,71 +0,0 @@
import { useState } from 'react';
import type {
SuccessorProposalListFieldsFragment,
NewMarketSuccessorFieldsFragment,
} from '@vegaprotocol/proposals';
import { useSuccessorProposalsListQuery } from '@vegaprotocol/proposals';
import {
ExternalLink,
Intent,
NotificationBanner,
} from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
export const MarketSuccessorProposalBanner = ({
marketId,
}: {
marketId?: string;
}) => {
const { data: proposals } = useSuccessorProposalsListQuery({
skip: !marketId,
});
const successors =
proposals?.proposalsConnection?.edges
?.map((item) => item?.node as SuccessorProposalListFieldsFragment)
.filter(
(item: SuccessorProposalListFieldsFragment) =>
(item.terms?.change as NewMarketSuccessorFieldsFragment)
?.successorConfiguration?.parentMarketId === marketId
) ?? [];
const [visible, setVisible] = useState(true);
const tokenLink = useLinks(DApp.Token);
if (visible && successors.length) {
return (
<NotificationBanner
intent={Intent.Primary}
onClose={() => {
setVisible(false);
}}
>
<div className="uppercase mb-1">
{successors.length === 1
? t('A successors to this market has been proposed')
: t('Successors to this market have been proposed')}
</div>
<div>
{successors.length === 1
? t('Check out the terms of the proposal and vote:')
: t('Check out the terms of the proposals and vote:')}{' '}
{successors.map((item, i) => {
const externalLink = tokenLink(
TOKEN_PROPOSAL.replace(':id', item.id || '')
);
return (
<>
<ExternalLink href={externalLink} key={i}>
{
(item.terms?.change as NewMarketSuccessorFieldsFragment)
?.instrument.name
}
</ExternalLink>
{i < successors.length - 1 && ', '}
</>
);
})}
</div>
</NotificationBanner>
);
}
return null;
};
+18 -15
View File
@@ -110,22 +110,25 @@ export class VegaDataSource implements DataSource {
this._decimalPlaces = data.market.decimalPlaces;
this._positionDecimalPlaces = data.market.positionDecimalPlaces;
let priceMonitoringBounds: PriceMonitoringBounds[] | undefined;
let priceMonitoringBounds: PriceMonitoringBounds | undefined;
if (data.market.data.priceMonitoringBounds) {
priceMonitoringBounds = data.market.data.priceMonitoringBounds.map(
(bounds) => ({
maxValidPrice: Number(
addDecimal(bounds.maxValidPrice, this._decimalPlaces)
),
minValidPrice: Number(
addDecimal(bounds.minValidPrice, this._decimalPlaces)
),
referencePrice: Number(
addDecimal(bounds.referencePrice, this._decimalPlaces)
),
})
);
if (
data.market.data.priceMonitoringBounds &&
data.market.data.priceMonitoringBounds.length > 0
) {
const bounds = data.market.data.priceMonitoringBounds[0];
priceMonitoringBounds = {
maxValidPrice: Number(
addDecimal(bounds.maxValidPrice, this._decimalPlaces)
),
minValidPrice: Number(
addDecimal(bounds.minValidPrice, this._decimalPlaces)
),
referencePrice: Number(
addDecimal(bounds.referencePrice, this._decimalPlaces)
),
};
}
return {
@@ -29,9 +29,6 @@ export function addGetNodes() {
location
name
pendingStake
rankingScore {
performanceScore
}
pubkey
stakedByDelegates
stakedByOperator
@@ -11,6 +11,6 @@ export const CenteredGridCellWrapper = ({
<div
className={classNames('flex h-[20px] p-0 justify-items-center', className)}
>
<div className="w-full self-center">{children}</div>
<div className="self-center">{children}</div>
</div>
);
+158 -153
View File
@@ -1,4 +1,4 @@
import { useMemo } from 'react';
import { forwardRef, useMemo } from 'react';
import {
addDecimalsFormatNumber,
addDecimalsFormatNumberQuantum,
@@ -9,6 +9,7 @@ import { t } from '@vegaprotocol/i18n';
import type { TypedDataAgGrid } from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import type {
ColDef,
ITooltipParams,
@@ -39,164 +40,168 @@ export interface LiquidityTableProps
quantum?: string | number;
}
export const LiquidityTable = ({
symbol = '',
assetDecimalPlaces,
stakeToCcyVolume,
quantum,
...props
}: LiquidityTableProps) => {
const colDefs = useMemo(() => {
const assetDecimalsFormatter = ({ value }: ITooltipParams) => {
if (!value) return '-';
return `${addDecimalsFormatNumber(value, assetDecimalPlaces ?? 0)}`;
};
export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
(
{ symbol = '', assetDecimalPlaces, stakeToCcyVolume, quantum, ...props },
ref
) => {
const colDefs = useMemo(() => {
const assetDecimalsFormatter = ({ value }: ITooltipParams) => {
if (!value) return '-';
return `${addDecimalsFormatNumber(value, assetDecimalPlaces ?? 0)}`;
};
const assetDecimalsQuantumFormatter = ({ value }: ValueFormatterParams) => {
if (!value) return '-';
return `${addDecimalsFormatNumberQuantum(
const assetDecimalsQuantumFormatter = ({
value,
assetDecimalPlaces ?? 0,
quantum ?? 0
)}`;
};
}: ValueFormatterParams) => {
if (!value) return '-';
return `${addDecimalsFormatNumberQuantum(
value,
assetDecimalPlaces ?? 0,
quantum ?? 0
)}`;
};
const stakeToCcyVolumeFormatter = ({ value }: ITooltipParams) => {
if (!value) return '-';
const newValue = new BigNumber(value)
.times(Number(stakeToCcyVolume) || 1)
.toString();
return `${addDecimalsFormatNumber(newValue, assetDecimalPlaces ?? 0)}`;
};
const stakeToCcyVolumeFormatter = ({ value }: ITooltipParams) => {
if (!value) return '-';
const newValue = new BigNumber(value)
.times(Number(stakeToCcyVolume) || 1)
.toString();
return `${addDecimalsFormatNumber(newValue, assetDecimalPlaces ?? 0)}`;
};
const stakeToCcyVolumeQuantumFormatter = ({
value,
}: ValueFormatterParams) => {
if (!value) return '-';
const newValue = new BigNumber(value)
.times(Number(stakeToCcyVolume) || 1)
.toString();
return `${addDecimalsFormatNumberQuantum(
newValue,
assetDecimalPlaces ?? 0,
quantum ?? 0
)}`;
};
const stakeToCcyVolumeQuantumFormatter = ({
value,
}: ValueFormatterParams) => {
if (!value) return '-';
const newValue = new BigNumber(value)
.times(Number(stakeToCcyVolume) || 1)
.toString();
return `${addDecimalsFormatNumberQuantum(
newValue,
assetDecimalPlaces ?? 0,
quantum ?? 0
)}`;
};
const defs: ColDef[] = [
{
headerName: t('Party'),
field: 'party.id',
headerTooltip: t('The public key of the party making this commitment.'),
},
{
headerName: t(`Commitment (${symbol})`),
field: 'commitmentAmount',
type: 'rightAligned',
headerTooltip: t(
'The amount committed to the market by this liquidity provider.'
),
valueFormatter: assetDecimalsQuantumFormatter,
tooltipValueGetter: assetDecimalsFormatter,
},
{
headerName: t(`Share`),
field: 'equityLikeShare',
type: 'rightAligned',
headerTooltip: t(
'The equity-like share of liquidity of the market used to determine allocation of LP fees. Calculated based on share of total liquidity, with a premium added for length of commitment.'
),
valueFormatter: percentageFormatter,
},
{
headerName: t('Proposed fee'),
headerTooltip: t(
'The fee percentage (per trade) proposed by each liquidity provider.'
),
field: 'fee',
type: 'rightAligned',
valueFormatter: percentageFormatter,
},
{
headerName: t('Market valuation at entry'),
field: 'averageEntryValuation',
type: 'rightAligned',
headerTooltip: t(
'The valuation of the market at the time the liquidity commitment was made. Commitments made at a lower valuation earlier in the lifetime of the market would be expected to have a higher equity-like share if the market has grown. If a commitment is amended, value will reflect the average of the market valuations across the lifetime of the commitment.'
),
minWidth: 160,
valueFormatter: assetDecimalsQuantumFormatter,
tooltipValueGetter: assetDecimalsFormatter,
},
{
headerName: t('Obligation'),
field: 'commitmentAmount',
type: 'rightAligned',
headerTooltip: t(
`The liquidity provider's obligation to the market, calculated as the liquidity commitment amount multiplied by the value of the stake_to_ccy_volume network parameter to convert into units of liquidity volume. The obligation can be met by a combination of LP orders and limit orders on the order book.`
),
valueFormatter: stakeToCcyVolumeQuantumFormatter,
tooltipValueGetter: stakeToCcyVolumeFormatter,
},
{
headerName: t('Supplied'),
field: 'balance',
type: 'rightAligned',
headerTooltip: t(
`The amount of liquidity volume supplied by the LP order in order to meet the obligation. If the obligation is already met in full by other limit orders from the same Vega key the LP order is not required and this value will be zero. Also note if the target stake for the market is less than the obligation the full value of the obligation may not be required.`
),
valueFormatter: stakeToCcyVolumeQuantumFormatter,
tooltipValueGetter: stakeToCcyVolumeFormatter,
},
{
headerName: t('Status'),
headerTooltip: t('The current status of this liquidity provision.'),
field: 'status',
valueFormatter: ({ value }) => {
if (!value) return value;
return LiquidityProvisionStatusMapping[
value as LiquidityProvisionStatus
];
const defs: ColDef[] = [
{
headerName: t('Party'),
field: 'party.id',
headerTooltip: t(
'The public key of the party making this commitment.'
),
},
},
{
headerName: t('Created'),
headerTooltip: t(
'The date and time this liquidity provision was created.'
),
field: 'createdAt',
type: 'rightAligned',
valueFormatter: dateValueFormatter,
},
{
headerName: t('Updated'),
headerTooltip: t(
'The date and time this liquidity provision was last updated.'
),
field: 'updatedAt',
type: 'rightAligned',
valueFormatter: dateValueFormatter,
},
];
return defs;
}, [assetDecimalPlaces, quantum, stakeToCcyVolume, symbol]);
{
headerName: t(`Commitment (${symbol})`),
field: 'commitmentAmount',
type: 'rightAligned',
headerTooltip: t(
'The amount committed to the market by this liquidity provider.'
),
valueFormatter: assetDecimalsQuantumFormatter,
tooltipValueGetter: assetDecimalsFormatter,
},
{
headerName: t(`Share`),
field: 'equityLikeShare',
type: 'rightAligned',
headerTooltip: t(
'The equity-like share of liquidity of the market used to determine allocation of LP fees. Calculated based on share of total liquidity, with a premium added for length of commitment.'
),
valueFormatter: percentageFormatter,
},
{
headerName: t('Proposed fee'),
headerTooltip: t(
'The fee percentage (per trade) proposed by each liquidity provider.'
),
field: 'fee',
type: 'rightAligned',
valueFormatter: percentageFormatter,
},
{
headerName: t('Market valuation at entry'),
field: 'averageEntryValuation',
type: 'rightAligned',
headerTooltip: t(
'The valuation of the market at the time the liquidity commitment was made. Commitments made at a lower valuation earlier in the lifetime of the market would be expected to have a higher equity-like share if the market has grown. If a commitment is amended, value will reflect the average of the market valuations across the lifetime of the commitment.'
),
minWidth: 160,
valueFormatter: assetDecimalsQuantumFormatter,
tooltipValueGetter: assetDecimalsFormatter,
},
{
headerName: t('Obligation'),
field: 'commitmentAmount',
type: 'rightAligned',
headerTooltip: t(
`The liquidity provider's obligation to the market, calculated as the liquidity commitment amount multiplied by the value of the stake_to_ccy_volume network parameter to convert into units of liquidity volume. The obligation can be met by a combination of LP orders and limit orders on the order book.`
),
valueFormatter: stakeToCcyVolumeQuantumFormatter,
tooltipValueGetter: stakeToCcyVolumeFormatter,
},
{
headerName: t('Supplied'),
field: 'balance',
type: 'rightAligned',
headerTooltip: t(
`The amount of liquidity volume supplied by the LP order in order to meet the obligation. If the obligation is already met in full by other limit orders from the same Vega key the LP order is not required and this value will be zero. Also note if the target stake for the market is less than the obligation the full value of the obligation may not be required.`
),
valueFormatter: stakeToCcyVolumeQuantumFormatter,
tooltipValueGetter: stakeToCcyVolumeFormatter,
},
{
headerName: t('Status'),
headerTooltip: t('The current status of this liquidity provision.'),
field: 'status',
valueFormatter: ({ value }) => {
if (!value) return value;
return LiquidityProvisionStatusMapping[
value as LiquidityProvisionStatus
];
},
},
{
headerName: t('Created'),
headerTooltip: t(
'The date and time this liquidity provision was created.'
),
field: 'createdAt',
type: 'rightAligned',
valueFormatter: dateValueFormatter,
},
{
headerName: t('Updated'),
headerTooltip: t(
'The date and time this liquidity provision was last updated.'
),
field: 'updatedAt',
type: 'rightAligned',
valueFormatter: dateValueFormatter,
},
];
return defs;
}, [assetDecimalPlaces, quantum, stakeToCcyVolume, symbol]);
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
overlayNoRowsTemplate={t('No liquidity provisions')}
getRowId={({ data }: { data: LiquidityProvisionData }) => data.id || ''}
tooltipShowDelay={500}
defaultColDef={{
resizable: true,
minWidth: 100,
tooltipComponent: TooltipCellComponent,
sortable: true,
}}
{...props}
columnDefs={colDefs}
/>
);
};
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
overlayNoRowsTemplate={t('No liquidity provisions')}
getRowId={({ data }: { data: LiquidityProvisionData }) => data.id || ''}
ref={ref}
tooltipShowDelay={500}
defaultColDef={{
resizable: true,
minWidth: 100,
tooltipComponent: TooltipCellComponent,
sortable: true,
}}
{...props}
columnDefs={colDefs}
/>
);
}
);
export default LiquidityTable;
+1 -1
View File
@@ -59,7 +59,6 @@ const CumulativeVol = memo(
}) => {
const volume = cumulativeValue ? (
<NumericCell
testId={testId}
value={cumulativeValue}
valueFormatted={addDecimalsFixedFormatNumber(
cumulativeValue,
@@ -70,6 +69,7 @@ const CumulativeVol = memo(
return onClick && volume ? (
<button
data-testid={testId}
onClick={() => onClick(cumulativeValue)}
className="hover:dark:bg-neutral-800 hover:bg-neutral-200 text-right pr-1"
>
@@ -16,7 +16,6 @@ query SuccessorMarketIds {
node {
id
successorMarketID
parentMarketID
}
}
}
+1 -2
View File
@@ -20,7 +20,7 @@ export type ParentMarketIdQuery = { __typename?: 'Query', market?: { __typename?
export type SuccessorMarketIdsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type SuccessorMarketIdsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, successorMarketID?: string | null, parentMarketID?: string | null } }> } | null };
export type SuccessorMarketIdsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, successorMarketID?: string | null } }> } | null };
export type SuccessorMarketQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
@@ -107,7 +107,6 @@ export const SuccessorMarketIdsDocument = gql`
node {
id
successorMarketID
parentMarketID
}
}
}
@@ -30,7 +30,6 @@ import { useOracleProofs } from '../../hooks';
import { OracleDialog } from '../oracle-dialog/oracle-dialog';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { useParentMarketIdQuery } from '../../__generated__';
import { useSuccessorMarketProposalDetailsQuery } from '@vegaprotocol/proposals';
type MarketInfoProps = {
market: MarketInfo;
@@ -145,14 +144,6 @@ export const KeyDetailsInfoPanel = ({ market }: MarketInfoProps) => {
},
skip: !FLAGS.SUCCESSOR_MARKETS,
});
const { data: successor } = useSuccessorMarketProposalDetailsQuery({
variables: {
proposalId: market.proposal?.id || '',
},
skip: !FLAGS.SUCCESSOR_MARKETS || !market.proposal?.id,
});
const assetDecimals =
market.tradableInstrument.instrument.product.settlementAsset.decimals;
@@ -164,11 +155,6 @@ export const KeyDetailsInfoPanel = ({ market }: MarketInfoProps) => {
name: market.tradableInstrument.instrument.name,
marketID: market.id,
parentMarketID: parentData?.market?.parentMarketID || '-',
insurancePoolFraction:
(successor?.proposal?.terms.change.__typename === 'NewMarket' &&
successor.proposal.terms.change.successorConfiguration
?.insurancePoolFraction) ||
'-',
tradingMode:
market.tradingMode &&
MarketTradingModeMapping[market.tradingMode],
@@ -102,8 +102,5 @@ export const tooltipMapping: Record<string, ReactNode> = {
`The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.`
),
suppliedStake: t('The current amount of liquidity supplied for this market.'),
parentMarketID: t('The ID of the market this market succeeds.'),
insurancePoolFraction: t(
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
),
parentMarketID: t('The ID of the market this market succeeds'),
};
@@ -1,4 +1,4 @@
import React, { forwardRef } from 'react';
import { forwardRef } from 'react';
import type { TypedDataAgGrid } from '@vegaprotocol/datagrid';
import {
AgGridLazy as AgGrid,
@@ -48,15 +48,10 @@ export const MarketListTable = forwardRef<
AgGridReact,
TypedDataAgGrid<MarketMaybeWithData> & {
onMarketClick: (marketId: string, metaKey?: boolean) => void;
SuccessorMarketRenderer?: React.FC<{ value: string }>;
}
>(({ onMarketClick, SuccessorMarketRenderer, ...props }, ref) => {
>(({ onMarketClick, ...props }, ref) => {
const columnDefs = useColumnDefs({ onMarketClick });
const components = {
PriceFlashCell,
MarketName,
...(SuccessorMarketRenderer ? { SuccessorMarketRenderer } : null),
};
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
@@ -65,7 +60,7 @@ export const MarketListTable = forwardRef<
defaultColDef={defaultColDef}
columnDefs={columnDefs}
suppressCellFocus
components={components}
components={{ PriceFlashCell, MarketName }}
{...props}
/>
);
@@ -4,21 +4,6 @@ import * as DataProviders from '@vegaprotocol/data-provider';
import { MockedProvider } from '@apollo/react-testing';
import type { MarketMaybeWithData } from '../../markets-provider';
import { MarketsContainer } from './markets-container';
import { FLAGS } from '@vegaprotocol/environment';
jest.mock('@vegaprotocol/environment', () => {
const actual = jest.requireActual('@vegaprotocol/environment');
return {
...actual,
FLAGS: {
...actual.FLAGS,
SUCCESSOR_MARKETS: true,
},
};
});
const SuccessorMarketRenderer = ({ value }: { value: string }) => {
return '-';
};
const market = {
id: 'id-1',
@@ -37,10 +22,8 @@ const market = {
} as unknown as MarketMaybeWithData;
describe('MarketsContainer', () => {
const spyOnSelect = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
it('context menu should stay open', async () => {
const spyOnSelect = jest.fn();
jest
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.spyOn<typeof DataProviders, any>(DataProviders, 'useDataProvider')
@@ -51,16 +34,12 @@ describe('MarketsContainer', () => {
data: [market],
};
});
});
it('context menu should stay open', async () => {
let rerenderRef: (ui: React.ReactElement) => void;
await act(async () => {
const { rerender } = render(
<MockedProvider>
<MarketsContainer
onSelect={spyOnSelect}
SuccessorMarketRenderer={SuccessorMarketRenderer}
/>
<MarketsContainer onSelect={spyOnSelect} />
</MockedProvider>
);
rerenderRef = rerender;
@@ -84,7 +63,7 @@ describe('MarketsContainer', () => {
screen.getByRole('button', {
name: (_name, element) =>
(element.parentNode as Element)?.getAttribute('id') ===
'cell-market-actions-9',
'cell-market-actions-8',
})
);
@@ -125,55 +104,4 @@ describe('MarketsContainer', () => {
).toBeInTheDocument();
}
});
it('SuccessorMarketRenderer should be rendered', async () => {
const successorMarketName = 'Successor Market Name';
const spySuccessorMarketRenderer = jest
.fn()
.mockReturnValue(successorMarketName);
render(
<MockedProvider>
<MarketsContainer
onSelect={spyOnSelect}
SuccessorMarketRenderer={spySuccessorMarketRenderer}
/>
</MockedProvider>
);
expect(spySuccessorMarketRenderer).toHaveBeenCalled();
expect(
screen.getByRole('columnheader', {
name: (_name, element) =>
element.getAttribute('col-id') === 'successorMarketID',
})
).toBeInTheDocument();
expect(
screen.getByRole('presentation', {
name: (_name, element) =>
element.getAttribute('id') === 'cell-successorMarketID-14',
})
).toHaveTextContent(successorMarketName);
});
it('feature flag should hide successorMarketID column', async () => {
const mockedFlags = jest.mocked(FLAGS);
mockedFlags.SUCCESSOR_MARKETS = false;
const spySuccessorMarketRenderer = jest.fn();
render(
<MockedProvider>
<MarketsContainer
onSelect={spyOnSelect}
SuccessorMarketRenderer={spySuccessorMarketRenderer}
/>
</MockedProvider>
);
expect(spySuccessorMarketRenderer).not.toHaveBeenCalled();
screen.getAllByRole('columnheader').forEach((element) => {
expect(element.getAttribute('col-id')).not.toEqual('successorMarketID');
});
});
});
@@ -1,5 +1,5 @@
import type { MouseEvent } from 'react';
import React, { useEffect, useRef } from 'react';
import { useEffect, useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import type { CellClickedEvent } from 'ag-grid-community';
import { t } from '@vegaprotocol/i18n';
@@ -11,13 +11,9 @@ import type { MarketMaybeWithData } from '../../markets-provider';
const POLLING_TIME = 2000;
interface MarketsContainerProps {
onSelect: (marketId: string, metaKey?: boolean) => void;
SuccessorMarketRenderer?: React.FC<{ value: string }>;
}
export const MarketsContainer = ({
onSelect,
SuccessorMarketRenderer,
}: MarketsContainerProps) => {
export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const { data, error, reload } = useDataProvider({
@@ -62,7 +58,6 @@ export const MarketsContainer = ({
}}
onMarketClick={onSelect}
overlayNoRowsTemplate={error ? error.message : t('No markets')}
SuccessorMarketRenderer={SuccessorMarketRenderer}
/>
</div>
);
@@ -1,6 +1,5 @@
import { useMemo } from 'react';
import type { ColDef } from 'ag-grid-community';
import compact from 'lodash/compact';
import { t } from '@vegaprotocol/i18n';
import type {
VegaICellRendererParams,
@@ -12,7 +11,6 @@ import * as Schema from '@vegaprotocol/types';
import { addDecimalsFormatNumber, toBigNum } from '@vegaprotocol/utils';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { FLAGS } from '@vegaprotocol/environment';
import type { MarketMaybeWithData } from '../../markets-provider';
import { MarketActionsDropdown } from './market-table-actions';
@@ -24,183 +22,166 @@ const { MarketTradingMode, AuctionTrigger } = Schema;
export const useColumnDefs = ({ onMarketClick }: Props) => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
return useMemo<ColDef[]>(
() =>
compact([
{
headerName: t('Market'),
field: 'tradableInstrument.instrument.code',
cellRenderer: 'MarketName',
cellRendererParams: { onMarketClick },
},
{
headerName: t('Description'),
field: 'tradableInstrument.instrument.name',
},
{
headerName: t('Trading mode'),
field: 'tradingMode',
minWidth: 170,
valueFormatter: ({
data,
}: VegaValueFormatterParams<MarketMaybeWithData, 'data'>) => {
if (!data?.data) return '-';
const { trigger, marketTradingMode } = data.data;
return marketTradingMode ===
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
trigger &&
trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
? `${Schema.MarketTradingModeMapping[marketTradingMode]}
() => [
{
headerName: t('Market'),
field: 'tradableInstrument.instrument.code',
cellRenderer: 'MarketName',
cellRendererParams: { onMarketClick },
},
{
headerName: t('Description'),
field: 'tradableInstrument.instrument.name',
},
{
headerName: t('Trading mode'),
field: 'tradingMode',
minWidth: 170,
valueFormatter: ({
data,
}: VegaValueFormatterParams<MarketMaybeWithData, 'data'>) => {
if (!data?.data) return '-';
const { trigger, marketTradingMode } = data.data;
return marketTradingMode ===
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
trigger &&
trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
? `${Schema.MarketTradingModeMapping[marketTradingMode]}
- ${Schema.AuctionTriggerMapping[trigger]}`
: Schema.MarketTradingModeMapping[marketTradingMode];
},
filter: SetFilter,
filterParams: {
set: Schema.MarketTradingModeMapping,
},
: Schema.MarketTradingModeMapping[marketTradingMode];
},
{
headerName: t('Status'),
field: 'state',
valueFormatter: ({
data,
}: VegaValueFormatterParams<MarketMaybeWithData, 'state'>) => {
return data?.state ? Schema.MarketStateMapping[data.state] : '-';
},
filter: SetFilter,
filterParams: {
set: Schema.MarketStateMapping,
},
filter: SetFilter,
filterParams: {
set: Schema.MarketTradingModeMapping,
},
FLAGS.SUCCESSOR_MARKETS && {
headerName: t('Successor market'),
field: 'successorMarketID',
cellRenderer: 'SuccessorMarketRenderer',
},
{
headerName: t('Status'),
field: 'state',
valueFormatter: ({
data,
}: VegaValueFormatterParams<MarketMaybeWithData, 'state'>) => {
return data?.state ? Schema.MarketStateMapping[data.state] : '-';
},
{
headerName: t('Best bid'),
field: 'data.bestBidPrice',
type: 'rightAligned',
cellRenderer: 'PriceFlashCell',
filter: 'agNumberColumnFilter',
valueGetter: ({
data,
}: VegaValueGetterParams<MarketMaybeWithData>) => {
return data?.data?.bestBidPrice === undefined
? undefined
: toBigNum(
data?.data?.bestBidPrice,
data.decimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<
MarketMaybeWithData,
'data.bestBidPrice'
>) =>
data?.data?.bestBidPrice === undefined
? '-'
: addDecimalsFormatNumber(
data.data.bestBidPrice,
data.decimalPlaces
),
filter: SetFilter,
filterParams: {
set: Schema.MarketStateMapping,
},
{
headerName: t('Best offer'),
field: 'data.bestOfferPrice',
type: 'rightAligned',
cellRenderer: 'PriceFlashCell',
filter: 'agNumberColumnFilter',
valueGetter: ({
data,
}: VegaValueGetterParams<MarketMaybeWithData>) => {
return data?.data?.bestOfferPrice === undefined
? undefined
: toBigNum(
data?.data?.bestOfferPrice,
data.decimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<
MarketMaybeWithData,
'data.bestOfferPrice'
>) =>
data?.data?.bestOfferPrice === undefined
? '-'
: addDecimalsFormatNumber(
data.data.bestOfferPrice,
data.decimalPlaces
),
},
{
headerName: t('Best bid'),
field: 'data.bestBidPrice',
type: 'rightAligned',
cellRenderer: 'PriceFlashCell',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<MarketMaybeWithData>) => {
return data?.data?.bestBidPrice === undefined
? undefined
: toBigNum(data?.data?.bestBidPrice, data.decimalPlaces).toNumber();
},
{
headerName: t('Mark price'),
field: 'data.markPrice',
type: 'rightAligned',
cellRenderer: 'PriceFlashCell',
filter: 'agNumberColumnFilter',
valueGetter: ({
data,
}: VegaValueGetterParams<MarketMaybeWithData>) => {
return data?.data?.markPrice === undefined
? undefined
: toBigNum(data?.data?.markPrice, data.decimalPlaces).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<MarketMaybeWithData, 'data.markPrice'>) =>
data?.data?.bestOfferPrice === undefined
? '-'
: addDecimalsFormatNumber(
data.data.markPrice,
data.decimalPlaces
),
valueFormatter: ({
data,
}: VegaValueFormatterParams<
MarketMaybeWithData,
'data.bestBidPrice'
>) =>
data?.data?.bestBidPrice === undefined
? '-'
: addDecimalsFormatNumber(
data.data.bestBidPrice,
data.decimalPlaces
),
},
{
headerName: t('Best offer'),
field: 'data.bestOfferPrice',
type: 'rightAligned',
cellRenderer: 'PriceFlashCell',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<MarketMaybeWithData>) => {
return data?.data?.bestOfferPrice === undefined
? undefined
: toBigNum(
data?.data?.bestOfferPrice,
data.decimalPlaces
).toNumber();
},
{
headerName: t('Settlement asset'),
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
cellRenderer: ({
data,
}: VegaICellRendererParams<
MarketMaybeWithData,
'tradableInstrument.instrument.product.settlementAsset.symbol'
>) => {
const value =
data?.tradableInstrument.instrument.product.settlementAsset;
return value ? (
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value.id, e.target as HTMLElement);
}}
>
{value.symbol}
</ButtonLink>
) : (
''
);
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<
MarketMaybeWithData,
'data.bestOfferPrice'
>) =>
data?.data?.bestOfferPrice === undefined
? '-'
: addDecimalsFormatNumber(
data.data.bestOfferPrice,
data.decimalPlaces
),
},
{
headerName: t('Mark price'),
field: 'data.markPrice',
type: 'rightAligned',
cellRenderer: 'PriceFlashCell',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<MarketMaybeWithData>) => {
return data?.data?.markPrice === undefined
? undefined
: toBigNum(data?.data?.markPrice, data.decimalPlaces).toNumber();
},
{
colId: 'market-actions',
field: 'id',
...COL_DEFS.actions,
cellRenderer: ({
data,
}: VegaICellRendererParams<MarketMaybeWithData>) => {
if (!data) return null;
return (
<MarketActionsDropdown
marketId={data.id}
assetId={
data.tradableInstrument.instrument.product.settlementAsset.id
}
/>
);
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<MarketMaybeWithData, 'data.markPrice'>) =>
data?.data?.bestOfferPrice === undefined
? '-'
: addDecimalsFormatNumber(data.data.markPrice, data.decimalPlaces),
},
{
headerName: t('Settlement asset'),
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
cellRenderer: ({
data,
}: VegaICellRendererParams<
MarketMaybeWithData,
'tradableInstrument.instrument.product.settlementAsset.symbol'
>) => {
const value =
data?.tradableInstrument.instrument.product.settlementAsset;
return value ? (
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value.id, e.target as HTMLElement);
}}
>
{value.symbol}
</ButtonLink>
) : (
''
);
},
]),
},
{
colId: 'market-actions',
field: 'id',
...COL_DEFS.actions,
cellRenderer: ({
data,
}: VegaICellRendererParams<MarketMaybeWithData>) => {
if (!data) return null;
return (
<MarketActionsDropdown
marketId={data.id}
assetId={
data.tradableInstrument.instrument.product.settlementAsset.id
}
/>
);
},
},
],
[onMarketClick, openAssetDetailsDialog]
);
};
+2 -33
View File
@@ -27,9 +27,8 @@ import {
filterAndSortMarkets,
} from './market-utils';
import { MarketsDocument } from './__generated__/markets';
import type { Candle } from './market-candles-provider';
import type { SuccessorMarketIdsQuery } from './__generated__/SuccessorMarket';
import { SuccessorMarketIdsDocument } from './__generated__';
export type Market = MarketFieldsFragment;
@@ -83,6 +82,7 @@ export const marketProvider = makeDerivedDataProvider<
);
export const useMarket = (marketId?: string) => {
console.log(marketId);
const variables = useMemo(() => ({ marketId: marketId || '' }), [marketId]);
return useDataProvider({
dataProvider: marketProvider,
@@ -240,34 +240,3 @@ export const useMarketList = () => {
reload,
};
};
export type MarketSuccessors = {
__typename?: 'Market';
id: string;
successorMarketID?: string | null;
parentMarketID?: string | null;
};
const getMarketSuccessorData = (
responseData: SuccessorMarketIdsQuery | null
): MarketSuccessors[] | null =>
responseData?.marketsConnection?.edges.map((edge) => edge.node) || null;
export const marketSuccessorProvider = makeDataProvider<
SuccessorMarketIdsQuery,
MarketSuccessors[],
never,
never
>({
query: SuccessorMarketIdsDocument,
getData: getMarketSuccessorData,
fetchPolicy: 'no-cache',
});
export const useSuccessorMarketIds = (marketId: string) => {
const { data } = useDataProvider({
dataProvider: marketSuccessorProvider,
variables: undefined,
skip: !marketId,
});
return data?.find((item) => item.id === marketId) ?? null;
};
@@ -14,23 +14,6 @@ import { createProposalListFieldsFragment } from '../../lib/proposals-data-provi
import type { ProposalsListQuery } from '../../lib';
import { ProposalsListDocument } from '../../lib';
import type { PartialDeep } from 'type-fest';
import { FLAGS } from '@vegaprotocol/environment';
jest.mock('@vegaprotocol/environment', () => {
const actual = jest.requireActual('@vegaprotocol/environment');
return {
...actual,
FLAGS: {
...actual.FLAGS,
SUCCESSOR_MARKETS: true,
},
};
});
const successorMarketName = 'Successor Market Name';
const spySuccessorMarketRenderer = jest
.fn()
.mockReturnValue(successorMarketName);
describe('ProposalsList', () => {
const createProposalsMock = (override?: PartialDeep<ProposalsListQuery>) => {
@@ -81,15 +64,13 @@ describe('ProposalsList', () => {
return mock;
};
beforeEach(() => {
jest.clearAllMocks();
});
it('should be properly rendered', async () => {
const mock = createProposalsMock();
await act(() => {
render(
<MockedProvider mocks={[mock]}>
<ProposalsList SuccessorMarketRenderer={spySuccessorMarketRenderer} />
<ProposalsList />
</MockedProvider>
);
});
@@ -101,55 +82,31 @@ describe('ProposalsList', () => {
});
it('some of states should be filtered out', async () => {
const proposalNode = createProposalListFieldsFragment({
id: 'id-1',
state: Types.ProposalState.STATE_ENACTED,
});
const mock = createProposalsMock({
proposalsConnection: {
edges: [
{
__typename: 'ProposalEdge',
node: {
...proposalNode,
terms: {
...proposalNode.terms,
change: {
...proposalNode.terms.change,
},
},
},
node: createProposalListFieldsFragment({
id: 'id-1',
state: Types.ProposalState.STATE_ENACTED,
}),
},
],
},
} as PartialDeep<ProposalsListQuery>);
});
await act(() => {
render(
<MockedProvider mocks={[mock]}>
<ProposalsList SuccessorMarketRenderer={spySuccessorMarketRenderer} />
<ProposalsList />
</MockedProvider>
);
});
const container = document.querySelector('.ag-center-cols-container');
await waitFor(() => {
expect(container).toBeInTheDocument();
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
});
expect(spySuccessorMarketRenderer).toHaveBeenCalled();
expect(
screen.getByRole('columnheader', {
name: (_name, element) =>
element.getAttribute('col-id') === 'parentMarket',
})
).toBeInTheDocument();
expect(
screen.getAllByRole('gridcell', {
name: (name, element) =>
element.getAttribute('col-id') === 'parentMarket',
})[0]
).toHaveTextContent(successorMarketName);
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
});
it('empty response should causes no data message display', async () => {
@@ -172,55 +129,10 @@ describe('ProposalsList', () => {
await act(() => {
render(
<MockedProvider mocks={[mock]}>
<ProposalsList SuccessorMarketRenderer={spySuccessorMarketRenderer} />
<ProposalsList />
</MockedProvider>
);
});
expect(await screen.findByText('No markets')).toBeInTheDocument();
expect(
screen.getByRole('columnheader', {
name: (_name, element) =>
element.getAttribute('col-id') === 'parentMarket',
})
).toBeInTheDocument();
});
it('feature flag should hide parent marketcolumn', async () => {
const mockedFlags = jest.mocked(FLAGS);
mockedFlags.SUCCESSOR_MARKETS = false;
const mock: MockedResponse<ProposalsListQuery> = {
request: {
query: ProposalsListDocument,
variables: {
proposalType: Types.ProposalType.TYPE_NEW_MARKET,
},
},
result: {
data: {
proposalsConnection: {
__typename: 'ProposalsConnection',
edges: [],
},
},
},
};
await act(() => {
render(
<MockedProvider mocks={[mock]}>
<ProposalsList SuccessorMarketRenderer={spySuccessorMarketRenderer} />
</MockedProvider>
);
});
await waitFor(() => {
expect(
screen.getByRole('columnheader', {
name: (_name, element) => element.getAttribute('col-id') === 'market',
})
).toBeInTheDocument();
});
screen.getAllByRole('columnheader').forEach((element) => {
expect(element.getAttribute('col-id')).not.toEqual('parentMarket');
});
});
});
@@ -1,4 +1,3 @@
import React from 'react';
import { useRef } from 'react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
@@ -18,13 +17,7 @@ export const getNewMarketProposals = (data: ProposalListFieldsFragment[]) =>
].includes(proposal.state)
);
interface ProposalListProps {
SuccessorMarketRenderer: React.FC<{ value: string }>;
}
export const ProposalsList = ({
SuccessorMarketRenderer,
}: ProposalListProps) => {
export const ProposalsList = () => {
const gridRef = useRef<AgGridReact | null>(null);
const { data } = useProposalsListQuery({
variables: {
@@ -48,7 +41,6 @@ export const ProposalsList = ({
getRowId={({ data }) => data.id}
style={{ width: '100%', height: '100%' }}
overlayNoRowsTemplate={t('No markets')}
components={{ SuccessorMarketRenderer }}
/>
</div>
);
@@ -1,14 +1,8 @@
import { useMemo } from 'react';
import BigNumber from 'bignumber.js';
import type { ColDef } from 'ag-grid-community';
import {
CenteredGridCellWrapper,
COL_DEFS,
DateRangeFilter,
SetFilter,
} from '@vegaprotocol/datagrid';
import compact from 'lodash/compact';
import { useEnvironment, FLAGS } from '@vegaprotocol/environment';
import { COL_DEFS, DateRangeFilter, SetFilter } from '@vegaprotocol/datagrid';
import { useEnvironment } from '@vegaprotocol/environment';
import { getDateTimeFormat } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
@@ -36,8 +30,9 @@ export const useColumnDefs = () => {
return new BigNumber(requiredMajority).times(100);
}, [params?.governance_proposal_market_requiredMajority]);
const cellCss = 'grid h-full items-center';
const columnDefs: ColDef[] = useMemo(() => {
return compact([
return [
{
colId: 'market',
headerName: t('Market'),
@@ -88,16 +83,10 @@ export const useColumnDefs = () => {
set: ProposalStateMapping,
},
},
FLAGS.SUCCESSOR_MARKETS && {
headerName: t('Parent market'),
field: 'id',
colId: 'parentMarket',
cellRenderer: 'SuccessorMarketRenderer',
cellRendererParams: { parent: true },
},
{
colId: 'voting',
headerName: t('Voting'),
cellClass: 'flex justify-between leading-tight font-mono',
cellRenderer: ({
data,
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
@@ -109,12 +98,12 @@ export const useColumnDefs = () => {
? new BigNumber(0)
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
return (
<CenteredGridCellWrapper>
<div className="uppercase flex h-full items-center justify-center">
<VoteProgress
threshold={requiredMajorityPercentage}
progress={yesPercentage}
/>
</CenteredGridCellWrapper>
</div>
);
}
return '-';
@@ -157,12 +146,13 @@ export const useColumnDefs = () => {
},
flex: 1,
},
]);
];
}, [VEGA_TOKEN_URL, requiredMajorityPercentage]);
const defaultColDef: ColDef = useMemo(() => {
return {
sortable: true,
cellClass: cellCss,
resizable: true,
filter: true,
filterParams: { buttons: ['reset'] },
@@ -327,33 +327,3 @@ query ProposalsList($proposalType: ProposalType, $inState: ProposalState) {
}
}
}
fragment NewMarketSuccessorFields on NewMarket {
instrument {
name
}
successorConfiguration {
parentMarketId
}
}
fragment SuccessorProposalListFields on Proposal {
id
terms {
change {
... on NewMarket {
...NewMarketSuccessorFields
}
}
}
}
query SuccessorProposalsList {
proposalsConnection(proposalType: TYPE_NEW_MARKET, inState: STATE_OPEN) {
edges {
node {
...SuccessorProposalListFields
}
}
}
}
File diff suppressed because one or more lines are too long
@@ -5,7 +5,6 @@ import type {
import * as Schema from '@vegaprotocol/types';
import type { PartialDeep } from 'type-fest';
import merge from 'lodash/merge';
import type { SuccessorMarketProposalDetailsQuery } from '../proposals-hooks';
export const proposalListQuery = (
override?: PartialDeep<ProposalsListQuery>
@@ -1285,27 +1284,3 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'Proposal',
},
];
export const successorMarketProposalDetailsQuery = (
override?: SuccessorMarketProposalDetailsQuery
): SuccessorMarketProposalDetailsQuery =>
merge(
{
__typename: 'Query',
proposal: {
__typename: 'Proposal',
terms: {
__typename: 'ProposalTerms',
change: {
__typename: 'NewMarket',
successorConfiguration: {
__typename: 'SuccessorConfiguration',
insurancePoolFraction: '0.75',
parentMarketId: 'PARENT-A',
},
},
},
},
},
override
);
@@ -40,30 +40,3 @@ query ProposalOfMarket($marketId: ID!) {
}
}
}
query SuccessorMarketProposalDetails($proposalId: ID!) {
proposal(id: $proposalId) {
id
terms {
change {
... on NewMarket {
successorConfiguration {
parentMarketId
insurancePoolFraction
}
}
}
}
}
}
query InstrumentDetails($marketId: ID!) {
market(id: $marketId) {
tradableInstrument {
instrument {
code
name
}
}
}
}
@@ -27,20 +27,6 @@ export type ProposalOfMarketQueryVariables = Types.Exact<{
export type ProposalOfMarketQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, terms: { __typename?: 'ProposalTerms', enactmentDatetime?: any | null } } | null };
export type SuccessorMarketProposalDetailsQueryVariables = Types.Exact<{
proposalId: Types.Scalars['ID'];
}>;
export type SuccessorMarketProposalDetailsQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string, insurancePoolFraction: string } | null } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter' } } } | null };
export type InstrumentDetailsQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type InstrumentDetailsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', code: string, name: string } } } | null };
export const ProposalEventFieldsFragmentDoc = gql`
fragment ProposalEventFields on Proposal {
id
@@ -161,89 +147,4 @@ export function useProposalOfMarketLazyQuery(baseOptions?: Apollo.LazyQueryHookO
}
export type ProposalOfMarketQueryHookResult = ReturnType<typeof useProposalOfMarketQuery>;
export type ProposalOfMarketLazyQueryHookResult = ReturnType<typeof useProposalOfMarketLazyQuery>;
export type ProposalOfMarketQueryResult = Apollo.QueryResult<ProposalOfMarketQuery, ProposalOfMarketQueryVariables>;
export const SuccessorMarketProposalDetailsDocument = gql`
query SuccessorMarketProposalDetails($proposalId: ID!) {
proposal(id: $proposalId) {
id
terms {
change {
... on NewMarket {
successorConfiguration {
parentMarketId
insurancePoolFraction
}
}
}
}
}
}
`;
/**
* __useSuccessorMarketProposalDetailsQuery__
*
* To run a query within a React component, call `useSuccessorMarketProposalDetailsQuery` and pass it any options that fit your needs.
* When your component renders, `useSuccessorMarketProposalDetailsQuery` 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 } = useSuccessorMarketProposalDetailsQuery({
* variables: {
* proposalId: // value for 'proposalId'
* },
* });
*/
export function useSuccessorMarketProposalDetailsQuery(baseOptions: Apollo.QueryHookOptions<SuccessorMarketProposalDetailsQuery, SuccessorMarketProposalDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<SuccessorMarketProposalDetailsQuery, SuccessorMarketProposalDetailsQueryVariables>(SuccessorMarketProposalDetailsDocument, options);
}
export function useSuccessorMarketProposalDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<SuccessorMarketProposalDetailsQuery, SuccessorMarketProposalDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<SuccessorMarketProposalDetailsQuery, SuccessorMarketProposalDetailsQueryVariables>(SuccessorMarketProposalDetailsDocument, options);
}
export type SuccessorMarketProposalDetailsQueryHookResult = ReturnType<typeof useSuccessorMarketProposalDetailsQuery>;
export type SuccessorMarketProposalDetailsLazyQueryHookResult = ReturnType<typeof useSuccessorMarketProposalDetailsLazyQuery>;
export type SuccessorMarketProposalDetailsQueryResult = Apollo.QueryResult<SuccessorMarketProposalDetailsQuery, SuccessorMarketProposalDetailsQueryVariables>;
export const InstrumentDetailsDocument = gql`
query InstrumentDetails($marketId: ID!) {
market(id: $marketId) {
tradableInstrument {
instrument {
code
name
}
}
}
}
`;
/**
* __useInstrumentDetailsQuery__
*
* To run a query within a React component, call `useInstrumentDetailsQuery` and pass it any options that fit your needs.
* When your component renders, `useInstrumentDetailsQuery` 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 } = useInstrumentDetailsQuery({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useInstrumentDetailsQuery(baseOptions: Apollo.QueryHookOptions<InstrumentDetailsQuery, InstrumentDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<InstrumentDetailsQuery, InstrumentDetailsQueryVariables>(InstrumentDetailsDocument, options);
}
export function useInstrumentDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<InstrumentDetailsQuery, InstrumentDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<InstrumentDetailsQuery, InstrumentDetailsQueryVariables>(InstrumentDetailsDocument, options);
}
export type InstrumentDetailsQueryHookResult = ReturnType<typeof useInstrumentDetailsQuery>;
export type InstrumentDetailsLazyQueryHookResult = ReturnType<typeof useInstrumentDetailsLazyQuery>;
export type InstrumentDetailsQueryResult = Apollo.QueryResult<InstrumentDetailsQuery, InstrumentDetailsQueryVariables>;
export type ProposalOfMarketQueryResult = Apollo.QueryResult<ProposalOfMarketQuery, ProposalOfMarketQueryVariables>;
@@ -3,4 +3,3 @@ export * from './use-proposal-event';
export * from './use-proposal-submit';
export * from './use-update-proposal';
export * from './use-update-network-paramaters-toasts';
export * from './use-successor-market-proposal-details';
@@ -1,39 +0,0 @@
import omit from 'lodash/omit';
import {
useInstrumentDetailsQuery,
useSuccessorMarketProposalDetailsQuery,
} from './__generated__/Proposal';
export const useSuccessorMarketProposalDetails = (
proposalId?: string | null
) => {
const { data: proposal } = useSuccessorMarketProposalDetailsQuery({
variables: {
proposalId: proposalId || '',
},
skip: !proposalId || proposalId.length === 0,
});
const successorDetails =
(proposal?.proposal &&
proposal.proposal?.terms.change.__typename === 'NewMarket' &&
proposal.proposal.terms.change.successorConfiguration) ||
undefined;
const { data: market } = useInstrumentDetailsQuery({
variables: {
marketId: successorDetails?.parentMarketId || '',
},
skip:
!successorDetails?.parentMarketId ||
successorDetails.parentMarketId.length === 0,
});
const details = {
...successorDetails,
code: market?.market?.tradableInstrument.instrument.code,
name: market?.market?.tradableInstrument.instrument.name,
};
return omit(details, '__typename');
};
+3 -4
View File
@@ -21,8 +21,7 @@ type Action<T> =
export const useFetch = <T>(
url: string,
options?: RequestInit,
initialFetch = true,
skip?: boolean
initialFetch = true
): {
state: State<T>;
refetch: (
@@ -106,10 +105,10 @@ export const useFetch = <T>(
useEffect(() => {
cancelRequest.current = false;
if (initialFetch && !skip) {
if (initialFetch) {
fetchCallback();
}
}, [fetchCallback, initialFetch, skip, url]);
}, [fetchCallback, initialFetch, url]);
useEffect(() => {
// Use the cleanup function for avoiding a possibly...
+1 -2
View File
@@ -67,10 +67,9 @@
"immer": "^9.0.12",
"iso8601-duration": "^2.1.1",
"js-sha3": "^0.8.0",
"jsondiffpatch": "^0.4.1",
"lodash": "^4.17.21",
"next": "13.3.0",
"pennant": "1.10.0",
"pennant": "1.9.0",
"react": "18.2.0",
"react-copy-to-clipboard": "^5.0.4",
"react-dom": "18.2.0",
-110
View File
@@ -1,110 +0,0 @@
from os import environ
from subprocess import check_output
from argparse import ArgumentParser
import json
projects = []
projects_e2e = []
previews = {
'governance': 'not deployed',
'explorer': 'not deployed',
'trading': 'not deployed',
'tools': 'not deployed',
}
main_apps = ['governance', 'explorer', 'trading']
preview_governance="not deployed"
preview_trading="not deployed"
preview_explorer="not deployed"
preview_tools="not deployed"
# take input from the pipeline
parser = ArgumentParser()
# let's generate slug from bash spell for now
parser.add_argument('--branch-slug', help='slug of branch')
parser.add_argument('--github-ref', help='current github ref')
parser.add_argument('--event-name', help='name of event in CI')
args = parser.parse_args()
# run yarn affected command
affected=check_output(f'yarn nx print-affected --base={environ["NX_BASE"]} --head={environ["NX_HEAD"]} --select=projects'.split()).decode('utf-8')
# print useful information
print(">>>> debug")
print(f"NX_BASE: { environ['NX_BASE'] }")
print(f"NX_HEAD: { environ['NX_HEAD'] }")
print(f"Branch slug: {args.branch_slug}")
print(f"Current ref: {args.github_ref}")
print(">> Affected output")
print(affected)
print(">>>> eof debug")
# define affection actions -> add to projects arrays and generate preview link
def affect_app(app, preview_name=None):
print(f"{app} is affected")
projects.append(app)
if not preview_name:
preview_name=app
previews[app] = f'https://{preview_name}.{args.branch_slug}.vega.rocks'
# check appearance in the affected string for main apps
for app in main_apps:
if app in affected:
affect_app(app)
# if non of main apps is affected - test all of them
if not projects:
for app in main_apps:
affect_app(app)
# generate e2e targets
projects_e2e = [f'{app}-e2e' for app in projects]
# check affection for multisig-signer which is deployed only from develop and pull requests
if args.event_name == 'pull_request' or 'develop' in args.github_ref:
if 'multisig-signer' in affected:
affect_app('multisig-signer', 'tools')
# now parse apps that are deployed from develop but don't have previews
if 'develop' in args.github_ref:
for app in ['static', 'ui-toolkit']:
if app in affected:
projects.append(app)
# if ref is in format release/{env}-{app} then only {app} is deployed
if 'release' in args.github_ref:
for app in main_apps:
if f'{args.github_ref}'.endswith(app):
projects = [app]
projects_e2e = [f'{app}-e2e']
projects = json.dumps(projects)
projects_e2e = json.dumps(projects_e2e)
print(f'Projects: {projects}')
print(f'Projects E2E: {projects_e2e}')
print('>> Previews')
for preview, preview_value in previews.items():
print(f'{preview}: {preview_value}')
print('>> EOF Previews')
lines_to_write = [
f'PREVIEW_GOVERNANCE={previews["governance"]}',
f'PREVIEW_EXPLORER={previews["explorer"]}',
f'PREVIEW_TRADING={previews["trading"]}',
f'PREVIEW_TOOLS={previews["tools"]}',
f'PROJECTS={projects}',
f'PROJECTS_E2E={projects_e2e}',
]
env_file = environ['GITHUB_ENV']
print(f'Line to add to GITHUB_ENV file: {env_file}')
print(lines_to_write)
with open(env_file, 'a') as _f:
_f.write('\n'.join(lines_to_write))
-61
View File
@@ -1,61 +0,0 @@
from argparse import ArgumentParser
from os import environ
# take input from the pipeline
parser = ArgumentParser()
# let's generate slug from bash spell for now
parser.add_argument('--github-ref', help='current github ref')
parser.add_argument('--app', help='current app')
args = parser.parse_args()
env_name = ''
domain = 'vega.rocks'
bucket_name = ''
if 'release/' in args.github_ref:
# remove prefixing release/ and take the first string limited by - which is supposed to be name of the environment for releasing (format: release/testnet-trading)
env_name = args.github_ref.replace('refs/heads/release/', '').split('-')[0]
elif 'develop' in args.github_ref:
env_name = 'stagnet1'
apps_deployed_from_develop_to_mainnet = {
'multisig-signer' :'tools.vega.xyz',
'static': 'static.vega.xyz',
'ui-toolkit' : 'ui.vega.rocks',
}
if args.app in apps_deployed_from_develop_to_mainnet:
env_name = 'mainnet'
bucket_name = apps_deployed_from_develop_to_mainnet[args.app]
# endswith to avoid confusion with mirror env
elif args.github_ref.endswith('mainnet'):
env_name = 'mainnet'
other_domains_to_deploy = {
'mainnet': 'vega.xyz',
'testnet': 'fairground.wtf',
}
if env_name in other_domains_to_deploy:
domain = other_domains_to_deploy[env_name]
if not bucket_name:
bucket_name = f'{args.app}.{domain}'
# testing envs on vega.rocks contain env_name in the url not like testnet / mainnet
if not bucket_name:
bucket_name = f'{args.app}.{env_name}.{domain}'
print(f'env name: {env_name}')
print(f'domain: {domain}')
print(f'bucket name: {bucket_name}')
lines_to_write = [
f'ENV_NAME={env_name}',
f'BUCKET_NAME={bucket_name}',
]
env_file = environ['GITHUB_ENV']
print(f'Line to add to GITHUB_ENV file: {env_file}')
print(lines_to_write)
with open(env_file, 'a') as _f:
_f.write('\n'.join(lines_to_write))
+6 -31
View File
@@ -9714,18 +9714,6 @@ allotment@1.18.1:
lodash.isequal "^4.5.0"
use-resize-observer "^9.0.0"
allotment@1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/allotment/-/allotment-1.19.0.tgz#8241a2e3db45e6b1e23f6ade29e392eab4297958"
integrity sha512-qL/1faHUoCOvMstCFGkaGSK/nVoSAVN6NLFuIW4P6D5fvKdgxV8/KNfMFeuQUXReodILp4I6z+5a9zmuqK5t4g==
dependencies:
classnames "^2.3.0"
eventemitter3 "^5.0.0"
lodash.clamp "^4.0.0"
lodash.debounce "^4.0.0"
lodash.isequal "^4.5.0"
use-resize-observer "^9.0.0"
alpha-lyrae@vegaprotocol/alpha-lyrae:
version "1.0.0"
resolved "https://codeload.github.com/vegaprotocol/alpha-lyrae/tar.gz/d7d51ca6945aebeca57077320535362a959b2ca8"
@@ -11274,7 +11262,7 @@ chalk@^1.0.0, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1:
chalk@^2.0.0, chalk@^2.4.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -12967,11 +12955,6 @@ didyoumean@^1.2.2:
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
diff-match-patch@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37"
integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==
diff-sequences@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
@@ -17436,14 +17419,6 @@ jsonc-parser@3.2.0:
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
jsondiffpatch@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/jsondiffpatch/-/jsondiffpatch-0.4.1.tgz#9fb085036767f03534ebd46dcd841df6070c5773"
integrity sha512-t0etAxTUk1w5MYdNOkZBZ8rvYYN5iL+2dHCCx/DpkFm/bW28M6y5nUS83D4XdZiHy35Fpaw6LBb+F88fHZnVCw==
dependencies:
chalk "^2.3.0"
diff-match-patch "^1.0.0"
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -20000,10 +19975,10 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
pennant@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/pennant/-/pennant-1.10.0.tgz#c30bcc62f1da5a166785b9bce87727715fa28455"
integrity sha512-y3Oi2c8QdW6ovH+NeSwtGcEPZNr7rrHGhDIpWnYulCTEC5RvUUzRyPfOpVM2jL/D3iPUQvkSdC9hZ0jNHCyiuw==
pennant@1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/pennant/-/pennant-1.9.0.tgz#d7332548e17d5140e6655905e41bd6671bd37fc3"
integrity sha512-HgM82NnjH94Hc0dhq9knZMOyGT0QyMfMsGFtB4S6UMe2wlSxMpnmQKjtVYmug4PYAkXXprqPrb+mUhjcsPtm5g==
dependencies:
"@babel/runtime" "^7.13.10"
"@d3fc/d3fc-technical-indicator" "^8.0.1"
@@ -20024,7 +19999,7 @@ pennant@1.10.0:
"@types/react" "^18.0.14"
"@types/react-dom" "^18.0.5"
"@types/react-virtualized-auto-sizer" "^1.0.0"
allotment "1.19.0"
allotment "1.18.1"
chroma-js "^2.4.2"
classnames "^2.2.6"
d3-array "2.8.0"