Compare commits

...
Author SHA1 Message Date
Madalina Raicu da9e4457b8 fix(trading): fix docs resources 2023-04-05 14:53:24 +01:00
Madalina Raicu b8dcbd2e44 Merge branch 'develop' of github.com:vegaprotocol/frontend-monorepo into develop 2023-04-03 10:09:46 +01:00
Maciek 7c82144f37 chore(trading): add metaKey detection - clean up some unit tests (#3337) 2023-04-03 10:20:20 +02:00
Joe Tsang 17ad0cc975 test(governance): add e2e tests for banner and footer links (#3340) 2023-04-03 09:16:42 +01:00
dexturr eb58dcd350 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-03 06:09:59 +00:00
dexturr c8c0bf2cc0 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-03 00:16:15 +00:00
dexturr 00579fcb1f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-02 18:06:45 +00:00
dexturr 5e13173efb chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-02 12:09:35 +00:00
dexturr 3a6c0554cc chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-02 06:06:55 +00:00
dexturr d4b237a6ba chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-02 00:17:15 +00:00
dexturr 5e765f3172 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-01 18:07:57 +00:00
dexturr 5ef8c11e6e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-01 12:07:28 +00:00
dexturr 84bae12def chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-01 06:06:47 +00:00
dexturr c5b22f5bfb chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-01 00:14:47 +00:00
dexturr 071dcc61a8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-31 18:09:16 +00:00
m.ray ce0ccdfebc feat(trading): add post only and reduce only orders (#3311) 2023-03-31 17:00:55 +01:00
Art f1524d3fcd feat(utils): make release script (#3290) 2023-03-31 17:01:19 +02:00
MaciekandMatthew Russell 82e5128ba1 chore(trading): add "holding CMD + click" external link (#3273)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-03-31 15:23:44 +02:00
Maciek 2bfc3abd15 chore(trading): liquidity provision, change query for set flag live on true (#3335) 2023-03-31 14:20:44 +01:00
dexturr bd3b557f3d chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-31 12:10:35 +00:00
MaciekandBartłomiej Głownia c899da52c2 fix(trading): stabilize the liquidity provision table (#3320)
Co-authored-by: Bartłomiej Głownia <bglownia@gmail.com>
2023-03-31 11:41:16 +01:00
Matthew Russell b9c4057ce5 feat(oracles,trading): 3224 add oracle links to market info (#3297) 2023-03-31 09:06:21 +01:00
dexturr d02feee5c6 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-31 06:07:09 +00:00
dexturr 6d1ab36a72 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-31 00:14:21 +00:00
dexturr 5690fe91a8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-30 18:09:18 +00:00
Joe Tsang 1daae6a233 chore(governance): additional acs added for governance proposals (#3322) 2023-03-30 17:18:41 +01:00
dexturr cd35a34246 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-30 12:10:13 +00:00
Bartłomiej Głownia 77ada964b5 feat(market-depth): always show the same number of decimal places in order book (#3317) 2023-03-30 10:54:13 +01:00
90 changed files with 2262 additions and 671 deletions
+3 -4
View File
@@ -1,15 +1,14 @@
---
name: Release
about:
A template to outline the steps needed to for a successful release of our frontend apps
about: A template to outline the steps needed to for a successful release of our frontend apps
title: 'Release [add dapp version]-core-[add core version]'
labels:
labels:
assignees: ''
---
### Tasks
- [ ] Review [link to core release](xxx)
- [ ] Review [link to core release](xxx)
- [ ] Tag frontend-monorepo
- [ ] Create release and generate release notes
- [ ] Run `@smoke` tests
+10 -34
View File
@@ -15,10 +15,9 @@ jobs:
pr:
runs-on: ubuntu-latest
steps:
- name: Checkout frontend mono repo
- name: Checkout
uses: actions/checkout@v3
with:
# We need to fetch all branches and commits so that Nx affected has a base to compare against.
fetch-depth: 0
- name: Check node version
@@ -32,45 +31,22 @@ jobs:
with:
node-version: ${{ steps.node-version.outputs.npmVersion }}
# Check SHAs
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v3
with:
main-branch-name: develop
# See affected apps
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }}
- name: Install root dependencies
run: yarn install --frozen-lockfile
- name: See affected apps
run: |
nx_version=$(cat package.json | grep '"nx"' | cut -d ':' -f 2 | tr -d '",[:space:]')
rm package.json yarn.lock
yarn add nx@$nx_version
echo ">>>> debug"
echo "NX Version: $nx_version"
echo "NX_BASE: ${{ env.NX_BASE }}"
echo "NX_HEAD: ${{ env.NX_HEAD }}"
# echo "Main branch name: ${{ github.base_ref || github.ref_name }}"
# echo "git rev-parse HEAD: $(git rev-parse HEAD)"
# echo "Head: ${{ github.head_ref }}"
# echo "command to execute: 'yarn nx print-affected --base=${{ github.base_ref || github.ref_name }} --head=${{ github.head_ref }} --select=projects'"
# merge_base=$(git merge-base origin/develop HEAD)
# echo "git merge-base origin/develop HEAD: $merge_base"
# head_sha="${{ github.event.pull_request.head.sha || github.sha }}"
# echo "Head SHA: $head_sha"
# echo "command to execute (without nx-set-sha): 'yarn nx print-affected --base=$merge_base --head=$head_sha --select=projects'"
echo ">>>> eof debug"
# affected_1=$(yarn nx print-affected --base=$merge_base --head=$head_sha --select=projects || true)
# echo -n "Affected projects (allowed to fail): $affected_1"
# affected=$(yarn nx print-affected --base=${{ github.base_ref || github.ref_name }} --head=${{ github.head_ref }} --select=projects)
# echo -n "Affected projects: $affected"
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=HEAD --select=projects)"
echo -n "Affected projects: $affected"
+2
View File
@@ -13,6 +13,8 @@ NX_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
#Test configuration variables
CYPRESS_FAIRGROUND=false
@@ -56,7 +56,7 @@ describe(
navigateTo(navigation.proposals);
});
// 3001-VOTE-055
// 3001-VOTE-050 3001-VOTE-054 3001-VOTE-055 3002-PROP-019
it('Newly created raw proposal details - shows proposal title and full description', function () {
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
@@ -128,7 +128,7 @@ context(
.and('be.visible');
});
// 3001-VOTE-048 3001-VOTE-049
// 3001-VOTE-048 3001-VOTE-049 3001-VOTE-050
it('Able to fail proposal due to lack of participation', function () {
const proposalTitle = 'Add New free form proposal with short enactment';
const proposalTx = createFreeFormProposalTxBody();
@@ -19,6 +19,7 @@ import {
waitForSpinner,
navigateTo,
navigation,
closeDialog,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
@@ -41,7 +42,6 @@ const vegaWalletNameElement = '[data-testid="wallet-name"]';
const vegaWallet = '[data-testid="vega-wallet"]';
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const dialogCloseButton = '[data-testid="dialog-close"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const rawProposalData = '[data-testid="proposal-data"]';
const minVoteButton = '[data-testid="min-vote"]';
@@ -177,7 +177,7 @@ context(
'be.visible'
);
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
cy.get(dialogCloseButton).click();
closeDialog();
waitForProposalSync();
navigateTo(navigation.proposals);
cy.get(rejectProposalsLink).click();
@@ -214,7 +214,7 @@ context(
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should('have.text', errorMsg);
cy.get(dialogCloseButton).click();
closeDialog();
});
// 3002-PROP-009
@@ -227,7 +227,7 @@ context(
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should('have.text', errorMsg);
cy.get(dialogCloseButton).click();
closeDialog();
});
it('Unable to create a freeform proposal - when json parent section contains unexpected field', function () {
@@ -251,7 +251,7 @@ context(
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should('have.text', errorMsg);
cy.get(dialogCloseButton).click();
closeDialog();
cy.get(rawProposalData)
.invoke('val')
.should('contain', "i shouldn't be here");
@@ -279,7 +279,7 @@ context(
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should('have.text', errorMsg);
cy.get(dialogCloseButton).click();
closeDialog();
});
// 1005-PROP-009
@@ -1,4 +1,5 @@
import {
closeDialog,
navigateTo,
navigation,
waitForSpinner,
@@ -39,7 +40,6 @@ const maxVoteDeadline = '[data-testid="max-vote"]';
const minValidationDeadline = '[data-testid="min-validation"]';
const minEnactDeadline = '[data-testid="min-enactment"]';
const maxEnactDeadline = '[data-testid="max-enactment"]';
const dialogCloseButton = '[data-testid="dialog-close"]';
const inputError = '[data-testid="input-error-text"]';
const enactmentDeadlineError =
'[data-testid="enactment-before-voting-deadline"]';
@@ -48,6 +48,7 @@ const feedbackError = '[data-testid="Error"]';
const viewProposalBtn = 'view-proposal-btn';
const liquidityVoteStatus = 'liquidity-votes-status';
const tokenVoteStatus = 'token-votes-status';
const proposalTermsSection = 'proposal';
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
@@ -68,7 +69,6 @@ context(
{ tags: '@slow' },
function () {
before('connect wallets and set approval limit', function () {
cy.createMarket();
cy.visit('/');
vegaWalletSetSpecifiedApprovalAmount('1000');
});
@@ -78,6 +78,7 @@ context(
waitForSpinner();
cy.connectVegaWallet();
ethereumWalletConnect();
cy.createMarket();
ensureSpecifiedUnstakedTokensAreAssociated('1');
navigateTo(navigation.proposals);
});
@@ -194,7 +195,7 @@ context(
'have.text',
'Invalid params: proposal_submission.terms.closing_timestamp (cannot be after enactment time)'
);
cy.get(dialogCloseButton).click();
closeDialog();
cy.get(minVoteDeadline).click();
cy.get(enactmentDeadlineError).should('not.exist');
});
@@ -286,7 +287,7 @@ context(
);
});
// 3001-VOTE-092
// 3001-VOTE-092 3004-PMAC-001
it('Able to submit update market proposal and vote for proposal', function () {
vegaWalletFaucetAssetsWithoutCheck(
'fUSDC',
@@ -347,8 +348,9 @@ context(
// 3001-VOTE-026 3001-VOTE-027 3001-VOTE-028 3001-VOTE-095 3001-VOTE-096 3005-PASN-001
it('Able to submit new asset proposal using min deadlines', function () {
const proposalTitle = 'Test new asset proposal';
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
cy.get(newProposalTitle).type('Test new asset proposal');
cy.get(newProposalTitle).type(proposalTitle);
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-asset').then((newAssetProposal) => {
const newAssetPayload = JSON.stringify(newAssetProposal);
@@ -367,7 +369,7 @@ context(
cy.contains('Proposal waiting for node vote', proposalTimeout).should(
'be.visible'
);
cy.get(dialogCloseButton).click();
closeDialog();
cy.get(newProposalSubmitButton).should('be.visible').click();
// cannot submit a proposal with ERC20 address already in use
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
@@ -377,6 +379,17 @@ context(
'PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE'
);
});
closeDialog();
navigateTo(navigation.proposals);
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.within(() => {
cy.getByTestId(viewProposalBtn).click();
});
cy.getByTestId(proposalTermsSection).within(() => {
cy.contains('USDT Coin').should('be.visible');
cy.contains('USDT').should('be.visible');
});
});
it('Unable to submit new asset proposal with missing/invalid fields', function () {
@@ -415,9 +428,15 @@ context(
getProposalInformationFromTable('Proposed enactment') // 3001-VOTE-044
.invoke('text')
.should('not.be.empty');
// 3001-VOTE-030 3001-VOTE-031
cy.getByTestId(proposalTermsSection).within(() => {
cy.contains('UpdateAsset').should('be.visible');
cy.contains('UpdateERC20').should('be.visible');
cy.contains('"lifetimeLimit": "10"').should('be.visible');
});
});
it('Able to submit update asset proposal using max deadline', function () {
it.only('Able to submit update asset proposal using max deadline', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
enterUpdateAssetProposalDetails();
cy.get(maxVoteDeadline).click();
@@ -183,6 +183,7 @@ context(
// 1004-ASSO-018
// 1004-ASSO-024
// 1004-ASSO-023
// 1004-ASSO-032
stakingPageAssociateTokens('2', {
type: 'contract',
@@ -6,159 +6,203 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.get('nav', { timeout: 10000 }).should('be.visible');
});
describe('with wallets disconnected', function () {
describe('Links and buttons', function () {
it('should have link for proposal page', function () {
cy.getByTestId('home-proposals').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
.and('have.text', 'Browse, vote, and propose');
});
});
it('should show open or enacted proposals with proposal summary', function () {
cy.get('body').then(($body) => {
if (!$body.find('[data-testid="proposals-list-item"]').length) {
cy.createMarket();
cy.reload();
waitForSpinner();
}
});
cy.getByTestId('proposals-list-item')
.should('have.length.at.least', 1)
.first()
.within(() => {
cy.getByTestId('proposal-title')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-type')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-description')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-details')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-status')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('vote-details')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('view-proposal-btn').should('be.visible');
});
});
it('should have external link for governance', function () {
cy.getByTestId('home-proposals').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and('contain', 'https://vega.xyz/governance');
});
});
it('should have link for validator page', function () {
cy.getByTestId('home-validators').within(() => {
cy.get('[href="/validators"]')
.first()
.should('exist')
.and('have.text', 'Browse, and stake');
});
});
it('should have external link for validators', function () {
cy.getByTestId('home-validators').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and(
'contain',
'https://community.vega.xyz/c/mainnet-validator-candidates'
);
});
});
it('should have information on active nodes', function () {
cy.getByTestId('node-information')
.first()
.should('contain.text', '2')
.and('contain.text', 'active nodes');
});
it('should have information on consensus nodes', function () {
cy.getByTestId('node-information')
.last()
.should('contain.text', '2')
.and('contain.text', 'consensus nodes');
});
it('should contain link to specific validators', function () {
cy.getByTestId('validators')
.should('have.length', '2')
.each(($validator) => {
cy.wrap($validator).find('a').should('have.attr', 'href');
});
});
it('should have link for rewards page', function () {
cy.getByTestId('home-rewards').within(() => {
cy.get('[href="/rewards"]')
.first()
.should('exist')
.and('have.text', 'See rewards');
});
});
it('should have link for withdrawal page', function () {
cy.getByTestId('home-vega-token').within(() => {
cy.get('[href="/token/withdraw"]')
.first()
.should('exist')
.and('have.text', 'Manage tokens');
});
describe('Links and buttons', function () {
it('should have link for proposal page', function () {
cy.getByTestId('home-proposals').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
.and('have.text', 'Browse, vote, and propose');
});
});
describe('Mobile view - navigation bar', function () {
before('Change to mobile resolution', function () {
cy.viewport('iphone-xr');
});
it('should have burger button', () => {
cy.getByTestId('button-menu-drawer').should('be.visible').click();
cy.getByTestId('menu-drawer').should('be.visible');
});
it('should have link for proposal page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
.and('have.text', 'Proposals');
it('should display announcement banner', function () {
cy.getByTestId('app-announcement')
.should('be.visible')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
cy.getByTestId('app-announcement-close').should('be.visible').click();
cy.getByTestId('app-announcement').should('not.exist');
});
it('should show open or enacted proposals with proposal summary', function () {
cy.get('body').then(($body) => {
if (!$body.find('[data-testid="proposals-list-item"]').length) {
cy.createMarket();
cy.reload();
waitForSpinner();
}
});
it('should have link for validator page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/validators"]')
cy.getByTestId('proposals-list-item')
.should('have.length.at.least', 1)
.first()
.within(() => {
cy.getByTestId('proposal-title')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-type').invoke('text').should('not.be.empty');
cy.getByTestId('proposal-description')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-details')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-status')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('vote-details').invoke('text').should('not.be.empty');
cy.getByTestId('view-proposal-btn').should('be.visible');
});
});
it('should have external link for governance', function () {
cy.getByTestId('home-proposals').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and('contain', 'https://vega.xyz/governance');
});
});
it('should have link for validator page', function () {
cy.getByTestId('home-validators').within(() => {
cy.get('[href="/validators"]')
.first()
.should('exist')
.and('have.text', 'Browse, and stake');
});
});
it('should have external link for validators', function () {
cy.getByTestId('home-validators').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and(
'contain',
'https://community.vega.xyz/c/mainnet-validator-candidates'
);
});
});
it('should have information on active nodes', function () {
cy.getByTestId('node-information')
.first()
.should('contain.text', '2')
.and('contain.text', 'active nodes');
});
it('should have information on consensus nodes', function () {
cy.getByTestId('node-information')
.last()
.should('contain.text', '2')
.and('contain.text', 'consensus nodes');
});
it('should contain link to specific validators', function () {
cy.getByTestId('validators')
.should('have.length', '2')
.each(($validator) => {
cy.wrap($validator).find('a').should('have.attr', 'href');
});
});
it('should have link for rewards page', function () {
cy.getByTestId('home-rewards').within(() => {
cy.get('[href="/rewards"]')
.first()
.should('exist')
.and('have.text', 'See rewards');
});
});
it('should have link for withdrawal page', function () {
cy.getByTestId('home-vega-token').within(() => {
cy.get('[href="/token/withdraw"]')
.first()
.should('exist')
.and('have.text', 'Manage tokens');
});
});
it('should display network data', function () {
cy.getByTestId('git-network-data')
.should('contain.text', 'Reading network data from')
.within(() => {
cy.get('span')
.first()
.should('exist')
.and('have.text', 'Validators');
.should('have.text', 'http://localhost:3028/query');
cy.getByTestId('link').should('exist');
});
});
});
it('should have link for rewards page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/rewards"]')
.first()
.should('exist')
.and('have.text', 'Rewards');
it('should display eth data', function () {
cy.getByTestId('git-eth-data')
.should('contain.text', 'Reading Ethereum data from')
.within(() => {
cy.get('span').should('have.text', 'http://localhost:8545');
});
});
it('should have link for withdrawal page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/token/withdraw"]')
.first()
.should('exist')
.and('have.text', 'Withdraw');
});
});
});
after(function () {
cy.viewport(
Cypress.config('viewportWidth'),
Cypress.config('viewportHeight')
);
it('should contain link for known issues on Github', function () {
cy.getByTestId('git-info').within(() => {
cy.contains('Known issues and feedback on')
.find('[data-testid="link"]')
.should(
'have.attr',
'href',
'https://github.com/vegaprotocol/feedback/discussions'
);
});
});
});
describe('Mobile view - navigation bar', function () {
before('Change to mobile resolution', function () {
cy.viewport('iphone-xr');
});
it('should have burger button', () => {
cy.getByTestId('button-menu-drawer').should('be.visible').click();
cy.getByTestId('menu-drawer').should('be.visible');
});
it('should have link for proposal page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
.and('have.text', 'Proposals');
});
});
it('should have link for validator page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/validators"]')
.first()
.should('exist')
.and('have.text', 'Validators');
});
});
it('should have link for rewards page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/rewards"]')
.first()
.should('exist')
.and('have.text', 'Rewards');
});
});
it('should have link for withdrawal page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/token/withdraw"]')
.first()
.should('exist')
.and('have.text', 'Withdraw');
});
});
after(function () {
cy.viewport(
Cypress.config('viewportWidth'),
Cypress.config('viewportHeight')
);
});
});
});
@@ -84,3 +84,7 @@ export function verifyEthWalletAssociatedBalance(amount: string) {
.parent(txTimeout)
.should('contain', amount, txTimeout);
}
export function closeDialog() {
cy.getByTestId('dialog-close').click();
}
@@ -1,4 +1,4 @@
import { navigateTo, navigation } from './common.functions';
import { closeDialog, navigateTo, navigation } from './common.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from './staking.functions';
const newProposalButton = '[data-testid="new-proposal-link"]';
@@ -12,7 +12,6 @@ const voteButtons = '[data-testid="vote-buttons"]';
const dialogTitle = '[data-testid="dialog-title"]';
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const dialogCloseButton = '[data-testid="dialog-close"]';
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
@@ -125,7 +124,7 @@ export function voteForProposal(vote: string) {
'have.text',
'Transaction complete'
);
cy.get(dialogCloseButton).click();
closeDialog();
}
export function waitForProposalSync() {
@@ -176,7 +175,7 @@ export function waitForProposalSubmitted() {
'be.visible'
);
cy.contains('Proposal submitted', proposalTimeout).should('be.visible');
cy.get(dialogCloseButton).click();
closeDialog();
}
export function createRawProposal(proposerBalance?: string) {
@@ -1,3 +1,4 @@
import { closeDialog } from './common.functions';
import { vegaWalletTeardown } from './wallet-teardown.functions';
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
@@ -18,7 +19,6 @@ const stakeValidatorListTotalStake = 'total-stake';
const stakeValidatorListTotalShare = 'total-stake-share';
const stakeValidatorListName = '[col-id="validator"]';
const vegaKeySelector = '#vega-key-selector';
const dialogCloseButton = '[data-testid="dialog-close"]';
const txTimeout = Cypress.env('txTimeout');
const epochTimeout = Cypress.env('epochTimeout');
@@ -54,7 +54,7 @@ export function stakingValidatorPageRemoveStake(stake: string) {
.and('contain', `Remove ${stake} $VEGA tokens at the end of epoch`)
.and('be.visible')
.click();
cy.get(dialogCloseButton).click();
closeDialog();
}
export function stakingPageAssociateTokens(
+1 -1
View File
@@ -5,6 +5,7 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_FAIRGROUND=false
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET3":"https://stagnet3.token.vega.xyz","TESTNET":"https://token.fairground.wtf","MAINNET":"https://token.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_CONFIG_URL=''
NX_VEGA_URL=http://localhost:3028/query
@@ -16,7 +17,6 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
#Test configuration variables
CYPRESS_FAIRGROUND=false
+341 -64
View File
@@ -561,7 +561,7 @@
"tranche_end": "2023-04-06T00:00:00.000Z",
"total_added": "14099",
"total_removed": "165.10784124668",
"locked_amount": "3067.69057198327407086",
"locked_amount": "1247.56462776284332054",
"deposits": [
{
"amount": "30",
@@ -3349,7 +3349,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "59299.9407029960677967199",
"locked_amount": "58349.7031275271807221145",
"deposits": [
{
"amount": "86666.297",
@@ -3415,7 +3415,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "861.882504070004",
"locked_amount": "806.91026276963785",
"deposits": [
{
"amount": "2500",
@@ -3536,7 +3536,7 @@
"tranche_end": "2023-09-01T00:00:00.000Z",
"total_added": "17500",
"total_removed": "0",
"locked_amount": "14717.5995118760055",
"locked_amount": "14336.97649330716525",
"deposits": [
{
"amount": "12500",
@@ -3802,8 +3802,8 @@
"tranche_start": "2023-02-01T00:00:00.000Z",
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "11535.201330975",
"locked_amount": "25637.78583486801375",
"total_removed": "11668.32172695",
"locked_amount": "24808.64650475752125",
"deposits": [
{
"amount": "7500",
@@ -3832,6 +3832,11 @@
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
"tx": "0x3f63318adc1797232c2578fa0f5eaccd21318427a3c243490817f778678bb6c6"
},
{
"amount": "133.120395975",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xf440dd4b160a155a08a3cc6e16706d5e4f9609bddd46c495217c377ef538c76f"
},
{
"amount": "92.587476975",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -3952,6 +3957,12 @@
"tranche_id": 34,
"tx": "0xe021c767a9587b28a6772b57666d72122e5d869c477596e246a4365ee6b7ce43"
},
{
"amount": "133.120395975",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 34,
"tx": "0xf440dd4b160a155a08a3cc6e16706d5e4f9609bddd46c495217c377ef538c76f"
},
{
"amount": "92.587476975",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -4062,8 +4073,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "2299.752052575",
"remaining_tokens": "5200.247947425"
"withdrawn_tokens": "2432.87244855",
"remaining_tokens": "5067.12755145"
},
{
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
@@ -4101,7 +4112,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "59245.836968509227749328",
"locked_amount": "58296.4663652644736979915",
"deposits": [
{
"amount": "129999.45",
@@ -4134,7 +4145,7 @@
"tranche_end": "2024-04-01T00:00:00.000Z",
"total_added": "54144.7663",
"total_removed": "0",
"locked_amount": "54144.7663",
"locked_amount": "53810.8835054071292707386",
"deposits": [
{
"amount": "54144.7663",
@@ -4167,7 +4178,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "26882.8490360223239",
"locked_amount": "26196.482198122780824",
"deposits": [
{
"amount": "10000",
@@ -4360,7 +4371,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "2338.973236935566",
"locked_amount": "2284.15160451547415",
"deposits": [
{
"amount": "5000",
@@ -4571,7 +4582,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "1336.84868966454578257644",
"locked_amount": "405.60456575344483061187",
"deposits": [
{
"amount": "97499.58",
@@ -4603,8 +4614,8 @@
"tranche_start": "2022-02-04T00:00:00.000Z",
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "98230.390980249184455396",
"locked_amount": "1827.2413614734815961140086816",
"total_removed": "133818.54965292963650862",
"locked_amount": "554.3914166779487086967342868",
"deposits": [
{
"amount": "135173.4239508",
@@ -4613,6 +4624,11 @@
}
],
"withdrawals": [
{
"amount": "35588.158672680452053224",
"user": "0xc90eA4d8D214D548221EE3622a8BE1D61f7077A2",
"tx": "0xb5e4c868526bc95258404cf26ce9d4dc8522e3a6352ebf503e3808023b45c90d"
},
{
"amount": "98230.390980249184455396",
"user": "0xc90eA4d8D214D548221EE3622a8BE1D61f7077A2",
@@ -4631,6 +4647,12 @@
}
],
"withdrawals": [
{
"amount": "35588.158672680452053224",
"user": "0xc90eA4d8D214D548221EE3622a8BE1D61f7077A2",
"tranche_id": 26,
"tx": "0xb5e4c868526bc95258404cf26ce9d4dc8522e3a6352ebf503e3808023b45c90d"
},
{
"amount": "98230.390980249184455396",
"user": "0xc90eA4d8D214D548221EE3622a8BE1D61f7077A2",
@@ -4639,8 +4661,8 @@
}
],
"total_tokens": "135173.4239508",
"withdrawn_tokens": "98230.390980249184455396",
"remaining_tokens": "36943.032970550815544604"
"withdrawn_tokens": "133818.54965292963650862",
"remaining_tokens": "1354.87429787036349138"
}
]
},
@@ -4650,7 +4672,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "562.38915759984502962888",
"locked_amount": "170.63083639627856110929",
"deposits": [
{
"amount": "32499.86",
@@ -4683,7 +4705,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "183.052213503540805372",
"locked_amount": "55.53868148454478352929",
"deposits": [
{
"amount": "10833.29",
@@ -4716,7 +4738,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "22749.93",
"total_removed": "4720.860935375",
"locked_amount": "684.29005804973844628731",
"locked_amount": "207.61599572971184611278",
"deposits": [
{
"amount": "6500",
@@ -4867,8 +4889,8 @@
"tranche_start": "2022-11-01T00:00:00.000Z",
"tranche_end": "2023-05-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "6111.900993675",
"locked_amount": "3946.20741252302085",
"total_removed": "6244.98110415",
"locked_amount": "3448.723814456722875",
"deposits": [
{
"amount": "7500",
@@ -4892,6 +4914,11 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x2ab0e85bffb0894e741265c4efb962d5f09831002daa4acc92ab87f72917bfe3"
},
{
"amount": "133.080110475",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x0e47007cb85268a4e0c3c1143b96e4c98c9880060c4218babcadd0c82f8247a9"
},
{
"amount": "92.593232025",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -5067,6 +5094,12 @@
"tranche_id": 33,
"tx": "0x2ab0e85bffb0894e741265c4efb962d5f09831002daa4acc92ab87f72917bfe3"
},
{
"amount": "133.080110475",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 33,
"tx": "0x0e47007cb85268a4e0c3c1143b96e4c98c9880060c4218babcadd0c82f8247a9"
},
{
"amount": "92.593232025",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -5249,8 +5282,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "6111.900993675",
"remaining_tokens": "1388.099006325"
"withdrawn_tokens": "6244.98110415",
"remaining_tokens": "1255.01889585"
},
{
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
@@ -5275,7 +5308,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "928642.9598472029154",
"locked_amount": "338796.7791632851902753616",
"locked_amount": "317526.7710493525900617104",
"deposits": [
{
"amount": "1852091.69",
@@ -8284,10 +8317,15 @@
"tranche_id": 10,
"tranche_start": "2021-07-15T23:37:11.000Z",
"tranche_end": "2021-07-15T23:37:11.000Z",
"total_added": "6359302.299000000000000001",
"total_removed": "6313483.280000000000000001",
"total_added": "6459302.299000000000000001",
"total_removed": "6413483.280000000000000001",
"locked_amount": "0",
"deposits": [
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tx": "0x8d1ae9c167843c9e98404d3db6231aa82dbfb942d41b6b9af46d44c2a5e194ab"
},
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@@ -8820,6 +8858,11 @@
}
],
"withdrawals": [
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tx": "0x8d1ae9c167843c9e98404d3db6231aa82dbfb942d41b6b9af46d44c2a5e194ab"
},
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@@ -9295,6 +9338,12 @@
{
"address": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"deposits": [
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tranche_id": 10,
"tx": "0x8d1ae9c167843c9e98404d3db6231aa82dbfb942d41b6b9af46d44c2a5e194ab"
},
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@@ -9489,6 +9538,12 @@
}
],
"withdrawals": [
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tranche_id": 10,
"tx": "0x8d1ae9c167843c9e98404d3db6231aa82dbfb942d41b6b9af46d44c2a5e194ab"
},
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@@ -9676,8 +9731,8 @@
"tx": "0xac16a4ce688d40a482a59914d68c3a676592f8804ee8f0781b66a4ba5ccfbdfc"
}
],
"total_tokens": "3156651",
"withdrawn_tokens": "3156651",
"total_tokens": "3256651",
"withdrawn_tokens": "3256651",
"remaining_tokens": "0"
},
{
@@ -10741,7 +10796,7 @@
"tranche_id": 11,
"tranche_start": "2021-09-03T00:00:00.000Z",
"tranche_end": "2022-09-03T00:00:00.000Z",
"total_added": "57188.000000000000000003",
"total_added": "57213.000000000000000003",
"total_removed": "48157.21518131551",
"locked_amount": "0",
"deposits": [
@@ -10760,6 +10815,11 @@
"user": "0xE9F41a0090fcc7eaf626037003AAD44B17098E7C",
"tx": "0x971e9bcaace01278dc20ecd83d5de588d15be22c1797f4934cfd2ffb357a95ef"
},
{
"amount": "25",
"user": "0x363D3d06d70761c34D6EfEB25E409A9549E6970D",
"tx": "0x180cd3aca2d716aa233483a9a52b02e83d9ac88716ca85d998269965c7a01a96"
},
{
"amount": "150",
"user": "0xEb01fF124D71b6C7E6613fd6E0A86c28C733F008",
@@ -22548,6 +22608,21 @@
"withdrawn_tokens": "0",
"remaining_tokens": "472"
},
{
"address": "0x363D3d06d70761c34D6EfEB25E409A9549E6970D",
"deposits": [
{
"amount": "25",
"user": "0x363D3d06d70761c34D6EfEB25E409A9549E6970D",
"tranche_id": 11,
"tx": "0x180cd3aca2d716aa233483a9a52b02e83d9ac88716ca85d998269965c7a01a96"
}
],
"withdrawals": [],
"total_tokens": "25",
"withdrawn_tokens": "0",
"remaining_tokens": "25"
},
{
"address": "0xEb01fF124D71b6C7E6613fd6E0A86c28C733F008",
"deposits": [
@@ -38966,7 +39041,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "700133.348465855088393",
"locked_amount": "545114.0177930545133092967",
"locked_amount": "512429.420752227829460135708",
"deposits": [
{
"amount": "1998.95815",
@@ -40347,8 +40422,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "812577.59329050683322952",
"locked_amount": "7232626.89306985865129106659442422357718304",
"total_removed": "814470.78267520605435452",
"locked_amount": "7116729.41051479466949006868895061288618547",
"deposits": [
{
"amount": "16249.93",
@@ -40867,6 +40942,21 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xe41c5aea235784efd3ce7c38abcaa73a915e4ad398fbd738c9cf76ee95487de4"
},
{
"amount": "499.227386873675625",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x48e6b25d368a94770abfeffd5b28d7bb8b42269831394aa4b653c6eedd471909"
},
{
"amount": "575.13946745617175",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x83497ea22d041f3b818b20cc112250ea3e38dc24b89a0bc5fee7198d6399a9cc"
},
{
"amount": "818.82253036937375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x58bac20de3636c17156dcb64322cc7612c48f553d941bc039c796ef4167eeeb5"
},
{
"amount": "545.4204142199725",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -42873,6 +42963,24 @@
"tranche_id": 2,
"tx": "0xe41c5aea235784efd3ce7c38abcaa73a915e4ad398fbd738c9cf76ee95487de4"
},
{
"amount": "499.227386873675625",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x48e6b25d368a94770abfeffd5b28d7bb8b42269831394aa4b653c6eedd471909"
},
{
"amount": "575.13946745617175",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x83497ea22d041f3b818b20cc112250ea3e38dc24b89a0bc5fee7198d6399a9cc"
},
{
"amount": "818.82253036937375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x58bac20de3636c17156dcb64322cc7612c48f553d941bc039c796ef4167eeeb5"
},
{
"amount": "545.4204142199725",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -44249,8 +44357,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "141121.612888745726875",
"remaining_tokens": "118877.274611254273125"
"withdrawn_tokens": "143014.802273444948",
"remaining_tokens": "116984.085226555052"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -46595,8 +46703,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "5790314.064810046934064816",
"locked_amount": "955669.740633044212659704752133556",
"total_removed": "5802489.518043560526965506",
"locked_amount": "848673.9247506211806821697552292785",
"deposits": [
{
"amount": "129284.449",
@@ -46830,6 +46938,36 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xa1dcf42aa5cdb34107d55521fd3b28c8dcea0a6a991ba7ec370f091c2a97a610"
},
{
"amount": "4202.72107310637911484",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tx": "0x55259c6fbd43ea2b1e081dc4964c6785ed4d03bb0cafc49de2f07685b056f487"
},
{
"amount": "691.90098422602867775",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xdf89247c6bdb472dcd750818d83cb70cbbe1ed660320ad1df6b1c94fb49f0161"
},
{
"amount": "797.50404161762011275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xf7c393e9346520e22750d3065cb66f49e315599b24406aed2b9570b5a25babe0"
},
{
"amount": "5343.35381683275644592",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tx": "0x5a00c2a54f777e2d2ac0f487c6978d4c0edd8a2b146636e45ba94c6988a330f8"
},
{
"amount": "4.83182614679928618",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tx": "0x14811c8d77a39b0331cb2fdc96049b462916877050cc73143c494277304c2496"
},
{
"amount": "1135.14149158400926325",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xae9047ab72d73da1001c570a12dddc5e82fd523454c1b377928e008da9bdfa85"
},
{
"amount": "755.482067130393772",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -49978,6 +50116,24 @@
"tranche_id": 3,
"tx": "0xa1dcf42aa5cdb34107d55521fd3b28c8dcea0a6a991ba7ec370f091c2a97a610"
},
{
"amount": "691.90098422602867775",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xdf89247c6bdb472dcd750818d83cb70cbbe1ed660320ad1df6b1c94fb49f0161"
},
{
"amount": "797.50404161762011275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xf7c393e9346520e22750d3065cb66f49e315599b24406aed2b9570b5a25babe0"
},
{
"amount": "1135.14149158400926325",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xae9047ab72d73da1001c570a12dddc5e82fd523454c1b377928e008da9bdfa85"
},
{
"amount": "755.482067130393772",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -52656,8 +52812,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "335078.02995866618372525",
"remaining_tokens": "24045.43961633381627475"
"withdrawn_tokens": "337702.576476093841779",
"remaining_tokens": "21420.893098906158221"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@@ -52899,6 +53055,24 @@
"tranche_id": 3,
"tx": "0xbe429d608aa735a1bea0099ee9806e2b0a293262199fa9881a3672215e68a6c3"
},
{
"amount": "4202.72107310637911484",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tranche_id": 3,
"tx": "0x55259c6fbd43ea2b1e081dc4964c6785ed4d03bb0cafc49de2f07685b056f487"
},
{
"amount": "5343.35381683275644592",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tranche_id": 3,
"tx": "0x5a00c2a54f777e2d2ac0f487c6978d4c0edd8a2b146636e45ba94c6988a330f8"
},
{
"amount": "4.83182614679928618",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tranche_id": 3,
"tx": "0x14811c8d77a39b0331cb2fdc96049b462916877050cc73143c494277304c2496"
},
{
"amount": "80421.5336580481619385",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
@@ -53231,8 +53405,8 @@
}
],
"total_tokens": "1266324.603486",
"withdrawn_tokens": "1179718.62954110669784558",
"remaining_tokens": "86605.97394489330215442"
"withdrawn_tokens": "1189269.53625719263269252",
"remaining_tokens": "77055.06722880736730748"
},
{
"address": "0xC5d9221EB9c28A69859264c0A2Fe0d3272228296",
@@ -54061,7 +54235,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "3381706.364737906998719578",
"locked_amount": "60687.4908264066119772663325364137",
"locked_amount": "18412.7968659549219832486449580318",
"deposits": [
{
"amount": "552496.6455",
@@ -56188,8 +56362,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "38870.3290450237949",
"locked_amount": "86376.432123878662920840984880776",
"total_removed": "40152.1324861581949",
"locked_amount": "81197.370889637811460062778488072",
"deposits": [
{
"amount": "3000",
@@ -62823,6 +62997,51 @@
"user": "0xdc040917f80aC350460e55467e4B6c52Fc5e220E",
"tx": "0xd1ded90c18e54cae27c885ec52af8db6260e3eebfd23eb61092de4ad4d20c301"
},
{
"amount": "327.28491882",
"user": "0x872502542bbE5115004B85e230dfA7436245eBEd",
"tx": "0x40aee0670fcfcf9c5514679f7207a6701cd301ddc2ffa2d97af13b92d5e948ef"
},
{
"amount": "0.001065448",
"user": "0x872502542bbE5115004B85e230dfA7436245eBEd",
"tx": "0xe3e87e4860455f3d794d59458dee4622fb4727c49290b747ddf791971d2c9066"
},
{
"amount": "327.28796296",
"user": "0x47F67fF46d5F8Aad8Cc2D77C6Fd893d71350EebE",
"tx": "0x8ceeb54fd69be5d8887aad5bf4d628c274319ee3d9a88db939281108f548623e"
},
{
"amount": "327.288571788",
"user": "0xB0473C4FeBd722a6d7eE561329d3134030318132",
"tx": "0xf41899ffea2bf68e78f747eb32043d983cf3803bd91ce550a70883f2b46f62b7"
},
{
"amount": "28.223592084",
"user": "0xD27929d68ac0E5fd5C919A5eb5968C1D06D3Fb83",
"tx": "0x4c003445f6506b21e28176229f41ae8dcb2d3feafda14d76081aa6b46996069b"
},
{
"amount": "26.371004566",
"user": "0x96d882E908C06cD697Ea07266Fb8D14ae129A50b",
"tx": "0x93540a3930099c8b54157bb83ae50c8ced993009ae2091888f85704e6f13869c"
},
{
"amount": "164.310191526",
"user": "0xfA580dabBa10A5Ef817C7dc26Aaa5fF579D45a9D",
"tx": "0xf1df34ff15a8c7238e188632cf69532edb92f0d8203bf3031e5d19338cb5a9ca"
},
{
"amount": "58.7761643844",
"user": "0xED71B9A9b5633e9d31A0986693658CBbf23c3c1B",
"tx": "0xda5812fda239af9581d2d60c8f4b959e0a8cc5466e5422c5b8fa9df72435d3bc"
},
{
"amount": "22.259969558",
"user": "0x28FC83947F02f59Cb36b40f97f2D32BBC5D00585",
"tx": "0x14da65fbf5ae2f3a593036daa58b3353ab88fc22666e5514f9160e252ad2fdf9"
},
{
"amount": "154.812094114",
"user": "0x27e2254A2A8c9c9D1321E5Fe64cAD88e7A4f0ba7",
@@ -82069,6 +82288,12 @@
}
],
"withdrawals": [
{
"amount": "58.7761643844",
"user": "0xED71B9A9b5633e9d31A0986693658CBbf23c3c1B",
"tranche_id": 5,
"tx": "0xda5812fda239af9581d2d60c8f4b959e0a8cc5466e5422c5b8fa9df72435d3bc"
},
{
"amount": "20.679657534",
"user": "0xED71B9A9b5633e9d31A0986693658CBbf23c3c1B",
@@ -82083,8 +82308,8 @@
}
],
"total_tokens": "180",
"withdrawn_tokens": "89.6315011398",
"remaining_tokens": "90.3684988602"
"withdrawn_tokens": "148.4076655242",
"remaining_tokens": "31.5923344758"
},
{
"address": "0x68927b7A4A360f5B615e104BA2cA2E264a563A33",
@@ -82798,10 +83023,17 @@
"tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "164.310191526",
"user": "0xfA580dabBa10A5Ef817C7dc26Aaa5fF579D45a9D",
"tranche_id": 5,
"tx": "0xf1df34ff15a8c7238e188632cf69532edb92f0d8203bf3031e5d19338cb5a9ca"
}
],
"total_tokens": "200",
"withdrawn_tokens": "0",
"remaining_tokens": "200"
"withdrawn_tokens": "164.310191526",
"remaining_tokens": "35.689808474"
},
{
"address": "0x40c9DF6b867cA219c0Ab4C2E390E8B919Be52e7e",
@@ -84358,6 +84590,12 @@
}
],
"withdrawals": [
{
"amount": "22.259969558",
"user": "0x28FC83947F02f59Cb36b40f97f2D32BBC5D00585",
"tranche_id": 5,
"tx": "0x14da65fbf5ae2f3a593036daa58b3353ab88fc22666e5514f9160e252ad2fdf9"
},
{
"amount": "17.060806698",
"user": "0x28FC83947F02f59Cb36b40f97f2D32BBC5D00585",
@@ -84390,8 +84628,8 @@
}
],
"total_tokens": "200",
"withdrawn_tokens": "142.641089548",
"remaining_tokens": "57.358910452"
"withdrawn_tokens": "164.901059106",
"remaining_tokens": "35.098940894"
},
{
"address": "0xBef628af8547cE5264835F32487055a7e3dF393D",
@@ -84919,6 +85157,12 @@
}
],
"withdrawals": [
{
"amount": "26.371004566",
"user": "0x96d882E908C06cD697Ea07266Fb8D14ae129A50b",
"tranche_id": 5,
"tx": "0x93540a3930099c8b54157bb83ae50c8ced993009ae2091888f85704e6f13869c"
},
{
"amount": "57.900076104",
"user": "0x96d882E908C06cD697Ea07266Fb8D14ae129A50b",
@@ -84939,8 +85183,8 @@
}
],
"total_tokens": "200",
"withdrawn_tokens": "137.635305682",
"remaining_tokens": "62.364694318"
"withdrawn_tokens": "164.006310248",
"remaining_tokens": "35.993689752"
},
{
"address": "0xD27929d68ac0E5fd5C919A5eb5968C1D06D3Fb83",
@@ -84953,6 +85197,12 @@
}
],
"withdrawals": [
{
"amount": "28.223592084",
"user": "0xD27929d68ac0E5fd5C919A5eb5968C1D06D3Fb83",
"tranche_id": 5,
"tx": "0x4c003445f6506b21e28176229f41ae8dcb2d3feafda14d76081aa6b46996069b"
},
{
"amount": "41.084474888",
"user": "0xD27929d68ac0E5fd5C919A5eb5968C1D06D3Fb83",
@@ -85015,8 +85265,8 @@
}
],
"total_tokens": "400",
"withdrawn_tokens": "299.769393708",
"remaining_tokens": "100.230606292"
"withdrawn_tokens": "327.992985792",
"remaining_tokens": "72.007014208"
},
{
"address": "0xF5037DDA4A660d67560200f45380FF8364e35540",
@@ -85305,10 +85555,17 @@
"tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "327.288571788",
"user": "0xB0473C4FeBd722a6d7eE561329d3134030318132",
"tranche_id": 5,
"tx": "0xf41899ffea2bf68e78f747eb32043d983cf3803bd91ce550a70883f2b46f62b7"
}
],
"total_tokens": "400",
"withdrawn_tokens": "0",
"remaining_tokens": "400"
"withdrawn_tokens": "327.288571788",
"remaining_tokens": "72.711428212"
},
{
"address": "0x8aBD57F15cB5f8BD8949EE90c67f77B680caB645",
@@ -85335,10 +85592,17 @@
"tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "327.28796296",
"user": "0x47F67fF46d5F8Aad8Cc2D77C6Fd893d71350EebE",
"tranche_id": 5,
"tx": "0x8ceeb54fd69be5d8887aad5bf4d628c274319ee3d9a88db939281108f548623e"
}
],
"total_tokens": "400",
"withdrawn_tokens": "0",
"remaining_tokens": "400"
"withdrawn_tokens": "327.28796296",
"remaining_tokens": "72.71203704"
},
{
"address": "0x872502542bbE5115004B85e230dfA7436245eBEd",
@@ -85350,10 +85614,23 @@
"tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "327.28491882",
"user": "0x872502542bbE5115004B85e230dfA7436245eBEd",
"tranche_id": 5,
"tx": "0x40aee0670fcfcf9c5514679f7207a6701cd301ddc2ffa2d97af13b92d5e948ef"
},
{
"amount": "0.001065448",
"user": "0x872502542bbE5115004B85e230dfA7436245eBEd",
"tranche_id": 5,
"tx": "0xe3e87e4860455f3d794d59458dee4622fb4727c49290b747ddf791971d2c9066"
}
],
"total_tokens": "400",
"withdrawn_tokens": "0",
"remaining_tokens": "400"
"withdrawn_tokens": "327.285984268",
"remaining_tokens": "72.714015732"
},
{
"address": "0x3f8CEEE4f53d8FFBABbA13E3E90334c6702A8440",
+1
View File
@@ -19,6 +19,7 @@ CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
CYPRESS_ORACLE_PUBKEY=6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
CYPRESS_VEGA_ENV=CUSTOM
@@ -180,7 +180,15 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
'termination.BTC.value'
);
// check that links to github for oracle proofs are shown
cy.getByTestId(accordionContent)
.getByTestId('oracle-proof-links')
.find(`[data-testid="${externalLink}"]`)
.should('have.attr', 'href')
.and('contain', 'https://github.com/vegaprotocol/well-known');
cy.getByTestId(accordionContent)
.getByTestId('oracle-spec-links')
.find(`[data-testid="${externalLink}"]`)
.should('have.attr', 'href')
.and('contain', '/oracles');
@@ -84,6 +84,8 @@ describe('must submit order', { tags: '@smoke' }, () => {
type: Schema.OrderType.TYPE_MARKET,
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
postOnly: false,
reduceOnly: false,
size: '100',
};
createOrder(order);
@@ -98,6 +100,8 @@ describe('must submit order', { tags: '@smoke' }, () => {
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
size: '100',
postOnly: false,
reduceOnly: false,
};
createOrder(order);
testOrderSubmission(order);
@@ -112,6 +116,8 @@ describe('must submit order', { tags: '@smoke' }, () => {
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '200',
};
createOrder(order);
@@ -126,6 +132,8 @@ describe('must submit order', { tags: '@smoke' }, () => {
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GFN,
size: '100',
postOnly: false,
reduceOnly: false,
price: '50000',
};
createOrder(order);
@@ -143,6 +151,8 @@ describe('must submit order', { tags: '@smoke' }, () => {
size: '100',
price: '1.00',
expiresAt: expiresAt.toISOString().substring(0, 16),
postOnly: false,
reduceOnly: false,
};
createOrder(order);
@@ -150,6 +160,8 @@ describe('must submit order', { tags: '@smoke' }, () => {
price: '100000',
expiresAt:
new Date(order.expiresAt as string).getTime().toString() + '000000',
postOnly: false,
reduceOnly: false,
});
});
});
@@ -182,6 +194,8 @@ describe(
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '200',
};
createOrder(order);
@@ -196,6 +210,8 @@ describe(
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '50000',
};
createOrder(order);
@@ -210,12 +226,16 @@ describe(
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTT,
size: '100',
postOnly: false,
reduceOnly: false,
price: '1.00',
expiresAt: displayTomorrow(),
};
createOrder(order);
testOrderSubmission(order, {
price: '100000',
postOnly: false,
reduceOnly: false,
expiresAt:
new Date(order.expiresAt as string).getTime().toString() + '000000',
});
@@ -251,6 +271,8 @@ describe(
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '200',
};
createOrder(order);
@@ -265,6 +287,8 @@ describe(
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '50000',
};
createOrder(order);
@@ -281,6 +305,8 @@ describe(
size: '100',
price: '1.00',
expiresAt: displayTomorrow(),
postOnly: false,
reduceOnly: false,
};
createOrder(order);
testOrderSubmission(order, {
@@ -320,6 +346,8 @@ describe(
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '200',
};
createOrder(order);
@@ -335,6 +363,8 @@ describe(
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
price: '50000',
postOnly: false,
reduceOnly: false,
};
createOrder(order);
testOrderSubmission(order, { price: '5000000000' });
@@ -350,6 +380,8 @@ describe(
size: '100',
price: '1.00',
expiresAt: displayTomorrow(),
postOnly: false,
reduceOnly: false,
};
createOrder(order);
testOrderSubmission(order, {
+84 -1
View File
@@ -35,6 +35,8 @@ type MarketPageMockData = {
trigger?: Schema.AuctionTrigger;
};
const ORACLE_PUBKEY = Cypress.env('ORACLE_PUBKEY');
const marketDataOverride = (
data: MarketPageMockData
): PartialDeep<MarketDataQuery> => ({
@@ -96,7 +98,54 @@ const mockTradingPage = (
aliasGQLQuery(req, 'Margins', marginsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery());
aliasGQLQuery(req, 'Asset', assetQuery());
aliasGQLQuery(req, 'MarketInfo', marketInfoQuery());
aliasGQLQuery(
req,
'MarketInfo',
marketInfoQuery({
market: {
tradableInstrument: {
instrument: {
product: {
dataSourceSpecForSettlementData: {
data: {
sourceType: {
sourceType: {
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: ORACLE_PUBKEY,
},
},
],
},
},
},
},
dataSourceSpecForTradingTermination: {
data: {
sourceType: {
sourceType: {
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: ORACLE_PUBKEY,
},
},
],
},
},
},
},
},
},
},
},
})
);
aliasGQLQuery(req, 'Trades', tradesQuery());
aliasGQLQuery(req, 'Chart', chartQuery());
aliasGQLQuery(req, 'Candles', candlesQuery());
@@ -127,6 +176,40 @@ export const addMockTradingPage = () => {
cy.mockGQL((req) => {
mockTradingPage(req, state, tradingMode, trigger);
});
// Prevent request to github, return some dummy content
cy.intercept(
'GET',
/^https:\/\/raw.githubusercontent.com\/vegaprotocol\/well-known/,
{
body: [
{
name: 'Another oracle',
url: 'https://zombo.com',
description_markdown:
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
oracle: {
status: 'GOOD',
status_reason: '',
first_verified: '2022-01-01T00:00:00.000Z',
last_verified: '2022-12-31T00:00:00.000Z',
type: 'public_key',
public_key: ORACLE_PUBKEY,
},
proofs: [
{
format: 'signed_message',
available: true,
type: 'public_key',
public_key: ORACLE_PUBKEY,
message: 'SOMEHEX',
},
],
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/public_key-${ORACLE_PUBKEY}.toml`,
},
],
}
);
}
);
};
+1
View File
@@ -2,6 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet3/vegawallet-stagnet3.toml
NX_VEGA_ENV=STAGNET3
NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
+211 -147
View File
@@ -1,4 +1,5 @@
import {
getId,
liquidityProvisionsDataProvider,
LiquidityTable,
lpAggregatedDataProvider,
@@ -25,7 +26,7 @@ import {
Indicator,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Header, HeaderStat, HeaderTitle } from '../../components/header';
@@ -37,6 +38,13 @@ import { Link, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { useMarket, useStaticMarketData } from '@vegaprotocol/market-list';
import isEqual from 'lodash/isEqual';
const enum LiquidityTabs {
Active = 'active',
Inactive = 'inactive',
MyLiquidityProvision = 'myLP',
}
export const Liquidity = () => {
const params = useParams();
@@ -48,10 +56,11 @@ const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider,
variables: { marketId: marketId || '' },
update: () => true,
skip: !marketId,
});
useEffect(() => {
const interval = setInterval(reload, 10000);
const interval = setInterval(reload, 30000);
return () => clearInterval(interval);
}, [reload]);
};
@@ -126,47 +135,9 @@ export const LiquidityContainer = ({
);
};
export const LiquidityViewContainer = ({
marketId,
}: {
marketId: string | undefined;
}) => {
const { pubKey } = useVegaWallet();
const gridRef = useRef<AgGridReact | null>(null);
const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId);
const dataRef = useRef<LiquidityProvisionData[] | null>(null);
// To be removed when liquidityProvision subscriptions are working
useReloadLiquidityData(marketId);
const update = useCallback(
({ data }: { data: LiquidityProvisionData[] | null }) => {
if (!gridRef.current?.api) {
return false;
}
if (dataRef.current?.length) {
dataRef.current = data;
gridRef.current.api.refreshInfiniteCache();
return true;
}
return false;
},
[gridRef]
);
const {
data: liquidityProviders,
loading,
error,
} = useDataProvider({
dataProvider: lpAggregatedDataProvider,
update,
variables: { marketId: marketId || '' },
skip: !marketId,
});
const targetStake = marketData?.targetStake;
const suppliedStake = marketData?.suppliedStake;
const assetDecimalPlaces =
@@ -178,44 +149,8 @@ export const LiquidityViewContainer = ({
NetworkParams.market_liquidity_stakeToCcyVolume,
NetworkParams.market_liquidity_targetstake_triggering_ratio,
]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
const triggeringRatio =
params.market_liquidity_targetstake_triggering_ratio || '1';
const myLpEdges = useMemo(
() => liquidityProviders?.filter((e) => e.party.id === pubKey),
[liquidityProviders, pubKey]
);
const activeEdges = useMemo(
() =>
liquidityProviders?.filter(
(e) => e.status === Schema.LiquidityProvisionStatus.STATUS_ACTIVE
),
[liquidityProviders]
);
const inactiveEdges = useMemo(
() =>
liquidityProviders?.filter(
(e) => e.status !== Schema.LiquidityProvisionStatus.STATUS_ACTIVE
),
[liquidityProviders]
);
const enum LiquidityTabs {
Active = 'active',
Inactive = 'inactive',
MyLiquidityProvision = 'myLP',
}
const getActiveDefaultId = () => {
if (myLpEdges && myLpEdges.length > 0) {
return LiquidityTabs.MyLiquidityProvision;
}
if (activeEdges?.length) return LiquidityTabs.Active;
else if (inactiveEdges && inactiveEdges.length > 0) {
return LiquidityTabs.Inactive;
}
return LiquidityTabs.Active;
};
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0,
@@ -223,66 +158,197 @@ export const LiquidityViewContainer = ({
triggeringRatio,
});
return (
<Header
title={
market?.tradableInstrument.instrument.name &&
market?.tradableInstrument.instrument.code &&
marketId && (
<HeaderTitle
primaryContent={`${market.tradableInstrument.instrument.code} ${t(
'liquidity provision'
)}`}
secondaryContent={
<Link to={Links[Routes.MARKET](marketId)}>
<UiToolkitLink>{t('Go to trading')}</UiToolkitLink>
</Link>
}
/>
)
}
>
<HeaderStat
heading={t('Target stake')}
description={tooltipMapping['targetStake']}
>
<div>
{targetStake
? `${addDecimalsFormatNumber(
targetStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat
heading={t('Supplied stake')}
description={tooltipMapping['suppliedStake']}
>
<div>
{suppliedStake
? `${addDecimalsFormatNumber(
suppliedStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat heading={t('Liquidity supplied')} testId="liquidity-supplied">
<Indicator variant={status} />
{formatNumberPercentage(percentage, 2)}
</HeaderStat>
<HeaderStat heading={t('Market ID')}>
<div className="break-word">{marketId}</div>
</HeaderStat>
</Header>
);
});
LiquidityViewHeader.displayName = 'LiquidityViewHeader';
const filterLiquidities = (
tab: string,
liquidities?: LiquidityProvisionData[] | null,
pubKey?: string | null
) => {
switch (tab) {
case LiquidityTabs.MyLiquidityProvision:
return pubKey
? (liquidities || []).filter((e) => e.party.id === pubKey)
: [];
break;
case LiquidityTabs.Active:
return (liquidities || []).filter(
(e) => e.status === Schema.LiquidityProvisionStatus.STATUS_ACTIVE
);
case LiquidityTabs.Inactive:
return (liquidities || []).filter(
(e) => e.status !== Schema.LiquidityProvisionStatus.STATUS_ACTIVE
);
default:
return [];
}
};
export const LiquidityViewContainer = ({
marketId,
}: {
marketId: string | undefined;
}) => {
const [tab, setTab] = useState('');
const { pubKey } = useVegaWallet();
const [liquidityProviders, setLiquidityProviders] = useState<
LiquidityProvisionData[] | null
>();
const gridRef = useRef<AgGridReact | null>(null);
const { data: market } = useMarket(marketId);
const dataRef = useRef<LiquidityProvisionData[] | null>(null);
// To be removed when liquidityProvision subscriptions are working
useReloadLiquidityData(marketId);
const update = useCallback(
({ data }: { data: LiquidityProvisionData[] | null }) => {
if (!dataRef.current) {
setLiquidityProviders(data);
dataRef.current = data;
}
if (!gridRef.current?.api) {
return false;
}
const updateRows: LiquidityProvisionData[] = [];
const addRows: LiquidityProvisionData[] = [];
if (gridRef.current?.api?.getModel().getType() === 'infinite') {
dataRef.current = data;
gridRef.current.api.refreshInfiniteCache();
} else {
const filteredData = filterLiquidities(tab, data, pubKey as string);
filteredData?.forEach((d) => {
const rowNode = gridRef.current?.api?.getRowNode(getId(d));
if (rowNode) {
if (!isEqual(rowNode.data, d)) {
updateRows.push(d);
}
} else {
addRows.push(d);
}
});
gridRef.current?.api?.applyTransaction({
update: updateRows,
add: addRows,
addIndex: 0,
});
}
return true;
},
[gridRef, tab, pubKey]
);
const { loading, error } = useDataProvider({
dataProvider: lpAggregatedDataProvider,
update,
variables: { marketId: marketId || '' },
skip: !marketId,
});
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
NetworkParams.market_liquidity_targetstake_triggering_ratio,
]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
const myLpEdges = useMemo(
() =>
filterLiquidities(
LiquidityTabs.MyLiquidityProvision,
liquidityProviders,
pubKey
),
[liquidityProviders, pubKey]
);
const activeEdges = useMemo(
() => filterLiquidities(LiquidityTabs.Active, liquidityProviders),
[liquidityProviders]
);
const inactiveEdges = useMemo(
() => filterLiquidities(LiquidityTabs.Inactive, liquidityProviders),
[liquidityProviders]
);
useEffect(() => {
if (tab) {
return;
}
let initialTab = LiquidityTabs.Active;
if (myLpEdges.length > 0) {
initialTab = LiquidityTabs.MyLiquidityProvision;
}
if (activeEdges?.length) {
initialTab = LiquidityTabs.Active;
} else if (inactiveEdges.length > 0) {
initialTab = LiquidityTabs.Inactive;
}
setTab(initialTab);
}, [tab, myLpEdges?.length, activeEdges?.length, inactiveEdges?.length]);
return (
<AsyncRenderer loading={loading} error={error} data={liquidityProviders}>
<div className="h-full grid grid-rows-[min-content_1fr]">
<Header
title={
market?.tradableInstrument.instrument.name &&
market?.tradableInstrument.instrument.code &&
marketId && (
<HeaderTitle
primaryContent={`${
market.tradableInstrument.instrument.code
} ${t('liquidity provision')}`}
secondaryContent={
<Link to={Links[Routes.MARKET](marketId)}>
<UiToolkitLink>{t('Go to trading')}</UiToolkitLink>
</Link>
}
/>
)
}
>
<HeaderStat
heading={t('Target stake')}
description={tooltipMapping['targetStake']}
>
<div>
{targetStake
? `${addDecimalsFormatNumber(
targetStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat
heading={t('Supplied stake')}
description={tooltipMapping['suppliedStake']}
>
<div>
{suppliedStake
? `${addDecimalsFormatNumber(
suppliedStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat
heading={t('Liquidity supplied')}
testId="liquidity-supplied"
>
<Indicator variant={status} />
{formatNumberPercentage(percentage, 2)}
</HeaderStat>
<HeaderStat heading={t('Market ID')}>
<div className="break-word">{marketId}</div>
</HeaderStat>
</Header>
<Tabs defaultValue={getActiveDefaultId()}>
<LiquidityViewHeader marketId={marketId} />
<Tabs value={tab} onValueChange={setTab}>
<Tab
id={LiquidityTabs.MyLiquidityProvision}
name={t('My liquidity provision')}
@@ -309,19 +375,17 @@ export const LiquidityViewContainer = ({
/>
)}
</Tab>
{
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
{inactiveEdges && (
<LiquidityTable
ref={gridRef}
rowData={inactiveEdges}
symbol={symbol}
assetDecimalPlaces={assetDecimalPlaces}
stakeToCcyVolume={stakeToCcyVolume}
/>
)}
</Tab>
}
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
{inactiveEdges && (
<LiquidityTable
ref={gridRef}
rowData={inactiveEdges}
symbol={symbol}
assetDecimalPlaces={assetDecimalPlaces}
stakeToCcyVolume={stakeToCcyVolume}
/>
)}
</Tab>
</Tabs>
</div>
</AsyncRenderer>
+3 -9
View File
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
@@ -13,6 +13,7 @@ import { useGlobalStore, usePageTitleStore } from '../../stores';
import { TradeGrid, TradePanels } from './trade-grid';
import { useNavigate, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
return markPrice && decimalPlaces
@@ -66,14 +67,7 @@ export const MarketPage = () => {
const update = useGlobalStore((store) => store.update);
const lastMarketId = useGlobalStore((store) => store.marketId);
const onSelect = useCallback(
(id: string) => {
if (id && id !== marketId) {
navigate(Links[Routes.MARKET](id));
}
},
[marketId, navigate]
);
const onSelect = useMarketClickHandler();
const { data, error, loading } = useDataProvider({
dataProvider: marketProvider,
@@ -8,7 +8,7 @@ import { TradesContainer } from '@vegaprotocol/trades';
import { LayoutPriority } from 'allotment';
import classNames from 'classnames';
import AutoSizer from 'react-virtualized-auto-sizer';
import { memo, useCallback, useState } from 'react';
import { memo, useState } from 'react';
import type { ReactNode, ComponentProps } from 'react';
import { DepthChartContainer } from '@vegaprotocol/market-depth';
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
@@ -27,9 +27,9 @@ import { TradeMarketHeader } from './trade-market-header';
import { NO_MARKET } from './constants';
import { LiquidityContainer } from '../liquidity/liquidity';
import { useNavigate } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import type { PinnedAsset } from '@vegaprotocol/accounts';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
type MarketDependantView =
| typeof CandlesChartContainer
@@ -66,7 +66,7 @@ type TradingView = keyof typeof TradingViews;
interface TradeGridProps {
market: Market | null;
onSelect: (marketId: string) => void;
onSelect: (marketId: string, metaKey?: boolean) => void;
pinnedAsset?: PinnedAsset;
}
@@ -78,15 +78,7 @@ interface BottomPanelProps {
const MarketBottomPanel = memo(
({ marketId, pinnedAsset }: BottomPanelProps) => {
const { screenSize } = useScreenDimensions();
const navigate = useNavigate();
const onMarketClick = useCallback(
(marketId: string) => {
navigate(Links[Routes.MARKET](marketId), {
replace: true,
});
},
[navigate]
);
const onMarketClick = useMarketClickHandler(true);
return 'xxxl' === screenSize ? (
<ResizableGrid proportionalLayout minSize={200}>
@@ -189,7 +181,7 @@ const MainGrid = memo(
pinnedAsset,
}: {
marketId: string;
onSelect?: (marketId: string) => void;
onSelect: (marketId: string, metaKey?: boolean) => void;
pinnedAsset?: PinnedAsset;
}) => {
const navigate = useNavigate();
@@ -230,12 +222,7 @@ const MainGrid = memo(
/>
</Tab>
<Tab id="info" name={t('Info')}>
<TradingViews.Info
marketId={marketId}
onSelect={(id: string) => {
onSelect?.(id);
}}
/>
<TradingViews.Info marketId={marketId} />
</Tab>
</Tabs>
</TradeGridChild>
@@ -304,7 +291,7 @@ const TradeGridChild = ({ children }: TradeGridChildProps) => {
interface TradePanelsProps {
market: Market | null;
onSelect: (marketId: string) => void;
onSelect: (marketId: string, metaKey?: boolean) => void;
onMarketClick?: (marketId: string) => void;
onClickCollateral: () => void;
pinnedAsset?: PinnedAsset;
@@ -320,7 +307,7 @@ export const TradePanels = ({
const renderView = () => {
const Component = memo<{
marketId: string;
onSelect: (marketId: string) => void;
onSelect: (marketId: string, metaKey?: boolean) => void;
onMarketClick?: (marketId: string) => void;
onClickCollateral: () => void;
pinnedAsset?: PinnedAsset;
@@ -22,7 +22,7 @@ import { MarketState as State } from '@vegaprotocol/types';
interface TradeMarketHeaderProps {
market: Market | null;
onSelect: (marketId: string) => void;
onSelect: (marketId: string, metaKey?: boolean) => void;
}
export const TradeMarketHeader = ({
@@ -91,7 +91,6 @@ export const TradeMarketHeader = ({
</HeaderStat>
<HeaderStatMarketTradingMode
marketId={market?.id}
onSelect={onSelect}
initialTradingMode={market?.tradingMode}
/>
<MarketState market={market} />
+2 -11
View File
@@ -1,16 +1,7 @@
import { useCallback } from 'react';
import { MarketsContainer } from '@vegaprotocol/market-list';
import { useNavigate } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
export const Markets = () => {
const navigate = useNavigate();
const handleOnSelect = useCallback(
(marketId: string) => {
navigate(Links[Routes.MARKET](marketId));
},
[navigate]
);
const handleOnSelect = useMarketClickHandler();
return <MarketsContainer onSelect={handleOnSelect} />;
};
@@ -19,25 +19,18 @@ import { usePageTitleStore } from '../../stores';
import { LedgerContainer } from '@vegaprotocol/ledger';
import { AccountsContainer } from '../../components/accounts-container';
import { AccountHistoryContainer } from './account-history-container';
import { useNavigate } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
export const Portfolio = () => {
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
}));
const navigate = useNavigate();
useEffect(() => {
updateTitle(titlefy([t('Portfolio')]));
}, [updateTitle]);
const onMarketClick = (marketId: string) => {
navigate(Links[Routes.MARKET](marketId), {
replace: true,
});
};
const onMarketClick = useMarketClickHandler(true);
const wrapperClasses = 'h-full max-h-full flex flex-col';
return (
@@ -25,7 +25,7 @@ const getTradingModeLabel = (
interface HeaderStatMarketTradingModeProps {
marketId?: string;
onSelect?: (marketId: string) => void;
onSelect?: (marketId: string, metaKey?: boolean) => void;
initialTradingMode?: Schema.MarketTradingMode;
initialTrigger?: Schema.AuctionTrigger;
}
@@ -66,7 +66,9 @@ export const MarketTradingMode = ({
return (
<Tooltip
description={<TradingModeTooltip marketId={marketId} skip={!inView} />}
description={
<TradingModeTooltip marketId={marketId} skip={!inView} skipGrid />
}
>
<span ref={ref}>
{getTradingModeLabel(
+5 -1
View File
@@ -23,6 +23,7 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { Links, Routes } from '../../pages/client-router';
import { createDocsLinks } from '@vegaprotocol/utils';
export const Navbar = ({
theme = 'system',
@@ -37,6 +38,7 @@ export const Navbar = ({
const tradingPath = marketId
? Links[Routes.MARKET](marketId)
: Links[Routes.MARKET]();
return (
<Navigation
appName="Console"
@@ -89,7 +91,9 @@ export const Navbar = ({
<NavigationContent>
<NavigationList>
<NavigationItem>
<NavExternalLink href={VEGA_DOCS_URL}>
<NavExternalLink
href={createDocsLinks(VEGA_DOCS_URL).NEW_TO_VEGA}
>
{t('Docs')}
</NavExternalLink>
</NavigationItem>
@@ -1,4 +1,4 @@
import type { RefObject } from 'react';
import type { RefObject, MouseEvent } from 'react';
import { FeesCell } from '@vegaprotocol/market-info';
import {
calcCandleHigh,
@@ -157,14 +157,14 @@ export const columnHeaders: Column[] = [
];
export type OnCellClickHandler = (
e: React.MouseEvent,
e: MouseEvent,
kind: ColumnKind,
value: string
) => void;
export const columns = (
market: MarketMaybeWithDataAndCandles,
onSelect: (id: string) => void,
onSelect: (id: string, metaKey?: boolean) => void,
onCellClick: OnCellClickHandler,
inViewRoot?: RefObject<HTMLElement>
) => {
@@ -174,14 +174,7 @@ export const columns = (
const candleLow = market.candles && calcCandleLow(market.candles);
const candleHigh = market.candles && calcCandleHigh(market.candles);
const candleVolume = market.candles && calcCandleVolume(market.candles);
const handleKeyPress = (
event: React.KeyboardEvent<HTMLAnchorElement>,
id: string
) => {
if (event.key === 'Enter' && onSelect) {
return onSelect(id);
}
};
const selectMarketColumns: Column[] = [
{
kind: ColumnKind.Market,
@@ -189,10 +182,10 @@ export const columns = (
<Link
to={Links[Routes.MARKET](market.id)}
data-testid={`market-link-${market.id}`}
onKeyPress={(event) => handleKeyPress(event, market.id)}
onClick={(e) => {
e.preventDefault();
onSelect(market.id);
e.stopPropagation();
onSelect(market.id, e.metaKey);
}}
>
<UILink>{market.tradableInstrument.instrument.code}</UILink>
@@ -352,7 +345,7 @@ export const columns = (
export const columnsPositionMarkets = (
market: MarketMaybeWithDataAndCandles,
onSelect: (id: string) => void,
onSelect: (id: string, metaKey?: boolean) => void,
inViewRoot?: RefObject<HTMLElement>,
openVolume?: string,
onCellClick?: OnCellClickHandler
@@ -362,14 +355,6 @@ export const columnsPositionMarkets = (
.filter((c: string | undefined): c is CandleClose => !isNil(c));
const candleLow = market.candles && calcCandleLow(market.candles);
const candleHigh = market.candles && calcCandleHigh(market.candles);
const handleKeyPress = (
event: React.KeyboardEvent<HTMLSpanElement>,
id: string
) => {
if (event.key === 'Enter' && onSelect) {
return onSelect(id);
}
};
const candleVolume = market.candles && calcCandleVolume(market.candles);
const selectMarketColumns: Column[] = [
{
@@ -378,10 +363,10 @@ export const columnsPositionMarkets = (
<Link
to={Links[Routes.MARKET](market.id)}
data-testid={`market-link-${market.id}`}
onKeyPress={(event) => handleKeyPress(event, market.id)}
onClick={(e) => {
e.preventDefault();
onSelect(market.id);
e.stopPropagation();
onSelect(market.id, e.metaKey);
}}
>
<UILink>{market.tradableInstrument.instrument.code}</UILink>
@@ -37,14 +37,14 @@ export const SelectMarketTableRow = ({
}: {
detailed?: boolean;
columns: Column[];
onSelect: (id: string) => void;
onSelect: (id: string, metaKey?: boolean) => void;
marketId: string;
}) => {
return (
<tr
className={`hover:bg-neutral-200 dark:hover:bg-neutral-700 cursor-pointer relative h-[34px]`}
onClick={() => {
onSelect(marketId);
onClick={(ev) => {
onSelect(marketId, ev.metaKey);
}}
data-testid={`market-link-${marketId}`}
>
@@ -178,6 +178,6 @@ describe('SelectMarket', () => {
expect(screen.getByText('25.00%')).toBeTruthy(); // price change
expect(container).toHaveTextContent(/1,000/); // volume
fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]);
expect(onSelect).toHaveBeenCalledWith('1');
expect(onSelect).toHaveBeenCalledWith('1', false);
});
});
@@ -40,7 +40,7 @@ export const SelectAllMarketsTableBody = ({
markets?: MarketMaybeWithDataAndCandles[] | null;
positions?: PositionFieldsFragment[];
title?: string;
onSelect: (id: string) => void;
onSelect: (id: string, metaKey?: boolean) => void;
onCellClick: OnCellClickHandler;
headers?: Column[];
tableColumns?: (
@@ -95,7 +95,7 @@ export const SelectMarketPopover = ({
}: {
marketCode: string;
marketName: string;
onSelect: (id: string) => void;
onSelect: (id: string, metaKey?: boolean) => void;
onCellClick: OnCellClickHandler;
}) => {
const { pubKey } = useVegaWallet();
@@ -116,8 +116,8 @@ export const SelectMarketPopover = ({
skip: !pubKey,
});
const onSelectMarket = useCallback(
(marketId: string) => {
onSelect(marketId);
(marketId: string, metaKey?: boolean) => {
onSelect(marketId, metaKey);
setOpen(false);
},
[onSelect]
@@ -0,0 +1,21 @@
import { useNavigate, useParams, useLocation } from 'react-router-dom';
import { useCallback } from 'react';
import { Links, Routes } from '../../pages/client-router';
export const useMarketClickHandler = (replace = false) => {
const navigate = useNavigate();
const { marketId } = useParams();
const { pathname } = useLocation();
const isMarketPage = pathname.match(/^\/markets\/(.+)/);
return useCallback(
(selectedId: string, metaKey?: boolean) => {
const link = Links[Routes.MARKET](selectedId);
if (metaKey) {
window.open(`/#${link}`, '_blank');
} else if (selectedId !== marketId || !isMarketPage) {
navigate(link, { replace });
}
},
[navigate, marketId, replace, isMarketPage]
);
};
@@ -474,15 +474,17 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
}
if (tx.order && tx.order.rejectionReason) {
const rejectionReason = getRejectionReason(tx.order) || ' ';
return (
<>
<ToastHeading>{t('Order rejected')}</ToastHeading>
<p>
{t(
'Your order has been rejected because: %s',
getRejectionReason(tx.order) || ''
)}
</p>
{rejectionReason ? (
<p>
{t('Your order has been rejected because: %s', [rejectionReason])}
</p>
) : (
<p>{t('Your order has been rejected.')}</p>
)}
{tx.txHash && (
<p className="break-all">
<ExternalLink
@@ -576,10 +578,9 @@ const VegaTxErrorToastContent = ({ tx }: VegaTxToastContentProps) => {
walletNoConnectionCodes.includes(tx.error.code);
if (orderRejection) {
label = t('Order rejected');
errorMessage = t(
'Your order has been rejected because: %s',
orderRejection
);
errorMessage = t('Your order has been rejected because: %s', [
orderRejection,
]);
}
if (walletError) {
label = t('Wallet disconnected');
+4 -4
View File
@@ -1,3 +1,5 @@
import { useMemo, useState } from 'react';
import classNames from 'classnames';
import Head from 'next/head';
import type { AppProps } from 'next/app';
import { t } from '@vegaprotocol/i18n';
@@ -23,14 +25,12 @@ import {
import './styles.css';
import { useGlobalStore, usePageTitleStore } from '../stores';
import { Footer } from '../components/footer';
import { useMemo, useState } from 'react';
import DialogsContainer from './dialogs-container';
import ToastsManager from './toasts-manager';
import { HashRouter, useLocation, useSearchParams } from 'react-router-dom';
import { Connectors } from '../lib/vega-connectors';
import { ViewingBanner } from '../components/viewing-banner';
import { Banner } from '../components/banner';
import classNames from 'classnames';
import { AppLoader, DynamicLoader } from '../components/app-loader';
import { Navbar } from '../components/navbar';
@@ -57,7 +57,7 @@ const Title = () => {
);
};
const TransactionsHandler = () => {
const InitializeHandlers = () => {
useVegaTransactionManager();
useVegaTransactionUpdater();
useEthTransactionManager();
@@ -93,7 +93,7 @@ function AppBody({ Component }: AppProps) {
</div>
<DialogsContainer />
<ToastsManager />
<TransactionsHandler />
<InitializeHandlers />
<MaybeConnectEagerly />
</div>
);
+1
View File
@@ -9,6 +9,7 @@ export * from './lib/cells/price-change-cell';
export * from './lib/cells/price-flash-cell';
export * from './lib/cells/vol-cell';
export * from './lib/cells/centered-grid-cell';
export * from './lib/cells/market-name-cell';
export * from './lib/filters/date-range-filter';
export * from './lib/filters/set-filter';
@@ -0,0 +1,35 @@
import type { MouseEvent } from 'react';
import { useCallback } from 'react';
import get from 'lodash/get';
interface MarketNameCellProps {
value?: string;
data?: { id?: string; marketId?: string; market?: { id: string } };
idPath?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}
export const MarketNameCell = ({
value,
data,
idPath,
onMarketClick,
}: MarketNameCellProps) => {
const id = data ? get(data, idPath ?? 'id', 'all') : '';
const handleOnClick = useCallback(
(ev: MouseEvent<HTMLButtonElement>) => {
ev.preventDefault();
ev.stopPropagation();
if (onMarketClick) {
onMarketClick(id, ev.metaKey);
}
},
[id, onMarketClick]
);
if (!data) return null;
return (
<button onClick={handleOnClick} tabIndex={0}>
{value}
</button>
);
};
@@ -107,6 +107,50 @@ describe('DealTicket', () => {
);
});
it('should use local storage state for initial values reduceOnly and postOnly', () => {
const expectedOrder = {
marketId: market.id,
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_SELL,
size: '0.1',
price: '300.22',
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
persist: true,
reduceOnly: true,
postOnly: false,
};
useOrderStore.setState({
orders: {
[expectedOrder.marketId]: expectedOrder,
},
});
render(generateJsx());
// Assert correct defaults are used from store
expect(
screen
.getByTestId(`order-type-${Schema.OrderType.TYPE_LIMIT}`)
.querySelector('input')
).toBeChecked();
expect(
screen.queryByTestId('order-side-SIDE_SELL')?.querySelector('input')
).toBeChecked();
expect(
screen.queryByTestId('order-side-SIDE_BUY')?.querySelector('input')
).not.toBeChecked();
expect(screen.getByTestId('order-size')).toHaveDisplayValue(
expectedOrder.size
);
expect(screen.getByTestId('order-tif')).toHaveValue(
expectedOrder.timeInForce
);
expect(screen.getByTestId('order-price')).toHaveDisplayValue(
expectedOrder.price
);
});
it('handles TIF select box dependent on order type', async () => {
render(generateJsx());
@@ -1,6 +1,6 @@
import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import { memo, useCallback, useEffect, useState, useRef } from 'react';
import { memo, useCallback, useEffect, useState, useRef, useMemo } from 'react';
import { Controller } from 'react-hook-form';
import { DealTicketAmount } from './deal-ticket-amount';
import { DealTicketButton } from './deal-ticket-button';
@@ -16,10 +16,12 @@ import {
useVegaWalletDialogStore,
} from '@vegaprotocol/wallet';
import {
Checkbox,
ExternalLink,
InputError,
Intent,
Notification,
Tooltip,
TinyScroll,
} from '@vegaprotocol/ui-toolkit';
@@ -146,6 +148,16 @@ export const DealTicket = ({
clearErrors,
]);
const disablePostOnlyCheckbox = useMemo(() => {
const disabled = order
? [
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
].includes(order.timeInForce)
: true;
return disabled;
}, [order]);
const onSubmit = useCallback(
(order: OrderSubmission) => {
const now = new Date().getTime();
@@ -239,7 +251,7 @@ export const DealTicket = ({
value={order.timeInForce}
orderType={order.type}
onSelect={(timeInForce) => {
update({ timeInForce });
update({ timeInForce, postOnly: false, reduceOnly: false });
// Set tif value for the given order type, so that when switching
// types we know the last used TIF for the given order type
setLastTIF((curr) => ({
@@ -276,6 +288,65 @@ export const DealTicket = ({
)}
/>
)}
<div className="flex gap-2 pb-2 justify-between">
<Controller
name="postOnly"
control={control}
render={() => (
<Checkbox
name="post-only"
checked={order.postOnly}
disabled={disablePostOnlyCheckbox}
onCheckedChange={() => {
update({ postOnly: !order.postOnly, reduceOnly: false });
}}
label={
<Tooltip
description={
<span>
{disablePostOnlyCheckbox
? t(
'"Post only" can not be used on "Fill or Kill" or "Immediate or Cancel" orders.'
)
: t(
'"Post only" will ensure the order is not filled immediately but is placed on the order book as a passive order. When the order is processed it is either stopped (if it would not be filled immediately), or placed in the order book as a passive order until the price taker matches with it.'
)}
</span>
}
>
<span className="text-xs">{t('Post only')}</span>
</Tooltip>
}
/>
)}
/>
<Controller
name="reduceOnly"
control={control}
render={() => (
<Checkbox
name="reduce-only"
checked={order.reduceOnly}
onCheckedChange={() => {
update({ postOnly: false, reduceOnly: !order.reduceOnly });
}}
label={
<Tooltip
description={
<span>
{t(
'"Reduce only" will ensure that this order will not increase the size of an open position. When the order is matched, it will only trade enough volume to bring your open volume towards 0 but never change the direction of your position. If applied to a limit order that is not instantly filled, the order will be stopped.'
)}
</span>
}
>
<span className="text-xs">{t('Reduce only')}</span>
</Tooltip>
}
/>
)}
/>
</div>
<SummaryMessage
errorMessage={errors.summary?.message}
asset={asset}
@@ -96,16 +96,6 @@ export const TimeInForceSelector = ({
id="select-time-in-force"
value={value}
onChange={(e) => {
// setPreviousTimeInForce({
// ...previousTimeInForce,
// [orderType]: e.target.value,
// });
// if (previousOrderType !== orderType) {
// setPreviousOrderType(orderType);
// const prev = previousTimeInForce[orderType as OrderType];
// onSelect(prev);
// }
onSelect(e.target.value as Schema.OrderTimeInForce);
}}
className="w-full"
@@ -26,7 +26,7 @@ export const compileGridData = (
| 'targetStake'
| 'trigger'
> | null,
onSelect?: (id: string) => void
onSelect?: (id: string, metaKey?: boolean) => void
): { label: ReactNode; value?: ReactNode }[] => {
const grid: SimpleGridProps['grid'] = [];
const isLiquidityMonitoringAuction =
@@ -78,7 +78,7 @@ export const compileGridData = (
label: (
<Link
to={`/liquidity/${market.id}`}
onClick={() => onSelect && onSelect(market.id)}
onClick={(ev) => onSelect && onSelect(market.id, ev.metaKey)}
>
<UILink>{t('Current liquidity')}</UILink>
</Link>
@@ -12,14 +12,16 @@ import { useMarket, useStaticMarketData } from '@vegaprotocol/market-list';
type TradingModeTooltipProps = {
marketId?: string;
onSelect?: (marketId: string) => void;
onSelect?: (marketId: string, metaKey?: boolean) => void;
skip?: boolean;
skipGrid?: boolean;
};
export const TradingModeTooltip = ({
marketId,
onSelect,
skip,
skipGrid,
}: TradingModeTooltipProps) => {
const { VEGA_DOCS_URL } = useEnvironment();
const { data: market } = useMarket(marketId);
@@ -42,7 +44,7 @@ export const TradingModeTooltip = ({
);
const compiledGrid =
onSelect && compileGridData(market, marketData, onSelect);
!skipGrid && compileGridData(market, marketData, onSelect);
switch (marketTradingMode) {
case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS: {
@@ -103,6 +105,7 @@ export const TradingModeTooltip = ({
{VEGA_DOCS_URL && (
<ExternalLink
href={createDocsLinks(VEGA_DOCS_URL).AUCTION_TYPE_OPENING}
className="ml-1"
>
{t('Find out more')}
</ExternalLink>
@@ -129,6 +132,7 @@ export const TradingModeTooltip = ({
createDocsLinks(VEGA_DOCS_URL)
.AUCTION_TYPE_LIQUIDITY_MONITORING
}
className="ml-1"
>
{t('Find out more')}
</ExternalLink>
@@ -153,6 +157,7 @@ export const TradingModeTooltip = ({
createDocsLinks(VEGA_DOCS_URL)
.AUCTION_TYPE_LIQUIDITY_MONITORING
}
className="ml-1"
>
{t('Find out more')}
</ExternalLink>
@@ -175,6 +180,7 @@ export const TradingModeTooltip = ({
createDocsLinks(VEGA_DOCS_URL)
.AUCTION_TYPE_PRICE_MONITORING
}
className="ml-1"
>
{t('Find out more')}
</ExternalLink>
+5
View File
@@ -1,2 +1,7 @@
import '@testing-library/jest-dom';
import 'jest-canvas-mock';
import ResizeObserver from 'resize-observer-polyfill';
import { defaultFallbackInView } from 'react-intersection-observer';
defaultFallbackInView(true);
global.ResizeObserver = ResizeObserver;
@@ -277,6 +277,7 @@ function compileEnvVars() {
),
ETH_LOCAL_PROVIDER_URL: process.env['NX_ETH_LOCAL_PROVIDER_URL'],
ETH_WALLET_MNEMONIC: process.env['NX_ETH_WALLET_MNEMONIC'],
ORACLE_PROOFS_URL: process.env['NX_ORACLE_PROOFS_URL'],
VEGA_DOCS_URL: process.env['NX_VEGA_DOCS_URL'],
VEGA_EXPLORER_URL: process.env['NX_VEGA_EXPLORER_URL'],
VEGA_TOKEN_URL: process.env['NX_VEGA_TOKEN_URL'],
-1
View File
@@ -21,7 +21,6 @@ const EmptyLinks: DAppLinks = {
[Networks.STAGNET3]: '',
[Networks.TESTNET]: '',
[Networks.MAINNET]: '',
[Networks.MIRROR]: '',
};
const ExplorerLinks = {
@@ -20,6 +20,7 @@ const schemaObject = {
GIT_COMMIT_HASH: z.optional(z.string()),
GIT_ORIGIN_URL: z.optional(z.string()),
GITHUB_FEEDBACK_URL: z.optional(z.string()),
ORACLE_PROOFS_URL: z.optional(z.string().url()),
VEGA_ENV: z.nativeEnum(Networks),
VEGA_EXPLORER_URL: z.optional(z.string()),
VEGA_TOKEN_URL: z.optional(z.string()),
+1 -1
View File
@@ -8,7 +8,7 @@ export const FillsContainer = ({
onMarketClick,
}: {
marketId?: string;
onMarketClick?: (marketId: string) => void;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}) => {
const { pubKey } = useVegaWallet();
+1 -1
View File
@@ -11,7 +11,7 @@ import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
interface FillsManagerProps {
partyId: string;
marketId?: string;
onMarketClick?: (marketId: string) => void;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}
export const FillsManager = ({
+6 -25
View File
@@ -14,16 +14,13 @@ import {
import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import { AgGridColumn } from 'ag-grid-react';
import { Link } from '@vegaprotocol/ui-toolkit';
import {
AgGridDynamic as AgGrid,
positiveClassNames,
negativeClassNames,
MarketNameCell,
} from '@vegaprotocol/datagrid';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import type { VegaValueFormatterParams } from '@vegaprotocol/datagrid';
import { forwardRef } from 'react';
import BigNumber from 'bignumber.js';
import type { Trade } from './fills-data-provider';
@@ -34,7 +31,7 @@ const MAKER = 'MAKER';
export type Props = (AgGridReactProps | AgReactUiProps) & {
partyId: string;
onMarketClick?: (marketId: string) => void;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
};
export const FillsTable = forwardRef<AgGridReact, Props>(
@@ -48,30 +45,14 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
getRowId={({ data }) => data?.id}
tooltipShowDelay={0}
tooltipHideDelay={2000}
components={{ MarketNameCell }}
{...props}
>
<AgGridColumn
headerName={t('Market')}
field="market.tradableInstrument.instrument.name"
cellRenderer={({
value,
data,
}: VegaICellRendererParams<
Trade,
'market.tradableInstrument.instrument.name'
>) =>
onMarketClick ? (
<Link
onClick={() =>
data?.market?.id && onMarketClick(data?.market?.id)
}
>
{value}
</Link>
) : (
value
)
}
cellRenderer="MarketNameCell"
cellRendererParams={{ idPath: 'market.id', onMarketClick }}
/>
<AgGridColumn
headerName={t('Size')}
@@ -57,7 +57,7 @@ fragment LiquidityProvisionFields on LiquidityProvision {
query LiquidityProvisions($marketId: ID!) {
market(id: $marketId) {
liquidityProvisionsConnection {
liquidityProvisionsConnection(live: true) {
edges {
node {
...LiquidityProvisionFields
@@ -1,6 +1,6 @@
fragment MarketNode on Market {
id
liquidityProvisionsConnection {
liquidityProvisionsConnection(live: true) {
edges {
node {
commitmentAmount
+2 -2
View File
@@ -138,7 +138,7 @@ export type MarketLpQueryResult = Apollo.QueryResult<MarketLpQuery, MarketLpQuer
export const LiquidityProvisionsDocument = gql`
query LiquidityProvisions($marketId: ID!) {
market(id: $marketId) {
liquidityProvisionsConnection {
liquidityProvisionsConnection(live: true) {
edges {
node {
...LiquidityProvisionFields
@@ -290,4 +290,4 @@ export function useLiquidityProviderFeeShareUpdateSubscription(baseOptions: Apol
return Apollo.useSubscription<LiquidityProviderFeeShareUpdateSubscription, LiquidityProviderFeeShareUpdateSubscriptionVariables>(LiquidityProviderFeeShareUpdateDocument, options);
}
export type LiquidityProviderFeeShareUpdateSubscriptionHookResult = ReturnType<typeof useLiquidityProviderFeeShareUpdateSubscription>;
export type LiquidityProviderFeeShareUpdateSubscriptionResult = Apollo.SubscriptionResult<LiquidityProviderFeeShareUpdateSubscription>;
export type LiquidityProviderFeeShareUpdateSubscriptionResult = Apollo.SubscriptionResult<LiquidityProviderFeeShareUpdateSubscription>;
+1 -1
View File
@@ -13,7 +13,7 @@ export type LiquidityProvisionMarketsQuery = { __typename?: 'Query', marketsConn
export const MarketNodeFragmentDoc = gql`
fragment MarketNode on Market {
id
liquidityProvisionsConnection {
liquidityProvisionsConnection(live: true) {
edges {
node {
commitmentAmount
@@ -96,8 +96,8 @@ export const getId = (
>
) =>
isLpFragment(entry)
? `${entry.party.id}${entry.status}${entry.createdAt}`
: `${entry.partyID}${entry.status}${entry.createdAt}`;
? `${entry.party.id}${entry.status}${entry.createdAt}${entry.updatedAt}`
: `${entry.partyID}${entry.status}${entry.createdAt}${entry.updatedAt}`;
export const marketLiquidityDataProvider = makeDataProvider<
MarketLpQuery,
+10 -4
View File
@@ -1,5 +1,5 @@
import React from 'react';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { addDecimalsFixedFormatNumber } from '@vegaprotocol/utils';
import { PriceCell, VolCell, CumulativeVol } from '@vegaprotocol/datagrid';
interface OrderbookRowProps {
@@ -39,14 +39,20 @@ export const OrderbookRow = React.memo(
<VolCell
testId={`bid-vol-${price}`}
value={bid}
valueFormatted={addDecimalsFormatNumber(bid, positionDecimalPlaces)}
valueFormatted={addDecimalsFixedFormatNumber(
bid,
positionDecimalPlaces
)}
relativeValue={relativeBid}
type="bid"
/>
<VolCell
testId={`ask-vol-${price}`}
value={ask}
valueFormatted={addDecimalsFormatNumber(ask, positionDecimalPlaces)}
valueFormatted={addDecimalsFixedFormatNumber(
ask,
positionDecimalPlaces
)}
relativeValue={relativeAsk}
type="ask"
/>
@@ -54,7 +60,7 @@ export const OrderbookRow = React.memo(
testId={`price-${price}`}
value={BigInt(price)}
onClick={() => onClick && onClick(price)}
valueFormatted={addDecimalsFormatNumber(price, decimalPlaces)}
valueFormatted={addDecimalsFixedFormatNumber(price, decimalPlaces)}
/>
<CumulativeVol
testId={`cumulative-vol-${price}`}
@@ -1,3 +1,34 @@
fragment DataSource on DataSourceDefinition {
sourceType {
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on PubKey {
key
}
... on ETHAddress {
address
}
}
}
}
}
}
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
}
}
query MarketInfo($marketId: ID!) {
market(id: $marketId) {
id
@@ -79,9 +110,15 @@ query MarketInfo($marketId: ID!) {
}
dataSourceSpecForSettlementData {
id
data {
...DataSource
}
}
dataSourceSpecForTradingTermination {
id
data {
...DataSource
}
}
dataSourceSpecBinding {
settlementDataProperty
@@ -3,14 +3,47 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type DataSourceFragment = { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } };
export type MarketInfoQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null } } | null };
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null } } | null };
export const DataSourceFragmentDoc = gql`
fragment DataSource on DataSourceDefinition {
sourceType {
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on PubKey {
key
}
... on ETHAddress {
address
}
}
}
}
}
}
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
}
}
`;
export const MarketInfoDocument = gql`
query MarketInfo($marketId: ID!) {
market(id: $marketId) {
@@ -93,9 +126,15 @@ export const MarketInfoDocument = gql`
}
dataSourceSpecForSettlementData {
id
data {
...DataSource
}
}
dataSourceSpecForTradingTermination {
id
data {
...DataSource
}
}
dataSourceSpecBinding {
settlementDataProperty
@@ -131,7 +170,7 @@ export const MarketInfoDocument = gql`
}
}
}
`;
${DataSourceFragmentDoc}`;
/**
* __useMarketInfoQuery__
@@ -39,12 +39,12 @@ import {
export interface InfoProps {
market: MarketInfoWithDataAndCandles;
onSelect: (id: string) => void;
onSelect?: (id: string, metaKey?: boolean) => void;
}
export interface MarketInfoContainerProps {
marketId: string;
onSelect?: (id: string) => void;
onSelect?: (id: string, metaKey?: boolean) => void;
}
export const MarketInfoContainer = ({
marketId,
@@ -73,7 +73,7 @@ export const MarketInfoContainer = ({
<AsyncRenderer data={data} loading={loading} error={error} reload={reload}>
{data ? (
<TinyScroll className="h-full overflow-auto">
<Info market={data} onSelect={(id) => onSelect?.(id)} />
<Info market={data} onSelect={onSelect} />
</TinyScroll>
) : (
<Splash>
@@ -85,7 +85,7 @@ export const MarketInfoContainer = ({
};
export const Info = ({ market, onSelect }: InfoProps) => {
const { VEGA_TOKEN_URL, VEGA_EXPLORER_URL } = useEnvironment();
const { VEGA_TOKEN_URL } = useEnvironment();
const headerClassName = 'uppercase text-lg';
if (!market) return null;
@@ -124,6 +124,10 @@ export const Info = ({ market, onSelect }: InfoProps) => {
title: t('Instrument'),
content: <InstrumentInfoPanel market={market} />,
},
{
title: t('Oracle'),
content: <OracleInfoPanel market={market} />,
},
{
title: t('Settlement asset'),
content: <SettlementAssetInfoPanel market={market} />,
@@ -165,7 +169,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
<LiquidityInfoPanel market={market}>
<Link
to={`/liquidity/${market.id}`}
onClick={() => onSelect(market.id)}
onClick={(ev) => onSelect?.(market.id, ev.metaKey)}
data-testid="view-liquidity-link"
>
<UILink>{t('View liquidity provision table')}</UILink>
@@ -177,23 +181,6 @@ export const Info = ({ market, onSelect }: InfoProps) => {
title: t('Liquidity price range'),
content: <LiquidityPriceRangeInfoPanel market={market} />,
},
{
title: t('Oracle'),
content: (
<OracleInfoPanel market={market}>
<ExternalLink
href={`${VEGA_EXPLORER_URL}/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForSettlementData.id}`}
>
{t('View settlement data oracle specification')}
</ExternalLink>
<ExternalLink
href={`${VEGA_EXPLORER_URL}/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForTradingTermination.id}`}
>
{t('View termination oracle specification')}
</ExternalLink>
</OracleInfoPanel>
),
},
];
const marketGovPanels = [
@@ -236,17 +223,17 @@ export const Info = ({ market, onSelect }: InfoProps) => {
return (
<div className="p-4">
<div className="mb-8">
<p className={headerClassName}>{t('Market data')}</p>
<h3 className={headerClassName}>{t('Market data')}</h3>
<Accordion panels={marketDataPanels} />
</div>
<div className="mb-8">
<MarketProposalNotification marketId={market.id} />
<p className={headerClassName}>{t('Market specification')}</p>
<h3 className={headerClassName}>{t('Market specification')}</h3>
<Accordion panels={marketSpecPanels} />
</div>
{VEGA_TOKEN_URL && market.proposal?.id && (
<div>
<p className={headerClassName}>{t('Market governance')}</p>
<h3 className={headerClassName}>{t('Market governance')}</h3>
<Accordion panels={marketGovPanels} />
</div>
)}
@@ -0,0 +1,188 @@
import { render, screen } from '@testing-library/react';
import {
ConditionOperator,
ConditionOperatorMapping,
} from '@vegaprotocol/types';
import { DataSourceProof } from './market-info-panels';
describe('DataSourceProof', () => {
const ORACLE_PUBKEY =
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f';
it('renders correct proof for external data sources', () => {
const props = {
data: {
sourceType: {
__typename: 'DataSourceDefinitionExternal' as const,
sourceType: {
__typename: 'DataSourceSpecConfiguration' as const,
signers: [
{
__typename: 'Signer' as const,
signer: {
__typename: 'PubKey' as const,
key: ORACLE_PUBKEY,
},
},
],
},
},
},
providers: [
{
name: 'Another oracle',
url: 'https://zombo.com',
description_markdown:
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
oracle: {
status: 'GOOD' as const,
status_reason: '',
first_verified: '2022-01-01T00:00:00.000Z',
last_verified: '2022-12-31T00:00:00.000Z',
type: 'public_key' as const,
public_key: ORACLE_PUBKEY,
},
proofs: [
{
format: 'signed_message' as const,
available: true,
type: 'public_key' as const,
public_key: ORACLE_PUBKEY,
message: 'SOMEHEX',
},
],
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/PubKey-${ORACLE_PUBKEY}.toml`,
},
],
type: 'termination' as const,
};
render(<DataSourceProof {...props} />);
expect(screen.getByRole('link')).toHaveAttribute(
'href',
props.providers[0].github_link
);
});
it('renders message if there are no providers', () => {
const props = {
data: {
sourceType: {
__typename: 'DataSourceDefinitionExternal' as const,
sourceType: {
__typename: 'DataSourceSpecConfiguration' as const,
signers: [
{
__typename: 'Signer' as const,
signer: {
__typename: 'PubKey' as const,
key: ORACLE_PUBKEY,
},
},
],
},
},
},
providers: [],
type: 'termination' as const,
};
render(<DataSourceProof {...props} />);
expect(
screen.getByText('No oracle proof for termination')
).toBeInTheDocument();
});
it('renders message if there are no matching proofs', () => {
const props = {
data: {
sourceType: {
__typename: 'DataSourceDefinitionExternal' as const,
sourceType: {
__typename: 'DataSourceSpecConfiguration' as const,
signers: [
{
__typename: 'Signer' as const,
signer: {
__typename: 'PubKey' as const,
key: ORACLE_PUBKEY,
},
},
],
},
},
},
providers: [
{
name: 'Another oracle',
url: 'https://zombo.com',
description_markdown:
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
oracle: {
status: 'GOOD' as const,
status_reason: '',
first_verified: '2022-01-01T00:00:00.000Z',
last_verified: '2022-12-31T00:00:00.000Z',
type: 'public_key' as const,
public_key: 'not-the-pubkey',
},
proofs: [
{
format: 'signed_message' as const,
available: true,
type: 'public_key' as const,
public_key: 'not-the-pubkey',
message: 'SOMEHEX',
},
],
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/PubKey-${ORACLE_PUBKEY}.toml`,
},
],
type: 'settlementData' as const,
};
render(<DataSourceProof {...props} />);
expect(
screen.getByText('No oracle proof for settlement data')
).toBeInTheDocument();
});
it('renders message if no data source on market', () => {
const props = {
data: {
sourceType: {
__typename: 'Invalid',
},
},
providers: [],
type: 'termination' as const,
};
// @ts-ignore types are invalid
render(<DataSourceProof {...props} />);
expect(screen.getByText('Invalid data source')).toBeInTheDocument();
});
it('renders conditions for internal data sources', () => {
const condition = {
__typename: 'Condition' as const,
operator: ConditionOperator.OPERATOR_GREATER_THAN,
value: '100',
};
const props = {
data: {
sourceType: {
__typename: 'DataSourceDefinitionInternal' as const,
sourceType: {
__typename: 'DataSourceSpecConfigurationTime' as const,
conditions: [condition],
},
},
},
providers: [],
type: 'termination' as const,
};
render(<DataSourceProof {...props} />);
expect(screen.getByText('Internal conditions')).toBeInTheDocument();
expect(
screen.getByText(
`${ConditionOperatorMapping[condition.operator]} ${condition.value}`
)
).toBeInTheDocument();
});
});
@@ -6,7 +6,7 @@ import {
calcCandleVolume,
totalFeesPercentage,
} from '@vegaprotocol/market-list';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import {
addDecimalsFormatNumber,
formatNumber,
@@ -21,7 +21,12 @@ import type {
MarketInfoWithDataAndCandles,
} from './market-info-data-provider';
import BigNumber from 'bignumber.js';
import type { DataSourceDefinition, SignerKind } from '@vegaprotocol/types';
import { ConditionOperatorMapping } from '@vegaprotocol/types';
import { MarketTradingModeMapping } from '@vegaprotocol/types';
import { useEnvironment } from '@vegaprotocol/environment';
import type { Provider } from '@vegaprotocol/oracles';
import { useOracleProofs } from '@vegaprotocol/oracles';
type PanelProps = Pick<
ComponentProps<typeof MarketInfoTable>,
@@ -399,7 +404,7 @@ export const LiquidityPriceRangeInfoPanel = ({
market.decimalPlaces
)} ${quoteUnit}`,
}}
></MarketInfoTable>
/>
</div>
</>
);
@@ -408,9 +413,156 @@ export const LiquidityPriceRangeInfoPanel = ({
export const OracleInfoPanel = ({
market,
...props
}: MarketInfoProps & PanelProps) => (
<MarketInfoTable
data={market.tradableInstrument.instrument.product.dataSourceSpecBinding}
{...props}
/>
);
}: MarketInfoProps & PanelProps) => {
const product = market.tradableInstrument.instrument.product;
const { VEGA_EXPLORER_URL, ORACLE_PROOFS_URL } = useEnvironment();
const { data } = useOracleProofs(ORACLE_PROOFS_URL);
return (
<MarketInfoTable data={product.dataSourceSpecBinding} {...props}>
<div
className="flex flex-col gap-2 mt-4"
data-testid="oracle-proof-links"
>
<DataSourceProof
data={product.dataSourceSpecForSettlementData.data}
providers={data}
type="settlementData"
/>
<DataSourceProof
data={product.dataSourceSpecForTradingTermination.data}
providers={data}
type="termination"
/>
</div>
<div className="flex flex-col gap-2" data-testid="oracle-spec-links">
<ExternalLink
href={`${VEGA_EXPLORER_URL}/oracles#${product.dataSourceSpecForSettlementData.id}`}
>
{t('View settlement data specification')}
</ExternalLink>
<ExternalLink
href={`${VEGA_EXPLORER_URL}/oracles#${product.dataSourceSpecForTradingTermination.id}`}
>
{t('View termination specification')}
</ExternalLink>
</div>
</MarketInfoTable>
);
};
export const DataSourceProof = ({
data,
providers,
type,
}: {
data: DataSourceDefinition;
providers: Provider[] | undefined;
type: 'settlementData' | 'termination';
}) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
const signers = data.sourceType.sourceType.signers || [];
if (!providers?.length) {
return <NoOracleProof type={type} />;
}
return (
<div className="flex flex-col gap-2">
{signers.map(({ signer }, i) => {
return (
<OracleLink
key={i}
providers={providers}
signer={signer}
type={type}
index={i}
/>
);
})}
</div>
);
}
if (data.sourceType.__typename === 'DataSourceDefinitionInternal') {
return (
<div>
<h3>{t('Internal conditions')}</h3>
{data.sourceType.sourceType.conditions.map((condition, i) => {
if (!condition) return null;
return (
<p key={i}>
{ConditionOperatorMapping[condition.operator]} {condition.value}
</p>
);
})}
</div>
);
}
return <div>{t('Invalid data source')}</div>;
};
const OracleLink = ({
providers,
signer,
type,
index,
}: {
providers: Provider[];
signer: SignerKind;
type: 'settlementData' | 'termination';
index: number;
}) => {
const text =
type === 'settlementData'
? t('View settlement oracle details')
: t('View termination oracle details');
const textWithCount = index > 0 ? `${text} (${index + 1})` : text;
const provider = providers.find((p) => {
if (signer.__typename === 'PubKey') {
if (
p.oracle.type === 'public_key' &&
p.oracle.public_key === signer.key
) {
return true;
}
}
if (signer.__typename === 'ETHAddress') {
if (
p.oracle.type === 'eth_address' &&
p.oracle.eth_address === signer.address
) {
return true;
}
}
return false;
});
if (!provider) {
return <NoOracleProof type={type} />;
}
return (
<p>
<ExternalLink href={provider.github_link}>{textWithCount}</ExternalLink>
</p>
);
};
const NoOracleProof = ({
type,
}: {
type: 'settlementData' | 'termination';
}) => {
return (
<p>
{t(
'No oracle proof for %s',
type === 'settlementData' ? 'settlement data' : 'termination'
)}
</p>
);
};
@@ -133,10 +133,44 @@ export const marketInfoQuery = (
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
data: {
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
},
},
],
},
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
data: {
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
},
},
],
},
},
},
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
@@ -10,6 +10,7 @@ import type {
import {
AgGridDynamic as AgGrid,
PriceFlashCell,
MarketNameCell,
} from '@vegaprotocol/datagrid';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { AgGridColumn } from 'ag-grid-react';
@@ -24,8 +25,10 @@ export const getRowId = ({ data }: { data: { id: string } }) => data.id;
export const MarketListTable = forwardRef<
AgGridReact,
TypedDataAgGrid<MarketMaybeWithData>
>((props, ref) => {
TypedDataAgGrid<MarketMaybeWithData> & {
onMarketClick: (marketId: string, metaKey?: boolean) => void;
}
>(({ onMarketClick, ...props }, ref) => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
return (
<AgGrid
@@ -41,22 +44,14 @@ export const MarketListTable = forwardRef<
filterParams: { buttons: ['reset'] },
}}
suppressCellFocus={true}
components={{ PriceFlashCell }}
components={{ PriceFlashCell, MarketNameCell }}
{...props}
>
<AgGridColumn
headerName={t('Market')}
field="tradableInstrument.instrument.code"
cellRenderer={({
value,
data,
}: VegaICellRendererParams<
MarketMaybeWithData,
'tradableInstrument.instrument.code'
>) => {
if (!data) return null;
return <span data-testid={`market-${data.id}`}>{value}</span>;
}}
cellRenderer="MarketNameCell"
cellRendererParams={{ onMarketClick }}
/>
<AgGridColumn
headerName={t('Description')}
@@ -1,12 +1,14 @@
import type { MouseEvent } from 'react';
import { t } from '@vegaprotocol/i18n';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { MarketListTable } from './market-list-table';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import type { RowClickedEvent } from 'ag-grid-community';
import type { CellClickedEvent } from 'ag-grid-community';
import { marketsWithDataProvider as dataProvider } from '../../markets-provider';
import type { MarketMaybeWithData } from '../../markets-provider';
interface MarketsContainerProps {
onSelect: (marketId: string) => void;
onSelect: (marketId: string, metaKey?: boolean) => void;
}
export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
@@ -21,16 +23,23 @@ export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
rowData={error ? [] : data}
suppressLoadingOverlay
suppressNoRowsOverlay
onRowClicked={(rowEvent: RowClickedEvent) => {
const { data, event } = rowEvent;
// filters out clicks on the symbol column because it should display asset details
onCellClicked={(cellEvent: CellClickedEvent) => {
const { data, column, event } = cellEvent;
const colId = column.getColId();
if (
(event?.target as HTMLElement).tagName.toUpperCase() === 'BUTTON'
[
'tradableInstrument.instrument.code',
'tradableInstrument.instrument.product.settlementAsset',
].includes(colId)
) {
return;
}
onSelect((data as MarketMaybeWithData).id);
onSelect(
(data as MarketMaybeWithData).id,
(event as unknown as MouseEvent)?.metaKey
);
}}
onMarketClick={onSelect}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
+12
View File
@@ -0,0 +1,12 @@
{
"presets": [
[
"@nrwl/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
+18
View File
@@ -0,0 +1,18 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
+7
View File
@@ -0,0 +1,7 @@
# oracles
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test oracles` to execute the unit tests via [Jest](https://jestjs.io).
+10
View File
@@ -0,0 +1,10 @@
/* eslint-disable */
export default {
displayName: 'oracles',
preset: '../../jest.preset.js',
transform: {
'^.+\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/oracles',
};
+4
View File
@@ -0,0 +1,4 @@
{
"name": "@vegaprotocol/oracles",
"version": "0.0.1"
}
+43
View File
@@ -0,0 +1,43 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/oracles/src",
"projectType": "library",
"tags": [],
"targets": {
"build": {
"executor": "@nrwl/web:rollup",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/oracles",
"tsConfig": "libs/oracles/tsconfig.lib.json",
"project": "libs/oracles/package.json",
"entryFile": "libs/oracles/src/index.ts",
"external": ["react/jsx-runtime"],
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
"compiler": "babel",
"assets": [
{
"glob": "libs/oracles/README.md",
"input": ".",
"output": "."
}
]
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/oracles/**/*.{ts,tsx,js,jsx}"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/libs/oracles"],
"options": {
"jestConfig": "libs/oracles/jest.config.ts",
"passWithNoTests": true
}
}
}
}
+2
View File
@@ -0,0 +1,2 @@
export * from './lib/oracle-schema';
export * from './lib/use-oracle-proofs';
+74
View File
@@ -0,0 +1,74 @@
import z from 'zod';
export type Provider = z.infer<typeof providerSchema>;
export type Oracle = z.infer<typeof oracleSchema>;
export type Proof = z.infer<typeof proofSchema>;
export type Status = z.infer<typeof statusSchema>;
const statusSchema = z.enum([
'UNKNOWN',
'GOOD',
'SUSPICIOUS',
'MALICIOUS',
'RETIRED',
'COMPROMISED',
]);
const baseProofSchema = z.object({
format: z.enum(['url', 'signed_message']),
available: z.boolean(),
});
const proofSchema = z.discriminatedUnion('type', [
baseProofSchema.extend({
type: z.literal('public_key'),
public_key: z.string().min(64),
message: z.string().min(1),
}),
baseProofSchema.extend({
type: z.literal('eth_address'),
eth_address: z.string().min(42),
message: z.string().min(1),
}),
baseProofSchema.extend({
type: z.literal('web'),
url: z.string().url(),
}),
baseProofSchema.extend({
type: z.literal('github'),
url: z.string().url(),
}),
baseProofSchema.extend({
type: z.literal('twitter'),
url: z.string().url(),
}),
]);
const baseOracleSchema = z.object({
status: statusSchema,
status_reason: z.string(),
first_verified: z.string(),
last_verified: z.string(),
});
const oracleSchema = z.discriminatedUnion('type', [
baseOracleSchema.extend({
type: z.literal('public_key'),
public_key: z.string().min(64),
}),
baseOracleSchema.extend({
type: z.literal('eth_address'),
eth_address: z.string().min(42),
}),
]);
const providerSchema = z.object({
name: z.string().min(1),
url: z.string().url(),
description_markdown: z.string(),
oracle: oracleSchema,
proofs: z.array(proofSchema),
github_link: z.string().url(),
});
export const providersSchema = z.array(providerSchema);
@@ -0,0 +1,135 @@
import { renderHook, waitFor } from '@testing-library/react';
import type { Provider } from './oracle-schema';
import { useOracleProofs, cache, invalidateCache } from './use-oracle-proofs';
global.fetch = jest.fn();
const mockFetch = global.fetch as jest.Mock;
const createOracleData = (): Provider[] => {
return [
{
name: 'Another oracle',
url: 'https://zombo.com',
description_markdown:
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
oracle: {
status: 'GOOD',
status_reason: '',
first_verified: '2022-01-01T00:00:00.000Z',
last_verified: '2022-12-31T00:00:00.000Z',
type: 'public_key',
public_key:
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
},
proofs: [
{
format: 'url',
available: true,
type: 'twitter',
url: 'https://twitter.com/vegaprotocol/status/956833487230730241',
},
{
format: 'signed_message',
available: true,
type: 'public_key',
public_key:
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
message: 'SOMEHEX',
},
],
github_link:
'https://github.com/vegaprotocol/well-known/blob/feat/add-process-script/oracle-providers/PubKey-69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f.toml',
},
];
};
describe('useOracleProofs', () => {
const url = 'https://foo.bar.com';
const setup = (data: Provider[]) => {
mockFetch.mockImplementation(() => {
return Promise.resolve({
ok: true,
json: () => Promise.resolve(data),
});
});
return renderHook(() => useOracleProofs(url));
};
beforeEach(() => {
mockFetch.mockClear();
});
describe('fetches and caches', () => {
it('fetches oracle data', async () => {
const data = createOracleData();
const { result } = setup(data);
expect(result.current.data).toBe(undefined);
expect(result.current.error).toBe(undefined);
expect(result.current.loading).toBe(true);
await waitFor(() => {
expect(result.current.data).toEqual(data);
expect(result.current.error).toBe(undefined);
expect(result.current.loading).toBe(false);
});
expect(mockFetch).toHaveBeenCalledTimes(1);
// check result was cached
expect(cache).toEqual({ [url]: data });
});
it('uses cached value if present', () => {
const data = createOracleData();
const { result } = setup(data);
expect(result.current.data).toEqual(data);
expect(result.current.error).toBe(undefined);
expect(result.current.loading).toBe(false);
expect(mockFetch).toHaveBeenCalledTimes(0);
});
});
it('handles invalid payload', async () => {
invalidateCache();
// @ts-ignore enforce invalid result
const { result } = setup([{ invalid: 'result' }]);
expect(result.current.data).toBe(undefined);
expect(result.current.error).toBe(undefined);
expect(result.current.loading).toBe(true);
await waitFor(() => {
expect(result.current.data).toBe(undefined);
expect(result.current.error instanceof Error).toBe(true);
expect(result.current.loading).toBe(false);
});
expect(mockFetch).toHaveBeenCalledTimes(1);
});
it('handles failed to fetch', async () => {
invalidateCache();
mockFetch.mockImplementation(() => {
return Promise.reject(new Error('failed to fetch'));
});
const { result } = renderHook(() => useOracleProofs(url));
expect(result.current.data).toBe(undefined);
expect(result.current.error).toBe(undefined);
expect(result.current.loading).toBe(true);
await waitFor(() => {
expect(result.current.data).toBe(undefined);
expect(result.current.error instanceof Error).toBe(true);
expect(result.current.error).toEqual(new Error('failed to fetch'));
expect(result.current.loading).toBe(false);
});
expect(mockFetch).toHaveBeenCalledTimes(1);
});
});
+64
View File
@@ -0,0 +1,64 @@
import { useEffect, useState } from 'react';
import type { Provider } from './oracle-schema';
import { providersSchema } from './oracle-schema';
export let cache: {
[url: string]: Provider[];
} = {};
export const useOracleProofs = (url?: string) => {
const [data, setData] = useState<Provider[] | undefined>(() =>
url ? cache[url] : undefined
);
const [status, setStatus] = useState<'idle' | 'loading' | 'done'>('idle');
const [error, setError] = useState<Error>();
useEffect(() => {
let ignore = false;
if (!url) return;
const run = async () => {
try {
if (cache[url]) {
setData(cache[url]);
} else {
setStatus('loading');
const res = await fetch(url);
const json = await res.json();
if (ignore) return;
const result = providersSchema.parse(json);
cache[url] = result;
setData(result);
}
} catch (err) {
if (err instanceof Error) {
setError(err);
} else {
setError(new Error('Something went wrong'));
}
} finally {
setStatus('done');
}
};
run();
return () => {
ignore = true;
};
}, [url]);
return {
data,
loading: status === 'loading',
error,
};
};
export const invalidateCache = () => {
cache = {};
};
+25
View File
@@ -0,0 +1,25 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
+23
View File
@@ -0,0 +1,23 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"jest.config.ts",
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}
+20
View File
@@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"**/*.test.ts",
"**/*.spec.ts",
"**/*.test.tsx",
"**/*.spec.tsx",
"**/*.test.js",
"**/*.spec.js",
"**/*.test.jsx",
"**/*.spec.jsx",
"**/*.d.ts"
]
}
@@ -9,7 +9,7 @@ export const OrderListContainer = ({
enforceBottomPlaceholder,
}: {
marketId?: string;
onMarketClick?: (marketId: string) => void;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
enforceBottomPlaceholder?: boolean;
}) => {
const { pubKey, isReadOnly } = useVegaWallet();
@@ -23,7 +23,7 @@ import type { Order, OrderEdge } from '../order-data-provider';
export interface OrderListManagerProps {
partyId: string;
marketId?: string;
onMarketClick?: (marketId: string) => void;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
isReadOnly: boolean;
enforceBottomPlaceholder?: boolean;
}
@@ -5,7 +5,7 @@ import {
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { AgGridColumn } from 'ag-grid-react';
import BigNumber from 'bignumber.js';
import { memo, forwardRef } from 'react';
@@ -15,6 +15,7 @@ import {
DateRangeFilter,
negativeClassNames,
positiveClassNames,
MarketNameCell,
} from '@vegaprotocol/datagrid';
import type {
TypedDataAgGrid,
@@ -29,7 +30,7 @@ type OrderListProps = TypedDataAgGrid<Order> & { marketId?: string };
export type OrderListTableProps = OrderListProps & {
cancel: (order: Order) => void;
setEditOrder: (order: Order) => void;
onMarketClick?: (marketId: string) => void;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
isReadOnly: boolean;
};
@@ -50,30 +51,14 @@ export const OrderListTable = memo(
height: '100%',
}}
getRowId={({ data }) => data.id}
components={{ MarketNameCell }}
{...props}
>
<AgGridColumn
headerName={t('Market')}
field="market.tradableInstrument.instrument.code"
cellRenderer={({
value,
data,
}: VegaICellRendererParams<
Order,
'market.tradableInstrument.instrument.code'
>) =>
onMarketClick ? (
<Link
onClick={() =>
data?.market?.id && onMarketClick(data?.market?.id)
}
>
{value}
</Link>
) : (
value
)
}
cellRenderer="MarketNameCell"
cellRendererParams={{ idPath: 'market.id', onMarketClick }}
minWidth={150}
/>
<AgGridColumn
@@ -13,6 +13,8 @@ export type OrderObj = {
price?: string;
expiresAt?: string | undefined;
persist: boolean; // key used to determine if order should be kept in localStorage
postOnly?: boolean;
reduceOnly?: boolean;
};
type OrderMap = { [marketId: string]: OrderObj | undefined };
@@ -114,4 +116,6 @@ export const getDefaultOrder = (marketId: string): OrderObj => ({
price: '0',
expiresAt: undefined,
persist: false,
postOnly: false,
reduceOnly: false,
});
+10 -17
View File
@@ -14,12 +14,12 @@ import {
PriceFlashCell,
signedNumberCssClass,
signedNumberCssClassRules,
MarketNameCell,
} from '@vegaprotocol/datagrid';
import {
ButtonLink,
Tooltip,
TooltipCellComponent,
Link,
ExternalLink,
Icon,
ProgressBarCell,
@@ -43,7 +43,7 @@ import { useEnvironment } from '@vegaprotocol/environment';
interface Props extends TypedDataAgGrid<Position> {
onClose?: (data: Position) => void;
onMarketClick?: (id: string) => void;
onMarketClick?: (id: string, metaKey?: boolean) => void;
style?: CSSProperties;
isReadOnly: boolean;
}
@@ -96,26 +96,19 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
filterParams: { buttons: ['reset'] },
tooltipComponent: TooltipCellComponent,
}}
components={{ AmountCell, PriceFlashCell, ProgressBarCell }}
components={{
AmountCell,
PriceFlashCell,
ProgressBarCell,
MarketNameCell,
}}
{...props}
>
<AgGridColumn
headerName={t('Market')}
field="marketName"
cellRenderer={({
value,
data,
}: VegaICellRendererParams<Position, 'marketName'>) =>
onMarketClick ? (
<Link
onClick={() => data?.marketId && onMarketClick(data?.marketId)}
>
{value}
</Link>
) : (
value
)
}
cellRenderer="MarketNameCell"
cellRendererParams={{ idPath: 'marketId', onMarketClick }}
minWidth={190}
/>
<AgGridColumn
+9
View File
@@ -1,3 +1,4 @@
import type { ConditionOperator } from './__generated__/types';
import type {
AccountType,
AuctionTrigger,
@@ -458,3 +459,11 @@ export const PositionStatusMapping: {
POSITION_STATUS_ORDERS_CLOSED: 'Maintained by network',
POSITION_STATUS_UNSPECIFIED: 'Normal',
};
export const ConditionOperatorMapping: { [C in ConditionOperator]: string } = {
OPERATOR_EQUALS: 'Equals',
OPERATOR_GREATER_THAN: 'Greater than',
OPERATOR_GREATER_THAN_OR_EQUAL: 'Greater than or equal to',
OPERATOR_LESS_THAN: 'Less than',
OPERATOR_LESS_THAN_OR_EQUAL: 'Less than or equal to',
};
@@ -54,7 +54,12 @@ export const Checkbox = ({
)}
</CheckboxPrimitive.CheckboxIndicator>
</CheckboxPrimitive.Root>
<label htmlFor={name} className="text-sm">
<label
htmlFor={name}
className={classNames('text-sm', {
'dark:text-neutral-400 text-neutral-600': disabled,
})}
>
{label}
</label>
</div>
+3 -3
View File
@@ -339,7 +339,7 @@ function makeDataProviderInternal<
}
};
const initialFetch = async () => {
const initialFetch = async (isUpdate = false) => {
if (!client) {
return;
}
@@ -394,7 +394,7 @@ function makeDataProviderInternal<
subscription = undefined;
} finally {
loading = false;
notifyAll();
notifyAll({ isUpdate });
}
};
@@ -410,7 +410,7 @@ function makeDataProviderInternal<
} else {
loading = true;
error = undefined;
initialFetch();
initialFetch(true);
}
};
+1
View File
@@ -3,6 +3,7 @@
*/
export const createDocsLinks = (docsUrl: string) => ({
NEW_TO_VEGA: `${docsUrl}/concepts/new-to-vega`,
AUCTION_TYPE_OPENING: `${docsUrl}/concepts/trading-on-vega/trading-modes#auction-type-opening`,
AUCTION_TYPE_LIQUIDITY_MONITORING: `${docsUrl}/concepts/trading-on-vega/trading-modes#auction-type-liquidity-monitoring`,
AUCTION_TYPE_PRICE_MONITORING: `${docsUrl}/concepts/trading-on-vega/trading-modes#auction-type-price-monitoring`,
@@ -45,6 +45,7 @@ export interface OrderSubmission {
size: string;
price?: string;
expiresAt?: string;
postOnly?: boolean;
reduceOnly?: boolean;
}
+2
View File
@@ -48,6 +48,8 @@ export const normalizeOrderSubmission = (
order.expiresAt && order.timeInForce === OrderTimeInForce.TIME_IN_FORCE_GTT
? toNanoSeconds(order.expiresAt)
: undefined,
postOnly: order.postOnly,
reduceOnly: order.reduceOnly,
});
export const normalizeOrderAmendment = <T extends Exact<OrderAmendment, T>>(
+3 -1
View File
@@ -11,7 +11,8 @@
"build:all": "nx run-many --all --target=build",
"lint:all": "nx run-many --all --target=lint",
"e2e:all": "nx run-many --all --target=e2e",
"vegacapsule": "vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl"
"vegacapsule": "vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl",
"release": "git checkout develop ; git pull ; node scripts/make-release.js"
},
"engines": {
"node": ">=16.15.1"
@@ -166,6 +167,7 @@
"flush-promises": "^1.0.2",
"glob": "^8.0.3",
"husky": "^7.0.4",
"inquirer": "^8.0.0",
"jest": "27.5.1",
"jest-canvas-mock": "^2.3.1",
"jest-websocket-mock": "^2.3.0",
+82
View File
@@ -0,0 +1,82 @@
const { execSync } = require('child_process');
const inquirer = require('inquirer');
const getTags = (take) => {
const tags = execSync(
"git for-each-ref --sort=taggerdate --format '%(refname)' refs/tags"
)
.toString()
.trim()
.split('\n')
.map((t) => t.replace('refs/tags/', ''))
.reverse()
.slice(0, take);
return tags;
};
const getReleaseBranches = () => {
const branches = execSync('git branch --remote | grep origin/release/')
.toString()
.trim()
.split('\n')
.map((b) => b.trim().replace('origin/release/', ''));
return branches;
};
const release = (tag, branch) => {
const steps = [
`git checkout ${branch}`,
'git pull',
`git reset --hard ${tag}`,
'git push --force',
];
try {
for (const cmd of steps) {
const result = execSync(cmd).toString();
}
} catch (err) {
console.error('Could not make a release');
}
};
inquirer
.prompt([
{
type: 'list',
name: 'tag',
message: 'What version would you like to release?',
choices: getTags(),
},
{
type: 'checkbox',
name: 'envs',
message: 'To what environment you wish to release?',
choices: ['mainnet', ...getReleaseBranches()],
validate(answer) {
return answer.length > 0;
},
},
])
.then((answers) => {
inquirer
.prompt({
type: 'confirm',
name: 'sure',
message: `Are you sure? This will release ${
answers.tag
} to ${answers.envs.join(', ')}`,
})
.then(() => {
for (const env of answers.envs) {
const branch = env === 'mainnet' ? 'master' : `release/${env}`;
release(answers.tag, branch);
}
execSync('git checkout develop');
})
.catch((err) => {
console.log('Something went wrong', err.toString());
});
})
.catch((err) => {
console.log('Something went wrong', err.toString());
});
+1
View File
@@ -36,6 +36,7 @@
"@vegaprotocol/mock": ["libs/cypress/mock.ts"],
"@vegaprotocol/network-info": ["libs/network-info/src/index.ts"],
"@vegaprotocol/network-stats": ["libs/network-stats/src/index.ts"],
"@vegaprotocol/oracles": ["libs/oracles/src/index.ts"],
"@vegaprotocol/orders": ["libs/orders/src/index.ts"],
"@vegaprotocol/positions": ["libs/positions/src/index.ts"],
"@vegaprotocol/proposals": ["libs/proposals/src/index.ts"],
+1
View File
@@ -26,6 +26,7 @@
"multisig-signer": "apps/multisig-signer",
"network-info": "libs/network-info",
"network-stats": "libs/network-stats",
"oracles": "libs/oracles",
"orders": "libs/orders",
"positions": "libs/positions",
"proposals": "libs/proposals",