diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ac7391e1d..ba9852abb 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ # Related issues 🔗 -Issue: #[Issue number here] +Closes #[Issue number here] # Description ℹ️ diff --git a/README.md b/README.md index dba942bf3..3ad469c9a 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,20 @@ In order to run a container on port 3000: docker run -p 3000:80 [TAG] ``` +On top of that there are two possible scenarios for running docker image - using nginx server (default) of ipfs daemon. + +to run ipfs on port 3000: + +```bash +docker run -p 3000:80 [TAG] ipfs +``` + +to run nginx on port 3000: + +```bash +docker run -p 3000:80 [TAG] +``` + ## Build instructions The [`docker`](./docker) subfolder has some docker configurations for easily setting up your own hosted version of Console either for the web, or ready for pinning on IPFS. @@ -150,7 +164,7 @@ As a prerequisite you need to perform build of `dist` directory and move its con You can build any of the containers locally with the following command: ```bash -docker build --dockerfile docker/node-outside-docker.Dockerfile . --tag=[TAG] +docker build -f docker/node-outside-docker.Dockerfile . --tag=[TAG] ``` ### Verifying ipfs-hash of existing current application version diff --git a/apps/explorer-e2e/.env.devnet b/apps/explorer-e2e/.env.devnet index de6c1a705..2533ef0cc 100644 --- a/apps/explorer-e2e/.env.devnet +++ b/apps/explorer-e2e/.env.devnet @@ -4,6 +4,8 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql NX_VEGA_ENV=DEVNET NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json + # App flags NX_EXPLORER_ASSETS=1 diff --git a/apps/explorer-e2e/.env.mainnet b/apps/explorer-e2e/.env.mainnet index 8336b670f..66e33d4e1 100644 --- a/apps/explorer-e2e/.env.mainnet +++ b/apps/explorer-e2e/.env.mainnet @@ -4,6 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocke NX_VEGA_URL=https://api.vega.community/graphql NX_VEGA_ENV=MAINNET NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json # App flags NX_EXPLORER_ASSETS=1 diff --git a/apps/explorer-e2e/.env.testnet b/apps/explorer-e2e/.env.testnet index bf4f7174e..f184fac2b 100644 --- a/apps/explorer-e2e/.env.testnet +++ b/apps/explorer-e2e/.env.testnet @@ -4,6 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql NX_VEGA_ENV=TESTNET NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json # App flags NX_EXPLORER_ASSETS=1 diff --git a/apps/explorer/.env.capsule b/apps/explorer/.env.capsule index 83e128a14..39ab79e82 100644 --- a/apps/explorer/.env.capsule +++ b/apps/explorer/.env.capsule @@ -3,6 +3,7 @@ NX_VEGA_ENV=CUSTOM NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json NX_VEGA_EXPLORER_URL=/ +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json # App flags NX_EXPLORER_TXS_LIST=0 diff --git a/apps/explorer/.env.devnet b/apps/explorer/.env.devnet index 7c429a0a6..abe211865 100644 --- a/apps/explorer/.env.devnet +++ b/apps/explorer/.env.devnet @@ -8,6 +8,7 @@ NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_VEGA_EXPLORER_URL=/ +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/ NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket diff --git a/apps/explorer/.env.mainnet b/apps/explorer/.env.mainnet index 69a4d78f9..21423c3db 100644 --- a/apps/explorer/.env.mainnet +++ b/apps/explorer/.env.mainnet @@ -9,6 +9,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/ NX_VEGA_CONSOLE_URL=https://console.vega.xyz +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_TENDERMINT_URL=https://be.vega.community NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket diff --git a/apps/explorer/.env.mainnet-mirror b/apps/explorer/.env.mainnet-mirror index c97564f1e..527870b91 100644 --- a/apps/explorer/.env.mainnet-mirror +++ b/apps/explorer/.env.mainnet-mirror @@ -9,6 +9,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/ NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket diff --git a/apps/explorer/.env.stagnet1 b/apps/explorer/.env.stagnet1 index fa167a129..73fd98dce 100644 --- a/apps/explorer/.env.stagnet1 +++ b/apps/explorer/.env.stagnet1 @@ -1,2 +1,3 @@ # .env is stagnet1, so there are no overrides required NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET1":"https://stagnet1.explorer.vega.xyz"}' +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json diff --git a/apps/explorer/.env.testnet b/apps/explorer/.env.testnet index 56d569ce6..eaa7350d5 100644 --- a/apps/explorer/.env.testnet +++ b/apps/explorer/.env.testnet @@ -9,6 +9,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_VEGA_CONSOLE_URL=https://console.fairground.wtf +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket diff --git a/apps/explorer/.env.validators-testnet b/apps/explorer/.env.validators-testnet index 3c609c7c2..348f6ce45 100644 --- a/apps/explorer/.env.validators-testnet +++ b/apps/explorer/.env.validators-testnet @@ -11,6 +11,7 @@ NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/ NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/ +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket diff --git a/apps/explorer/.env.vegacapsule b/apps/explorer/.env.vegacapsule index cf7308afe..ad5d2d29e 100644 --- a/apps/explorer/.env.vegacapsule +++ b/apps/explorer/.env.vegacapsule @@ -5,4 +5,5 @@ NX_VEGA_ENV=CUSTOM NX_BLOCK_EXPLORER= NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json -NX_VEGA_EXPLORER_URL=/ \ No newline at end of file +NX_VEGA_EXPLORER_URL=/ +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json diff --git a/apps/explorer/src/app/components/proposals/proposals-table.tsx b/apps/explorer/src/app/components/proposals/proposals-table.tsx index cd794c3a9..2986def3d 100644 --- a/apps/explorer/src/app/components/proposals/proposals-table.tsx +++ b/apps/explorer/src/app/components/proposals/proposals-table.tsx @@ -33,7 +33,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => { const { params } = useNetworkParams([ NetworkParams.governance_proposal_market_requiredMajority, ]); - const tokenLink = useLinks(DApp.Token); + const tokenLink = useLinks(DApp.Governance); const requiredMajorityPercentage = useMemo(() => { const requiredMajority = params?.governance_proposal_market_requiredMajority ?? 1; diff --git a/apps/explorer/src/app/components/txs/details/proposal/signature-bundle/bundle-signers.tsx b/apps/explorer/src/app/components/txs/details/proposal/signature-bundle/bundle-signers.tsx index 154a717e8..07fb67724 100644 --- a/apps/explorer/src/app/components/txs/details/proposal/signature-bundle/bundle-signers.tsx +++ b/apps/explorer/src/app/components/txs/details/proposal/signature-bundle/bundle-signers.tsx @@ -35,7 +35,7 @@ export const BundleSigners = ({ tx, id, }: BundleSignersProps) => { - const tokenLink = useLinks(DApp.Token); + const tokenLink = useLinks(DApp.Governance); const bridgeFunction: BridgeFunction = tx?.changes?.erc20 && 'contractAddress' in tx.changes.erc20 diff --git a/apps/explorer/src/app/routes/validators/validators-page.tsx b/apps/explorer/src/app/routes/validators/validators-page.tsx index 3c2d888bc..02a7e4877 100644 --- a/apps/explorer/src/app/routes/validators/validators-page.tsx +++ b/apps/explorer/src/app/routes/validators/validators-page.tsx @@ -139,7 +139,7 @@ export const ValidatorsPage = () => { const [vegaDialog, setVegaDialog] = useState(false); const [tmDialog, setTmDialog] = useState(false); - const tokenLink = useLinks(DApp.Token); + const tokenLink = useLinks(DApp.Governance); return ( <> diff --git a/apps/governance-e2e/.env b/apps/governance-e2e/.env index 2e6ff789a..aa289efe5 100644 --- a/apps/governance-e2e/.env +++ b/apps/governance-e2e/.env @@ -14,6 +14,8 @@ NX_ETH_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-stagnet1-k8s.ops.vega.xyz +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json + NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 diff --git a/apps/governance-e2e/.env.devnet b/apps/governance-e2e/.env.devnet index 53db0acbd..7a3080201 100644 --- a/apps/governance-e2e/.env.devnet +++ b/apps/governance-e2e/.env.devnet @@ -3,3 +3,4 @@ NX_VEGA_ENV=DEVNET NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHERSCAN_URL=https://sepolia.etherscan.io +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json diff --git a/apps/governance-e2e/.env.mainnet b/apps/governance-e2e/.env.mainnet index 7596bcd07..9a252e56e 100644 --- a/apps/governance-e2e/.env.mainnet +++ b/apps/governance-e2e/.env.mainnet @@ -3,3 +3,4 @@ NX_VEGA_ENV=MAINNET NX_VEGA_URL=https://api.vega.community/graphql NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHERSCAN_URL=https://etherscan.io +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json diff --git a/apps/governance-e2e/.env.testnet b/apps/governance-e2e/.env.testnet index 218bc4e94..76a430550 100644 --- a/apps/governance-e2e/.env.testnet +++ b/apps/governance-e2e/.env.testnet @@ -3,3 +3,4 @@ NX_VEGA_ENV=TESTNET NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHERSCAN_URL=https://sepolia.etherscan.io +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json diff --git a/apps/governance-e2e/src/fixtures/mocks/network-upgrade.ts b/apps/governance-e2e/src/fixtures/mocks/network-upgrade.ts index 8aabb8974..2c229de58 100644 --- a/apps/governance-e2e/src/fixtures/mocks/network-upgrade.ts +++ b/apps/governance-e2e/src/fixtures/mocks/network-upgrade.ts @@ -19,7 +19,22 @@ export const upgradeProposalsData = { }, { node: { - upgradeBlockHeight: '1955065', + upgradeBlockHeight: '10001', + vegaReleaseTag: 'v0.71.0+dev-12156-bca1d57e', + approvers: [ + '02a6531716b7a6d82779b7793c3ad2fcb47290ea2ff0912c56f61219bd9675ff', + '121934387281812a2d5e6913e5d57c0f85a8f169e2752347ee2e23b52d46623c', + '65c80e2f5f84e2109eec30810f137ba04cbbecaba8f27706c146cc6c6f90db29', + 'bd6339d2428c79ac3bc9011771236d17bac92bcb1806423388d52fb440043aef', + ], + status: 'PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED', + __typename: 'ProtocolUpgradeProposal', + }, + __typename: 'ProtocolUpgradeProposalEdge', + }, + { + node: { + upgradeBlockHeight: '20', vegaReleaseTag: 'v0.71.0+dev-12156-bca1d57e', approvers: [ '02a6531716b7a6d82779b7793c3ad2fcb47290ea2ff0912c56f61219bd9675ff', diff --git a/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts b/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts index c5e67fd51..6a00b3d70 100644 --- a/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts +++ b/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts @@ -212,7 +212,7 @@ context( closeStakingDialog(); navigateTo(navigation.validators); cy.get(`[row-id="${0}"]`) - .eq(1) + .first() .within(() => { cy.getByTestId(stakeValidatorListTotalStake) .should('have.text', '3,002.00') @@ -222,7 +222,7 @@ context( .and('be.visible'); }); cy.get(`[row-id="${1}"]`) - .eq(1) + .first() .within(() => { cy.getByTestId(stakeValidatorListTotalStake) .scrollIntoView() @@ -262,10 +262,10 @@ context( '2' ); waitForBeginningOfEpoch(); - cy.getByTestId(stakeValidatorListStakePercentage).should( - 'have.text', - '50.02%' - ); + cy.getByTestId( + stakeValidatorListStakePercentage, + epochTimeout + ).should('have.text', '50.02%'); navigateTo(navigation.validators); validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%'); } diff --git a/apps/governance-e2e/src/integration/view/proposal.cy.ts b/apps/governance-e2e/src/integration/view/proposal.cy.ts index 0c98565a3..d81460944 100644 --- a/apps/governance-e2e/src/integration/view/proposal.cy.ts +++ b/apps/governance-e2e/src/integration/view/proposal.cy.ts @@ -15,6 +15,7 @@ const proposalDocumentationLink = 'proposal-documentation-link'; const connectToVegaWalletButton = 'connect-to-vega-wallet-btn'; const governanceDocsUrl = 'https://vega.xyz/governance'; const networkUpgradeProposalListItem = 'protocol-upgrade-proposals-list-item'; +const proposalUpgradeBlockHeight = 'protocol-upgrade-proposal-block-height'; const closedProposals = 'closed-proposals'; const closedProposalToggle = 'closed-proposals-toggle-networkUpgrades'; const protocolUpgradeTime = 'protocol-upgrade-time'; @@ -184,7 +185,7 @@ context( 'have.text', 'Vega release tag: v1' ); - cy.getByTestId('protocol-upgrade-proposal-block-height').should( + cy.getByTestId(proposalUpgradeBlockHeight).should( 'have.text', 'Upgrade block height: 2015942' ); @@ -199,7 +200,15 @@ context( }); cy.getByTestId(closedProposalToggle).click(); cy.getByTestId(closedProposals).within(() => { - cy.getByTestId(networkUpgradeProposalListItem).should('have.length', 1); + cy.getByTestId(networkUpgradeProposalListItem).should('have.length', 2); + cy.getByTestId(networkUpgradeProposalListItem) + .first() + .within(() => { + cy.getByTestId(proposalUpgradeBlockHeight).should( + 'contain.text', + '10001' + ); + }); }); }); diff --git a/apps/governance-e2e/src/support/staking.functions.ts b/apps/governance-e2e/src/support/staking.functions.ts index e8ad75851..73802df1b 100644 --- a/apps/governance-e2e/src/support/staking.functions.ts +++ b/apps/governance-e2e/src/support/staking.functions.ts @@ -170,21 +170,20 @@ export function clickOnValidatorFromList( validatorName = null ) { cy.contains('Loading...', epochTimeout).should('not.exist'); - waitForBeginningOfEpoch(); // below is to ensure validator list is shown cy.get(stakeValidatorListName, { timeout: 10000 }).should('exist'); cy.get(stakeValidatorListPendingStake, txTimeout).should( 'not.contain', '2,000,000,000,000,000,000.00' // number due to bug #936 ); + waitForBeginningOfEpoch(); if (validatorName) { cy.contains(validatorName).click(); } else { cy.get(`[row-id="${validatorNumber}"]`) .should('be.visible') .first() - .as('validatorOnList'); - cy.get('@validatorOnList').click(); + .click(); } } @@ -196,7 +195,7 @@ export function validateValidatorListTotalStakeAndShare( cy.contains('Loading...', epochTimeout).should('not.exist'); waitForBeginningOfEpoch(); cy.get(`[row-id="${positionOnList}"]`) - .eq(1) + .first() .within(() => { cy.getByTestId(stakeValidatorListTotalStake, epochTimeout).should( 'have.text', diff --git a/apps/governance/.env.capsule b/apps/governance/.env.capsule index 9e818f3ac..64d641033 100644 --- a/apps/governance/.env.capsule +++ b/apps/governance/.env.capsule @@ -22,6 +22,8 @@ NX_VEGA_REST_URL=http://localhost:3008/api/v2/ NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json + NX_TENDERMINT_URL=http://localhost:26617 NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket diff --git a/apps/governance/.env.devnet b/apps/governance/.env.devnet index e1a177258..3acb4de4d 100644 --- a/apps/governance/.env.devnet +++ b/apps/governance/.env.devnet @@ -19,6 +19,8 @@ NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fa NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/ NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json + # Cosmic elevator flags NX_SUCCESSOR_MARKETS=true diff --git a/apps/governance/.env.mainnet b/apps/governance/.env.mainnet index cf9e143f5..9208e58b4 100644 --- a/apps/governance/.env.mainnet +++ b/apps/governance/.env.mainnet @@ -16,6 +16,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou NX_VEGA_REST_URL=https://api.vega.community/api/v2/ NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_TENDERMINT_URL=https://be.vega.community NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket diff --git a/apps/governance/.env.mainnet-mirror b/apps/governance/.env.mainnet-mirror index 466f87123..3e20d16a3 100644 --- a/apps/governance/.env.mainnet-mirror +++ b/apps/governance/.env.mainnet-mirror @@ -15,6 +15,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/ NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket diff --git a/apps/governance/.env.stagnet1 b/apps/governance/.env.stagnet1 index 64171ba1f..29d15032f 100644 --- a/apps/governance/.env.stagnet1 +++ b/apps/governance/.env.stagnet1 @@ -9,6 +9,7 @@ NX_DELEGATIONS_PAGINATION=50 NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/ +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground diff --git a/apps/governance/.env.testnet b/apps/governance/.env.testnet index 408804c11..38cf946ce 100644 --- a/apps/governance/.env.testnet +++ b/apps/governance/.env.testnet @@ -14,6 +14,7 @@ NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground diff --git a/apps/governance/.env.validators-testnet b/apps/governance/.env.validators-testnet index d4489c8bc..7be31358f 100644 --- a/apps/governance/.env.validators-testnet +++ b/apps/governance/.env.validators-testnet @@ -11,6 +11,7 @@ NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground diff --git a/apps/governance/src/routes/home/index.tsx b/apps/governance/src/routes/home/index.tsx index 3d098cc9a..a2ae40d6a 100644 --- a/apps/governance/src/routes/home/index.tsx +++ b/apps/governance/src/routes/home/index.tsx @@ -45,7 +45,7 @@ const HomeProposals = ({ const { t } = useTranslation(); return ( -
+

{t('homeProposalsIntro')}

diff --git a/apps/governance/src/routes/proposals/components/proposal/proposal.tsx b/apps/governance/src/routes/proposals/components/proposal/proposal.tsx index ebd3b0da7..327d84cfe 100644 --- a/apps/governance/src/routes/proposals/components/proposal/proposal.tsx +++ b/apps/governance/src/routes/proposals/components/proposal/proposal.tsx @@ -127,7 +127,7 @@ export const Proposal = ({ voteState={voteState} /> -
+
diff --git a/apps/governance/src/routes/staking/home/validator-tables/consensus-validators-table.tsx b/apps/governance/src/routes/staking/home/validator-tables/consensus-validators-table.tsx index cb25126fc..b9b42cad3 100644 --- a/apps/governance/src/routes/staking/home/validator-tables/consensus-validators-table.tsx +++ b/apps/governance/src/routes/staking/home/validator-tables/consensus-validators-table.tsx @@ -352,7 +352,6 @@ export const ConsensusValidatorsTable = ({ field: ValidatorFields.RANKING_INDEX, headerName: '#', width: 60, - pinned: 'left', }, { field: ValidatorFields.VALIDATOR, @@ -362,7 +361,6 @@ export const ConsensusValidatorsTable = ({ if (a === b) return 0; return a > b ? 1 : -1; }, - pinned: 'left', width: 260, }, { diff --git a/apps/governance/src/routes/staking/home/validator-tables/standby-pending-validators-table.tsx b/apps/governance/src/routes/staking/home/validator-tables/standby-pending-validators-table.tsx index c3a1d55ef..f1553756a 100644 --- a/apps/governance/src/routes/staking/home/validator-tables/standby-pending-validators-table.tsx +++ b/apps/governance/src/routes/staking/home/validator-tables/standby-pending-validators-table.tsx @@ -195,14 +195,12 @@ export const StandbyPendingValidatorsTable = ({ field: ValidatorFields.RANKING_INDEX, headerName: '#', width: 60, - pinned: 'left', }, { field: ValidatorFields.VALIDATOR, headerName: t(ValidatorFields.VALIDATOR).toString(), cellRenderer: ValidatorRenderer, comparator: ({ name: a }, { name: b }) => Math.sign(a - b), - pinned: 'left', width: 260, }, { diff --git a/apps/governance/src/routes/staking/node/validator-table.tsx b/apps/governance/src/routes/staking/node/validator-table.tsx index dcec7bb68..a91ab84e9 100644 --- a/apps/governance/src/routes/staking/node/validator-table.tsx +++ b/apps/governance/src/routes/staking/node/validator-table.tsx @@ -122,38 +122,43 @@ export const ValidatorTable = ({
- - - - {t('id')} - - {node.id} - - - - {t('ABOUT THIS VALIDATOR')} +
+ + + + {t('id')} + + {node.id} + + + + {t('ABOUT THIS VALIDATOR')} - - - {node.infoUrl} - - - - - - {t('STATUS')} - - - - - - {t(statusTranslationKey(node.rankingScore.status))} - + + + {node.infoUrl} + + + + + + {t('STATUS')} - - - - + + + + + {t(statusTranslationKey(node.rankingScore.status))} + + + + + + +
{t('validatorTableIntro')}{' '} @@ -167,144 +172,154 @@ export const ValidatorTable = ({
- - - - {t('VEGA ADDRESS / PUBLIC KEY')} - - {node.pubkey} - - - - {t('SERVER LOCATION')} - - {countryData.find((c) => c.code === node.location)?.name || - t('not available')} - - - - {t('ETHEREUM ADDRESS')} - - - {node.ethereumAddress} - - - - - +
+ + + + {t('VEGA ADDRESS / PUBLIC KEY')} + + {node.pubkey} + + + + {t('SERVER LOCATION')} + + {countryData.find((c) => c.code === node.location)?.name || + t('not available')} + + + + {t('ETHEREUM ADDRESS')} + + + {node.ethereumAddress} + + + + + +
- - - - {t('STAKED BY OPERATOR')} +
+ + + + {t('STAKED BY OPERATOR')} - - - {formatNumber(toBigNum(node.stakedByOperator, decimals))} + + + {formatNumber(toBigNum(node.stakedByOperator, decimals))} + + + + + {t('STAKED BY DELEGATES')} + + + + {formatNumber(toBigNum(node.stakedByDelegates, decimals))} + + + + + + {t('TOTAL STAKE')} - - - - {t('STAKED BY DELEGATES')} - - - {formatNumber(toBigNum(node.stakedByDelegates, decimals))} + + + {formatNumber(toBigNum(node.stakedTotal, decimals))} + - - - - - {t('TOTAL STAKE')} - + + + {t('PENDING STAKE')} - - - {formatNumber(toBigNum(node.stakedTotal, decimals))} - - - - - {t('PENDING STAKE')} + + + {formatNumber(toBigNum(node.pendingStake, decimals))} + + + + + {t('STAKE SHARE')} - - - {formatNumber(toBigNum(node.pendingStake, decimals))} - - - - - {t('STAKE SHARE')} - - - {stakePercentage} - - - - + + {stakePercentage} + + + + +
- - - - {t('OVERSTAKED PENALTY')} +
+ + + + {t('OVERSTAKED PENALTY')} - - - {formatNumberPercentage(penalties.overstaked, 2)} - - - - - {t('PERFORMANCE PENALTY')} + + + {formatNumberPercentage(penalties.overstaked, 2)} + + + + + {t('PERFORMANCE PENALTY')} - - - {formatNumberPercentage(penalties.performance, 2)} + + + {formatNumberPercentage(penalties.performance, 2)} + + + + + + {t('TOTAL PENALTIES')} - - - - - {t('TOTAL PENALTIES')} - - - {formatNumberPercentage(penalties.overall, 2)} - - - - + + + {formatNumberPercentage(penalties.overall, 2)} + + + + + +
- - - - {t('UNNORMALISED VOTING POWER')} +
+ + + + {t('UNNORMALISED VOTING POWER')} - - - {getUnnormalisedVotingPower(rawValidatorScore)} + + + {getUnnormalisedVotingPower(rawValidatorScore)} + + + + + + {t('NORMALISED VOTING POWER')} - - - - - {t('NORMALISED VOTING POWER')} - - - - {getNormalisedVotingPower(node.rankingScore.votingPower)} - - - - - + + + {getNormalisedVotingPower(node.rankingScore.votingPower)} + + + + + +
); diff --git a/apps/governance/src/routes/token/token-details/token-details.tsx b/apps/governance/src/routes/token/token-details/token-details.tsx index 7472c48e1..1012106c4 100644 --- a/apps/governance/src/routes/token/token-details/token-details.tsx +++ b/apps/governance/src/routes/token/token-details/token-details.tsx @@ -54,7 +54,7 @@ export const TokenDetails = ({ config.token_vesting_contract?.address || ENV.addresses.tokenVestingAddress; return ( -
+
diff --git a/apps/trading-e2e/.env b/apps/trading-e2e/.env index a575afdea..95b8d5c19 100644 --- a/apps/trading-e2e/.env +++ b/apps/trading-e2e/.env @@ -15,6 +15,8 @@ NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/ NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session" NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_SENTRY_DSN=https://dummy@o999999.ingest.sentry.io/9999999 +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json + # Expose some env vars to cypress environment for market setup CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session diff --git a/apps/trading-e2e/.env.capsule b/apps/trading-e2e/.env.capsule index 145a6fb7c..cf2ef8201 100644 --- a/apps/trading-e2e/.env.capsule +++ b/apps/trading-e2e/.env.capsule @@ -12,6 +12,8 @@ NX_VEGA_URL=http://localhost:3008/graphql NX_VEGA_WALLET_URL=http://localhost:1789 NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/ NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session" +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json + # Expose some env vars to cypress environment for market setup CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session diff --git a/apps/trading-e2e/src/integration/deposit.cy.ts b/apps/trading-e2e/src/integration/deposit.cy.ts index ab2858c40..b1947c2d6 100644 --- a/apps/trading-e2e/src/integration/deposit.cy.ts +++ b/apps/trading-e2e/src/integration/deposit.cy.ts @@ -104,7 +104,7 @@ describe('deposit actions', { tags: '@smoke' }, () => { cy.visit('/#/markets/market-1'); }); - it('Deposit to trade is visible', () => { + it.skip('Deposit to trade is visible', () => { cy.getByTestId('Collateral').click(); cy.get('[row-id="asset-id"]').contains('tEURO').should('be.visible'); cy.contains('[data-testid="deposit"]', 'Deposit').should('be.visible'); diff --git a/apps/trading-e2e/src/integration/market-info.cy.ts b/apps/trading-e2e/src/integration/market-info.cy.ts deleted file mode 100644 index 35720c4a6..000000000 --- a/apps/trading-e2e/src/integration/market-info.cy.ts +++ /dev/null @@ -1,256 +0,0 @@ -import { MarketTradingModeMapping } from '@vegaprotocol/types'; -import { MarketState } from '@vegaprotocol/types'; -import compact from 'lodash/compact'; - -const accordionContent = 'accordion-content'; -const blockExplorerLink = 'block-explorer-link'; -const dialogClose = 'dialog-close'; -const dialogContent = 'dialog-content'; -const externalLink = 'external-link'; -const githubLink = 'github-link'; -const liquidityLink = 'view-liquidity-link'; -const marketInfoBtn = 'Info'; -const marketTitle = 'accordion-title'; -const providerName = 'provider-name'; -const row = 'key-value-table-row'; -const verifiedProofs = 'verified-proofs'; - -describe('market info is displayed', { tags: '@smoke' }, () => { - beforeEach(() => { - cy.mockTradingPage(); - }); - - before(() => { - cy.setOnBoardingViewed(); - cy.mockTradingPage(MarketState.STATE_ACTIVE); - cy.mockSubscription(); - cy.visit('/#/markets/market-0'); - cy.wait('@Markets'); - cy.getByTestId(marketInfoBtn).click(); - cy.wait('@MarketInfo'); - }); - - it('current fees displayed', () => { - // 6002-MDET-101 - cy.getByTestId(marketTitle).contains('Current fees').click(); - validateMarketDataRow(0, 'Maker Fee', '0.02%'); - validateMarketDataRow(1, 'Infrastructure Fee', '0.05%'); - validateMarketDataRow(2, 'Liquidity Fee', '1.00%'); - validateMarketDataRow(3, 'Total Fees', '1.07%'); - }); - - it('market price', () => { - // 6002-MDET-102 - cy.getByTestId(marketTitle).contains('Market price').click(); - validateMarketDataRow(0, 'Mark Price', '46,126.90058'); - validateMarketDataRow(1, 'Best Bid Price', '44,126.90058 '); - validateMarketDataRow(2, 'Best Offer Price', '48,126.90058 '); - validateMarketDataRow(3, 'Quote Unit', 'BTC'); - }); - - it('market volume displayed', () => { - // 6002-MDET-103 - cy.getByTestId(marketTitle).contains('Market volume').click(); - validateMarketDataRow(1, 'Open Interest', '-'); - validateMarketDataRow(2, 'Best Bid Volume', '1'); - validateMarketDataRow(3, 'Best Offer Volume', '3'); - validateMarketDataRow(4, 'Best Static Bid Volume', '2'); - validateMarketDataRow(5, 'Best Static Offer Volume', '4'); - }); - - it('insurance pool displayed', () => { - // 6002-MDET-104 - cy.getByTestId(marketTitle).contains('Insurance pool').click(); - validateMarketDataRow(0, 'Balance', '0'); - }); - - it('key details displayed', () => { - // 6002-MDET-201 - cy.getByTestId(marketTitle).contains('Key details').click(); - - const rows: [string, string][] = compact([ - ['Name', 'BTCUSD Monthly (30 Jun 2022)'], - ['Market ID', 'market-0'], - Cypress.env('NX_SUCCESSOR_MARKETS') && ['Parent Market ID', 'PARENT-A'], - Cypress.env('NX_SUCCESSOR_MARKETS') && [ - 'Insurance Pool Fraction', - '0.75', - ], - ['Trading Mode', MarketTradingModeMapping.TRADING_MODE_CONTINUOUS], - ['Market Decimal Places', '5'], - ['Position Decimal Places', '0'], - ['Settlement Asset Decimal Places', '5'], - ]); - - for (const rowNumber in rows) { - const [name, value] = rows[rowNumber]; - validateMarketDataRow(Number(rowNumber), name, value); - } - }); - - it('instrument displayed', () => { - // 6002-MDET-202 - cy.getByTestId(marketTitle).contains('Instrument').click(); - - validateMarketDataRow(0, 'Market Name', 'BTCUSD Monthly (30 Jun 2022)'); - validateMarketDataRow(1, 'Code', 'BTCUSD.MF21'); - validateMarketDataRow(2, 'Product Type', 'Future'); - validateMarketDataRow(3, 'Quote Name', 'BTC'); - }); - - it('oracle displayed', () => { - // 6002-MDET-203 - cy.getByTestId(marketTitle).contains('Oracle').click(); - - cy.getByTestId(accordionContent) - .getByTestId(providerName) - .and('contain', 'Another oracle'); - - cy.getByTestId(providerName).should('be.visible').click(); - cy.getByTestId(dialogContent) - .eq(1) - .within(() => { - cy.getByTestId(blockExplorerLink).contains('Block explorer'); - cy.getByTestId(githubLink).contains('Oracle repository'); - }); - cy.getByTestId(dialogClose).click(); - - cy.getByTestId(accordionContent) - .getByTestId(verifiedProofs) - .and('contain', '1'); - }); - - it('settlement asset displayed', () => { - // 6002-MDET-206 - cy.getByTestId(marketTitle).contains('Settlement asset').click(); - cy.window().then((win) => { - cy.stub(win, 'prompt').returns('DISABLED WINDOW PROMPT'); - }); - validateMarketDataRow(0, 'ID', 'asset-id'); - validateMarketDataRow(1, 'Type', 'ERC20'); - validateMarketDataRow(2, 'Name', 'Euro'); - validateMarketDataRow(3, 'Symbol', 'tEURO'); - validateMarketDataRow(4, 'Decimals', '5'); - validateMarketDataRow(5, 'Quantum', '1'); - validateMarketDataRow(6, 'Status', 'Enabled'); - validateMarketDataRow(7, 'Contract address', '0x0158…78a4'); - validateMarketDataRow(8, 'Withdrawal threshold', '0.0005'); - validateMarketDataRow(9, 'Lifetime limit', '1,230'); - validateMarketDataRow(10, 'Infrastructure fee account balance', '0.00001'); - validateMarketDataRow(11, 'Global reward pool account balance', '0.00002'); - }); - - it('metadata displayed', () => { - // 6002-MDET-207 - cy.getByTestId(marketTitle).contains('Metadata').click(); - - validateMarketDataRow(0, 'Formerly', '076BB86A5AA41E3E'); - validateMarketDataRow(1, 'Base', 'BTC'); - validateMarketDataRow(2, 'Quote', 'USD'); - validateMarketDataRow(3, 'Class', 'fx/crypto'); - validateMarketDataRow(4, 'Sector', 'crypto'); - }); - - it('risk model displayed', () => { - // 6002-MDET-208 - cy.getByTestId(marketTitle).contains('Risk model').click(); - validateMarketDataRow(0, 'Tau', '0.0001140771161'); - validateMarketDataRow(1, 'Risk Aversion Parameter', '0.01'); - }); - - it('risk parameters displayed', () => { - // 6002-MDET-209 - cy.getByTestId(marketTitle).contains('Risk parameters').click(); - validateMarketDataRow(0, 'R', '0.016'); - validateMarketDataRow(1, 'Sigma', '0.3'); - }); - - it('risk factors displayed', () => { - // 6002-MDET-210 - cy.getByTestId(marketTitle).contains('Risk factors').click(); - - validateMarketDataRow(0, 'Short', '0.008571790367285281'); - validateMarketDataRow(1, 'Long', '0.008508132993273576'); - }); - - it('price monitoring bounds displayed', () => { - // 6002-MDET-211 - cy.getByTestId(marketTitle).contains('Price monitoring bounds 1').click(); - cy.get('p.col-span-1').contains('99.99999% probability price bounds'); - cy.get('p.col-span-1').contains('Within 43,200 seconds'); - validateMarketDataRow(0, 'Highest Price', '7.97323 '); - validateMarketDataRow(1, 'Lowest Price', '6.54701 '); - }); - - it('liquidity monitoring parameters displayed', () => { - // 6002-MDET-212 - cy.getByTestId(marketTitle) - .contains('Liquidity monitoring parameters') - .click(); - - validateMarketDataRow(0, 'Triggering Ratio', '0.7'); - validateMarketDataRow(1, 'Time Window', '3,600'); - validateMarketDataRow(2, 'Scaling Factor', '10'); - }); - - it('liquidity displayed', () => { - // 6002-MDET-213 - cy.getByTestId(marketTitle) - .contains(/Liquidity(?! m)/) - .click(); - - validateMarketDataRow(0, 'Target Stake', '10.00 tBTC'); - validateMarketDataRow(1, 'Supplied Stake', '0.01 tBTC'); - validateMarketDataRow(2, 'Market Value Proxy', '20.00 tBTC'); - cy.getByTestId(liquidityLink).should( - 'have.text', - 'View liquidity provision table' - ); - }); - - it('liquidity price range displayed', () => { - // 6002-MDET-214 - cy.getByTestId(marketTitle).contains('Liquidity price range').click(); - - validateMarketDataRow(0, 'Liquidity Price Range', '2.00% of mid price'); - validateMarketDataRow(1, 'Lowest Price', '45,204.362 BTC'); - validateMarketDataRow(2, 'Highest Price', '47,049.438 BTC'); - }); - - it('proposal displayed', () => { - // 6002-MDET-301 - cy.getByTestId(marketTitle).contains('Proposal').click(); - - cy.getByTestId(accordionContent) - .find(`[data-testid="${externalLink}"]`) - .first() - .should('have.text', 'View governance proposal') - .and('have.attr', 'href') - .and('contain', '/proposals/market-0'); - cy.getByTestId(accordionContent) - .find(`[data-testid="${externalLink}"]`) - .eq(1) - .should('have.text', 'Propose a change to market') - .and('have.attr', 'href') - .and('contain', '/proposals/propose/update-market'); - }); - - afterEach('close toggle', () => { - cy.get('[data-state="open"]').then((tab) => { - if (tab) tab.find('button').trigger('click'); - }); - }); - - function validateMarketDataRow( - rowNumber: number, - name: string, - value: string - ) { - cy.getByTestId(row) - .eq(rowNumber) - .within(() => { - cy.get('dt').should('contain.text', name); - cy.get('dd').should('contain.text', value); - }); - } -}); diff --git a/apps/trading-e2e/src/integration/wallet-vega.cy.ts b/apps/trading-e2e/src/integration/wallet-vega.cy.ts index 850b1146d..f0b5c19eb 100644 --- a/apps/trading-e2e/src/integration/wallet-vega.cy.ts +++ b/apps/trading-e2e/src/integration/wallet-vega.cy.ts @@ -10,10 +10,10 @@ const dialogContent = 'dialog-content'; describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => { beforeEach(() => { // Using portfolio page as it requires vega wallet connection - cy.visit('/#/portfolio'); cy.mockTradingPage(); cy.mockSubscription(); cy.setOnBoardingViewed(); + cy.visit('/#/portfolio'); cy.get('[data-testid="pathname-/portfolio"]').should('exist'); }); diff --git a/apps/trading/.env b/apps/trading/.env index 78e6de583..1e3dba3ca 100644 --- a/apps/trading/.env +++ b/apps/trading/.env @@ -1,27 +1,28 @@ +NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 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/stagnet1/vegawallet-stagnet1.toml -NX_VEGA_ENV=STAGNET1 -NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks +NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613 +NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml +NX_VEGA_ENV=TESTNET +NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"} NX_VEGA_TOKEN_URL=https://governance.fairground.wtf NX_VEGA_WALLET_URL=http://localhost:1789 NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases -NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json -NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 +NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json +NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports +NX_VEGA_CONSOLE_URL=https://console.fairground.wtf NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground - # Cosmic elevator flags NX_SUCCESSOR_MARKETS=true NX_STOP_ORDERS=true -# NX_ICEBERG_ORDERS +NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS -NX_METAMASK_SNAPS=true +NX_METAMASK_SNAPS=false -NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks -NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket +NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz +NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket diff --git a/apps/trading/.env.mainnet b/apps/trading/.env.mainnet index b02a11b93..547137f08 100644 --- a/apps/trading/.env.mainnet +++ b/apps/trading/.env.mainnet @@ -15,15 +15,18 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports NX_VEGA_CONSOLE_URL=https://console.vega.xyz NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json + + # TAG name of the current app version - TODO: bump to the latest upon release -NX_APP_VERSION=v0.21.0-core-0.72.14 +NX_APP_VERSION=v0.21.1-core-0.72.14 # Cosmic elevator flags NX_SUCCESSOR_MARKETS=true NX_STOP_ORDERS=true NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS -NX_METAMASK_SNAPS=false +NX_METAMASK_SNAPS=true NX_TENDERMINT_URL=https://be.vega.community NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket diff --git a/apps/trading/.env.stagnet1 b/apps/trading/.env.stagnet1 index f2e4129c8..fc93d40cb 100644 --- a/apps/trading/.env.stagnet1 +++ b/apps/trading/.env.stagnet1 @@ -15,6 +15,8 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json + # Cosmic elevator flags NX_SUCCESSOR_MARKETS=true diff --git a/apps/trading/.env.testnet b/apps/trading/.env.testnet index 1e3dba3ca..78caf98d6 100644 --- a/apps/trading/.env.testnet +++ b/apps/trading/.env.testnet @@ -16,13 +16,14 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports NX_VEGA_CONSOLE_URL=https://console.fairground.wtf NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json # Cosmic elevator flags NX_SUCCESSOR_MARKETS=true NX_STOP_ORDERS=true NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS -NX_METAMASK_SNAPS=false +NX_METAMASK_SNAPS=true NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket diff --git a/apps/trading/.env.validators-testnet b/apps/trading/.env.validators-testnet index 21af40de4..f36a8c454 100644 --- a/apps/trading/.env.validators-testnet +++ b/apps/trading/.env.validators-testnet @@ -17,6 +17,7 @@ NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground +NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json # Cosmic elevator flags NX_SUCCESSOR_MARKETS=true diff --git a/apps/trading/client-pages/home/home.tsx b/apps/trading/client-pages/home/home.tsx index 7d0b48cf2..c4974bc99 100644 --- a/apps/trading/client-pages/home/home.tsx +++ b/apps/trading/client-pages/home/home.tsx @@ -1,20 +1,16 @@ import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { marketsWithDataProvider } from '@vegaprotocol/markets'; -import { useDataProvider } from '@vegaprotocol/data-provider'; -import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; +import { Loader, Splash } from '@vegaprotocol/ui-toolkit'; import { Links, Routes } from '../../pages/client-router'; import { useGlobalStore } from '../../stores'; +import { useTopTradedMarkets } from '../../lib/hooks/use-top-traded-markets'; +// The home pages only purpose is to redirect to the users last market, +// the top traded if they are new, or fall back to the list of markets. +// Thats why we just render a loader here export const Home = () => { const navigate = useNavigate(); - // The default market selected in the platform behind the overlay - // should be the oldest market that is currently trading in continuous mode(i.e. not in auction). - const { data, error, loading } = useDataProvider({ - dataProvider: marketsWithDataProvider, - variables: undefined, - }); - const update = useGlobalStore((store) => store.update); + const { data } = useTopTradedMarkets(); const marketId = useGlobalStore((store) => store.marketId); useEffect(() => { @@ -32,12 +28,11 @@ export const Home = () => { navigate(Links[Routes.MARKETS]()); } } - }, [marketId, data, navigate, update]); + }, [marketId, data, navigate]); return ( - - {/* Render a loading and error state but we will redirect if markets are found */} - {null} - + + + ); }; diff --git a/apps/trading/client-pages/market/market.tsx b/apps/trading/client-pages/market/market.tsx index 2175a48c2..05f8a3159 100644 --- a/apps/trading/client-pages/market/market.tsx +++ b/apps/trading/client-pages/market/market.tsx @@ -11,6 +11,7 @@ import { TradePanels } from './trade-panels'; import { useNavigate, useParams } from 'react-router-dom'; import { Links, Routes } from '../../pages/client-router'; import { ViewType, useSidebar } from '../../components/sidebar'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; const calculatePrice = (markPrice?: string, decimalPlaces?: number) => { return markPrice && decimalPlaces @@ -58,7 +59,9 @@ const TitleUpdater = ({ export const MarketPage = () => { const { marketId } = useParams(); const navigate = useNavigate(); - const { init, view, setView } = useSidebar(); + const currentRouteId = useGetCurrentRouteId(); + const { setViews, getView } = useSidebar(); + const view = getView(currentRouteId); const { screenSize } = useScreenDimensions(); const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize); const update = useGlobalStore((store) => store.update); @@ -72,12 +75,11 @@ export const MarketPage = () => { } }, [update, lastMarketId, data?.id]); - // Make sidebar open on deal ticket by default useEffect(() => { - if (init && view === null) { - setView({ type: ViewType.Order }); + if (largeScreen && view === undefined) { + setViews({ type: ViewType.Order }, currentRouteId); } - }, [init, view, setView]); + }, [setViews, view, currentRouteId, largeScreen]); const tradeView = useMemo(() => { if (largeScreen) { diff --git a/apps/trading/client-pages/market/trade-grid.tsx b/apps/trading/client-pages/market/trade-grid.tsx index ab64f77b1..79433a3b1 100644 --- a/apps/trading/client-pages/market/trade-grid.tsx +++ b/apps/trading/client-pages/market/trade-grid.tsx @@ -56,6 +56,7 @@ const MainGrid = memo( } > @@ -72,7 +73,7 @@ const MainGrid = memo( @@ -180,7 +181,7 @@ const TradeGridChild = ({ children }: TradeGridChildProps) => { {({ width, height }) => (
{children}
diff --git a/apps/trading/client-pages/markets/closed.spec.tsx b/apps/trading/client-pages/markets/closed.spec.tsx index 85f302be9..d5482ba26 100644 --- a/apps/trading/client-pages/markets/closed.spec.tsx +++ b/apps/trading/client-pages/markets/closed.spec.tsx @@ -1,4 +1,4 @@ -import { act, render, screen, within, waitFor } from '@testing-library/react'; +import { act, render, screen, within } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { Closed } from './closed'; import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types'; @@ -10,55 +10,21 @@ import type { OracleSpecDataConnectionQuery, MarketsDataQuery, MarketsQuery, - SuccessorMarketIdsQuery, } from '@vegaprotocol/markets'; import { OracleSpecDataConnectionDocument, MarketsDataDocument, MarketsDocument, - SuccessorMarketIdsDocument, } from '@vegaprotocol/markets'; import type { VegaWalletContextShape } from '@vegaprotocol/wallet'; import { VegaWalletContext } from '@vegaprotocol/wallet'; import { addDecimalsFormatNumber } from '@vegaprotocol/utils'; -import { FLAGS } from '@vegaprotocol/environment'; import { createMarketFragment, marketsQuery, marketsDataQuery, createMarketsDataFragment, } from '@vegaprotocol/mock'; -import type { FeatureFlags } from '@vegaprotocol/environment'; - -jest.mock('@vegaprotocol/markets', () => ({ - ...jest.requireActual('@vegaprotocol/markets'), - useSuccessorMarket: (marketId: string) => - marketId === 'include-0' - ? { - data: { - id: 'successorMarketID', - state: 'STATE_ACTIVE', - tradableInstrument: { - instrument: { - name: 'Successor Market Name', - code: 'SuccessorCode', - }, - }, - }, - } - : { data: undefined }, -})); - -jest.mock('@vegaprotocol/environment', () => { - const actual = jest.requireActual('@vegaprotocol/environment'); - return { - ...actual, - FLAGS: { - ...actual.FLAGS, - SUCCESSOR_MARKETS: true, - } as FeatureFlags, - }; -}); describe('Closed', () => { let originalNow: typeof Date.now; @@ -218,10 +184,8 @@ describe('Closed', () => { const headers = screen.getAllByRole('columnheader'); const expectedHeaders = [ 'Market', - 'Description', 'Status', 'Settlement date', - 'Successor market', 'Best bid', 'Best offer', 'Mark price', @@ -235,10 +199,8 @@ describe('Closed', () => { const cells = screen.getAllByRole('gridcell'); const expectedValues = [ market.tradableInstrument.instrument.code, - market.tradableInstrument.instrument.name, MarketStateMapping[market.state], '3 days ago', - '-', /* eslint-disable @typescript-eslint/no-non-null-assertion */ addDecimalsFormatNumber(marketsData.bestBidPrice, market.decimalPlaces), addDecimalsFormatNumber( @@ -340,43 +302,26 @@ describe('Closed', () => { .getAllByRole('gridcell') .filter((cell) => cell.getAttribute('col-id') === 'code') .map((cell) => { - const marketId = within(cell) - .getByTestId('market-code') - .getAttribute('data-market-id'); - return marketId; + const marketCode = within(cell).getByTestId('stack-cell-primary'); + return marketCode.textContent; }); - expect(cells).toEqual(expectedRows.map((m) => m.node.id)); + expect(cells).toEqual( + expectedRows.map((m) => m.node.tradableInstrument.instrument.code) + ); }); it('successor marked should be visible', async () => { - const mixedMarkets = [ + const marketsWithSuccessorID = [ { __typename: 'MarketEdge' as const, node: createMarketFragment({ id: 'include-0', state: MarketState.STATE_SETTLED, + successorMarketID: 'successor', }), }, - { - __typename: 'MarketEdge' as const, - node: { - ...createMarketFragment({ - id: 'successorMarketID', - state: MarketState.STATE_ACTIVE, - }), - tradableInstrument: { - ...createMarketFragment().tradableInstrument, - instrument: { - ...createMarketFragment().tradableInstrument.instrument, - id: 'successorAssset', - name: 'Successor Market Name', - code: 'SuccessorCode', - }, - }, - }, - }, ]; - const mixedMarketsMock: MockedResponse = { + const mockWithSuccessors: MockedResponse = { request: { query: MarketsDocument, }, @@ -384,42 +329,17 @@ describe('Closed', () => { data: { marketsConnection: { __typename: 'MarketConnection', - edges: mixedMarkets, + edges: marketsWithSuccessorID, }, }, }, }; - const successorMarketsMock: MockedResponse = { - request: { - query: SuccessorMarketIdsDocument, - }, - result: { - data: { - marketsConnection: { - __typename: 'MarketConnection', - edges: [ - { - node: { - id: 'include-0', - successorMarketID: 'successorMarketID', - parentMarketID: '', - }, - }, - ], - }, - }, - }, - }; - await act(() => { + + await act(async () => { render( { ); }); - await waitFor(() => { - expect( - screen.getByRole('button', { name: /^SuccessorCode/ }) - ).toBeInTheDocument(); - }); - expect( - screen.getByRole('columnheader', { - name: (_name, element) => - element.getAttribute('col-id') === 'successorMarket', - }) - ).toBeInTheDocument(); - screen - .getAllByRole('gridcell', { - name: (_name, element) => - element.getAttribute('col-id') === 'successorMarket', - }) - .forEach((element) => { - expect(element.querySelector('[title="Future"]')?.textContent).toEqual( - 'Futr' - ); - }); - }); + const container = within( + document.querySelector('.ag-center-cols-container') as HTMLElement + ); + const cell = container.getAllByRole('gridcell', { + name: (_name, element) => element.getAttribute('col-id') === 'code', + })[0]; - it('feature flag should hide successors', async () => { - const mockedFlags = jest.mocked(FLAGS); - mockedFlags.SUCCESSOR_MARKETS = false; - - const mixedMarkets = [ - { - __typename: 'MarketEdge' as const, - node: createMarketFragment({ - id: 'include-0', - state: MarketState.STATE_SETTLED, - }), - }, - { - __typename: 'MarketEdge' as const, - node: { - ...createMarketFragment({ - id: 'successorMarketID', - state: MarketState.STATE_ACTIVE, - }), - tradableInstrument: { - ...createMarketFragment().tradableInstrument, - instrument: { - ...createMarketFragment().tradableInstrument.instrument, - id: 'successorAssset', - name: 'Successor Market Name', - code: 'SuccessorCode', - }, - }, - }, - }, - ]; - const mixedMarketsMock: MockedResponse = { - request: { - query: MarketsDocument, - }, - result: { - data: { - marketsConnection: { - __typename: 'MarketConnection', - edges: mixedMarkets, - }, - }, - }, - }; - const successorMarketsMock: MockedResponse = { - request: { - query: SuccessorMarketIdsDocument, - }, - result: { - data: { - marketsConnection: { - __typename: 'MarketConnection', - edges: [ - { - node: { - id: 'include-0', - successorMarketID: 'successorMarketID', - parentMarketID: '', - }, - }, - ], - }, - }, - }, - }; - render( - - - - - - - + expect(within(cell).getByTestId('stack-cell-secondary')).toHaveTextContent( + 'PRNT' ); - await waitFor(() => { - expect( - screen.getByRole('columnheader', { - name: (_name, element) => - element.getAttribute('col-id') === 'settlementDate', - }) - ).toBeInTheDocument(); - }); - screen.getAllByRole('columnheader').forEach((element) => { - expect(element.getAttribute('col-id')).not.toEqual('successorMarket'); - }); }); }); diff --git a/apps/trading/client-pages/markets/closed.tsx b/apps/trading/client-pages/markets/closed.tsx index cc5b2267c..03b49b31f 100644 --- a/apps/trading/client-pages/markets/closed.tsx +++ b/apps/trading/client-pages/markets/closed.tsx @@ -4,13 +4,10 @@ import type { VegaICellRendererParams, VegaValueFormatterParams, } from '@vegaprotocol/datagrid'; -import { - AgGridLazy as AgGrid, - COL_DEFS, - MarketNameCell, -} from '@vegaprotocol/datagrid'; +import { AgGridLazy as AgGrid, COL_DEFS } from '@vegaprotocol/datagrid'; import { useMemo } from 'react'; import { t } from '@vegaprotocol/i18n'; +import type { ProductType } from '@vegaprotocol/types'; import { MarketState, MarketStateMapping } from '@vegaprotocol/types'; import { addDecimalsFormatNumber, @@ -20,17 +17,13 @@ import type { DataSourceFilterFragment, MarketMaybeWithData, } from '@vegaprotocol/markets'; -import { - MarketActionsDropdown, - closedMarketsWithDataProvider, -} from '@vegaprotocol/markets'; +import { closedMarketsWithDataProvider } from '@vegaprotocol/markets'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; -import type { ColDef } from 'ag-grid-community'; -import { FLAGS } from '@vegaprotocol/environment'; import { SettlementDateCell } from './settlement-date-cell'; import { SettlementPriceCell } from './settlement-price-cell'; import { useDataProvider } from '@vegaprotocol/data-provider'; -import { SuccessorMarketRenderer } from './successor-market-cell'; +import { MarketActionsDropdown } from './market-table-actions'; +import { MarketCodeCell } from './market-code-cell'; type SettlementAsset = MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset']; @@ -51,7 +44,9 @@ interface Row { setlementDataSourceFilter: DataSourceFilterFragment | undefined; tradingTerminationOracleId: string; settlementAsset: SettlementAsset; - productType: string; + productType: ProductType | undefined; + successorMarketID: string | null | undefined; + parentMarketID: string | null | undefined; } export const Closed = () => { @@ -95,16 +90,19 @@ export const Closed = () => { tradingTerminationOracleId: instrument.product.dataSourceSpecForTradingTermination.id, settlementAsset: instrument.product.settlementAsset, - productType: instrument.product.__typename || '', + productType: instrument.product.__typename, + successorMarketID: market.successorMarketID, + parentMarketID: market.parentMarketID, }; return row; }); - return ( -
- -
- ); + + return ; +}; + +const components = { + MarketCodeCell, }; const ClosedMarketsDataGrid = ({ @@ -117,15 +115,11 @@ const ClosedMarketsDataGrid = ({ const openAssetDialog = useAssetDetailsDialogStore((store) => store.open); const colDefs = useMemo(() => { - const cols: ColDef[] = compact([ + return [ { headerName: t('Market'), field: 'code', - cellRenderer: 'MarketNameCell', - }, - { - headerName: t('Description'), - field: 'name', + cellRenderer: 'MarketCodeCell', }, { headerName: t('Status'), @@ -176,12 +170,6 @@ const ClosedMarketsDataGrid = ({ }, }, }, - FLAGS.SUCCESSOR_MARKETS && { - headerName: t('Successor market'), - field: 'id', - colId: 'successorMarket', - cellRenderer: 'SuccessorMarketRenderer', - }, { headerName: t('Best bid'), field: 'bestBidPrice', @@ -263,12 +251,13 @@ const ClosedMarketsDataGrid = ({ ); }, }, - ]); - return cols; + ]; }, [openAssetDialog]); return ( @@ -276,8 +265,9 @@ const ClosedMarketsDataGrid = ({ rowData={rowData} columnDefs={colDefs} getRowId={({ data }) => data.id} - components={{ SuccessorMarketRenderer, MarketNameCell }} overlayNoRowsTemplate={error ? error.message : t('No markets')} + components={components} + rowHeight={45} /> ); }; diff --git a/apps/trading/client-pages/markets/market-code-cell.spec.tsx b/apps/trading/client-pages/markets/market-code-cell.spec.tsx new file mode 100644 index 000000000..5570aae86 --- /dev/null +++ b/apps/trading/client-pages/markets/market-code-cell.spec.tsx @@ -0,0 +1,82 @@ +import { render, screen } from '@testing-library/react'; +import { ProductTypeShortName } from '@vegaprotocol/types'; +import type { MarketCodeCellProps } from './market-code-cell'; +import { MarketCodeCell } from './market-code-cell'; + +describe('MarketCodeCell', () => { + const renderComponent = (props: MarketCodeCellProps) => { + return render(); + }; + + it('renders SCCR if the market is a successor', () => { + const productType = 'Future'; + const code = 'code'; + const props = { + value: 'code', + data: { + productType, + parentMarketID: 'foo', + successorMarketID: undefined, + }, + } as const; + renderComponent(props); + expect(screen.getByTestId('stack-cell-primary')).toHaveTextContent(code); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + ProductTypeShortName[productType] + ); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + 'SCCR' + ); + expect(screen.getByTestId('stack-cell-secondary')).not.toHaveTextContent( + 'PRNT' + ); + }); + + it('renders PRNT if the market is a parent', () => { + const productType = 'Future'; + const code = 'code'; + const props = { + value: 'code', + data: { + productType, + parentMarketID: undefined, + successorMarketID: 'foo', + }, + } as const; + renderComponent(props); + expect(screen.getByTestId('stack-cell-primary')).toHaveTextContent(code); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + ProductTypeShortName[productType] + ); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + 'PRNT' + ); + expect(screen.getByTestId('stack-cell-secondary')).not.toHaveTextContent( + 'SCCR' + ); + }); + + it('renders both SCCR and PRNT if the market is both a parent and a successor', () => { + const productType = 'Future'; + const code = 'code'; + const props = { + value: 'code', + data: { + productType, + parentMarketID: 'foo', + successorMarketID: 'bar', + }, + } as const; + renderComponent(props); + expect(screen.getByTestId('stack-cell-primary')).toHaveTextContent(code); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + ProductTypeShortName[productType] + ); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + 'PRNT' + ); + expect(screen.getByTestId('stack-cell-secondary')).toHaveTextContent( + 'SCCR' + ); + }); +}); diff --git a/apps/trading/client-pages/markets/market-code-cell.tsx b/apps/trading/client-pages/markets/market-code-cell.tsx new file mode 100644 index 000000000..68c68e988 --- /dev/null +++ b/apps/trading/client-pages/markets/market-code-cell.tsx @@ -0,0 +1,51 @@ +import compact from 'lodash/compact'; +import type { ProductType } from '@vegaprotocol/types'; +import { ProductTypeMapping, ProductTypeShortName } from '@vegaprotocol/types'; +import { StackedCell } from '@vegaprotocol/datagrid'; +import { t } from '@vegaprotocol/i18n'; + +export interface MarketCodeCellProps { + value: string | undefined; // market code + data: { + productType: ProductType | undefined; + parentMarketID: string | null | undefined; + successorMarketID: string | null | undefined; + }; +} + +export const MarketCodeCell = ({ value, data }: MarketCodeCellProps) => { + if (!value || !data || !data.productType) return null; + + const infoSpanClasses = + 'mr-1 pr-1 uppercase border-r last:pr-0 last:mr-0 last:border-r-0 border-vega-clight-200 dark:border-vega-cdark-200'; + + const info = compact([ + + {ProductTypeShortName[data.productType]} + , + data.parentMarketID && ( + + {t('SCCR')} + + ), + data.successorMarketID && ( + + {t('PRNT')} + + ), + ]); + + return ; +}; diff --git a/apps/trading/client-pages/markets/market-list-table.tsx b/apps/trading/client-pages/markets/market-list-table.tsx new file mode 100644 index 000000000..8ead66f63 --- /dev/null +++ b/apps/trading/client-pages/markets/market-list-table.tsx @@ -0,0 +1,35 @@ +import type { TypedDataAgGrid } from '@vegaprotocol/datagrid'; +import { AgGridLazy as AgGrid, PriceFlashCell } from '@vegaprotocol/datagrid'; +import type { MarketMaybeWithData } from '@vegaprotocol/markets'; +import { useColumnDefs } from './use-column-defs'; + +export const getRowId = ({ data }: { data: { id: string } }) => data.id; + +const defaultColDef = { + sortable: true, + filter: true, + filterParams: { buttons: ['reset'] }, +}; + +const components = { + PriceFlashCell, +}; + +type Props = TypedDataAgGrid; + +export const MarketListTable = (props: Props) => { + const columnDefs = useColumnDefs(); + + return ( + + ); +}; + +export default MarketListTable; diff --git a/libs/markets/src/lib/components/markets-container/market-table-actions.tsx b/apps/trading/client-pages/markets/market-table-actions.tsx similarity index 60% rename from libs/markets/src/lib/components/markets-container/market-table-actions.tsx rename to apps/trading/client-pages/markets/market-table-actions.tsx index 21c8df2da..01db1d70f 100644 --- a/libs/markets/src/lib/components/markets-container/market-table-actions.tsx +++ b/apps/trading/client-pages/markets/market-table-actions.tsx @@ -9,14 +9,21 @@ import { } from '@vegaprotocol/ui-toolkit'; import { DApp, EXPLORER_MARKET, useLinks } from '@vegaprotocol/environment'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; +import { useNavigate } from 'react-router-dom'; +import { Links, Routes } from '../../pages/client-router'; export const MarketActionsDropdown = ({ marketId, assetId, + successorMarketID, + parentMarketID, }: { marketId: string; assetId: string; + successorMarketID: string | null | undefined; + parentMarketID: string | null | undefined; }) => { + const navigate = useNavigate(); const open = useAssetDetailsDialogStore((store) => store.open); const linkCreator = useLinks(DApp.Explorer); @@ -42,6 +49,26 @@ export const MarketActionsDropdown = ({ {t('View settlement asset details')} + {parentMarketID && ( + { + navigate(Links[Routes.MARKET](parentMarketID)); + }} + > + + {t('View parent market')} + + )} + {successorMarketID && ( + { + navigate(Links[Routes.MARKET](successorMarketID)); + }} + > + + {t('View successor market')} + + )} ); }; diff --git a/apps/trading/client-pages/markets/markets-page.tsx b/apps/trading/client-pages/markets/markets-page.tsx index e318222c5..666e63d81 100644 --- a/apps/trading/client-pages/markets/markets-page.tsx +++ b/apps/trading/client-pages/markets/markets-page.tsx @@ -6,7 +6,7 @@ import { Tab, TradingAnchorButton, } from '@vegaprotocol/ui-toolkit'; -import { Markets } from './markets'; +import { OpenMarkets } from './open-markets'; import { Proposed } from './proposed'; import { usePageTitleStore } from '../../stores'; import { Closed } from './closed'; @@ -21,8 +21,8 @@ export const MarketsPage = () => { updateTitle: store.updateTitle, })); - const tokenLink = useLinks(DApp.Token); - const externalLink = tokenLink(TOKEN_NEW_MARKET_PROPOSAL); + const governanceLink = useLinks(DApp.Governance); + const externalLink = governanceLink(TOKEN_NEW_MARKET_PROPOSAL); useEffect(() => { updateTitle(titlefy(['Markets'])); @@ -33,7 +33,7 @@ export const MarketsPage = () => {
- + { - const handleOnSelect = useMarketClickHandler(); - return ( - - ); -}; diff --git a/apps/trading/client-pages/markets/open-markets.tsx b/apps/trading/client-pages/markets/open-markets.tsx new file mode 100644 index 000000000..ee8a20473 --- /dev/null +++ b/apps/trading/client-pages/markets/open-markets.tsx @@ -0,0 +1,63 @@ +import { useDataProvider } from '@vegaprotocol/data-provider'; +import type { MarketMaybeWithData } from '@vegaprotocol/markets'; +import { marketListProvider } from '@vegaprotocol/markets'; +import { useEffect } from 'react'; +import { t } from '@vegaprotocol/i18n'; +import type { CellClickedEvent } from 'ag-grid-community'; +import MarketListTable from './market-list-table'; +import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; +import { Interval } from '@vegaprotocol/types'; +import { useYesterday } from '@vegaprotocol/react-helpers'; + +const POLLING_TIME = 2000; + +export const OpenMarkets = () => { + const handleOnSelect = useMarketClickHandler(); + const yesterday = useYesterday(); + const { data, error, reload } = useDataProvider({ + dataProvider: marketListProvider, + variables: { + since: new Date(yesterday).toISOString(), + interval: Interval.INTERVAL_I1H, + }, + }); + + useEffect(() => { + const interval = setInterval(() => { + reload(); + }, POLLING_TIME); + return () => { + clearInterval(interval); + }; + }, [reload]); + + return ( + ) => { + if (!data) return; + + // prevent navigating to the market page if any of the below cells are clicked + // event.preventDefault or event.stopPropagation dont seem to apply for aggird + const colId = column.getColId(); + + if ( + [ + 'tradableInstrument.instrument.product.settlementAsset.symbol', + 'market-actions', + ].includes(colId) + ) { + return; + } + + // @ts-ignore metaKey exists + handleOnSelect(data.id, event ? event.metaKey : false); + }} + overlayNoRowsTemplate={error ? error.message : t('No markets')} + /> + ); +}; diff --git a/apps/trading/client-pages/markets/oracle-status.tsx b/apps/trading/client-pages/markets/oracle-status.tsx new file mode 100644 index 000000000..4e4f2f984 --- /dev/null +++ b/apps/trading/client-pages/markets/oracle-status.tsx @@ -0,0 +1,46 @@ +import { useEnvironment } from '@vegaprotocol/environment'; +import { Icon } from '@vegaprotocol/ui-toolkit'; +import type { IconName } from '@blueprintjs/icons'; +import type { Market } from '@vegaprotocol/markets'; +import { + getMatchingOracleProvider, + getVerifiedStatusIcon, + useOracleProofs, +} from '@vegaprotocol/markets'; + +export const OracleStatus = ({ + dataSourceSpecForSettlementData, + dataSourceSpecForTradingTermination, +}: Pick< + Market['tradableInstrument']['instrument']['product'], + 'dataSourceSpecForSettlementData' | 'dataSourceSpecForTradingTermination' +>) => { + const { ORACLE_PROOFS_URL } = useEnvironment(); + const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL); + + if (providers) { + const settlementDataProvider = getMatchingOracleProvider( + dataSourceSpecForSettlementData.data, + providers + ); + const tradingTerminationDataProvider = getMatchingOracleProvider( + dataSourceSpecForTradingTermination.data, + providers + ); + let maliciousOracleProvider = null; + + if (settlementDataProvider?.oracle.status !== 'GOOD') { + maliciousOracleProvider = settlementDataProvider; + } else if (tradingTerminationDataProvider?.oracle.status !== 'GOOD') { + maliciousOracleProvider = tradingTerminationDataProvider; + } + + if (!maliciousOracleProvider) return null; + + const { icon } = getVerifiedStatusIcon(maliciousOracleProvider); + + return ; + } + + return null; +}; diff --git a/apps/trading/client-pages/markets/parent-market-cell.tsx b/apps/trading/client-pages/markets/parent-market-cell.tsx new file mode 100644 index 000000000..b5a4bb2fe --- /dev/null +++ b/apps/trading/client-pages/markets/parent-market-cell.tsx @@ -0,0 +1,15 @@ +import { useMarketsMapProvider } from '@vegaprotocol/markets'; + +export const ParentMarketCell = ({ + value, +}: { + value: string; // parentMarketId +}) => { + const { data, loading } = useMarketsMapProvider(); + + if (loading) return null; + + if (!data || !data[value]) return -; + + return
{data[value].tradableInstrument.instrument.code}
; +}; diff --git a/apps/trading/client-pages/markets/proposed.tsx b/apps/trading/client-pages/markets/proposed.tsx index 67a20cbc9..0cf9175c1 100644 --- a/apps/trading/client-pages/markets/proposed.tsx +++ b/apps/trading/client-pages/markets/proposed.tsx @@ -1,6 +1,10 @@ import { ProposalsList } from '@vegaprotocol/proposals'; -import { SuccessorMarketRenderer } from './successor-market-cell'; +import { ParentMarketCell } from './parent-market-cell'; + +const cellRenderers = { + ParentMarketCell, +}; export const Proposed = () => { - return ; + return ; }; diff --git a/apps/trading/client-pages/markets/successor-market-cell.spec.tsx b/apps/trading/client-pages/markets/successor-market-cell.spec.tsx deleted file mode 100644 index f91997194..000000000 --- a/apps/trading/client-pages/markets/successor-market-cell.spec.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { render, screen, act, waitFor } from '@testing-library/react'; -import { MockedProvider } from '@apollo/client/testing'; -import userEvent from '@testing-library/user-event'; -import type { Market } from '@vegaprotocol/markets'; -import { SuccessorMarketRenderer } from './successor-market-cell'; -import { - MarketsDocument, - SuccessorMarketIdsDocument, -} from '@vegaprotocol/markets'; -import { createMarketFragment } from '@vegaprotocol/mock'; - -const mockSuccessorsQuery = [ - { - id: 'market1', - parentMarketID: 'parentMarket1', - successorMarketID: 'successorMarket1', - }, - { id: 'market2', parentMarketID: 'parentMarket2' }, - { id: 'market3', successorMarketID: 'successorMarket3' }, -]; -const parentMarket1 = { - id: 'parentMarket1', - tradableInstrument: { - instrument: { code: 'code parent 1', id: '1' }, - }, -} as unknown as Market; -const successorMarket1 = { - id: 'successorMarket1', - tradableInstrument: { - instrument: { code: 'code successor 1', id: '2' }, - }, -} as unknown as Market; -const parentMarket2 = { - id: 'parentMarket2', - tradableInstrument: { - instrument: { code: 'code parent 2', id: '3' }, - }, -} as unknown as Market; -const successorMarket3 = { - id: 'successorMarket3', - tradableInstrument: { - instrument: { code: 'code successor 3', id: '4' }, - }, -} as unknown as Market; - -const mockMarkets = [ - parentMarket1, - successorMarket1, - parentMarket2, - successorMarket3, -]; - -const mockClickHandler = jest.fn(); -jest.mock('../../lib/hooks/use-market-click-handler', () => ({ - useMarketClickHandler: jest.fn().mockImplementation(() => mockClickHandler), -})); - -const marketMock = { - request: { - query: MarketsDocument, - variables: undefined, - }, - result: { - data: { - marketsConnection: { - edges: mockMarkets.map((item) => ({ - node: { - ...createMarketFragment(item), - }, - })), - }, - }, - }, -}; - -const successorMock = { - request: { - query: SuccessorMarketIdsDocument, - }, - result: { - data: { - marketsConnection: { - edges: mockSuccessorsQuery.map((item) => ({ - node: { - ...item, - }, - })), - }, - }, - }, -}; - -const mocks = [marketMock, successorMock]; - -describe('SuccessorMarketRenderer', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should properly rendered successor market', async () => { - const successorValue = 'market1'; - render( - - - - ); - await waitFor(() => { - expect(screen.getByTestId('market-code')).toBeInTheDocument(); - }); - expect(screen.getByText('code successor 1')).toBeInTheDocument(); - expect(screen.getByText('Futr')).toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button')); - await waitFor(() => { - expect(mockClickHandler).toHaveBeenCalledWith('successorMarket1', false); - }); - }); - it('should properly rendered parent market', async () => { - const successorValue = 'market1'; - render( - - - - ); - await waitFor(() => { - expect(screen.getByTestId('market-code')).toBeInTheDocument(); - }); - expect(screen.getByText('code parent 1')).toBeInTheDocument(); - expect(screen.getByText('Futr')).toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button')); - - await waitFor(() => { - expect(mockClickHandler).toHaveBeenCalledWith('parentMarket1', false); - }); - }); - it('should properly rendered only parent market', async () => { - const successorValue = 'market2'; - const { rerender } = render( - - - - ); - await waitFor(() => { - expect(screen.getByTestId('market-code')).toBeInTheDocument(); - }); - expect(screen.getByText('code parent 2')).toBeInTheDocument(); - expect(screen.getByText('Futr')).toBeInTheDocument(); - await userEvent.click(screen.getByRole('button')); - - await waitFor(() => { - expect(mockClickHandler).toHaveBeenCalledWith('parentMarket2', false); - }); - - rerender( - - - - ); - - expect(screen.getByText('-')).toBeInTheDocument(); - }); - it('should properly rendered only successor market', async () => { - const successorValue = 'market3'; - const { rerender } = render( - - - - ); - await waitFor(() => { - expect(screen.getByTestId('market-code')).toBeInTheDocument(); - }); - expect(screen.getByText('code successor 3')).toBeInTheDocument(); - expect(screen.getByText('Futr')).toBeInTheDocument(); - - await userEvent.click(screen.getByRole('button')); - - await waitFor(() => { - expect(mockClickHandler).toHaveBeenCalledWith('successorMarket3', false); - }); - - await act(() => { - rerender( - - - - ); - }); - expect(screen.getByText('-')).toBeInTheDocument(); - }); -}); diff --git a/apps/trading/client-pages/markets/successor-market-cell.tsx b/apps/trading/client-pages/markets/successor-market-cell.tsx deleted file mode 100644 index da8bb4677..000000000 --- a/apps/trading/client-pages/markets/successor-market-cell.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { MarketNameCell } from '@vegaprotocol/datagrid'; -import { useDataProvider } from '@vegaprotocol/data-provider'; -import { marketProvider, useSuccessorMarketIds } from '@vegaprotocol/markets'; -import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; - -export const SuccessorMarketRenderer = ({ - value, - parent, -}: { - value: string; - parent?: boolean; -}) => { - const successors = useSuccessorMarketIds(value); - const onMarketClick = useMarketClickHandler(); - - const lookupValue = successors - ? parent - ? successors.parentMarketID - : successors.successorMarketID - : ''; - - const { data } = useDataProvider({ - dataProvider: marketProvider, - variables: { - marketId: lookupValue || '', - }, - skip: !lookupValue, - }); - - return data ? ( - - ) : ( - '-' - ); -}; diff --git a/apps/trading/client-pages/markets/use-column-defs.tsx b/apps/trading/client-pages/markets/use-column-defs.tsx new file mode 100644 index 000000000..6f9156c9f --- /dev/null +++ b/apps/trading/client-pages/markets/use-column-defs.tsx @@ -0,0 +1,226 @@ +import { useMemo } from 'react'; +import type { ColDef, ValueFormatterParams } from 'ag-grid-community'; +import { t } from '@vegaprotocol/i18n'; +import type { + VegaICellRendererParams, + VegaValueFormatterParams, + VegaValueGetterParams, +} from '@vegaprotocol/datagrid'; +import { COL_DEFS, SetFilter } from '@vegaprotocol/datagrid'; +import * as Schema from '@vegaprotocol/types'; +import { addDecimalsFormatNumber, toBigNum } from '@vegaprotocol/utils'; +import { ButtonLink, Tooltip } from '@vegaprotocol/ui-toolkit'; +import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; +import type { + MarketMaybeWithData, + MarketMaybeWithDataAndCandles, +} from '@vegaprotocol/markets'; +import { MarketActionsDropdown } from './market-table-actions'; +import { calcCandleVolume } from '@vegaprotocol/markets'; +import { MarketCodeCell } from './market-code-cell'; + +const { MarketTradingMode, AuctionTrigger } = Schema; + +export const useColumnDefs = () => { + const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); + return useMemo( + () => [ + { + headerName: t('Market'), + field: 'tradableInstrument.instrument.code', + flex: 2, + cellRenderer: ({ + value, + data, + }: VegaICellRendererParams< + MarketMaybeWithData, + 'tradableInstrument.instrument.code' + >) => ( + + ), + }, + { + headerName: t('Description'), + field: 'tradableInstrument.instrument.name', + flex: 2, + }, + { + headerName: t('Trading mode'), + field: 'tradingMode', + cellRenderer: ({ + data, + }: VegaICellRendererParams) => { + if (!data?.data) return '-'; + const { trigger, marketTradingMode } = data.data; + + const withTriggerInfo = + marketTradingMode === + MarketTradingMode.TRADING_MODE_MONITORING_AUCTION && + trigger && + trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED; + + if (withTriggerInfo) { + return ( + + + {Schema.MarketTradingModeMapping[marketTradingMode]} + + + ); + } + + return Schema.MarketTradingModeMapping[marketTradingMode]; + }, + filter: SetFilter, + filterParams: { + set: Schema.MarketTradingModeMapping, + }, + }, + { + headerName: t('Status'), + field: 'state', + valueFormatter: ({ + data, + }: VegaValueFormatterParams) => { + return data?.state ? Schema.MarketStateMapping[data.state] : '-'; + }, + filter: SetFilter, + filterParams: { + set: Schema.MarketStateMapping, + }, + }, + { + headerName: t('Mark price'), + field: 'data.markPrice', + type: 'rightAligned', + cellRenderer: 'PriceFlashCell', + filter: 'agNumberColumnFilter', + valueGetter: ({ data }: VegaValueGetterParams) => { + return data?.data?.markPrice === undefined + ? undefined + : toBigNum(data?.data?.markPrice, data.decimalPlaces).toNumber(); + }, + valueFormatter: ({ + data, + }: VegaValueFormatterParams) => + data?.data?.bestOfferPrice === undefined + ? '-' + : addDecimalsFormatNumber(data.data.markPrice, data.decimalPlaces), + }, + { + headerName: t('24h volume'), + type: 'rightAligned', + field: 'data.candles', + valueGetter: ({ + data, + }: VegaValueGetterParams) => { + if (!data) return 0; + const candles = data?.candles; + const vol = candles ? calcCandleVolume(candles) : '0'; + return Number(vol); + }, + valueFormatter: ({ + data, + }: ValueFormatterParams) => { + const candles = data?.candles; + const vol = candles ? calcCandleVolume(candles) : '0'; + const volume = + data && vol && vol !== '0' + ? addDecimalsFormatNumber(vol, data.positionDecimalPlaces) + : '0.00'; + return volume; + }, + }, + { + headerName: t('Settlement asset'), + field: 'tradableInstrument.instrument.product.settlementAsset.symbol', + cellRenderer: ({ + data, + }: VegaICellRendererParams< + MarketMaybeWithData, + 'tradableInstrument.instrument.product.settlementAsset.symbol' + >) => { + const value = + data?.tradableInstrument.instrument.product.settlementAsset; + return value ? ( + { + openAssetDetailsDialog(value.id, e.target as HTMLElement); + }} + > + {value.symbol} + + ) : ( + '' + ); + }, + }, + { + headerName: t('Spread'), + field: 'data.bestBidPrice', + type: 'rightAligned', + filter: 'agNumberColumnFilter', + cellRenderer: 'PriceFlashCell', + valueGetter: ({ data }: VegaValueGetterParams) => { + if (!data || !data.data?.bestOfferPrice || !data.data?.bestBidPrice) { + return undefined; + } + + const offer = toBigNum(data.data.bestOfferPrice, data.decimalPlaces); + const bid = toBigNum(data.data.bestBidPrice, data.decimalPlaces); + + const spread = offer.minus(bid).toNumber(); + + // The calculation above can result in '-0' being rendered after formatting + // so return Math.abs to remove it and just render '0' + if (spread === 0) { + return Math.abs(spread); + } + + return spread; + }, + valueFormatter: ({ + value, + }: VegaValueFormatterParams< + MarketMaybeWithData, + 'data.bestBidPrice' + >) => { + if (!value) return '-'; + return value.toString(); + }, + }, + { + colId: 'market-actions', + field: 'id', + ...COL_DEFS.actions, + cellRenderer: ({ + data, + }: VegaICellRendererParams) => { + if (!data) return null; + return ( + + ); + }, + }, + ], + [openAssetDetailsDialog] + ); +}; diff --git a/apps/trading/client-pages/portfolio/portfolio.tsx b/apps/trading/client-pages/portfolio/portfolio.tsx index 5fb0c0b38..e33348f11 100644 --- a/apps/trading/client-pages/portfolio/portfolio.tsx +++ b/apps/trading/client-pages/portfolio/portfolio.tsx @@ -23,6 +23,7 @@ import { ViewType, useSidebar } from '../../components/sidebar'; import { AccountsMenu } from '../../components/accounts-menu'; import { DepositsMenu } from '../../components/deposits-menu'; import { WithdrawalsMenu } from '../../components/withdrawals-menu'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; const WithdrawalsIndicator = () => { const { ready } = useIncompleteWithdrawals(); @@ -37,7 +38,10 @@ const WithdrawalsIndicator = () => { }; export const Portfolio = () => { - const { init, view, setView } = useSidebar(); + const currentRouteId = useGetCurrentRouteId(); + const { getView, setViews } = useSidebar(); + const view = getView(currentRouteId); + const { updateTitle } = usePageTitleStore((store) => ({ updateTitle: store.updateTitle, })); @@ -48,10 +52,10 @@ export const Portfolio = () => { // Make transfer sidebar open by default useEffect(() => { - if (init && view === null) { - setView({ type: ViewType.Transfer }); + if (view === undefined) { + setViews({ type: ViewType.Transfer }, currentRouteId); } - }, [init, view, setView]); + }, [view, setViews, currentRouteId]); const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' }); const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col'; diff --git a/apps/trading/components/accounts-container/accounts-container.tsx b/apps/trading/components/accounts-container/accounts-container.tsx index 73f2d04a4..9574fa9f1 100644 --- a/apps/trading/components/accounts-container/accounts-container.tsx +++ b/apps/trading/components/accounts-container/accounts-container.tsx @@ -12,6 +12,7 @@ import type { DataGridSlice } from '../../stores/datagrid-store-slice'; import { createDataGridSlice } from '../../stores/datagrid-store-slice'; import { ViewType, useSidebar } from '../sidebar'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; export const AccountsContainer = ({ pinnedAsset, @@ -21,7 +22,8 @@ export const AccountsContainer = ({ const onMarketClick = useMarketClickHandler(true); const { pubKey, isReadOnly } = useVegaWallet(); const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); - const setView = useSidebar((store) => store.setView); + const currentRouteId = useGetCurrentRouteId(); + const setViews = useSidebar((store) => store.setViews); const gridStore = useAccountStore((store) => store.gridStore); const updateGridStore = useAccountStore((store) => store.updateGridStore); @@ -49,13 +51,13 @@ export const AccountsContainer = ({ partyId={pubKey} onClickAsset={onClickAsset} onClickWithdraw={(assetId) => { - setView({ type: ViewType.Withdraw, assetId }); + setViews({ type: ViewType.Withdraw, assetId }, currentRouteId); }} onClickDeposit={(assetId) => { - setView({ type: ViewType.Deposit, assetId }); + setViews({ type: ViewType.Deposit, assetId }, currentRouteId); }} onClickTransfer={(assetId) => { - setView({ type: ViewType.Transfer, assetId }); + setViews({ type: ViewType.Transfer, assetId }, currentRouteId); }} onMarketClick={onMarketClick} isReadOnly={isReadOnly} diff --git a/apps/trading/components/accounts-menu/accounts-menu.tsx b/apps/trading/components/accounts-menu/accounts-menu.tsx index 86c80e91c..8bfed5d76 100644 --- a/apps/trading/components/accounts-menu/accounts-menu.tsx +++ b/apps/trading/components/accounts-menu/accounts-menu.tsx @@ -1,22 +1,24 @@ import { t } from '@vegaprotocol/i18n'; import { TradingButton } from '@vegaprotocol/ui-toolkit'; import { ViewType, useSidebar } from '../sidebar'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; export const AccountsMenu = () => { - const setView = useSidebar((store) => store.setView); + const currentRouteId = useGetCurrentRouteId(); + const setViews = useSidebar((store) => store.setViews); return ( <> setView({ type: ViewType.Transfer })} + onClick={() => setViews({ type: ViewType.Transfer }, currentRouteId)} > {t('Transfer')} setView({ type: ViewType.Deposit })} + onClick={() => setViews({ type: ViewType.Deposit }, currentRouteId)} > {t('Deposit')} diff --git a/apps/trading/components/constants.ts b/apps/trading/components/constants.ts index 4e9781d22..b157c3b5f 100644 --- a/apps/trading/components/constants.ts +++ b/apps/trading/components/constants.ts @@ -1,2 +1 @@ export const THROTTLE_UPDATE_TIME = 500; -export const ONBOARDING_VIEWED_KEY = 'vega_onboarding_viewed'; diff --git a/apps/trading/components/deposits-menu/deposits-menu.tsx b/apps/trading/components/deposits-menu/deposits-menu.tsx index 81b6edf60..d67225692 100644 --- a/apps/trading/components/deposits-menu/deposits-menu.tsx +++ b/apps/trading/components/deposits-menu/deposits-menu.tsx @@ -1,14 +1,16 @@ import { t } from '@vegaprotocol/i18n'; import { TradingButton } from '@vegaprotocol/ui-toolkit'; import { ViewType, useSidebar } from '../sidebar'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; export const DepositsMenu = () => { - const setView = useSidebar((store) => store.setView); + const currentRouteId = useGetCurrentRouteId(); + const setViews = useSidebar((store) => store.setViews); return ( setView({ type: ViewType.Deposit })} + onClick={() => setViews({ type: ViewType.Deposit }, currentRouteId)} data-testid="deposit-button" > {t('Deposit')} diff --git a/apps/trading/components/layouts/layout-with-sidebar.tsx b/apps/trading/components/layouts/layout-with-sidebar.tsx index e9558833f..0e777b993 100644 --- a/apps/trading/components/layouts/layout-with-sidebar.tsx +++ b/apps/trading/components/layouts/layout-with-sidebar.tsx @@ -4,16 +4,19 @@ import classNames from 'classnames'; import { Routes as AppRoutes } from '../../pages/client-router'; import { MarketHeader } from '../market-header'; import { LiquidityHeader } from '../liquidity-header'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; export const LayoutWithSidebar = () => { - const sidebarView = useSidebar((store) => store.view); + const currentRouteId = useGetCurrentRouteId(); + const views = useSidebar((store) => store.views); + const sidebarView = views[currentRouteId] || null; const sidebarOpen = sidebarView !== null; - const gridClasses = classNames( 'h-full relative z-0 grid', 'grid-rows-[min-content_1fr_40px]', 'lg:grid-rows-[min-content_1fr]', - 'lg:grid-cols-[1fr_350px_40px]' + 'lg:grid-cols-[1fr_280px_40px]', + 'xxxl:grid-cols-[1fr_320px_40px]' ); return ( diff --git a/apps/trading/components/market-banner/market-successor-proposal-banner.tsx b/apps/trading/components/market-banner/market-successor-proposal-banner.tsx index 8797909bf..bccac258d 100644 --- a/apps/trading/components/market-banner/market-successor-proposal-banner.tsx +++ b/apps/trading/components/market-banner/market-successor-proposal-banner.tsx @@ -29,7 +29,7 @@ export const MarketSuccessorProposalBanner = ({ ?.successorConfiguration?.parentMarketId === marketId ) ?? []; const [visible, setVisible] = useState(true); - const tokenLink = useLinks(DApp.Token); + const tokenLink = useLinks(DApp.Governance); if (visible && successors.length) { return ( ({ diff --git a/apps/trading/components/market-selector/use-market-selector-list.ts b/apps/trading/components/market-selector/use-market-selector-list.ts index 47f68e4c8..f22a3ca09 100644 --- a/apps/trading/components/market-selector/use-market-selector-list.ts +++ b/apps/trading/components/market-selector/use-market-selector-list.ts @@ -1,18 +1,11 @@ import { useMemo } from 'react'; import orderBy from 'lodash/orderBy'; -import { MarketState } from '@vegaprotocol/types'; import { calcTradedFactor, useMarketList } from '@vegaprotocol/markets'; import { priceChangePercentage } from '@vegaprotocol/utils'; import type { Filter } from '../../components/market-selector/market-selector'; import { Sort } from './sort-dropdown'; import { Product } from './product-selector'; - -// Used for sort order and filter -const MARKET_TEMPLATE = [ - MarketState.STATE_ACTIVE, - MarketState.STATE_SUSPENDED, - MarketState.STATE_PENDING, -]; +import { isMarketActive } from '../../lib/utils'; export const useMarketSelectorList = ({ product, @@ -87,7 +80,3 @@ export const useMarketSelectorList = ({ return { markets, data, loading, error, reload }; }; - -export const isMarketActive = (state: MarketState) => { - return MARKET_TEMPLATE.includes(state); -}; diff --git a/apps/trading/components/navbar/navbar.spec.tsx b/apps/trading/components/navbar/navbar.spec.tsx index cb8d40a87..941f8f140 100644 --- a/apps/trading/components/navbar/navbar.spec.tsx +++ b/apps/trading/components/navbar/navbar.spec.tsx @@ -57,6 +57,7 @@ describe('Navbar', () => { ['/markets/all', 'Markets'], [`/markets/${marketId}`, 'Trading'], ['/portfolio', 'Portfolio'], + [expect.stringContaining('governance'), 'Governance'], ]; const links = screen.getAllByRole('link'); @@ -88,6 +89,7 @@ describe('Navbar', () => { ['/markets/all', 'Markets'], [`/markets/${marketId}`, 'Trading'], ['/portfolio', 'Portfolio'], + [expect.stringContaining('governance'), 'Governance'], ]; const links = menu.getAllByRole('link'); links.forEach((link, i) => { diff --git a/apps/trading/components/navbar/navbar.tsx b/apps/trading/components/navbar/navbar.tsx index 4d27ed3f6..4cc0f1281 100644 --- a/apps/trading/components/navbar/navbar.tsx +++ b/apps/trading/components/navbar/navbar.tsx @@ -1,6 +1,12 @@ import type { ButtonHTMLAttributes, LiHTMLAttributes, ReactNode } from 'react'; import { useState } from 'react'; -import { useEnvironment, DocsLinks, Networks } from '@vegaprotocol/environment'; +import { + useEnvironment, + DocsLinks, + Networks, + DApp, + useLinks, +} from '@vegaprotocol/environment'; import { t } from '@vegaprotocol/i18n'; import { useGlobalStore } from '../../stores'; import { VegaWalletConnectButton } from '../vega-wallet-connect-button'; @@ -59,14 +65,14 @@ export const Navbar = ({ {/* Left section */} -
{children}
+
{children}
{/* Used to show header in nav on mobile */}
setMenu(null)} />
{/* Right section */} -
+
{ @@ -100,7 +106,7 @@ export const Navbar = ({ onOpenChange={(open) => setMenu((x) => (open ? x : null))} > -
+
setMenu(null)}> {t('Close menu')} @@ -179,6 +185,11 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => { {t('Portfolio')} + + + {t('Governance')} + + {t('Resources')} @@ -336,7 +347,7 @@ const NavbarLinkExternal = ({ {children} - + ); @@ -364,8 +375,8 @@ const BurgerIcon = () => ( const NavbarListDivider = () => { return ( -
-
+
+
); }; diff --git a/apps/trading/components/orderbook-container/orderbook-container.tsx b/apps/trading/components/orderbook-container/orderbook-container.tsx index 07b0eb7ca..f2fdc53c6 100644 --- a/apps/trading/components/orderbook-container/orderbook-container.tsx +++ b/apps/trading/components/orderbook-container/orderbook-container.tsx @@ -1,16 +1,18 @@ import { OrderbookManager } from '@vegaprotocol/market-depth'; import { ViewType, useSidebar } from '../sidebar'; import { useDealTicketFormValues } from '@vegaprotocol/deal-ticket'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; export const OrderbookContainer = ({ marketId }: { marketId: string }) => { + const currentRouteId = useGetCurrentRouteId(); const update = useDealTicketFormValues((state) => state.updateAll); - const setView = useSidebar((store) => store.setView); + const setViews = useSidebar((store) => store.setViews); return ( { update(marketId, values); - setView({ type: ViewType.Order }); + setViews({ type: ViewType.Order }, currentRouteId); }} /> ); diff --git a/apps/trading/components/sidebar/sidebar.spec.tsx b/apps/trading/components/sidebar/sidebar.spec.tsx index 5867d6525..5a78360c3 100644 --- a/apps/trading/components/sidebar/sidebar.spec.tsx +++ b/apps/trading/components/sidebar/sidebar.spec.tsx @@ -11,6 +11,7 @@ import { MemoryRouter, Route, Routes } from 'react-router-dom'; import { VegaIconNames } from '@vegaprotocol/ui-toolkit'; import type { VegaWalletContextShape } from '@vegaprotocol/wallet'; import { VegaWalletContext } from '@vegaprotocol/wallet'; +import { Routes as AppRoutes } from '../../pages/client-router'; jest.mock('../node-health', () => ({ NodeHealthContainer: () => , @@ -115,7 +116,11 @@ describe('SidebarContent', () => { - } /> + } + /> @@ -124,13 +129,17 @@ describe('SidebarContent', () => { expect(container).toBeEmptyDOMElement(); act(() => { - useSidebar.setState({ view: { type: ViewType.Transfer } }); + useSidebar.setState({ + views: { [AppRoutes.MARKET]: { type: ViewType.Transfer } }, + }); }); expect(screen.getByTestId('transfer')).toBeInTheDocument(); act(() => { - useSidebar.setState({ view: { type: ViewType.Deposit } }); + useSidebar.setState({ + views: { [AppRoutes.MARKET]: { type: ViewType.Deposit } }, + }); }); expect(screen.getByTestId('deposit')).toBeInTheDocument(); @@ -141,26 +150,36 @@ describe('SidebarContent', () => { - } /> + } + /> ); act(() => { - useSidebar.setState({ view: { type: ViewType.Order } }); + useSidebar.setState({ + views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Order } }, + }); }); expect(container).toBeEmptyDOMElement(); act(() => { - useSidebar.setState({ view: { type: ViewType.Settings } }); + useSidebar.setState({ + views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Settings } }, + }); }); expect(screen.getByTestId('settings')).toBeInTheDocument(); act(() => { - useSidebar.setState({ view: { type: ViewType.Info } }); + useSidebar.setState({ + views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Info } }, + }); }); expect(container).toBeEmptyDOMElement(); @@ -178,6 +197,7 @@ describe('SidebarButton', () => { tooltip="INFO" onClick={onClick} view={view} + routeId="current-route-id" /> ); diff --git a/apps/trading/components/sidebar/sidebar.tsx b/apps/trading/components/sidebar/sidebar.tsx index d32d0d8d0..fcfc9ff4b 100644 --- a/apps/trading/components/sidebar/sidebar.tsx +++ b/apps/trading/components/sidebar/sidebar.tsx @@ -16,6 +16,7 @@ import { WithdrawContainer } from '../withdraw-container'; import { Routes as AppRoutes } from '../../pages/client-router'; import { GetStarted } from '../welcome-dialog'; import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; export enum ViewType { Order = 'Order', @@ -51,6 +52,7 @@ type SidebarView = }; export const Sidebar = () => { + const currentRouteId = useGetCurrentRouteId(); const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1'; const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen); const { pubKeys } = useVegaWallet(); @@ -62,16 +64,19 @@ export const Sidebar = () => { view={ViewType.Deposit} icon={VegaIconNames.DEPOSIT} tooltip={t('Deposit')} + routeId={currentRouteId} /> {/* buttons for specific routes */} @@ -94,11 +99,13 @@ export const Sidebar = () => { view={ViewType.Order} icon={VegaIconNames.TICKET} tooltip={t('Order')} + routeId={currentRouteId} /> } @@ -114,12 +121,13 @@ export const Sidebar = () => { icon={VegaIconNames.EYE} tooltip={t('View as party')} disabled={Boolean(pubKeys)} + routeId={currentRouteId} /> - @@ -133,23 +141,25 @@ export const SidebarButton = ({ tooltip, disabled = false, onClick, + routeId, }: { view?: ViewType; icon: VegaIconNames; tooltip: string; disabled?: boolean; onClick?: () => void; + routeId: string; }) => { - const { currView, setView } = useSidebar((store) => ({ - currView: store.view, - setView: store.setView, + const { setViews, getView } = useSidebar((store) => ({ + setViews: store.setViews, + getView: store.getView, })); - + const currView = getView(routeId); const onSelect = (view: SidebarView['type']) => { if (view === currView?.type) { - setView(null); + setViews(null, routeId); } else { - setView({ type: view }); + setViews({ type: view }, routeId); } }; @@ -195,8 +205,10 @@ const SidebarDivider = () => { export const SidebarContent = () => { const params = useParams(); - const { view, setView } = useSidebar(); + const currentRouteId = useGetCurrentRouteId(); + const { setViews, getView } = useSidebar(); + const view = getView(currentRouteId); if (!view) return null; if (view.type === ViewType.Order) { @@ -206,7 +218,7 @@ export const SidebarContent = () => { - setView({ type: ViewType.Deposit, assetId }) + setViews({ type: ViewType.Deposit, assetId }, currentRouteId) } /> @@ -288,25 +300,21 @@ const ContentWrapper = ({ /** If rendered will close sidebar */ const CloseSidebar = () => { - const setView = useSidebar((store) => store.setView); + const currentRouteId = useGetCurrentRouteId(); + const setViews = useSidebar((store) => store.setViews); useEffect(() => { - setView(null); - }, [setView]); + setViews(null, currentRouteId); + }, [setViews, currentRouteId]); return null; }; export const useSidebar = create<{ - init: boolean; - view: SidebarView | null; - setView: (view: SidebarView | null) => void; -}>()((set) => ({ - init: true, - view: null, - setView: (x) => - set(() => { - if (x == null) { - return { view: null, init: false }; - } - return { view: x, init: false }; - }), + views: { [key: string]: SidebarView | null }; + setViews: (view: SidebarView | null, routeId: string) => void; + getView: (routeId: string) => SidebarView | null | undefined; +}>()((set, get) => ({ + views: {}, + setViews: (x, routeId) => + set(({ views }) => ({ views: { ...views, [routeId]: x } })), + getView: (routeId) => get().views[routeId], })); diff --git a/apps/trading/components/telemetry/index.ts b/apps/trading/components/telemetry/index.ts new file mode 100644 index 000000000..a790051f1 --- /dev/null +++ b/apps/trading/components/telemetry/index.ts @@ -0,0 +1 @@ +export { Telemetry } from './telemetry'; diff --git a/apps/trading/components/welcome-dialog/telemetry-approval.spec.tsx b/apps/trading/components/telemetry/telemetry-approval.spec.tsx similarity index 100% rename from apps/trading/components/welcome-dialog/telemetry-approval.spec.tsx rename to apps/trading/components/telemetry/telemetry-approval.spec.tsx diff --git a/apps/trading/components/welcome-dialog/telemetry-approval.tsx b/apps/trading/components/telemetry/telemetry-approval.tsx similarity index 100% rename from apps/trading/components/welcome-dialog/telemetry-approval.tsx rename to apps/trading/components/telemetry/telemetry-approval.tsx diff --git a/apps/trading/components/telemetry/telemetry.tsx b/apps/trading/components/telemetry/telemetry.tsx new file mode 100644 index 000000000..e40a5e2d3 --- /dev/null +++ b/apps/trading/components/telemetry/telemetry.tsx @@ -0,0 +1,69 @@ +import type { Toast } from '@vegaprotocol/ui-toolkit'; +import { Intent, useToasts } from '@vegaprotocol/ui-toolkit'; +import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval'; +import { useCallback, useEffect } from 'react'; +import { TelemetryApproval } from './telemetry-approval'; +import { t } from '@vegaprotocol/i18n'; +import { useOnboardingStore } from '../welcome-dialog/use-get-onboarding-step'; + +const TELEMETRY_APPROVAL_TOAST_ID = 'telemetry_tost_id'; + +export const Telemetry = () => { + const onboardingDissmissed = useOnboardingStore((store) => store.dismissed); + const [telemetryValue, setTelemetryValue, isTelemetryNeeded, closeTelemetry] = + useTelemetryApproval(); + + const [setToast, hasToast, removeToast] = useToasts((store) => [ + store.setToast, + store.hasToast, + store.remove, + ]); + + const onApprovalClose = useCallback(() => { + closeTelemetry(); + removeToast(TELEMETRY_APPROVAL_TOAST_ID); + }, [closeTelemetry, removeToast]); + + const setTelemetryApprovalAndClose = useCallback( + (value: string) => { + setTelemetryValue(value); + onApprovalClose(); + }, + [onApprovalClose, setTelemetryValue] + ); + + useEffect(() => { + if (isTelemetryNeeded && onboardingDissmissed) { + const toast: Toast = { + id: TELEMETRY_APPROVAL_TOAST_ID, + intent: Intent.Primary, + content: ( + <> +

+ {t('Improve vega console')} +

+ + + ), + onClose: onApprovalClose, + }; + if (!hasToast(TELEMETRY_APPROVAL_TOAST_ID)) { + setToast(toast); + } + return; + } + }, [ + telemetryValue, + isTelemetryNeeded, + onboardingDissmissed, + setToast, + hasToast, + onApprovalClose, + setTelemetryApprovalAndClose, + ]); + + return null; +}; diff --git a/apps/trading/components/vega-wallet-connect-button/vega-wallet-connect-button.spec.tsx b/apps/trading/components/vega-wallet-connect-button/vega-wallet-connect-button.spec.tsx index b572323a1..ed4eb9af5 100644 --- a/apps/trading/components/vega-wallet-connect-button/vega-wallet-connect-button.spec.tsx +++ b/apps/trading/components/vega-wallet-connect-button/vega-wallet-connect-button.spec.tsx @@ -11,6 +11,10 @@ jest.mock('@vegaprotocol/wallet', () => ({ useVegaWalletDialogStore: () => mockUpdateDialogOpen, })); +jest.mock('../../lib/hooks/use-get-current-route-id', () => ({ + useGetCurrentRouteId: jest.fn().mockReturnValue('current-route-id'), +})); + beforeEach(() => { jest.clearAllMocks(); }); diff --git a/apps/trading/components/vega-wallet-connect-button/vega-wallet-connect-button.tsx b/apps/trading/components/vega-wallet-connect-button/vega-wallet-connect-button.tsx index 0a1bb8f80..1d47dc692 100644 --- a/apps/trading/components/vega-wallet-connect-button/vega-wallet-connect-button.tsx +++ b/apps/trading/components/vega-wallet-connect-button/vega-wallet-connect-button.tsx @@ -22,13 +22,15 @@ import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet'; import { useCopyTimeout } from '@vegaprotocol/react-helpers'; import { ViewType, useSidebar } from '../sidebar'; import classNames from 'classnames'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; export const VegaWalletConnectButton = () => { const [dropdownOpen, setDropdownOpen] = useState(false); const openVegaWalletDialog = useVegaWalletDialogStore( (store) => store.openVegaWalletDialog ); - const setView = useSidebar((store) => store.setView); + const currentRouteId = useGetCurrentRouteId(); + const setViews = useSidebar((store) => store.setViews); const { pubKey, pubKeys, @@ -95,7 +97,7 @@ export const VegaWalletConnectButton = () => { { - setView({ type: ViewType.Transfer }); + setViews({ type: ViewType.Transfer }, currentRouteId); setDropdownOpen(false); }} > diff --git a/apps/trading/components/vega-wallet/vega-wallet-menu.tsx b/apps/trading/components/vega-wallet/vega-wallet-menu.tsx index 8e3f9d75c..089082e88 100644 --- a/apps/trading/components/vega-wallet/vega-wallet-menu.tsx +++ b/apps/trading/components/vega-wallet/vega-wallet-menu.tsx @@ -10,6 +10,7 @@ import { useVegaWallet, type PubKey } from '@vegaprotocol/wallet'; import { useCallback, useMemo } from 'react'; import CopyToClipboard from 'react-copy-to-clipboard'; import { ViewType, useSidebar } from '../sidebar'; +import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; export const VegaWalletMenu = ({ setMenu, @@ -17,7 +18,8 @@ export const VegaWalletMenu = ({ setMenu: (open: 'nav' | 'wallet' | null) => void; }) => { const { pubKey, pubKeys, selectPubKey, disconnect } = useVegaWallet(); - const setView = useSidebar((store) => store.setView); + const currentRouteId = useGetCurrentRouteId(); + const setViews = useSidebar((store) => store.setViews); const activeKey = useMemo(() => { return pubKeys?.find((pk) => pk.publicKey === pubKey); @@ -46,7 +48,7 @@ export const VegaWalletMenu = ({
diff --git a/libs/positions/src/lib/stacked-cell.tsx b/libs/datagrid/src/lib/cells/stacked-cell.tsx similarity index 100% rename from libs/positions/src/lib/stacked-cell.tsx rename to libs/datagrid/src/lib/cells/stacked-cell.tsx diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx index 16c20a96a..74289f8b8 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx @@ -11,8 +11,7 @@ import { AccountBreakdownDialog } from '@vegaprotocol/accounts'; import { formatRange, formatValue } from '@vegaprotocol/utils'; import { marketMarginDataProvider } from '@vegaprotocol/accounts'; import { useDataProvider } from '@vegaprotocol/data-provider'; - -import * as Accordion from '@radix-ui/react-accordion'; +import * as AccordionPrimitive from '@radix-ui/react-accordion'; import { MARGIN_DIFF_TOOLTIP_TEXT, @@ -24,7 +23,13 @@ import { } from '../../constants'; import { useEstimateFees } from '../../hooks'; import { KeyValue } from './key-value'; -import { TOOLTIP_TRIGGER_CLASS_NAME } from '@vegaprotocol/ui-toolkit'; +import { + Accordion, + AccordionChevron, + AccordionPanel, + Tooltip, +} from '@vegaprotocol/ui-toolkit'; +import classNames from 'classnames'; const emptyValue = '-'; @@ -246,31 +251,56 @@ export const DealTicketMarginDetails = ({ const quoteName = market.tradableInstrument.instrument.product.quoteName; return ( - <> - - - - {t('Margin required')} - - } - value={formatRange( - marginRequiredBestCase, - marginRequiredWorstCase, - assetDecimals - )} - formattedValue={formatRange( - marginRequiredBestCase, - marginRequiredWorstCase, - assetDecimals, - quantum - )} - labelDescription={MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol)} - symbol={assetSymbol} - /> - +
+ + +
+
+ + {t('Margin required')} + + + +
+ +
+ {formatRange( + marginRequiredBestCase, + marginRequiredWorstCase, + assetDecimals, + quantum + )}{' '} + {assetSymbol || ''} +
+
+
+ + } + > +
- - - +
+
+
{projectedMargin} )} - +
); }; diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.spec.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.spec.tsx index bdaaf081e..a4c7d1054 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.spec.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.spec.tsx @@ -84,6 +84,7 @@ const ocoPostfix = (id: string, postfix = true) => (postfix ? `${id}-oco` : id); const mockDataProvider = jest.fn((...args) => ({ data: Array(0), + reload: jest.fn(), })); jest.mock('@vegaprotocol/data-provider', () => ({ ...jest.requireActual('@vegaprotocol/data-provider'), @@ -473,6 +474,7 @@ describe('StopOrder', () => { it('shows limit of active stop orders number', async () => { mockDataProvider.mockReturnValue({ + reload: jest.fn(), data: Array(4), }); render(generateJsx()); @@ -484,6 +486,7 @@ describe('StopOrder', () => { it('counts oco as two orders', async () => { mockDataProvider.mockReturnValue({ + reload: jest.fn(), data: Array(3), }); render(generateJsx()); diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx index 8abb895bb..9daafbcb5 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx @@ -538,7 +538,7 @@ const NotionalAndFees = ({ market.positionDecimalPlaces ); return ( -
+
)} -
+
{ const displayValue = `${formattedValue ?? '-'} ${symbol || ''}`; const valueElement = onClick ? ( - ) : ( -
{displayValue}
+
{displayValue}
); return (
-
{label}
+
{label}
- + {valueElement}
diff --git a/libs/deal-ticket/src/constants.ts b/libs/deal-ticket/src/constants.ts index 1517df713..92c5c6bdf 100644 --- a/libs/deal-ticket/src/constants.ts +++ b/libs/deal-ticket/src/constants.ts @@ -8,7 +8,7 @@ export const EST_MARGIN_TOOLTIP_TEXT = (settlementAsset: string) => [settlementAsset] ); export const EST_TOTAL_MARGIN_TOOLTIP_TEXT = t( - 'Estimated total margin that will cover open position, active orders and this order.' + 'Estimated total margin that will cover open positions, active orders and this order.' ); export const MARGIN_ACCOUNT_TOOLTIP_TEXT = t('Margin account balance.'); export const MARGIN_DIFF_TOOLTIP_TEXT = (settlementAsset: string) => @@ -60,7 +60,7 @@ export const EST_FEES_TOOLTIP_TEXT = t( ); export const LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT = t( - 'This is a approximation to the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.' + 'This is an approximation (or a range) for the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.' ); export const EST_SLIPPAGE = t( diff --git a/libs/deal-ticket/src/hooks/index.ts b/libs/deal-ticket/src/hooks/index.ts index 12aea9146..8ddd5f510 100644 --- a/libs/deal-ticket/src/hooks/index.ts +++ b/libs/deal-ticket/src/hooks/index.ts @@ -1,3 +1,4 @@ export * from './__generated__/EstimateOrder'; export * from './use-estimate-fees'; export * from './use-form-values'; +export * from './use-position-estimate'; diff --git a/libs/deal-ticket/src/hooks/use-position-estimate.spec.ts b/libs/deal-ticket/src/hooks/use-position-estimate.spec.ts new file mode 100644 index 000000000..422dbccc4 --- /dev/null +++ b/libs/deal-ticket/src/hooks/use-position-estimate.spec.ts @@ -0,0 +1,33 @@ +import { renderHook } from '@testing-library/react'; +import { usePositionEstimate } from './use-position-estimate'; +import * as positionsModule from '@vegaprotocol/positions'; +import type { + EstimatePositionQuery, + EstimatePositionQueryVariables, +} from '@vegaprotocol/positions'; +import type { QueryResult } from '@apollo/client'; + +let mockData: object | undefined = {}; + +describe('usePositionEstimate', () => { + const args = { + marketId: 'marketId', + openVolume: '10', + orders: [], + collateralAvailable: '200', + skip: false, + }; + it('should return proper data', () => { + jest + .spyOn(positionsModule, 'useEstimatePositionQuery') + .mockReturnValue({ data: mockData } as unknown as QueryResult< + EstimatePositionQuery, + EstimatePositionQueryVariables + >); + const { result, rerender } = renderHook(() => usePositionEstimate(args)); + expect(result.current).toEqual(mockData); + mockData = undefined; + rerender(true); + expect(result.current).toEqual({}); + }); +}); diff --git a/libs/deal-ticket/src/hooks/use-position-estimate.ts b/libs/deal-ticket/src/hooks/use-position-estimate.ts new file mode 100644 index 000000000..6dd5255d1 --- /dev/null +++ b/libs/deal-ticket/src/hooks/use-position-estimate.ts @@ -0,0 +1,38 @@ +import type { + EstimatePositionQueryVariables, + EstimatePositionQuery, +} from '@vegaprotocol/positions'; +import { useEstimatePositionQuery } from '@vegaprotocol/positions'; +import { useEffect, useState } from 'react'; + +interface PositionEstimateProps extends EstimatePositionQueryVariables { + skip: boolean; +} + +export const usePositionEstimate = ({ + marketId, + openVolume, + orders, + collateralAvailable, + skip, +}: PositionEstimateProps) => { + const [estimates, setEstimates] = useState( + undefined + ); + const { data } = useEstimatePositionQuery({ + variables: { + marketId, + openVolume, + orders, + collateralAvailable, + }, + skip, + fetchPolicy: 'no-cache', + }); + useEffect(() => { + if (data) { + setEstimates(data); + } + }, [data]); + return estimates; +}; diff --git a/libs/environment/src/components/network-switcher/network-switcher.tsx b/libs/environment/src/components/network-switcher/network-switcher.tsx index 991b185f2..2bcd96028 100644 --- a/libs/environment/src/components/network-switcher/network-switcher.tsx +++ b/libs/environment/src/components/network-switcher/network-switcher.tsx @@ -87,7 +87,7 @@ export const NetworkSwitcher = ({ className, }: NetworkSwitcherProps) => { const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment(); - const tokenLink = useLinks(DApp.Token); + const tokenLink = useLinks(DApp.Governance); const [isOpen, setOpen] = useState(false); const [isAdvancedView, setAdvancedView] = useState(false); diff --git a/libs/environment/src/hooks/use-links.ts b/libs/environment/src/hooks/use-links.ts index 785449568..0c76a3056 100644 --- a/libs/environment/src/hooks/use-links.ts +++ b/libs/environment/src/hooks/use-links.ts @@ -11,7 +11,7 @@ type Net = Exclude; export enum DApp { Explorer = 'Explorer', Console = 'Console', - Token = 'Token', + Governance = 'Governance', } type DAppLinks = { @@ -45,7 +45,7 @@ const ConsoleLinks = { [Networks.MAINNET_MIRROR]: 'https://console.mainnet-mirror.vega.rocks', }; -const TokenLinks = { +const GovernanceLinks = { ...EmptyLinks, [Networks.DEVNET]: 'https://dev.governance.vega.xyz', [Networks.STAGNET1]: 'https://governance.stagnet1.vega.rocks', @@ -59,7 +59,7 @@ const TokenLinks = { const Links: { [k in DApp]: DAppLinks } = { [DApp.Explorer]: ExplorerLinks, [DApp.Console]: ConsoleLinks, - [DApp.Token]: TokenLinks, + [DApp.Governance]: GovernanceLinks, }; export const DocsLinks = VEGA_DOCS_URL @@ -88,7 +88,7 @@ export const useLinks = (dapp: DApp, network?: Net) => { useEnvironment(); const fallback = { [DApp.Explorer]: VEGA_EXPLORER_URL, - [DApp.Token]: VEGA_TOKEN_URL, + [DApp.Governance]: VEGA_TOKEN_URL, [DApp.Console]: VEGA_CONSOLE_URL, }; @@ -99,7 +99,10 @@ export const useLinks = (dapp: DApp, network?: Net) => { let baseUrl = trim(Links[dapp][net], '/'); if (baseUrl.length === 0 && Object.keys(fallback).includes(dapp)) { - baseUrl = trim(fallback[dapp as DApp.Explorer | DApp.Token] || '', '/'); + baseUrl = trim( + fallback[dapp as DApp.Explorer | DApp.Governance] || '', + '/' + ); } const link = useCallback( @@ -134,7 +137,7 @@ export const TOKEN_VALIDATOR = '/validators/:id'; * Generates link to the protocol upgrade proposal details on Governance */ export const useProtocolUpgradeProposalLink = () => { - const governance = useLinks(DApp.Token); + const governance = useLinks(DApp.Governance); return (releaseTag: string, blockHeight: string) => governance( TOKEN_PROTOCOL_UPGRADE_PROPOSAL.replace( diff --git a/libs/markets/src/lib/__generated__/markets.ts b/libs/markets/src/lib/__generated__/markets.ts index addf669a7..9a3feea33 100644 --- a/libs/markets/src/lib/__generated__/markets.ts +++ b/libs/markets/src/lib/__generated__/markets.ts @@ -7,12 +7,12 @@ export type DataSourceFilterFragment = { __typename?: 'Filter', key: { __typenam export type DataSourceSpecFragment = { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }; -export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } }; +export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, parentMarketID?: string | null, successorMarketID?: string | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } }; export type MarketsQueryVariables = Types.Exact<{ [key: string]: never; }>; -export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null }; +export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, parentMarketID?: string | null, successorMarketID?: string | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null }; export const DataSourceFilterFragmentDoc = gql` fragment DataSourceFilter on Filter { @@ -55,6 +55,8 @@ export const MarketFieldsFragmentDoc = gql` positionDecimalPlaces state tradingMode + parentMarketID + successorMarketID fees { factors { makerFee diff --git a/libs/markets/src/lib/components/index.ts b/libs/markets/src/lib/components/index.ts index 64fd41a55..b8bcfcaea 100644 --- a/libs/markets/src/lib/components/index.ts +++ b/libs/markets/src/lib/components/index.ts @@ -2,7 +2,6 @@ export * from './fees-breakdown'; export * from './last-24h-price-change'; export * from './last-24h-volume'; export * from './market-info'; -export * from './markets-container'; export * from './oracle-banner'; export * from './oracle-basic-profile'; export * from './oracle-full-profile'; diff --git a/libs/markets/src/lib/components/market-info/market-info-panels.tsx b/libs/markets/src/lib/components/market-info/market-info-panels.tsx index a1acca924..acc7cbd65 100644 --- a/libs/markets/src/lib/components/market-info/market-info-panels.tsx +++ b/libs/markets/src/lib/components/market-info/market-info-panels.tsx @@ -277,7 +277,7 @@ const SuccessionLineItem = ({ }); const marketData = data?.market; - const governanceLink = useLinks(DApp.Token); + const governanceLink = useLinks(DApp.Governance); const proposalLink = marketData?.proposal?.id ? governanceLink(TOKEN_PROPOSAL.replace(':id', marketData?.proposal?.id)) : undefined; @@ -658,7 +658,7 @@ export const LiquidityMonitoringParametersInfoPanel = ({ parentMarket.liquidityMonitoringParameters.targetStakeParameters .scalingFactor, } - : {}; + : undefined; return ; }; diff --git a/libs/markets/src/lib/components/markets-container/index.ts b/libs/markets/src/lib/components/markets-container/index.ts deleted file mode 100644 index 1c9c1e696..000000000 --- a/libs/markets/src/lib/components/markets-container/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './markets-container'; -export * from './market-table-actions'; diff --git a/libs/markets/src/lib/components/markets-container/market-list-table.tsx b/libs/markets/src/lib/components/markets-container/market-list-table.tsx deleted file mode 100644 index a16613077..000000000 --- a/libs/markets/src/lib/components/markets-container/market-list-table.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import type { TypedDataAgGrid } from '@vegaprotocol/datagrid'; -import { - AgGridLazy as AgGrid, - PriceFlashCell, - MarketNameCell, -} from '@vegaprotocol/datagrid'; -import type { MarketMaybeWithData } from '../../markets-provider'; -import { OracleStatus } from './oracle-status'; -import { useColumnDefs } from './use-column-defs'; - -export const getRowId = ({ data }: { data: { id: string } }) => data.id; - -interface MarketNameCellProps { - value?: string; - data?: MarketMaybeWithData; - onMarketClick?: (marketId: string, metaKey?: boolean) => void; -} - -const MarketName = (props: MarketNameCellProps) => ( - <> - - {props.data ? ( - - ) : null} - -); - -const defaultColDef = { - sortable: true, - filter: true, - filterParams: { buttons: ['reset'] }, -}; -type Props = TypedDataAgGrid & { - onMarketClick: (marketId: string, metaKey?: boolean) => void; - SuccessorMarketRenderer?: React.FC<{ value: string }>; -}; -export const MarketListTable = ({ - onMarketClick, - SuccessorMarketRenderer, - ...props -}: Props) => { - const columnDefs = useColumnDefs({ onMarketClick }); - const components = { - PriceFlashCell, - MarketName, - ...(SuccessorMarketRenderer ? { SuccessorMarketRenderer } : null), - }; - return ( - - ); -}; - -export default MarketListTable; diff --git a/libs/markets/src/lib/components/markets-container/markets-container.spec.tsx b/libs/markets/src/lib/components/markets-container/markets-container.spec.tsx deleted file mode 100644 index 34e2d74e0..000000000 --- a/libs/markets/src/lib/components/markets-container/markets-container.spec.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import { render, screen, act, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import * as DataProviders from '@vegaprotocol/data-provider'; -import { MockedProvider } from '@apollo/react-testing'; -import type { MarketMaybeWithData } from '../../markets-provider'; -import { MarketsContainer } from './markets-container'; -import { FLAGS } from '@vegaprotocol/environment'; - -jest.mock('@vegaprotocol/environment', () => { - const actual = jest.requireActual('@vegaprotocol/environment'); - return { - ...actual, - FLAGS: { - ...actual.FLAGS, - SUCCESSOR_MARKETS: true, - }, - }; -}); -const SuccessorMarketRenderer = ({ value }: { value: string }) => { - return '-'; -}; - -const market = { - id: 'id-1', - tradableInstrument: { - instrument: { - product: { settlementAsset: { id: 'assetId-1' } }, - }, - }, - decimalPlaces: 1, - positionDecimalPlaces: 1, - state: 'STATE_ACTIVE', - tradingMode: 'TRADING_MODE_OPENING_AUCTION', - data: { - bestBidPrice: 100, - }, -} as unknown as MarketMaybeWithData; - -describe('MarketsContainer', () => { - const spyOnSelect = jest.fn(); - beforeEach(() => { - jest.clearAllMocks(); - - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(DataProviders, 'useDataProvider') - .mockImplementation(() => { - return { - error: null, - reload: jest.fn(), - data: [market], - }; - }); - }); - it('context menu should stay open', async () => { - let rerenderRef: (ui: React.ReactElement) => void; - await act(async () => { - const { rerender } = render( - - - - ); - rerenderRef = rerender; - }); - - // make sure ag grid is finished initializaing - const rowContainer = await screen.findByRole('rowgroup', { - name: (_name, element) => - element.classList.contains('ag-center-cols-container'), - }); - expect(within(rowContainer).getAllByRole('row')).toHaveLength(1); - expect( - screen.getByRole('rowgroup', { - name: (_name, element) => - element.classList.contains('ag-pinned-right-cols-container'), - }) - ).toBeInTheDocument(); - - // open the dropdown - await userEvent.click( - screen.getByRole('button', { - name: (_name, element) => - (element.parentNode as Element)?.getAttribute('id') === - 'cell-market-actions-9', - }) - ); - - await checkDropdown(); - - // reset the mock and rerender so the component - // updates with new data - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(DataProviders, 'useDataProvider') - .mockImplementation(() => { - return { - error: null, - reload: jest.fn(), - data: [{ ...market, state: 'STATE_PENDING' }], - }; - }); - - // @ts-ignore we await the act above so rerenderRef is definitely defined - rerenderRef( - - - - ); - - // make sure dropdown is still open - await checkDropdown(); - - async function checkDropdown() { - const dropdownContent = await screen.findByTestId( - 'market-actions-content' - ); - expect(dropdownContent).toBeInTheDocument(); - expect( - within(dropdownContent).getByRole('menuitem', { - name: 'Copy Market ID', - }) - ).toBeInTheDocument(); - } - }); - - it('SuccessorMarketRenderer should be rendered', async () => { - const successorMarketName = 'Successor Market Name'; - const spySuccessorMarketRenderer = jest - .fn() - .mockReturnValue(successorMarketName); - - render( - - - - ); - - expect(spySuccessorMarketRenderer).toHaveBeenCalled(); - expect( - screen.getByRole('columnheader', { - name: (_name, element) => - element.getAttribute('col-id') === 'successorMarketID', - }) - ).toBeInTheDocument(); - expect( - screen.getByRole('presentation', { - name: (_name, element) => - element.getAttribute('id') === 'cell-successorMarketID-14', - }) - ).toHaveTextContent(successorMarketName); - }); - - it('feature flag should hide successorMarketID column', async () => { - const mockedFlags = jest.mocked(FLAGS); - mockedFlags.SUCCESSOR_MARKETS = false; - - const spySuccessorMarketRenderer = jest.fn(); - - render( - - - - ); - - expect(spySuccessorMarketRenderer).not.toHaveBeenCalled(); - screen.getAllByRole('columnheader').forEach((element) => { - expect(element.getAttribute('col-id')).not.toEqual('successorMarketID'); - }); - }); -}); diff --git a/libs/markets/src/lib/components/markets-container/markets-container.tsx b/libs/markets/src/lib/components/markets-container/markets-container.tsx deleted file mode 100644 index 8a8d39fb9..000000000 --- a/libs/markets/src/lib/components/markets-container/markets-container.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import type { MouseEvent } from 'react'; -import React, { useEffect } from 'react'; -import type { CellClickedEvent } from 'ag-grid-community'; -import { t } from '@vegaprotocol/i18n'; -import { MarketListTable } from './market-list-table'; -import { useDataProvider } from '@vegaprotocol/data-provider'; -import { marketsWithDataProvider as dataProvider } from '../../markets-provider'; -import type { MarketMaybeWithData } from '../../markets-provider'; - -const POLLING_TIME = 2000; -interface MarketsContainerProps { - onSelect: (marketId: string, metaKey?: boolean) => void; - SuccessorMarketRenderer?: React.FC<{ value: string }>; -} - -export const MarketsContainer = ({ - onSelect, - SuccessorMarketRenderer, -}: MarketsContainerProps) => { - const { data, error, reload } = useDataProvider({ - dataProvider, - variables: undefined, - }); - - useEffect(() => { - const interval = setInterval(() => { - reload(); - }, POLLING_TIME); - return () => { - clearInterval(interval); - }; - }, [reload]); - - return ( -
- { - const { data, column, event } = cellEvent; - // prevent navigating to the market page if any of the below cells are clicked - // event.preventDefault or event.stopPropagation dont seem to apply for aggird - const colId = column.getColId(); - if ( - [ - 'id', - 'tradableInstrument.instrument.code', - 'tradableInstrument.instrument.product.settlementAsset', - 'tradableInstrument.instrument.product.settlementAsset.symbol', - 'market-actions', - ].includes(colId) - ) { - return; - } - onSelect( - (data as MarketMaybeWithData).id, - (event as unknown as MouseEvent)?.metaKey || - (event as unknown as MouseEvent)?.ctrlKey - ); - }} - onMarketClick={onSelect} - overlayNoRowsTemplate={error ? error.message : t('No markets')} - SuccessorMarketRenderer={SuccessorMarketRenderer} - /> -
- ); -}; diff --git a/libs/markets/src/lib/components/markets-container/oracle-status.tsx b/libs/markets/src/lib/components/markets-container/oracle-status.tsx deleted file mode 100644 index 3165c6ce7..000000000 --- a/libs/markets/src/lib/components/markets-container/oracle-status.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useMemo } from 'react'; -import { useEnvironment } from '@vegaprotocol/environment'; -import { Icon } from '@vegaprotocol/ui-toolkit'; -import type { IconName } from '@blueprintjs/icons'; -import { getMatchingOracleProvider, useOracleProofs } from '../../hooks'; -import type { Market } from '../../markets-provider'; -import { getVerifiedStatusIcon } from '../oracle-basic-profile'; - -export const OracleStatus = ({ - dataSourceSpecForSettlementData, - dataSourceSpecForTradingTermination, -}: Pick< - Market['tradableInstrument']['instrument']['product'], - 'dataSourceSpecForSettlementData' | 'dataSourceSpecForTradingTermination' ->) => { - const { ORACLE_PROOFS_URL } = useEnvironment(); - const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL); - return useMemo(() => { - if (providers) { - const settlementDataProvider = getMatchingOracleProvider( - dataSourceSpecForSettlementData.data, - providers - ); - const tradingTerminationDataProvider = getMatchingOracleProvider( - dataSourceSpecForTradingTermination.data, - providers - ); - let maliciousOracleProvider = null; - if (settlementDataProvider?.oracle.status !== 'GOOD') { - maliciousOracleProvider = settlementDataProvider; - } else if (tradingTerminationDataProvider?.oracle.status !== 'GOOD') { - maliciousOracleProvider = tradingTerminationDataProvider; - } - if (!maliciousOracleProvider) return null; - const { icon } = getVerifiedStatusIcon(maliciousOracleProvider); - return ; - } - return null; - }, [ - providers, - dataSourceSpecForSettlementData, - dataSourceSpecForTradingTermination, - ]); -}; diff --git a/libs/markets/src/lib/components/markets-container/summary-cell.tsx b/libs/markets/src/lib/components/markets-container/summary-cell.tsx deleted file mode 100644 index 1d558ee9d..000000000 --- a/libs/markets/src/lib/components/markets-container/summary-cell.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import * as React from 'react'; - -/* -import { useMarketOverview } from '../../../../hooks/use-market-overview' -import { colorByMarketMovement } from '../../../../lib/vega-colours' -import { Sparkline } from '../../components/sparkline' -import { VEGA_TABLE_CLASSES } from '../../components/vega-table' -*/ - -export interface SummaryCellProps { - value: string; // marketId -} - -export const SummaryCellView = ({ value }: SummaryCellProps) => { - // const { sparkline, change, bullish } = useMarketOverview(value) - // const color = colorByMarketMovement(bullish) - - return ( - <> - {/* */} - {'change'} - - ); -}; - -SummaryCellView.displayName = 'SummaryCellView'; - -export const SummaryCell = React.memo(SummaryCellView); -SummaryCell.displayName = 'SummaryCell'; diff --git a/libs/markets/src/lib/components/markets-container/use-column-defs.tsx b/libs/markets/src/lib/components/markets-container/use-column-defs.tsx deleted file mode 100644 index 4271debcd..000000000 --- a/libs/markets/src/lib/components/markets-container/use-column-defs.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import { useMemo } from 'react'; -import type { ColDef } from 'ag-grid-community'; -import compact from 'lodash/compact'; -import { t } from '@vegaprotocol/i18n'; -import type { - VegaICellRendererParams, - VegaValueFormatterParams, - VegaValueGetterParams, -} from '@vegaprotocol/datagrid'; -import { COL_DEFS, SetFilter } from '@vegaprotocol/datagrid'; -import * as Schema from '@vegaprotocol/types'; -import { addDecimalsFormatNumber, toBigNum } from '@vegaprotocol/utils'; -import { ButtonLink } from '@vegaprotocol/ui-toolkit'; -import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; -import { FLAGS } from '@vegaprotocol/environment'; -import type { MarketMaybeWithData } from '../../markets-provider'; -import { MarketActionsDropdown } from './market-table-actions'; - -interface Props { - onMarketClick: (marketId: string, metaKey?: boolean) => void; -} - -const { MarketTradingMode, AuctionTrigger } = Schema; - -export const useColumnDefs = ({ onMarketClick }: Props) => { - const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); - return useMemo( - () => - compact([ - { - headerName: t('Market'), - field: 'tradableInstrument.instrument.code', - cellRenderer: 'MarketName', - cellRendererParams: { onMarketClick }, - }, - { - headerName: t('Description'), - field: 'tradableInstrument.instrument.name', - }, - { - headerName: t('Trading mode'), - field: 'tradingMode', - valueFormatter: ({ - data, - }: VegaValueFormatterParams) => { - if (!data?.data) return '-'; - const { trigger, marketTradingMode } = data.data; - return marketTradingMode === - MarketTradingMode.TRADING_MODE_MONITORING_AUCTION && - trigger && - trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED - ? `${Schema.MarketTradingModeMapping[marketTradingMode]} - - ${Schema.AuctionTriggerMapping[trigger]}` - : Schema.MarketTradingModeMapping[marketTradingMode]; - }, - filter: SetFilter, - filterParams: { - set: Schema.MarketTradingModeMapping, - }, - }, - { - headerName: t('Status'), - field: 'state', - valueFormatter: ({ - data, - }: VegaValueFormatterParams) => { - return data?.state ? Schema.MarketStateMapping[data.state] : '-'; - }, - filter: SetFilter, - filterParams: { - set: Schema.MarketStateMapping, - }, - }, - FLAGS.SUCCESSOR_MARKETS && { - headerName: t('Successor market'), - field: 'successorMarketID', - cellRenderer: 'SuccessorMarketRenderer', - }, - { - headerName: t('Best bid'), - field: 'data.bestBidPrice', - type: 'rightAligned', - cellRenderer: 'PriceFlashCell', - filter: 'agNumberColumnFilter', - valueGetter: ({ - data, - }: VegaValueGetterParams) => { - return data?.data?.bestBidPrice === undefined - ? undefined - : toBigNum( - data?.data?.bestBidPrice, - data.decimalPlaces - ).toNumber(); - }, - valueFormatter: ({ - data, - }: VegaValueFormatterParams< - MarketMaybeWithData, - 'data.bestBidPrice' - >) => - data?.data?.bestBidPrice === undefined - ? '-' - : addDecimalsFormatNumber( - data.data.bestBidPrice, - data.decimalPlaces - ), - }, - { - headerName: t('Best offer'), - field: 'data.bestOfferPrice', - type: 'rightAligned', - cellRenderer: 'PriceFlashCell', - filter: 'agNumberColumnFilter', - valueGetter: ({ - data, - }: VegaValueGetterParams) => { - return data?.data?.bestOfferPrice === undefined - ? undefined - : toBigNum( - data?.data?.bestOfferPrice, - data.decimalPlaces - ).toNumber(); - }, - valueFormatter: ({ - data, - }: VegaValueFormatterParams< - MarketMaybeWithData, - 'data.bestOfferPrice' - >) => - data?.data?.bestOfferPrice === undefined - ? '-' - : addDecimalsFormatNumber( - data.data.bestOfferPrice, - data.decimalPlaces - ), - }, - { - headerName: t('Mark price'), - field: 'data.markPrice', - type: 'rightAligned', - cellRenderer: 'PriceFlashCell', - filter: 'agNumberColumnFilter', - valueGetter: ({ - data, - }: VegaValueGetterParams) => { - return data?.data?.markPrice === undefined - ? undefined - : toBigNum(data?.data?.markPrice, data.decimalPlaces).toNumber(); - }, - valueFormatter: ({ - data, - }: VegaValueFormatterParams) => - data?.data?.bestOfferPrice === undefined - ? '-' - : addDecimalsFormatNumber( - data.data.markPrice, - data.decimalPlaces - ), - }, - { - headerName: t('Settlement asset'), - field: 'tradableInstrument.instrument.product.settlementAsset.symbol', - cellRenderer: ({ - data, - }: VegaICellRendererParams< - MarketMaybeWithData, - 'tradableInstrument.instrument.product.settlementAsset.symbol' - >) => { - const value = - data?.tradableInstrument.instrument.product.settlementAsset; - return value ? ( - { - openAssetDetailsDialog(value.id, e.target as HTMLElement); - }} - > - {value.symbol} - - ) : ( - '' - ); - }, - }, - { - colId: 'market-actions', - field: 'id', - ...COL_DEFS.actions, - cellRenderer: ({ - data, - }: VegaICellRendererParams) => { - if (!data) return null; - return ( - - ); - }, - }, - ]), - [onMarketClick, openAssetDetailsDialog] - ); -}; diff --git a/libs/markets/src/lib/components/oracle-basic-profile/oracle-basic-profile.tsx b/libs/markets/src/lib/components/oracle-basic-profile/oracle-basic-profile.tsx index a8afd2966..53531bca1 100644 --- a/libs/markets/src/lib/components/oracle-basic-profile/oracle-basic-profile.tsx +++ b/libs/markets/src/lib/components/oracle-basic-profile/oracle-basic-profile.tsx @@ -43,6 +43,7 @@ export const getVerifiedStatusIcon = (provider: Provider) => { const lastVerified = provider.oracle.last_verified ? new Date(provider.oracle.last_verified) : new Date(provider.oracle.first_verified); + return { ...getIconIntent(), message: t( @@ -112,13 +113,13 @@ export const OracleBasicProfile = ({ -

+

{message}

{oracleMarkets && (

{t('Involved in %s %s', [ oracleMarkets.length.toString(), @@ -130,9 +131,9 @@ export const OracleBasicProfile = ({

{links.map((link) => ( - + - {link.type} + {link.type} diff --git a/libs/markets/src/lib/markets-provider.ts b/libs/markets/src/lib/markets-provider.ts index 021b78fca..c9ded0346 100644 --- a/libs/markets/src/lib/markets-provider.ts +++ b/libs/markets/src/lib/markets-provider.ts @@ -28,8 +28,6 @@ import { } from './market-utils'; import { MarketsDocument } from './__generated__/markets'; import type { Candle } from './market-candles-provider'; -import type { SuccessorMarketIdsQuery } from './__generated__/SuccessorMarket'; -import { SuccessorMarketIdsDocument } from './__generated__'; export type Market = MarketFieldsFragment; @@ -240,34 +238,3 @@ export const useMarketList = () => { reload, }; }; - -export type MarketSuccessors = { - __typename?: 'Market'; - id: string; - successorMarketID?: string | null; - parentMarketID?: string | null; -}; -const getMarketSuccessorData = ( - responseData: SuccessorMarketIdsQuery | null -): MarketSuccessors[] | null => - responseData?.marketsConnection?.edges.map((edge) => edge.node) || null; - -export const marketSuccessorProvider = makeDataProvider< - SuccessorMarketIdsQuery, - MarketSuccessors[], - never, - never ->({ - query: SuccessorMarketIdsDocument, - getData: getMarketSuccessorData, - fetchPolicy: 'no-cache', -}); - -export const useSuccessorMarketIds = (marketId: string) => { - const { data } = useDataProvider({ - dataProvider: marketSuccessorProvider, - variables: undefined, - skip: !marketId, - }); - return data?.find((item) => item.id === marketId) ?? null; -}; diff --git a/libs/markets/src/lib/markets.graphql b/libs/markets/src/lib/markets.graphql index 84e25a6e2..ca424da28 100644 --- a/libs/markets/src/lib/markets.graphql +++ b/libs/markets/src/lib/markets.graphql @@ -36,6 +36,8 @@ fragment MarketFields on Market { positionDecimalPlaces state tradingMode + parentMarketID + successorMarketID fees { factors { makerFee diff --git a/libs/markets/src/lib/markets.mock.ts b/libs/markets/src/lib/markets.mock.ts index 01af90c17..1a9d1ae48 100644 --- a/libs/markets/src/lib/markets.mock.ts +++ b/libs/markets/src/lib/markets.mock.ts @@ -42,6 +42,8 @@ export const createMarketFragment = ( close: null, open: null, }, + successorMarketID: null, + parentMarketID: null, fees: { __typename: 'Fees', factors: { diff --git a/libs/positions/src/lib/liquidation-price.tsx b/libs/positions/src/lib/liquidation-price.tsx index f5afd0ccb..283dc6d6a 100644 --- a/libs/positions/src/lib/liquidation-price.tsx +++ b/libs/positions/src/lib/liquidation-price.tsx @@ -7,13 +7,21 @@ export const LiquidationPrice = ({ marketId, openVolume, collateralAvailable, - marketDecimalPlaces, + decimalPlaces, + formatDecimals, }: { marketId: string; openVolume: string; collateralAvailable: string; - marketDecimalPlaces: number; + decimalPlaces: number; + formatDecimals: number; }) => { + // NOTE! + // + // The estimate order query API gives us the liquidation price unformatted but expecting to be converted + // using asset decimal placse. + // + // We need to convert it with asset decimals, but display it formatted with market decimals precision until the API changes. const { data: currentData, previousData } = useEstimatePositionQuery({ variables: { marketId, @@ -43,8 +51,8 @@ export const LiquidationPrice = ({ /\..*/, '' ); - worstCase = addDecimalsFormatNumber(worstCase, marketDecimalPlaces); - bestCase = addDecimalsFormatNumber(bestCase, marketDecimalPlaces); + worstCase = addDecimalsFormatNumber(worstCase, decimalPlaces, formatDecimals); + bestCase = addDecimalsFormatNumber(bestCase, decimalPlaces, formatDecimals); return ( {t('Worst case')} - {worstCase} + {worstCase} {t('Best case')} - {bestCase} + {bestCase} diff --git a/libs/positions/src/lib/positions-table.tsx b/libs/positions/src/lib/positions-table.tsx index a7ef20e93..097149226 100644 --- a/libs/positions/src/lib/positions-table.tsx +++ b/libs/positions/src/lib/positions-table.tsx @@ -15,6 +15,7 @@ import { MarketNameCell, ProgressBarCell, MarketProductPill, + StackedCell, } from '@vegaprotocol/datagrid'; import { ButtonLink, @@ -40,7 +41,6 @@ import { import { DocsLinks } from '@vegaprotocol/environment'; import { PositionActionsDropdown } from './position-actions-dropdown'; import { LiquidationPrice } from './liquidation-price'; -import { StackedCell } from './stacked-cell'; interface Props extends TypedDataAgGrid { onClose?: (data: Position) => void; @@ -339,12 +339,16 @@ export const PositionsTable = ({ if (!data) { return '-'; } + // The estimate order query API gives us the liquidation price unformatted but expecting + // conversion using asset decimals. We need to convert it with asset decimals, but format + // it with market decimals precision until the API changes. return ( ); }, @@ -364,7 +368,7 @@ export const PositionsTable = ({ DocsLinks?.LOSS_SOCIALIZATION ?? ''; if (!args.data) { - return <>-; + return null; } const losses = parseInt( @@ -373,7 +377,9 @@ export const PositionsTable = ({ if (losses <= 0) { // eslint-disable-next-line react/jsx-no-useless-fragment - return <>{args.valueFormatted}; + return ( + + ); } const lossesFormatted = addDecimalsFormatNumber( diff --git a/libs/proposals/src/components/asset-proposal-notification.tsx b/libs/proposals/src/components/asset-proposal-notification.tsx index 2301d8a3e..9c2a71937 100644 --- a/libs/proposals/src/components/asset-proposal-notification.tsx +++ b/libs/proposals/src/components/asset-proposal-notification.tsx @@ -10,7 +10,7 @@ type AssetProposalNotificationProps = { export const AssetProposalNotification = ({ assetId, }: AssetProposalNotificationProps) => { - const tokenLink = useLinks(DApp.Token); + const tokenLink = useLinks(DApp.Governance); const { data: proposal } = useUpdateProposal({ id: assetId, proposalType: Schema.ProposalType.TYPE_UPDATE_ASSET, diff --git a/libs/proposals/src/components/market-proposal-notification.tsx b/libs/proposals/src/components/market-proposal-notification.tsx index 99ee236fc..07f82804d 100644 --- a/libs/proposals/src/components/market-proposal-notification.tsx +++ b/libs/proposals/src/components/market-proposal-notification.tsx @@ -10,7 +10,7 @@ type MarketProposalNotificationProps = { export const MarketProposalNotification = ({ marketId, }: MarketProposalNotificationProps) => { - const tokenLink = useLinks(DApp.Token); + const tokenLink = useLinks(DApp.Governance); const { data: proposal } = useUpdateProposal({ id: marketId, proposalType: Schema.ProposalType.TYPE_UPDATE_MARKET, diff --git a/libs/proposals/src/components/proposal-actions-dropdown.tsx b/libs/proposals/src/components/proposal-actions-dropdown.tsx index 369ec044c..49cbe2344 100644 --- a/libs/proposals/src/components/proposal-actions-dropdown.tsx +++ b/libs/proposals/src/components/proposal-actions-dropdown.tsx @@ -9,7 +9,7 @@ import { t } from '@vegaprotocol/i18n'; import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment'; export const ProposalActionsDropdown = ({ id }: { id: string }) => { - const linkCreator = useLinks(DApp.Token); + const linkCreator = useLinks(DApp.Governance); return ( diff --git a/libs/proposals/src/components/proposals-list/proposal-list.spec.tsx b/libs/proposals/src/components/proposals-list/proposal-list.spec.tsx index dc003b0e9..80354dbc1 100644 --- a/libs/proposals/src/components/proposals-list/proposal-list.spec.tsx +++ b/libs/proposals/src/components/proposals-list/proposal-list.spec.tsx @@ -1,10 +1,4 @@ -import { - render, - screen, - act, - waitFor, - getAllByRole, -} from '@testing-library/react'; +import { render, screen, waitFor, within } from '@testing-library/react'; import merge from 'lodash/merge'; import type { MockedResponse } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing'; @@ -14,25 +8,13 @@ import { createProposalListFieldsFragment } from '../../lib/proposals-data-provi import type { ProposalsListQuery } from '../../lib'; import { ProposalsListDocument } from '../../lib'; import type { PartialDeep } from 'type-fest'; -import { FLAGS } from '@vegaprotocol/environment'; -jest.mock('@vegaprotocol/environment', () => { - const actual = jest.requireActual('@vegaprotocol/environment'); - return { - ...actual, - FLAGS: { - ...actual.FLAGS, - SUCCESSOR_MARKETS: true, - }, - }; -}); - -const successorMarketName = 'Successor Market Name'; -const spySuccessorMarketRenderer = jest - .fn() - .mockReturnValue(successorMarketName); +const parentMarketName = 'Parent Market Name'; +const ParentMarketCell = () => {parentMarketName}; describe('ProposalsList', () => { + const rowContainerSelector = '.ag-center-cols-container'; + const createProposalsMock = (override?: PartialDeep) => { const defaultProposalEdges = [ { @@ -81,75 +63,55 @@ describe('ProposalsList', () => { return mock; }; + beforeEach(() => { jest.clearAllMocks(); }); + it('should be properly rendered', async () => { const mock = createProposalsMock(); - await act(() => { - render( - - - - ); - }); - const container = document.querySelector('.ag-center-cols-container'); + render( + + + + ); + await waitFor(() => { - expect(container).toBeInTheDocument(); - }); - expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(3); - }); - - it('some of states should be filtered out', async () => { - const proposalNode = createProposalListFieldsFragment({ - id: 'id-1', - state: Types.ProposalState.STATE_ENACTED, + expect(document.querySelector(rowContainerSelector)).toBeInTheDocument(); }); - const mock = createProposalsMock({ - proposalsConnection: { - edges: [ - { - __typename: 'ProposalEdge', - node: { - ...proposalNode, - terms: { - ...proposalNode.terms, - change: { - ...proposalNode.terms.change, - }, - }, - }, - }, - ], - }, - } as PartialDeep); - await act(() => { - render( - - - - ); - }); - const container = document.querySelector('.ag-center-cols-container'); - await waitFor(() => { - expect(container).toBeInTheDocument(); - expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2); - }); + const expectedHeaders = [ + 'Market', + 'Settlement asset', + 'State', + 'Parent market', + 'Voting', + 'Closing date', + 'Enactment date', + '', // actions col + ]; - expect(spySuccessorMarketRenderer).toHaveBeenCalled(); + const headers = screen.getAllByRole('columnheader'); + expect(headers).toHaveLength(expectedHeaders.length); expect( - screen.getByRole('columnheader', { - name: (_name, element) => - element.getAttribute('col-id') === 'parentMarket', - }) - ).toBeInTheDocument(); + headers.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim()) + ).toEqual(expectedHeaders); + + const container = within( + document.querySelector(rowContainerSelector) as HTMLElement + ); + expect(container.getAllByRole('row')).toHaveLength( + // @ts-ignore data is mocked + mock?.result?.data.proposalsConnection.edges.length + ); + expect( - screen.getAllByRole('gridcell', { - name: (name, element) => - element.getAttribute('col-id') === 'parentMarket', + container.getAllByRole('gridcell', { + name: (_, element) => + element.getAttribute('col-id') === + 'terms.change.successorConfiguration.parentMarketId', })[0] - ).toHaveTextContent(successorMarketName); + ).toHaveTextContent(parentMarketName); }); it('empty response should causes no data message display', async () => { @@ -169,58 +131,11 @@ describe('ProposalsList', () => { }, }, }; - await act(() => { - render( - - - - ); - }); - expect(await screen.findByText('No markets')).toBeInTheDocument(); - - expect( - screen.getByRole('columnheader', { - name: (_name, element) => - element.getAttribute('col-id') === 'parentMarket', - }) - ).toBeInTheDocument(); - }); - - it('feature flag should hide parent marketcolumn', async () => { - const mockedFlags = jest.mocked(FLAGS); - mockedFlags.SUCCESSOR_MARKETS = false; - const mock: MockedResponse = { - request: { - query: ProposalsListDocument, - variables: { - proposalType: Types.ProposalType.TYPE_NEW_MARKET, - }, - }, - result: { - data: { - proposalsConnection: { - __typename: 'ProposalsConnection', - edges: [], - }, - }, - }, - }; - await act(() => { - render( - - - - ); - }); - await waitFor(() => { - expect( - screen.getByRole('columnheader', { - name: (_name, element) => element.getAttribute('col-id') === 'market', - }) - ).toBeInTheDocument(); - }); - screen.getAllByRole('columnheader').forEach((element) => { - expect(element.getAttribute('col-id')).not.toEqual('parentMarket'); - }); + render( + + + + ); + expect(await screen.findByText('No proposed markets')).toBeInTheDocument(); }); }); diff --git a/libs/proposals/src/components/proposals-list/proposals-list.tsx b/libs/proposals/src/components/proposals-list/proposals-list.tsx index 9cdb01aef..d4cd1a8c2 100644 --- a/libs/proposals/src/components/proposals-list/proposals-list.tsx +++ b/libs/proposals/src/components/proposals-list/proposals-list.tsx @@ -1,11 +1,11 @@ -import React from 'react'; +import type { FC } from 'react'; import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { t } from '@vegaprotocol/i18n'; import * as Types from '@vegaprotocol/types'; -import { MarketNameProposalCell, useColumnDefs } from './use-column-defs'; +import { removePaginationWrapper } from '@vegaprotocol/utils'; import type { ProposalListFieldsFragment } from '../../lib/proposals-data-provider/__generated__/Proposals'; import { useProposalsListQuery } from '../../lib/proposals-data-provider/__generated__/Proposals'; -import { removePaginationWrapper } from '@vegaprotocol/utils'; +import { useColumnDefs } from './use-column-defs'; export const getNewMarketProposals = (data: ProposalListFieldsFragment[]) => data.filter((proposal) => @@ -16,13 +16,19 @@ export const getNewMarketProposals = (data: ProposalListFieldsFragment[]) => ].includes(proposal.state) ); +const defaultColDef = { + sortable: true, + filter: true, + filterParams: { buttons: ['reset'] }, +}; + interface ProposalListProps { - SuccessorMarketRenderer: React.FC<{ value: string }>; + cellRenderers: { + [name: string]: FC<{ value: string; data: ProposalListFieldsFragment }>; + }; } -export const ProposalsList = ({ - SuccessorMarketRenderer, -}: ProposalListProps) => { +export const ProposalsList = ({ cellRenderers }: ProposalListProps) => { const { data } = useProposalsListQuery({ variables: { proposalType: Types.ProposalType.TYPE_NEW_MARKET, @@ -32,7 +38,7 @@ export const ProposalsList = ({ const filteredData = getNewMarketProposals( removePaginationWrapper(data?.proposalsConnection?.edges) ); - const { columnDefs, defaultColDef } = useColumnDefs(); + const columnDefs = useColumnDefs(); return ( data.id} - overlayNoRowsTemplate={t('No markets')} - components={{ SuccessorMarketRenderer, MarketNameProposalCell }} + overlayNoRowsTemplate={t('No proposed markets')} + components={cellRenderers} + rowHeight={45} /> ); }; diff --git a/libs/proposals/src/components/proposals-list/use-column-defs.tsx b/libs/proposals/src/components/proposals-list/use-column-defs.tsx index 795a0fa8c..c0ce65d31 100644 --- a/libs/proposals/src/components/proposals-list/use-column-defs.tsx +++ b/libs/proposals/src/components/proposals-list/use-column-defs.tsx @@ -6,9 +6,9 @@ import { COL_DEFS, DateRangeFilter, SetFilter, + StackedCell, } from '@vegaprotocol/datagrid'; import compact from 'lodash/compact'; -import { useEnvironment, FLAGS } from '@vegaprotocol/environment'; import { getDateTimeFormat } from '@vegaprotocol/utils'; import { t } from '@vegaprotocol/i18n'; import { @@ -19,50 +19,15 @@ import type { VegaICellRendererParams, VegaValueFormatterParams, } from '@vegaprotocol/datagrid'; -import { ExternalLink, Pill } from '@vegaprotocol/ui-toolkit'; import { - ProposalProductTypeMapping, - ProposalProductTypeShortName, + ProductTypeMapping, + ProductTypeShortName, ProposalStateMapping, } from '@vegaprotocol/types'; import type { ProposalListFieldsFragment } from '../../lib/proposals-data-provider/__generated__/Proposals'; import { VoteProgress } from '../voting-progress'; import { ProposalActionsDropdown } from '../proposal-actions-dropdown'; -export const MarketNameProposalCell = ({ - value, - data, -}: VegaICellRendererParams< - ProposalListFieldsFragment, - 'terms.change.instrument.code' ->) => { - const { VEGA_TOKEN_URL } = useEnvironment(); - const { change } = data?.terms || {}; - if (change?.__typename === 'NewMarket' && VEGA_TOKEN_URL) { - const type = change.instrument.futureProduct?.__typename; - const content = ( - <> - {value as string} - {type && ( - - {ProposalProductTypeShortName[type]} - - )} - - ); - if (data?.id) { - const link = `${VEGA_TOKEN_URL}/proposals/${data.id}`; - return {content}; - } - return content; - } - return null; -}; - export const useColumnDefs = () => { const { params } = useNetworkParams([ NetworkParams.governance_proposal_market_requiredMajority, @@ -80,17 +45,36 @@ export const useColumnDefs = () => { headerName: t('Market'), field: 'terms.change.instrument.code', cellStyle: { lineHeight: '14px' }, - cellRenderer: 'MarketNameProposalCell', - }, - { - colId: 'description', - headerName: t('Description'), - field: 'terms.change.instrument.name', + cellRenderer: ({ + value, + data, + }: { + value: string; + data: ProposalListFieldsFragment; + }) => { + if (!value || !data) return '-'; + + // TODO: update when we switch to ProductConfiguration + const productType = 'Future'; + return ( + + {ProductTypeShortName[productType]} + + } + /> + ); + }, }, { colId: 'asset', headerName: t('Settlement asset'), - field: 'terms.change.instrument.futureProduct.settlementAsset.name', + field: 'terms.change.instrument.futureProduct.settlementAsset.symbol', }, { colId: 'state', @@ -105,12 +89,10 @@ export const useColumnDefs = () => { set: ProposalStateMapping, }, }, - FLAGS.SUCCESSOR_MARKETS && { + { headerName: t('Parent market'), - field: 'id', - colId: 'parentMarket', - cellRenderer: 'SuccessorMarketRenderer', - cellRendererParams: { parent: true }, + field: 'terms.change.successorConfiguration.parentMarketId', + cellRenderer: 'ParentMarketCell', }, { colId: 'voting', @@ -169,25 +151,12 @@ export const useColumnDefs = () => { data, }: VegaICellRendererParams) => { if (!data?.id) return null; + return ; }, }, ]); }, [requiredMajorityPercentage]); - const defaultColDef: ColDef = useMemo(() => { - return { - sortable: true, - filter: true, - filterParams: { buttons: ['reset'] }, - }; - }, []); - - return useMemo( - () => ({ - columnDefs, - defaultColDef, - }), - [columnDefs, defaultColDef] - ); + return columnDefs; }; diff --git a/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql b/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql index dde320b6c..1429c9107 100644 --- a/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql +++ b/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql @@ -114,6 +114,9 @@ fragment NewMarketFields on NewMarket { lpPriceRange # linearSlippageFactor # quadraticSlippageFactor + successorConfiguration { + parentMarketId + } } fragment UpdateMarketFields on UpdateMarket { diff --git a/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts b/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts index 8f4c71956..90aa2c975 100644 --- a/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts +++ b/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts @@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; const defaultOptions = {} as const; -export type NewMarketFieldsFragment = { __typename?: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } }; +export type NewMarketFieldsFragment = { __typename?: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null }; export type UpdateMarketFieldsFragment = { __typename?: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } }; @@ -13,7 +13,7 @@ export type UpdateAssetFieldsFragment = { __typename?: 'UpdateAsset', assetId: s export type UpdateNetworkParameterFielsFragment = { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } }; -export type ProposalListFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, requiredMajority: string, requiredParticipation: string, requiredLpMajority?: string | null, requiredLpParticipation?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'NewFreeform' } | { __typename: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } } | { __typename: 'NewTransfer' } | { __typename: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } }; +export type ProposalListFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, requiredMajority: string, requiredParticipation: string, requiredLpMajority?: string | null, requiredLpParticipation?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'NewFreeform' } | { __typename: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null } | { __typename: 'NewTransfer' } | { __typename: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } }; export type ProposalsListQueryVariables = Types.Exact<{ proposalType?: Types.InputMaybe; @@ -21,7 +21,7 @@ export type ProposalsListQueryVariables = Types.Exact<{ }>; -export type ProposalsListQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, requiredMajority: string, requiredParticipation: string, requiredLpMajority?: string | null, requiredLpParticipation?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'NewFreeform' } | { __typename: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } } | { __typename: 'NewTransfer' } | { __typename: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } } } | null> | null } | null }; +export type ProposalsListQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, requiredMajority: string, requiredParticipation: string, requiredLpMajority?: string | null, requiredLpParticipation?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'NewFreeform' } | { __typename: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null } | { __typename: 'NewTransfer' } | { __typename: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } } } | null> | null } | null }; export type NewMarketSuccessorFieldsFragment = { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null }; @@ -132,6 +132,9 @@ export const NewMarketFieldsFragmentDoc = gql` } metadata lpPriceRange + successorConfiguration { + parentMarketId + } } `; export const UpdateMarketFieldsFragmentDoc = gql` diff --git a/libs/proposals/src/lib/proposals-data-provider/proposals.mock.ts b/libs/proposals/src/lib/proposals-data-provider/proposals.mock.ts index 3b8add493..2fd351dee 100644 --- a/libs/proposals/src/lib/proposals-data-provider/proposals.mock.ts +++ b/libs/proposals/src/lib/proposals-data-provider/proposals.mock.ts @@ -109,6 +109,58 @@ export const marketUpdateProposal: ProposalListFieldsFragment = { export const createProposalListFieldsFragment = ( override?: PartialDeep ): ProposalListFieldsFragment => { + const newMarket = { + decimalPlaces: 1, + lpPriceRange: '', + riskParameters: { + __typename: 'SimpleRiskModel', + params: { + __typename: 'SimpleRiskModelParams', + factorLong: 0, + factorShort: 1, + }, + }, + metadata: undefined, + successorConfiguration: { + __typename: 'SuccessorConfiguration', + parentMarketId: 'xyz', + }, + instrument: { + code: 'ETHUSD', + name: 'ETHUSD', + futureProduct: { + settlementAsset: { + id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663', + name: 'tDAI TEST', + symbol: 'tDAI', + decimals: 1, + quantum: '1', + __typename: 'Asset', + }, + quoteName: '', + dataSourceSpecBinding: { + __typename: 'DataSourceSpecToFutureBinding', + settlementDataProperty: '', + tradingTerminationProperty: '', + }, + dataSourceSpecForSettlementData: { + __typename: 'DataSourceDefinition', + sourceType: { + __typename: 'DataSourceDefinitionInternal', + }, + }, + dataSourceSpecForTradingTermination: { + __typename: 'DataSourceDefinition', + sourceType: { + __typename: 'DataSourceDefinitionInternal', + }, + }, + __typename: 'FutureProduct', + }, + __typename: 'InstrumentConfiguration', + }, + __typename: 'NewMarket', + } as const; const defaultProposal: ProposalListFieldsFragment = { id: 'e9ec6d5c46a7e7bcabf9ba7a893fa5a5eeeec08b731f06f7a6eb7bf0e605b829', reference: 'injected_at_runtime', @@ -147,54 +199,7 @@ export const createProposalListFieldsFragment = ( terms: { closingDatetime: '2022-11-15T12:44:34Z', enactmentDatetime: '2022-11-15T12:44:54Z', - change: { - decimalPlaces: 1, - lpPriceRange: '', - riskParameters: { - __typename: 'SimpleRiskModel', - params: { - __typename: 'SimpleRiskModelParams', - factorLong: 0, - factorShort: 1, - }, - }, - metadata: [], - instrument: { - code: 'ETHUSD', - name: 'ETHUSD', - futureProduct: { - settlementAsset: { - id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663', - name: 'tDAI TEST', - symbol: 'tDAI', - decimals: 1, - quantum: '1', - __typename: 'Asset', - }, - quoteName: '', - dataSourceSpecBinding: { - __typename: 'DataSourceSpecToFutureBinding', - settlementDataProperty: '', - tradingTerminationProperty: '', - }, - dataSourceSpecForSettlementData: { - __typename: 'DataSourceDefinition', - sourceType: { - __typename: 'DataSourceDefinitionInternal', - }, - }, - dataSourceSpecForTradingTermination: { - __typename: 'DataSourceDefinition', - sourceType: { - __typename: 'DataSourceDefinitionInternal', - }, - }, - __typename: 'FutureProduct', - }, - __typename: 'InstrumentConfiguration', - }, - __typename: 'NewMarket', - }, + change: newMarket, __typename: 'ProposalTerms', }, __typename: 'Proposal', diff --git a/libs/proposals/src/lib/proposals-hooks/use-update-network-paramaters-toasts.tsx b/libs/proposals/src/lib/proposals-hooks/use-update-network-paramaters-toasts.tsx index 8b620c40b..35970bb41 100644 --- a/libs/proposals/src/lib/proposals-hooks/use-update-network-paramaters-toasts.tsx +++ b/libs/proposals/src/lib/proposals-hooks/use-update-network-paramaters-toasts.tsx @@ -26,7 +26,7 @@ const UpdateNetworkParameterToastContent = ({ }: { proposal: Proposal; }) => { - const tokenLink = useLinks(DApp.Token); + const tokenLink = useLinks(DApp.Governance); const change = proposal.terms.change as UpdateNetworkParameter; const title = t('Network change proposal %s').replace( '%s', diff --git a/libs/ui-toolkit/src/components/accordion/accordion.tsx b/libs/ui-toolkit/src/components/accordion/accordion.tsx index 9e52f8250..190060525 100644 --- a/libs/ui-toolkit/src/components/accordion/accordion.tsx +++ b/libs/ui-toolkit/src/components/accordion/accordion.tsx @@ -1,5 +1,6 @@ import * as AccordionPrimitive from '@radix-ui/react-accordion'; import classNames from 'classnames'; +import type { VegaIconProps } from '../icon'; import { VegaIcon, VegaIconNames } from '../icon'; export interface AccordionItemProps { @@ -24,6 +25,28 @@ export const Accordion = ({ panels, children }: AccordionProps) => { ); }; +export const AccordionPanel = ({ + trigger, + children, + itemId, +}: { + trigger: React.ReactNode; + children: React.ReactNode; + itemId: string; +}) => { + return ( + + {trigger} + + {children} + + + ); +}; + export const AccordionItem = ({ title, content, @@ -31,7 +54,7 @@ export const AccordionItem = ({ }: AccordionPanelProps) => { const triggerClassNames = classNames( 'w-full py-2', - 'flex items-center justify-between border-b border-vega-light-200 dark:border-vega-dark-200 text-sm', + 'flex items-center justify-between gap-2 border-b border-vega-light-200 dark:border-vega-dark-200 text-sm', 'group' ); return ( @@ -41,7 +64,9 @@ export const AccordionItem = ({ data-testid="accordion-toggle" className={triggerClassNames} > - {title} + + {title} + @@ -55,15 +80,17 @@ export const AccordionItem = ({ ); }; -export const AccordionChevron = () => { +export const AccordionChevron = ({ + size = 16, +}: Pick) => { return ( - + ); }; diff --git a/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-eye-off.tsx b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-eye-off.tsx index 31aae4fc8..74c558b9d 100644 --- a/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-eye-off.tsx +++ b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-eye-off.tsx @@ -1,7 +1,8 @@ export const IconEyeOff = ({ size = 16 }: { size: number }) => { return ( - + + ); }; diff --git a/libs/ui-toolkit/src/components/tabs/tabs.tsx b/libs/ui-toolkit/src/components/tabs/tabs.tsx index d20720556..69b8c3e1b 100644 --- a/libs/ui-toolkit/src/components/tabs/tabs.tsx +++ b/libs/ui-toolkit/src/components/tabs/tabs.tsx @@ -113,7 +113,9 @@ export const Tabs = ({ return ( {child.props.children} @@ -131,6 +133,7 @@ interface TabProps { name: string; indicator?: ReactNode; hidden?: boolean; + overflowHidden?: boolean; menu?: ReactNode; } diff --git a/libs/ui-toolkit/src/components/tooltip/tooltip.tsx b/libs/ui-toolkit/src/components/tooltip/tooltip.tsx index fdbae9814..1b3d8f30c 100644 --- a/libs/ui-toolkit/src/components/tooltip/tooltip.tsx +++ b/libs/ui-toolkit/src/components/tooltip/tooltip.tsx @@ -8,6 +8,7 @@ import { Portal, } from '@radix-ui/react-tooltip'; import type { ITooltipParams } from 'ag-grid-community'; +import classNames from 'classnames'; const tooltipContentClasses = 'max-w-sm bg-vega-light-100 dark:bg-vega-dark-100 border border-vega-light-200 dark:border-vega-dark-200 px-2 py-1 z-20 rounded text-xs text-black dark:text-white break-word'; @@ -18,10 +19,14 @@ export interface TooltipProps { align?: 'start' | 'center' | 'end'; side?: 'top' | 'right' | 'bottom' | 'left'; sideOffset?: number; + noUnderline?: boolean; } -export const TOOLTIP_TRIGGER_CLASS_NAME = - 'underline underline-offset-2 decoration-neutral-400 dark:decoration-neutral-400 decoration-dashed'; +export const TOOLTIP_TRIGGER_CLASS_NAME = (noUnderline?: boolean) => + classNames( + { 'underline underline-offset-2': !noUnderline }, + 'decoration-neutral-400 dark:decoration-neutral-400 decoration-dashed' + ); // Conditionally rendered tooltip if description content is provided. export const Tooltip = ({ @@ -31,11 +36,12 @@ export const Tooltip = ({ sideOffset, align = 'start', side = 'bottom', + noUnderline, }: TooltipProps) => description ? ( - + {children} {description && ( diff --git a/libs/ui-toolkit/src/components/trading-button/trading-button.tsx b/libs/ui-toolkit/src/components/trading-button/trading-button.tsx index add985aba..8ffe55cdc 100644 --- a/libs/ui-toolkit/src/components/trading-button/trading-button.tsx +++ b/libs/ui-toolkit/src/components/trading-button/trading-button.tsx @@ -6,6 +6,7 @@ import type { ReactNode, } from 'react'; import { Intent } from '../../utils/intent'; +import { Link } from 'react-router-dom'; type TradingButtonProps = { size?: 'large' | 'medium' | 'small' | 'extra-small'; @@ -39,17 +40,17 @@ const getClassName = ( }, // colours { - 'bg-vega-yellow hover:bg-vega-yellow-550 dark:bg-vega-yellow dark:hover:bg-vega-yellow-450': + 'bg-vega-yellow enabled:hover:bg-vega-yellow-550 dark:bg-vega-yellow dark:enabled:hover:bg-vega-yellow-450': intent === Intent.Primary, - 'bg-vega-clight-500 hover:bg-vega-clight-400 dark:bg-vega-cdark-500 dark:hover:bg-vega-cdark-400': + 'bg-vega-clight-500 enabled:hover:bg-vega-clight-400 dark:bg-vega-cdark-500 dark:enabled:hover:bg-vega-cdark-400': intent === Intent.None, - 'bg-vega-blue-350 hover:bg-vega-blue-400 dark:bg-vega-blue-650 dark:hover:bg-vega-blue-600': + 'bg-vega-blue-350 enabled:hover:bg-vega-blue-400 dark:bg-vega-blue-650 dark:enabled:hover:bg-vega-blue-600': intent === Intent.Info, - 'bg-vega-orange-350 hover:bg-vega-orange-400 dark:bg-vega-orange-650 dark:hover:bg-vega-orange-600': + 'bg-vega-orange-350 enabled:hover:bg-vega-orange-400 dark:bg-vega-orange-650 dark:enabled:hover:bg-vega-orange-600': intent === Intent.Warning, - 'bg-vega-red-350 hover:bg-vega-red-400 dark:bg-vega-red-650 dark:hover:bg-vega-red-600': + 'bg-vega-red-350 enabled:hover:bg-vega-red-400 dark:bg-vega-red-650 dark:enabled:hover:bg-vega-red-600': intent === Intent.Danger, - 'bg-vega-green-350 hover:bg-vega-green-400 dark:bg-vega-green-650 dark:hover:bg-vega-green-600': + 'bg-vega-green-350 enabled:hover:bg-vega-green-400 dark:bg-vega-green-650 dark:enabled:hover:bg-vega-green-600': intent === Intent.Success, 'text-vega-clight-50 dark:text-vega-cdark-50': intent !== Intent.Primary, 'text-vega-clight-900 dark:text-vega-cdark-900': @@ -73,7 +74,7 @@ const Content = ({ children, }: Pick) => ( <> - + {children} {icon} @@ -119,30 +120,22 @@ export const TradingButton = forwardRef< ) ); -export const TradingAnchorButton = forwardRef< - HTMLAnchorElement, - AnchorHTMLAttributes & TradingButtonProps ->( - ( - { - size = 'medium', - intent = Intent.None, - icon, - href, - children, - className, - subLabel, - ...props - }, - ref - ) => ( - - - - ) +export const TradingAnchorButton = ({ + size = 'medium', + intent = Intent.None, + icon, + href, + children, + className, + subLabel, + ...props +}: AnchorHTMLAttributes & + TradingButtonProps & { href: string }) => ( + + + ); diff --git a/libs/wallet/src/connect-dialog/connect-dialog.tsx b/libs/wallet/src/connect-dialog/connect-dialog.tsx index ab8050183..f96e5ecee 100644 --- a/libs/wallet/src/connect-dialog/connect-dialog.tsx +++ b/libs/wallet/src/connect-dialog/connect-dialog.tsx @@ -241,51 +241,49 @@ const ConnectorList = ({ 'Connect securely, deposit funds and approve or reject transactions with the Vega wallet' )}

-
-
- {isBrowserWalletInstalled() ? ( - onSelect('injected')} - title={ - <> - {t('Vega Wallet')} - {' '} - {t('full featured')} - - } - description={t( - `Connect Vega Wallet extension +
+ {isBrowserWalletInstalled() ? ( + onSelect('injected')} + title={ + <> + {t('Vega Wallet')} + {' '} + {t('full featured')} + + } + description={t( + `Connect Vega Wallet extension + for %s to access all features including key + management and detailed transaction views from your + browser.`, + [browserName] + )} + /> + ) : ( +
+

+ {t('Vega Wallet')} + {' '} + {t('full featured')} +

+

+ {t( + `Install Vega Wallet extension for %s to access all features including key management and detailed transaction views from your browser.`, [browserName] )} +

+ - ) : ( -
-

- {t('Vega Wallet')} - {' '} - {t('full featured')} -

-

- {t( - `Install Vega Wallet extension - for %s to access all features including key - management and detailed transaction views from your - browser.`, - [browserName] - )} -

- -
- )} -
+
+ )} {connectors['snap'] !== undefined ? (
{snapStatus === SnapStatus.INSTALLED ? ( @@ -335,7 +333,7 @@ const ConnectorList = ({ }} /> {snapStatus === SnapStatus.NOT_SUPPORTED ? ( -

+

{t('No MetaMask version that supports snaps detected.')}{' '} {t('Learn more about')}{' '} @@ -348,7 +346,7 @@ const ConnectorList = ({

) : null}
-

{t('Advanced / Other options...')}

+

{t('Advanced / Other options...')}

{title}

-

{description}

+

{description}

{t('Custom wallet location')}

onSelect('jsonRpc')} /> {isDesktopWalletRunning !== null && ( -

+

{isDesktopWalletRunning ? ( ) : ( - <> +

{t( 'No running Desktop App/CLI detected. Open your app now to connect or enter a' @@ -636,9 +637,9 @@ const CustomUrlInput = ({ > {t('custom wallet location')} - +

)} -

+
)} ); diff --git a/libs/wallet/src/connect-dialog/view-connector-form.tsx b/libs/wallet/src/connect-dialog/view-connector-form.tsx index 736e0ead7..4f3c8a791 100644 --- a/libs/wallet/src/connect-dialog/view-connector-form.tsx +++ b/libs/wallet/src/connect-dialog/view-connector-form.tsx @@ -88,10 +88,13 @@ export function ViewConnectorForm({
)} diff --git a/libs/web3/src/lib/use-ethereum-transaction-manager.spec.tsx b/libs/web3/src/lib/use-ethereum-transaction-manager.spec.tsx index 57f6421b1..761444a94 100644 --- a/libs/web3/src/lib/use-ethereum-transaction-manager.spec.tsx +++ b/libs/web3/src/lib/use-ethereum-transaction-manager.spec.tsx @@ -15,6 +15,7 @@ const requestedTransactionUpdate = { status: EthTxStatus.Requested, error: null, confirmations: 0, + notify: true, }; const mockDepositAsset = jest.fn(); @@ -57,6 +58,7 @@ const createTransaction = ( dialogOpen: false, txHash: null, receipt: null, + notify: true, ...transaction, }); @@ -158,6 +160,7 @@ describe('useVegaTransactionManager', () => { expect(update.mock.calls[1][1]).toEqual({ status: EthTxStatus.Pending, txHash, + notify: true, }); }); @@ -197,6 +200,7 @@ describe('useVegaTransactionManager', () => { expect(update.mock.calls[3][1]).toEqual({ status: EthTxStatus.Confirmed, receipt, + notify: true, }); }); diff --git a/libs/web3/src/lib/use-ethereum-transaction-manager.tsx b/libs/web3/src/lib/use-ethereum-transaction-manager.tsx index af1ce499e..8bcc136ae 100644 --- a/libs/web3/src/lib/use-ethereum-transaction-manager.tsx +++ b/libs/web3/src/lib/use-ethereum-transaction-manager.tsx @@ -25,6 +25,7 @@ export const useEthTransactionManager = () => { status: EthTxStatus.Requested, error: null, confirmations: 0, + notify: true, }); const { contract, @@ -48,6 +49,7 @@ export const useEthTransactionManager = () => { update(transaction.id, { status: EthTxStatus.Error, error: err as EthereumError, + notify: true, }); return; } @@ -61,6 +63,7 @@ export const useEthTransactionManager = () => { update(transaction.id, { status: EthTxStatus.Pending, txHash: tx.hash, + notify: true, }); for (let i = 1; i <= requiredConfirmations; i++) { @@ -77,19 +80,31 @@ export const useEthTransactionManager = () => { } if (requiresConfirmation) { - update(transaction.id, { status: EthTxStatus.Complete, receipt }); + update(transaction.id, { + status: EthTxStatus.Complete, + receipt, + }); } else { - update(transaction.id, { status: EthTxStatus.Confirmed, receipt }); + update(transaction.id, { + status: EthTxStatus.Confirmed, + receipt, + notify: true, + }); } } catch (err) { if (err instanceof Error || isEthereumError(err)) { if (!isExpectedEthereumError(err)) { - update(transaction.id, { status: EthTxStatus.Error, error: err }); + update(transaction.id, { + status: EthTxStatus.Error, + error: err, + notify: true, + }); } } else { update(transaction.id, { status: EthTxStatus.Error, error: new Error('Something went wrong'), + notify: true, }); } return; diff --git a/libs/web3/src/lib/use-ethereum-transaction-store.tsx b/libs/web3/src/lib/use-ethereum-transaction-store.tsx index 2647d4619..386a86044 100644 --- a/libs/web3/src/lib/use-ethereum-transaction-store.tsx +++ b/libs/web3/src/lib/use-ethereum-transaction-store.tsx @@ -27,7 +27,13 @@ export interface EthStoredTxState extends EthTxState { methodName: ContractMethod; args: string[]; requiredConfirmations: number; - requiresConfirmation: boolean; // whether or not the tx needs external confirmation (IE from a subscription even) + // whether or not the tx needs external confirmation (IE from a subscription even) + requiresConfirmation: boolean; + // whether or not to notify via toast + // true = force open toast + // false = force close toast + // undefined = leave alone + notify: boolean | undefined; assetId?: string; deposit?: DepositBusEventFieldsFragment; withdrawal?: WithdrawalBusEventFieldsFragment; @@ -49,7 +55,7 @@ export interface EthTransactionStore { update?: Partial< Pick< EthStoredTxState, - 'status' | 'error' | 'receipt' | 'confirmations' | 'txHash' + 'status' | 'error' | 'receipt' | 'confirmations' | 'txHash' | 'notify' > > ) => void; @@ -89,6 +95,7 @@ export const useEthTransactionStore = create()( requiresConfirmation, assetId, withdrawal, + notify: true, }; set({ transactions: transactions.concat(transaction) }); return transaction.id; @@ -135,6 +142,7 @@ export const useEthTransactionStore = create()( transaction.deposit = deposit; transaction.dialogOpen = true; transaction.updatedAt = new Date(); + transaction.notify = true; }) ); }, diff --git a/libs/web3/src/lib/use-ethereum-transaction-toasts.tsx b/libs/web3/src/lib/use-ethereum-transaction-toasts.tsx index f0e49b482..84e4d0e1f 100644 --- a/libs/web3/src/lib/use-ethereum-transaction-toasts.tsx +++ b/libs/web3/src/lib/use-ethereum-transaction-toasts.tsx @@ -1,4 +1,5 @@ import type { ReactNode } from 'react'; +import { useEffect } from 'react'; import { useAssetsDataProvider } from '@vegaprotocol/assets'; import { EtherscanLink } from '@vegaprotocol/environment'; import { formatNumber, toBigNum } from '@vegaprotocol/utils'; @@ -169,11 +170,13 @@ export const useEthereumTransactionToasts = () => { ]); const dismissTx = useEthTransactionStore((state) => state.dismiss); + const updateTx = useEthTransactionStore((state) => state.update); const onClose = useCallback( (tx: EthStoredTxState) => () => { dismissTx(tx.id); removeToast(`eth-${tx.id}`); + updateTx(tx.id, { notify: false }); // closes related "Funds released" toast after successful withdrawal if ( isWithdrawTransaction(tx) && @@ -183,7 +186,7 @@ export const useEthereumTransactionToasts = () => { closeToastBy({ withdrawalId: tx.withdrawal.id }); } }, - [closeToastBy, dismissTx, removeToast] + [closeToastBy, dismissTx, removeToast, updateTx] ); const fromEthTransaction = useCallback( @@ -213,17 +216,25 @@ export const useEthereumTransactionToasts = () => { loader: [EthTxStatus.Pending, EthTxStatus.Complete].includes(tx.status), content, closeAfter, + hidden: !tx.notify, }; }, [onClose] ); - useEthTransactionStore.subscribe( - (state) => compact(state.transactions.filter((tx) => tx?.dialogOpen)), - (txs) => { - txs.forEach((tx) => { - setToast(fromEthTransaction(tx)); - }); - } - ); + // Only register a subscription once + useEffect(() => { + const unsubscribe = useEthTransactionStore.subscribe( + (state) => compact(state.transactions.filter((tx) => tx?.dialogOpen)), + (txs) => { + txs.forEach((tx) => { + setToast(fromEthTransaction(tx)); + }); + } + ); + + return () => { + unsubscribe(); + }; + }, [fromEthTransaction, setToast]); }; diff --git a/package.json b/package.json index 20c092dc0..29292e363 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "jsondiffpatch": "^0.4.1", "lodash": "^4.17.21", "next": "13.3.0", - "pennant": "1.11.1", + "pennant": "1.12.0", "react": "18.2.0", "react-copy-to-clipboard": "^5.0.4", "react-dom": "18.2.0", diff --git a/yarn.lock b/yarn.lock index 5176b3eea..c16018fa1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20480,10 +20480,10 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== -pennant@1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/pennant/-/pennant-1.11.1.tgz#f47bceaade01db215eeba666cd755840d16fe9ed" - integrity sha512-U26OxjxETWLJAvCFj20oH0y5gzyBfJ5oD4O3dYg1aWnH4giUYDPWuUDFnzlMJ+yXolyYECccXm6S1BkoVcVzDg== +pennant@1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/pennant/-/pennant-1.12.0.tgz#e12707d5f1aac554d81bad060637e608335e0b50" + integrity sha512-xosg5erRf+Ke9iORdqyv+SOGcD3uJX1dgf990q1DvHuzz36w2txCZsfnvcXhRO++HYVKS9sU/YLPRLJgolFGtA== dependencies: "@babel/runtime" "^7.13.10" "@d3fc/d3fc-technical-indicator" "^8.0.1"