Compare commits
91
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85faa3b1d0 | ||
|
|
4dd63da62b | ||
|
|
2e8dd294de | ||
|
|
8917ceb08a | ||
|
|
87f116daee | ||
|
|
9e91746488 | ||
|
|
5b4ed1a0c3 | ||
|
|
56731e34cc | ||
|
|
026e5f5679 | ||
|
|
e75c160579 | ||
|
|
5d613396e1 | ||
|
|
da551f9d3c | ||
|
|
47e4861fdb | ||
|
|
1a85a89440 | ||
|
|
7ab412a8f7 | ||
|
|
5a14174a81 | ||
|
|
5e00b93783 | ||
|
|
31d9b023e6 | ||
|
|
103e503a47 | ||
|
|
1952cb0e78 | ||
|
|
69340f4ddf | ||
|
|
c52bf2200e | ||
|
|
8dccee69f2 | ||
|
|
c7a6fdd879 | ||
|
|
8bcdaf4cda | ||
|
|
3eb1359504 | ||
|
|
39cf8ad4d6 | ||
|
|
faef98f0ae | ||
|
|
710e2daa27 | ||
|
|
06ea3924fa | ||
|
|
965e2d8972 | ||
|
|
73de2fed43 | ||
|
|
536859e067 | ||
|
|
54bfaef473 | ||
|
|
e2cad707a7 | ||
|
|
ec8d9798ab | ||
|
|
e629c1e81a | ||
|
|
25feed793c | ||
|
|
2867367e16 | ||
|
|
50b408e74d | ||
|
|
8f531c7c05 | ||
|
|
350d16fee1 | ||
|
|
c052c66266 | ||
|
|
e2032df236 | ||
|
|
9ece19869d | ||
|
|
22abc8160c | ||
|
|
b1a7a22bf9 | ||
|
|
c4b38cc0dc | ||
|
|
44d6d931d5 | ||
|
|
7f1e47d7fd | ||
|
|
0eddfa4073 | ||
|
|
83a034ce91 | ||
|
|
5ab5aa01a2 | ||
|
|
b22f9156b5 | ||
|
|
bab5e2018b | ||
|
|
3f7692ce75 | ||
|
|
b69fb3b0e5 | ||
|
|
83af6e74f6 | ||
|
|
b9908003ba | ||
|
|
1f9af79215 | ||
|
|
e31e40a7ef | ||
|
|
81f6b3cc15 | ||
|
|
8adb208a91 | ||
|
|
545c3a17d2 | ||
|
|
ad76988f63 | ||
|
|
d2bbd7e1fc | ||
|
|
fb1ebb3bdf | ||
|
|
b2489537ef | ||
|
|
382a7ea411 | ||
|
|
1ae2b4c153 | ||
|
|
e8875e817e | ||
|
|
4a01c880f3 | ||
|
|
f7f00dc7bb | ||
|
|
d151b6e923 | ||
|
|
8f8e9c1061 | ||
|
|
b40358a723 | ||
|
|
01f0934da3 | ||
|
|
df7755dbc4 | ||
|
|
9073234040 | ||
|
|
053cd0fa27 | ||
|
|
961dc88dc7 | ||
|
|
b16607a844 | ||
|
|
d0d804631c | ||
|
|
3e778a88dc | ||
|
|
dbb09c9c79 | ||
|
|
06f44b67de | ||
|
|
1861852852 | ||
|
|
3ee0eee80d | ||
|
|
69ce870a81 | ||
|
|
d8467c3206 | ||
|
|
de87f40a01 |
@@ -50,6 +50,20 @@ jobs:
|
||||
projects=[${projects// /,}]
|
||||
echo PROJECTS=$projects >> $GITHUB_ENV
|
||||
|
||||
# Rename required because some of the files contains the colon character (in the dates)
|
||||
- name: Rename files to allow archive
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
while read -r file; do
|
||||
mv "${file}" "$(echo ${file} | sed 's|:|-|g')"
|
||||
done< <(find /home/runner/.vegacapsule/testnet/logs -type f)
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: logs-${{ matrix.project }}
|
||||
path: /home/runner/.vegacapsule/testnet/logs
|
||||
|
||||
outputs:
|
||||
projects: ${{ env.PROJECTS }}
|
||||
|
||||
|
||||
@@ -64,12 +64,11 @@ jobs:
|
||||
######
|
||||
|
||||
- name: Run Cypress tests
|
||||
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
|
||||
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome --env.grepTags="${{ inputs.tags }}"
|
||||
working-directory: frontend-monorepo
|
||||
env:
|
||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
|
||||
CYPRESS_grepTags: ${{ inputs.tags }}
|
||||
|
||||
######
|
||||
## Upload logs
|
||||
|
||||
@@ -8,14 +8,16 @@ jobs:
|
||||
lint_pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Use Node.js 16
|
||||
id: Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.14.0
|
||||
- name: Install commitlint cli and config
|
||||
run: npm install @commitlint/cli @commitlint/config-conventional
|
||||
- name: Create config
|
||||
run: echo "module.exports = {extends:['@commitlint/config-conventional']};" > commitlint.config.js
|
||||
- name: Install root dependencies
|
||||
run: yarn install
|
||||
- name: Check PR title
|
||||
run: echo "${{ github.event.pull_request.title }}" | npx commitlint
|
||||
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
|
||||
|
||||
@@ -6,7 +6,7 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
||||
NX_TENDERMINT_URL=https://tm.n01.sandbox.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.sandbox.vega.xyz/websocket
|
||||
NX_TENDERMINT_URL=https://tm.sandbox.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.sandbox.vega.xyz/websocket
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_GOVERNANCE_URL=https://sandbox.token.vega.xyz
|
||||
|
||||
@@ -6,3 +6,4 @@ NX_VEGA_ENV=STAGNET3
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
|
||||
NX_VEGA_GOVERNANCE_URL=https://stagnet3.token.vega.xyz
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases/
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Main } from './components/main';
|
||||
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { Footer } from './components/footer/footer';
|
||||
import { AnnouncementBanner, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
function App() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
@@ -40,6 +41,15 @@ function App() {
|
||||
return (
|
||||
<TendermintWebsocketProvider>
|
||||
<NetworkLoader cache={cacheConfig}>
|
||||
<AnnouncementBanner>
|
||||
<div className="font-alpha calt uppercase text-center text-lg text-white">
|
||||
<span className="pr-4">The Mainnet sims are live!</span>
|
||||
<ExternalLink href="https://fairground.wtf/">
|
||||
Come help stress test the network
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</AnnouncementBanner>
|
||||
|
||||
<div className={layoutClasses}>
|
||||
<Header menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
|
||||
<Nav menuOpen={menuOpen} />
|
||||
|
||||
@@ -2,6 +2,7 @@ query ExplorerMarket($id: ID!) {
|
||||
market(id: $id) {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
name
|
||||
|
||||
+2
-1
@@ -8,7 +8,7 @@ export type ExplorerMarketQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', decimals: number } } } } } | null };
|
||||
export type ExplorerMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', decimals: number } } } } } | null };
|
||||
|
||||
|
||||
export const ExplorerMarketDocument = gql`
|
||||
@@ -16,6 +16,7 @@ export const ExplorerMarketDocument = gql`
|
||||
market(id: $id) {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
name
|
||||
|
||||
@@ -55,6 +55,7 @@ describe('Market link component', () => {
|
||||
market: {
|
||||
id: '123',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 2,
|
||||
state: 'irrelevant-test-data',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
|
||||
@@ -117,7 +117,7 @@ const NestedDataListItem = ({
|
||||
)}
|
||||
</h4>
|
||||
{!hasChildren && (
|
||||
<code className="text-vega-light-400 mb-2 last:mb-0 dark:text-vega-dark-400 break-all">
|
||||
<code className="text-vega-light-100 mb-2 last:mb-0 dark:text-vega-dark-100 break-all">
|
||||
{JSON.stringify(value, null, ' ')}
|
||||
</code>
|
||||
)}
|
||||
|
||||
@@ -19,6 +19,7 @@ fragment ExplorerDeterministicOrderFields on Order {
|
||||
market {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
name
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } };
|
||||
export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } };
|
||||
|
||||
export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{
|
||||
orderId: Types.Scalars['ID'];
|
||||
@@ -11,7 +11,7 @@ export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } } };
|
||||
export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } } };
|
||||
|
||||
export const ExplorerDeterministicOrderFieldsFragmentDoc = gql`
|
||||
fragment ExplorerDeterministicOrderFields on Order {
|
||||
@@ -35,6 +35,7 @@ export const ExplorerDeterministicOrderFieldsFragmentDoc = gql`
|
||||
market {
|
||||
id
|
||||
decimalPlaces
|
||||
positionDecimalPlaces
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
name
|
||||
|
||||
@@ -61,6 +61,7 @@ function renderExistingAmend(id: string, version: number, amend: Amend) {
|
||||
__typename: 'Market',
|
||||
id: '789',
|
||||
state: 'STATUS_ACTIVE',
|
||||
positionDecimalPlaces: 2,
|
||||
decimalPlaces: '5',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
@@ -88,6 +89,7 @@ function renderExistingAmend(id: string, version: number, amend: Amend) {
|
||||
market: {
|
||||
id: '789',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 2,
|
||||
state: 'irrelevant-test-data',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { MarketLink } from '../links';
|
||||
import PriceInMarket from '../price-in-market/price-in-market';
|
||||
import { Time } from '../time';
|
||||
import { sideText, statusText, tifFull, tifShort } from './lib/order-labels';
|
||||
import SizeInMarket from '../size-in-market/size-in-market';
|
||||
|
||||
export interface DeterministicOrderDetailsProps {
|
||||
id: string;
|
||||
@@ -90,7 +91,7 @@ const DeterministicOrderDetails = ({
|
||||
<div className="mb-12 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">{t('Size')}</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0">
|
||||
{o.size}
|
||||
<SizeInMarket size={o.size} marketId={o.market.id} />
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ const mock = {
|
||||
__typename: 'Market',
|
||||
id: '789',
|
||||
state: 'STATE_ACTIVE',
|
||||
positionDecimalPlaces: 2,
|
||||
decimalPlaces: 2,
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
|
||||
@@ -90,6 +90,7 @@ describe('Order TX Summary component', () => {
|
||||
market: {
|
||||
id: '123',
|
||||
decimalPlaces: 2,
|
||||
positionDecimalPlaces: 2,
|
||||
state: 'irrelevant-test-data',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
|
||||
@@ -37,6 +37,7 @@ const fullMock = {
|
||||
market: {
|
||||
id: '123',
|
||||
decimalPlaces: 2,
|
||||
positionDecimalPlaces: 2,
|
||||
state: 'irrelevant-test-data',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import React from 'react';
|
||||
import { StatusMessage } from '../status-message';
|
||||
|
||||
interface RenderFetchedProps {
|
||||
|
||||
@@ -66,7 +66,9 @@ export const Search = () => {
|
||||
className="text-white"
|
||||
hasError={Boolean(error?.message)}
|
||||
type="text"
|
||||
placeholder={t('Enter block number, party id or transaction hash')}
|
||||
placeholder={t(
|
||||
'Enter block number, public key or transaction hash'
|
||||
)}
|
||||
/>
|
||||
{error?.message && (
|
||||
<div className="bg-white border border-t-0 border-accent absolute top-[100%] flex-1 w-full pb-2 px-2 rounded-b text-black">
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { render } from '@testing-library/react';
|
||||
import SizeInMarket from './size-in-market';
|
||||
import type { DecimalSource } from './size-in-market';
|
||||
import { ExplorerMarketDocument } from '../links/market-link/__generated__/Market';
|
||||
|
||||
function renderComponent(
|
||||
price: string,
|
||||
marketId: string,
|
||||
mocks: MockedResponse[],
|
||||
decimalSource: DecimalSource = 'MARKET'
|
||||
) {
|
||||
return (
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<MemoryRouter>
|
||||
<SizeInMarket
|
||||
marketId={marketId}
|
||||
size={price}
|
||||
decimalSource={decimalSource}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
}
|
||||
|
||||
const fullMock = {
|
||||
request: {
|
||||
query: ExplorerMarketDocument,
|
||||
variables: {
|
||||
id: '123',
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
market: {
|
||||
id: '123',
|
||||
decimalPlaces: 2,
|
||||
positionDecimalPlaces: 2,
|
||||
state: 'irrelevant-test-data',
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'test dai',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: 'dai',
|
||||
settlementAsset: {
|
||||
decimals: 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('Size in Market component', () => {
|
||||
it('Renders the raw size when there is no market data', () => {
|
||||
const res = render(renderComponent('100', '123', []));
|
||||
expect(res.getByText('100')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders the formatted size when market data is fetched', async () => {
|
||||
const res = render(renderComponent('100', '123', [fullMock]));
|
||||
expect(await res.findByText('1.00')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
|
||||
import { useExplorerMarketQuery } from '../links/market-link/__generated__/Market';
|
||||
|
||||
export type DecimalSource = 'MARKET';
|
||||
|
||||
export type PriceInMarketProps = {
|
||||
marketId: string;
|
||||
size: string | number;
|
||||
decimalSource?: DecimalSource;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a market ID and an order size it will fetch the market
|
||||
* order size, and format the size accordingly
|
||||
*/
|
||||
const SizeInMarket = ({
|
||||
marketId,
|
||||
size,
|
||||
decimalSource = 'MARKET',
|
||||
}: PriceInMarketProps) => {
|
||||
const { data } = useExplorerMarketQuery({
|
||||
variables: { id: marketId },
|
||||
fetchPolicy: 'cache-first',
|
||||
});
|
||||
|
||||
let label = size;
|
||||
|
||||
if (data) {
|
||||
if (decimalSource === 'MARKET' && data.market?.positionDecimalPlaces) {
|
||||
label = addDecimalsFormatNumber(size, data.market.positionDecimalPlaces);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<label>
|
||||
<span>{label}</span>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
export default SizeInMarket;
|
||||
@@ -0,0 +1,62 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { BlockLink } from '../../links';
|
||||
import { StatusMessage } from '../../status-message';
|
||||
import { ENV } from '../../../config/env';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
|
||||
interface TxDetailsProtocolUpgradeProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validator event: Protocol Upgrade proposal
|
||||
*/
|
||||
export const TxDetailsProtocolUpgrade = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsProtocolUpgradeProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const upgrade: components['schemas']['v1ProtocolUpgradeProposal'] =
|
||||
txData.command.protocolUpgradeProposal;
|
||||
|
||||
if (!upgrade || !upgrade.upgradeBlockHeight || !upgrade.vegaReleaseTag) {
|
||||
return (
|
||||
<StatusMessage>{t('Invalid upgrade proposal format')}</StatusMessage>
|
||||
);
|
||||
}
|
||||
|
||||
const urlBase = ENV.dataSources.vegaRepoUrl;
|
||||
const release = upgrade.vegaReleaseTag;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8">
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Upgrade at block')}</TableCell>
|
||||
<TableCell>
|
||||
<BlockLink height={upgrade.upgradeBlockHeight} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Upgrade to')}</TableCell>
|
||||
<TableCell>
|
||||
<ExternalLink href={`${urlBase}${release}`}>
|
||||
{upgrade.vegaReleaseTag}
|
||||
</ExternalLink>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -19,6 +19,8 @@ import { TxDetailsLiquidityAmendment } from './tx-liquidity-amend';
|
||||
import { TxDetailsLiquidityCancellation } from './tx-liquidity-cancel';
|
||||
import { TxDetailsDataSubmission } from './tx-data-submission';
|
||||
import { TxProposalVote } from './tx-proposal-vote';
|
||||
import { TxDetailsProtocolUpgrade } from './tx-details-protocol-upgrade';
|
||||
import { TxDetailsIssueSignatures } from './tx-issue-signatures';
|
||||
|
||||
interface TxDetailsWrapperProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -66,10 +68,14 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
||||
|
||||
// These come from https://github.com/vegaprotocol/vega/blob/develop/core/txn/command.go#L72-L98
|
||||
switch (txData.type) {
|
||||
case 'Issue Signatures':
|
||||
return TxDetailsIssueSignatures;
|
||||
case 'Submit Order':
|
||||
return TxDetailsOrder;
|
||||
case 'Submit Oracle Data':
|
||||
return TxDetailsDataSubmission;
|
||||
case 'Protocol Upgrade':
|
||||
return TxDetailsProtocolUpgrade;
|
||||
case 'Cancel Order':
|
||||
return TxDetailsOrderCancel;
|
||||
case 'Amend Order':
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
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 {
|
||||
EthExplorerLink,
|
||||
EthExplorerLinkTypes,
|
||||
} from '../../links/eth-explorer-link/eth-explorer-link';
|
||||
import { BlockLink } from '../../links';
|
||||
|
||||
type EthKeyRotate = components['schemas']['v1EthereumKeyRotateSubmission'];
|
||||
interface TxDetailsEthKeyRotateProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A node is changing ethereum key
|
||||
*/
|
||||
export const TxDetailsEthKeyRotate = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsEthKeyRotateProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const k: EthKeyRotate = txData.command;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8">
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{k.targetBlock ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Target block')}</TableCell>
|
||||
<TableCell>
|
||||
<BlockLink height={k.targetBlock} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.currentAddress ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Old Address')}</TableCell>
|
||||
<TableCell>
|
||||
<EthExplorerLink
|
||||
type={EthExplorerLinkTypes.address}
|
||||
id={k.currentAddress}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.newAddress ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('New Address')}</TableCell>
|
||||
<TableCell>
|
||||
<EthExplorerLink
|
||||
type={EthExplorerLinkTypes.address}
|
||||
id={k.newAddress}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.submitterAddress ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Submitter address')}</TableCell>
|
||||
<TableCell>
|
||||
<EthExplorerLink
|
||||
type={EthExplorerLinkTypes.address}
|
||||
id={k.submitterAddress}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -11,8 +11,7 @@ interface TxDetailsGenericProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is not yet a custom component for a transaction, just display
|
||||
* the basic details. This allows someone to view the decoded transaction.
|
||||
* A node is
|
||||
*/
|
||||
export const TxDetailsGeneric = ({
|
||||
txData,
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
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 { TableRow, TableCell, TableWithTbody } from '../../table';
|
||||
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import {
|
||||
EthExplorerLink,
|
||||
EthExplorerLinkTypes,
|
||||
} from '../../links/eth-explorer-link/eth-explorer-link';
|
||||
import { NodeLink } from '../../links';
|
||||
|
||||
type Command = components['schemas']['v1IssueSignatures'];
|
||||
|
||||
const kind: Record<components['schemas']['v1NodeSignatureKind'], string> = {
|
||||
NODE_SIGNATURE_KIND_UNSPECIFIED: 'Unspecified',
|
||||
NODE_SIGNATURE_KIND_ASSET_NEW: 'New asset',
|
||||
NODE_SIGNATURE_KIND_ASSET_WITHDRAWAL: 'Asset withdrawal',
|
||||
NODE_SIGNATURE_KIND_ASSET_UPDATE: ' Asset update',
|
||||
NODE_SIGNATURE_KIND_ERC20_MULTISIG_SIGNER_ADDED: 'Multisig signer added',
|
||||
NODE_SIGNATURE_KIND_ERC20_MULTISIG_SIGNER_REMOVED: 'Multisig signer removed',
|
||||
};
|
||||
|
||||
interface TxDetailsGenericProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is not yet a custom component for a transaction, just display
|
||||
* the basic details. This allows someone to view the decoded transaction.
|
||||
*/
|
||||
export const TxDetailsIssueSignatures = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsGenericProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const cmd: Command = txData.command;
|
||||
const k = cmd.kind ? kind[cmd.kind] : null;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8">
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{k ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Kind')}</TableCell>
|
||||
<TableCell>{k}</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{cmd.submitter ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('ETH key')}</TableCell>
|
||||
<TableCell>
|
||||
<EthExplorerLink
|
||||
id={cmd.submitter}
|
||||
type={EthExplorerLinkTypes.address}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{cmd.validatorNodeId ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Validator')}</TableCell>
|
||||
<TableCell>
|
||||
<NodeLink id={cmd.validatorNodeId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
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 { BlockLink, PartyLink } from '../../links';
|
||||
|
||||
type KeyRotate = components['schemas']['v1KeyRotateSubmission'];
|
||||
interface TxDetailsKeyRotateProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A node is changing Vega key
|
||||
*/
|
||||
export const TxDetailsKeyRotate = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsKeyRotateProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const k: KeyRotate = txData.command;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8">
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{k.targetBlock ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Target block')}</TableCell>
|
||||
<TableCell>
|
||||
<BlockLink height={k.targetBlock} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.currentPubKeyHash ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Old Address')}</TableCell>
|
||||
<TableCell>
|
||||
<PartyLink id={k.currentPubKeyHash} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.currentPubKeyHash ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('New Address')}</TableCell>
|
||||
<TableCell>
|
||||
<PartyLink id={k.currentPubKeyHash} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.newPubKeyIndex ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Key index')}</TableCell>
|
||||
<TableCell>
|
||||
<code>{k.newPubKeyIndex}</code>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,11 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import { MarketLink } from '../../links';
|
||||
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 type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
|
||||
export type LiquidityCancellation =
|
||||
components['schemas']['v1LiquidityProvisionCancellation'];
|
||||
|
||||
@@ -63,7 +63,7 @@ export const TxDataView = ({ txData, blockData }: TxDataViewProps) => {
|
||||
<Select
|
||||
placeholder="View as..."
|
||||
onChange={(v) => setShowTxData(v.target.value as ShowTxDataType)}
|
||||
value={'JSON'}
|
||||
value={showTxData}
|
||||
>
|
||||
<option value={'JSON'}>JSON</option>
|
||||
<option value={'base64'}>Base64</option>
|
||||
|
||||
@@ -10,6 +10,7 @@ const truthy = ['1', 'true'];
|
||||
export const ENV = {
|
||||
// Data sources
|
||||
// Environment
|
||||
env: windowOrDefault('NX_VEGA_ENV'),
|
||||
dsn: windowOrDefault('NX_EXPLORER_SENTRY_DSN'),
|
||||
dataSources: {
|
||||
blockExplorerUrl: windowOrDefault('NX_BLOCK_EXPLORER'),
|
||||
@@ -17,6 +18,7 @@ export const ENV = {
|
||||
tendermintWebsocketUrl: windowOrDefault('NX_TENDERMINT_WEBSOCKET_URL'),
|
||||
ethExplorerUrl: windowOrDefault('NX_ETHERSCAN_URL'),
|
||||
governanceUrl: windowOrDefault('NX_VEGA_GOVERNANCE_URL'),
|
||||
vegaRepoUrl: windowOrDefault('NX_VEGA_REPO_URL'),
|
||||
},
|
||||
flags: {
|
||||
assets: truthy.includes(windowOrDefault('NX_EXPLORER_ASSETS')),
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
BlockExplorerTransactions,
|
||||
} from '../routes/types/block-explorer-response';
|
||||
import { DATA_SOURCES } from '../config';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
|
||||
export interface TxsStateProps {
|
||||
txsData: BlockExplorerTransactionResult[];
|
||||
@@ -54,15 +55,15 @@ export const useTxsData = ({ limit, filters }: IUseTxsData) => {
|
||||
} = useFetch<BlockExplorerTransactions>(url, {}, false);
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.transactions?.length) {
|
||||
if (data && isNumber(data?.transactions?.length)) {
|
||||
setTxsState((prev) => ({
|
||||
txsData: [...prev.txsData, ...data.transactions],
|
||||
hasMoreTxs: true,
|
||||
hasMoreTxs: data.transactions.length > 0,
|
||||
lastCursor:
|
||||
data.transactions[data.transactions.length - 1].cursor || '',
|
||||
data.transactions[data.transactions.length - 1]?.cursor || '',
|
||||
}));
|
||||
}
|
||||
}, [setTxsState, data?.transactions]);
|
||||
}, [setTxsState, data]);
|
||||
|
||||
const loadTxs = useCallback(() => {
|
||||
return refetch({
|
||||
|
||||
@@ -10,7 +10,7 @@ export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec',
|
||||
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 }, 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> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, 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 }, 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> } } } } } } } | null> | null } | null };
|
||||
|
||||
export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{
|
||||
id: Types.Scalars['ID'];
|
||||
@@ -73,7 +73,7 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
||||
`;
|
||||
export const ExplorerOracleDataConnectionFragmentDoc = gql`
|
||||
fragment ExplorerOracleDataConnection on OracleSpec {
|
||||
dataConnection(pagination: {first: 1}) {
|
||||
dataConnection {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
@@ -113,13 +113,11 @@ export const ExplorerOracleSpecsDocument = gql`
|
||||
edges {
|
||||
node {
|
||||
...ExplorerOracleDataSource
|
||||
...ExplorerOracleDataConnection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${ExplorerOracleDataSourceFragmentDoc}
|
||||
${ExplorerOracleDataConnectionFragmentDoc}`;
|
||||
${ExplorerOracleDataSourceFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useExplorerOracleSpecsQuery__
|
||||
|
||||
@@ -22,7 +22,7 @@ export type SourceType =
|
||||
interface OracleDetailsProps {
|
||||
id: string;
|
||||
dataSource: ExplorerOracleDataSourceFragment;
|
||||
dataConnection: ExplorerOracleDataConnectionFragment;
|
||||
dataConnection?: ExplorerOracleDataConnectionFragment;
|
||||
// Defaults to false. Hides the count of 'broadcasts' this oracle has seen
|
||||
showBroadcasts?: boolean;
|
||||
}
|
||||
@@ -41,7 +41,8 @@ export const OracleDetails = ({
|
||||
showBroadcasts = false,
|
||||
}: OracleDetailsProps) => {
|
||||
const sourceType = dataSource.dataSourceSpec.spec.data.sourceType;
|
||||
const reportsCount: number = dataConnection.dataConnection.edges?.length || 0;
|
||||
const reportsCount: number =
|
||||
dataConnection?.dataConnection.edges?.length || 0;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -63,7 +64,9 @@ export const OracleDetails = ({
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
<OracleFilter data={dataSource} />
|
||||
{showBroadcasts ? <OracleData data={dataConnection} /> : null}
|
||||
{showBroadcasts && dataConnection ? (
|
||||
<OracleData data={dataConnection} />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ const Oracles = () => {
|
||||
<OracleDetails
|
||||
id={id}
|
||||
dataSource={o?.node}
|
||||
dataConnection={o?.node}
|
||||
showBroadcasts={false}
|
||||
/>
|
||||
<details>
|
||||
<summary className="pointer">JSON</summary>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
export const JumpToParty = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useDocumentTitle(['Parties']);
|
||||
useDocumentTitle(['Public keys']);
|
||||
|
||||
const handleSubmit = (e: React.SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -27,8 +27,8 @@ export const JumpToParty = () => {
|
||||
};
|
||||
return (
|
||||
<JumpTo
|
||||
label={t('Go to party')}
|
||||
placeholder={t('Party id')}
|
||||
label={t('Go to public key')}
|
||||
placeholder={t('Public key')}
|
||||
inputId="party-input"
|
||||
inputType="text"
|
||||
inputName="partyId"
|
||||
@@ -40,7 +40,7 @@ export const JumpToParty = () => {
|
||||
const Parties = () => {
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="parties-header">{t('Parties')}</RouteTitle>
|
||||
<RouteTitle data-testid="parties-header">{t('Public keys')}</RouteTitle>
|
||||
<JumpToParty />
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ type PartyIdErrorProps = {
|
||||
const PartyIdError = ({ id, error }: PartyIdErrorProps) => {
|
||||
const end = isValidPartyId(id)
|
||||
? t('No accounts or transactions found for: ')
|
||||
: 'Invalid party id: ';
|
||||
: 'Invalid public key: ';
|
||||
return (
|
||||
<section>
|
||||
<p>
|
||||
|
||||
@@ -16,7 +16,7 @@ import { PartyAccounts } from './components/party-accounts';
|
||||
const Party = () => {
|
||||
const { party } = useParams<{ party: string }>();
|
||||
|
||||
useDocumentTitle(['Parties', party || '-']);
|
||||
useDocumentTitle(['Public keys', party || '-']);
|
||||
const partyId = toNonHex(party ? party : '');
|
||||
const { isMobile } = useScreenDimensions();
|
||||
const visibleChars = useMemo(() => (isMobile ? 10 : 14), [isMobile]);
|
||||
@@ -44,7 +44,7 @@ const Party = () => {
|
||||
/>
|
||||
) : (
|
||||
<Panel>
|
||||
<p>No party found for key {party}</p>
|
||||
<p>No data found for public key {party}</p>
|
||||
</Panel>
|
||||
);
|
||||
|
||||
@@ -71,7 +71,7 @@ const Party = () => {
|
||||
className="font-alpha uppercase font-xl mb-4 text-zinc-800 dark:text-zinc-200"
|
||||
data-testid="parties-header"
|
||||
>
|
||||
{t('Party')}
|
||||
{t('Public key')}
|
||||
</h1>
|
||||
{partyRes.data ? (
|
||||
<>
|
||||
|
||||
@@ -38,7 +38,7 @@ const Tx = () => {
|
||||
to={`/${Routes.TX}`}
|
||||
>
|
||||
<Icon
|
||||
className="text-vega-light-300 dark:text-vega-light-300"
|
||||
className="text-vega-light-150 dark:text-vega-light-150"
|
||||
name={IconNames.CHEVRON_LEFT}
|
||||
/>
|
||||
All Transactions
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
File diff suppressed because it is too large
Load Diff
@@ -16,4 +16,16 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
CYPRESS_VEGA_URL=http://localhost:3028/query
|
||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
||||
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
|
||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
|
||||
CYPRESS_VEGA_ENV=CUSTOM
|
||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
||||
CYPRESS_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
CYPRESS_VEGA_URL=http://localhost:3028/query
|
||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
|
||||
@@ -2,56 +2,59 @@
|
||||
"changes": {
|
||||
"decimalPlaces": "5",
|
||||
"positionDecimalPlaces": "5",
|
||||
"lpPriceRange": "10",
|
||||
"instrument": {
|
||||
"name": "Oranges Daily",
|
||||
"code": "ORANGES.24h",
|
||||
"name": "Token test market",
|
||||
"code": "Token.24h",
|
||||
"future": {
|
||||
"settlementAsset": "8b52d4a3a4b0ffe733cddbc2b67be273816cfeb6ca4c8b339bac03ffba08e4e4",
|
||||
"quoteName": "tEuro",
|
||||
"settlementDataDecimals": 5,
|
||||
"settlementAsset": "fBTC",
|
||||
"quoteName": "fBTC",
|
||||
"dataSourceSpecForSettlementData": {
|
||||
"signers": [
|
||||
{
|
||||
"signer": {
|
||||
"__typename": "ETHAddress",
|
||||
"address": "0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "prices.BTC.value",
|
||||
"type": "TYPE_INTEGER"
|
||||
},
|
||||
"conditions": [
|
||||
"external": {
|
||||
"oracle": {
|
||||
"signers": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN",
|
||||
"value": "0"
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "prices.ETH.value",
|
||||
"type": "TYPE_INTEGER",
|
||||
"numberDecimalPlaces": "0"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"dataSourceSpecForTradingTermination": {
|
||||
"signers": [
|
||||
{
|
||||
"signer": {
|
||||
"__typename": "ETHAddress",
|
||||
"address": "0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC"
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "vegaprotocol.builtin.timestamp",
|
||||
"type": "TYPE_TIMESTAMP"
|
||||
"name": "trading.terminated.ETH5",
|
||||
"type": "TYPE_BOOLEAN"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN_OR_EQUAL",
|
||||
"value": "1648684800000000000"
|
||||
"operator": "OPERATOR_EQUALS",
|
||||
"value": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -59,7 +62,7 @@
|
||||
},
|
||||
"dataSourceSpecBinding": {
|
||||
"settlementPriceProperty": "prices.BTC.value",
|
||||
"tradingTerminationProperty": "vegaprotocol.builtin.timestamp"
|
||||
"tradingTerminationProperty": "trading.terminated.ETH5"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,83 +1,78 @@
|
||||
{
|
||||
"marketId": "315a8e48db0a292c92b617264728048c82c20efc922c75fd292fc54e5c727c81",
|
||||
"changes": {
|
||||
"instrument": {
|
||||
"code": "ORANGES.24h",
|
||||
"future": {
|
||||
"quoteName": "tEuro",
|
||||
"settlementDataDecimals": 5,
|
||||
"dataSourceSpecForSettlementData": {
|
||||
"signers": [
|
||||
{
|
||||
"signer": {
|
||||
"__typename": "ETHAddress",
|
||||
"address": "0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC"
|
||||
"instrument": {
|
||||
"code": "TEST.24h",
|
||||
"future": {
|
||||
"quoteName": "fUSDC",
|
||||
"settlementDataDecimals": 5,
|
||||
"dataSourceSpecForSettlementData": {
|
||||
"signers": [
|
||||
{
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "prices.ETH.value",
|
||||
"type": "TYPE_INTEGER"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dataSourceSpecForTradingTermination": {
|
||||
"signers": [
|
||||
{
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "prices.BTC.value",
|
||||
"type": "TYPE_INTEGER"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dataSourceSpecForTradingTermination": {
|
||||
"signers": [
|
||||
{
|
||||
"signer": {
|
||||
"__typename": "ETHAddress",
|
||||
"address": "0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC"
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "trading.terminated.ETH5",
|
||||
"type": "TYPE_BOOLEAN"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN_OR_EQUAL",
|
||||
"value": "1648684800000000000"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "vegaprotocol.builtin.timestamp",
|
||||
"type": "TYPE_TIMESTAMP"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN_OR_EQUAL",
|
||||
"value": "1648684800000000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dataSourceSpecBinding": {
|
||||
"settlementPriceProperty": "prices.BTC.value",
|
||||
"tradingTerminationProperty": "vegaprotocol.builtin.timestamp"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dataSourceSpecBinding": {
|
||||
"settlementPriceProperty": "prices.ETH.value",
|
||||
"tradingTerminationProperty": "trading.terminated.ETH5"
|
||||
}
|
||||
},
|
||||
"metadata": ["sector:energy", "sector:food", "source:docs.vega.xyz"],
|
||||
"priceMonitoringParameters": {
|
||||
"triggers": [
|
||||
{
|
||||
"horizon": "43200",
|
||||
"probability": "0.9999999",
|
||||
"auctionExtension": "600"
|
||||
}
|
||||
]
|
||||
},
|
||||
"logNormal": {
|
||||
"tau": 0.0001140771161,
|
||||
"riskAversionParameter": 0.001,
|
||||
"params": {
|
||||
"mu": 0,
|
||||
"r": 0.016,
|
||||
"sigma": 0.3
|
||||
}
|
||||
},
|
||||
"metadata": ["sector:energy", "sector:food", "source:docs.vega.xyz"],
|
||||
"priceMonitoringParameters": {
|
||||
"triggers": [
|
||||
{
|
||||
"horizon": "43200",
|
||||
"probability": "0.9999999",
|
||||
"auctionExtension": "600"
|
||||
}
|
||||
]
|
||||
},
|
||||
"logNormal": {
|
||||
"tau": 0.0001140771161,
|
||||
"riskAversionParameter": 0.001,
|
||||
"params": {
|
||||
"mu": 0,
|
||||
"r": 0.016,
|
||||
"sigma": 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
123
|
||||
0123245
|
||||
|
||||
@@ -11,6 +11,7 @@ const newProposalSubmitButton = '[data-testid="proposal-submit"]';
|
||||
const dialogCloseButton = '[data-testid="dialog-close"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
const closedProposals = '[data-testid="closed-proposals"]';
|
||||
const proposalVoteProgressForPercentage =
|
||||
'[data-testid="vote-progress-indicator-percentage-for"]';
|
||||
const proposalVoteProgressAgainstPercentage =
|
||||
@@ -22,7 +23,9 @@ const proposalVoteProgressAgainstTokens =
|
||||
const changeVoteButton = '[data-testid="change-vote-button"]';
|
||||
const proposalDetailsTitle = '[data-testid="proposal-title"]';
|
||||
const proposalDetailsDescription = '[data-testid="proposal-description"]';
|
||||
const proposalStatus = '[data-testid="proposal-status"]';
|
||||
const rawProposalData = '[data-testid="proposal-data"]';
|
||||
const votesTable = '[data-testid="votes-table"]';
|
||||
const minVoteButton = '[data-testid="min-vote"]';
|
||||
const maxVoteButton = '[data-testid="max-vote"]';
|
||||
const voteButtons = '[data-testid="vote-buttons"]';
|
||||
@@ -37,6 +40,10 @@ const txTimeout = Cypress.env('txTimeout');
|
||||
const epochTimeout = Cypress.env('epochTimeout');
|
||||
const proposalTimeout = { timeout: 14000 };
|
||||
|
||||
const minCloseDays = 2;
|
||||
const maxCloseDays = 3;
|
||||
const requiredParticipation = 0.001;
|
||||
|
||||
const governanceProposalType = {
|
||||
NETWORK_PARAMETER: 'Network parameter',
|
||||
NEW_MARKET: 'New market',
|
||||
@@ -65,21 +72,6 @@ context(
|
||||
network_parameters['governance.proposal.freeform.requiredMajority'] *
|
||||
100
|
||||
).as('requiredMajority');
|
||||
cy.wrap(
|
||||
network_parameters[
|
||||
'governance.proposal.freeform.requiredParticipation'
|
||||
] * 100
|
||||
).as('requiredParticipation');
|
||||
cy.wrap(
|
||||
network_parameters['governance.proposal.freeform.minClose'].split(
|
||||
'h'
|
||||
)[0] / 24
|
||||
).as('minCloseDays');
|
||||
cy.wrap(
|
||||
network_parameters['governance.proposal.freeform.maxClose'].split(
|
||||
'h'
|
||||
)[0] / 24
|
||||
).as('maxCloseDays');
|
||||
cy.wrap(
|
||||
network_parameters['governance.proposal.freeform.minClose'].split(
|
||||
'h'
|
||||
@@ -108,21 +100,6 @@ context(
|
||||
0.00001,
|
||||
'Asserting that value is at least 0.00001 for network parameter minVoterBalance'
|
||||
);
|
||||
assert.isAtLeast(
|
||||
parseFloat(this.requiredParticipation),
|
||||
0.00001,
|
||||
'Asserting that value is at least 0.00001 for network parameter requiredParticipation'
|
||||
);
|
||||
assert.isAtLeast(
|
||||
parseInt(this.minCloseDays),
|
||||
1,
|
||||
'Asserting that value is at least 1 for network parameter minCloseDays'
|
||||
);
|
||||
assert.isAtLeast(
|
||||
parseInt(this.maxCloseDays),
|
||||
parseInt(this.minCloseDays + 1),
|
||||
'Asserting that network parameter maxCloseDays is at least 1 day higher than minCloseDays'
|
||||
);
|
||||
// workaround for first eth tx hanging
|
||||
associateTokenStartOfTests();
|
||||
}
|
||||
@@ -182,10 +159,10 @@ context(
|
||||
this.minProposerBalance
|
||||
);
|
||||
let proposalDays = [
|
||||
this.minCloseDays + 1,
|
||||
this.maxCloseDays,
|
||||
this.minCloseDays + 3,
|
||||
this.minCloseDays + 2,
|
||||
minCloseDays + 1,
|
||||
maxCloseDays,
|
||||
minCloseDays + 3,
|
||||
minCloseDays + 2,
|
||||
];
|
||||
for (var index = 0; index < proposalDays.length; index++) {
|
||||
cy.go_to_make_new_proposal(governanceProposalType.RAW);
|
||||
@@ -227,6 +204,7 @@ context(
|
||||
cy.navigate_to('validators');
|
||||
cy.click_on_validator_from_list(0);
|
||||
cy.staking_validator_page_add_stake('2');
|
||||
cy.close_staking_dialog();
|
||||
|
||||
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
|
||||
|
||||
@@ -297,10 +275,12 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
// Skipping test due to bug: #1320
|
||||
it.skip('Newly created freeform proposals list - shows proposal participation - both met and not', function () {
|
||||
createFreeformProposal(this.minProposerBalance);
|
||||
cy.get_submitted_proposal_from_proposal_list()
|
||||
// 3001-VOTE-071
|
||||
it('Newly created freeform proposals list - shows proposal participation - both met and not', function () {
|
||||
const proposalTitle = generateProposalTitle();
|
||||
createFreeformProposal(this.minProposerBalance, proposalTitle);
|
||||
|
||||
cy.get_submitted_proposal_from_proposal_list(proposalTitle)
|
||||
.as('submittedProposal')
|
||||
.within(() => {
|
||||
// 3001-VOTE-039
|
||||
@@ -312,7 +292,7 @@ context(
|
||||
.invoke('text')
|
||||
.then((totalSupply) => {
|
||||
let tokensRequiredToAchieveResult = parseFloat(
|
||||
(totalSupply.replace(/,/g, '') * this.requiredParticipation) / 100
|
||||
(totalSupply.replace(/,/g, '') * requiredParticipation) / 100
|
||||
).toFixed(2);
|
||||
cy.ensure_specified_unstaked_tokens_are_associated(
|
||||
tokensRequiredToAchieveResult
|
||||
@@ -321,16 +301,17 @@ context(
|
||||
cy.get('@submittedProposal').within(() =>
|
||||
cy.get(viewProposalButton).click()
|
||||
);
|
||||
cy.get_proposal_information_from_table('Participation met')
|
||||
cy.get_proposal_information_from_table('Token participation met')
|
||||
.contains('👍')
|
||||
.should('be.visible');
|
||||
cy.navigate_to('proposals');
|
||||
cy.get('@submittedProposal').within(() =>
|
||||
cy.get(voteStatus).should('have.text', 'Participation met')
|
||||
cy.get(voteStatus).should('have.text', 'Set to pass')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// 3001-VOTE-055
|
||||
it('Newly created raw proposal details - shows proposal title and full description', function () {
|
||||
createRawProposal(this.minProposerBalance);
|
||||
cy.get('@rawProposal').then((rawProposal) => {
|
||||
@@ -357,6 +338,7 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
// 3001-VOTE-043
|
||||
it('Newly created freeform proposal details - shows proposed and closing dates', function () {
|
||||
const closingVoteHrs = '72';
|
||||
const proposalTitle = generateProposalTitle();
|
||||
@@ -399,6 +381,7 @@ context(
|
||||
it('Newly created proposal details - shows default status set to fail', function () {
|
||||
// 3001-VOTE-037
|
||||
// 3001-VOTE-040
|
||||
// 3001-VOTE-067
|
||||
createRawProposal(this.minProposerBalance);
|
||||
cy.get('@rawProposal').then((rawProposal) => {
|
||||
cy.get_submitted_proposal_from_proposal_list(
|
||||
@@ -413,15 +396,17 @@ context(
|
||||
.should('be.visible');
|
||||
// 3001-VOTE-062
|
||||
// 3001-VOTE-040
|
||||
// 3001-VOTE-070
|
||||
cy.get_proposal_information_from_table('Token majority met')
|
||||
.contains('👎')
|
||||
.should('be.visible');
|
||||
// 3001-VOTE-068
|
||||
cy.get_proposal_information_from_table('Token participation met')
|
||||
.contains('👎')
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
// 3001-VOTE-080 3001-VOTE-090
|
||||
// 3001-VOTE-080 3001-VOTE-090 3001-VOTE-069 3001-VOTE-072 3001-VOTE-073
|
||||
it('Newly created proposal details - ability to vote for and against proposal - with minimum required tokens associated', function () {
|
||||
createRawProposal(this.minProposerBalance);
|
||||
cy.get('@rawProposal').then((rawProposal) => {
|
||||
@@ -446,7 +431,7 @@ context(
|
||||
.contains(votedDate)
|
||||
.should('be.visible');
|
||||
});
|
||||
cy.get(proposalVoteProgressForPercentage)
|
||||
cy.get(proposalVoteProgressForPercentage) // 3001-VOTE-072
|
||||
.contains('100.00%')
|
||||
.and('be.visible');
|
||||
cy.get(proposalVoteProgressAgainstPercentage)
|
||||
@@ -466,10 +451,10 @@ context(
|
||||
.and('be.visible');
|
||||
// 3001-VOTE-061
|
||||
cy.get_proposal_information_from_table('Participation required')
|
||||
.contains(`${this.requiredParticipation}%`)
|
||||
.contains(`${requiredParticipation}%`)
|
||||
.should('be.visible');
|
||||
// 3001-VOTE-066
|
||||
cy.get_proposal_information_from_table('Majority Required')
|
||||
cy.get_proposal_information_from_table('Majority Required') // 3001-VOTE-073
|
||||
.contains(`${parseFloat(this.requiredMajority).toFixed(2)}%`)
|
||||
.should('be.visible');
|
||||
cy.get_proposal_information_from_table('Number of voting parties')
|
||||
@@ -512,7 +497,7 @@ context(
|
||||
.invoke('text')
|
||||
.then((totalSupply) => {
|
||||
let tokensRequiredToAchieveResult = parseFloat(
|
||||
(totalSupply.replace(/,/g, '') * this.requiredParticipation) / 100
|
||||
(totalSupply.replace(/,/g, '') * requiredParticipation) / 100
|
||||
).toFixed(2);
|
||||
cy.ensure_specified_unstaked_tokens_are_associated(
|
||||
tokensRequiredToAchieveResult
|
||||
@@ -536,6 +521,11 @@ context(
|
||||
cy.get(proposalVoteProgressAgainstTokens)
|
||||
.contains('0.00')
|
||||
.and('be.visible');
|
||||
cy.get_proposal_information_from_table(
|
||||
'Total tokens voted percentage'
|
||||
)
|
||||
.should('have.text', '0.00%')
|
||||
.and('be.visible');
|
||||
cy.get_proposal_information_from_table('Tokens for proposal')
|
||||
.should('have.text', tokensRequiredToAchieveResult)
|
||||
.and('be.visible');
|
||||
@@ -588,6 +578,7 @@ context(
|
||||
);
|
||||
});
|
||||
|
||||
// 3001-VOTE-006
|
||||
it('Creating a proposal - proposal rejected - able to access rejected proposals', function () {
|
||||
cy.ensure_specified_unstaked_tokens_are_associated(
|
||||
this.minProposerBalance
|
||||
@@ -649,6 +640,7 @@ context(
|
||||
cy.get(dialogCloseButton).click();
|
||||
});
|
||||
|
||||
// 3002-PROP-009
|
||||
it('Unable to create a proposal - when some but not enough tokens are associated', function () {
|
||||
cy.ensure_specified_unstaked_tokens_are_associated(
|
||||
this.minProposerBalance - 0.000001
|
||||
@@ -669,7 +661,7 @@ context(
|
||||
});
|
||||
|
||||
it('Unable to create a freeform proposal - when json parent section contains unexpected field', function () {
|
||||
// 3001-VOTE-038
|
||||
// 3001-VOTE-038 3002-PROP-013 3002-PROP-014
|
||||
cy.ensure_specified_unstaked_tokens_are_associated(
|
||||
this.minProposerBalance
|
||||
);
|
||||
@@ -695,6 +687,9 @@ context(
|
||||
'Invalid params: the transaction is malformed'
|
||||
);
|
||||
cy.get(dialogCloseButton).click();
|
||||
cy.get(rawProposalData)
|
||||
.invoke('val')
|
||||
.should('contain', "i shouldn't be here");
|
||||
});
|
||||
|
||||
it('Unable to create a freeform proposal - when json terms section contains unexpected field', function () {
|
||||
@@ -792,6 +787,107 @@ context(
|
||||
cy.contains('You voted: Against').should('be.visible');
|
||||
});
|
||||
|
||||
// 3001-VOTE-006
|
||||
it('Able to view enacted proposal', function () {
|
||||
cy.createMarket();
|
||||
cy.reload();
|
||||
cy.wait_for_spinner();
|
||||
cy.get(closedProposals).within(() => {
|
||||
cy.get(proposalDetailsTitle).should(
|
||||
'have.text',
|
||||
'Add Lorem Ipsum market'
|
||||
);
|
||||
cy.get(proposalStatus).should('have.text', 'Enacted ');
|
||||
cy.get(viewProposalButton).click();
|
||||
});
|
||||
cy.getByTestId('proposal-type').should('have.text', 'New market');
|
||||
cy.get_proposal_information_from_table('State')
|
||||
.contains('Enacted')
|
||||
.and('be.visible');
|
||||
cy.get(votesTable).within(() => {
|
||||
cy.contains('Vote passed.').should('be.visible');
|
||||
cy.contains('Voting has ended.').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
// 3001-VOTE-047
|
||||
it('Able to enact freeform proposal', function () {
|
||||
const proposalTitle = 'Add New free form proposal with short enactment';
|
||||
cy.ensure_specified_unstaked_tokens_are_associated(
|
||||
this.minProposerBalance
|
||||
);
|
||||
cy.sendWalletTxFreeFormProposal();
|
||||
cy.navigate_to('proposals');
|
||||
cy.reload();
|
||||
cy.wait_for_spinner();
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
cy.get_proposal_information_from_table('State')
|
||||
.contains('Open')
|
||||
.and('be.visible');
|
||||
cy.vote_for_proposal('for');
|
||||
cy.get_proposal_information_from_table('State')
|
||||
.contains('Enacted', epochTimeout)
|
||||
.and('be.visible');
|
||||
});
|
||||
|
||||
// 3001-VOTE-046 3001-VOTE-044 3001-VOTE-074 3001-VOTE-074
|
||||
it('Able to enact proposal by voting', function () {
|
||||
const proposalTitle = 'Add New proposal with short enactment';
|
||||
cy.ensure_specified_unstaked_tokens_are_associated(
|
||||
this.minProposerBalance
|
||||
);
|
||||
cy.sendWalletTxUpdateNetworkProposal();
|
||||
cy.navigate_to('proposals');
|
||||
cy.reload();
|
||||
cy.wait_for_spinner();
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
cy.get_proposal_information_from_table('State')
|
||||
.contains('Open')
|
||||
.and('be.visible');
|
||||
cy.vote_for_proposal('for');
|
||||
cy.get_proposal_information_from_table('State') // 3001-VOTE-047
|
||||
.contains('Passed', txTimeout)
|
||||
.and('be.visible');
|
||||
cy.get_proposal_information_from_table('State')
|
||||
.contains('Enacted', epochTimeout)
|
||||
.and('be.visible');
|
||||
cy.get(votesTable).within(() => {
|
||||
cy.contains('Vote passed.').should('be.visible');
|
||||
cy.contains('Voting has ended.').should('be.visible');
|
||||
});
|
||||
cy.get(proposalVoteProgressForPercentage)
|
||||
.contains('100.00%')
|
||||
.and('be.visible');
|
||||
});
|
||||
|
||||
// 3001-VOTE-048 3001-VOTE-049
|
||||
it('Able to fail proposal due to lack of participation', function () {
|
||||
const proposalTitle = 'Add New free form proposal with short enactment';
|
||||
cy.ensure_specified_unstaked_tokens_are_associated(
|
||||
this.minProposerBalance
|
||||
);
|
||||
cy.sendWalletTxFreeFormProposal();
|
||||
cy.navigate_to('proposals');
|
||||
cy.reload();
|
||||
cy.wait_for_spinner();
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
cy.get_proposal_information_from_table('State')
|
||||
.contains('Open')
|
||||
.and('be.visible');
|
||||
cy.get_proposal_information_from_table('State') // 3001-VOTE-047
|
||||
.contains('Declined', txTimeout)
|
||||
.and('be.visible');
|
||||
cy.get_proposal_information_from_table('Rejection reason')
|
||||
.contains('PROPOSAL_ERROR_PARTICIPATION_THRESHOLD_NOT_REACHED')
|
||||
.and('be.visible');
|
||||
});
|
||||
|
||||
function createRawProposal(proposerBalance) {
|
||||
if (proposerBalance)
|
||||
cy.ensure_specified_unstaked_tokens_are_associated(proposerBalance);
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
const proposalListItem = '[data-testid="proposals-list-item"]';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
const proposalType = '[data-testid="proposal-type"]';
|
||||
const proposalDetails = '[data-testid="proposal-details"]';
|
||||
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
|
||||
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
|
||||
const proposalValidationDeadline =
|
||||
@@ -209,18 +213,15 @@ context(
|
||||
);
|
||||
});
|
||||
|
||||
// skipped because no markets available to select in capsule
|
||||
it.skip('Able to submit update market proposal', function () {
|
||||
const marketId =
|
||||
'315a8e48db0a292c92b617264728048c82c20efc922c75fd292fc54e5c727c81';
|
||||
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_MARKET);
|
||||
cy.get(newProposalTitle).type('Test update asset proposal');
|
||||
cy.get(newProposalTitle).type('Test update market proposal');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.get(proposalMarketSelect).select(marketId);
|
||||
cy.get(proposalMarketSelect).select('Test market 1');
|
||||
cy.get('[data-testid="update-market-details"]').within(() => {
|
||||
cy.get('dd').eq(0).should('have.text', 'Oranges Daily');
|
||||
cy.get('dd').eq(1).should('have.text', 'ORANGES.24h');
|
||||
cy.get('dd').eq(2).should('have.text', marketId);
|
||||
cy.get('dd').eq(0).should('have.text', 'Test market 1');
|
||||
cy.get('dd').eq(1).should('have.text', 'TEST.24h');
|
||||
cy.get('dd').eq(2).should('not.be.empty');
|
||||
});
|
||||
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
|
||||
let newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
|
||||
@@ -281,12 +282,28 @@ context(
|
||||
});
|
||||
|
||||
it('Able to submit update asset proposal using min deadline', function () {
|
||||
const assetId =
|
||||
'ebcd94151ae1f0d39a4bde3b21a9c7ae81a80ea4352fb075a92e07608d9c953d';
|
||||
|
||||
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_ASSET);
|
||||
enterUpdateAssetProposalDetails();
|
||||
cy.get(minVoteDeadline).click();
|
||||
cy.get(minEnactDeadline).click();
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.wait_for_proposal_submitted();
|
||||
cy.navigate_to('proposals');
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.get(proposalType)
|
||||
.contains('Update asset')
|
||||
.parentsUntil(proposalListItem)
|
||||
.within(() => {
|
||||
cy.get(proposalDetails).should('contain.text', assetId); // 3001-VOTE-029
|
||||
cy.getByTestId('view-proposal-btn').click();
|
||||
});
|
||||
});
|
||||
cy.get_proposal_information_from_table('Proposed enactment') // 3001-VOTE-044
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('Able to submit update asset proposal using max deadline', function () {
|
||||
|
||||
@@ -866,6 +866,10 @@ context(
|
||||
|
||||
cy.get(stakeTokenSubmitButton).should('contain', 'Add 1 $VEGA tokens');
|
||||
});
|
||||
|
||||
after('teardown wallet', function () {
|
||||
cy.vega_wallet_teardown();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -37,8 +37,8 @@ context(
|
||||
function () {
|
||||
cy.reload();
|
||||
cy.wait_for_spinner();
|
||||
cy.connectVegaWallet();
|
||||
cy.ethereum_wallet_connect();
|
||||
cy.connectVegaWallet();
|
||||
cy.vega_wallet_teardown();
|
||||
cy.navigate_to('validators');
|
||||
}
|
||||
@@ -57,7 +57,16 @@ context(
|
||||
//0005-ETXN-006
|
||||
//0005-ETXN-003
|
||||
//0005-ETXN-005
|
||||
cy.staking_page_associate_tokens('2');
|
||||
cy.staking_page_associate_tokens('2', { skipConfirmation: true });
|
||||
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
3
|
||||
);
|
||||
cy.validate_wallet_currency('Associated', '0.00');
|
||||
cy.validate_wallet_currency('Pending association', '2.00');
|
||||
cy.validate_wallet_currency('Total associated after pending', '2.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
|
||||
// 0005-ETXN-002
|
||||
cy.get(ethWalletAssociatedBalances, txTimeout)
|
||||
@@ -98,6 +107,15 @@ context(
|
||||
|
||||
cy.staking_page_disassociate_tokens('2');
|
||||
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
3
|
||||
);
|
||||
cy.validate_wallet_currency('Associated', '2.00');
|
||||
cy.validate_wallet_currency('Pending association', '2.00');
|
||||
cy.validate_wallet_currency('Total associated after pending', '0.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
|
||||
cy.get(ethWalletAssociatedBalances, txTimeout).should('not.exist');
|
||||
|
||||
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
|
||||
@@ -194,7 +212,19 @@ context(
|
||||
// 1004-ASSO-024
|
||||
// 1004-ASSO-023
|
||||
|
||||
cy.staking_page_associate_tokens('2', { type: 'contract' });
|
||||
cy.staking_page_associate_tokens('2', {
|
||||
type: 'contract',
|
||||
skipConfirmation: true,
|
||||
});
|
||||
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
3
|
||||
);
|
||||
cy.validate_wallet_currency('Associated', '0.00');
|
||||
cy.validate_wallet_currency('Pending association', '2.00');
|
||||
cy.validate_wallet_currency('Total associated after pending', '2.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
|
||||
cy.get(ethWalletAssociatedBalances, txTimeout)
|
||||
.contains(vegaWalletPublicKeyShort)
|
||||
@@ -210,7 +240,19 @@ context(
|
||||
});
|
||||
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
cy.staking_page_disassociate_tokens('1', { type: 'contract' });
|
||||
cy.staking_page_disassociate_tokens('1', {
|
||||
type: 'contract',
|
||||
skipConfirmation: true,
|
||||
});
|
||||
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
3
|
||||
);
|
||||
cy.validate_wallet_currency('Associated', '2.00');
|
||||
cy.validate_wallet_currency('Pending association', '1.00');
|
||||
cy.validate_wallet_currency('Total associated after pending', '1.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
|
||||
cy.get(ethWalletAssociatedBalances, txTimeout)
|
||||
.contains(vegaWalletPublicKeyShort)
|
||||
@@ -286,6 +328,35 @@ context(
|
||||
});
|
||||
|
||||
// 1004-ASSO-004
|
||||
|
||||
it('Pending association outside of app is shown', function () {
|
||||
cy.vega_wallet_associate('2');
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
3
|
||||
);
|
||||
cy.validate_wallet_currency('Associated', '0.00');
|
||||
cy.validate_wallet_currency('Pending association', '2.00');
|
||||
cy.validate_wallet_currency('Total associated after pending', '2.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
cy.validate_wallet_currency('Associated', '2.00');
|
||||
});
|
||||
|
||||
it('Disassociation outside of app is shown', function () {
|
||||
cy.staking_page_associate_tokens('2');
|
||||
cy.validate_wallet_currency('Associated', '2.00');
|
||||
cy.vega_wallet_disassociate('2');
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
3
|
||||
);
|
||||
cy.validate_wallet_currency('Associated', '2.00');
|
||||
cy.validate_wallet_currency('Pending association', '2.00');
|
||||
cy.validate_wallet_currency('Total associated after pending', '0.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
cy.validate_wallet_currency('Associated', '0.00');
|
||||
});
|
||||
|
||||
it('Able to associate tokens to different public key of connected vega wallet', function () {
|
||||
cy.get(ethWalletAssociateButton).first().click();
|
||||
cy.get(associateWalletRadioButton).click();
|
||||
@@ -310,6 +381,7 @@ context(
|
||||
'vegaWalletPublicKey2Short'
|
||||
)} can now participate in governance and nominate a validator with your associated $VEGA.`
|
||||
);
|
||||
cy.staking_page_disassociate_all_tokens();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,7 +20,10 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
|
||||
it('Able to connect public key via wallet', function () {
|
||||
verifyConnectedToPubKey();
|
||||
cy.getByTestId('currency-title').should('contain.text', 'USDC (fake)');
|
||||
cy.getByTestId('currency-title', Cypress.env('epochTimeout')).should(
|
||||
'contain.text',
|
||||
'USDC (fake)'
|
||||
);
|
||||
});
|
||||
|
||||
it('Able to connect public key using url', function () {
|
||||
|
||||
@@ -10,6 +10,16 @@ const pendingStake = '[data-testid="pending-stake"]';
|
||||
const stakedByOperator = '[data-testid="staked-by-operator"]';
|
||||
const stakedByDelegates = '[data-testid="staked-by-delegates"]';
|
||||
const stakeShare = '[data-testid="stake-percentage"]';
|
||||
const stakedByOperatorToolTip = "[data-testid='staked-operator-tooltip']";
|
||||
const stakedByDelegatesToolTip = "[data-testid='staked-delegates-tooltip']";
|
||||
const totalStakedToolTip = "[data-testid='total-staked-tooltip']";
|
||||
const unnormalisedVotingPowerToolTip =
|
||||
"[data-testid='unnormalised-voting-power-tooltip']";
|
||||
const normalisedVotingPowerToolTip =
|
||||
"[data-testid='normalised-voting-power-tooltip']";
|
||||
const performancePenaltyToolTip = "[data-testid='performance-penalty-tooltip']";
|
||||
const overstakedPenaltyToolTip = "[data-testid='overstaked-penalty-tooltip']";
|
||||
const totalPenaltyToolTip = "[data-testid='total-penalty-tooltip']";
|
||||
const epochCountDown = '[data-testid="epoch-countdown"]';
|
||||
const stakeNumberRegex = /^\d*\.?\d*$/;
|
||||
|
||||
@@ -56,37 +66,70 @@ context('Staking Page - verify elements on page', function () {
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake', function () {
|
||||
cy.get('[col-id="stake"] > div > span')
|
||||
cy.get('[col-id="stake"] > div > span > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($stake) => {
|
||||
cy.wrap($stake).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake tooltip', function () {
|
||||
cy.get('[col-id="stake"] > div > span > span').first().realHover();
|
||||
|
||||
cy.get(stakedByOperatorToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by operator: 0.00');
|
||||
cy.get(stakedByDelegatesToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by delegates: 0.00');
|
||||
cy.get(totalStakedToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total stake: 0.00');
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power', function () {
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span')
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($vPower) => {
|
||||
cy.wrap($vPower).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power', function () {
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($vPower) => {
|
||||
cy.wrap($vPower).should('not.be.empty');
|
||||
});
|
||||
it('Should be able to see validator voting power tooltip', function () {
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
|
||||
.first()
|
||||
.realHover();
|
||||
|
||||
cy.get(unnormalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Unnormalised voting power: 0.00%');
|
||||
cy.get(normalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Normalised voting power: 0.10%');
|
||||
});
|
||||
|
||||
it('Should be able to see validator total penalties', function () {
|
||||
cy.get('[col-id="totalPenalties"] > div > span')
|
||||
cy.get('[col-id="totalPenalties"] > div > span > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($penalties) => {
|
||||
cy.wrap($penalties).should('contain.text', '0%');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator penalties tooltip', function () {
|
||||
cy.get('[col-id="totalPenalties"] > div > span > span').realHover();
|
||||
|
||||
cy.get(performancePenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Performance penalty: 100.00%');
|
||||
cy.get(overstakedPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
|
||||
cy.get(totalPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total penalties: 0.00%');
|
||||
});
|
||||
|
||||
it('Should be able to see validator pending stake', function () {
|
||||
cy.get('[col-id="pendingStake"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
|
||||
@@ -321,6 +321,8 @@ context(
|
||||
'200000',
|
||||
vegaWalletPublicKey
|
||||
);
|
||||
cy.reload();
|
||||
cy.wait_for_spinner();
|
||||
cy.connectVegaWallet();
|
||||
cy.ethereum_wallet_connect();
|
||||
});
|
||||
|
||||
@@ -46,6 +46,6 @@ Cypress.Commands.add('verify_page_header', (text) => {
|
||||
});
|
||||
|
||||
Cypress.Commands.add('wait_for_spinner', () => {
|
||||
cy.get(navigation.pageSpinner).should('exist');
|
||||
cy.get(navigation.pageSpinner, Cypress.env('epochTimeout')).should('exist');
|
||||
cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist');
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import '@vegaprotocol/cypress';
|
||||
import 'cypress-real-events/support';
|
||||
|
||||
import './common.functions.js';
|
||||
import './staking.functions.js';
|
||||
@@ -6,6 +7,7 @@ import './governance.functions.js';
|
||||
import './wallet-eth.functions.js';
|
||||
import './wallet-teardown.functions.js';
|
||||
import './wallet-vega.functions.js';
|
||||
import './proposal.functions.js';
|
||||
import registerCypressGrep from '@cypress/grep';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { addSeconds, millisecondsToSeconds } from 'date-fns';
|
||||
|
||||
const walletName = Cypress.env('vegaWalletName');
|
||||
const walletPubKey = Cypress.env('vegaWalletPublicKey');
|
||||
const walletLocation = Cypress.env('vegaWalletLocation');
|
||||
const walletPassphraseFile = './src/fixtures/wallet/passphrase';
|
||||
|
||||
Cypress.Commands.add('sendWalletTxUpdateNetworkProposal', () => {
|
||||
const MIN_CLOSE_SEC = 8;
|
||||
const MIN_ENACT_SEC = 5;
|
||||
|
||||
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
|
||||
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
|
||||
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
|
||||
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
|
||||
|
||||
cy.exec(
|
||||
`vegawallet transaction send --wallet ${walletName} --pubkey ${walletPubKey} -p ${walletPassphraseFile} --network DV '{
|
||||
"proposalSubmission": {
|
||||
"rationale": {
|
||||
"title": "Add New proposal with short enactment",
|
||||
"description": "E2E enactment test"
|
||||
},
|
||||
"terms": {
|
||||
"updateNetworkParameter": {
|
||||
"changes": {
|
||||
"key": "governance.proposal.updateNetParam.minProposerBalance",
|
||||
"value": "2"
|
||||
}
|
||||
},
|
||||
"closingTimestamp": ${closingTimestamp},
|
||||
"enactmentTimestamp": ${enactmentTimestamp}
|
||||
}
|
||||
}
|
||||
}' --home ${walletLocation}`,
|
||||
{ failOnNonZeroExit: false }
|
||||
)
|
||||
.its('stderr')
|
||||
.should('contain', '');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('sendWalletTxUpdateAssetProposal', () => {
|
||||
const MIN_CLOSE_SEC = 8;
|
||||
const MIN_ENACT_SEC = 5;
|
||||
|
||||
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
|
||||
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
|
||||
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
|
||||
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
|
||||
|
||||
cy.exec(
|
||||
`vegawallet transaction send --wallet ${walletName} --pubkey ${walletPubKey} -p ${walletPassphraseFile} --network DV '{
|
||||
"proposalSubmission": {
|
||||
"rationale": {
|
||||
"title": "Update Asset set to fail",
|
||||
"description": "E2E fail test"
|
||||
},
|
||||
"terms": {
|
||||
"updateAsset": {
|
||||
"assetId": "ebcd94151ae1f0d39a4bde3b21a9c7ae81a80ea4352fb075a92e07608d9c953d",
|
||||
"changes": {
|
||||
"quantum": "1",
|
||||
"erc20": {
|
||||
"withdrawThreshold": "10",
|
||||
"lifetimeLimit": "10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"closingTimestamp": ${closingTimestamp},
|
||||
"enactmentTimestamp": ${enactmentTimestamp}
|
||||
}
|
||||
}
|
||||
}' --home ${walletLocation}`,
|
||||
{ failOnNonZeroExit: false }
|
||||
)
|
||||
.its('stderr')
|
||||
.should('contain', '');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('sendWalletTxFreeFormProposal', () => {
|
||||
const MIN_CLOSE_SEC = 5;
|
||||
|
||||
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
|
||||
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
|
||||
|
||||
cy.exec(
|
||||
`vegawallet transaction send --wallet ${walletName} --pubkey ${walletPubKey} -p ${walletPassphraseFile} --network DV '{
|
||||
"proposalSubmission": {
|
||||
"rationale": {
|
||||
"title": "Add New free form proposal with short enactment",
|
||||
"description": "E2E enactment test"
|
||||
},
|
||||
"terms": {
|
||||
"newFreeform": {},
|
||||
"closingTimestamp": ${closingTimestamp}
|
||||
}
|
||||
}
|
||||
}' --home ${walletLocation}`,
|
||||
{ failOnNonZeroExit: false }
|
||||
)
|
||||
.its('stderr')
|
||||
.should('contain', '');
|
||||
});
|
||||
@@ -58,6 +58,8 @@ Cypress.Commands.add('staking_validator_page_remove_stake', (stake) => {
|
||||
Cypress.Commands.add('staking_page_associate_tokens', (amount, options) => {
|
||||
let approve = options && options.approve ? options.approve : false;
|
||||
let type = options && options.type ? options.type : 'wallet';
|
||||
let skipConfirmation =
|
||||
options && options.skipConfirmation ? options.skipConfirmation : false;
|
||||
|
||||
cy.highlight(`Associating ${amount} tokens from ${type}`);
|
||||
cy.get(ethWalletAssociateButton).first().click();
|
||||
@@ -79,16 +81,19 @@ Cypress.Commands.add('staking_page_associate_tokens', (amount, options) => {
|
||||
);
|
||||
}
|
||||
cy.get(tokenSubmitButton, txTimeout).should('be.enabled').click();
|
||||
cy.contains(
|
||||
`Associating with Vega key. Waiting for ${Cypress.env(
|
||||
'blockConfirmations'
|
||||
)} more confirmations..`,
|
||||
txTimeout
|
||||
).should('be.visible');
|
||||
cy.contains(
|
||||
'can now participate in governance and nominate a validator',
|
||||
txTimeout
|
||||
).should('be.visible');
|
||||
|
||||
if (!skipConfirmation) {
|
||||
cy.contains(
|
||||
`Associating with Vega key. Waiting for ${Cypress.env(
|
||||
'blockConfirmations'
|
||||
)} more confirmations..`,
|
||||
txTimeout
|
||||
).should('be.visible');
|
||||
cy.contains(
|
||||
'can now participate in governance and nominate a validator',
|
||||
txTimeout
|
||||
).should('be.visible');
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add('staking_page_disassociate_tokens', (amount, options) => {
|
||||
@@ -216,3 +221,19 @@ Cypress.Commands.add('close_staking_dialog', () => {
|
||||
cy.get('a').should('have.text', 'Back to Staking').click();
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
'validate_wallet_currency',
|
||||
(currencyTitle, expectedAmount) => {
|
||||
cy.get("[data-testid='currency-title']")
|
||||
.contains(currencyTitle)
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.getByTestId('currency-value', txTimeout).should(
|
||||
'have.text',
|
||||
expectedAmount
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
import { ethers, Wallet } from 'ethers';
|
||||
|
||||
const vegaWalletContainer = '[data-testid="vega-wallet"]';
|
||||
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
|
||||
const vegaWalletMnemonic = Cypress.env('vegaWalletMnemonic');
|
||||
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
|
||||
const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
|
||||
@@ -43,6 +42,11 @@ before('Vega wallet teardown prep', function () {
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
cy.wrap(this.stakingBridgeContract).as('stakingBridgeContract');
|
||||
cy.wrap(this.vestingContract).as('vestingContract');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('deposit_asset', function (assetEthAddress) {
|
||||
cy.get('@signer', { log: false }).then((signer) => {
|
||||
// Approve asset
|
||||
@@ -91,29 +95,25 @@ Cypress.Commands.add('faucet_asset', function (assetEthAddress) {
|
||||
});
|
||||
|
||||
Cypress.Commands.add('vega_wallet_teardown', function () {
|
||||
cy.get('[data-testid="associated-amount"]')
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.as('associatedAmount');
|
||||
cy.get('body').then(($body) => {
|
||||
if (
|
||||
$body.find('[data-testid="eth-wallet-associated-balances"]').length ||
|
||||
this.associatedAmount != '0.00'
|
||||
) {
|
||||
cy.vega_wallet_teardown_vesting(this.vestingContract);
|
||||
cy.vega_wallet_teardown_staking(this.stakingBridgeContract);
|
||||
}
|
||||
});
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance)
|
||||
.first()
|
||||
.invoke('text')
|
||||
.then((balance) => {
|
||||
if (balance != '0.00') {
|
||||
cy.vega_wallet_teardown_vesting(this.vestingContract);
|
||||
cy.vega_wallet_teardown_staking(this.stakingBridgeContract);
|
||||
}
|
||||
});
|
||||
cy.get('[data-testid="associated-amount"]', { timeout: 30000 }).should(
|
||||
'contain.text',
|
||||
'0.00'
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, { timeout: transactionTimeout }).should(
|
||||
'contain',
|
||||
'0.00',
|
||||
{ timeout: transactionTimeout }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
@@ -176,6 +176,22 @@ Cypress.Commands.add('vega_wallet_teardown_vesting', (vestingContract) => {
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('vega_wallet_associate', (amount) => {
|
||||
amount = amount + '0'.repeat(18);
|
||||
cy.highlight('Associating tokens');
|
||||
cy.get('@stakingBridgeContract').then((stakingBridgeContract) => {
|
||||
stakingBridgeContract.stake(amount, vegaWalletPubKey);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('vega_wallet_disassociate', (amount) => {
|
||||
amount = amount + '0'.repeat(18);
|
||||
cy.highlight('Disassociating tokens');
|
||||
cy.get('@stakingBridgeContract').then((stakingBridgeContract) => {
|
||||
stakingBridgeContract.remove_stake(amount, vegaWalletPubKey);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('wait_for_transaction', (tx) => {
|
||||
cy.wrap(tx.wait(1).catch(cy.log), { timeout: transactionTimeout });
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"allowJs": true,
|
||||
"types": ["cypress", "node", "@cypress/grep"]
|
||||
"types": ["cypress", "node", "cypress-real-events", "@cypress/grep"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
|
||||
NX_VEGA_URL=https://api.n10.testnet.vega.xyz/graphql
|
||||
NX_VEGA_URL=https://api.n08.testnet.vega.xyz/graphql
|
||||
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET3":"https://stagnet3.token.vega.xyz","TESTNET":"https://token.fairground.wtf","MAINNET":"https://token.vega.xyz"}'
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
|
||||
@@ -12,5 +12,10 @@ export default {
|
||||
moduleNameMapper: {
|
||||
'^d3-(.*)$': `d3-$1/dist/d3-$1`,
|
||||
},
|
||||
collectCoverageFrom: ['**/*.{ts,tsx}', '!**/node_modules/**'],
|
||||
collectCoverageFrom: [
|
||||
'**/*.{ts,tsx}',
|
||||
'!**/node_modules/**',
|
||||
'!**/__generated__/**',
|
||||
'!**/__generated___/**',
|
||||
],
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { AppLoader } from './app-loader';
|
||||
import { NetworkInfo } from '@vegaprotocol/network-info';
|
||||
import { BalanceManager } from './components/balance-manager';
|
||||
import { EthWallet } from './components/eth-wallet';
|
||||
import { AppLayout } from './components/page-templates/app-layout';
|
||||
import { TemplateSidebar } from './components/page-templates/template-sidebar';
|
||||
import { TransactionModal } from './components/transactions-modal';
|
||||
import { VegaWallet } from './components/vega-wallet';
|
||||
@@ -88,14 +89,14 @@ const Web3Container = ({
|
||||
<AppLoader>
|
||||
<BalanceManager>
|
||||
<>
|
||||
<div className="app w-full max-w-[1500px] mx-auto grid grid-rows-[min-content_min-content_1fr_min-content] min-h-full border-neutral-700 lg:border-l lg:border-r lg:text-body-large">
|
||||
<AppLayout>
|
||||
<TemplateSidebar sidebar={sideBar}>
|
||||
<AppRouter />
|
||||
</TemplateSidebar>
|
||||
<footer className="p-4 border-t border-neutral-700">
|
||||
<NetworkInfo />
|
||||
</footer>
|
||||
</div>
|
||||
</AppLayout>
|
||||
<VegaWalletDialogs />
|
||||
<TransactionModal />
|
||||
<WithdrawalDialog />
|
||||
|
||||
@@ -2,20 +2,24 @@ import * as Sentry from '@sentry/react';
|
||||
import { toBigNum } from '@vegaprotocol/react-helpers';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||
import { useContracts } from '../../contexts/contracts/contracts-context';
|
||||
import { useGetAssociationBreakdown } from '../../hooks/use-get-association-breakdown';
|
||||
import { useGetUserTrancheBalances } from '../../hooks/use-get-user-tranche-balances';
|
||||
import { useBalances } from '../../lib/balances/balances-store';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useListenForStakingEvents as useListenForAssociationEvents } from '../../hooks/use-listen-for-staking-events';
|
||||
|
||||
interface BalanceManagerProps {
|
||||
children: React.ReactElement;
|
||||
children: ReactElement;
|
||||
}
|
||||
|
||||
export const BalanceManager = ({ children }: BalanceManagerProps) => {
|
||||
const contracts = useContracts();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { account } = useWeb3React();
|
||||
const {
|
||||
appState: { decimals },
|
||||
@@ -23,6 +27,20 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
|
||||
const { updateBalances: updateStoreBalances } = useBalances();
|
||||
const { config } = useEthereumConfig();
|
||||
|
||||
const numberOfConfirmations = config?.confirmations || 0;
|
||||
|
||||
useListenForAssociationEvents(
|
||||
contracts?.staking.contract,
|
||||
pubKey,
|
||||
numberOfConfirmations
|
||||
);
|
||||
|
||||
useListenForAssociationEvents(
|
||||
contracts?.vesting.contract,
|
||||
pubKey,
|
||||
numberOfConfirmations
|
||||
);
|
||||
|
||||
const getUserTrancheBalances = useGetUserTrancheBalances(
|
||||
account || '',
|
||||
contracts?.vesting
|
||||
@@ -34,7 +52,7 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
|
||||
);
|
||||
|
||||
// update balances on connect to Ethereum
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const updateBalances = async () => {
|
||||
if (!account || !config) return;
|
||||
try {
|
||||
@@ -75,13 +93,13 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
|
||||
]);
|
||||
|
||||
// This use effect hook is very expensive and is kept separate to prevent expensive reloading of data.
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
getUserTrancheBalances();
|
||||
}
|
||||
}, [account, getUserTrancheBalances]);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
getAssociationBreakdown();
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ export const LockedProgress = ({
|
||||
unlocked,
|
||||
leftLabel,
|
||||
rightLabel,
|
||||
leftColor = Colors.vega.pink,
|
||||
rightColor = Colors.vega.green,
|
||||
leftColor = Colors.vega.pink.DEFAULT,
|
||||
rightColor = Colors.vega.green.DEFAULT,
|
||||
decimals = 2,
|
||||
}: LockedProgressProps) => {
|
||||
const lockedPercentage = React.useMemo(() => {
|
||||
|
||||
@@ -18,7 +18,7 @@ export const NavDropDown = ({ navbarTheme }: { navbarTheme: NavbarTheme }) => {
|
||||
<AppNavLink
|
||||
name={
|
||||
<NavDropdownMenuTrigger
|
||||
className="w-auto text-capMenu"
|
||||
className="w-auto flex items-center"
|
||||
data-testid="state-trigger"
|
||||
onClick={() => setOpen(!isOpen)}
|
||||
>
|
||||
@@ -38,6 +38,7 @@ export const NavDropDown = ({ navbarTheme }: { navbarTheme: NavbarTheme }) => {
|
||||
name={t(r.name)}
|
||||
path={r.path}
|
||||
navbarTheme={'inherit'}
|
||||
subNav={true}
|
||||
end={true}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
@@ -13,6 +13,7 @@ interface AppNavLinkProps {
|
||||
target?: HTMLAttributeAnchorTarget;
|
||||
end?: boolean;
|
||||
fullWidth?: boolean;
|
||||
subNav?: boolean;
|
||||
}
|
||||
|
||||
export const AppNavLink = ({
|
||||
@@ -23,28 +24,32 @@ export const AppNavLink = ({
|
||||
testId,
|
||||
end = false,
|
||||
fullWidth = false,
|
||||
subNav = false,
|
||||
}: AppNavLinkProps) => {
|
||||
const borderClasses = classNames('absolute h-1 w-full bottom-[-1px] left-0', {
|
||||
'bg-black dark:bg-vega-yellow': navbarTheme !== 'yellow',
|
||||
'bg-black': navbarTheme === 'yellow',
|
||||
});
|
||||
const borderClasses = classNames(
|
||||
'absolute h-0.5 w-full bottom-[-1px] left-0',
|
||||
{
|
||||
'bg-black dark:bg-vega-yellow': navbarTheme !== 'yellow',
|
||||
'bg-black': navbarTheme === 'yellow',
|
||||
}
|
||||
);
|
||||
return (
|
||||
<NavLink
|
||||
key={path}
|
||||
data-testid={testId}
|
||||
to={{ pathname: path }}
|
||||
className={getNavLinkClassNames(navbarTheme, fullWidth)}
|
||||
className={getNavLinkClassNames(navbarTheme, fullWidth, subNav)}
|
||||
target={target}
|
||||
end={end}
|
||||
>
|
||||
{({ isActive }) => {
|
||||
return (
|
||||
<>
|
||||
<div className={subNav ? 'inline-block relative pb-1' : undefined}>
|
||||
{name}
|
||||
{isActive && (
|
||||
<span data-testid="link-active" className={borderClasses} />
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</NavLink>
|
||||
|
||||
@@ -22,9 +22,8 @@ const renderComponent = (initialEntries?: string[]) => {
|
||||
};
|
||||
|
||||
describe('nav', () => {
|
||||
it('Renders title and logo with link to home', () => {
|
||||
it('Renders logo with link to home', () => {
|
||||
renderComponent();
|
||||
expect(screen.getByText('Governance')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('logo-link')).toHaveProperty(
|
||||
'href',
|
||||
'http://localhost/'
|
||||
|
||||
@@ -3,7 +3,8 @@ import { NetworkSwitcher } from '@vegaprotocol/environment';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { TOP_LEVEL_ROUTES } from '../../routes/routes';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import vegaWhite from '../../images/vega_white.png';
|
||||
import logoWhiteText from '../../images/logo-white-text.png';
|
||||
import logoBlackText from '../../images/logo-black-text.png';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { NavDrawer } from './nav-draw';
|
||||
import { Nav as ToolkitNav } from '@vegaprotocol/ui-toolkit';
|
||||
@@ -46,14 +47,19 @@ export const Nav = ({ navbarTheme = 'inherit' }: NavbarProps) => {
|
||||
navbarTheme={navbarTheme}
|
||||
icon={
|
||||
<Link to="/" data-testid="logo-link">
|
||||
<img alt="Vega" src={vegaWhite} height={30} width={30} />
|
||||
<img
|
||||
alt="Vega"
|
||||
src={navbarTheme === 'yellow' ? logoBlackText : logoWhiteText}
|
||||
height={30}
|
||||
width={250}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
title={t('Governance')}
|
||||
title={undefined}
|
||||
titleContent={<NetworkSwitcher />}
|
||||
>
|
||||
{isDesktop ? (
|
||||
<nav className="flex items-center flex-1 px-2">
|
||||
<nav className="flex items-center flex-1 px-4">
|
||||
{TOP_LEVEL_ROUTES.map((r) => (
|
||||
<AppNavLink
|
||||
key={r.path}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import classNames from 'classnames';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
interface AppLayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
export const AppLayout = ({ children }: AppLayoutProps) => {
|
||||
const { isReadOnly } = useVegaWallet();
|
||||
const AppLayoutClasses = classNames(
|
||||
'app w-full max-w-[1500px] mx-auto grid min-h-full',
|
||||
'border-neutral-700 lg:border-l lg:border-r',
|
||||
'lg:text-body-large',
|
||||
{
|
||||
'grid-rows-[repeat(2,min-content)_1fr_min-content]': !isReadOnly,
|
||||
'grid-rows-[repeat(3,min-content)_1fr_min-content]': isReadOnly,
|
||||
}
|
||||
);
|
||||
|
||||
return <div className={AppLayoutClasses}>{children}</div>;
|
||||
};
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
ViewingAsBanner,
|
||||
AnnouncementBanner,
|
||||
ExternalLink,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import React from 'react';
|
||||
|
||||
@@ -15,12 +19,18 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||
return (
|
||||
<>
|
||||
<AnnouncementBanner>
|
||||
<div className="font-alpha calt uppercase text-center text-lg text-white">
|
||||
<span className="pr-4">The Mainnet sims are live!</span>
|
||||
<ExternalLink href="https://fairground.wtf/">
|
||||
Come help stress test the network
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</AnnouncementBanner>
|
||||
<Nav navbarTheme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
||||
{isReadOnly ? (
|
||||
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
) : null}
|
||||
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]">
|
||||
<main className="col-start-1 p-4">{children}</main>
|
||||
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
|
||||
|
||||
@@ -25,6 +25,8 @@ import { usePollForDelegations } from './hooks';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { toBigNum } from '@vegaprotocol/react-helpers';
|
||||
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
|
||||
import { StakingEventType } from '../../hooks/use-get-association-breakdown';
|
||||
|
||||
export const VegaWallet = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -119,6 +121,10 @@ interface VegaWalletConnectedProps {
|
||||
}
|
||||
|
||||
const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
const pendingBalances = usePendingBalancesStore(
|
||||
(state) => state.pendingBalances
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
appDispatch,
|
||||
@@ -126,6 +132,27 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
} = useAppState();
|
||||
const { delegations, currentStakeAvailable, delegatedNodes, accounts } =
|
||||
usePollForDelegations();
|
||||
const amountRemoved = BigNumber.sum.apply(null, [
|
||||
new BigNumber(0),
|
||||
...pendingBalances
|
||||
.filter(({ event }) => event === StakingEventType.Stake_Removed)
|
||||
.map(({ args }) => toBigNum(args?.[1].toString(), decimals)),
|
||||
]);
|
||||
|
||||
const amountAdded = BigNumber.sum.apply(null, [
|
||||
new BigNumber(0),
|
||||
...pendingBalances
|
||||
.filter(({ event }) => event === StakingEventType.Stake_Deposited)
|
||||
.map(({ args }) => toBigNum(args?.[1].toString(), decimals)),
|
||||
]);
|
||||
const totalPending = React.useMemo(
|
||||
() => amountRemoved.plus(amountAdded),
|
||||
[amountAdded, amountRemoved]
|
||||
);
|
||||
const pendingStakeAmount = React.useMemo(
|
||||
() => currentStakeAvailable.plus(amountAdded).minus(amountRemoved),
|
||||
[amountAdded, amountRemoved, currentStakeAvailable]
|
||||
);
|
||||
|
||||
const unstaked = React.useMemo(() => {
|
||||
const totalDelegated = delegations.reduce<BigNumber>(
|
||||
@@ -161,6 +188,26 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
symbol="VEGA"
|
||||
balance={currentStakeAvailable}
|
||||
/>
|
||||
{totalPending.eq(0) ? null : (
|
||||
<>
|
||||
<WalletCardAsset
|
||||
image={vegaWhite}
|
||||
decimals={decimals}
|
||||
name="VEGA"
|
||||
subheading={t('Pending association')}
|
||||
symbol="VEGA"
|
||||
balance={totalPending}
|
||||
/>
|
||||
<WalletCardAsset
|
||||
image={vegaWhite}
|
||||
decimals={decimals}
|
||||
name="VEGA"
|
||||
subheading={t('Total associated after pending')}
|
||||
symbol="VEGA"
|
||||
balance={pendingStakeAmount}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<div data-testid="vega-wallet-balance-unstaked">
|
||||
<WalletCardRow label={t('unstaked')} value={unstaked} />
|
||||
</div>
|
||||
|
||||
@@ -30,9 +30,12 @@ export function useAnimateValue(
|
||||
) {
|
||||
elRef.current?.animate(
|
||||
[
|
||||
{ backgroundColor: customColors.vega.pink, color: colors.white },
|
||||
{
|
||||
backgroundColor: customColors.vega.pink,
|
||||
backgroundColor: customColors.vega.pink.DEFAULT,
|
||||
color: colors.white,
|
||||
},
|
||||
{
|
||||
backgroundColor: customColors.vega.pink.DEFAULT,
|
||||
color: colors.white,
|
||||
offset: 0.8,
|
||||
},
|
||||
@@ -53,11 +56,11 @@ export function useAnimateValue(
|
||||
elRef.current?.animate(
|
||||
[
|
||||
{
|
||||
backgroundColor: customColors.vega.green,
|
||||
backgroundColor: customColors.vega.green.DEFAULT,
|
||||
color: colors.white,
|
||||
},
|
||||
{
|
||||
backgroundColor: customColors.vega.green,
|
||||
backgroundColor: customColors.vega.green.DEFAULT,
|
||||
color: colors.white,
|
||||
offset: 0.8,
|
||||
},
|
||||
|
||||
@@ -11,6 +11,11 @@ import { useAppState } from '../contexts/app-state/app-state-context';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useBalances } from '../lib/balances/balances-store';
|
||||
|
||||
export enum StakingEventType {
|
||||
Stake_Removed = 'Stake_Removed',
|
||||
Stake_Deposited = 'Stake_Deposited',
|
||||
}
|
||||
|
||||
export function useGetAssociationBreakdown(
|
||||
ethAddress: string,
|
||||
staking: StakingBridge,
|
||||
@@ -63,8 +68,8 @@ function combineStakeEventsByVegaKey(
|
||||
const res = events.reduce((obj, e) => {
|
||||
const vegaKey = e.args?.vega_public_key;
|
||||
const amount = parseEventAmount(e, decimals);
|
||||
const isDeposit = e.event === 'Stake_Deposited';
|
||||
const isRemove = e.event === 'Stake_Removed';
|
||||
const isDeposit = e.event === StakingEventType.Stake_Deposited;
|
||||
const isRemove = e.event === StakingEventType.Stake_Removed;
|
||||
|
||||
if (!isDeposit && !isRemove) return obj;
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { useListenForPendingEthEvents } from './use-listen-for-pending-eth-events';
|
||||
import { renderHook, cleanup, waitFor } from '@testing-library/react';
|
||||
import type { Contract, EventFilter, Event } from 'ethers';
|
||||
|
||||
let contract: Contract;
|
||||
let filter: EventFilter;
|
||||
let addPendingTxs: (event: Event[]) => void;
|
||||
let removePendingTx: (event: Event) => void;
|
||||
let resetPendingTxs: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
contract = {
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
queryFilter: jest.fn().mockResolvedValue([]),
|
||||
} as unknown as Contract;
|
||||
filter = {} as EventFilter;
|
||||
addPendingTxs = jest.fn();
|
||||
removePendingTx = jest.fn();
|
||||
resetPendingTxs = jest.fn();
|
||||
});
|
||||
|
||||
jest.mock('@web3-react/core', () => ({
|
||||
useWeb3React: () => ({
|
||||
provider: {
|
||||
getBlockNumber: jest.fn().mockResolvedValue(1),
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useListenForPendingEthEvents', () => {
|
||||
it('listens for events on contract', async () => {
|
||||
renderHook(() =>
|
||||
useListenForPendingEthEvents(
|
||||
1,
|
||||
contract,
|
||||
filter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
)
|
||||
);
|
||||
|
||||
expect(contract.on).toHaveBeenCalledWith(filter, expect.any(Function));
|
||||
cleanup();
|
||||
expect(contract.off).toHaveBeenCalledWith(filter, expect.any(Function));
|
||||
});
|
||||
|
||||
it('waits for correct number of confirmations before removing tx from pending txs', async () => {
|
||||
renderHook(() => {
|
||||
useListenForPendingEthEvents(
|
||||
2,
|
||||
contract,
|
||||
filter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
);
|
||||
});
|
||||
|
||||
const listener = (contract.on as jest.Mock).mock.calls[0][1];
|
||||
const event = {
|
||||
getTransaction: jest.fn().mockResolvedValue({
|
||||
wait: jest.fn().mockResolvedValue({}),
|
||||
}),
|
||||
} as unknown as Event;
|
||||
listener(null, null, null, event);
|
||||
|
||||
expect(addPendingTxs).toHaveBeenCalledWith([event]);
|
||||
expect(removePendingTx).not.toHaveBeenCalled();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(removePendingTx).toHaveBeenCalledWith(event);
|
||||
});
|
||||
});
|
||||
|
||||
it('gets existing transactions', async () => {
|
||||
const event = {
|
||||
getTransaction: jest.fn().mockResolvedValue({
|
||||
wait: jest.fn().mockResolvedValue(null),
|
||||
}),
|
||||
} as unknown as Event;
|
||||
|
||||
contract.queryFilter = jest.fn().mockResolvedValue([event]);
|
||||
|
||||
renderHook(() => {
|
||||
useListenForPendingEthEvents(
|
||||
2,
|
||||
contract,
|
||||
filter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(contract.queryFilter).toHaveBeenCalledWith(filter, -1);
|
||||
expect(addPendingTxs).toHaveBeenCalledWith([event]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import type { Contract, Event, EventFilter } from 'ethers';
|
||||
|
||||
export const useListenForPendingEthEvents = (
|
||||
numberOfConfirmations: number,
|
||||
contract: Contract | undefined,
|
||||
filter: EventFilter | null,
|
||||
addPendingTxs: (event: Event[]) => void,
|
||||
removePendingTx: (event: Event) => void,
|
||||
resetPendingTxs: () => void
|
||||
) => {
|
||||
const { provider } = useWeb3React();
|
||||
|
||||
/**
|
||||
* Add listener for the ethereum events on the contract passed in for the filter passed in.
|
||||
* Push the event into the store and wait for the correct number of confirmations before removing it.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!contract || !filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const listener = async (...args: any[]) => {
|
||||
try {
|
||||
const event = args[3] as Event;
|
||||
addPendingTxs([event]);
|
||||
const tx = await event.getTransaction();
|
||||
await tx.wait(numberOfConfirmations);
|
||||
removePendingTx(event);
|
||||
} catch (e) {
|
||||
Sentry.captureException(`Error listening for pending eth events ${e}`);
|
||||
}
|
||||
};
|
||||
|
||||
contract.on(filter, listener);
|
||||
|
||||
return () => {
|
||||
contract.off(filter, listener);
|
||||
};
|
||||
}, [addPendingTxs, contract, filter, numberOfConfirmations, removePendingTx]);
|
||||
|
||||
/**
|
||||
* Get all transactions that exist on the blockchain but have yet to reach the number of confirmations
|
||||
*/
|
||||
const getExistingTransactions = useCallback(async () => {
|
||||
const blockNumber = (await provider?.getBlockNumber()) || 0;
|
||||
if (!filter || !contract) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
return await contract.queryFilter(
|
||||
filter,
|
||||
blockNumber - numberOfConfirmations
|
||||
);
|
||||
} catch (e) {
|
||||
Sentry.captureException(`Error getting existing transactions ${e}`);
|
||||
return [];
|
||||
}
|
||||
}, [contract, filter, numberOfConfirmations, provider]);
|
||||
|
||||
const waitForExistingTransactions = useCallback(
|
||||
(events: Event[], numberOfConfirmations: number) => {
|
||||
events.map(async (event) => {
|
||||
try {
|
||||
const tx = await event.getTransaction();
|
||||
|
||||
await tx.wait(Math.max(numberOfConfirmations, 0));
|
||||
|
||||
removePendingTx(event);
|
||||
} catch (e) {
|
||||
Sentry.captureException(
|
||||
`Error waiting for existing transactions ${e}`
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
[removePendingTx]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
resetPendingTxs();
|
||||
getExistingTransactions().then((events) => {
|
||||
if (!cancelled) {
|
||||
addPendingTxs([...events]);
|
||||
waitForExistingTransactions([...events], numberOfConfirmations);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [
|
||||
addPendingTxs,
|
||||
getExistingTransactions,
|
||||
numberOfConfirmations,
|
||||
resetPendingTxs,
|
||||
waitForExistingTransactions,
|
||||
]);
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useMemo } from 'react';
|
||||
import { usePendingBalancesStore } from './use-pending-balances-manager';
|
||||
import type { Contract } from 'ethers';
|
||||
import { useListenForPendingEthEvents } from './use-listen-for-pending-eth-events';
|
||||
import { prepend0x } from '@vegaprotocol/smart-contracts';
|
||||
|
||||
export const useListenForStakingEvents = (
|
||||
contract: Contract | undefined,
|
||||
vegaPublicKey: string | null,
|
||||
numberOfConfirmations: number
|
||||
) => {
|
||||
const { addPendingTxs, removePendingTx, resetPendingTxs } =
|
||||
usePendingBalancesStore((state) => ({
|
||||
addPendingTxs: state.addPendingTxs,
|
||||
removePendingTx: state.removePendingTx,
|
||||
resetPendingTxs: state.resetPendingTxs,
|
||||
}));
|
||||
const addFilter = useMemo(
|
||||
() =>
|
||||
vegaPublicKey && contract
|
||||
? contract.filters.Stake_Deposited(null, null, prepend0x(vegaPublicKey))
|
||||
: null,
|
||||
[contract, vegaPublicKey]
|
||||
);
|
||||
const removeFilter = useMemo(
|
||||
() =>
|
||||
vegaPublicKey && contract
|
||||
? contract.filters.Stake_Removed(null, null, prepend0x(vegaPublicKey))
|
||||
: null,
|
||||
[contract, vegaPublicKey]
|
||||
);
|
||||
|
||||
/**
|
||||
* Listen for all add stake events
|
||||
*/
|
||||
useListenForPendingEthEvents(
|
||||
numberOfConfirmations,
|
||||
contract,
|
||||
addFilter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
);
|
||||
|
||||
/**
|
||||
* Listen for all remove stake events
|
||||
*/
|
||||
useListenForPendingEthEvents(
|
||||
numberOfConfirmations,
|
||||
contract,
|
||||
removeFilter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
import { act } from '@testing-library/react-hooks';
|
||||
import { usePendingBalancesStore } from './use-pending-balances-manager';
|
||||
import type { Event } from 'ethers';
|
||||
|
||||
afterEach(() => {
|
||||
usePendingBalancesStore.setState((state) => ({
|
||||
...state,
|
||||
pendingBalances: [],
|
||||
}));
|
||||
});
|
||||
|
||||
const event1 = { transactionHash: 'tx1' } as Event;
|
||||
const event2 = { transactionHash: 'tx2' } as Event;
|
||||
|
||||
describe('usePendingBalancesStore', () => {
|
||||
it('should add new events to the pendingBalances state and remove duplicates', () => {
|
||||
const { addPendingTxs } = usePendingBalancesStore.getState();
|
||||
const duplicateEvent = { transactionHash: 'tx1' } as Event;
|
||||
|
||||
act(() => {
|
||||
addPendingTxs([event1, duplicateEvent]);
|
||||
});
|
||||
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
event1,
|
||||
]);
|
||||
|
||||
act(() => {
|
||||
addPendingTxs([event2]);
|
||||
});
|
||||
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
event1,
|
||||
event2,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove a specific event from the pendingBalances state', () => {
|
||||
const { addPendingTxs, removePendingTx } =
|
||||
usePendingBalancesStore.getState();
|
||||
const eventToRemove = { transactionHash: 'tx1' } as Event;
|
||||
|
||||
act(() => {
|
||||
addPendingTxs([eventToRemove, event2]);
|
||||
});
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
eventToRemove,
|
||||
event2,
|
||||
]);
|
||||
|
||||
act(() => {
|
||||
removePendingTx(eventToRemove);
|
||||
});
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
event2,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should reset the pendingBalances state', () => {
|
||||
const { addPendingTxs, resetPendingTxs } =
|
||||
usePendingBalancesStore.getState();
|
||||
|
||||
act(() => {
|
||||
addPendingTxs([event1, event2]);
|
||||
});
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
event1,
|
||||
event2,
|
||||
]);
|
||||
|
||||
act(() => {
|
||||
resetPendingTxs();
|
||||
});
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { Event } from 'ethers';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
|
||||
import create from 'zustand';
|
||||
|
||||
export type PendingTxsStore = {
|
||||
pendingBalances: Event[];
|
||||
addPendingTxs: (event: Event[]) => void;
|
||||
removePendingTx: (event: Event) => void;
|
||||
resetPendingTxs: () => void;
|
||||
};
|
||||
|
||||
export const usePendingBalancesStore = create<PendingTxsStore>((set, get) => ({
|
||||
pendingBalances: [],
|
||||
addPendingTxs: (event: Event[]) => {
|
||||
set({
|
||||
pendingBalances: uniqBy(
|
||||
[...get().pendingBalances, ...event],
|
||||
'transactionHash'
|
||||
),
|
||||
});
|
||||
},
|
||||
removePendingTx: (event: Event) => {
|
||||
set({
|
||||
pendingBalances: [
|
||||
...get().pendingBalances.filter(
|
||||
({ transactionHash }) => transactionHash !== event.transactionHash
|
||||
),
|
||||
],
|
||||
});
|
||||
},
|
||||
resetPendingTxs: () => {
|
||||
set({ pendingBalances: [] });
|
||||
},
|
||||
}));
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
+1
-1
@@ -15,7 +15,7 @@ export const ProposalFormTransactionDialog = ({
|
||||
finalizedProposal,
|
||||
TransactionDialog,
|
||||
}: ProposalFormTransactionDialogProps) => {
|
||||
// Render a custom complete UI if the proposal was rejected other wise
|
||||
// Render a custom complete UI if the proposal was rejected otherwise
|
||||
// pass undefined so that the default vega transaction dialog UI gets used
|
||||
const completeContent = finalizedProposal?.rejectionReason ? (
|
||||
<p>{finalizedProposal.rejectionReason}</p>
|
||||
|
||||
@@ -102,7 +102,7 @@ export const VoteDetails = ({
|
||||
</table>
|
||||
</section>
|
||||
)}
|
||||
<section>
|
||||
<section data-testid="votes-table">
|
||||
<SubHeading title={t('tokenVotes')} />
|
||||
<p>
|
||||
<span>
|
||||
|
||||
@@ -90,22 +90,15 @@ describe('Raw proposal form', () => {
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
busEvents: [
|
||||
{
|
||||
__typename: 'BusEvent',
|
||||
type: Schema.BusEventType.Proposal,
|
||||
event: {
|
||||
__typename: 'Proposal',
|
||||
id: '2fca514cebf9f465ae31ecb4c5721e3a6f5f260425ded887ca50ba15b81a5d50',
|
||||
reference: 'proposal-reference',
|
||||
state: Schema.ProposalState.STATE_OPEN,
|
||||
rejectionReason:
|
||||
Schema.ProposalRejectionReason
|
||||
.PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE,
|
||||
errorDetails: 'error-details',
|
||||
},
|
||||
},
|
||||
],
|
||||
proposals: {
|
||||
__typename: 'Proposal',
|
||||
id: '2fca514cebf9f465ae31ecb4c5721e3a6f5f260425ded887ca50ba15b81a5d50',
|
||||
reference: 'proposal-reference',
|
||||
state: Schema.ProposalState.STATE_OPEN,
|
||||
rejectionReason:
|
||||
Schema.ProposalRejectionReason.PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE,
|
||||
errorDetails: 'error-details',
|
||||
},
|
||||
},
|
||||
},
|
||||
delay: 300,
|
||||
|
||||
@@ -126,10 +126,10 @@ export const VotingPowerRenderer = ({ data }: VotingPowerRendererProps) => {
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<div>
|
||||
<div data-testid="unnormalised-voting-power-tooltip">
|
||||
{t('unnormalisedVotingPower')}: {data.unnormalisedVotingPower}
|
||||
</div>
|
||||
<div>
|
||||
<div data-testid="normalised-voting-power-tooltip">
|
||||
{t('normalisedVotingPower')}: {data.normalisedVotingPower}
|
||||
</div>
|
||||
</>
|
||||
@@ -155,13 +155,13 @@ export const TotalStakeRenderer = ({ data }: TotalStakeRendererProps) => {
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<div>
|
||||
<div data-testid="staked-operator-tooltip">
|
||||
{t('stakedByOperator')}: {data.stakedByOperator.toString()}
|
||||
</div>
|
||||
<div>
|
||||
<div data-testid="staked-delegates-tooltip">
|
||||
{t('stakedByDelegates')}: {data.stakedByDelegates.toString()}
|
||||
</div>
|
||||
<div>
|
||||
<div data-testid="total-staked-tooltip">
|
||||
{t('totalStake')}: <span className="font-bold">{data.stake}</span>
|
||||
</div>
|
||||
</>
|
||||
@@ -177,7 +177,7 @@ interface TotalPenaltiesRendererProps {
|
||||
performanceScore: string;
|
||||
performancePenalty: string;
|
||||
overstakedAmount: string;
|
||||
overstakedPenalty: string;
|
||||
overstakingPenalty: string;
|
||||
totalPenalties: string;
|
||||
};
|
||||
}
|
||||
@@ -191,23 +191,13 @@ export const TotalPenaltiesRenderer = ({
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<div>
|
||||
<span>
|
||||
{t('performancePenalty')}: {data.performancePenalty}
|
||||
</span>
|
||||
<span className="pl-2">
|
||||
({t('score')} {data.performanceScore})
|
||||
</span>
|
||||
<div data-testid="performance-penalty-tooltip">
|
||||
{t('performancePenalty')}: {data.performancePenalty}
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
{t('overstakedPenalty')}: {data.overstakedPenalty}
|
||||
</span>
|
||||
<span className="pl-2">
|
||||
({t('overstaked')} {data.overstakedAmount})
|
||||
</span>
|
||||
<div data-testid="overstaked-penalty-tooltip">
|
||||
{t('overstakedPenalty')}: {data.overstakingPenalty}
|
||||
</div>
|
||||
<div>
|
||||
<div data-testid="total-penalty-tooltip">
|
||||
{t('totalPenalties')}:{' '}
|
||||
<span className="font-bold">{data.totalPenalties}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Dialog, Icon, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import Routes from '../../routes';
|
||||
import type { StakeAction } from './staking-form';
|
||||
import { Actions, RemoveType } from './staking-form';
|
||||
@@ -23,6 +23,7 @@ export const StakeSuccess = ({
|
||||
toggleDialog,
|
||||
}: StakeSuccessProps) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const isAdd = action === Actions.Add;
|
||||
const title = isAdd
|
||||
? t('stakeAddSuccessTitle', { amount })
|
||||
@@ -44,7 +45,15 @@ export const StakeSuccess = ({
|
||||
<div>
|
||||
<p>{message}</p>
|
||||
<p>
|
||||
<Link className="underline" to={Routes.VALIDATORS}>
|
||||
<Link
|
||||
className="underline"
|
||||
to={Routes.VALIDATORS}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
toggleDialog();
|
||||
setTimeout(() => navigate(Routes.VALIDATORS), 0);
|
||||
}}
|
||||
>
|
||||
{t('backToStaking')}
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const TrancheProgress = ({
|
||||
<span className="tranches__progress-title">{t('Locked')}</span>
|
||||
<ProgressBar
|
||||
width={220}
|
||||
color={Colors.vega.pink}
|
||||
color={Colors.vega.pink.DEFAULT}
|
||||
percentage={lockedPercentage}
|
||||
/>
|
||||
<span className="tranches__progress-numbers">
|
||||
@@ -40,7 +40,7 @@ export const TrancheProgress = ({
|
||||
<span className="tranches__progress-title">{t('Redeemed')}</span>
|
||||
<ProgressBar
|
||||
width={220}
|
||||
color={Colors.vega.green}
|
||||
color={Colors.vega.green.DEFAULT}
|
||||
percentage={removedPercentage}
|
||||
/>
|
||||
<span className="tranches__progress-numbers">
|
||||
|
||||
@@ -34,10 +34,10 @@ export const VestingChart = () => {
|
||||
<AreaChart data={data}>
|
||||
<defs>
|
||||
{[
|
||||
['pink', Colors.vega.pink],
|
||||
['green', Colors.vega.green],
|
||||
['pink', Colors.vega.pink.DEFAULT],
|
||||
['green', Colors.vega.green.DEFAULT],
|
||||
['orange', Colors.warning],
|
||||
['yellow', Colors.vega.yellow],
|
||||
['yellow', Colors.vega.yellow.DEFAULT],
|
||||
].map(([key, color]) => (
|
||||
<linearGradient key={key} id={key} x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor={color} stopOpacity={0.85} />
|
||||
@@ -97,7 +97,7 @@ export const VestingChart = () => {
|
||||
dot={false}
|
||||
type="linear"
|
||||
dataKey="team"
|
||||
stroke={Colors.vega.pink}
|
||||
stroke={Colors.vega.pink.DEFAULT}
|
||||
fill="url(#pink)"
|
||||
yAxisId={0}
|
||||
strokeWidth={2}
|
||||
@@ -109,7 +109,7 @@ export const VestingChart = () => {
|
||||
dot={false}
|
||||
type="monotone"
|
||||
dataKey="earlyInvestors"
|
||||
stroke={Colors.vega.green}
|
||||
stroke={Colors.vega.green.DEFAULT}
|
||||
fill="url(#green)"
|
||||
yAxisId={0}
|
||||
strokeWidth={2}
|
||||
@@ -121,7 +121,7 @@ export const VestingChart = () => {
|
||||
dot={false}
|
||||
type="monotone"
|
||||
dataKey="publicSale"
|
||||
stroke={Colors.vega.yellow}
|
||||
stroke={Colors.vega.yellow.DEFAULT}
|
||||
fill="url(#yellow)"
|
||||
yAxisId={0}
|
||||
strokeWidth={2}
|
||||
|
||||
@@ -13,3 +13,9 @@
|
||||
@apply mb-2 text-neutral-400;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.border-default {
|
||||
@apply border-vega-dark-200;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,18 +24,155 @@ const txTimeout = Cypress.env('txTimeout');
|
||||
const sepoliaUrl = Cypress.env('ETHERSCAN_URL');
|
||||
const btcName =
|
||||
'BTC (local)5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c - tBTC';
|
||||
const vegaName =
|
||||
'Vegab4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b - VEGA';
|
||||
const btcSymbol = 'tBTC';
|
||||
const vegaSymbol = 'VEGA';
|
||||
const usdcSymbol = 'fUSDC';
|
||||
const toastContent = 'toast-content';
|
||||
const ordersTab = 'Orders';
|
||||
const depositsTab = 'Deposits';
|
||||
const collateralTab = 'Collateral';
|
||||
const toastCloseBtn = 'toast-close';
|
||||
const price = '390';
|
||||
const size = '0.0005';
|
||||
const newPrice = '200';
|
||||
const completeWithdrawalBtn = 'complete-withdrawal';
|
||||
const submitTransferBtn = '[type="submit"]';
|
||||
const transferForm = 'transfer-form';
|
||||
|
||||
// TODO: ensure this test runs only if capsule is running via workflow
|
||||
// Because the tests are run on a live network to optimize time, the tests are interdependent and must be run in the given order.
|
||||
describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
before(() => {
|
||||
cy.createMarket();
|
||||
cy.get('@markets').then((markets) => {
|
||||
cy.wrap(markets[0]).as('market');
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
|
||||
it('can deposit', function () {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
|
||||
// 1001-DEPO-001
|
||||
// 1001-DEPO-002
|
||||
// 1001-DEPO-003
|
||||
// 1001-DEPO-005
|
||||
// 1001-DEPO-006
|
||||
// 1001-DEPO-007
|
||||
// 1001-DEPO-008
|
||||
// 1001-DEPO-009
|
||||
// 1001-DEPO-010
|
||||
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
cy.getByTestId('deposit-approve-submit').click();
|
||||
cy.getByTestId('dialog-title').should('contain.text', 'Approve complete');
|
||||
cy.get('[data-testid="Return to deposit"]').click();
|
||||
cy.get(amountField).clear().type('10');
|
||||
cy.getByTestId('deposit-submit').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
`Transaction confirmedYour transaction has been confirmed.View on EtherscanDeposit 10.00 ${btcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
cy.getByTestId('Collateral').click();
|
||||
|
||||
cy.highlight('deposit verification');
|
||||
|
||||
cy.getByTestId('asset', txTimeout).should('contain.text', btcSymbol);
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.get('.ag-cell-value', txTimeout).should('contain.text', btcSymbol);
|
||||
cy.get('[col-id="status"]').should('not.have.text', 'Open', txTimeout);
|
||||
|
||||
cy.get('[col-id="txHash"]')
|
||||
.should('have.length.above', 2)
|
||||
.eq(1)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', btcSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '10.00');
|
||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
||||
cy.get('[col-id="status"]').should('have.text', 'Finalized');
|
||||
cy.get('[col-id="txHash"]')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
});
|
||||
|
||||
it('can key to key transfers', function () {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId('open-transfer-dialog').click();
|
||||
cy.getByTestId('transfer-form').should('be.visible');
|
||||
cy.getByTestId('transfer-form').find('[name="toAddress"]').select(1);
|
||||
cy.get('select option')
|
||||
.contains('BTC')
|
||||
.invoke('index')
|
||||
.then((index) => {
|
||||
cy.get(assetSelectField).select(index, { force: true });
|
||||
});
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.focus()
|
||||
.type('1', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Transfer completeYour transaction has been confirmed TransferTo 7f9cf0…c255351.00 tBTC'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
});
|
||||
|
||||
it('can not withdrawal because of no MultiSign', function () {
|
||||
// 1002-WITH-022
|
||||
// 1002-WITH-023
|
||||
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Funds unlocked'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
cy.getByTestId('tab-withdrawals').within(() => {
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('[col-id="status"]').should('contain.text', 'Pending');
|
||||
});
|
||||
});
|
||||
|
||||
cy.highlight('withdrawals verification');
|
||||
cy.getByTestId('toast-complete-withdrawal').click();
|
||||
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Error occurredprocessing response error'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId(completeWithdrawalBtn).should(
|
||||
'contain.text',
|
||||
'Complete withdrawal'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('capsule', { tags: '@slow' }, () => {
|
||||
before(() => {
|
||||
cy.createMarket();
|
||||
@@ -121,20 +258,22 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can edit order', function () {
|
||||
// comment because of bug #2695
|
||||
it.skip('can edit order', function () {
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.getByTestId('edit').first().should('be.visible').click();
|
||||
cy.getByTestId('dialog-title').should('contain.text', 'Edit order');
|
||||
cy.get('#limitPrice').focus().clear().type(newPrice);
|
||||
cy.getByTestId('edit-order').find('[type="submit"]').click();
|
||||
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
`ConfirmedYour transaction has been confirmed View in block explorerEdit order - activeTEST.24h+${size} @ ${price}.00 ${usdcSymbol}+${size} @ ${newPrice}.00 ${usdcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(ordersTab).click();
|
||||
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
.first()
|
||||
@@ -145,8 +284,8 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderUpdatedAt);
|
||||
});
|
||||
});
|
||||
|
||||
it('can cancel order', function () {
|
||||
// comment because of bug #2695
|
||||
it.skip('can cancel order', function () {
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.getByTestId('cancel').first().click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
@@ -164,62 +303,8 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
.should('contain.text', OrderStatusMapping.STATUS_CANCELLED);
|
||||
});
|
||||
|
||||
it('can deposit', function () {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
|
||||
// 1001-DEPO-001
|
||||
// 1001-DEPO-002
|
||||
// 1001-DEPO-003
|
||||
// 1001-DEPO-005
|
||||
// 1001-DEPO-006
|
||||
// 1001-DEPO-007
|
||||
// 1001-DEPO-008
|
||||
// 1001-DEPO-009
|
||||
// 1001-DEPO-010
|
||||
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
cy.getByTestId('deposit-approve-submit').click();
|
||||
cy.getByTestId('dialog-title').should('contain.text', 'Approve complete');
|
||||
cy.get('[data-testid="Return to deposit"]').click();
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('deposit-submit').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
`Transaction confirmedYour transaction has been confirmed.View on EtherscanDeposit 1.00 ${btcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
cy.getByTestId('Collateral').click();
|
||||
|
||||
cy.highlight('deposit verification');
|
||||
|
||||
cy.getByTestId('asset', txTimeout).should('contain.text', btcSymbol);
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.get('.ag-cell-value', txTimeout).should('contain.text', btcSymbol);
|
||||
cy.get('[col-id="status"]').should('not.have.text', 'Open', txTimeout);
|
||||
|
||||
cy.get('[col-id="txHash"]')
|
||||
.should('have.length.above', 2)
|
||||
.eq(1)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', btcSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '1.00');
|
||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
||||
cy.get('[col-id="status"]').should('have.text', 'Finalized');
|
||||
cy.get('[col-id="txHash"]')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
});
|
||||
|
||||
it('can withdrawal', function () {
|
||||
// 1002-WITH-001
|
||||
// 1002-WITH-0014
|
||||
// 1002-WITH-006
|
||||
// 1002-WITH-009
|
||||
// 1002-WITH-011
|
||||
@@ -229,21 +314,24 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
// 1002-WITH-014
|
||||
// 1002-WITH-015
|
||||
// 1002-WITH-016
|
||||
// 1002-WITH-017
|
||||
// 1002-WITH-019
|
||||
// 1002-WITH-020
|
||||
// 1002-WITH-021
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
cy.get(assetSelectField, txTimeout).select(
|
||||
'BTC (local)5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c - tBTC',
|
||||
{ force: true }
|
||||
);
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Funds unlocked'
|
||||
);
|
||||
|
||||
cy.getByTestId('tab-withdrawals').within(() => {
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
@@ -255,12 +343,24 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
|
||||
cy.highlight('withdrawals verification');
|
||||
cy.getByTestId('toast-complete-withdrawal').click();
|
||||
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Transaction confirmed'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
|
||||
cy.getByTestId('complete-withdrawal', txTimeout).should('not.exist');
|
||||
cy.wrap(null).then(() => {
|
||||
try {
|
||||
cy.getByTestId(completeWithdrawalBtn)
|
||||
.eq(0, txTimeout)
|
||||
.should('not.exist');
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'Assertion failed, but we are continuing because this is our wait to complete transaction'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
cy.get('[col-id="txHash"]', txTimeout)
|
||||
.should('have.length.above', 1)
|
||||
@@ -281,9 +381,12 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
// comment because of bug #2819
|
||||
// cy.getByTestId('withdraw-dialog-button').click();
|
||||
// cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '0')
|
||||
});
|
||||
|
||||
it('deposit - if approved amount is less than deposit: must see that an approval is needed and be prompted to approve more', function () {
|
||||
it('approved amount is less than deposit', function () {
|
||||
// 1001-DEPO-006
|
||||
|
||||
cy.getByTestId(depositsTab).click();
|
||||
@@ -292,6 +395,67 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
cy.get(amountField).clear().type('20000000');
|
||||
cy.getByTestId('deposit-approve-submit').should('be.visible');
|
||||
});
|
||||
|
||||
it('withdraw - delay verification', function () {
|
||||
// 1001-DEPO-007
|
||||
// 1001-DEPO-024
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(vegaName, { force: true });
|
||||
cy.getByTestId('deposit-approve-submit').click();
|
||||
cy.getByTestId('dialog-title').should('contain.text', 'Approve complete');
|
||||
cy.get('[data-testid="Return to deposit"]').click();
|
||||
cy.get(amountField).clear().type('10000');
|
||||
cy.getByTestId('deposit-submit').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
`Your transaction has been confirmed.`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
cy.getByTestId('Collateral').click();
|
||||
|
||||
cy.highlight('deposit verification');
|
||||
|
||||
cy.getByTestId('asset', txTimeout).should('contain.text', vegaSymbol);
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.get('.ag-cell-value', txTimeout).should('contain.text', vegaSymbol);
|
||||
cy.get('[col-id="status"]').should('not.have.text', 'Open', txTimeout);
|
||||
|
||||
cy.get('[col-id="txHash"]')
|
||||
.should('have.length.above', 2)
|
||||
.eq(1)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', vegaSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '10,000.00');
|
||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
||||
cy.get('[col-id="status"]').should('have.text', 'Finalized');
|
||||
cy.get('[col-id="txHash"]')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
cy.get(assetSelectField, txTimeout).select(vegaName, { force: true });
|
||||
cy.get(amountField).clear().type('10000');
|
||||
cy.getByTestId('DELAY_TIME_value').should('have.text', '5 days');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Your funds have been unlocked'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
cy.getByTestId(completeWithdrawalBtn).first().should('be.visible').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should('contain.text', 'Delayed');
|
||||
});
|
||||
});
|
||||
|
||||
function checkIfDataAndTimeOfCreationAndUpdateIsEqual(date: string) {
|
||||
|
||||
@@ -4,6 +4,7 @@ const marketInfoBtn = 'Info';
|
||||
const row = 'key-value-table-row';
|
||||
const marketTitle = 'accordion-title';
|
||||
const externalLink = 'external-link';
|
||||
const accordionContent = 'accordion-content';
|
||||
|
||||
describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
@@ -179,7 +180,8 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
'termination.BTC.value'
|
||||
);
|
||||
|
||||
cy.getByTestId(externalLink)
|
||||
cy.getByTestId(accordionContent)
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/oracles');
|
||||
});
|
||||
@@ -187,12 +189,14 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
it('proposal displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Proposal').click();
|
||||
|
||||
cy.getByTestId(externalLink)
|
||||
cy.getByTestId(accordionContent)
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.first()
|
||||
.should('have.text', 'View governance proposal')
|
||||
.and('have.attr', 'href')
|
||||
.and('contain', '/proposals/market-0');
|
||||
cy.getByTestId(externalLink)
|
||||
cy.getByTestId(accordionContent)
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.eq(1)
|
||||
.should('have.text', 'Propose a change to market')
|
||||
.and('have.attr', 'href')
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { marketQuery } from '@vegaprotocol/mock';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
||||
|
||||
describe('markets table', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
@@ -101,7 +104,8 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
'have.length',
|
||||
10
|
||||
);
|
||||
cy.getByTestId('external-link')
|
||||
cy.getByTestId('tab-proposed-markets')
|
||||
.find('[data-testid="external-link"]')
|
||||
.should('have.length', 11)
|
||||
.last()
|
||||
.should('have.text', 'Propose a new market')
|
||||
@@ -111,6 +115,53 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
`${Cypress.env('VEGA_TOKEN_URL')}/proposals/propose/new-market`
|
||||
);
|
||||
});
|
||||
|
||||
it('opening auction subsets should be properly displayed', () => {
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
);
|
||||
cy.mockGQL((req) => {
|
||||
const override = {
|
||||
market: {
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: `opening auction MARKET`,
|
||||
},
|
||||
},
|
||||
state: Schema.MarketState.STATE_ACTIVE,
|
||||
tradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
||||
},
|
||||
};
|
||||
const market = marketQuery(override);
|
||||
aliasGQLQuery(req, 'Market', market);
|
||||
aliasGQLQuery(req, 'ProposalOfMarket', {
|
||||
proposal: { terms: { enactmentDatetime: '2023-01-31 12:00:01' } },
|
||||
});
|
||||
});
|
||||
cy.visit('/');
|
||||
cy.visit('#/markets/market-0');
|
||||
cy.getByTestId('item-value').contains('Opening auction').realHover();
|
||||
cy.getByTestId('opening-auction-sub-status').should(
|
||||
'contain.text',
|
||||
'Opening auction: Not enough liquidity to open'
|
||||
);
|
||||
|
||||
const now = new Date(Date.parse('2023-01-30 12:00:01')).getTime();
|
||||
cy.clock(now, ['Date']); // Set "now" to BEFORE reservation
|
||||
cy.visit('/');
|
||||
cy.visit('#/markets/market-0');
|
||||
cy.getByTestId('item-value').contains('Opening auction').realHover();
|
||||
cy.getByTestId('opening-auction-sub-status').should(
|
||||
'contain.text',
|
||||
`Opening auction: Closing on ${getDateTimeFormat().format(
|
||||
new Date('2023-01-31 12:00:01')
|
||||
)}`
|
||||
);
|
||||
cy.clock().then((clock) => {
|
||||
clock.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function openMarketDropDown() {
|
||||
|
||||
@@ -452,7 +452,7 @@ describe('limit order validations', { tags: '@smoke' }, () => {
|
||||
//7002-SORD-018
|
||||
cy.getByTestId(orderPriceField)
|
||||
.siblings('label')
|
||||
.should('have.text', 'Price (tBTC)');
|
||||
.should('have.text', 'Price (BTC)');
|
||||
});
|
||||
|
||||
it('must see warning when placing an order with expiry date in past', () => {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { marketsDataQuery } from '@vegaprotocol/mock';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
@@ -17,6 +20,47 @@ describe('positions', { tags: '@smoke' }, () => {
|
||||
validatePositionsDisplayed();
|
||||
});
|
||||
|
||||
it('renders position among some graphql errors', () => {
|
||||
const errors = [
|
||||
{
|
||||
message: 'no market data for market: market-2',
|
||||
path: ['market', 'data'],
|
||||
extensions: {
|
||||
code: 13,
|
||||
type: 'Internal',
|
||||
},
|
||||
},
|
||||
];
|
||||
const marketData = marketsDataQuery();
|
||||
const edges = marketData.marketsConnection?.edges.map((market) => {
|
||||
const replace =
|
||||
market.node.data?.market.id === 'market-2' ? null : market.node.data;
|
||||
return { ...market, node: { ...market.node, data: replace } };
|
||||
});
|
||||
const overrides = {
|
||||
...marketData,
|
||||
marketsConnection: { ...marketData.marketsConnection, edges },
|
||||
};
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'MarketsData', overrides, errors);
|
||||
});
|
||||
cy.visit('/#/markets/market-0');
|
||||
const emptyCells = [
|
||||
'notional',
|
||||
'markPrice',
|
||||
'liquidationPrice',
|
||||
'currentLeverage',
|
||||
'averageEntryPrice',
|
||||
];
|
||||
cy.getByTestId('tab-positions').within(() => {
|
||||
cy.get('[row-id="market-2"]').within(() => {
|
||||
emptyCells.forEach((cell) => {
|
||||
cy.get(`[col-id="${cell}"]`).should('contain.text', '-');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function validatePositionsDisplayed() {
|
||||
cy.getByTestId('tab-positions').should('be.visible');
|
||||
cy.getByTestId('tab-positions').within(() => {
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import { selectAsset } from '../support/helpers';
|
||||
|
||||
const formFieldError = 'input-error-text';
|
||||
const toAddressField = '[name="toAddress"]';
|
||||
const amountField = 'input[name="amount"]';
|
||||
const submitTransferBtn = '[type="submit"]';
|
||||
const transferForm = 'transfer-form';
|
||||
const errorText = 'input-error-text';
|
||||
const openTransferDialog = 'open-transfer-dialog';
|
||||
const closeDialog = 'dialog-close';
|
||||
const dialogTransferText = 'dialog-transfer-text';
|
||||
|
||||
const ASSET_SEPOLIA_TBTC = 2;
|
||||
const ASSET_EURO = 1;
|
||||
|
||||
const toastContent = 'toast-content';
|
||||
const collateralTab = 'Collateral';
|
||||
const toastCloseBtn = 'toast-close';
|
||||
|
||||
describe(
|
||||
'transfer form validation and transfer from options',
|
||||
{ tags: '@smoke' },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
});
|
||||
|
||||
it('empty fields', () => {
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
|
||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
||||
// only 2 despite 3 fields because the ethereum address will be auto populated
|
||||
cy.getByTestId(formFieldError).should('have.length', 3);
|
||||
});
|
||||
it('min amount', () => {
|
||||
// 1002-WITH-010
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.get(amountField).clear().type('0');
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'Value is below minimum'
|
||||
);
|
||||
});
|
||||
it('max amount', () => {
|
||||
selectAsset(ASSET_EURO); // Will be above maximum because the vega wallet doesn't have any collateral
|
||||
cy.get(amountField).clear().type('1001', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'You cannot transfer more than your available collateral'
|
||||
);
|
||||
});
|
||||
|
||||
it('can start transfer from vega wallet', () => {
|
||||
cy.getByTestId(closeDialog).click();
|
||||
cy.getByTestId('manage-vega-wallet').click();
|
||||
cy.getByTestId('wallet-transfer').should('have.text', 'Transfer').click();
|
||||
cy.getByTestId(dialogTransferText).should(
|
||||
'contain.text',
|
||||
'Transfer funds to another Vega key from 02ecea…342f65 If you are at all unsure, stop and seek advice.'
|
||||
);
|
||||
});
|
||||
|
||||
it('can start transfer from trading collateral table', () => {
|
||||
cy.getByTestId(closeDialog).click();
|
||||
cy.getByTestId('Trading').first().click();
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
cy.getByTestId(dialogTransferText).should(
|
||||
'contain.text',
|
||||
'Transfer funds to another Vega key from 02ecea…342f65 If you are at all unsure, stop and seek advice.'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe('withdraw actions', { tags: '@regression' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
cy.mockVegaWalletTransaction();
|
||||
});
|
||||
|
||||
it('key to key transfers by select key', function () {
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.getByTestId(transferForm).find(toAddressField).select(1);
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm).find(amountField).type('1', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Awaiting confirmation'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
});
|
||||
|
||||
it('key to key transfers by enter manual key', function () {
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.contains('Enter manually').click();
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm).find(amountField).type('1', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Awaiting confirmation'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
});
|
||||
});
|
||||
@@ -41,6 +41,7 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
|
||||
cy.get(toAddressField).should('have.value', ethAddressValue);
|
||||
});
|
||||
it('min amount', () => {
|
||||
// 1002-WITH-010
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.get(amountField).clear().type('0');
|
||||
cy.getByTestId(submitWithdrawBtn).click();
|
||||
@@ -50,6 +51,8 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
|
||||
);
|
||||
});
|
||||
it('max amount', () => {
|
||||
// 1002-WITH-005
|
||||
// 1002-WITH-008
|
||||
selectAsset(ASSET_EURO); // Will be above maximum because the vega wallet doesn't have any collateral
|
||||
cy.get(amountField).clear().type('1001', { delay: 100 });
|
||||
cy.getByTestId(submitWithdrawBtn).click();
|
||||
@@ -60,6 +63,7 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
it('can set amount using use maximum button', () => {
|
||||
// 1002-WITH-004
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(useMaximumAmount).click();
|
||||
cy.get(amountField).should('have.value', '1000.00000');
|
||||
@@ -68,6 +72,9 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
|
||||
|
||||
describe('withdraw actions', { tags: '@regression' }, () => {
|
||||
// this is extremely ugly hack, but setting it properly in contract is too much effort for such simple validation
|
||||
|
||||
// 1002-WITH-018
|
||||
|
||||
const withdrawalThreshold =
|
||||
Cypress.env('VEGA_ENV') === 'CUSTOM' ? '0.00' : '100.00';
|
||||
before(() => {
|
||||
@@ -88,6 +95,8 @@ describe('withdraw actions', { tags: '@regression' }, () => {
|
||||
});
|
||||
|
||||
it('triggers transaction when submitted', () => {
|
||||
// 1002-WITH-002
|
||||
// 1002-WITH-003
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId('BALANCE_AVAILABLE_label').should(
|
||||
'contain.text',
|
||||
@@ -108,7 +117,4 @@ describe('withdraw actions', { tags: '@regression' }, () => {
|
||||
cy.getByTestId(submitWithdrawBtn).click();
|
||||
cy.getByTestId('toast').should('contain.text', 'Awaiting confirmation');
|
||||
});
|
||||
|
||||
it.skip('creates a withdrawal on submit'); // Needs capsule
|
||||
it.skip('creates a withdrawal on submit and prompts to complete withdrawal'); // Needs capsule
|
||||
});
|
||||
|
||||
@@ -8,3 +8,4 @@ NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"
|
||||
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
|
||||
@@ -9,6 +9,7 @@ NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
NX_VEGA_URL=http://localhost:3028/query
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
@@ -8,3 +8,4 @@ NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"
|
||||
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases
|
||||
|
||||
@@ -7,3 +7,4 @@ NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\",\"TESTNET\":\"h
|
||||
NX_VEGA_TOKEN_URL=https://token.vega.xyz
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
|
||||
@@ -7,4 +7,5 @@ NX_VEGA_EXPLORER_URL=https://mainnet-mirror.explorer.vega.xyz
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\",\"STAGNET3\":\"https://stagnet3.console.vega.xyz\"}
|
||||
NX_VEGA_TOKEN_URL=https://mainnet-mirror.token.vega.xyz
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
|
||||
@@ -7,3 +7,4 @@ NX_VEGA_NETWORKS={\"DEVNET\":\"https://dev.token.vega.xyz\",\"STAGNET3\":\"https
|
||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/sandbox-network.json
|
||||
NX_VEGA_EXPLORER_URL=https://sandbox.explorer.vega.xyz
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
|
||||
@@ -8,3 +8,4 @@ NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"
|
||||
NX_VEGA_TOKEN_URL=https://stagnet1.token.vega.xyz
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user