Compare commits

..
Author SHA1 Message Date
Matthew Russell 6178faf36f fix: incorrect argument for renderComponent helper 2024-01-18 10:02:39 -05:00
Matthew Russell af6b691405 fix: reset tx state to idle if an error is thrown 2024-01-17 18:55:18 -05:00
Matthew Russell 3bd9fb8ce1 fix: align header text with data 2024-01-17 18:47:51 -05:00
Matthew Russell 0af61ea04b fix: collapsing header on members table 2024-01-17 18:47:12 -05:00
Matthew Russell 22e9d8a6ae chore: align competitions grid card cta 2024-01-17 18:40:02 -05:00
Matthew Russell 49b16b6654 chore: move layouts into appropriate dirs 2024-01-17 17:05:48 -05:00
Matthew Russell a706633192 chore: left align no games text 2024-01-17 17:05:48 -05:00
Matthew Russell 8fa64e93fb feat(trading): join team (#5629) 2024-01-17 22:04:51 +00:00
Matthew Russell 6b5fa440fb feat(trading): create team (#5610) 2024-01-17 10:44:14 -05:00
asiaznik 0894854d3e chore: fallback team avatars 2024-01-17 10:44:14 -05:00
asiaznik e59b2e049f chore: team avatar sizes 2024-01-17 10:44:14 -05:00
asiaznik e0f04ab1b4 chore: sky layout, blurred layout 2024-01-17 10:44:14 -05:00
asiaznik 4484bdf0e0 chore: married with epic branch 2024-01-17 10:44:14 -05:00
asiaznik 316c31ee5d chore(trading): competitions home page
feat(trading): competitions
2024-01-17 10:44:14 -05:00
Matthew Russell 398fda6245 Feat/5486 team page (#5620) 2024-01-17 10:44:13 -05:00
Matthew Russell 47aff2a24d chore: tidy handling of proposal union type 2024-01-17 10:43:47 -05:00
Matthew Russell cadf9509e7 chore: handle proposal union type 2024-01-17 10:43:47 -05:00
Matthew Russell 85489a6a6e chore: fix ts errors in trading app 2024-01-17 10:43:47 -05:00
Matthew Russell e00d933c84 chore: fix ts errors in candles-chart 2024-01-17 10:43:47 -05:00
Matthew Russell 799a95e7a1 chore: fix type errors in market lib 2024-01-17 10:43:47 -05:00
Matthew Russell 01c03377e2 chore: fix type errors in proposals lib 2024-01-17 10:43:47 -05:00
Matthew Russell 3ef3ac9cd2 chore: fix global type mappings 2024-01-17 10:43:47 -05:00
Matthew Russell 18e461ecea chore: fix type issues 2024-01-17 10:43:47 -05:00
Matthew Russell 7fa1f1ec5c chore: use extract to narrow type 2024-01-17 10:43:47 -05:00
Matthew Russell 4e13ad52a9 chore: regen types 2024-01-17 10:43:45 -05:00
1059 changed files with 22110 additions and 50404 deletions
+1 -2
View File
@@ -196,9 +196,9 @@ jobs:
cypress: cypress:
needs: [build-sources, check-e2e-needed] needs: [build-sources, check-e2e-needed]
name: '(CI) cypress' name: '(CI) cypress'
if: ${{ needs.check-e2e-needed.outputs.run-tests == 'true' }}
uses: ./.github/workflows/cypress-run.yml uses: ./.github/workflows/cypress-run.yml
secrets: inherit secrets: inherit
if: needs.check-e2e-needed.outputs.run-tests == 'true' && (contains(needs.build-sources.outputs.projects, 'governance') || contains(needs.build-sources.outputs.projects, 'explorer'))
with: with:
projects: ${{ needs.build-sources.outputs.projects-e2e }} projects: ${{ needs.build-sources.outputs.projects-e2e }}
tags: '@smoke' tags: '@smoke'
@@ -287,7 +287,6 @@ jobs:
steps: steps:
- run: | - run: |
result="${{ needs.cypress.result }}" result="${{ needs.cypress.result }}"
echo "Result: $result"
if [[ $result == "success" || $result == "skipped" ]]; then if [[ $result == "success" || $result == "skipped" ]]; then
exit 0 exit 0
else else
+4 -4
View File
@@ -10,7 +10,7 @@ on:
inputs: inputs:
console-test-branch: console-test-branch:
type: choice type: choice
description: 'main: v0.73.13, develop: v0.74.0' description: 'main: v0.73.5, develop: v0.73.5'
options: options:
- main - main
- develop - develop
@@ -19,7 +19,7 @@ jobs:
create-docker-image: create-docker-image:
name: Create docker image for console-test name: Create docker image for console-test
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
timeout-minutes: 45 timeout-minutes: 90
steps: steps:
#---------------------------------------------- #----------------------------------------------
# check-out frontend-monorepo # check-out frontend-monorepo
@@ -138,7 +138,7 @@ jobs:
name: run-tests name: run-tests
runs-on: 8-cores runs-on: 8-cores
needs: [create-docker-image, console-test-branch] needs: [create-docker-image, console-test-branch]
timeout-minutes: 45 timeout-minutes: 90
steps: steps:
#---------------------------------------------- #----------------------------------------------
# load docker image # load docker image
@@ -205,7 +205,7 @@ jobs:
# run tests # run tests
#---------------------------------------------- #----------------------------------------------
- name: Run tests - name: Run tests
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v --numprocesses 4 --dist loadfile --durations=45 run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 2 --dist loadfile --durations=90
working-directory: apps/trading/e2e working-directory: apps/trading/e2e
#---------------------------------------------- #----------------------------------------------
# upload traces # upload traces
+36
View File
@@ -0,0 +1,36 @@
name: Cypress Console tests -- live environment
# This workflow runs using provided url
on:
workflow_dispatch:
inputs:
url:
description: 'Url'
required: true
type: string
jobs:
cypress-run:
name: Run Cypress Trading tests -- live environment
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node.js 20
id: Node
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Run Cypress tests
uses: cypress-io/github-action@v4
with:
browser: chrome
record: true
project: ./apps/trading-e2e
config: baseUrl=${{ github.event.inputs.url }}
env: grepTags=@live
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -12,6 +12,7 @@ on:
options: options:
- explorer-e2e - explorer-e2e
- governance-e2e - governance-e2e
- trading-e2e
tags: tags:
description: 'Test tags to run' description: 'Test tags to run'
required: true required: true
+1 -1
View File
@@ -10,5 +10,5 @@ jobs:
uses: ./.github/workflows/cypress-run.yml uses: ./.github/workflows/cypress-run.yml
secrets: inherit secrets: inherit
with: with:
projects: '["explorer-e2e","governance-e2e"]' projects: '["explorer-e2e","governance-e2e","trading-e2e"]'
tags: '@smoke @regression @slow' tags: '@smoke @regression @slow'
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
rm package.json rm package.json
npm install --no-save @commitlint/cli@16.3.0 @commitlint/config-conventional@18.6.1 @commitlint/config-nx-scopes@18.6.1 nx@17.1.2 npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx
- name: Check PR title - name: Check PR title
run: echo "${{ github.event.pull_request.title }}" | npx @commitlint/cli@16.3.0 --config ./commitlint.config-ci.js run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
-28
View File
@@ -1,28 +0,0 @@
# path to a directory with all packages
storage: ../tmp/local-registry/storage
# a list of other known repositories we can talk to
uplinks:
npmjs:
url: https://registry.yarnpkg.com
maxage: 60m
packages:
'**':
# give all users (including non-authenticated users) full access
# because it is a local registry
access: $all
publish: $all
unpublish: $all
# if package is not available locally, proxy requests to npm registry
proxy: npmjs
# log settings
logs:
type: stdout
format: pretty
level: warn
publish:
allow_offline: true # set offline to true to allow publish offline
+6 -6
View File
@@ -4,7 +4,7 @@ The front-end monorepo provides a toolkit for building apps that interact with V
This repository is managed using [Nx](https://nx.dev). This repository is managed using [Nx](https://nx.dev).
## 🔎 Applications in this repo # 🔎 Applications in this repo
### [Block explorer](./apps/explorer) ### [Block explorer](./apps/explorer)
@@ -30,7 +30,7 @@ Hosting for static content being shared across apps, for example fonts.
The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract. The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract.
## 🧱 Libraries in this repo # 🧱 Libraries in this repo
### [UI toolkit](./libs/ui-toolkit) ### [UI toolkit](./libs/ui-toolkit)
@@ -53,7 +53,7 @@ A utility library for connecting to the Ethereum network and interacting with Ve
Generic react helpers that can be used across multiple applications, along with other utilities. Generic react helpers that can be used across multiple applications, along with other utilities.
## 💻 Develop # 💻 Develop
### Set up ### Set up
@@ -103,7 +103,7 @@ In CI linting, formatting and also run. These checks can be seen in the [CI work
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more. Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
## 🐋 Hosting a console # 🐋 Hosting a console
To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions). To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions).
@@ -136,7 +136,7 @@ The [`docker`](./docker) subfolder has some docker configurations for easily set
Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build). The multistage builds ensures consistent CPU architecture and build toolchains are used so that the result will be identical. Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build). The multistage builds ensures consistent CPU architecture and build toolchains are used so that the result will be identical.
```bash ```bash
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=20.11 --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile . docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=20.9.1 --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
``` ```
### Computing ipfs-hash of the build ### Computing ipfs-hash of the build
@@ -226,6 +226,6 @@ Note: The script is only needed if capsule was built for first time or fresh. To
vega wallet service run -n DV --load-tokens --tokens-passphrase-file passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet vega wallet service run -n DV --load-tokens --tokens-passphrase-file passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet
``` ```
## 📑 License # 📑 License
[MIT](./LICENSE) [MIT](./LICENSE)
@@ -1,7 +1,7 @@
import { getNewAssetTxBody } from '../support/governance.functions'; import { getNewAssetTxBody } from '../support/governance.functions';
context('Proposal page', { tags: '@smoke' }, function () { context('Proposal page', { tags: '@smoke' }, function () {
describe.skip('Verify elements on page', function () { describe('Verify elements on page', function () {
const proposalHeading = 'proposals-heading'; const proposalHeading = 'proposals-heading';
const dateTimeRegex = const dateTimeRegex =
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm; /(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
@@ -24,6 +24,10 @@ context('Proposal page', { tags: '@smoke' }, function () {
cy.get_element_by_col_id('title').should('have.text', proposalTitle); cy.get_element_by_col_id('title').should('have.text', proposalTitle);
cy.get_element_by_col_id('type').should('have.text', 'NewMarket'); cy.get_element_by_col_id('type').should('have.text', 'NewMarket');
cy.get_element_by_col_id('state').should('have.text', 'Enacted'); cy.get_element_by_col_id('state').should('have.text', 'Enacted');
cy.getByTestId('vote-progress').should('be.visible');
cy.getByTestId('vote-progress-bar-for')
.invoke('attr', 'style')
.should('eq', 'width: 100%;');
cy.get('[col-id="cDate"]') cy.get('[col-id="cDate"]')
.invoke('text') .invoke('text')
.should('match', dateTimeRegex); .should('match', dateTimeRegex);
@@ -69,6 +73,10 @@ context('Proposal page', { tags: '@smoke' }, function () {
'have.text', 'have.text',
'Waiting for Node Vote' 'Waiting for Node Vote'
); );
cy.getByTestId('vote-progress').should('be.visible');
cy.getByTestId('vote-progress-bar-against')
.invoke('attr', 'style')
.should('eq', 'width: 100%;');
cy.get('[col-id="cDate"]') cy.get('[col-id="cDate"]')
.invoke('text') .invoke('text')
.should('match', dateTimeRegex); .should('match', dateTimeRegex);
+2
View File
@@ -1,4 +1,5 @@
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.rocks NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.rocks
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1 NX_VEGA_ENV=STAGNET1
@@ -8,6 +9,7 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.rocks/rest NX_BLOCK_EXPLORER=https://be.stagnet1.vega.rocks/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -1,5 +1,6 @@
# App configuration variables # App configuration variables
NX_VEGA_ENV=CUSTOM 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_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_VEGA_EXPLORER_URL=/ NX_VEGA_EXPLORER_URL=/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
+1
View File
@@ -2,6 +2,7 @@
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_VEGA_ENV=DEVNET NX_VEGA_ENV=DEVNET
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://dev.governance.vega.xyz NX_VEGA_GOVERNANCE_URL=https://dev.governance.vega.xyz
NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql 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_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
+1
View File
@@ -4,6 +4,7 @@ NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/maste
NX_VEGA_URL=https://api.vega.community/graphql NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.vega.community/rest NX_BLOCK_EXPLORER=https://be.vega.community/rest
NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/ NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
+1
View File
@@ -4,6 +4,7 @@ NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-inter
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
NX_VEGA_ENV=MAINNET_MIRROR NX_VEGA_ENV=MAINNET_MIRROR
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json 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_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/
+1
View File
@@ -4,6 +4,7 @@ NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-inter
NX_VEGA_ENV=TESTNET NX_VEGA_ENV=TESTNET
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
+1
View File
@@ -4,6 +4,7 @@ NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/maste
NX_VEGA_URL=https://api-validators-testnet.vega.rocks/graphql NX_VEGA_URL=https://api-validators-testnet.vega.rocks/graphql
NX_VEGA_REST=https://api-validators-testnet.vega.rocks/ NX_VEGA_REST=https://api-validators-testnet.vega.rocks/
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
+1
View File
@@ -3,6 +3,7 @@ NX_TENDERMINT_URL=http://localhost:26607/
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket
NX_VEGA_ENV=CUSTOM NX_VEGA_ENV=CUSTOM
NX_BLOCK_EXPLORER= NX_BLOCK_EXPLORER=
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_VEGA_EXPLORER_URL=/ NX_VEGA_EXPLORER_URL=/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
@@ -7,7 +7,6 @@ export type AssetBalanceProps = {
price: string; price: string;
showAssetLink?: boolean; showAssetLink?: boolean;
showAssetSymbol?: boolean; showAssetSymbol?: boolean;
rounded?: boolean;
}; };
/** /**
@@ -19,17 +18,12 @@ const AssetBalance = ({
price, price,
showAssetLink = true, showAssetLink = true,
showAssetSymbol = false, showAssetSymbol = false,
rounded = false,
}: AssetBalanceProps) => { }: AssetBalanceProps) => {
const { data: asset, loading } = useAssetDataProvider(assetId); const { data: asset, loading } = useAssetDataProvider(assetId);
const label = const label =
!loading && asset && asset.decimals !loading && asset && asset.decimals
? addDecimalsFixedFormatNumber( ? addDecimalsFixedFormatNumber(price, asset.decimals)
price,
asset.decimals,
rounded ? 0 : undefined
)
: price; : price;
return ( return (
@@ -41,7 +41,6 @@ export const Header = () => {
Routes.ASSETS, Routes.ASSETS,
Routes.MARKETS, Routes.MARKETS,
Routes.GOVERNANCE, Routes.GOVERNANCE,
Routes.TREASURY,
Routes.NETWORK_PARAMETERS, Routes.NETWORK_PARAMETERS,
Routes.GENESIS, Routes.GENESIS,
].map((n) => pages.find((r) => r.path === n)) ].map((n) => pages.find((r) => r.path === n))
@@ -0,0 +1,34 @@
import React from 'react';
import { DATA_SOURCES } from '../../../config';
import Hash from '../hash';
export enum EthExplorerLinkTypes {
block = 'block',
address = 'address',
tx = 'tx',
}
export type EthExplorerLinkProps = Partial<typeof HTMLAnchorElement> & {
id: string;
type: EthExplorerLinkTypes;
};
export const EthExplorerLink = ({
id,
type,
...props
}: EthExplorerLinkProps) => {
const link = `${DATA_SOURCES.ethExplorerUrl}/${type}/${id}`;
return (
<a
className="underline external font-mono"
target="_blank"
rel="noopener noreferrer"
{...props}
href={link}
>
<Hash text={id} />
</a>
);
};
@@ -1,34 +0,0 @@
import type { ChainIdMapping } from './external-chain';
import { SUPPORTED_CHAIN_IDS, SUPPORTED_CHAIN_LABELS } from './external-chain';
export const SUPPORTED_CHAIN_ICON_URLS: ChainIdMapping = {
'1': '/assets/chain-eth-logo.svg',
'100': '/assets/chain-gno-logo.svg',
'42161': '/assets/chain-arb-logo.svg',
'11155111': '/assets/chain-eth-logo.svg',
};
export type ExternalChainIconProps = {
chainId?: string;
};
export const ExternalChainIcon = ({
// If chainID is not provided, default to a non-existent chain
chainId = '-1',
}: ExternalChainIconProps) => {
if (SUPPORTED_CHAIN_IDS.includes(chainId)) {
const url = SUPPORTED_CHAIN_ICON_URLS[chainId];
const alt = SUPPORTED_CHAIN_LABELS[chainId];
return (
<img
src={url}
className="inline-block w-4 h-4 mr-1 dark:invert"
alt={alt}
title={alt}
/>
);
} else {
return null;
}
};
@@ -1,38 +0,0 @@
export type ChainIdMapping = {
[K in typeof SUPPORTED_CHAIN_IDS[number]]: string;
};
export const SUPPORTED_CHAIN_IDS: string[] = ['1', '100', '42161', '11155111'];
export const SUPPORTED_CHAIN_LABELS: ChainIdMapping = {
'1': 'Ethereum',
'100': 'Gnosis',
'42161': 'Arbitrum',
'11155111': 'Sepolia',
};
export function getExternalExplorerLink(chainId: string, type: string) {
if (SUPPORTED_CHAIN_IDS.includes(chainId)) {
switch (chainId) {
case '1':
return 'https://etherscan.io';
case '100':
return 'https://gnosisscan.io';
case '42161':
return 'https://arbiscan.io';
case '11155111':
return 'https://sepolia.etherscan.io';
default:
return '#';
}
} else {
return '#';
}
}
export function getExternalChainLabel(chainId?: string) {
if (chainId && SUPPORTED_CHAIN_IDS.includes(chainId)) {
return SUPPORTED_CHAIN_LABELS[chainId];
} else {
return 'Custom Chain';
}
}
@@ -1,41 +0,0 @@
import Hash from '../hash';
import { getExternalExplorerLink } from './external-chain';
import { ExternalChainIcon } from './external-chain-icon';
export enum EthExplorerLinkTypes {
block = 'block',
address = 'address',
tx = 'tx',
}
export type ExternalExplorerLinkProps = Partial<typeof HTMLAnchorElement> & {
id: string;
type: EthExplorerLinkTypes;
// Defaults to Ethereum Mainnet, as chain support was added late
chain?: string;
code?: boolean;
};
export const ExternalExplorerLink = ({
id,
type,
chain = '1',
code = false,
...props
}: ExternalExplorerLinkProps) => {
const link = `${getExternalExplorerLink(chain, type)}/${type}/${id}${
code ? '#code' : ''
}`;
return (
<a
className="underline external font-mono"
target="_blank"
rel="noopener noreferrer"
{...props}
href={link}
>
<ExternalChainIcon chainId={chain} />
<Hash text={id} />
</a>
);
};
@@ -1,4 +1,4 @@
export type HashProps = React.HTMLProps<HTMLSpanElement> & { export type HashProps = {
text: string; text: string;
truncate?: boolean; truncate?: boolean;
}; };
@@ -2,5 +2,4 @@ export { default as BlockLink } from './block-link/block-link';
export { default as PartyLink } from './party-link/party-link'; export { default as PartyLink } from './party-link/party-link';
export { default as NodeLink } from './node-link/node-link'; export { default as NodeLink } from './node-link/node-link';
export { default as MarketLink } from './market-link/market-link'; export { default as MarketLink } from './market-link/market-link';
export { default as NetworkParameterLink } from './network-parameter-link/network-parameter-link';
export * from './asset-link/asset-link'; export * from './asset-link/asset-link';
@@ -1,30 +0,0 @@
import React from 'react';
import { Routes } from '../../../routes/route-names';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
import Hash from '../hash';
export type NetworkParameterLinkProps = Partial<ComponentProps<typeof Link>> & {
parameter: string;
};
/**
* Links a given network parameter to the relevant page and anchor on the page
*/
const NetworkParameterLink = ({
parameter,
...props
}: NetworkParameterLinkProps) => {
return (
<Link
className="underline"
{...props}
to={`/${Routes.NETWORK_PARAMETERS}#${parameter}`}
>
<Hash text={parameter} />
</Link>
);
};
export default NetworkParameterLink;
@@ -32,17 +32,9 @@ export function getNameForParty(id: string, data?: ExplorerNodeNamesQuery) {
export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & { export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string; id: string;
truncate?: boolean; truncate?: boolean;
networkLabel?: string;
truncateLength?: number;
}; };
const PartyLink = ({ const PartyLink = ({ id, truncate = false, ...props }: PartyLinkProps) => {
id,
truncate = false,
truncateLength = 4,
networkLabel = t('Network'),
...props
}: PartyLinkProps) => {
const { data } = useExplorerNodeNamesQuery(); const { data } = useExplorerNodeNamesQuery();
const name = useMemo(() => getNameForParty(id, data), [data, id]); const name = useMemo(() => getNameForParty(id, data), [data, id]);
const useName = name !== id; const useName = name !== id;
@@ -52,7 +44,7 @@ const PartyLink = ({
if (id === SPECIAL_CASE_NETWORK || id === SPECIAL_CASE_NETWORK_ID) { if (id === SPECIAL_CASE_NETWORK || id === SPECIAL_CASE_NETWORK_ID) {
return ( return (
<span className="font-mono" data-testid="network"> <span className="font-mono" data-testid="network">
{networkLabel} {t('Network')}
</span> </span>
); );
} }
@@ -78,11 +70,7 @@ const PartyLink = ({
{useName ? ( {useName ? (
name name
) : ( ) : (
<Hash <Hash text={truncate ? truncateMiddle(id, 4, 4) : id} />
text={
truncate ? truncateMiddle(id, truncateLength, truncateLength) : id
}
/>
)} )}
</Link> </Link>
</span> </span>
@@ -3,11 +3,7 @@ import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing'; import type { MockedResponse } from '@apollo/client/testing';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import ProposalLink from './proposal-link'; import ProposalLink from './proposal-link';
import { import { ExplorerProposalDocument } from './__generated__/Proposal';
ExplorerProposalDocument,
type ExplorerProposalQuery,
type ExplorerProposalQueryVariables,
} from './__generated__/Proposal';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
function renderComponent(id: string, mocks: MockedResponse[]) { function renderComponent(id: string, mocks: MockedResponse[]) {
@@ -27,10 +23,7 @@ describe('Proposal link component', () => {
}); });
it('Renders the ID on error', async () => { it('Renders the ID on error', async () => {
const mock: MockedResponse< const mock = {
ExplorerProposalQuery,
ExplorerProposalQueryVariables
> = {
request: { request: {
query: ExplorerProposalDocument, query: ExplorerProposalDocument,
variables: { variables: {
@@ -47,22 +40,17 @@ describe('Proposal link component', () => {
}); });
it('Renders the proposal title when the query returns a result', async () => { it('Renders the proposal title when the query returns a result', async () => {
const proposalId = '123'; const mock = {
const mock: MockedResponse<
ExplorerProposalQuery,
ExplorerProposalQueryVariables
> = {
request: { request: {
query: ExplorerProposalDocument, query: ExplorerProposalDocument,
variables: { variables: {
id: proposalId, id: '123',
}, },
}, },
result: { result: {
data: { data: {
proposal: { proposal: {
__typename: 'Proposal', id: '123',
id: proposalId,
rationale: { rationale: {
title: 'test-title', title: 'test-title',
description: 'test description', description: 'test description',
@@ -72,16 +60,13 @@ describe('Proposal link component', () => {
}, },
}; };
const res = render(renderComponent(proposalId, [mock])); const res = render(renderComponent('123', [mock]));
expect(res.getByText(proposalId)).toBeInTheDocument(); expect(res.getByText('123')).toBeInTheDocument();
expect(await res.findByText('test-title')).toBeInTheDocument(); expect(await res.findByText('test-title')).toBeInTheDocument();
}); });
it('Leaves the proposal id when the market is not found', async () => { it('Leaves the proposal id when the market is not found', async () => {
const mock: MockedResponse< const mock = {
ExplorerProposalQuery,
ExplorerProposalQueryVariables
> = {
request: { request: {
query: ExplorerProposalDocument, query: ExplorerProposalDocument,
variables: { variables: {
@@ -26,7 +26,7 @@ const ProposalLink = ({ id, text }: ProposalLinkProps) => {
>; >;
const base = ENV.dataSources.governanceUrl; const base = ENV.dataSources.governanceUrl;
const label = proposal?.rationale?.title || id; const label = proposal?.rationale.title || id;
return ( return (
<ExternalLink href={`${base}/proposals/${id}`}> <ExternalLink href={`${base}/proposals/${id}`}>
@@ -1,7 +1,6 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/markets'; import type { MarketInfoWithData } from '@vegaprotocol/markets';
import { import {
LiquidationStrategyInfoPanel,
LiquidityPriceRangeInfoPanel, LiquidityPriceRangeInfoPanel,
LiquiditySLAParametersInfoPanel, LiquiditySLAParametersInfoPanel,
MarginScalingFactorsPanel, MarginScalingFactorsPanel,
@@ -95,8 +94,6 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
</> </>
) )
)} )}
<h2 className={headerClassName}>{t('Liquidation strategy')}</h2>
<LiquidationStrategyInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity monitoring')}</h2> <h2 className={headerClassName}>{t('Liquidity monitoring')}</h2>
<LiquidityMonitoringParametersInfoPanel market={market} /> <LiquidityMonitoringParametersInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity price range')}</h2> <h2 className={headerClassName}>{t('Liquidity price range')}</h2>
@@ -1,5 +1,9 @@
import { render, waitFor } from '@testing-library/react'; import { render, waitFor } from '@testing-library/react';
import { NestedDataList, sortNestedDataByChildren } from './nested-data-list'; import {
BORDER_COLOURS,
NestedDataList,
sortNestedDataByChildren,
} from './nested-data-list';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
const mockData = { const mockData = {
@@ -57,6 +61,38 @@ describe('NestedDataList', () => {
expect(parent[0].querySelector('li')).toHaveClass('pl-4 border-l-4 pt-2'); expect(parent[0].querySelector('li')).toHaveClass('pl-4 border-l-4 pt-2');
}); });
it('should repeat the border colours in the correct order', () => {
const colourMockData = {
t0: {
t1: {
t2: {
t3: {
t4: {
t5: {
t6: {
t7: {
t8: {
hello: 'world',
},
},
},
},
},
},
},
},
},
};
const tree = render(<NestedDataList data={colourMockData} />);
const { getByTestId } = tree;
for (let i = 0; i < 8; i++) {
const item = getByTestId(`T${i}`);
const expected = BORDER_COLOURS.light[i % 5];
expect(item.style.borderColor.toUpperCase()).toBe(expected);
}
});
it('should sort the data by values with children', () => { it('should sort the data by values with children', () => {
const mockData = { const mockData = {
nonce: '5980890939790185837', nonce: '5980890939790185837',
@@ -175,7 +175,6 @@ describe('Amend order details', () => {
const res = renderExistingAmend('123', 1, amend); const res = renderExistingAmend('123', 1, amend);
expect(await res.findByText('New size')).toBeInTheDocument(); expect(await res.findByText('New size')).toBeInTheDocument();
expect(await res.findByText('Size ±')).toBeInTheDocument();
}); });
it('Renders Reference if provided', async () => { it('Renders Reference if provided', async () => {
@@ -82,7 +82,7 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
{amend.sizeDelta && amend.sizeDelta !== '0' ? ( {amend.sizeDelta && amend.sizeDelta !== '0' ? (
<div className="mb-12 md:mb-0"> <div className="mb-12 md:mb-0">
<h2 className="text-dark mb-4 text-2xl font-bold"> <h2 className="text-dark mb-4 text-2xl font-bold">
{t('Size ±')} {t('New size')}
</h2> </h2>
<h5 <h5
className={`mb-0 text-lg font-medium capitalize text-gray-500 ${getSideDeltaColour( className={`mb-0 text-lg font-medium capitalize text-gray-500 ${getSideDeltaColour(
@@ -93,16 +93,6 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
</h5> </h5>
</div> </div>
) : null} ) : null}
{o && (
<div className="">
<h2 className="text-dark mb-4 text-2xl font-bold">
{t('New size')}
</h2>
<h5 className="mb-0 text-lg font-medium text-gray-500">
{o ? o.size : null}
</h5>
</div>
)}
{amend.price && amend.price !== '0' ? ( {amend.price && amend.price !== '0' ? (
<div className=""> <div className="">
@@ -1,4 +1,5 @@
import { type ProposalListFieldsFragment } from '@vegaprotocol/proposals'; import { type ProposalListFieldsFragment } from '@vegaprotocol/proposals';
import { VoteProgress } from '@vegaprotocol/proposals';
import { type AgGridReact } from 'ag-grid-react'; import { type AgGridReact } from 'ag-grid-react';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
@@ -11,7 +12,12 @@ import { type ColDef } from 'ag-grid-community';
import type { RowClickedEvent } from 'ag-grid-community'; import type { RowClickedEvent } from 'ag-grid-community';
import { getDateTimeFormat } from '@vegaprotocol/utils'; import { getDateTimeFormat } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { ProposalStateMapping } from '@vegaprotocol/types'; import { ProposalStateMapping } from '@vegaprotocol/types';
import BigNumber from 'bignumber.js';
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment'; import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
import { BREAKPOINT_MD } from '../../config/breakpoints'; import { BREAKPOINT_MD } from '../../config/breakpoints';
import { JsonViewerDialog } from '../dialogs/json-viewer-dialog'; import { JsonViewerDialog } from '../dialogs/json-viewer-dialog';
@@ -25,7 +31,15 @@ type ProposalsTableProps = {
data: ProposalListFieldsFragment[] | null; data: ProposalListFieldsFragment[] | null;
}; };
export const ProposalsTable = ({ data }: ProposalsTableProps) => { export const ProposalsTable = ({ data }: ProposalsTableProps) => {
const { params } = useNetworkParams([
NetworkParams.governance_proposal_market_requiredMajority,
]);
const tokenLink = useLinks(DApp.Governance); const tokenLink = useLinks(DApp.Governance);
const requiredMajorityPercentage = useMemo(() => {
const requiredMajority =
params?.governance_proposal_market_requiredMajority ?? 1;
return new BigNumber(requiredMajority).times(100);
}, [params?.governance_proposal_market_requiredMajority]);
const gridRef = useRef<AgGridReact>(null); const gridRef = useRef<AgGridReact>(null);
useLayoutEffect(() => { useLayoutEffect(() => {
@@ -76,6 +90,33 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
return value ? ProposalStateMapping[value] : '-'; return value ? ProposalStateMapping[value] : '-';
}, },
}, },
{
colId: 'voting',
maxWidth: 100,
hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Voting'),
cellRenderer: ({
data,
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
if (data) {
const yesTokens = new BigNumber(data.votes.yes.totalTokens);
const noTokens = new BigNumber(data.votes.no.totalTokens);
const totalTokensVoted = yesTokens.plus(noTokens);
const yesPercentage = totalTokensVoted.isZero()
? new BigNumber(0)
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
return (
<div className="flex h-full items-center justify-center pt-2 uppercase">
<VoteProgress
threshold={requiredMajorityPercentage}
progress={yesPercentage}
/>
</div>
);
}
return '-';
},
},
{ {
colId: 'cDate', colId: 'cDate',
maxWidth: 150, maxWidth: 150,
@@ -143,7 +184,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
}, },
}, },
], ],
[tokenLink] [requiredMajorityPercentage, tokenLink]
); );
return ( return (
<> <>
@@ -1,10 +1,9 @@
import { TableCell, TableRow } from '../../../table'; import { TableCell, TableRow } from '../../../table';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
import { getExternalChainLabel } from '../../../links/external-explorer-link/external-chain';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { defaultAbiCoder, base64 } from 'ethers/lib/utils'; import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
import { BigNumber } from 'ethers'; import { BigNumber } from 'ethers';
@@ -45,10 +44,10 @@ export const TxDetailsContractCall = ({
}, },
}); });
if (!contractCall) { if (!contractCall || !contractCall.result) {
return null; return null;
} }
const chainLabel = getExternalChainLabel(contractCall.sourceChainId);
return ( return (
<> <>
{contractCall.specId && ( {contractCall.specId && (
@@ -65,12 +64,9 @@ export const TxDetailsContractCall = ({
)} )}
{contractCall.blockHeight && ( {contractCall.blockHeight && (
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('ETH block')}</TableCell>
<TableCell> <TableCell>
{chainLabel} {t('block')} <EthExplorerLink
</TableCell>
<TableCell>
<ExternalExplorerLink
chain={contractCall.sourceChainId}
id={contractCall.blockHeight} id={contractCall.blockHeight}
type={EthExplorerLinkTypes.block} type={EthExplorerLinkTypes.block}
/> />
@@ -79,24 +75,14 @@ export const TxDetailsContractCall = ({
)} )}
{data?.oracleSpec?.dataSourceSpec && ( {data?.oracleSpec?.dataSourceSpec && (
<OracleEthSource <OracleEthSource
chain={contractCall.sourceChainId}
sourceType={data.oracleSpec.dataSourceSpec.spec.data.sourceType} sourceType={data.oracleSpec.dataSourceSpec.spec.data.sourceType}
/> />
)} )}
{contractCall.error && ( <TableRow modifier="bordered">
<TableRow modifier="bordered"> <TableCell>{t('Result')}</TableCell>
<TableCell>{t('Call error')}</TableCell> <TableCell>{decodeEthCallResult(contractCall.result)}</TableCell>
<TableCell>{contractCall.error}</TableCell> </TableRow>
</TableRow>
)}
{contractCall.result && (
<TableRow modifier="bordered">
<TableCell>{t('Result')}</TableCell>
<TableCell>{decodeEthCallResult(contractCall.result)}</TableCell>
</TableRow>
)}
</> </>
); );
}; };
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { AssetLink } from '../../../links'; import { AssetLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventErc20AssetLimitsUpdatedProps { interface TxDetailsChainEventErc20AssetLimitsUpdatedProps {
assetLimitsUpdated: components['schemas']['vegaERC20AssetLimitsUpdated']; assetLimitsUpdated: components['schemas']['vegaERC20AssetLimitsUpdated'];
@@ -42,7 +42,7 @@ export const TxDetailsChainEventErc20AssetLimitsUpdated = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('ERC20 asset')}</TableCell> <TableCell>{t('ERC20 asset')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={assetLimitsUpdated.sourceEthereumAddress} id={assetLimitsUpdated.sourceEthereumAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { AssetLink } from '../../../links'; import { AssetLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventErc20AssetListProps { interface TxDetailsChainEventErc20AssetListProps {
assetList: components['schemas']['vegaERC20AssetList']; assetList: components['schemas']['vegaERC20AssetList'];
@@ -32,7 +32,7 @@ export const TxDetailsChainEventErc20AssetList = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Source')}</TableCell> <TableCell>{t('Source')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={assetList.assetSource} id={assetList.assetSource}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { AssetLink, PartyLink } from '../../../links'; import { AssetLink, PartyLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventProps { interface TxDetailsChainEventProps {
deposit: components['schemas']['vegaERC20Deposit']; deposit: components['schemas']['vegaERC20Deposit'];
@@ -36,7 +36,7 @@ export const TxDetailsChainEventDeposit = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Source')}</TableCell> <TableCell>{t('Source')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={deposit.sourceEthereumAddress} id={deposit.sourceEthereumAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { AssetLink } from '../../../links'; import { AssetLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventWithdrawalProps { interface TxDetailsChainEventWithdrawalProps {
withdrawal: components['schemas']['vegaERC20Withdrawal']; withdrawal: components['schemas']['vegaERC20Withdrawal'];
@@ -35,7 +35,7 @@ export const TxDetailsChainEventWithdrawal = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Recipient')}</TableCell> <TableCell>{t('Recipient')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={withdrawal.targetEthereumAddress} id={withdrawal.targetEthereumAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { PartyLink } from '../../../links'; import { PartyLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventStakeDepositProps { interface TxDetailsChainEventStakeDepositProps {
deposit: components['schemas']['vegaStakeDeposited']; deposit: components['schemas']['vegaStakeDeposited'];
@@ -39,7 +39,7 @@ export const TxDetailsChainEventStakeDeposit = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Source')}</TableCell> <TableCell>{t('Source')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={deposit.ethereumAddress} id={deposit.ethereumAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -3,9 +3,9 @@ import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { PartyLink } from '../../../links'; import { PartyLink } from '../../../links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventStakeRemoveProps { interface TxDetailsChainEventStakeRemoveProps {
remove: components['schemas']['vegaStakeRemoved']; remove: components['schemas']['vegaStakeRemoved'];
@@ -39,7 +39,7 @@ export const TxDetailsChainEventStakeRemove = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Source')}</TableCell> <TableCell>{t('Source')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={remove.ethereumAddress} id={remove.ethereumAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -3,9 +3,9 @@ import { t } from '@vegaprotocol/i18n';
import { TableRow, TableCell } from '../../../table'; import { TableRow, TableCell } from '../../../table';
import type { components } from '../../../../../types/explorer'; import type { components } from '../../../../../types/explorer';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../links/external-explorer-link/external-explorer-link'; } from '../../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsChainEventStakeTotalSupplyProps { interface TxDetailsChainEventStakeTotalSupplyProps {
update: components['schemas']['vegaStakeTotalSupply']; update: components['schemas']['vegaStakeTotalSupply'];
@@ -38,7 +38,7 @@ export const TxDetailsChainEventStakeTotalSupply = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Source')}</TableCell> <TableCell>{t('Source')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={update.tokenAddress} id={update.tokenAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -5,10 +5,5 @@ query ExplorerProposalStatus($id: ID!) {
state state
rejectionReason rejectionReason
} }
... on BatchProposal {
id
state
rejectionReason
}
} }
} }
@@ -8,7 +8,7 @@ export type ExplorerProposalStatusQueryVariables = Types.Exact<{
}>; }>;
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null }; export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
export const ExplorerProposalStatusDocument = gql` export const ExplorerProposalStatusDocument = gql`
@@ -19,11 +19,6 @@ export const ExplorerProposalStatusDocument = gql`
state state
rejectionReason rejectionReason
} }
... on BatchProposal {
id
state
rejectionReason
}
} }
} }
`; `;
@@ -1,257 +0,0 @@
import { render, screen } from '@testing-library/react';
import { BatchItem } from './batch-item';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import type { components } from '../../../../../types/explorer';
type Item = components['schemas']['vegaBatchProposalTermsChange'];
describe('BatchItem', () => {
it('Renders "Unknown proposal type" by default', () => {
const item = {};
render(<BatchItem item={item} />);
expect(screen.getByText('Unknown proposal type')).toBeInTheDocument();
});
it('Renders "Unknown proposal type" for unknown items', () => {
const item = {
newLochNessMonster: {
location: 'Loch Ness',
},
} as unknown as Item;
render(<BatchItem item={item} />);
expect(screen.getByText('Unknown proposal type')).toBeInTheDocument();
});
it('Renders "New spot market"', () => {
const item = {
newSpotMarket: {},
};
render(<BatchItem item={item} />);
expect(screen.getByText('New spot market')).toBeInTheDocument();
});
it('Renders "Cancel transfer"', () => {
const item = {
cancelTransfer: {
changes: {
transferId: 'transfer123',
},
},
};
render(<BatchItem item={item} />);
expect(screen.getByText('Cancel transfer')).toBeInTheDocument();
expect(screen.getByText('transf')).toBeInTheDocument();
});
it('Renders "Cancel transfer" without an id', () => {
const item = {
cancelTransfer: {
changes: {},
},
};
render(<BatchItem item={item} />);
expect(screen.getByText('Cancel transfer')).toBeInTheDocument();
});
it('Renders "New freeform"', () => {
const item = {
newFreeform: {},
};
render(<BatchItem item={item} />);
expect(screen.getByText('New freeform proposal')).toBeInTheDocument();
});
it('Renders "New market"', () => {
const item = {
newMarket: {},
};
render(<BatchItem item={item} />);
expect(screen.getByText('New market')).toBeInTheDocument();
});
it('Renders "New transfer"', () => {
const item = {
newTransfer: {},
};
render(<BatchItem item={item} />);
expect(screen.getByText('New transfer')).toBeInTheDocument();
});
it('Renders "Update asset" with assetId', () => {
const item = {
updateAsset: {
assetId: 'asset123',
},
};
render(
<MemoryRouter>
<MockedProvider>
<BatchItem item={item} />
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('Update asset')).toBeInTheDocument();
expect(screen.getByText('asset123')).toBeInTheDocument();
});
it('Renders "Update asset" even if assetId is not set', () => {
const item = {
updateAsset: {
assetId: undefined,
},
};
render(<BatchItem item={item} />);
expect(screen.getByText('Update asset')).toBeInTheDocument();
});
it('Renders "Update market state" with marketId', () => {
const item = {
updateMarketState: {
changes: {
marketId: 'market123',
},
},
};
render(
<MemoryRouter>
<MockedProvider>
<BatchItem item={item} />
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('Update market state')).toBeInTheDocument();
expect(screen.getByText('market123')).toBeInTheDocument();
});
it('Renders "Update market state" even if marketId is not set', () => {
const item = {
updateMarketState: {
changes: {
marketId: undefined,
},
},
};
render(
<MemoryRouter>
<MockedProvider>
<BatchItem item={item} />
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('Update market state')).toBeInTheDocument();
});
it('Renders "Update network parameter" with parameter', () => {
const item = {
updateNetworkParameter: {
changes: {
key: 'parameter123',
},
},
};
render(
<MockedProvider>
<MemoryRouter>
<BatchItem item={item} />
</MemoryRouter>
</MockedProvider>
);
expect(screen.getByText('Update network parameter')).toBeInTheDocument();
expect(screen.getByText('parameter123')).toBeInTheDocument();
});
it('Renders "Update network parameter" even if parameter is not set', () => {
const item = {
updateNetworkParameter: {
changes: {
key: undefined,
},
},
};
render(<BatchItem item={item} />);
expect(screen.getByText('Update network parameter')).toBeInTheDocument();
});
it('Renders "Update referral program"', () => {
const item = {
updateReferralProgram: {},
};
render(<BatchItem item={item} />);
expect(screen.getByText('Update referral program')).toBeInTheDocument();
});
it('Renders "Update spot market" with marketId', () => {
const item = {
updateSpotMarket: {
marketId: 'market123',
},
};
render(
<MemoryRouter>
<MockedProvider>
<BatchItem item={item} />
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('Update spot market')).toBeInTheDocument();
expect(screen.getByText('market123')).toBeInTheDocument();
});
it('Renders "Update spot market" even if marketId is not set', () => {
const item = {
updateSpotMarket: {
marketId: undefined,
},
};
render(
<MemoryRouter>
<MockedProvider>
<BatchItem item={item} />
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('Update spot market')).toBeInTheDocument();
});
it('Renders "Update market" with marketId', () => {
const item = {
updateMarket: {
marketId: 'market123',
},
};
render(
<MemoryRouter>
<MockedProvider>
<BatchItem item={item} />
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('Update market')).toBeInTheDocument();
expect(screen.getByText('market123')).toBeInTheDocument();
});
it('Renders "Update market" even if marketId is not set', () => {
const item = {
updateMarket: {
marketId: undefined,
},
};
render(
<MemoryRouter>
<MockedProvider>
<BatchItem item={item} />
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('Update market')).toBeInTheDocument();
});
it('Renders "Update volume discount program"', () => {
const item = {
updateVolumeDiscountProgram: {},
};
render(<BatchItem item={item} />);
expect(
screen.getByText('Update volume discount program')
).toBeInTheDocument();
});
});
@@ -1,87 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { AssetLink, MarketLink, NetworkParameterLink } from '../../../links';
import type { components } from '../../../../../types/explorer';
import Hash from '../../../links/hash';
type Item = components['schemas']['vegaBatchProposalTermsChange'];
export interface BatchItemProps {
item: Item;
}
/**
* Produces a one line summary for an item in a batch proposal. Could
* easily be adapted to summarise individual proposals, but there is no
* place for that yet.
*
* Details (like IDs) should be shown and linked if available, but handled
* if not available. This is adequate as the ProposalSummary component contains
* a JSON viewer for the full proposal.
*/
export const BatchItem = ({ item }: BatchItemProps) => {
if (item.cancelTransfer) {
const transferId = item?.cancelTransfer?.changes?.transferId || false;
return (
<span>
{t('Cancel transfer')}&nbsp;
{transferId && (
<Hash className="ml-1" truncate={true} text={transferId} />
)}
</span>
);
} else if (item.newFreeform) {
return <span>{t('New freeform proposal')}</span>;
} else if (item.newMarket) {
return <span>{t('New market')}</span>;
} else if (item.newSpotMarket) {
return <span>{t('New spot market')}</span>;
} else if (item.newTransfer) {
return <span>{t('New transfer')}</span>;
} else if (item.updateAsset) {
const assetId = item?.updateAsset?.assetId || false;
return (
<span>
{t('Update asset')}
{assetId && <AssetLink className="ml-1" assetId={assetId} />}
</span>
);
} else if (item.updateMarket) {
const marketId = item?.updateMarket?.marketId || false;
return (
<span>
{t('Update market')}{' '}
{marketId && <MarketLink className="ml-1" id={marketId} />}
</span>
);
} else if (item.updateMarketState) {
const marketId = item?.updateMarketState?.changes?.marketId || false;
return (
<span>
{t('Update market state')}
{marketId && <MarketLink className="ml-1" id={marketId} />}
</span>
);
} else if (item.updateNetworkParameter) {
const param = item?.updateNetworkParameter?.changes?.key || false;
return (
<span>
{t('Update network parameter')}
{param && <NetworkParameterLink className="ml-1" parameter={param} />}
</span>
);
} else if (item.updateReferralProgram) {
return <span>{t('Update referral program')}</span>;
} else if (item.updateSpotMarket) {
const marketId = item?.updateSpotMarket?.marketId || '';
return (
<span>
{t('Update spot market')}
<MarketLink className="ml-1" id={marketId} />
</span>
);
} else if (item.updateVolumeDiscountProgram) {
return <span>{t('Update volume discount program')}</span>;
}
return <span>{t('Unknown proposal type')}</span>;
};
@@ -1,4 +1,6 @@
import type { ProposalTerms } from '../tx-proposal';
import { useState } from 'react'; import { useState } from 'react';
import type { components } from '../../../../../types/explorer';
import { JsonViewerDialog } from '../../../dialogs/json-viewer-dialog'; import { JsonViewerDialog } from '../../../dialogs/json-viewer-dialog';
import ProposalLink from '../../../links/proposal-link/proposal-link'; import ProposalLink from '../../../links/proposal-link/proposal-link';
import truncate from 'lodash/truncate'; import truncate from 'lodash/truncate';
@@ -7,12 +9,7 @@ import ReactMarkdown from 'react-markdown';
import { ProposalDate } from './proposal-date'; import { ProposalDate } from './proposal-date';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { ProposalTerms } from '../tx-proposal';
import type { components } from '../../../../../types/explorer';
import { BatchItem } from './batch-item';
type Rationale = components['schemas']['vegaProposalRationale']; type Rationale = components['schemas']['vegaProposalRationale'];
type Batch = components['schemas']['v1BatchProposalSubmissionTerms']['changes'];
type ProposalTermsDialog = { type ProposalTermsDialog = {
open: boolean; open: boolean;
@@ -24,7 +21,6 @@ interface ProposalSummaryProps {
id: string; id: string;
rationale?: Rationale; rationale?: Rationale;
terms?: ProposalTerms; terms?: ProposalTerms;
batch?: Batch;
} }
/** /**
@@ -35,7 +31,6 @@ export const ProposalSummary = ({
id, id,
rationale, rationale,
terms, terms,
batch,
}: ProposalSummaryProps) => { }: ProposalSummaryProps) => {
const [dialog, setDialog] = useState<ProposalTermsDialog>({ const [dialog, setDialog] = useState<ProposalTermsDialog>({
open: false, open: false,
@@ -64,9 +59,7 @@ export const ProposalSummary = ({
return ( return (
<div className="w-auto max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5"> <div className="w-auto max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5">
{id && <ProposalStatusIcon id={id} />} {id && <ProposalStatusIcon id={id} />}
{rationale?.title && ( {rationale?.title && <h1 className="text-xl pb-1">{rationale.title}</h1>}
<h1 className="text-xl pb-1 break-all">{rationale.title}</h1>
)}
{rationale?.description && ( {rationale?.description && (
<div className="pt-2 text-sm leading-tight"> <div className="pt-2 text-sm leading-tight">
<ReactMarkdown <ReactMarkdown
@@ -79,18 +72,6 @@ export const ProposalSummary = ({
</ReactMarkdown> </ReactMarkdown>
</div> </div>
)} )}
{batch && (
<section className="pt-2 text-sm leading-tight my-3">
<h2 className="text-lg pb-1">{t('Changes')}</h2>
<ol>
{batch.map((change, index) => (
<li className="ml-4 list-decimal" key={`batch-${index}`}>
<BatchItem item={change} />
</li>
))}
</ol>
</section>
)}
<div className="pt-5"> <div className="pt-5">
<button className="underline max-md:hidden mr-5" onClick={openDialog}> <button className="underline max-md:hidden mr-5" onClick={openDialog}>
{t('View terms')} {t('View terms')}
@@ -1,46 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { TxDetailsShared } from '../shared/tx-details-shared';
import { TableWithTbody } from '../../../table';
import type { components } from '../../../../../types/explorer';
import type { BlockExplorerTransactionResult } from '../../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../../routes/blocks/tendermint-blocks-response';
import { TableCell, TableRow } from '../../../table';
type Update = components['schemas']['v1UpdatePartyProfile'];
interface TxDetailsUpdatePartyProfileProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* Party profiles can be an alias and arbitrary key/values pairs.
* This component displays the alias, if any, but not the metadata. When there is
* some wider usage, we can decide how to render it. For now, it's available in the
* full TX details.
*/
export const TxDetailsUpdatePartyProfile = ({
txData,
pubKey,
blockData,
}: TxDetailsUpdatePartyProfileProps) => {
if (!txData?.command.updatePartyProfile) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const update: Update = txData.command.updatePartyProfile;
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{update.alias && (
<TableRow modifier="bordered">
<TableCell>{t('New alias')}</TableCell>
<TableCell>{update.alias}</TableCell>
</TableRow>
)}
</TableWithTbody>
);
};
@@ -1,54 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerReferralCodeOwnerQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerReferralCodeOwnerQuery = { __typename?: 'Query', referralSets: { __typename?: 'ReferralSetConnection', edges: Array<{ __typename?: 'ReferralSetEdge', node: { __typename?: 'ReferralSet', createdAt: any, updatedAt: any, referrer: string } } | null> } };
export const ExplorerReferralCodeOwnerDocument = gql`
query ExplorerReferralCodeOwner($id: ID!) {
referralSets(id: $id) {
edges {
node {
createdAt
updatedAt
referrer
}
}
}
}
`;
/**
* __useExplorerReferralCodeOwnerQuery__
*
* To run a query within a React component, call `useExplorerReferralCodeOwnerQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerReferralCodeOwnerQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerReferralCodeOwnerQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerReferralCodeOwnerQuery(baseOptions: Apollo.QueryHookOptions<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>(ExplorerReferralCodeOwnerDocument, options);
}
export function useExplorerReferralCodeOwnerLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>(ExplorerReferralCodeOwnerDocument, options);
}
export type ExplorerReferralCodeOwnerQueryHookResult = ReturnType<typeof useExplorerReferralCodeOwnerQuery>;
export type ExplorerReferralCodeOwnerLazyQueryHookResult = ReturnType<typeof useExplorerReferralCodeOwnerLazyQuery>;
export type ExplorerReferralCodeOwnerQueryResult = Apollo.QueryResult<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>;
@@ -1,11 +0,0 @@
query ExplorerReferralCodeOwner($id: ID!) {
referralSets(id: $id) {
edges {
node {
createdAt
updatedAt
referrer
}
}
}
}
@@ -1,95 +0,0 @@
import { render, waitFor } from '@testing-library/react';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import { MemoryRouter } from 'react-router-dom';
import { ReferralCodeOwner } from './referral-code-owner';
import type { ReferralCodeOwnerProps } from './referral-code-owner';
import { ExplorerReferralCodeOwnerDocument } from './__generated__/code-owner';
const renderComponent = (
props: ReferralCodeOwnerProps,
mocks: MockedResponse[]
) => {
return render(
<MockedProvider mocks={mocks}>
<MemoryRouter>
<ReferralCodeOwner {...props} />
</MemoryRouter>
</MockedProvider>
);
};
describe('ReferralCodeOwner', () => {
it('should render loading state', () => {
const mocks = [
{
request: {
query: ExplorerReferralCodeOwnerDocument,
variables: {
id: 'ABC123',
},
},
},
];
const { getByText } = renderComponent({ code: 'ABC123' }, mocks);
expect(getByText('Loading...')).toBeInTheDocument();
});
it('should render error state', async () => {
const errorMessage = 'Error fetching referrer: ABC123';
const mocks = [
{
request: {
query: ExplorerReferralCodeOwnerDocument,
variables: {
id: 'ABC123',
},
},
error: new Error('nope'),
},
];
const { getByText } = renderComponent({ code: 'ABC123' }, mocks);
await waitFor(() => {
expect(getByText(errorMessage)).toBeInTheDocument();
});
});
it('should render link to referring party', async () => {
const referrerId = 'DEF789';
const mocks = [
{
request: {
query: ExplorerReferralCodeOwnerDocument,
variables: {
id: 'ABC123',
},
},
result: {
data: {
referralSets: {
edges: [
{
node: {
__typename: 'ReferralSet',
referrer: referrerId,
createdAt: '123',
updatedAt: '456',
},
},
],
},
},
},
},
];
const { getByText } = renderComponent({ code: 'ABC123' }, mocks);
await waitFor(() => {
expect(getByText(referrerId)).toBeInTheDocument();
});
});
});
@@ -1,26 +0,0 @@
import { TableCell } from '../../../table';
import { useExplorerReferralCodeOwnerQuery } from './__generated__/code-owner';
import { PartyLink } from '../../../links';
export interface ReferralCodeOwnerProps {
code: string;
}
/**
* Render the owner of a referral code
*/
export const ReferralCodeOwner = ({ code }: ReferralCodeOwnerProps) => {
const { data, error, loading } = useExplorerReferralCodeOwnerQuery({
variables: {
id: code,
},
});
const referrer = data?.referralSets.edges[0]?.node.referrer || '';
return (
<TableCell>
{loading && 'Loading...'}
{error && `Error fetching referrer: ${code}`}
{referrer.length > 0 && <PartyLink id={referrer} />}
</TableCell>
);
};
@@ -1,93 +0,0 @@
import { render } from '@testing-library/react';
import { ReferralTeam } from './team';
import type { CreateReferralSet } from './team';
import { MockedProvider } from '@apollo/client/testing';
describe('ReferralTeam', () => {
const team = {
name: 'Test Team',
teamUrl: 'https://example.com/team',
avatarUrl: 'https://example.com/avatar',
closed: false,
};
const mockTx: CreateReferralSet = {
team,
};
const mockId = '123456';
const mockCreator = 'JohnDoe';
it('should render the team name', () => {
const { getByText } = render(
<MockedProvider>
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
</MockedProvider>
);
expect(getByText('Test Team')).toBeInTheDocument();
});
it('should render the team ID', () => {
const { getByText } = render(
<MockedProvider>
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
</MockedProvider>
);
expect(getByText('Id')).toBeInTheDocument();
expect(getByText(mockId)).toBeInTheDocument();
});
it('should render the creator', () => {
const { getByText } = render(
<MockedProvider>
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
</MockedProvider>
);
expect(getByText('Creator')).toBeInTheDocument();
expect(getByText(mockCreator)).toBeInTheDocument();
});
it('should render the team URL', () => {
const { getByText } = render(
<MockedProvider>
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
</MockedProvider>
);
expect(getByText('Team URL')).toBeInTheDocument();
expect(getByText(team.teamUrl)).toBeInTheDocument();
});
it('should render the avatar URL', () => {
const { getByText } = render(
<MockedProvider>
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
</MockedProvider>
);
expect(getByText('Avatar')).toBeInTheDocument();
expect(getByText(team.avatarUrl)).toBeInTheDocument();
});
it('should render the open status as a tick if closed is falsy', () => {
const { getByTestId } = render(
<MockedProvider>
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
</MockedProvider>
);
expect(getByTestId('open-yes')).toBeInTheDocument();
});
it('should render the open status as a cross if it is truthy', () => {
const m = {
team: {
closed: true,
},
};
const { getByTestId } = render(
<MockedProvider>
<ReferralTeam tx={m} id={mockId} creator={mockCreator} />
</MockedProvider>
);
expect(getByTestId('open-no')).toBeInTheDocument();
});
});
@@ -1,73 +0,0 @@
import {
VegaIcon,
Icon,
KeyValueTable,
KeyValueTableRow,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import type { components } from '../../../../../types/explorer';
import Hash from '../../../links/hash';
import { t } from 'i18next';
import { PartyLink } from '../../../links';
export type CreateReferralSet = components['schemas']['v1CreateReferralSet'];
export type ReferralTeam = CreateReferralSet['team'];
export interface ReferralTeamProps {
tx: CreateReferralSet;
id: string;
creator: string;
}
/**
* Renders the details for a team in a CreateReferralSet or UpdateReferralSet transaction.
*
* Intentionally does not render the avatar image or link to the team url.
*/
export const ReferralTeam = ({ tx, id, creator }: ReferralTeamProps) => {
if (!tx.team) {
return null;
}
return (
<section>
<div className="inline-block mr-2 leading-none">
<VegaIcon name={VegaIconNames.TEAM} />
</div>
{tx.team.name && (
<h3 className="inline-block leading-loose">{tx.team.name}</h3>
)}
<div className="min-w-fit max-w-2xl block">
<KeyValueTable>
<KeyValueTableRow>
{t('Id')}
<Hash text={id} truncate={false} />
</KeyValueTableRow>
<KeyValueTableRow>
{t('Creator')}
<PartyLink id={creator} truncate={false} />
</KeyValueTableRow>
{tx.team.teamUrl && (
<KeyValueTableRow>
{t('Team URL')}
<Hash text={tx.team.teamUrl} truncate={false} />
</KeyValueTableRow>
)}
{tx.team.avatarUrl && (
<KeyValueTableRow>
{t('Avatar')}
<Hash text={tx.team.avatarUrl} truncate={false} />
</KeyValueTableRow>
)}
<KeyValueTableRow>
{t('Open')}
<span data-testid={!tx.team.closed ? 'open-yes' : 'open-no'}>
{!tx.team.closed ? <Icon name="tick" /> : <Icon name="cross" />}
</span>
</KeyValueTableRow>
</KeyValueTable>
</div>
</section>
);
};
@@ -27,8 +27,7 @@ export const sharedHeaderProps = {
className: 'align-top', className: 'align-top',
}; };
// The incoming type field is usually the right thing to show. Exceptions are listed here const Labels: Record<BlockExplorerTransactionResult['type'], string> = {
const LabelOverrides: Record<BlockExplorerTransactionResult['type'], string> = {
'Stop Orders Submission': 'Stop Order', 'Stop Orders Submission': 'Stop Order',
'Stop Orders Cancellation': 'Cancel Stop Order', 'Stop Orders Cancellation': 'Cancel Stop Order',
}; };
@@ -51,7 +50,7 @@ export const TxDetailsShared = ({
const time: string = blockData?.result.block.header.time || ''; const time: string = blockData?.result.block.header.time || '';
const height: string = blockData?.result.block.header.height || txData.block; const height: string = blockData?.result.block.header.height || txData.block;
const type = LabelOverrides[txData.type] || txData.type; const type = Labels[txData.type] || txData.type;
return ( return (
<> <>
@@ -1,18 +0,0 @@
query ExplorerTransferStatus($id: ID!) {
transfer(id: $id) {
transfer {
reference
timestamp
status
reason
fromAccountType
from
to
toAccountType
asset {
id
}
amount
}
}
}
@@ -1,61 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerTransferStatusQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerTransferStatusQuery = { __typename?: 'Query', transfer?: { __typename?: 'TransferNode', transfer: { __typename?: 'Transfer', reference?: string | null, timestamp: any, status: Types.TransferStatus, reason?: string | null, fromAccountType: Types.AccountType, from: string, to: string, toAccountType: Types.AccountType, amount: string, asset?: { __typename?: 'Asset', id: string } | null } } | null };
export const ExplorerTransferStatusDocument = gql`
query ExplorerTransferStatus($id: ID!) {
transfer(id: $id) {
transfer {
reference
timestamp
status
reason
fromAccountType
from
to
toAccountType
asset {
id
}
amount
}
}
}
`;
/**
* __useExplorerTransferStatusQuery__
*
* To run a query within a React component, call `useExplorerTransferStatusQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerTransferStatusQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerTransferStatusQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerTransferStatusQuery(baseOptions: Apollo.QueryHookOptions<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>(ExplorerTransferStatusDocument, options);
}
export function useExplorerTransferStatusLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>(ExplorerTransferStatusDocument, options);
}
export type ExplorerTransferStatusQueryHookResult = ReturnType<typeof useExplorerTransferStatusQuery>;
export type ExplorerTransferStatusLazyQueryHookResult = ReturnType<typeof useExplorerTransferStatusLazyQuery>;
export type ExplorerTransferStatusQueryResult = Apollo.QueryResult<ExplorerTransferStatusQuery, ExplorerTransferStatusQueryVariables>;
@@ -41,7 +41,6 @@ const AccountType: Record<AccountTypes, string> = {
ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY: 'Reward Return Volatility', ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY: 'Reward Return Volatility',
ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING: 'Reward Validator Ranking', ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING: 'Reward Validator Ranking',
ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD: 'Pending Fee Referral Reward', ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD: 'Pending Fee Referral Reward',
ACCOUNT_TYPE_ORDER_MARGIN: 'Order Margin',
}; };
interface TransferParticipantsProps { interface TransferParticipantsProps {
@@ -111,7 +110,7 @@ export function TransferParticipants({
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 9" viewBox="0 0 16 9"
className="fill-white dark:fill-black" className="fill-vega-light-100 dark:fill-black"
> >
<path d="M0,0L8,9l8,-9Z" /> <path d="M0,0L8,9l8,-9Z" />
</svg> </svg>
@@ -120,7 +119,7 @@ export function TransferParticipants({
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 9" viewBox="0 0 16 9"
className="fill-vega-light-200 dark:fill-vega-dark-200" className="fill-vega-light-100 dark:fill-vega-dark-200"
> >
<path d="M0,0L8,9l8,-9Z" /> <path d="M0,0L8,9l8,-9Z" />
</svg> </svg>
@@ -1,223 +1,97 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { AssetLink, MarketLink } from '../../../../links'; import { AssetLink, MarketLink } from '../../../../links';
import { headerClasses, wrapperClasses } from '../transfer-details';
import type { components } from '../../../../../../types/explorer'; import type { components } from '../../../../../../types/explorer';
import type { Recurring } from '../transfer-details'; import type { Recurring } from '../transfer-details';
import { import { DispatchMetricLabels } from '@vegaprotocol/types';
DispatchMetricLabels,
DistributionStrategy,
} from '@vegaprotocol/types';
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { formatNumber } from '@vegaprotocol/utils';
export type Metric = components['schemas']['vegaDispatchMetric']; export type Metric = components['schemas']['vegaDispatchMetric'];
export type Strategy = components['schemas']['vegaDispatchStrategy']; export type Strategy = components['schemas']['vegaDispatchStrategy'];
export const wrapperClasses = 'border pv-2 w-full flex-auto basis-full';
export const headerClasses =
'bg-solid bg-vega-light-150 dark:bg-vega-dark-150 text-center text-xl py-2 font-alpha calt';
const metricLabels: Record<Metric, string> = { const metricLabels: Record<Metric, string> = {
DISPATCH_METRIC_UNSPECIFIED: 'Unknown metric', DISPATCH_METRIC_UNSPECIFIED: 'Unknown metric',
...DispatchMetricLabels, ...DispatchMetricLabels,
}; };
// Maps the two (non-null) values of entityScope to the icon that represents it
const entityScopeIcons: Record<
string,
typeof VegaIconNames[keyof typeof VegaIconNames]
> = {
ENTITY_SCOPE_INDIVIDUALS: VegaIconNames.MAN,
ENTITY_SCOPE_TEAMS: VegaIconNames.TEAM,
};
const distributionStrategyLabel: Record<DistributionStrategy, string> = {
[DistributionStrategy.DISTRIBUTION_STRATEGY_PRO_RATA]: 'Pro Rata',
[DistributionStrategy.DISTRIBUTION_STRATEGY_RANK]: 'Ranked',
};
interface TransferRewardsProps { interface TransferRewardsProps {
recurring: Recurring; recurring: Recurring;
} }
/** /**
* Renders recurring transfers/game details in a way that is, perhaps, easy to understand * Renderer for a transfer. These can vary quite
* widely, essentially every field can be null.
* *
* @param transfer A recurring transfer object * @param transfer A recurring transfer object
*/ */
export function TransferRewards({ recurring }: TransferRewardsProps) { export function TransferRewards({ recurring }: TransferRewardsProps) {
const metric =
recurring?.dispatchStrategy?.metric || 'DISPATCH_METRIC_UNSPECIFIED';
if (!recurring || !recurring.dispatchStrategy) { if (!recurring || !recurring.dispatchStrategy) {
return null; return null;
} }
// Destructure to make things a bit more readable
const {
entityScope,
individualScope,
teamScope,
distributionStrategy,
lockPeriod,
markets,
stakingRequirement,
windowLength,
notionalTimeWeightedAveragePositionRequirement,
rankTable,
nTopPerformers,
} = recurring.dispatchStrategy;
return ( return (
<div className={wrapperClasses}> <div className={wrapperClasses}>
<h2 className={headerClasses}>{getRewardTitle(entityScope)}</h2> <h2 className={headerClasses}>{t('Reward metrics')}</h2>
<ul className="relative block rounded-lg py-6 text-left p-6"> <ul className="relative block rounded-lg py-6 text-center p-6">
{entityScope && entityScopeIcons[entityScope] ? ( {recurring.dispatchStrategy.assetForMetric ? (
<li> <li>
<strong>{t('Scope')}</strong>:{' '} <strong>{t('Asset')}</strong>:{' '}
<VegaIcon name={entityScopeIcons[entityScope]} /> <AssetLink assetId={recurring.dispatchStrategy.assetForMetric} />
&nbsp;
{individualScope ? individualScopeLabels[individualScope] : null}
{getScopeLabel(entityScope, teamScope)}
</li> </li>
) : null} ) : null}
{recurring.dispatchStrategy && <li>
recurring.dispatchStrategy.assetForMetric && ( <strong>{t('Metric')}</strong>: {metricLabels[metric]}
<li> </li>
<strong>{t('Asset for metric')}</strong>:{' '} {recurring.dispatchStrategy.markets &&
<AssetLink assetId={recurring.dispatchStrategy.assetForMetric} /> recurring.dispatchStrategy.markets.length > 0 ? (
</li>
)}
{recurring.dispatchStrategy.metric &&
metricLabels[recurring.dispatchStrategy.metric] && (
<li>
<strong>{t('Metric')}</strong>:{' '}
{metricLabels[recurring.dispatchStrategy.metric]}
</li>
)}
{lockPeriod && (
<li>
<strong>{t('Reward lock')}</strong>:&nbsp;
{recurring.dispatchStrategy.lockPeriod}{' '}
{recurring.dispatchStrategy.lockPeriod === '1'
? t('epoch')
: t('epochs')}
</li>
)}
{markets && markets.length > 0 ? (
<li> <li>
<strong>{t('Markets in scope')}</strong>: <strong>{t('Markets in scope')}</strong>:
<ul className="inline-block ml-1"> <ul>
{markets.map((m) => ( {recurring.dispatchStrategy.markets.map((m) => (
<li key={m} className="inline-block mr-2"> <li key={m}>
<MarketLink id={m} /> <MarketLink id={m} />
</li> </li>
))} ))}
</ul> </ul>
</li> </li>
) : null} ) : null}
<li>
{stakingRequirement && stakingRequirement !== '0' ? ( <strong>{t('Factor')}</strong>: {recurring.factor}
<li> </li>
<strong>{t('Staking requirement')}</strong>: {stakingRequirement}
</li>
) : null}
{windowLength && windowLength !== '0' ? (
<li>
<strong>{t('Window length')}</strong>:{' '}
{recurring.dispatchStrategy.windowLength}{' '}
{recurring.dispatchStrategy.windowLength === '1'
? t('epoch')
: t('epochs')}
</li>
) : null}
{notionalTimeWeightedAveragePositionRequirement &&
notionalTimeWeightedAveragePositionRequirement !== '' ? (
<li>
<strong>{t('Notional TWAP')}</strong>:{' '}
{notionalTimeWeightedAveragePositionRequirement}
</li>
) : null}
{nTopPerformers && (
<li>
<strong>{t('Elligible team members:')}</strong> top{' '}
{`${formatNumber(Number(nTopPerformers) * 100, 0)}%`}
</li>
)}
{distributionStrategy &&
distributionStrategy !== 'DISTRIBUTION_STRATEGY_UNSPECIFIED' && (
<li>
<strong>{t('Distribution strategy')}</strong>:{' '}
{distributionStrategyLabel[distributionStrategy]}
</li>
)}
</ul> </ul>
<div className="px-6 pt-1 pb-5">
{rankTable && rankTable.length > 0 ? (
<table className="border-collapse border border-gray-400 ">
<thead>
<tr>
<th className="border border-gray-300 bg-gray-300 px-3">
<strong>{t('Start rank')}</strong>
</th>
<th className="border border-gray-300 bg-gray-300 px-3">
<strong>{t('Share of reward pool')}</strong>
</th>
</tr>
</thead>
<tbody>
{rankTable.map((row, i) => {
return (
<tr key={`rank-${i}`}>
<td className="border border-slate-300 text-center">
{row.startRank}
</td>
<td className="border border-slate-300 text-center">
{row.shareRatio}
</td>
</tr>
);
})}
</tbody>
</table>
) : null}
</div>
</div> </div>
); );
} }
export function getScopeLabel( interface TransferRecurringStrategyProps {
scope: components['schemas']['vegaEntityScope'] | undefined, strategy: Strategy;
teamScope: readonly string[] | undefined
): string {
if (scope === 'ENTITY_SCOPE_TEAMS') {
if (teamScope && teamScope.length !== 0) {
return ` ${teamScope.length} teams`;
} else {
return t('All teams');
}
} else if (scope === 'ENTITY_SCOPE_INDIVIDUALS') {
return t('Individuals');
} else {
return '';
}
}
export function getRewardTitle(
scope?: components['schemas']['vegaEntityScope']
) {
if (scope === 'ENTITY_SCOPE_TEAMS') {
return t('Game');
}
return t('Reward metrics');
} }
const individualScopeLabels: Record< /**
components['schemas']['vegaIndividualScope'], * Simple renderer for a dispatch strategy in a recurring transfer
string *
> = { * @param strategy Dispatch strategy object
// Unspecified and All are not rendered */
INDIVIDUAL_SCOPE_UNSPECIFIED: '', export function TransferRecurringStrategy({
INDIVIDUAL_SCOPE_ALL: '', strategy,
INDIVIDUAL_SCOPE_IN_TEAM: '(in team)', }: TransferRecurringStrategyProps) {
INDIVIDUAL_SCOPE_NOT_IN_TEAM: '(not in team)', if (!strategy) {
}; return null;
}
return (
<>
{strategy.assetForMetric ? (
<li>
<strong>{t('Asset for metric')}</strong>:{' '}
<AssetLink assetId={strategy.assetForMetric} />
</li>
) : null}
<li>
<strong>{t('Metric')}</strong>: {strategy.metric}
</li>
</>
);
}
@@ -1,100 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { headerClasses, wrapperClasses } from '../transfer-details';
import { Icon, Loader } from '@vegaprotocol/ui-toolkit';
import type { IconName } from '@vegaprotocol/ui-toolkit';
import type { ApolloError } from '@apollo/client';
import { TransferStatus, TransferStatusMapping } from '@vegaprotocol/types';
import { IconNames } from '@blueprintjs/icons';
interface TransferStatusProps {
status: TransferStatus | undefined;
error: ApolloError | undefined;
loading: boolean;
}
/**
* Renderer for a transfer. These can vary quite
* widely, essentially every field can be null.
*
* @param transfer A recurring transfer object
*/
export function TransferStatusView({ status, loading }: TransferStatusProps) {
if (!status) {
status = TransferStatus.STATUS_PENDING;
}
return (
<div className={wrapperClasses}>
<h2 className={headerClasses}>{t('Status')}</h2>
<div className="relative block rounded-lg py-6 text-center p-6">
{loading ? (
<div className="leading-10 mt-12">
<Loader size={'small'} />
</div>
) : (
<>
<p className="leading-10 my-2">
<TransferStatusIcon status={status} />
</p>
<p className="leading-10 my-2">{TransferStatusMapping[status]}</p>
</>
)}
</div>
</div>
);
}
interface TransferStatusIconProps {
status: TransferStatus;
}
export function TransferStatusIcon({ status }: TransferStatusIconProps) {
return (
<span title={TransferStatusMapping[status]}>
<Icon
name={getIconForStatus(status)}
className={getColourForStatus(status)}
/>
</span>
);
}
/**
* Simple mapping from status to icon name
* @param status TransferStatus
* @returns IconName
*/
export function getIconForStatus(status: TransferStatus): IconName {
switch (status) {
case TransferStatus.STATUS_PENDING:
return IconNames.TIME;
case TransferStatus.STATUS_DONE:
return IconNames.TICK;
case TransferStatus.STATUS_REJECTED:
return IconNames.CROSS;
case TransferStatus.STATUS_CANCELLED:
return IconNames.CROSS;
default:
return IconNames.TIME;
}
}
/**
* Simple mapping from status to colour
* @param status TransferStatus
* @returns string Tailwind classname
*/
export function getColourForStatus(status: TransferStatus): string {
switch (status) {
case TransferStatus.STATUS_PENDING:
return 'text-yellow-500';
case TransferStatus.STATUS_DONE:
return 'text-green-500';
case TransferStatus.STATUS_REJECTED:
return 'text-red-500';
case TransferStatus.STATUS_CANCELLED:
return 'text-red-600';
default:
return 'text-yellow-500';
}
}
@@ -2,15 +2,12 @@ import type { components } from '../../../../../types/explorer';
import { TransferRepeat } from './blocks/transfer-repeat'; import { TransferRepeat } from './blocks/transfer-repeat';
import { TransferRewards } from './blocks/transfer-rewards'; import { TransferRewards } from './blocks/transfer-rewards';
import { TransferParticipants } from './blocks/transfer-participants'; import { TransferParticipants } from './blocks/transfer-participants';
import { useExplorerTransferStatusQuery } from './__generated__/Transfer';
import { TransferStatusView } from './blocks/transfer-status';
import { TransferStatus } from '@vegaprotocol/types';
export type Recurring = components['schemas']['commandsv1RecurringTransfer']; export type Recurring = components['schemas']['commandsv1RecurringTransfer'];
export type Metric = components['schemas']['vegaDispatchMetric']; export type Metric = components['schemas']['vegaDispatchMetric'];
export const wrapperClasses = export const wrapperClasses =
'border border-vega-light-150 dark:border-vega-dark-200 pv-2 w-full sm:w-1/3 basis-1/3'; 'border border-vega-light-150 dark:border-vega-dark-200 rounded-md pv-2 mb-5 w-full sm:w-1/4 min-w-[200px] ';
export const headerClasses = export const headerClasses =
'bg-solid bg-vega-light-150 dark:bg-vega-dark-150 border-vega-light-150 text-center text-xl py-2 font-alpha calt'; 'bg-solid bg-vega-light-150 dark:bg-vega-dark-150 border-vega-light-150 text-center text-xl py-2 font-alpha calt';
@@ -19,7 +16,6 @@ export type Transfer = components['schemas']['commandsv1Transfer'];
interface TransferDetailsProps { interface TransferDetailsProps {
transfer: Transfer; transfer: Transfer;
from: string; from: string;
id: string;
} }
/** /**
@@ -28,24 +24,13 @@ interface TransferDetailsProps {
* *
* @param transfer A recurring transfer object * @param transfer A recurring transfer object
*/ */
export function TransferDetails({ transfer, from, id }: TransferDetailsProps) { export function TransferDetails({ transfer, from }: TransferDetailsProps) {
const recurring = transfer.recurring; const recurring = transfer.recurring;
// Currently all this is passed in to TransferStatus, but the extra details
// may be useful in the future.
const { data, error, loading } = useExplorerTransferStatusQuery({
variables: { id },
});
const status = error
? TransferStatus.STATUS_REJECTED
: data?.transfer?.transfer.status;
return ( return (
<div className="flex flex-wrap"> <div className="flex gap-5 flex-wrap">
<TransferParticipants from={from} transfer={transfer} /> <TransferParticipants from={from} transfer={transfer} />
{recurring ? <TransferRepeat recurring={transfer.recurring} /> : null} {recurring ? <TransferRepeat recurring={transfer.recurring} /> : null}
<TransferStatusView status={status} error={error} loading={loading} />
{recurring && recurring.dispatchStrategy ? ( {recurring && recurring.dispatchStrategy ? (
<TransferRewards recurring={transfer.recurring} /> <TransferRewards recurring={transfer.recurring} />
) : null} ) : null}
@@ -1,172 +0,0 @@
import {
getScopeLabel,
getRewardTitle,
TransferRewards,
} from './blocks/transfer-rewards';
import { render } from '@testing-library/react';
import type { components } from '../../../../../types/explorer';
import type { Recurring } from './transfer-details';
import {
DispatchMetric,
DistributionStrategy,
EntityScope,
IndividualScope,
} from '@vegaprotocol/types';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
describe('getScopeLabel', () => {
it('should return the correct label for ENTITY_SCOPE_TEAMS with teamScope', () => {
const scope = 'ENTITY_SCOPE_TEAMS';
const teamScope = ['team1', 'team2', 'team3'];
const expectedLabel = ' 3 teams';
const result = getScopeLabel(scope, teamScope);
expect(result).toEqual(expectedLabel);
});
it('should return the correct label for ENTITY_SCOPE_TEAMS without teamScope', () => {
const scope = 'ENTITY_SCOPE_TEAMS';
const teamScope = undefined;
const expectedLabel = 'All teams';
const result = getScopeLabel(scope, teamScope);
expect(result).toEqual(expectedLabel);
});
it('should return the correct label for ENTITY_SCOPE_INDIVIDUALS', () => {
const scope = 'ENTITY_SCOPE_INDIVIDUALS';
const teamScope = undefined;
const expectedLabel = 'Individuals';
const result = getScopeLabel(scope, teamScope);
expect(result).toEqual(expectedLabel);
});
it('should return an empty string for unknown scope', () => {
const scope = 'UNKNOWN_SCOPE';
const teamScope = undefined;
const expectedLabel = '';
const result = getScopeLabel(
scope as unknown as components['schemas']['vegaEntityScope'],
teamScope
);
expect(result).toEqual(expectedLabel);
});
});
describe('getRewardTitle', () => {
it('should return the correct title for ENTITY_SCOPE_TEAMS', () => {
const scope = 'ENTITY_SCOPE_TEAMS';
const expectedTitle = 'Game';
const result = getRewardTitle(scope);
expect(result).toEqual(expectedTitle);
});
it('should return the correct title for other scopes', () => {
const scope = 'ENTITY_SCOPE_INDIVIDUALS';
const expectedTitle = 'Reward metrics';
const result = getRewardTitle(scope);
expect(result).toEqual(expectedTitle);
});
});
describe('TransferRewards', () => {
it('should render nothing if recurring dispatchStrategy is not provided', () => {
const { container } = render(
<TransferRewards recurring={null as unknown as Recurring} />
);
expect(container.firstChild).toBeNull();
});
it('should render nothing if recurring.dispatchStrategy is not provided', () => {
const { container } = render(
<TransferRewards recurring={{} as unknown as Recurring} />
);
expect(container.firstChild).toBeNull();
});
it('should render the reward details correctly', () => {
const recurring = {
dispatchStrategy: {
metric: DispatchMetric.DISPATCH_METRIC_AVERAGE_POSITION,
assetForMetric: '123',
entityScope: EntityScope.ENTITY_SCOPE_TEAMS,
individualScope: IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM,
teamScope: [],
distributionStrategy:
DistributionStrategy.DISTRIBUTION_STRATEGY_PRO_RATA,
lockPeriod: 'lockPeriod',
markets: ['market1', 'market2'],
stakingRequirement: '1',
windowLength: 'windowLength',
notionalTimeWeightedAveragePositionRequirement:
'notionalTimeWeightedAveragePositionRequirement',
rankTable: [
{ startRank: 1, shareRatio: 0.2 },
{ startRank: 2, shareRatio: 0.3 },
],
nTopPerformers: 'nTopPerformers',
},
};
const { getByText } = render(
<MemoryRouter>
<MockedProvider>
<TransferRewards recurring={recurring} />
</MockedProvider>
</MemoryRouter>
);
expect(getByText('Game')).toBeInTheDocument();
expect(getByText('Scope')).toBeInTheDocument();
expect(getByText('Asset for metric')).toBeInTheDocument();
expect(getByText('Metric')).toBeInTheDocument();
expect(getByText('Reward lock')).toBeInTheDocument();
expect(getByText('Markets in scope')).toBeInTheDocument();
expect(getByText('Staking requirement')).toBeInTheDocument();
expect(getByText('Window length')).toBeInTheDocument();
expect(getByText('Notional TWAP')).toBeInTheDocument();
expect(getByText('Elligible team members:')).toBeInTheDocument();
expect(getByText('Distribution strategy')).toBeInTheDocument();
expect(getByText('Start rank')).toBeInTheDocument();
expect(getByText('Share of reward pool')).toBeInTheDocument();
});
it('should not render a rank table if recurring.dispatchStrategy.rankTable is not provided', () => {
const recurring = {
dispatchStrategy: {
entityScope: EntityScope.ENTITY_SCOPE_INDIVIDUALS,
individualScope: IndividualScope.INDIVIDUAL_SCOPE_ALL,
teamScope: ['team1', 'team2', 'team3'],
distributionStrategy:
DistributionStrategy.DISTRIBUTION_STRATEGY_PRO_RATA,
lockPeriod: 'lockPeriod',
markets: ['market1', 'market2'],
stakingRequirement: 'stakingRequirement',
windowLength: 'windowLength',
notionalTimeWeightedAveragePositionRequirement:
'notionalTimeWeightedAveragePositionRequirement',
nTopPerformers: 'nTopPerformers',
},
};
const { container } = render(
<MemoryRouter>
<MockedProvider>
<TransferRewards recurring={recurring} />
</MockedProvider>
</MemoryRouter>
);
expect(container.querySelector('table')).toBeNull();
});
});
@@ -1,47 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import { ReferralCodeOwner } from './referrals/referral-code-owner';
interface TxDetailsApplyReferralCodeProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* The signature can be turned in to an id with txSignatureToDeterministicId but
*/
export const TxDetailsApplyReferralCode = ({
txData,
pubKey,
blockData,
}: TxDetailsApplyReferralCodeProps) => {
if (!txData || !txData.command.applyReferralCode || !txData.signature) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const referralCode = txData.command.applyReferralCode.id;
return (
<div>
<TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
<TableRow modifier="bordered">
<TableCell>{t('Applied Code')}</TableCell>
<TableCell>{referralCode}</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Referrer')}</TableCell>
<ReferralCodeOwner code={referralCode} />
</TableRow>
</TableWithTbody>
</div>
);
};
@@ -1,75 +0,0 @@
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { sharedHeaderProps, TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
import { ProposalSummary } from './proposal/summary';
import Hash from '../../links/hash';
import { t } from '@vegaprotocol/i18n';
export type Proposal = components['schemas']['v1BatchProposalSubmission'];
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
interface TxBatchProposalProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
*
*/
export const TxBatchProposal = ({
txData,
pubKey,
blockData,
}: TxBatchProposalProps) => {
if (!txData || !txData.command.batchProposalSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
let deterministicId = '';
const proposal: Proposal = txData.command.batchProposalSubmission;
const sig = txData?.signature?.value;
if (sig) {
deterministicId = txSignatureToDeterministicId(sig);
}
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
<TableCell>{t('Batch proposal')}</TableCell>
</TableRow>
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
hideTypeRow={true}
/>
<TableRow modifier="bordered">
<TableCell>{t('Batch size')}</TableCell>
<TableCell>
{proposal.terms?.changes?.length || t('No changes')}
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Proposal ID')}</TableCell>
<TableCell>
<Hash text={deterministicId} />
</TableCell>
</TableRow>
</TableWithTbody>
{proposal && (
<ProposalSummary
id={deterministicId}
rationale={proposal?.rationale}
terms={proposal.terms}
batch={proposal.terms?.changes}
/>
)}
</>
);
};
@@ -1,52 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
import { ReferralTeam } from './referrals/team';
interface TxDetailsCreateReferralProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* The signature can be turned in to an id with txSignatureToDeterministicId but
*/
export const TxDetailsCreateReferralSet = ({
txData,
pubKey,
blockData,
}: TxDetailsCreateReferralProps) => {
if (!txData || !txData.command.createReferralSet || !txData.signature) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const id = txSignatureToDeterministicId(txData.signature.value);
const isTeam = txData.command.createReferralSet.isTeam || false;
return (
<div>
<TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
<TableRow modifier="bordered">
<TableCell>{isTeam ? t('Team ID') : t('Referral code')}</TableCell>
<TableCell>{id}</TableCell>
</TableRow>
</TableWithTbody>
<ReferralTeam
tx={txData.command.createReferralSet}
id={id}
creator={txData.submitter}
/>
</div>
);
};
@@ -28,13 +28,6 @@ import { TxProposal } from './tx-proposal';
import { TxDetailsTransfer } from './tx-transfer'; import { TxDetailsTransfer } from './tx-transfer';
import { TxDetailsStopOrderSubmission } from './tx-stop-order-submission'; import { TxDetailsStopOrderSubmission } from './tx-stop-order-submission';
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission'; import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
import { TxDetailsCreateReferralSet } from './tx-create-referral-set';
import { TxDetailsApplyReferralCode } from './tx-apply-referral-code';
import { TxDetailsUpdateReferralSet } from './tx-update-referral-set';
import { TxDetailsJoinTeam } from './tx-join-team';
import { TxDetailsUpdateMarginMode } from './tx-update-margin-mode';
import { TxBatchProposal } from './tx-batch-proposal';
import { TxDetailsUpdatePartyProfile } from './proposal/tx-update-party-profile';
interface TxDetailsWrapperProps { interface TxDetailsWrapperProps {
txData: BlockExplorerTransactionResult | undefined; txData: BlockExplorerTransactionResult | undefined;
@@ -128,20 +121,6 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
return TxDetailsStopOrderSubmission; return TxDetailsStopOrderSubmission;
case 'Transfer Funds': case 'Transfer Funds':
return TxDetailsTransfer; return TxDetailsTransfer;
case 'Create Referral Set':
return TxDetailsCreateReferralSet;
case 'Update Referral Set':
return TxDetailsUpdateReferralSet;
case 'Apply Referral Code':
return TxDetailsApplyReferralCode;
case 'Join Team':
return TxDetailsJoinTeam;
case 'Update Margin Mode':
return TxDetailsUpdateMarginMode;
case 'Batch Proposal':
return TxBatchProposal;
case 'Update Party Profile':
return TxDetailsUpdatePartyProfile;
default: default:
return TxDetailsGeneric; return TxDetailsGeneric;
} }
@@ -5,9 +5,9 @@ import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table'; import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer'; import type { components } from '../../../../types/explorer';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../links/external-explorer-link/external-explorer-link'; } from '../../links/eth-explorer-link/eth-explorer-link';
import { BlockLink } from '../../links'; import { BlockLink } from '../../links';
type EthKeyRotate = components['schemas']['v1EthereumKeyRotateSubmission']; type EthKeyRotate = components['schemas']['v1EthereumKeyRotateSubmission'];
@@ -46,7 +46,7 @@ export const TxDetailsEthKeyRotate = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Old Address')}</TableCell> <TableCell>{t('Old Address')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
id={k.currentAddress} id={k.currentAddress}
/> />
@@ -57,7 +57,7 @@ export const TxDetailsEthKeyRotate = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('New Address')}</TableCell> <TableCell>{t('New Address')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
id={k.newAddress} id={k.newAddress}
/> />
@@ -68,7 +68,7 @@ export const TxDetailsEthKeyRotate = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Submitter address')}</TableCell> <TableCell>{t('Submitter address')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
id={k.submitterAddress} id={k.submitterAddress}
/> />
@@ -6,9 +6,9 @@ import { TableRow, TableCell, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer'; import type { components } from '../../../../types/explorer';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../links/external-explorer-link/external-explorer-link'; } from '../../links/eth-explorer-link/eth-explorer-link';
import { NodeLink } from '../../links'; import { NodeLink } from '../../links';
type Command = components['schemas']['v1IssueSignatures']; type Command = components['schemas']['v1IssueSignatures'];
@@ -57,7 +57,7 @@ export const TxDetailsIssueSignatures = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('ETH key')}</TableCell> <TableCell>{t('ETH key')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={cmd.submitter} id={cmd.submitter}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -1,47 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import { ReferralCodeOwner } from './referrals/referral-code-owner';
interface TxDetailsJoinTeamProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* The signature can be turned in to an id with txSignatureToDeterministicId but
*/
export const TxDetailsJoinTeam = ({
txData,
pubKey,
blockData,
}: TxDetailsJoinTeamProps) => {
if (!txData || !txData.command.joinTeam || !txData.signature) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const team = txData.command.joinTeam.id;
return (
<div>
<TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
<TableRow modifier="bordered">
<TableCell>{t('Team')}</TableCell>
<TableCell>{team}</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Referrer')}</TableCell>
<ReferralCodeOwner code={team} />
</TableRow>
</TableWithTbody>
</div>
);
};
@@ -6,9 +6,9 @@ import { TableRow, TableCell, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer'; import type { components } from '../../../../types/explorer';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../links/external-explorer-link/external-explorer-link'; } from '../../links/eth-explorer-link/eth-explorer-link';
import { PartyLink } from '../../links'; import { PartyLink } from '../../links';
import Hash from '../../links/hash'; import Hash from '../../links/hash';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
@@ -72,7 +72,7 @@ export const TxDetailsNodeAnnounce = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Ethereum Address')}</TableCell> <TableCell>{t('Ethereum Address')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
id={cmd.ethereumAddress} id={cmd.ethereumAddress}
/> />
@@ -8,9 +8,9 @@ import { useExplorerNodeVoteQuery } from './__generated__/Node-vote';
import { PartyLink } from '../../links'; import { PartyLink } from '../../links';
import { Time } from '../../time'; import { Time } from '../../time';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../links/external-explorer-link/external-explorer-link'; } from '../../links/eth-explorer-link/eth-explorer-link';
interface TxDetailsNodeVoteProps { interface TxDetailsNodeVoteProps {
txData: BlockExplorerTransactionResult | undefined; txData: BlockExplorerTransactionResult | undefined;
@@ -143,7 +143,7 @@ export function TxHash({ hash }: TxDetailsEthTxHashProps) {
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>Ethereum TX:</TableCell> <TableCell>Ethereum TX:</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink id={hash} type={EthExplorerLinkTypes.tx} /> <EthExplorerLink id={hash} type={EthExplorerLinkTypes.tx} />
</TableCell> </TableCell>
</TableRow> </TableRow>
); );
@@ -12,8 +12,6 @@ import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update'; import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
import { MarketLink } from '../../links'; import { MarketLink } from '../../links';
import { formatNumber } from '@vegaprotocol/utils'; import { formatNumber } from '@vegaprotocol/utils';
import { TransferDetails } from './transfer/transfer-details';
import { proposalToTransfer } from '../lib/proposal-to-transfer';
export type Proposal = components['schemas']['v1ProposalSubmission']; export type Proposal = components['schemas']['v1ProposalSubmission'];
export type ProposalTerms = components['schemas']['vegaProposalTerms']; export type ProposalTerms = components['schemas']['vegaProposalTerms'];
@@ -106,12 +104,6 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
? ProposalSignatureBundleNewAsset ? ProposalSignatureBundleNewAsset
: ProposalSignatureBundleUpdateAsset; : ProposalSignatureBundleUpdateAsset;
let transfer, from;
if (proposal.terms?.newTransfer?.changes) {
transfer = proposalToTransfer(proposal.terms?.newTransfer.changes);
from = proposal.terms.newTransfer.changes.source;
}
return ( return (
<> <>
<TableWithTbody className="mb-8" allowWrap={true}> <TableWithTbody className="mb-8" allowWrap={true}>
@@ -157,26 +149,14 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
</> </>
) : null} ) : null}
</TableWithTbody> </TableWithTbody>
<ProposalSummary <ProposalSummary
id={deterministicId} id={deterministicId}
rationale={proposal.rationale} rationale={proposal.rationale}
terms={proposal?.terms} terms={proposal?.terms}
/> />
{proposalRequiresSignatureBundle(proposal) && ( {proposalRequiresSignatureBundle(proposal) && (
<SignatureBundleComponent id={deterministicId} tx={tx} /> <SignatureBundleComponent id={deterministicId} tx={tx} />
)} )}
{transfer && (
<div className="mt-8">
<TransferDetails
transfer={transfer}
from={from || ''}
id={deterministicId}
/>
</div>
)}
</> </>
); );
}; };
@@ -13,7 +13,6 @@ import {
SPECIAL_CASE_NETWORK_ID, SPECIAL_CASE_NETWORK_ID,
} from '../../links/party-link/party-link'; } from '../../links/party-link/party-link';
import { txSignatureToDeterministicId } from '../lib/deterministic-ids'; import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
import Hash from '../../links/hash';
type Transfer = components['schemas']['commandsv1Transfer']; type Transfer = components['schemas']['commandsv1Transfer'];
@@ -61,7 +60,7 @@ export const TxDetailsTransfer = ({
} }
const from = txData.submitter; const from = txData.submitter;
const id = txSignatureToDeterministicId(txData.signature.value);
return ( return (
<> <>
<TableWithTbody className="mb-8" allowWrap={true}> <TableWithTbody className="mb-8" allowWrap={true}>
@@ -72,7 +71,7 @@ export const TxDetailsTransfer = ({
<TableRow modifier="bordered" data-testid="id"> <TableRow modifier="bordered" data-testid="id">
<TableCell {...sharedHeaderProps}>{t('Transfer ID')}</TableCell> <TableCell {...sharedHeaderProps}>{t('Transfer ID')}</TableCell>
<TableCell> <TableCell>
<Hash text={id} /> {txSignatureToDeterministicId(txData.signature.value)}
</TableCell> </TableCell>
</TableRow> </TableRow>
<TxDetailsShared <TxDetailsShared
@@ -106,7 +105,7 @@ export const TxDetailsTransfer = ({
</TableRow> </TableRow>
) : null} ) : null}
</TableWithTbody> </TableWithTbody>
<TransferDetails from={from} transfer={transfer} id={id} /> <TransferDetails from={from} transfer={transfer} />
</> </>
); );
}; };
@@ -8,7 +8,7 @@ import GovernanceAssetBalance from '../../asset-balance/governance-asset-balance
import type { components } from '../../../../types/explorer'; import type { components } from '../../../../types/explorer';
export const methodText: Record< export const methodText: Record<
components['schemas']['v1UndelegateSubmissionMethod'], components['schemas']['UndelegateSubmissionMethod'],
string string
> = { > = {
METHOD_NOW: 'Immediate', METHOD_NOW: 'Immediate',
@@ -1,60 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { MarketLink } from '../../links';
interface TxDetailsUpdateMarginModeProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
type Mode = components['schemas']['UpdateMarginModeMode'];
const MarginModeLabels: Record<Mode, string> = {
MODE_CROSS_MARGIN: t('Cross margin'),
MODE_ISOLATED_MARGIN: t('Isolated margin'),
MODE_UNSPECIFIED: t('Unspecified'),
};
export const TxDetailsUpdateMarginMode = ({
txData,
pubKey,
blockData,
}: TxDetailsUpdateMarginModeProps) => {
if (!txData || !txData.command.updateMarginMode) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const u: components['schemas']['v1UpdateMarginMode'] =
txData.command.updateMarginMode;
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{u.marketId && (
<TableRow modifier="bordered">
<TableCell>{t('Market ID')}</TableCell>
<TableCell>
<MarketLink id={u.marketId} />
</TableCell>
</TableRow>
)}
{u.mode && (
<TableRow modifier="bordered">
<TableCell>{t('New margin mode')}</TableCell>
<TableCell>{MarginModeLabels[u.mode]}</TableCell>
</TableRow>
)}
{u.marginFactor && (
<TableRow modifier="bordered">
<TableCell>{t('Margin factor')}</TableCell>
<TableCell>{u.marginFactor}</TableCell>
</TableRow>
)}
</TableWithTbody>
);
};
@@ -1,54 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
import { ReferralTeam } from './referrals/team';
interface TxDetailsUpdateReferralProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* A copy of create referral set, effectively.
* Updating a referral set without a team doesn't make sense,
* but is valid, so this component ignores sense.
*/
export const TxDetailsUpdateReferralSet = ({
txData,
pubKey,
blockData,
}: TxDetailsUpdateReferralProps) => {
if (!txData || !txData.command.updateReferralSet || !txData.signature) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const id = txSignatureToDeterministicId(txData.signature.value);
const isTeam = txData.command.updateReferralSet.isTeam || false;
return (
<div>
<TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
<TableRow modifier="bordered">
<TableCell>{isTeam ? t('Team ID') : t('Referral code')}</TableCell>
<TableCell>{id}</TableCell>
</TableRow>
</TableWithTbody>
<ReferralTeam
tx={txData.command.updateReferralSet}
id={id}
creator={txData.submitter}
/>
</div>
);
};
@@ -4,9 +4,9 @@ import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint
import { TxDetailsShared } from './shared/tx-details-shared'; import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table'; import { TableCell, TableRow, TableWithTbody } from '../../table';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../links/external-explorer-link/external-explorer-link'; } from '../../links/eth-explorer-link/eth-explorer-link';
import { txSignatureToDeterministicId } from '../lib/deterministic-ids'; import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
import AssetBalance from '../../asset-balance/asset-balance'; import AssetBalance from '../../asset-balance/asset-balance';
import { useScrollToLocation } from '../../../hooks/scroll-to-location'; import { useScrollToLocation } from '../../../hooks/scroll-to-location';
@@ -57,7 +57,7 @@ export const TxDetailsWithdrawSubmission = ({
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Recipient')}</TableCell> <TableCell>{t('Recipient')}</TableCell>
<TableCell> <TableCell>
<ExternalExplorerLink <EthExplorerLink
id={w.ext.erc20.receiverAddress} id={w.ext.erc20.receiverAddress}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -1,29 +0,0 @@
import type { components } from '../../../../types/explorer';
type TransferProposal = components['schemas']['vegaNewTransferConfiguration'];
type ActualTransfer = components['schemas']['commandsv1Transfer'];
/**
* Converts a governance proposal for a transfer in to a transfer command that the
* TransferDetails component can then render. The types are very similar, but do not
* map precisely to each other due to some missing fields and some different field
* names.
*
* @param proposal Governance proposal for a transfer
* @returns transfer a Transfer object as if it had been submitted
*/
export function proposalToTransfer(proposal: TransferProposal): ActualTransfer {
return {
amount: proposal.amount,
asset: proposal.asset,
// On a transfer, 'from' is determined by the submitter, so there is no 'from' field
// fromAccountType does exist and is just named differently on the proposal
fromAccountType: proposal.sourceType,
oneOff: proposal.oneOff,
recurring: proposal.recurring,
// There is no reference applied on governance initiated transfers
reference: '',
to: proposal.destination,
toAccountType: proposal.destinationType,
};
}
@@ -20,7 +20,6 @@ export type FilterOption =
| 'Amend Order' | 'Amend Order'
| 'Apply Referral Code' | 'Apply Referral Code'
| 'Batch Market Instructions' | 'Batch Market Instructions'
| 'Batch Proposal'
| 'Cancel LiquidityProvision Order' | 'Cancel LiquidityProvision Order'
| 'Cancel Order' | 'Cancel Order'
| 'Cancel Transfer Funds' | 'Cancel Transfer Funds'
@@ -29,7 +28,6 @@ export type FilterOption =
| 'Delegate' | 'Delegate'
| 'Ethereum Key Rotate Submission' | 'Ethereum Key Rotate Submission'
| 'Issue Signatures' | 'Issue Signatures'
| 'Join Team'
| 'Key Rotate Submission' | 'Key Rotate Submission'
| 'Liquidity Provision Order' | 'Liquidity Provision Order'
| 'Node Signature' | 'Node Signature'
@@ -44,61 +42,50 @@ export type FilterOption =
| 'Submit Order' | 'Submit Order'
| 'Transfer Funds' | 'Transfer Funds'
| 'Undelegate' | 'Undelegate'
| 'Update Party Profile'
| 'Update Referral Set' | 'Update Referral Set'
| 'Update Margin Mode'
| 'Validator Heartbeat' | 'Validator Heartbeat'
| 'Vote on Proposal' | 'Vote on Proposal'
| 'Withdraw'; | 'Withdraw';
export const filterOptions: Record<string, FilterOption[]> = { // Alphabetised list of transaction types to appear at the top level
'Market Instructions': [ export const PrimaryFilterOptions: FilterOption[] = [
'Amend LiquidityProvision Order', 'Amend LiquidityProvision Order',
'Amend Order', 'Amend Order',
'Batch Market Instructions', 'Batch Market Instructions',
'Cancel LiquidityProvision Order', 'Cancel LiquidityProvision Order',
'Cancel Order', 'Cancel Order',
'Liquidity Provision Order', 'Cancel Transfer Funds',
'Stop Orders Submission', 'Delegate',
'Stop Orders Cancellation', 'Liquidity Provision Order',
'Submit Order', 'Proposal',
'Update Margin Mode', 'Stop Orders Submission',
], 'Stop Orders Cancellation',
'Transfers and Withdrawals': [ 'Submit Oracle Data',
'Transfer Funds', 'Submit Order',
'Cancel Transfer Funds', 'Transfer Funds',
'Withdraw', 'Undelegate',
], 'Vote on Proposal',
Governance: [ 'Withdraw',
'Batch Proposal', ];
'Delegate',
'Undelegate',
'Vote on Proposal',
'Proposal',
],
Referrals: [
'Apply Referral Code',
'Create Referral Set',
'Join Team',
'Update Party Profile',
'Update Referral Set',
],
'External Data': ['Chain Event', 'Submit Oracle Data'],
Validators: [
'Ethereum Key Rotate Submission',
'Issue Signatures',
'Key Rotate Submission',
'Node Signature',
'Node Vote',
'Protocol Upgrade',
'Register new Node',
'State Variable Proposal',
'Validator Heartbeat',
],
};
export const AllFilterOptions: FilterOption[] = // Alphabetised list of transaction types to nest under a 'More...' submenu
Object.values(filterOptions).flat(); export const SecondaryFilterOptions: FilterOption[] = [
'Chain Event',
'Ethereum Key Rotate Submission',
'Issue Signatures',
'Key Rotate Submission',
'Node Signature',
'Node Vote',
'Protocol Upgrade',
'Register new Node',
'State Variable Proposal',
'Validator Heartbeat',
];
export const AllFilterOptions: FilterOption[] = [
...PrimaryFilterOptions,
...SecondaryFilterOptions,
];
export interface TxFilterProps { export interface TxFilterProps {
filters: Set<FilterOption>; filters: Set<FilterOption>;
@@ -135,33 +122,46 @@ export const TxsFilter = ({ filters, setFilters }: TxFilterProps) => {
<DropdownMenuSeparator /> <DropdownMenuSeparator />
</> </>
)} )}
{PrimaryFilterOptions.map((f) => (
{Object.entries(filterOptions).map(([key, value]) => ( <DropdownMenuCheckboxItem
<DropdownMenuSub key={key}> key={f}
<DropdownMenuSubTrigger> checked={filters.has(f)}
{t(key)} onCheckedChange={() => {
<Icon name="chevron-right" /> // NOTE: These act like radio buttons until the API supports multiple filters
</DropdownMenuSubTrigger> setFilters(new Set([f]));
<DropdownMenuSubContent> }}
{value.map((f) => ( id={`radio-${f}`}
<DropdownMenuCheckboxItem >
key={f} {f}
checked={filters.has(f)} <DropdownMenuItemIndicator>
onCheckedChange={(checked) => { <Icon name="tick-circle" />
// NOTE: These act like radio buttons until the API supports multiple filters </DropdownMenuItemIndicator>
setFilters(new Set([f])); </DropdownMenuCheckboxItem>
}}
id={`radio-${f}`}
>
{f}
<DropdownMenuItemIndicator>
<Icon name="tick-circle" className="inline" />
</DropdownMenuItemIndicator>
</DropdownMenuCheckboxItem>
))}
</DropdownMenuSubContent>
</DropdownMenuSub>
))} ))}
<DropdownMenuSub>
<DropdownMenuSubTrigger>
{t('More Types')}
<Icon name="chevron-right" />
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
{SecondaryFilterOptions.map((f) => (
<DropdownMenuCheckboxItem
key={f}
checked={filters.has(f)}
onCheckedChange={(checked) => {
// NOTE: These act like radio buttons until the API supports multiple filters
setFilters(new Set([f]));
}}
id={`radio-${f}`}
>
{f}
<DropdownMenuItemIndicator>
<Icon name="tick-circle" className="inline" />
</DropdownMenuItemIndicator>
</DropdownMenuCheckboxItem>
))}
</DropdownMenuSubContent>
</DropdownMenuSub>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
); );
@@ -61,4 +61,53 @@ describe('TxsListNavigation', () => {
expect(nextPageMock).toHaveBeenCalledTimes(1); expect(nextPageMock).toHaveBeenCalledTimes(1);
}); });
it('disables "Older" button if hasMoreTxs is false', () => {
render(
<TxsListNavigation
refreshTxs={NOOP}
nextPage={NOOP}
previousPage={NOOP}
hasMoreTxs={false}
hasPreviousPage={false}
>
<span></span>
</TxsListNavigation>
);
expect(screen.getByText('Older')).toBeDisabled();
});
it('disables "Newer" button if hasPreviousPage is false', () => {
render(
<TxsListNavigation
refreshTxs={NOOP}
nextPage={NOOP}
previousPage={NOOP}
hasMoreTxs={true}
hasPreviousPage={false}
>
<span></span>
</TxsListNavigation>
);
expect(screen.getByText('Newer')).toBeDisabled();
});
it('disables both buttons when more and previous are false', () => {
render(
<TxsListNavigation
refreshTxs={NOOP}
nextPage={NOOP}
previousPage={NOOP}
hasMoreTxs={false}
hasPreviousPage={false}
>
<span></span>
</TxsListNavigation>
);
expect(screen.getByText('Newer')).toBeDisabled();
expect(screen.getByText('Older')).toBeDisabled();
});
}); });
@@ -10,8 +10,7 @@ export interface TxListNavigationProps {
loading?: boolean; loading?: boolean;
hasPreviousPage: boolean; hasPreviousPage: boolean;
hasMoreTxs: boolean; hasMoreTxs: boolean;
children?: React.ReactNode; children: React.ReactNode;
isEmpty?: boolean;
} }
/** /**
* Displays a list of transactions with filters and controls to navigate through the list. * Displays a list of transactions with filters and controls to navigate through the list.
@@ -22,8 +21,9 @@ export const TxsListNavigation = ({
refreshTxs, refreshTxs,
nextPage, nextPage,
previousPage, previousPage,
hasMoreTxs,
hasPreviousPage,
children, children,
isEmpty,
loading = false, loading = false,
}: TxListNavigationProps) => { }: TxListNavigationProps) => {
return ( return (
@@ -35,6 +35,7 @@ export const TxsListNavigation = ({
<Button <Button
className="mr-2" className="mr-2"
size="xs" size="xs"
disabled={!hasPreviousPage || loading}
onClick={() => { onClick={() => {
previousPage(); previousPage();
}} }}
@@ -43,7 +44,7 @@ export const TxsListNavigation = ({
</Button> </Button>
<Button <Button
size="xs" size="xs"
disabled={isEmpty} disabled={!hasMoreTxs}
onClick={() => { onClick={() => {
nextPage(); nextPage();
}} }}
@@ -1,7 +1,6 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { components } from '../../../types/explorer'; import type { components } from '../../../types/explorer';
import { VoteIcon } from '../vote-icon/vote-icon'; import { VoteIcon } from '../vote-icon/vote-icon';
import { ExternalChainIcon } from '../links/external-explorer-link/external-chain-icon';
interface TxOrderTypeProps { interface TxOrderTypeProps {
orderType: string; orderType: string;
@@ -49,8 +48,6 @@ const displayString: StringMap = {
StopOrdersSubmission: 'Stop', StopOrdersSubmission: 'Stop',
StopOrdersCancellation: 'Cancel stop', StopOrdersCancellation: 'Cancel stop',
'Stop Orders Cancellation': 'Cancel stop', 'Stop Orders Cancellation': 'Cancel stop',
'Apply Referral Code': 'Referral',
'Create Referral Set': 'Create referral',
}; };
export function getLabelForStopOrderType( export function getLabelForStopOrderType(
@@ -189,9 +186,6 @@ export function getLabelForChainEvent(
} }
return t('Multisig update'); return t('Multisig update');
} else if (chainEvent.contractCall) { } else if (chainEvent.contractCall) {
if (chainEvent.contractCall.error) {
return t('Call error');
}
return t('Contract call'); return t('Contract call');
} }
return t('Chain Event'); return t('Chain Event');
@@ -261,11 +255,6 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
data-testid="tx-type" data-testid="tx-type"
className={`text-sm rounded-md leading-tight px-2 inline-block whitespace-nowrap ${colours}`} className={`text-sm rounded-md leading-tight px-2 inline-block whitespace-nowrap ${colours}`}
> >
{command?.chainEvent && (
<ExternalChainIcon
chainId={command?.chainEvent?.contractCall?.sourceChainId}
/>
)}
{type} {type}
</div> </div>
); );
+1
View File
@@ -14,6 +14,7 @@ export const ENV = {
blockExplorerUrl: windowOrDefault('NX_BLOCK_EXPLORER'), blockExplorerUrl: windowOrDefault('NX_BLOCK_EXPLORER'),
tendermintUrl: windowOrDefault('NX_TENDERMINT_URL'), tendermintUrl: windowOrDefault('NX_TENDERMINT_URL'),
tendermintWebsocketUrl: windowOrDefault('NX_TENDERMINT_WEBSOCKET_URL'), tendermintWebsocketUrl: windowOrDefault('NX_TENDERMINT_WEBSOCKET_URL'),
ethExplorerUrl: windowOrDefault('NX_ETHERSCAN_URL'),
governanceUrl: windowOrDefault('NX_VEGA_GOVERNANCE_URL'), governanceUrl: windowOrDefault('NX_VEGA_GOVERNANCE_URL'),
vegaRepoUrl: windowOrDefault('NX_VEGA_REPO_URL'), vegaRepoUrl: windowOrDefault('NX_VEGA_REPO_URL'),
}, },
@@ -43,7 +43,7 @@ export const getTxsDataUrl = (params: IGetTxsDataUrl) => {
url.searchParams.append('first', count); url.searchParams.append('first', count);
url.searchParams.append('after', params.after); url.searchParams.append('after', params.after);
} else { } else {
url.searchParams.append('first', count); url.searchParams.append('last', count);
} }
// Hacky fix for param as array // Hacky fix for param as array
@@ -6,7 +6,7 @@ describe('getTxsDataUrl', () => {
count: 10, count: 10,
baseUrl: 'https://example.com/transactions', baseUrl: 'https://example.com/transactions',
}; };
const expectedUrl = 'https://example.com/transactions?first=10'; const expectedUrl = 'https://example.com/transactions?last=10';
expect(getTxsDataUrl(params)).toEqual(expectedUrl); expect(getTxsDataUrl(params)).toEqual(expectedUrl);
}); });
@@ -41,7 +41,7 @@ describe('getTxsDataUrl', () => {
baseUrl: 'https://example.com/transactions', baseUrl: 'https://example.com/transactions',
}; };
const expectedUrl = const expectedUrl =
'https://example.com/transactions?first=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234'; 'https://example.com/transactions?last=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
expect(getTxsDataUrl(params)).toEqual(expectedUrl); expect(getTxsDataUrl(params)).toEqual(expectedUrl);
}); });
+6 -6
View File
@@ -31,14 +31,14 @@ export interface IUseTxsData {
} }
export const useTxsData = ({ export const useTxsData = ({
count = 50, count = 25,
before, before,
after, after,
filters, filters,
party, party,
}: IUseTxsData) => { }: IUseTxsData) => {
const [, setSearchParams] = useSearchParams(); const [, setSearchParams] = useSearchParams();
let hasMoreTxs = false; let hasMoreTxs = true;
let txsData: BlockExplorerTransactionResult[] = []; let txsData: BlockExplorerTransactionResult[] = [];
const url = getTxsDataUrl({ const url = getTxsDataUrl({
@@ -60,8 +60,8 @@ export const useTxsData = ({
} }
const nextPage = useCallback(() => { const nextPage = useCallback(() => {
const before = data?.transactions.at(-1)?.cursor || ''; const after = data?.transactions.at(-1)?.cursor || '';
const params: URLSearchParamsInit = { before }; const params: URLSearchParamsInit = { after };
if (filters) { if (filters) {
params.filters = Array.from(filters).join(','); params.filters = Array.from(filters).join(',');
} }
@@ -69,8 +69,8 @@ export const useTxsData = ({
}, [filters, data, setSearchParams]); }, [filters, data, setSearchParams]);
const previousPage = useCallback(() => { const previousPage = useCallback(() => {
const after = data?.transactions[0]?.cursor || ''; const before = data?.transactions[0]?.cursor || '';
const params: URLSearchParamsInit = { after }; const params: URLSearchParamsInit = { before };
if (filters && filters.size > 0 && filters.size === 1) { if (filters && filters.size > 0 && filters.size === 1) {
params.filters = Array.from(filters)[0]; params.filters = Array.from(filters)[0];
} }
@@ -78,7 +78,6 @@ fragment ExplorerOracleDataSource on OracleSpec {
} }
} }
} }
sourceChainId
filters { filters {
key { key {
name name
@@ -119,7 +118,6 @@ fragment ExplorerOracleDataSource on OracleSpec {
address address
requiredConfirmations requiredConfirmations
method method
sourceChainId
filters { filters {
key { key {
type type
@@ -67,7 +67,6 @@ fragment ExplorerOracleDataSourceSpec on ExternalDataSourceSpec {
sourceType { sourceType {
... on EthCallSpec { ... on EthCallSpec {
address address
sourceChainId
} }
... on DataSourceSpecConfiguration { ... on DataSourceSpecConfiguration {
signers { signers {
@@ -5,19 +5,19 @@ import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; const defaultOptions = {} as const;
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } }; export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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 }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, sourceChainId: number, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } }; export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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 }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>; export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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 }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, sourceChainId: number, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null }; export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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 }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{ export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{
id: Types.Scalars['ID']; id: Types.Scalars['ID'];
}>; }>;
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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 }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, sourceChainId: number, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null }; export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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 }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
export const ExplorerOracleDataConnectionFragmentDoc = gql` export const ExplorerOracleDataConnectionFragmentDoc = gql`
fragment ExplorerOracleDataConnection on OracleSpec { fragment ExplorerOracleDataConnection on OracleSpec {
@@ -101,7 +101,6 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
} }
} }
} }
sourceChainId
filters { filters {
key { key {
name name
@@ -142,7 +141,6 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
address address
requiredConfirmations requiredConfirmations
method method
sourceChainId
filters { filters {
key { key {
type type
@@ -9,12 +9,12 @@ export type ExplorerOracleFutureFragment = { __typename?: 'Future', dataSourceSp
export type ExplorerOracleForMarketsMarketFragment = { __typename?: 'Market', id: string, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Spot' } } } }; export type ExplorerOracleForMarketsMarketFragment = { __typename?: 'Market', id: string, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Spot' } } } };
export type ExplorerOracleDataSourceSpecFragment = { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } | { __typename?: 'EthCallSpec', address: string, sourceChainId: number } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }; export type ExplorerOracleDataSourceSpecFragment = { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } | { __typename?: 'EthCallSpec', address: string } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } };
export type ExplorerOracleFormMarketsQueryVariables = Types.Exact<{ [key: string]: never; }>; export type ExplorerOracleFormMarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerOracleFormMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Spot' } } } } }> } | null, oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } | { __typename?: 'EthCallSpec', address: string, sourceChainId: number } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null }; export type ExplorerOracleFormMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Spot' } } } } }> } | null, oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } | { __typename?: 'EthCallSpec', address: string } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
export const ExplorerOracleFutureFragmentDoc = gql` export const ExplorerOracleFutureFragmentDoc = gql`
fragment ExplorerOracleFuture on Future { fragment ExplorerOracleFuture on Future {
@@ -90,7 +90,6 @@ export const ExplorerOracleDataSourceSpecFragmentDoc = gql`
sourceType { sourceType {
... on EthCallSpec { ... on EthCallSpec {
address address
sourceChainId
} }
... on DataSourceSpecConfiguration { ... on DataSourceSpecConfiguration {
signers { signers {
@@ -28,7 +28,7 @@ export function isInternalSourceType(s: SourceType) {
export function getExternalType(s: SourceType) { export function getExternalType(s: SourceType) {
if (s.sourceType.__typename === 'EthCallSpec') { if (s.sourceType.__typename === 'EthCallSpec') {
return 'Contract Call'; return 'Ethereum Contract Call';
} else { } else {
return 'External Data'; return 'External Data';
} }
@@ -1,24 +1,18 @@
import { TableRow, TableCell, TableHeader } from '../../../components/table'; import { TableRow, TableCell, TableHeader } from '../../../components/table';
import type { SourceType } from './oracle'; import type { SourceType } from './oracle';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../components/links/external-explorer-link/external-explorer-link'; } from '../../../components/links/eth-explorer-link/eth-explorer-link';
import { getExternalChainLabel } from '../../../components/links/external-explorer-link/external-chain';
import { t } from 'i18next';
interface OracleDetailsEthSourceProps { interface OracleDetailsEthSourceProps {
sourceType: SourceType; sourceType: SourceType;
chain?: string;
} }
/** /**
* Given an Oracle that sources data from Ethereum, this component will render * Given an Oracle that sources data from Ethereum, this component will render
* a link to the smart contract and some basic details * a link to the smart contract and some basic details
*/ */
export function OracleEthSource({ export function OracleEthSource({ sourceType }: OracleDetailsEthSourceProps) {
sourceType,
chain = '1',
}: OracleDetailsEthSourceProps) {
if ( if (
sourceType.__typename !== 'DataSourceDefinitionExternal' || sourceType.__typename !== 'DataSourceDefinitionExternal' ||
sourceType.sourceType.__typename !== 'EthCallSpec' sourceType.sourceType.__typename !== 'EthCallSpec'
@@ -32,20 +26,11 @@ export function OracleEthSource({
return null; return null;
} }
const chainLabel = getExternalChainLabel(chain);
return ( return (
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableHeader scope="row"> <TableHeader scope="row">Ethereum Contract</TableHeader>
{chainLabel} {t('Contract')}
</TableHeader>
<TableCell modifier="bordered"> <TableCell modifier="bordered">
<ExternalExplorerLink <EthExplorerLink id={address} type={EthExplorerLinkTypes.address} />
chain={chain}
id={address}
type={EthExplorerLinkTypes.address}
code={true}
/>
<span className="mx-3">&rArr;</span> <span className="mx-3">&rArr;</span>
<code>{sourceType.sourceType.method}</code> <code>{sourceType.sourceType.method}</code>
</TableCell> </TableCell>
@@ -1,8 +1,8 @@
import { PartyLink } from '../../../components/links'; import { PartyLink } from '../../../components/links';
import { import {
ExternalExplorerLink, EthExplorerLink,
EthExplorerLinkTypes, EthExplorerLinkTypes,
} from '../../../components/links/external-explorer-link/external-explorer-link'; } from '../../../components/links/eth-explorer-link/eth-explorer-link';
import { TableRow, TableCell, TableHeader } from '../../../components/table'; import { TableRow, TableCell, TableHeader } from '../../../components/table';
import { remove0x } from '@vegaprotocol/utils'; import { remove0x } from '@vegaprotocol/utils';
@@ -37,15 +37,13 @@ export function getAddressLink(signer: Signer) {
} }
if (signer.__typename === 'ETHAddress') { if (signer.__typename === 'ETHAddress') {
return ( return <EthExplorerLink id={address} type={EthExplorerLinkTypes.address} />;
<ExternalExplorerLink id={address} type={EthExplorerLinkTypes.address} />
);
} else if (signer.__typename === 'PubKey' && address.length !== 64) { } else if (signer.__typename === 'PubKey' && address.length !== 64) {
// This is a hack: some older oracles were submitted before proper checks stopped // This is a hack: some older oracles were submitted before proper checks stopped
// ETH addresses being returned as Vega addresses // ETH addresses being returned as Vega addresses
// Hacky 0x prefixing as a bonus // Hacky 0x prefixing as a bonus
return ( return (
<ExternalExplorerLink <EthExplorerLink
id={`0x${remove0x(address)}`} id={`0x${remove0x(address)}`}
type={EthExplorerLinkTypes.address} type={EthExplorerLinkTypes.address}
/> />
@@ -63,10 +61,7 @@ interface OracleDetailsSignersProps {
/** /**
* Given an Oracle, this component will render either a link to Ethereum * Given an Oracle, this component will render either a link to Ethereum
* or the Vega party depending on which type is specified. * or the Vega party depending on which type is specified
*
* Note that this won't be shown for external contract calls as they do not
* have a signer in the same way.
*/ */
export function OracleSigners({ sourceType }: OracleDetailsSignersProps) { export function OracleSigners({ sourceType }: OracleDetailsSignersProps) {
if (sourceType.__typename !== 'DataSourceDefinitionExternal') { if (sourceType.__typename !== 'DataSourceDefinitionExternal') {
@@ -42,11 +42,6 @@ export const OracleDetails = ({
dataConnection, dataConnection,
}: OracleDetailsProps) => { }: OracleDetailsProps) => {
const sourceType = dataSource.dataSourceSpec.spec.data.sourceType; const sourceType = dataSource.dataSourceSpec.spec.data.sourceType;
const chain =
dataSource.dataSourceSpec.spec.data.sourceType.sourceType.__typename ===
'EthCallSpec'
? dataSource.dataSourceSpec.spec.data.sourceType.sourceType.sourceChainId.toString()
: undefined;
return ( return (
<div> <div>
@@ -65,7 +60,7 @@ export const OracleDetails = ({
</TableCell> </TableCell>
</TableRow> </TableRow>
<OracleSigners sourceType={sourceType} /> <OracleSigners sourceType={sourceType} />
<OracleEthSource sourceType={sourceType} chain={chain} /> <OracleEthSource sourceType={sourceType} />
<OracleMarkets id={id} /> <OracleMarkets id={id} />
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableHeader scope="row">{t('Filter')}</TableHeader> <TableHeader scope="row">{t('Filter')}</TableHeader>
@@ -12,5 +12,4 @@ export const Routes = {
ORACLES: 'oracles', ORACLES: 'oracles',
NETWORK_PARAMETERS: 'network-parameters', NETWORK_PARAMETERS: 'network-parameters',
DISCLAIMER: 'disclaimer', DISCLAIMER: 'disclaimer',
TREASURY: 'treasury',
}; };
@@ -30,7 +30,6 @@ import { PartyAccountsByAsset } from './parties/id/accounts';
import { Disclaimer } from './pages/disclaimer'; import { Disclaimer } from './pages/disclaimer';
import { useFeatureFlags } from '@vegaprotocol/environment'; import { useFeatureFlags } from '@vegaprotocol/environment';
import RestrictedPage from './restricted'; import RestrictedPage from './restricted';
import { NetworkTreasury } from './treasury';
export type Navigable = { export type Navigable = {
path: string; path: string;
@@ -230,17 +229,6 @@ export const useRouterConfig = () => {
] ]
: []; : [];
const treasuryRoutes: Route[] = [
{
path: Routes.TREASURY,
handle: {
name: t('Treasury'),
text: t('Treasury'),
breadcrumb: () => <Link to={Routes.TREASURY}>{t('Treasury')}</Link>,
},
element: <NetworkTreasury />,
},
];
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
? [ ? [
{ {
@@ -370,7 +358,6 @@ export const useRouterConfig = () => {
...marketsRoutes, ...marketsRoutes,
...networkParametersRoutes, ...networkParametersRoutes,
...validators, ...validators,
...treasuryRoutes,
], ],
}, },
{ {
@@ -1,12 +0,0 @@
query ExplorerTreasury {
assetsConnection(pagination: { last: 1000 }) {
edges {
node {
id
networkTreasuryAccount {
balance
}
}
}
}
}
@@ -1,44 +0,0 @@
query ExplorerTreasuryTransfers {
transfersConnection(
partyId: "network"
direction: ToOrFrom
pagination: { last: 200 }
) {
pageInfo {
hasNextPage
}
edges {
node {
transfer {
timestamp
from
amount
to
status
reason
toAccountType
fromAccountType
asset {
id
}
id
status
kind {
... on OneOffTransfer {
deliverOn
}
... on RecurringTransfer {
startEpoch
}
... on OneOffGovernanceTransfer {
deliverOn
}
... on RecurringGovernanceTransfer {
endEpoch
}
}
}
}
}
}
}
@@ -1,52 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerTreasuryQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerTreasuryQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, networkTreasuryAccount?: { __typename?: 'AccountBalance', balance: string } | null } } | null> | null } | null };
export const ExplorerTreasuryDocument = gql`
query ExplorerTreasury {
assetsConnection(pagination: {last: 1000}) {
edges {
node {
id
networkTreasuryAccount {
balance
}
}
}
}
}
`;
/**
* __useExplorerTreasuryQuery__
*
* To run a query within a React component, call `useExplorerTreasuryQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerTreasuryQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerTreasuryQuery({
* variables: {
* },
* });
*/
export function useExplorerTreasuryQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>(ExplorerTreasuryDocument, options);
}
export function useExplorerTreasuryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>(ExplorerTreasuryDocument, options);
}
export type ExplorerTreasuryQueryHookResult = ReturnType<typeof useExplorerTreasuryQuery>;
export type ExplorerTreasuryLazyQueryHookResult = ReturnType<typeof useExplorerTreasuryLazyQuery>;
export type ExplorerTreasuryQueryResult = Apollo.QueryResult<ExplorerTreasuryQuery, ExplorerTreasuryQueryVariables>;
@@ -1,84 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerTreasuryTransfersQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerTreasuryTransfersQuery = { __typename?: 'Query', transfersConnection?: { __typename?: 'TransferConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'TransferEdge', node: { __typename?: 'TransferNode', transfer: { __typename?: 'Transfer', timestamp: any, from: string, amount: string, to: string, status: Types.TransferStatus, reason?: string | null, toAccountType: Types.AccountType, fromAccountType: Types.AccountType, id: string, asset?: { __typename?: 'Asset', id: string } | null, kind: { __typename?: 'OneOffGovernanceTransfer', deliverOn?: any | null } | { __typename?: 'OneOffTransfer', deliverOn?: any | null } | { __typename?: 'RecurringGovernanceTransfer', endEpoch?: number | null } | { __typename?: 'RecurringTransfer', startEpoch: number } } } } | null> | null } | null };
export const ExplorerTreasuryTransfersDocument = gql`
query ExplorerTreasuryTransfers {
transfersConnection(
partyId: "network"
direction: ToOrFrom
pagination: {last: 200}
) {
pageInfo {
hasNextPage
}
edges {
node {
transfer {
timestamp
from
amount
to
status
reason
toAccountType
fromAccountType
asset {
id
}
id
status
kind {
... on OneOffTransfer {
deliverOn
}
... on RecurringTransfer {
startEpoch
}
... on OneOffGovernanceTransfer {
deliverOn
}
... on RecurringGovernanceTransfer {
endEpoch
}
}
}
}
}
}
}
`;
/**
* __useExplorerTreasuryTransfersQuery__
*
* To run a query within a React component, call `useExplorerTreasuryTransfersQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerTreasuryTransfersQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerTreasuryTransfersQuery({
* variables: {
* },
* });
*/
export function useExplorerTreasuryTransfersQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>(ExplorerTreasuryTransfersDocument, options);
}
export function useExplorerTreasuryTransfersLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>(ExplorerTreasuryTransfersDocument, options);
}
export type ExplorerTreasuryTransfersQueryHookResult = ReturnType<typeof useExplorerTreasuryTransfersQuery>;
export type ExplorerTreasuryTransfersLazyQueryHookResult = ReturnType<typeof useExplorerTreasuryTransfersLazyQuery>;
export type ExplorerTreasuryTransfersQueryResult = Apollo.QueryResult<ExplorerTreasuryTransfersQuery, ExplorerTreasuryTransfersQueryVariables>;

Some files were not shown because too many files have changed in this diff Show More