Compare commits

..
Author SHA1 Message Date
Matthew Russell 435af22ea7 chore: switch to use user-event click otherwise test errors with unhandled promise 2023-01-25 22:42:33 -08:00
Matthew Russell 52c38e04bf chore: add test to ensure portal 2023-01-25 15:38:42 -08:00
Matthew Russell fc52deaf6d chore: use portal by default for dropdown menus 2023-01-25 15:13:47 -08:00
Bartłomiej GłowniaandMatthew Russell 129274a8e6 feat: add seperate transaction toast for completed eth transaction (#2733)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-01-25 12:25:44 -08:00
Bartłomiej Głownia 3fadda15fd chore: remove sentry captureException in marketDepthProvider update (#2713) 2023-01-25 11:58:37 -08:00
m.rayandasiaznik 7ea7edc1e2 feat(#2565): orderbook populate limit (#2690)
Co-authored-by: asiaznik <artur@vegaprotocol.io>
2023-01-25 11:38:26 -08:00
Edd dad953b45b fix(explorer): fix various small lp tx view bugs (#2738) 2023-01-25 18:18:59 +00:00
mattrussell36 ed1363b035 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-25 18:08:10 +00:00
Art b2ab4f49d9 feat: rich asset details dropdown (2557) (#2711) 2023-01-25 14:24:53 +00:00
Edd a60379b40f feat(explorer): data submission tx view (#2698) 2023-01-25 13:33:12 +00:00
mattrussell36 7db2a58f9d chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-25 12:08:53 +00:00
m.ray 7279556bf7 feat(#2599): liquidity price range added in market info (#2716) 2023-01-25 13:42:29 +02:00
Dexter Edwards 52cc2ef7df fix: add risk parameters (#2706) 2023-01-25 10:36:22 +00:00
macqbat a218da93a6 feat: logging - working solution (#2696) 2023-01-25 09:43:11 +01:00
daro-maj 10fe82dea8 chore: deposit and withdrawal capsule e2e tests (#2720) 2023-01-25 08:21:17 +01:00
mattrussell36 b0de4dfbf3 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-25 06:06:42 +00:00
Matthew Russell cd0e2e41a8 chore(#2668): use ui-toolkit checkbox for orderbook (#2702) 2023-01-24 21:49:59 -08:00
mattrussell36 dc3330d55e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-25 00:12:48 +00:00
Matthew Russell a016feab2a fix: dont use current value in update callback for estimate queries (#2721) 2023-01-24 21:33:55 +00:00
mattrussell36 68446ccf65 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-24 18:08:20 +00:00
Matthew Russell c7aec5cfcb chore: add cypress files to ignore list and use glob ignore option (#2700) 2023-01-24 08:06:46 -08:00
Radosław Szpiech f58144d785 chore: fix skip cache typo in workflow (#2715) 2023-01-24 15:59:53 +00:00
Dexter Edwards 415b902f53 feat: splash loader more central (#2707) 2023-01-24 14:02:43 +00:00
165b131b27 chore: move token's web3 url connector to web3 lib (#2655)
Co-authored-by: Dexter <dexter.edwards93@gmail.com>
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2023-01-24 14:02:17 +00:00
Bartłomiej Głownia 0cff5895c7 feat: call update with null data from useDataProvider hook on variables change (#2689) 2023-01-24 14:01:51 +00:00
Dexter Edwards 31881f8e5a fix: calculation of required stake (#2687) 2023-01-24 14:00:38 +00:00
Art 346af6118d chore: consolidate persisted order entries (#2673) 2023-01-24 13:56:12 +00:00
Dexter Edwards bcbc3bb146 fix: adjust route strcuture (#2708) 2023-01-24 13:44:11 +00:00
Radosław Szpiech d7440cfe54 chore: correct runner name (#2710) 2023-01-24 13:25:29 +01:00
mattrussell36 801606e37f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-24 12:08:25 +00:00
Dexter Edwards 01e7b8caee fix: show error message correctly (#2705) 2023-01-24 11:29:42 +00:00
Joe Tsang e883ef5c5e test(#2639): e2e view public key tests (#2688) 2023-01-24 10:52:01 +00:00
mattrussell36 2f391cde5b chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-24 06:07:12 +00:00
mattrussell36 3620314dca chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-24 00:11:13 +00:00
mattrussell36 9bdd1ed387 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-23 18:06:31 +00:00
mattrussell36 37dc8bf110 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-23 12:08:39 +00:00
149 changed files with 2850 additions and 1259 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ on:
jobs:
nightly:
uses: ./.github/workflows/cypress-pr.yml
uses: ./.github/workflows/cypress-run.yml
secrets: inherit
with:
projects: '["explorer-e2e","token-e2e","trading-e2e"]'
+3
View File
@@ -40,10 +40,12 @@ jobs:
- name: See affected apps
run: |
affected=$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)
echo -n "Affected projects: $affected"
projects=""
if [[ $affected == *"token"* ]]; then projects+='"token-e2e" '; fi
if [[ $affected == *"trading"* ]]; then projects+='"trading-e2e" '; fi
if [[ $affected == *"explorer"* ]]; then projects+='"explorer-e2e" '; fi
if [[ -z "$projects" ]]; then projects+='"token-e2e" "trading-e2e" "explorer-e2e" '; fi
projects=${projects%?}
projects=[${projects// /,}]
echo PROJECTS=$projects >> $GITHUB_ENV
@@ -53,6 +55,7 @@ jobs:
run:
needs: pr
if: ${{ needs.pr.outputs.projects != '[]' }}
uses: ./.github/workflows/cypress-run.yml
secrets: inherit
with:
+3 -2
View File
@@ -14,15 +14,16 @@ on:
jobs:
e2e:
strategy:
fail-fast: false
matrix:
project: ${{ fromJSON(inputs.projects) }}
runs-on: self-hosted
runs-on: self-hosted-runner
timeout-minutes: 30
steps:
# Checks if skip cache was requested
- name: Set skip-nx-cache flag
if: ${{ inputs.skip-cache == true }}
run: echo SKIP_CACHE="--skip-nx-cache" >> GITHUB_ENV
run: echo "SKIP_CACHE=--skip-nx-cache" >> $GITHUB_ENV
# Checkout front ends
- name: Checkout frontend mono repo
@@ -2,16 +2,5 @@ query ExplorerSettlementAssetForMarket($id: ID!) {
market(id: $id) {
id
decimalPlaces
tradableInstrument {
instrument {
product {
... on Future {
settlementAsset {
decimals
}
}
}
}
}
}
}
@@ -8,7 +8,7 @@ export type ExplorerSettlementAssetForMarketQueryVariables = Types.Exact<{
}>;
export type ExplorerSettlementAssetForMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', settlementAsset: { __typename?: 'Asset', decimals: number } } } } } | null };
export type ExplorerSettlementAssetForMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number } | null };
export const ExplorerSettlementAssetForMarketDocument = gql`
@@ -16,17 +16,6 @@ export const ExplorerSettlementAssetForMarketDocument = gql`
market(id: $id) {
id
decimalPlaces
tradableInstrument {
instrument {
product {
... on Future {
settlementAsset {
decimals
}
}
}
}
}
}
}
`;
@@ -91,7 +91,7 @@ describe('LiquidityProvisionDetails component', () => {
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
expect(res.getByText('-1')).toBeInTheDocument();
expect(res.getByText('Best Bid')).toBeInTheDocument();
expect(res.getByText('40% (normalised from: 20%)')).toBeInTheDocument();
expect(res.getByText('40%')).toBeInTheDocument();
});
it('handles a missing offset gracefully (should not happen)', () => {
@@ -125,7 +125,7 @@ describe('LiquidityProvisionDetails component', () => {
);
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
expect(res.getByText('40% (normalised from: 20%)')).toBeInTheDocument();
expect(res.getByText('40%')).toBeInTheDocument();
expect(res.getByText('-')).toBeInTheDocument();
});
});
@@ -67,11 +67,7 @@ export function LiquidityProvisionDetailsRow({
<td className="text-center">
{order.reference ? LiquidityReferenceLabel[order.reference] : '-'}
</td>
<td className="text-center">
{proportion === order.proportion
? `${proportion}%`
: `${proportion}% (normalised from: ${order.proportion}%)`}{' '}
</td>
<td className="text-center">{proportion}%</td>
</TableRow>
);
}
@@ -13,15 +13,6 @@ const decimalsMock: ExplorerSettlementAssetForMarketQuery = {
id: '123',
__typename: 'Market',
decimalPlaces: 5,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
decimals: 5,
},
},
},
},
},
};
@@ -36,7 +36,7 @@ export function LiquidityProvisionOffset({
}
/**
* Does the work of formatting the number now we have the settlement decimal places.
* Does the work of formatting the number now we have the market decimal places.
* If no market data is assigned (i.e. during loading, or if the market doesn't exist)
* this function will return the unformatted number
*
@@ -49,9 +49,7 @@ export function getFormattedOffset(
offset: string,
data?: ExplorerSettlementAssetForMarketQuery
) {
const decimals =
data?.market?.tradableInstrument.instrument.product.settlementAsset
.decimals;
const decimals = data?.market?.decimalPlaces;
if (!decimals) {
return offset;
@@ -203,7 +203,7 @@ describe('LiquidityProvisionDetails component', () => {
};
const res = renderComponent(mock);
expect(res.getByText('45% (normalised from: 25%)')).toBeInTheDocument();
expect(res.getByText('55% (normalised from: 30%)')).toBeInTheDocument();
expect(res.getByText('45%')).toBeInTheDocument();
expect(res.getByText('55%')).toBeInTheDocument();
});
});
@@ -0,0 +1,45 @@
import { render } from '@testing-library/react';
import { JSONOracleData, parseData } from './data-submission-json-oracle';
describe('JSONOracleData', () => {
it('renders a code block if there is a payload that is base64 encoded', () => {
const p = 'eyJwcmljZXMuRVRIOTkudmFsdWUiOiAiMTMwNTAwMDAwMCJ9';
const screen = render(<JSONOracleData payload={p} />);
expect(screen.getByTestId('json-code')).toBeInTheDocument();
});
it('renders an error if b64 decoding fails', () => {
const p = 'not-encoded';
const screen = render(<JSONOracleData payload={p} />);
expect(
screen.getByText('Awaiting Block Explorer transaction details')
).toBeInTheDocument();
});
});
describe('Parse JSON oracle data', () => {
it('returns null for an invalid b64 string', () => {
expect(parseData('🤷')).toBeNull();
});
it('returns null for a string that looks like an object', () => {
expect(parseData('{}')).toBeNull();
});
it('returns null for null', () => {
expect(parseData(null as unknown as string)).toBeNull();
});
it('returns a string for b64 encoded data', () => {
const res = parseData('dGVzdCA9IHRydWU=');
expect(typeof res).toEqual('string');
expect(res).toEqual('test = true');
});
it('returns a string for b64 encoded json, without any extra parsing', () => {
const res = parseData('eyJ0ZXN0Ijp0cnVlfQ==');
expect(typeof res).toEqual('string');
expect(res).toEqual('{"test":true}');
});
});
@@ -0,0 +1,40 @@
import { t } from '@vegaprotocol/react-helpers';
interface JSONOracleDataProps {
payload: string;
}
/**
* Renders a JSON oracle. Given the shape could be any valid JSON,
* this doesn't try to do anything smart - it just renders the message
*/
export const JSONOracleData = ({ payload }: JSONOracleDataProps) => {
const decodedSubmission = parseData(payload);
if (!decodedSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
return (
<section>
<code data-testid="json-code">{decodedSubmission}</code>
</section>
);
};
/**
* Safely parses the JSON Oracle payload
*
* @param payload base64 encoded JSON
* @returns Object or null
*/
export function parseData(payload: string) {
try {
if (!payload || typeof payload !== 'string') {
throw new Error('Not a string');
}
return atob(payload);
} catch (e) {
return null;
}
}
@@ -0,0 +1,58 @@
import { render } from '@testing-library/react';
import { OpenOracleData, parseData } from './data-submission-open-oracle';
describe('OpenOracleData', () => {
it('renders a table if there is data', () => {
const p =
'eyJtZXNzYWdlcyI6IFsiMHgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2M2M3ZTMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNGYxZjM4ZWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjcwNzI2OTYzNjU3MzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNDI1NDQzMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJdLCAicHJpY2VzIjogeyJFVEgiOiIxMjMifSwgInNpZ25hdHVyZXMiOiBbIjB4N2U0Nzc2OWEyNzI5NzIwN2Q5ZTIyMmM3YTY2YjBlYzk3Njg4MWY3NmVkYjg5OGFhYzQ5OTRlZWYwOTc4MmI3NGUxMGE5MTJmZWQ1Y2E3NmFlN2U3NzVjOWZiOTBjZGE4ZjhkMDhlNzUyOTE1NzQ2ZTY2OGE1ZWM3OWRmOTZmNmQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDFjIl0sICJ0aW1lc3RhbXAiOiAiMTIzIn0=';
const screen = render(<OpenOracleData payload={p} />);
expect(screen.getByTestId('decoded-payload')).toBeInTheDocument();
expect(screen.getByText('ETH')).toBeInTheDocument();
expect(screen.getByText('123')).toBeInTheDocument();
});
it('renders an error if decoding fails', () => {
const p = 'not-encoded';
const screen = render(<OpenOracleData payload={p} />);
expect(
screen.getByText('Awaiting Block Explorer transaction details')
).toBeInTheDocument();
});
});
describe('Parse Open oracle data', () => {
it('returns null for an invalid b64 string', () => {
expect(parseData('🤷')).toBeNull();
});
it('returns null for a string that looks like an object', () => {
expect(parseData('{}')).toBeNull();
});
it('returns null for null', () => {
expect(parseData(null as unknown as string)).toBeNull();
});
it('returns a string for b64 encoded data', () => {
const res = parseData('dGVzdCA9IHRydWU=');
expect(res).toBeNull();
});
it('returns null if the parsed object does not look like te right shape', () => {
// Encoded: {"test":true}
const res = parseData('eyJ0ZXN0Ijp0cnVlfQ==');
expect(res).toBeNull();
});
it('returns an object if the payload parses and looks fine', () => {
// Encoded: {"messages": [], "prices": {}, "signatures": [], "timestamp": "123"}
const res = parseData(
'eyJtZXNzYWdlcyI6IFtdLCAicHJpY2VzIjoge30sICJzaWduYXR1cmVzIjogW10sICJ0aW1lc3RhbXAiOiAiMTIzIn0='
);
expect(typeof res.prices).toEqual('object');
expect(Array.isArray(res.messages)).toBeTruthy();
expect(Array.isArray(res.signatures)).toBeTruthy();
});
});
@@ -0,0 +1,64 @@
import { t } from '@vegaprotocol/react-helpers';
import { NestedDataList } from '../../../nested-data-list';
import { OpenOraclePrices } from './open-oracle/open-oracle-prices';
interface OpenOracleDataProps {
payload: string;
}
/**
* Decodes Open Oracle data based on it's main parts:
* - messages
* - signatures
* - prices
*/
export const OpenOracleData = ({ payload }: OpenOracleDataProps) => {
const decodedSubmission = parseData(payload);
if (!decodedSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const date = decodedSubmission.timestamp;
const formattedDate = new Date(date * 1000).toLocaleString();
return (
<section>
<span>{formattedDate}</span>
<code>
<OpenOraclePrices
prices={decodedSubmission.prices}
messages={decodedSubmission.messages}
signatures={decodedSubmission.signatures}
/>
</code>
<details data-testid="decoded-payload">
<summary>{t('Decoded payload')}</summary>
<NestedDataList data={decodedSubmission} />
</details>
</section>
);
};
/**
* Safely parses the Open Oracle payload
*
* @param payload base64 encoded JSON
* @returns Object or null
*/
export function parseData(payload: string) {
try {
if (!payload || typeof payload !== 'string') {
throw new Error('Not a string');
}
const res = JSON.parse(atob(payload));
if (!res.prices || !res.messages || !res.signatures) {
throw new Error('Not an open oracle format message');
}
return res;
} catch (e) {
return null;
}
}
@@ -0,0 +1,88 @@
import { render } from '@testing-library/react';
import { OpenOraclePrices } from './open-oracle-prices';
import type { Price } from './open-oracle-prices';
describe('OpenOraclePrices', () => {
it('Will not render with no prices', () => {
const p = undefined as unknown as Price;
const m: string[] = [];
const s: string[] = [];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('Will not render with no messages', () => {
const m = undefined as unknown as string[];
const p: Price = {};
const s: string[] = [];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('will not render with no signatures', () => {
const s = undefined as unknown as string[];
const m: string[] = [];
const p: Price = {};
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('will not render with mismatched prices/signatures', () => {
const p: Price = { ETH: '123' };
const m = ['0x123', '0x456'];
const s: string[] = [];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('will not render with mismatched signatures and messages', () => {
const p: Price = { ETH: '123', BTC: '12' };
const s = ['0xcac', '0xb33f'];
const m = ['0x1204o42c', '0xb3ddd3f', '0x9999'];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('renders a table when there are prices and signatures and messages', () => {
const p: Price = { ETH: '123', BTC: '12' };
const s = ['0xcac', '0xb33f'];
const m = ['0x120442c', '0xb3ddd3f'];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.getByTestId('openoracleprices')).toBeInTheDocument();
// Table headings
expect(screen.getByText('Asset')).toBeInTheDocument();
expect(screen.getByText('Price')).toBeInTheDocument();
expect(screen.getByText('Signature')).toBeInTheDocument();
expect(screen.getByText('Message')).toBeInTheDocument();
// Disabled - see vegaprotocol/frontend-monorepo#/2726
// expect(screen.getByText('Signer')).toBeInTheDocument();
// One row per asset
expect(screen.getByText('ETH')).toBeInTheDocument();
expect(screen.getByText('123')).toBeInTheDocument();
expect(screen.getByText('0xcac')).toBeInTheDocument();
expect(screen.getByText('0xb33f')).toBeInTheDocument();
// Does not test signer element for this row
expect(screen.getByText('BTC')).toBeInTheDocument();
expect(screen.getByText('12')).toBeInTheDocument();
expect(screen.getByText('0x120442c')).toBeInTheDocument();
expect(screen.getByText('0xb3ddd3f')).toBeInTheDocument();
// Does not test signer element for this row
});
});
@@ -0,0 +1,132 @@
import { t } from '@vegaprotocol/react-helpers';
import { CopyWithTooltip } from '@vegaprotocol/ui-toolkit';
import { TableRow } from '../../../../table';
import { TruncateInline } from '../../../../truncate/truncate';
import { utils } from 'ethers';
export type Price = {
[key: string]: string;
};
interface OpenOraclePricesProps {
prices: Price;
signatures: string[];
messages: string[];
}
/**
* Open Oracle price table, showing the entries in this
* decoded message
*
* Notes:
* - Signer is derived by recovering the address from the
* signature and the message. This is currently disabled
* as the implementation is incorrect
*/
export function OpenOraclePrices({
prices,
messages,
signatures,
}: OpenOraclePricesProps) {
if (
!prices ||
!messages ||
!signatures ||
Object.keys(prices).length !== messages.length ||
signatures.length !== messages.length
) {
return null;
}
return (
<table data-testid="openoracleprices">
<thead>
<TableRow modifier="bordered">
<th>{t('Asset')}</th>
<th className="text-right">{t('Price')}</th>
<th>{t('Signature')}</th>
<th>{t('Message')}</th>
{/* <th>{t('Signer')}</th> */}
</TableRow>
</thead>
<tbody>
{Object.keys(prices).map((k: string, i: number) => {
if (k && prices[k]) {
const message = messages[i];
const signature = signatures[i];
return (
<OpenOraclePrice
key={`price-${i}`}
asset={k}
value={prices[k]}
message={message}
signature={signature}
/>
);
} else {
return null;
}
})}
</tbody>
</table>
);
}
type OpenOraclePriceProps = {
asset: string;
value: string;
message: string;
signature: string;
};
export function OpenOraclePrice({
asset,
value,
signature,
message,
}: OpenOraclePriceProps) {
// const addr = getAddressFromMessageAndSignature(message, signature);
return (
<TableRow modifier="bordered" key={`${asset}`}>
<td className="px-4">{asset}</td>
<td className="px-4 text-right">{value}</td>
<td className="px-4">
<CopyWithTooltip text={signature}>
<button>
<TruncateInline text={signature} startChars={5} endChars={5} />
</button>
</CopyWithTooltip>
</td>
<td className="px-4">
<CopyWithTooltip text={message}>
<button>
<TruncateInline text={message} startChars={5} endChars={5} />
</button>
</CopyWithTooltip>
</td>
{/*<td className="px-4">
<CopyWithTooltip text={message}>
<button>
<TruncateInline text={addr} startChars={5} endChars={5} />
</button>
</CopyWithTooltip>
</td> */}
</TableRow>
);
}
export function getAddressFromMessageAndSignature(
message: string,
signature: string
) {
try {
const m = utils.hashMessage(utils.arrayify(utils.keccak256(message)));
const s = utils.splitSignature(
signature.slice(0, 130) + signature.slice(-2)
);
return utils.recoverAddress(m, s);
} catch (e) {
return '-';
}
}
@@ -0,0 +1,55 @@
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 { TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { OpenOracleData } from './oracle-data/data-submission-open-oracle';
import { JSONOracleData } from './oracle-data/data-submission-json-oracle';
export type OracleSubmissionSource =
components['schemas']['OracleDataSubmissionOracleSource'];
interface TxDetailsDataSubmissionProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* Someone cancelled an order
*/
export const TxDetailsDataSubmission = ({
txData,
pubKey,
blockData,
}: TxDetailsDataSubmissionProps) => {
if (!txData || !txData.command.oracleDataSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const type: OracleSubmissionSource =
txData.command.oracleDataSubmission.source;
if (!type) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const payload = txData.command.oracleDataSubmission.payload;
return (
<>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
</TableWithTbody>
{type === 'ORACLE_SOURCE_OPEN_ORACLE' ? (
<OpenOracleData payload={payload} />
) : (
<JSONOracleData payload={payload} />
)}
</>
);
};
@@ -19,6 +19,7 @@ import { TxDetailsUndelegate } from './tx-undelegation';
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
import { TxDetailsLiquidityAmendment } from './tx-liquidity-amend';
import { TxDetailsLiquidityCancellation } from './tx-liquidity-cancel';
import { TxDetailsDataSubmission } from './tx-data-submission';
interface TxDetailsWrapperProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -82,6 +83,8 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
switch (txData.type) {
case 'Submit Order':
return TxDetailsOrder;
case 'Submit Oracle Data':
return TxDetailsDataSubmission;
case 'Cancel Order':
return TxDetailsOrderCancel;
case 'Amend Order':
@@ -27,7 +27,7 @@ export const TxDetailsLiquidityCancellation = ({
pubKey,
blockData,
}: TxDetailsLiquidityCancellationProps) => {
if (!txData || !txData.command.liquidityProvisionAmendment) {
if (!txData || !txData.command.liquidityProvisionCancellation) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
+321 -133
View File
@@ -5,7 +5,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "74970.8484078692916834118",
"locked_amount": "74377.3343235149335839749",
"deposits": [
{
"amount": "86666.297",
@@ -71,7 +71,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "1768.46096611721625",
"locked_amount": "1734.125553266178",
"deposits": [
{
"amount": "2500",
@@ -450,7 +450,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "74902.446941890958771325",
"locked_amount": "74309.47436445978496023",
"deposits": [
{
"amount": "129999.45",
@@ -516,7 +516,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "38202.11449771689492",
"locked_amount": "37773.4128805175029",
"deposits": [
{
"amount": "10000",
@@ -709,7 +709,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "3243.067922374429",
"locked_amount": "3208.826579147641",
"deposits": [
{
"amount": "5000",
@@ -920,7 +920,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "16694.5245943615718995368",
"locked_amount": "16112.873733795746655318",
"deposits": [
{
"amount": "97499.58",
@@ -953,7 +953,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "98230.390980249184455396",
"locked_amount": "22818.532931059293584087640192",
"locked_amount": "22023.516622497674397517810344",
"deposits": [
{
"amount": "135173.4239508",
@@ -999,7 +999,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "7023.098197828817127502",
"locked_amount": "6778.4077253618634870844",
"deposits": [
{
"amount": "32499.86",
@@ -1032,7 +1032,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "2285.9503128615195526146",
"locked_amount": "2206.3059384936688557712",
"deposits": [
{
"amount": "10833.29",
@@ -1065,7 +1065,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "22749.93",
"total_removed": "0",
"locked_amount": "8545.392827257853411082",
"locked_amount": "8247.664367621075063898",
"deposits": [
{
"amount": "6500",
@@ -1203,8 +1203,8 @@
"tranche_start": "2022-11-01T00:00:00.000Z",
"tranche_end": "2023-05-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "3338.59298265",
"locked_amount": "12150.492058011051",
"total_removed": "3539.640404325",
"locked_amount": "11839.76605662983325",
"deposits": [
{
"amount": "7500",
@@ -1228,6 +1228,11 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x63c8fb032e621103aa45aa0cba24fdedc19f9640271b6d59058b576a00862340"
},
{
"amount": "201.047421675",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xbca53bb0a17ad84f130058da0d3773b8a649ec4270702b3541d75fcbeba5899a"
},
{
"amount": "286.3317219",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -1288,6 +1293,12 @@
"tranche_id": 33,
"tx": "0x63c8fb032e621103aa45aa0cba24fdedc19f9640271b6d59058b576a00862340"
},
{
"amount": "201.047421675",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 33,
"tx": "0xbca53bb0a17ad84f130058da0d3773b8a649ec4270702b3541d75fcbeba5899a"
},
{
"amount": "286.3317219",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -1332,8 +1343,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "3338.59298265",
"remaining_tokens": "4161.40701735"
"withdrawn_tokens": "3539.640404325",
"remaining_tokens": "3960.359595675"
},
{
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
@@ -1358,7 +1369,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "928642.9598472029154",
"locked_amount": "689572.566861289866199601",
"locked_amount": "676287.4161622965221101968",
"deposits": [
{
"amount": "1852091.69",
@@ -1710,7 +1721,7 @@
"tranche_end": "2023-02-01T00:00:00.000Z",
"total_added": "42500",
"total_removed": "24434.0787288",
"locked_amount": "2019.664288949276625",
"locked_amount": "1442.306857638887255",
"deposits": [
{
"amount": "12500",
@@ -6700,8 +6711,8 @@
"tranche_id": 11,
"tranche_start": "2021-09-03T00:00:00.000Z",
"tranche_end": "2022-09-03T00:00:00.000Z",
"total_added": "53637.000000000000000003",
"total_removed": "42547.21518131551",
"total_added": "53657.000000000000000003",
"total_removed": "42606.21518131551",
"locked_amount": "0",
"deposits": [
{
@@ -6809,6 +6820,11 @@
"user": "0xB12Cdb63E38f9a6ff2C0F68aBC3CA9beFCb7148c",
"tx": "0x304ceeda11c67174ffc52347ec240ea5aa832675cbb1c828adc098c8886f96fc"
},
{
"amount": "20",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tx": "0xe3fd6d538990df7bf818b8a178ecf7135a5e34f4d07778e9d6dc637cf470e2e0"
},
{
"amount": "20",
"user": "0xB12Cdb63E38f9a6ff2C0F68aBC3CA9beFCb7148c",
@@ -16636,6 +16652,16 @@
"user": "0xB12Cdb63E38f9a6ff2C0F68aBC3CA9beFCb7148c",
"tx": "0x1a790207d0bd9f42d33e5ecd3a1b946c1d1ec870bf2b339e7d7d7f48ab859839"
},
{
"amount": "39",
"user": "0xd84D7BDc4b79171917C81ddBB22A01139EB4cd84",
"tx": "0x1279996e85e8201f1d39b29cb11612b9c7cb5b6374cd020d7198030251f439ca"
},
{
"amount": "20",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tx": "0xebb86840b89a0f2e4753dd646d342faff415ce5e2fcce0eef9918dbeb8e39c2e"
},
{
"amount": "880",
"user": "0xB12Cdb63E38f9a6ff2C0F68aBC3CA9beFCb7148c",
@@ -18471,6 +18497,100 @@
"withdrawn_tokens": "0",
"remaining_tokens": "20"
},
{
"address": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"deposits": [
{
"amount": "20",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0xe3fd6d538990df7bf818b8a178ecf7135a5e34f4d07778e9d6dc637cf470e2e0"
},
{
"amount": "15",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x07b9757e77de7a2de6e3fea89bc48801656fdcd51d9c81b92324b35aa800e0c8"
},
{
"amount": "20",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x561607bb6a1c0c4c45fc7ac4bac7d8287015b80e624c32633ed5e06b4d0f1c3a"
},
{
"amount": "12",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x0a36e69fa542ca075481e08073f159ea7bea4f2c73ef9b52d22d4614db19a5a0"
},
{
"amount": "2",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x8ab01cde20c499bea4d28c208749cfc8139ac1bbae7b65f3fb77a903e2af1ace"
},
{
"amount": "5",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x600702eb08091f59365fdf6af580b0e60c81600ae57a8fc3ad7473d67cc738dd"
},
{
"amount": "10",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0xfd7edebf2b9d7359188e31fde4d44674bfa8fc119d7f9c71e673b80dc3913899"
},
{
"amount": "10",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0xd055543cf69ebdb0d8862fd917723705853caa63e97e6dfa942419e707bff520"
},
{
"amount": "20",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x15da44cda1d0e1d31e82e3cf7f39080b5f61eca6fa870af430c48ceb73ae3dc3"
},
{
"amount": "10",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x99b34aa5c033c78b690dbe9262a3e621bc7643f60cafffeeafcc6a8885fabd74"
}
],
"withdrawals": [
{
"amount": "20",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0xebb86840b89a0f2e4753dd646d342faff415ce5e2fcce0eef9918dbeb8e39c2e"
},
{
"amount": "3.86333155832",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x068081c78445c62a22f68a81cbfd73d1c906b35c31882e5dcbd95b158485b227"
},
{
"amount": "41.64068004762",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x2794e03baf2be9d03ff553075bdbe9296e7975ee72647f08a42299cc05a4e3ab"
},
{
"amount": "58.49598839406",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0xfeb269c626db43a4d16a117e47a21a148c9f911d708d97d9dfd0caf08e1c09e7"
}
],
"total_tokens": "124",
"withdrawn_tokens": "124",
"remaining_tokens": "0"
},
{
"address": "0xc7eC0d77a1417C97fb455f404c4B425A69a0004d",
"deposits": [
@@ -21254,10 +21374,17 @@
"tx": "0x69d4f66ec03d75e285036c8f58c037bb1632aec6af4a2a4e8b7e9cbdf02cfffa"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "39",
"user": "0xd84D7BDc4b79171917C81ddBB22A01139EB4cd84",
"tranche_id": 11,
"tx": "0x1279996e85e8201f1d39b29cb11612b9c7cb5b6374cd020d7198030251f439ca"
}
],
"total_tokens": "39",
"withdrawn_tokens": "0",
"remaining_tokens": "39"
"withdrawn_tokens": "39",
"remaining_tokens": "0"
},
{
"address": "0x58e87362C786fa1A19A7bD9Ee0b8b46926a26856",
@@ -28027,88 +28154,6 @@
"withdrawn_tokens": "0",
"remaining_tokens": "27"
},
{
"address": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"deposits": [
{
"amount": "15",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x07b9757e77de7a2de6e3fea89bc48801656fdcd51d9c81b92324b35aa800e0c8"
},
{
"amount": "20",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x561607bb6a1c0c4c45fc7ac4bac7d8287015b80e624c32633ed5e06b4d0f1c3a"
},
{
"amount": "12",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x0a36e69fa542ca075481e08073f159ea7bea4f2c73ef9b52d22d4614db19a5a0"
},
{
"amount": "2",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x8ab01cde20c499bea4d28c208749cfc8139ac1bbae7b65f3fb77a903e2af1ace"
},
{
"amount": "5",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x600702eb08091f59365fdf6af580b0e60c81600ae57a8fc3ad7473d67cc738dd"
},
{
"amount": "10",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0xfd7edebf2b9d7359188e31fde4d44674bfa8fc119d7f9c71e673b80dc3913899"
},
{
"amount": "10",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0xd055543cf69ebdb0d8862fd917723705853caa63e97e6dfa942419e707bff520"
},
{
"amount": "20",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x15da44cda1d0e1d31e82e3cf7f39080b5f61eca6fa870af430c48ceb73ae3dc3"
},
{
"amount": "10",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x99b34aa5c033c78b690dbe9262a3e621bc7643f60cafffeeafcc6a8885fabd74"
}
],
"withdrawals": [
{
"amount": "3.86333155832",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x068081c78445c62a22f68a81cbfd73d1c906b35c31882e5dcbd95b158485b227"
},
{
"amount": "41.64068004762",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0x2794e03baf2be9d03ff553075bdbe9296e7975ee72647f08a42299cc05a4e3ab"
},
{
"amount": "58.49598839406",
"user": "0xC6D7208DaDEe4F431bd0f3f11E7d4c91fF51bfb2",
"tranche_id": 11,
"tx": "0xfeb269c626db43a4d16a117e47a21a148c9f911d708d97d9dfd0caf08e1c09e7"
}
],
"total_tokens": "104",
"withdrawn_tokens": "104",
"remaining_tokens": "0"
},
{
"address": "0xd996efF75E43357F04E3f37D61EF6A6851A0b8f1",
"deposits": [
@@ -33051,8 +33096,8 @@
"tranche_start": "2022-03-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "440207.6636629258374",
"locked_amount": "1084134.27632684810233644228",
"total_removed": "441714.6865497406814",
"locked_amount": "1063719.625100700820753200245",
"deposits": [
{
"amount": "1998.95815",
@@ -33226,6 +33271,11 @@
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tx": "0x9ebd87280b813a7a6303eae36955702359987101aed56818307b8d0e24ef0686"
},
{
"amount": "1507.022886814844",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tx": "0x63016a4e870bc38285eac8057aab58aa9fe896f1659f4f2e7e4e8de19c1202db"
},
{
"amount": "1906.7742941475005",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
@@ -33566,6 +33616,12 @@
"tranche_id": 1,
"tx": "0x9ebd87280b813a7a6303eae36955702359987101aed56818307b8d0e24ef0686"
},
{
"amount": "1507.022886814844",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tranche_id": 1,
"tx": "0x63016a4e870bc38285eac8057aab58aa9fe896f1659f4f2e7e4e8de19c1202db"
},
{
"amount": "1906.7742941475005",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
@@ -33814,8 +33870,8 @@
}
],
"total_tokens": "187637.95",
"withdrawn_tokens": "131749.961310162617",
"remaining_tokens": "55887.988689837383"
"withdrawn_tokens": "133256.984196977461",
"remaining_tokens": "54380.965803022539"
},
{
"address": "0x1A71e3ED1996CAbB91bB043f880CE963D601707e",
@@ -34321,8 +34377,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "542155.72695065342848952",
"locked_amount": "9143958.1214222535716086074253685564476885",
"total_removed": "545472.50137003958740452",
"locked_amount": "9071569.0789202252235565659217193396309814",
"deposits": [
{
"amount": "16249.93",
@@ -34901,6 +34957,31 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x4cb3f59b0a28a2d377f09bd924b70ccd5fe7d2f1a249cf54813eb41a83497386"
},
{
"amount": "82.7990407600915",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tx": "0x55802ccf2bf20a0bf6ffffa5add678cacd8d31450517a2ac65ff384ee5f71f22"
},
{
"amount": "548.4516268498245",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x2221aafeab49a64ec0b85aa53438b6c66a06eff11fee7ab0a254f33657e47aab"
},
{
"amount": "1084.093674",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
"tx": "0x108f2ebc78fb2f656e95e842a65a0c522e7ad6b6b06b158dbd549831e63814c2"
},
{
"amount": "939.30801110682379",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
"tx": "0x7deb028bfbd706c2281d6344cca117acc153a35ad68dd9a58f3ad7867f643d37"
},
{
"amount": "662.122066669419125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x6d913b124af23621f8b97087a51d4cd912c6a8d9cca325cb756b56d4970f3f62"
},
{
"amount": "434.254023685890375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -36423,6 +36504,18 @@
"tranche_id": 2,
"tx": "0x4cb3f59b0a28a2d377f09bd924b70ccd5fe7d2f1a249cf54813eb41a83497386"
},
{
"amount": "548.4516268498245",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x2221aafeab49a64ec0b85aa53438b6c66a06eff11fee7ab0a254f33657e47aab"
},
{
"amount": "662.122066669419125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x6d913b124af23621f8b97087a51d4cd912c6a8d9cca325cb756b56d4970f3f62"
},
{
"amount": "434.254023685890375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -37439,8 +37532,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "110011.628404552763625",
"remaining_tokens": "149987.259095447236375"
"withdrawn_tokens": "111222.20209807200725",
"remaining_tokens": "148776.68540192799275"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -37673,6 +37766,12 @@
"tranche_id": 2,
"tx": "0xf149b821cee5aea7ae7eb60d37a32e27362f624ffa5562b4bc2ccc93173f205e"
},
{
"amount": "939.30801110682379",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
"tranche_id": 2,
"tx": "0x7deb028bfbd706c2281d6344cca117acc153a35ad68dd9a58f3ad7867f643d37"
},
{
"amount": "1253.72073256229405",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
@@ -37855,8 +37954,8 @@
}
],
"total_tokens": "150551.801",
"withdrawn_tokens": "63277.35071526854989",
"remaining_tokens": "87274.45028473145011"
"withdrawn_tokens": "64216.65872637537368",
"remaining_tokens": "86335.14227362462632"
},
{
"address": "0x4d982Ab0823fD2f48e934a7be2bb0a5374a26148",
@@ -38076,6 +38175,12 @@
"tranche_id": 2,
"tx": "0xb4122843632a406c44f6edbf144385588d448715cc6b4143e1288893d4e84f64"
},
{
"amount": "1084.093674",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
"tranche_id": 2,
"tx": "0x108f2ebc78fb2f656e95e842a65a0c522e7ad6b6b06b158dbd549831e63814c2"
},
{
"amount": "1573.9558",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
@@ -38228,8 +38333,8 @@
}
],
"total_tokens": "200000",
"withdrawn_tokens": "84061.938192",
"remaining_tokens": "115938.061808"
"withdrawn_tokens": "85146.031866",
"remaining_tokens": "114853.968134"
},
{
"address": "0x1b956E6c00E238194B331eddEFF72Cb5f28A8d01",
@@ -38695,6 +38800,12 @@
"tranche_id": 2,
"tx": "0x695fb4ae4af737855a85c7c21d2e454f30c35011f3d1391651f9010d7ad8f61f"
},
{
"amount": "82.7990407600915",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tranche_id": 2,
"tx": "0x55802ccf2bf20a0bf6ffffa5add678cacd8d31450517a2ac65ff384ee5f71f22"
},
{
"amount": "104.762232778042",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
@@ -38925,8 +39036,8 @@
}
],
"total_tokens": "12362.05",
"withdrawn_tokens": "5163.251673168822",
"remaining_tokens": "7198.798326831178"
"withdrawn_tokens": "5246.0507139289135",
"remaining_tokens": "7115.9992860710865"
},
{
"address": "0xb091D456d0dFCB94dcba6f355379056C5bb995fC",
@@ -39557,8 +39668,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "3695906.28705291681680046",
"locked_amount": "2720198.526682862795802990368517396",
"total_removed": "3706973.15022981060693393",
"locked_amount": "2653369.424853388202023095424913904",
"deposits": [
{
"amount": "129284.449",
@@ -39837,6 +39948,26 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x857dff30402919622c2e93e0b21f5b0722fb686b1db12957622df40f37fc89fe"
},
{
"amount": "760.23237641543178025",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x074c8b4e60fda9e8dba220410f5a5805076e763589315992ca70db527a0bc674"
},
{
"amount": "9201.72686220155355072",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tx": "0x77247f5a8850ad76c194b1e7481ddf61b1106a3f9df35117b7e146bd70c66616"
},
{
"amount": "645.31140451891061",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xe96d96d780f7aec7b2e42a190a91993ac93bb61d74b12ff72e87c0ee4a2e2712"
},
{
"amount": "459.5925337578941925",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xc1e8ef457dbeb52bd2d02aefd418a9a67bd17f016c1ca6a1e80716d86ce3c132"
},
{
"amount": "12758.73908063512839093",
"user": "0x91715128a71c9C734CDC20E5EdEEeA02E72e428E",
@@ -42597,6 +42728,24 @@
"tranche_id": 3,
"tx": "0x857dff30402919622c2e93e0b21f5b0722fb686b1db12957622df40f37fc89fe"
},
{
"amount": "760.23237641543178025",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x074c8b4e60fda9e8dba220410f5a5805076e763589315992ca70db527a0bc674"
},
{
"amount": "645.31140451891061",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xe96d96d780f7aec7b2e42a190a91993ac93bb61d74b12ff72e87c0ee4a2e2712"
},
{
"amount": "459.5925337578941925",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xc1e8ef457dbeb52bd2d02aefd418a9a67bd17f016c1ca6a1e80716d86ce3c132"
},
{
"amount": "762.7902369187144135",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -44909,8 +45058,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "291949.96143389721358025",
"remaining_tokens": "67173.50814110278641975"
"withdrawn_tokens": "293815.097748589450163",
"remaining_tokens": "65308.371826410549837"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@@ -45145,6 +45294,12 @@
"tranche_id": 3,
"tx": "0xf63582606fc184dd521fb7a6e0b2678905332d88618287ce5aff11251ea57f74"
},
{
"amount": "9201.72686220155355072",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tranche_id": 3,
"tx": "0x77247f5a8850ad76c194b1e7481ddf61b1106a3f9df35117b7e146bd70c66616"
},
{
"amount": "69746.42241726556416138",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
@@ -45447,8 +45602,8 @@
}
],
"total_tokens": "1266324.603486",
"withdrawn_tokens": "1023647.47222319572005858",
"remaining_tokens": "242677.13126280427994142"
"withdrawn_tokens": "1032849.1990853972736093",
"remaining_tokens": "233475.4044006027263907"
},
{
"address": "0xC5d9221EB9c28A69859264c0A2Fe0d3272228296",
@@ -46214,8 +46369,8 @@
"tranche_start": "2021-10-05T00:00:00.000Z",
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "2618613.258302882195581789",
"locked_amount": "757863.485975937255527811092932209",
"total_removed": "2622261.560853924298939789",
"locked_amount": "731458.903664069114159753480553424",
"deposits": [
{
"amount": "552496.6455",
@@ -46364,6 +46519,11 @@
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
"tx": "0x0475da9ad860bc2d5fa88136475a133d6a85c309b11e6c74f2c42a6ab3766351"
},
{
"amount": "3648.302551042103358",
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
"tx": "0xd359b3f549f00ffbf9f2e4a50a20a4a83856c954efba00f734e6eb6237066cc1"
},
{
"amount": "9162.0421945609117395",
"user": "0x91715128a71c9C734CDC20E5EdEEeA02E72e428E",
@@ -47784,6 +47944,12 @@
"tranche_id": 4,
"tx": "0x0475da9ad860bc2d5fa88136475a133d6a85c309b11e6c74f2c42a6ab3766351"
},
{
"amount": "3648.302551042103358",
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
"tranche_id": 4,
"tx": "0xd359b3f549f00ffbf9f2e4a50a20a4a83856c954efba00f734e6eb6237066cc1"
},
{
"amount": "8474.492376535891923",
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
@@ -48002,8 +48168,8 @@
}
],
"total_tokens": "331498.5873",
"withdrawn_tokens": "284629.036724988638379",
"remaining_tokens": "46869.550575011361621"
"withdrawn_tokens": "288277.339276030741737",
"remaining_tokens": "43221.248023969258263"
},
{
"address": "0x16da609341ed67750A8BCC5AAa2005471006Cd77",
@@ -48098,8 +48264,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "29541.7924886366685",
"locked_amount": "171787.27325971447761506494977168",
"total_removed": "29683.1054262326685",
"locked_amount": "168552.45507780998384909840791476",
"deposits": [
{
"amount": "3000",
@@ -54763,6 +54929,16 @@
"user": "0xdc040917f80aC350460e55467e4B6c52Fc5e220E",
"tx": "0xb4ceffa66b5fe75c9d5d46f03c33c15693545cd69abd0e45e27c5322367eef56"
},
{
"amount": "32.761796044",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
"tx": "0xb78be3e23b9a4da892da17ef91159617e27a587be2aa78c85f96f01b57d74435"
},
{
"amount": "108.551141552",
"user": "0xf751033D4e6864a88Be93E36258246F26AEf577c",
"tx": "0xb912d9e24a80ec0d9ed4f3b03c20937d4fec08dd99031447ba84a8b6a69ef351"
},
{
"amount": "229.648541348",
"user": "0x65C13724928ea0AfA68e09c7b70449bB8f6f3Fc8",
@@ -67960,6 +68136,12 @@
}
],
"withdrawals": [
{
"amount": "32.761796044",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
"tranche_id": 5,
"tx": "0xb78be3e23b9a4da892da17ef91159617e27a587be2aa78c85f96f01b57d74435"
},
{
"amount": "20.707914768",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
@@ -68028,8 +68210,8 @@
}
],
"total_tokens": "400",
"withdrawn_tokens": "222.073503296",
"remaining_tokens": "177.926496704"
"withdrawn_tokens": "254.83529934",
"remaining_tokens": "145.16470066"
},
{
"address": "0x5c90765F50629570738fEe7b7FA82ae118f81Ed1",
@@ -72836,6 +73018,12 @@
}
],
"withdrawals": [
{
"amount": "108.551141552",
"user": "0xf751033D4e6864a88Be93E36258246F26AEf577c",
"tranche_id": 5,
"tx": "0xb912d9e24a80ec0d9ed4f3b03c20937d4fec08dd99031447ba84a8b6a69ef351"
},
{
"amount": "147.346867072",
"user": "0xf751033D4e6864a88Be93E36258246F26AEf577c",
@@ -72844,8 +73032,8 @@
}
],
"total_tokens": "400",
"withdrawn_tokens": "147.346867072",
"remaining_tokens": "252.653132928"
"withdrawn_tokens": "255.898008624",
"remaining_tokens": "144.101991376"
},
{
"address": "0x667ee9816155807e4120dba069CC42aED064399b",
@@ -829,7 +829,7 @@ context(
cy.highlight(`Associating tokens for first time`);
cy.ethereum_wallet_connect();
cy.connectVegaWallet();
cy.get('[href="/validators/associate"]').first().click();
cy.get('[href="/token/associate"]').first().click();
cy.getByTestId('associate-radio-wallet', { timeout: 30000 }).click();
cy.getByTestId('token-amount-input', epochTimeout).type('1');
cy.getByTestId('token-input-submit-button', txTimeout)
@@ -61,7 +61,7 @@ context(
// 3002-PROP-007
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalParameterSelect).find('option').should('have.length', 115);
cy.get(proposalParameterSelect).find('option').should('have.length', 116);
cy.get(proposalParameterSelect).select(
// 3007-PNEC-002
'governance_proposal_asset_minEnact'
@@ -93,6 +93,7 @@ context(
.contains(2.0, epochTimeout)
.should('be.visible');
cy.close_staking_dialog();
cy.navigate_to('validators');
// 2002-SINC-007
@@ -145,6 +146,7 @@ context(
.contains(2.0, epochTimeout)
.should('be.visible');
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share(
@@ -209,6 +211,7 @@ context(
.contains(6.0, epochTimeout)
.should('be.visible');
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share(
@@ -243,6 +246,7 @@ context(
.parent()
.should('contain', 2.0, txTimeout);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.click_on_validator_from_list(1);
@@ -264,6 +268,7 @@ context(
.eq(1)
.should('contain', 1.0, txTimeout);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.get(`[row-id="${0}"]`).within(() => {
@@ -323,6 +328,7 @@ context(
txTimeout
);
cy.close_staking_dialog();
cy.navigate_to('validators');
// 2001-STKE-040
cy.click_on_validator_from_list(0);
@@ -388,6 +394,7 @@ context(
txTimeout
);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.click_on_validator_from_list('0');
@@ -447,6 +454,7 @@ context(
txTimeout
);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.click_on_validator_from_list(0);
@@ -488,6 +496,7 @@ context(
txTimeout
);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.click_on_validator_from_list(0);
@@ -7,11 +7,11 @@ const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
const txTimeout = Cypress.env('txTimeout');
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
const ethWalletAssociateButton = '[href="/validators/associate"]';
const ethWalletAssociateButton = '[href="/token/associate"]';
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
const ethWalletDissociateButton = '[href="/validators/disassociate"]';
const ethWalletDissociateButton = '[href="/token/disassociate"]';
const vestingContractSection = '[data-testid="vega-in-vesting-contract"]';
const vegaInWalletSection = '[data-testid="vega-in-wallet"]';
const connectedVegaKey = '[data-testid="connected-vega-key"]';
@@ -170,6 +170,27 @@ context(
});
});
it('Unable to withdraw asset on pub key view', function () {
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
// Disconnect vega wallet
cy.getByTestId('manage-vega-wallet').click();
cy.getByTestId('disconnect').click();
cy.connectPublicKey(vegaWalletPubKey);
cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(selectAsset).select(usdtName);
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(amountInput).click().type('100');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId('dialog-content').within(() => {
cy.get('h1').should('have.text', 'Transaction failed');
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
});
});
function waitForAssetsDisplayed(expectedAsset) {
cy.contains(expectedAsset, txTimeout).should('be.visible');
}
@@ -131,7 +131,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.get(associateVegaLink)
.should('be.visible')
.and('have.attr', 'href')
.and('equal', '/validators/associate');
.and('equal', '/token/associate');
});
it('should have STAKING button', function () {
cy.get(stakingBtn)
@@ -0,0 +1,62 @@
/// <reference types="cypress" />
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey2');
const vegaPubkeyTruncated = Cypress.env('vegaWalletPublicKey2Short');
const banner = 'view-banner';
context('View functionality with public key', { tags: '@smoke' }, function () {
before('send asset to wallet', function () {
cy.vega_wallet_faucet_assets_without_check(
'fUSDC',
'1000000',
vegaWalletPubKey
);
});
beforeEach('visit home page', function () {
cy.visit('/');
cy.wait_for_spinner();
cy.connectPublicKey(vegaWalletPubKey);
});
it('Able to connect public key via wallet', function () {
verifyConnectedToPubKey();
cy.getByTestId('currency-title').should('contain.text', 'USDC (fake)');
});
it('Able to connect public key using url', function () {
cy.getByTestId('exit-view').click();
cy.visit(`/?address=${vegaWalletPubKey}`);
verifyConnectedToPubKey();
});
it('Unable to submit proposal with public key', function () {
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
cy.navigate_to('proposals');
cy.go_to_make_new_proposal('Freeform');
cy.enter_unique_freeform_proposal_body('50', 'pub key proposal test');
cy.getByTestId('proposal-submit').should('be.visible').click();
cy.getByTestId('dialog-content').within(() => {
cy.get('h1').should('have.text', 'Transaction failed');
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
});
});
it('Able to disconnect via banner', function () {
cy.getByTestId('exit-view').click();
cy.getByTestId(banner).should('not.exist');
});
it('Able to disconnect via wallet', function () {
cy.getByTestId('manage-vega-wallet').click();
cy.getByTestId('disconnect').click();
cy.getByTestId(banner).should('not.exist');
});
function verifyConnectedToPubKey() {
cy.getByTestId(banner).should(
'contain.text',
`Viewing as Vega user: ${vegaPubkeyTruncated}`
);
}
});
@@ -2,8 +2,8 @@ const walletContainer = '[data-testid="ethereum-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1';
const connectToEthButton = '[data-testid="connect-to-eth-wallet-button"]';
const connectorList = '[data-testid="web3-connector-list"]';
const associate = '[href="/validators/associate"]';
const disassociate = '[href="/validators/disassociate"]';
const associate = '[href="/token/associate"]';
const disassociate = '[href="/token/disassociate"]';
const disconnect = '[data-testid="disconnect-from-eth-wallet-button"]';
const accountNo = '[data-testid="ethereum-account-truncated"]';
const currencyTitle = '[data-testid="currency-title"]';
@@ -3,8 +3,8 @@ const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
const tokenInputApprove = '[data-testid="token-input-approve-button"]';
const addStakeRadioButton = '[data-testid="add-stake-radio"]';
const removeStakeRadioButton = '[data-testid="remove-stake-radio"]';
const ethWalletAssociateButton = '[href="/validators/associate"]';
const ethWalletDissociateButton = '[href="/validators/disassociate"]';
const ethWalletAssociateButton = '[href="/token/associate"]';
const ethWalletDissociateButton = '[href="/token/disassociate"]';
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
@@ -40,7 +40,6 @@ Cypress.Commands.add('staking_validator_page_add_stake', (stake) => {
.and('contain', `Add ${stake} $VEGA tokens`)
.and('be.visible')
.click();
cy.get(dialogCloseButton).click();
});
Cypress.Commands.add('staking_validator_page_remove_stake', (stake) => {
@@ -164,12 +164,12 @@ const ConnectedKey = () => {
)}
</section>
<WalletCardActions>
<Link className="flex-1" to={`${Routes.VALIDATORS}/associate`}>
<Link className="flex-1" to={Routes.ASSOCIATE}>
<Button size="sm" fill={true}>
{t('associate')}
</Button>
</Link>
<Link className="flex-1" to={`${Routes.VALIDATORS}/disassociate`}>
<Link className="flex-1" to={Routes.DISASSOCIATE}>
<Button size="sm" fill={true}>
{t('disassociate')}
</Button>
@@ -21,7 +21,7 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
) : (
<div />
)}
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[min-content_1fr] lg:grid-cols-[1fr_450px]">
<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">
{sidebar.map((Component, i) => (
@@ -12,7 +12,7 @@ export const SplashLoader = ({ text = 'Loading' }: { text?: string }) => {
return (
<div className="flex flex-col items-center" data-testid="splash-loader">
<div className="flex flex-wrap w-[50px] h-[50px] mb-20">
<div className="flex flex-wrap w-[50px] h-[50px] mb-4">
{new Array(25).fill(null).map((_, i) => {
return (
<div
@@ -1,5 +1,5 @@
import BigNumber from 'bignumber.js';
import create from 'zustand';
import { create } from 'zustand';
import type { UserTrancheBalance } from '../../contexts/app-state/app-state-context';
export interface AssociationBreakdown {
+4 -3
View File
@@ -3,16 +3,17 @@ import type { Web3ReactHooks } from '@web3-react/core';
import { initializeConnector } from '@web3-react/core';
import { MetaMask } from '@web3-react/metamask';
import { WalletConnect } from '@web3-react/walletconnect';
import { Url } from './url-connector';
import type { Connector } from '@web3-react/types';
import { ENV } from '../config/env';
import { UrlConnector } from '@vegaprotocol/web3';
const [metamask, metamaskHooks] = initializeConnector<MetaMask>(
(actions) => new MetaMask(actions)
);
const [urlConnector, urlHooks] = initializeConnector<Url>(
(actions) => new Url(actions, ENV.localProviderUrl)
const [urlConnector, urlHooks] = initializeConnector<UrlConnector>(
(actions) =>
new UrlConnector(actions, ENV.localProviderUrl, ENV.ethWalletMnemonic)
);
export const createDefaultProvider = (providerUrl: string, chainId: number) => {
@@ -20,6 +20,24 @@ query Proposal($proposalId: ID!) {
... on NewMarket {
decimalPlaces
metadata
lpPriceRange
riskParameters {
... on LogNormalRiskModel {
riskAversionParameter
tau
params {
mu
r
sigma
}
}
... on SimpleRiskModel {
params {
factorLong
factorShort
}
}
}
instrument {
name
code
@@ -32,6 +50,7 @@ query Proposal($proposalId: ID!) {
quantum
}
quoteName
dataSourceSpecForSettlementData {
sourceType {
... on DataSourceDefinitionInternal {
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -264,8 +264,6 @@ const routerConfig = [
path: Routes.VALIDATORS,
element: <LazyStaking name="Staking" />,
children: [
{ path: 'associate', element: <LazyStakingAssociate /> },
{ path: 'disassociate', element: <LazyStakingDisassociate /> },
{ path: ':node', element: <LazyStakingNode /> },
{
index: true,
@@ -311,6 +309,8 @@ const routerConfig = [
},
],
},
{ path: 'associate', element: <LazyStakingAssociate /> },
{ path: 'disassociate', element: <LazyStakingDisassociate /> },
],
},
{
+2 -2
View File
@@ -12,8 +12,8 @@ const Routes = {
REDEEM: '/token/redeem',
WITHDRAWALS: '/token/withdraw',
SUPPLY: '/token/tranches',
ASSOCIATE: '/validators/associate',
DISASSOCIATE: '/validators/disassociate',
ASSOCIATE: '/token/associate',
DISASSOCIATE: '/token/disassociate',
};
export default Routes;
@@ -18,6 +18,7 @@ import type {
NodesFragmentFragment,
} from '../__generated___/Nodes';
import type { PreviousEpochQuery } from '../../__generated___/PreviousEpoch';
import BigNumber from 'bignumber.js';
export interface ValidatorsTableProps {
data: NodesQuery | undefined;
@@ -77,8 +78,8 @@ export const ValidatorTables = ({
const lowestRankingConsensusScore = consensusValidators.reduce(
(lowest: NodesFragmentFragment, validator: NodesFragmentFragment) => {
if (
validator?.rankingScore &&
Number(validator.rankingScore) < Number(lowest.rankingScore)
Number(validator.rankingScore.rankingScore) <
Number(lowest.rankingScore.rankingScore)
) {
lowest = validator;
}
@@ -87,10 +88,12 @@ export const ValidatorTables = ({
).rankingScore.rankingScore;
const lowestRankingBigNum = toBigNum(lowestRankingConsensusScore, 0);
const totalStakeBigNum = toBigNum(totalStake, decimals);
const consensusStakedTotal = consensusValidators.reduce((acc, cur) => {
return acc.plus(toBigNum(cur.stakedTotal, decimals));
}, new BigNumber(0));
stakeNeededForPromotion = formatNumber(
lowestRankingBigNum.times(totalStakeBigNum),
lowestRankingBigNum.times(consensusStakedTotal),
2
).toString();
}
@@ -5,12 +5,14 @@ interface StakeFailureProps {
nodeName: string;
isDialogVisible: boolean;
toggleDialog: () => void;
error: Error | null;
}
export const StakeFailure = ({
nodeName,
isDialogVisible,
toggleDialog,
error,
}: StakeFailureProps) => {
const { t } = useTranslation();
return (
@@ -20,11 +22,15 @@ export const StakeFailure = ({
open={isDialogVisible}
onChange={toggleDialog}
>
<p>
{t('stakeFailed', {
node: nodeName,
})}
</p>
{error ? (
<p>{error.message}</p>
) : (
<p>
{t('stakeFailed', {
node: nodeName,
})}
</p>
)}
</Dialog>
);
};
@@ -13,6 +13,7 @@ interface StakeFormTxStatusesProps {
removeType: RemoveType;
isDialogVisible: boolean;
toggleDialog: () => void;
error: Error | null;
}
export const StakingFormTxStatuses = ({
@@ -23,6 +24,7 @@ export const StakingFormTxStatuses = ({
removeType,
isDialogVisible,
toggleDialog,
error,
}: StakeFormTxStatusesProps) => {
switch (formState) {
case FormState.Requested:
@@ -59,6 +61,7 @@ export const StakingFormTxStatuses = ({
nodeName={nodeName}
isDialogVisible={isDialogVisible}
toggleDialog={toggleDialog}
error={error}
/>
);
default:
@@ -70,6 +70,7 @@ export const StakingForm = ({
const { appState } = useAppState();
const { sendTx } = useVegaWallet();
const [formState, setFormState] = React.useState(FormState.Default);
const [error, setError] = useState<Error | null>(null);
const [isDialogVisible, setIsDialogVisible] = useState(false);
const { t } = useTranslation();
const [action, setAction] = React.useState<StakeAction>(
@@ -135,12 +136,15 @@ export const StakingForm = ({
// await success via poll
} catch (err) {
if (err instanceof Error) {
setError(err);
}
setFormState(FormState.Failure);
Sentry.captureException(err);
}
}
const [delegationSearch, { data, error }] = usePartyDelegationsLazyQuery({
const [delegationSearch, { data }] = usePartyDelegationsLazyQuery({
variables: {
partyId: pubKey,
},
@@ -170,10 +174,6 @@ export const StakingForm = ({
clearInterval(interval);
}
}
if (error) {
Sentry.captureException(error);
}
}, 1000);
}
@@ -306,6 +306,7 @@ export const StakingForm = ({
removeType={removeType}
isDialogVisible={isDialogVisible}
toggleDialog={toggleDialog}
error={error}
/>
</>
);
+1 -1
View File
@@ -93,7 +93,7 @@ const Home = ({ name }: RouteChildProps) => {
<p>
<Link
data-testid="associate-vega-tokens-link-on-homepage"
to={`${Routes.VALIDATORS}/associate`}
to={Routes.ASSOCIATE}
className="underline text-white"
>
{t('Associate VEGA tokens')}
+1 -1
View File
@@ -1,6 +1,6 @@
import type ethers from 'ethers';
import type { GetState, SetState } from 'zustand';
import create from 'zustand';
import { create } from 'zustand';
export interface TxData {
tx: ethers.ContractTransaction;
+2
View File
@@ -9,6 +9,8 @@ NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_WALLET_URL=http://localhost:1789
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
# Expose some env vars to cypress environment for market setup
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
+2
View File
@@ -9,6 +9,8 @@ NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_WALLET_URL=http://localhost:1789
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
# Expose some env vars to cypress environment for market setup
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
+1
View File
@@ -42,5 +42,6 @@ module.exports = defineConfig({
grepTags: '@regression @smoke @slow',
grepFilterSpecs: true,
grepOmitFiltered: true,
txTimeout: { timeout: 70000 },
},
});
+155 -19
View File
@@ -8,6 +8,7 @@ import {
} from '@vegaprotocol/types';
import { isBefore, isAfter, addSeconds, subSeconds } from 'date-fns';
import { createOrder } from '../support/create-order';
import { connectEthereumWallet } from '../support/ethereum-wallet';
const orderSize = 'size';
const orderType = 'type';
@@ -17,6 +18,16 @@ const orderPrice = 'price';
const orderTimeInForce = 'timeInForce';
const orderCreatedAt = 'createdAt';
const orderUpdatedAt = 'updatedAt';
const assetSelectField = 'select[name="asset"]';
const amountField = 'input[name="amount"]';
const txTimeout = Cypress.env('txTimeout');
const btcName = 'BTC (local)';
const btcSymbol = 'tBTC';
const usdcSymbol = 'fUSDC';
const toastContent = 'toast-content';
const ordersTab = 'Orders';
const depositsTab = 'Deposits';
const toastCloseBtn = 'toast-close';
// TODO: ensure this test runs only if capsule is running via workflow
describe('capsule', { tags: '@slow' }, () => {
@@ -25,6 +36,7 @@ describe('capsule', { tags: '@slow' }, () => {
cy.get('@markets').then((markets) => {
cy.wrap(markets[0]).as('market');
});
cy.updateCapsuleMultiSig();
});
beforeEach(() => {
@@ -45,15 +57,16 @@ describe('capsule', { tags: '@slow' }, () => {
const rawPrice = removeDecimal(order.price, market.decimalPlaces);
const rawSize = removeDecimal(order.size, market.positionDecimalPlaces);
cy.getByTestId('Collateral').click();
cy.getByTestId('asset', txTimeout).should('contain.text', usdcSymbol);
createOrder(order);
cy.getByTestId('dialog-title').should(
cy.getByTestId(toastContent).should(
'contain.text',
'Awaiting network confirmation'
`ConfirmedYour transaction has been confirmed View in block explorerSubmit order - activeTEST.24h+0.0005 @ 390.00 ${usdcSymbol}`
);
cy.getByTestId('dialog-title').should('contain.text', 'Order submitted');
cy.getByTestId('dialog-close').click();
cy.getByTestId(toastCloseBtn).click();
// orderbook cells are keyed by price level
cy.getByTestId('tab-orderbook')
.get(`[data-testid="price-${rawPrice}"]`)
@@ -61,7 +74,8 @@ describe('capsule', { tags: '@slow' }, () => {
.get(`[data-testid="bid-vol-${rawPrice}"]`)
.should('contain.text', rawSize);
cy.getByTestId('Orders').click();
cy.getByTestId(ordersTab).click();
cy.getByTestId('edit').should('contain.text', 'Edit');
cy.getByTestId('tab-orders').within(() => {
cy.get('.ag-center-cols-container')
.children()
@@ -99,14 +113,18 @@ describe('capsule', { tags: '@slow' }, () => {
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderCreatedAt);
});
});
cy.getByTestId('edit').first().click();
//edit order
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('200');
cy.getByTestId('edit-order').find('[type="submit"]').click();
cy.getByTestId('dialog-title').should('contain.text', 'Order updated');
cy.getByTestId('dialog-close').click();
cy.getByTestId(toastContent).should(
'contain.text',
`ConfirmedYour transaction has been confirmed View in block explorerEdit order - activeTEST.24h+0.0005 @ 200.00 ${usdcSymbol}+0.0005 @ 200.00 ${usdcSymbol}`
);
cy.getByTestId(ordersTab).click();
cy.getByTestId(toastCloseBtn).click();
cy.get('.ag-center-cols-container')
.children()
.first()
@@ -116,26 +134,144 @@ describe('capsule', { tags: '@slow' }, () => {
});
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderUpdatedAt);
});
//cancel order
cy.getByTestId(ordersTab).click();
cy.getByTestId('cancel').first().click();
cy.getByTestId('dialog-title').should(
cy.getByTestId(toastContent).should(
'contain.text',
'Awaiting network confirmation'
`ConfirmedYour transaction has been confirmed View in block explorerCancel order - cancelledTEST.24h+0.0005 @ 200.00 ${usdcSymbol}`
);
cy.getByTestId('dialog-title').should('contain.text', 'Order cancelled');
cy.getByTestId('dialog-close').click();
cy.getByTestId(toastCloseBtn).click();
cy.getByTestId('tab-orders')
.get('.ag-center-cols-container')
.children()
.first()
.get(`[col-id='${orderStatus}']`)
.get(`[col-id='${orderStatus}']`, txTimeout)
.should('contain.text', OrderStatusMapping.STATUS_CANCELLED);
});
it('can deposit and withdrawal', function () {
// 1001-DEPO-001
// 1001-DEPO-002
// 1001-DEPO-003
// 1001-DEPO-005
// 1001-DEPO-006
// 1001-DEPO-007
// 1001-DEPO-008
// 1001-DEPO-009
// 1002-WITH-001
// 1002-WITH-006
// 1002-WITH-009
// 002-WITH-011
// 1002-WITH-024
// 1002-WITH-012
// 1002-WITH-013
// 1002-WITH-014
// 1002-WITH-015
// 1002-WITH-016
// 1002-WITH-019
cy.visit('/#/portfolio');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.highlight('creating deposit');
cy.getByTestId(depositsTab).click();
cy.getByTestId('deposit-button').click();
connectEthereumWallet('Unknown');
cy.get(assetSelectField, txTimeout).select(btcName);
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 completedYour transaction has been completedView on EtherscanDeposit 1.00 ${btcSymbol}`
);
cy.getByTestId(toastCloseBtn).click();
cy.getByTestId('Collateral').click();
cy.highlight('deposit verification');
cy.getByTestId('asset', txTimeout).should('contain.text', btcSymbol);
// need to reload page to see deposit history complete
cy.reload();
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', 'https://sepolia.etherscan.io/tx');
});
cy.highlight('creating withdrawals');
cy.getByTestId('Withdrawals').click();
cy.getByTestId('withdraw-dialog-button').click();
connectEthereumWallet('Unknown');
cy.get(assetSelectField).select(btcName);
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()
.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',
'Transaction completed'
);
cy.getByTestId('complete-withdrawal', txTimeout).should('not.exist');
cy.get('[col-id="txHash"]', txTimeout)
.should('have.length.above', 1)
.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="details.receiverAddress"]')
.find('a')
.should('have.attr', 'href')
.and('contain', 'https://sepolia.etherscan.io/address/');
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
cy.get('[col-id="withdrawnTimestamp"]').should('not.be.empty');
cy.get('[col-id="status"]').should('have.text', 'Completed');
cy.get('[col-id="txHash"]')
.find('a')
.should('have.attr', 'href')
.and('contain', 'https://sepolia.etherscan.io/tx/0x');
});
});
});
function checkIfDataAndTimeOfCreationAndUpdateIsEqual(date: string) {
cy.get(`[col-id='${date}']`)
cy.get(`[col-id='${date}'] .ag-cell-wrapper`)
.children('span')
.children('span')
.invoke('data', 'value')
.then(($dateTime) => {
@@ -1,10 +1,13 @@
import { connectEthereumWallet } from '../support/ethereum-wallet';
import { selectAsset } from '../support/helpers';
const assetSelectField = 'select[name="asset"]';
const toAddressField = 'input[name="to"]';
const amountField = 'input[name="amount"]';
const formFieldError = 'input-error-text';
const ASSET_EURO = 1;
describe('deposit form validation', { tags: '@smoke' }, () => {
before(() => {
cy.mockWeb3Provider();
@@ -16,7 +19,7 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
cy.getByTestId('Deposits').click();
cy.getByTestId('deposit-button').click();
cy.wait('@Assets');
connectEthereumWallet();
connectEthereumWallet('MetaMask');
cy.getByTestId('deposit-submit').click();
});
@@ -43,7 +46,8 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
it('invalid amount', () => {
// Deposit amount smaller than minimum viable for selected asset
// Select an amount so that we have a known decimal places value to work with
cy.get(assetSelectField).select('Euro');
selectAsset(ASSET_EURO);
cy.get(amountField)
.clear()
.type('0.00000000000000000000000000000000001')
@@ -119,7 +119,7 @@ describe('ethereum wallet', { tags: '@smoke' }, () => {
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');
cy.getByTestId('Deposits').click();
cy.getByTestId('deposit-button').click();
connectEthereumWallet();
connectEthereumWallet('MetaMask');
cy.get('#ethereum-address').should('have.value', ethWalletAddress);
cy.getByTestId('disconnect-ethereum-wallet')
.should('have.text', 'Disconnect')
@@ -161,6 +161,14 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
);
});
it('liquidity price range displayed', () => {
cy.getByTestId(marketTitle).contains('Liquidity price range').click();
validateMarketDataRow(0, 'Liquidity Price Range', '2.00%');
validateMarketDataRow(1, 'LP Volume Min', '0.05634 tBTC');
validateMarketDataRow(2, 'LP Volume Max', '0.05864 tBTC');
});
it('oracle displayed', () => {
cy.getByTestId(marketTitle).contains('Oracle').click();
+10 -18
View File
@@ -1,14 +1,15 @@
import { connectEthereumWallet } from '../support/ethereum-wallet';
import { selectAsset } from '../support/helpers';
const formFieldError = 'input-error-text';
const toAddressField = 'input[name="to"]';
const assetSelectField = 'select[name="asset"]';
const amountField = 'input[name="amount"]';
const useMaximumAmount = 'use-maximum';
const submitWithdrawBtn = 'submit-withdrawal';
const ethAddressValue = Cypress.env('ETHEREUM_WALLET_ADDRESS');
const asset1Name = 'Sepolia tBTC';
const asset2Name = 'Euro';
const ASSET_SEPOLIA_TBTC = 2;
const ASSET_EURO = 1;
describe('withdraw form validation', { tags: '@smoke' }, () => {
before(() => {
@@ -23,7 +24,7 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
cy.getByTestId('withdraw-dialog-button').click();
// It also requires connection Ethereum wallet
connectEthereumWallet();
connectEthereumWallet('MetaMask');
cy.wait('@Accounts');
cy.wait('@Assets');
@@ -40,7 +41,7 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
cy.get(toAddressField).should('have.value', ethAddressValue);
});
it('min amount', () => {
selectAsset(asset1Name);
selectAsset(ASSET_SEPOLIA_TBTC);
cy.get(amountField).clear().type('0');
cy.getByTestId(submitWithdrawBtn).click();
cy.get('[data-testid="input-error-text"]').should(
@@ -49,7 +50,7 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
);
});
it('max amount', () => {
selectAsset(asset2Name); // Will be above maximum because the vega wallet doesn't have any collateral
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();
cy.get('[data-testid="input-error-text"]').should(
@@ -59,7 +60,7 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
});
it('can set amount using use maximum button', () => {
selectAsset(asset1Name);
selectAsset(ASSET_SEPOLIA_TBTC);
cy.getByTestId(useMaximumAmount).click();
cy.get(amountField).should('have.value', '1000.00000');
});
@@ -79,7 +80,7 @@ describe('withdraw actions', { tags: '@regression' }, () => {
cy.getByTestId('Withdrawals').click();
cy.getByTestId('withdraw-dialog-button').click();
connectEthereumWallet();
connectEthereumWallet('MetaMask');
cy.wait('@Accounts');
cy.wait('@Assets');
@@ -87,7 +88,7 @@ describe('withdraw actions', { tags: '@regression' }, () => {
});
it('triggers transaction when submitted', () => {
selectAsset(asset1Name);
selectAsset(ASSET_SEPOLIA_TBTC);
cy.getByTestId('BALANCE_AVAILABLE_label').should(
'contain.text',
'Balance available'
@@ -111,12 +112,3 @@ describe('withdraw actions', { tags: '@regression' }, () => {
it.skip('creates a withdrawal on submit'); // Needs capsule
it.skip('creates a withdrawal on submit and prompts to complete withdrawal'); // Needs capsule
});
const selectAsset = (assetName: string) => {
cy.get(assetSelectField).select(assetName);
// The asset only gets set once the queries (getWithdrawThreshold, getDelay)
// against the Ethereum change resolve, we should fix this but for now just force
// some wait time
// eslint-disable-next-line
cy.wait(100);
};
@@ -1,5 +1,5 @@
export const connectEthereumWallet = () => {
export const connectEthereumWallet = (connectorName: string) => {
cy.getByTestId('connect-eth-wallet-btn').should('be.enabled').click();
cy.getByTestId('web3-connector-list').should('be.visible');
cy.getByTestId('web3-connector-MetaMask').click();
cy.getByTestId(`web3-connector-${connectorName}`).click();
};
+11
View File
@@ -0,0 +1,11 @@
export const selectAsset = (assetIndex: number) => {
cy.log(`selecting asset: ${assetIndex}`);
cy.getByTestId('select-asset').click();
cy.get('[data-testid="rich-select-option"]').eq(assetIndex).click();
// The asset only gets set once the queries (getWithdrawThreshold, getDelay)
// against the Ethereum change resolve, we should fix this but for now just force
// some wait time
// eslint-disable-next-line
cy.wait(100);
};
+3
View File
@@ -9,3 +9,6 @@ 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_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
+2 -2
View File
@@ -1,3 +1,3 @@
import { Market } from './market';
import { MarketPage } from './market';
export default Market;
export default MarketPage;
+53 -46
View File
@@ -5,6 +5,7 @@ import {
t,
titlefy,
useDataProvider,
useThrottledDataProvider,
} from '@vegaprotocol/react-helpers';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import type {
@@ -31,7 +32,47 @@ export interface SingleMarketData extends SingleMarketFieldsFragment {
data: MarketData;
}
export const Market = () => {
const TitleUpdater = ({
marketId,
marketName,
decimalPlaces,
}: {
marketId?: string;
marketName?: string;
decimalPlaces?: number;
}) => {
const pageTitle = usePageTitleStore((store) => store.pageTitle);
const updateTitle = usePageTitleStore((store) => store.updateTitle);
const { data: marketData } = useThrottledDataProvider<
MarketData,
MarketDataUpdateFieldsFragment
>(
{
dataProvider: marketDataProvider,
variables: useMemo(() => ({ marketId }), [marketId]),
skip: !marketId,
},
1000
);
useEffect(() => {
const marketPrice = calculatePrice(marketData?.markPrice, decimalPlaces);
if (marketName) {
const newPageTitle = titlefy([marketName, marketPrice]);
if (pageTitle !== newPageTitle) {
updateTitle(newPageTitle);
}
}
}, [
decimalPlaces,
marketName,
marketData?.markPrice,
pageTitle,
updateTitle,
]);
return null;
};
export const MarketPage = () => {
const { marketId } = useParams();
const navigate = useNavigate();
@@ -39,9 +80,6 @@ export const Market = () => {
const update = useGlobalStore((store) => store.update);
const lastMarketId = useGlobalStore((store) => store.marketId);
const pageTitle = usePageTitleStore((store) => store.pageTitle);
const updateTitle = usePageTitleStore((store) => store.updateTitle);
const onSelect = useCallback(
(id: string) => {
if (id && id !== marketId) {
@@ -51,56 +89,20 @@ export const Market = () => {
[marketId, navigate]
);
const variables = useMemo(
() => ({
marketId: marketId || '',
}),
[marketId]
);
const updateMarketId = useCallback(
({ data }: { data: { id?: string } | null }) => {
if (data?.id && data.id !== lastMarketId) {
update({ marketId: data.id });
}
return true;
},
[update, lastMarketId]
);
const { data, error, loading } = useDataProvider<
SingleMarketFieldsFragment,
never
>({
dataProvider: marketProvider,
variables,
update: updateMarketId,
variables: useMemo(() => ({ marketId: marketId || '' }), [marketId]),
skip: !marketId,
});
const marketName = data?.tradableInstrument.instrument.name;
const updateProvider = useCallback(
({ data: marketData }: { data: MarketData | null }) => {
const marketPrice = calculatePrice(
marketData?.markPrice,
data?.decimalPlaces
);
if (marketName) {
const newPageTitle = titlefy([marketName, marketPrice]);
if (pageTitle !== newPageTitle) {
updateTitle(newPageTitle);
}
}
return true;
},
[updateTitle, pageTitle, marketName, data?.decimalPlaces]
);
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
dataProvider: marketDataProvider,
update: updateProvider,
variables,
skip: !marketId || !data,
});
useEffect(() => {
if (data?.id && data.id !== lastMarketId) {
update({ marketId: data.id });
}
}, [update, lastMarketId, data?.id]);
const tradeView = useMemo(() => {
if (w > 960) {
@@ -123,6 +125,11 @@ export const Market = () => {
data={data || undefined}
noDataCondition={(data) => false}
>
<TitleUpdater
marketId={data?.id}
marketName={data?.tradableInstrument.instrument.name}
decimalPlaces={data?.decimalPlaces}
/>
{tradeView}
</AsyncRenderer>
);
@@ -139,16 +139,19 @@ const AccountHistoryManager = ({
<div className="h-full w-full flex flex-col gap-8">
<div className="w-full flex flex-col-reverse lg:flex-row items-start lg:items-center justify-between gap-4 px-2">
<div className="flex items-center gap-4 shrink-0">
<DropdownMenu>
<DropdownMenuTrigger>
{accountType
? `${
AccountTypeMapping[
accountType as keyof typeof Schema.AccountType
]
} Account`
: t('Select account type')}
</DropdownMenuTrigger>
<DropdownMenu
trigger={
<DropdownMenuTrigger>
{accountType
? `${
AccountTypeMapping[
accountType as keyof typeof Schema.AccountType
]
} Account`
: t('Select account type')}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
{[
Schema.AccountType.ACCOUNT_TYPE_GENERAL,
@@ -164,10 +167,13 @@ const AccountHistoryManager = ({
))}
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger>
{asset ? asset.symbol : t('Select asset')}
</DropdownMenuTrigger>
<DropdownMenu
trigger={
<DropdownMenuTrigger>
{asset ? asset.symbol : t('Select asset')}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
{assets.map((a) => (
<DropdownMenuItem key={a.id} onClick={() => setAsset(a)}>
+15 -3
View File
@@ -28,7 +28,8 @@ export function AppLoader({ children }: AppLoaderProps) {
export const Web3Provider = ({ children }: { children: ReactNode }) => {
const { config, loading, error } = useEthereumConfig();
const { ETHEREUM_PROVIDER_URL } = useEnvironment();
const { ETHEREUM_PROVIDER_URL, ETH_LOCAL_PROVIDER_URL, ETH_WALLET_MNEMONIC } =
useEnvironment();
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
store.connectors,
store.initialize,
@@ -37,11 +38,22 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
useEffect(() => {
if (config?.chain_id) {
return initializeConnectors(
createConnectors(ETHEREUM_PROVIDER_URL, Number(config?.chain_id)),
createConnectors(
ETHEREUM_PROVIDER_URL,
Number(config?.chain_id),
ETH_LOCAL_PROVIDER_URL,
ETH_WALLET_MNEMONIC
),
Number(config.chain_id)
);
}
}, [config?.chain_id, ETHEREUM_PROVIDER_URL, initializeConnectors]);
}, [
config?.chain_id,
ETHEREUM_PROVIDER_URL,
initializeConnectors,
ETH_LOCAL_PROVIDER_URL,
ETH_WALLET_MNEMONIC,
]);
return (
<AsyncRenderer
+2 -2
View File
@@ -43,8 +43,8 @@ export const HeaderStat = ({
testId?: string;
}) => {
const itemClass =
'min-w-min w-[120px] whitespace-nowrap pb-3 px-4 border-l border-default';
const itemHeading = 'text-neutral-500 dark:text-neutral-400';
'min-w-min w-[120px] whitespace-nowrap pb-3 px-4 border-l border-default text-neutral-500 dark:text-neutral-400';
const itemHeading = 'text-black dark:text-white';
return (
<div data-testid={testId} className={itemClass}>
@@ -3,6 +3,7 @@ import { VegaWalletContext } from '@vegaprotocol/wallet';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletConnectButton } from './vega-wallet-connect-button';
import { truncateByChars } from '@vegaprotocol/react-helpers';
import userEvent from '@testing-library/user-event';
const mockUpdateDialogOpen = jest.fn();
jest.mock('@vegaprotocol/wallet', () => ({
@@ -31,7 +32,7 @@ it('Not connected', () => {
expect(mockUpdateDialogOpen).toHaveBeenCalled();
});
it('Connected', () => {
it('Connected', async () => {
const pubKey = { publicKey: '123456__123456', name: 'test' };
render(
generateJsx({
@@ -42,6 +43,6 @@ it('Connected', () => {
const button = screen.getByTestId('manage-vega-wallet');
expect(button).toHaveTextContent(truncateByChars(pubKey.publicKey));
fireEvent.click(button);
userEvent.click(button);
expect(mockUpdateDialogOpen).not.toHaveBeenCalled();
});
@@ -142,15 +142,21 @@ export const VegaWalletConnectButton = () => {
return (
<>
<div className="hidden lg:block">
<DropdownMenu open={dropdownOpen}>
<DropdownMenuTrigger
data-testid="manage-vega-wallet"
onClick={() => setDropdownOpen((curr) => !curr)}
>
{activeKey && <span className="uppercase">{activeKey.name}</span>}
{': '}
{truncateByChars(pubKey)}
</DropdownMenuTrigger>
<DropdownMenu
open={dropdownOpen}
trigger={
<DropdownMenuTrigger
data-testid="manage-vega-wallet"
onClick={() => setDropdownOpen((curr) => !curr)}
>
{activeKey && (
<span className="uppercase">{activeKey.name}</span>
)}
{': '}
{truncateByChars(pubKey)}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent
onInteractOutside={() => setDropdownOpen(false)}
>
@@ -1,3 +1,4 @@
import type { ReactNode } from 'react';
import { useAssetsDataProvider } from '@vegaprotocol/assets';
import { ETHERSCAN_TX, useEtherscanLink } from '@vegaprotocol/environment';
import { formatNumber, t, toBigNum } from '@vegaprotocol/react-helpers';
@@ -18,60 +19,59 @@ const intentMap: { [s in EthTxStatus]: Intent } = {
Requested: Intent.Warning,
Pending: Intent.Warning,
Error: Intent.Danger,
Complete: Intent.Success,
Complete: Intent.Warning,
Confirmed: Intent.Success,
};
const isWithdrawTransaction = (tx: EthStoredTxState) =>
tx.methodName === 'withdraw_asset';
const isDepositTransaction = (tx: EthStoredTxState) =>
tx.methodName === 'withdraw_asset';
const EthTransactionDetails = ({ tx }: { tx: EthStoredTxState }) => {
const { data } = useAssetsDataProvider();
if (!data) return null;
const { data: assets } = useAssetsDataProvider();
if (!assets) return null;
const ETH_WITHDRAW =
tx.methodName === 'withdraw_asset' && tx.args.length > 2 && tx.assetId;
const ETH_DEPOSIT =
tx.methodName === 'deposit_asset' && tx.args.length > 2 && tx.assetId;
let label = '';
if (ETH_WITHDRAW) label = t('Withdraw');
if (ETH_DEPOSIT) label = t('Deposit');
if (ETH_WITHDRAW || ETH_DEPOSIT) {
const asset = data.find((a) => a.id === tx.assetId);
const isWithdraw = isWithdrawTransaction(tx);
const isDeposit = isDepositTransaction(tx);
let assetInfo: ReactNode;
if ((isWithdraw || isDeposit) && tx.args.length > 2 && tx.assetId) {
const asset = assets.find((a) => a.id === tx.assetId);
if (asset) {
const num = formatNumber(
toBigNum(tx.args[1], asset.decimals),
asset.decimals
);
const details = (
let label = '';
if (isWithdraw) label = t('Withdraw');
if (isDeposit) label = t('Deposit');
assetInfo = (
<div className="mt-[5px]">
<span className="font-mono text-xs p-1 bg-gray-100 rounded">
{label} {num} {asset.symbol}
{label}{' '}
{formatNumber(toBigNum(tx.args[1], asset.decimals), asset.decimals)}{' '}
{asset.symbol}
</span>
</div>
);
return (
<>
{details}
{tx.requiresConfirmation &&
[EthTxStatus.Pending].includes(tx.status) && (
<div className="mt-[10px]">
<span className="font-mono text-xs">
{t('Awaiting confirmations')}{' '}
{`(${tx.confirmations}/${tx.requiredConfirmations})`}
</span>
<ProgressBar
value={(tx.confirmations / tx.requiredConfirmations) * 100}
intent={Intent.Warning}
/>
</div>
)}
</>
);
}
}
return null;
return (
<>
{assetInfo}
{tx.status === EthTxStatus.Pending && (
<div className="mt-[10px]">
<span className="font-mono text-xs">
{t('Awaiting confirmations')}{' '}
{`(${tx.confirmations}/${tx.requiredConfirmations})`}
</span>
<ProgressBar
value={(tx.confirmations / tx.requiredConfirmations) * 100}
intent={Intent.Warning}
/>
</div>
)}
</>
);
};
type EthTxToastContentProps = {
@@ -93,21 +93,11 @@ const EthTxRequestedToastContent = ({ tx }: EthTxToastContentProps) => {
};
const EthTxPendingToastContent = ({ tx }: EthTxToastContentProps) => {
const etherscanLink = useEtherscanLink();
return (
<div>
<h3 className="font-bold">{t('Awaiting confirmation')}</h3>
<p>{t('Please wait for your transaction to be confirmed')}</p>
{tx.txHash && (
<p className="break-all">
<ExternalLink
href={etherscanLink(ETHERSCAN_TX.replace(':hash', tx.txHash))}
rel="noreferrer"
>
{t('View on Etherscan')}
</ExternalLink>
</p>
)}
<EtherscanLink tx={tx} />
<EthTransactionDetails tx={tx} />
</div>
);
@@ -130,22 +120,43 @@ const EthTxErrorToastContent = ({ tx }: EthTxToastContentProps) => {
);
};
const EthTxConfirmedToastContent = ({ tx }: EthTxToastContentProps) => {
const EtherscanLink = ({ tx }: EthTxToastContentProps) => {
const etherscanLink = useEtherscanLink();
return tx.txHash ? (
<p className="break-all">
<ExternalLink
href={etherscanLink(ETHERSCAN_TX.replace(':hash', tx.txHash))}
rel="noreferrer"
>
{t('View on Etherscan')}
</ExternalLink>
</p>
) : null;
};
const EthTxConfirmedToastContent = ({ tx }: EthTxToastContentProps) => {
return (
<div>
<h3 className="font-bold">{t('Transaction completed')}</h3>
<p>{t('Your transaction has been completed')}</p>
{tx.txHash && (
<p className="break-all">
<ExternalLink
href={etherscanLink(ETHERSCAN_TX.replace(':hash', tx.txHash))}
rel="noreferrer"
>
{t('View on Etherscan')}
</ExternalLink>
</p>
)}
<h3 className="font-bold">{t('Transaction confirmed')}</h3>
<p>{t('Your transaction has been confirmed')}</p>
<EtherscanLink tx={tx} />
<EthTransactionDetails tx={tx} />
</div>
);
};
const EthTxCompletedToastContent = ({ tx }: EthTxToastContentProps) => {
const isDeposit = isDepositTransaction(tx);
return (
<div>
<h3 className="font-bold">
{t('Processing')} {isDeposit && 'deposit'}
</h3>
<p>
{t('Your transaction has been completed.')}
{isDeposit && t('Waiting for deposit confirmation')}
</p>
<EtherscanLink tx={tx} />
<EthTransactionDetails tx={tx} />
</div>
);
@@ -167,10 +178,10 @@ export const useEthereumTransactionToasts = () => {
if (tx.status === EthTxStatus.Pending) {
content = <EthTxPendingToastContent tx={tx} />;
}
if (
tx.status === EthTxStatus.Confirmed ||
tx.status === EthTxStatus.Complete
) {
if (tx.status === EthTxStatus.Complete) {
content = <EthTxCompletedToastContent tx={tx} />;
}
if (tx.status === EthTxStatus.Confirmed) {
content = <EthTxConfirmedToastContent tx={tx} />;
}
if (tx.status === EthTxStatus.Error) {
@@ -181,7 +192,7 @@ export const useEthereumTransactionToasts = () => {
id: `eth-${tx.id}`,
intent: intentMap[tx.status],
onClose: () => dismissEthTransaction(tx.id),
loader: tx.status === EthTxStatus.Pending,
loader: [EthTxStatus.Pending, EthTxStatus.Complete].includes(tx.status),
content,
};
},
@@ -95,7 +95,7 @@ const Details = ({
title?: string;
}) => (
<div className="pt-[5px]" data-testid="vega-tx-details" title={title}>
<div className="font-mono text-xs p-2 bg-neutral-100 rounded">
<div className="font-mono text-xs p-2 bg-neutral-100 rounded dark:bg-neutral-700 dark:text-white">
{children}
</div>
</div>
@@ -430,6 +430,7 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
const completeWithdrawalButton = tx.withdrawal && (
<div className="mt-[10px]">
<Button
data-testid="toast-complete-withdrawal"
size="xs"
onClick={() => {
createEthWithdrawalApproval(
+1 -1
View File
@@ -1,5 +1,5 @@
import { LocalStorage } from '@vegaprotocol/react-helpers';
import create from 'zustand';
import { create } from 'zustand';
import produce from 'immer';
interface GlobalStore {
@@ -17,15 +17,11 @@ export const useAccountBalance = (assetId?: string) => {
const account = assetId
? getSettlementAccount({ accounts: data, assetId })
: undefined;
if (accountBalance !== account?.balance) {
setAccountBalance(account?.balance || '');
}
if (accountDecimals !== account?.asset.decimals) {
setAccountDecimals(account?.asset.decimals || null);
}
setAccountBalance(account?.balance || '');
setAccountDecimals(account?.asset.decimals || null);
return true;
},
[accountBalance, accountDecimals, assetId]
[assetId]
);
useDataProvider({
@@ -9,26 +9,23 @@ export const useMarketAccountBalance = (marketId: string) => {
const { pubKey } = useVegaWallet();
const [accountBalance, setAccountBalance] = useState<string>('');
const [accountDecimals, setAccountDecimals] = useState<number | null>(null);
const variables = useMemo(() => {
return { partyId: pubKey || '' };
}, [pubKey]);
const update = useCallback(
({ data }: { data: Account[] | null }) => {
const account = getMarketAccount({ accounts: data, marketId });
if (accountBalance !== account?.balance) {
if (account?.balance) {
setAccountBalance(account?.balance || '');
}
if (accountDecimals !== account?.asset.decimals) {
if (account?.asset.decimals) {
setAccountDecimals(account?.asset.decimals || null);
}
return true;
},
[accountBalance, accountDecimals, marketId]
[marketId]
);
useDataProvider({
dataProvider: accountsDataProvider,
variables,
variables: { partyId: pubKey || '' },
skip: !pubKey || !marketId,
update,
});
+3 -2
View File
@@ -14,6 +14,7 @@ import { onError } from '@apollo/client/link/error';
import { RetryLink } from '@apollo/client/link/retry';
import ApolloLinkTimeout from 'apollo-link-timeout';
import type { GraphQLErrors } from '@apollo/client/errors';
import { localLoggerFactory } from '@vegaprotocol/react-helpers';
const isBrowser = typeof window !== 'undefined';
@@ -68,12 +69,12 @@ export function createClient(base?: string, cacheConfig?: InMemoryCacheConfig) {
if (graphQLErrors) {
graphQLErrors.forEach((e) => {
if (e.extensions && e.extensions['type'] !== NOT_FOUND) {
console.log(e);
localLoggerFactory({ application: 'apollo-client' }).error(e);
}
});
}
if (networkError) {
console.log(networkError);
localLoggerFactory({ application: 'apollo-client' }).error(networkError);
}
});
+1 -1
View File
@@ -1,7 +1,7 @@
import { t } from '@vegaprotocol/react-helpers';
import { useAssetsDataProvider } from './assets-data-provider';
import { Button, Dialog, Icon, Splash } from '@vegaprotocol/ui-toolkit';
import create from 'zustand';
import { create } from 'zustand';
import { AssetDetailsTable } from './asset-details-table';
import { AssetProposalNotification } from '@vegaprotocol/governance';
+20 -12
View File
@@ -60,10 +60,13 @@ export const CandlesChartContainer = ({
return (
<div className="h-full flex flex-col">
<div className="px-4 py-2 flex flex-row flex-wrap gap-4">
<DropdownMenu>
<DropdownMenuTrigger>
{t(`Interval: ${intervalLabels[interval]}`)}
</DropdownMenuTrigger>
<DropdownMenu
trigger={
<DropdownMenuTrigger>
{t(`Interval: ${intervalLabels[interval]}`)}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
<DropdownMenuRadioGroup
value={interval}
@@ -84,10 +87,13 @@ export const CandlesChartContainer = ({
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger>
<Icon name={chartTypeIcon.get(chartType) as IconName} />
</DropdownMenuTrigger>
<DropdownMenu
trigger={
<DropdownMenuTrigger>
<Icon name={chartTypeIcon.get(chartType) as IconName} />
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
<DropdownMenuRadioGroup
value={chartType}
@@ -104,8 +110,9 @@ export const CandlesChartContainer = ({
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger>{t('Overlays')}</DropdownMenuTrigger>
<DropdownMenu
trigger={<DropdownMenuTrigger>{t('Overlays')}</DropdownMenuTrigger>}
>
<DropdownMenuContent>
{Object.values(Overlay).map((overlay) => (
<DropdownMenuCheckboxItem
@@ -128,8 +135,9 @@ export const CandlesChartContainer = ({
))}
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger>{t('Studies')}</DropdownMenuTrigger>
<DropdownMenu
trigger={<DropdownMenuTrigger>{t('Studies')}</DropdownMenuTrigger>}
>
<DropdownMenuContent>
{Object.values(Study).map((study) => (
<DropdownMenuCheckboxItem
+2
View File
@@ -16,6 +16,7 @@ import {
} from './lib/commands/vega-wallet-connect';
import { addMockTransactionResponse } from './lib/commands/mock-transaction-response';
import { addCreateMarket } from './lib/commands/create-market';
import { addConnectPublicKey } from './lib/commands/add-connect-public-key';
addGetTestIdcommand();
addSlackCommand();
@@ -33,6 +34,7 @@ addVegaWalletConnect();
addSetVegaWallet();
addMockTransactionResponse();
addCreateMarket();
addConnectPublicKey();
export { mockConnectWallet } from './lib/commands/vega-wallet-connect';
export type { onMessage } from './lib/mock-ws';
@@ -3,7 +3,7 @@ import { requestGQL } from './request';
export async function getEthereumConfig() {
const query = gql`
{
query {
networkParameter(key: "blockchains.ethereumConfig") {
value
}
+1 -1
View File
@@ -3,7 +3,7 @@ import { requestGQL } from './request';
export async function getMarkets() {
const query = gql`
{
query {
marketsConnection {
edges {
node {
@@ -3,7 +3,7 @@ import { requestGQL } from './request';
export async function getPartyStake(partyId: string) {
const query = gql`
{
query {
party(id:"${partyId}") {
stakingSummary {
currentStakeAvailable
+1 -1
View File
@@ -3,7 +3,7 @@ import { requestGQL } from './request';
export async function getProposal(id: string) {
const query = gql`
{
query {
proposal(id: "${id}") {
id
state
@@ -3,7 +3,7 @@ import { requestGQL } from './request';
export async function getVegaAsset() {
const query = gql`
{
query {
assetsConnection {
edges {
node {
@@ -0,0 +1,23 @@
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
connectPublicKey(publicKey: string): void;
}
}
}
export const addConnectPublicKey = () => {
Cypress.Commands.add('connectPublicKey', (publicKey) => {
cy.getByTestId('connect-vega-wallet').then((connectWallet) => {
if (connectWallet.length) {
cy.getByTestId('connect-vega-wallet').click();
cy.getByTestId('connector-view').should('be.visible').click();
cy.getByTestId('address').click();
cy.getByTestId('address').type(publicKey);
cy.getByTestId('connect').click();
}
});
});
};
@@ -52,7 +52,9 @@ function generateJsx(order?: OrderSubmissionBody['orderSubmission']) {
}
describe('DealTicket', () => {
beforeEach(() => window.localStorage.clear());
beforeEach(() => {
window.localStorage.clear();
});
afterEach(() => {
window.localStorage.clear();
jest.clearAllMocks();
@@ -87,38 +89,6 @@ describe('DealTicket', () => {
);
});
it('can edit deal ticket', async () => {
render(generateJsx());
// BUY is selected by default
expect(
screen.getByTestId('order-side-SIDE_BUY')?.querySelector('input')
).toBeChecked();
await act(async () => {
fireEvent.change(screen.getByTestId('order-size'), {
target: { value: '200' },
});
});
expect(screen.getByTestId('order-size')).toHaveDisplayValue('200');
fireEvent.change(screen.getByTestId('order-tif'), {
target: { value: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC },
});
expect(screen.getByTestId('order-tif')).toHaveValue(
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC
);
// Switch to limit order
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
// Check all TIF options shown
expect(screen.getByTestId('order-tif').children).toHaveLength(
Object.keys(Schema.OrderTimeInForce).length
);
});
it('handles TIF select box dependent on order type', () => {
render(generateJsx());
@@ -221,4 +191,36 @@ describe('DealTicket', () => {
).toHaveTextContent('Wrong trading mode');
});
});
it('can edit deal ticket', async () => {
render(generateJsx());
// BUY is selected by default
expect(
screen.getByTestId('order-side-SIDE_BUY')?.querySelector('input')
).toBeChecked();
await act(async () => {
fireEvent.change(screen.getByTestId('order-size'), {
target: { value: '200' },
});
});
expect(screen.getByTestId('order-size')).toHaveDisplayValue('200');
fireEvent.change(screen.getByTestId('order-tif'), {
target: { value: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC },
});
expect(screen.getByTestId('order-tif')).toHaveValue(
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC
);
// Switch to limit order
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
// Check all TIF options shown
expect(screen.getByTestId('order-tif').children).toHaveLength(
Object.keys(Schema.OrderTimeInForce).length
);
});
});
@@ -15,7 +15,6 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
import { InputError } from '@vegaprotocol/ui-toolkit';
import { useOrderMarginValidation } from '../../hooks/use-order-margin-validation';
import { MarginWarning } from '../deal-ticket-validation/margin-warning';
import { usePersistedOrder } from '../../hooks/use-persisted-order';
import {
getDefaultOrder,
validateMarketState,
@@ -27,6 +26,10 @@ import { ZeroBalanceError } from '../deal-ticket-validation/zero-balance-error';
import { SummaryValidationType } from '../../constants';
import { useHasNoBalance } from '../../hooks/use-has-no-balance';
import type { MarketDealTicket } from '@vegaprotocol/market-list';
import {
usePersistedOrderStore,
usePersistedOrderStoreSubscription,
} from '@vegaprotocol/orders';
export type TransactionStatus = 'default' | 'pending';
@@ -43,7 +46,13 @@ export type DealTicketFormFields = OrderSubmissionBody['orderSubmission'] & {
export const DealTicket = ({ market, submit }: DealTicketProps) => {
const { pubKey } = useVegaWallet();
const [persistedOrder, setPersistedOrder] = usePersistedOrder(market);
const { getPersistedOrder, setPersistedOrder } = usePersistedOrderStore(
(store) => ({
getPersistedOrder: store.getOrder,
setPersistedOrder: store.setOrder,
})
);
const {
register,
control,
@@ -52,11 +61,23 @@ export const DealTicket = ({ market, submit }: DealTicketProps) => {
setError,
clearErrors,
formState: { errors },
setValue,
} = useForm<DealTicketFormFields>({
defaultValues: persistedOrder || getDefaultOrder(market),
defaultValues: getPersistedOrder(market.id) || getDefaultOrder(market),
});
const order = watch();
watch((orderData) => {
setPersistedOrder(orderData as DealTicketFormFields);
});
usePersistedOrderStoreSubscription(market.id, (storedOrder) => {
if (order.price !== storedOrder.price) {
setValue('price', storedOrder.price);
}
});
const marketStateError = validateMarketState(market.data.marketState);
const hasNoBalance = useHasNoBalance(
market.tradableInstrument.instrument.product.settlementAsset.id
@@ -84,9 +105,6 @@ export const DealTicket = ({ market, submit }: DealTicketProps) => {
errors.summary?.type,
]);
// When order state changes persist it in local storage
useEffect(() => setPersistedOrder(order), [order, setPersistedOrder]);
const onSubmit = useCallback(
(order: OrderSubmissionBody['orderSubmission']) => {
if (!pubKey) {
@@ -1,20 +0,0 @@
import { useLocalStorage } from '@vegaprotocol/react-helpers';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { useCallback, useMemo } from 'react';
type OrderData = OrderSubmissionBody['orderSubmission'] | null;
export const usePersistedOrder = (market: {
id: string;
}): [OrderData, (value: OrderData) => void] => {
const [value, setValue] = useLocalStorage(`deal-ticket-order-${market.id}`);
const order = value != null ? (JSON.parse(value) as OrderData) : null;
const setOrder = useCallback(
(order: OrderData) => setValue(JSON.stringify(order)),
[setValue]
);
return useMemo<[OrderData, (value: OrderData) => void]>(
() => [order, setOrder],
[order, setOrder]
);
};
+1 -1
View File
@@ -1,4 +1,4 @@
import create from 'zustand';
import { create } from 'zustand';
import { t } from '@vegaprotocol/react-helpers';
import type { Intent } from '@vegaprotocol/ui-toolkit';
import { Dialog } from '@vegaprotocol/ui-toolkit';
+21 -13
View File
@@ -16,7 +16,8 @@ import {
Icon,
Input,
InputError,
Select,
RichSelect,
Option,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useWeb3React } from '@web3-react/core';
@@ -171,22 +172,29 @@ export const DepositForm = ({
},
}}
render={({ field }) => (
<Select
id="asset"
{...field}
onChange={(e) => {
field.onChange(e);
onSelectAsset(e.target.value);
<RichSelect
data-testid="select-asset"
id={field.name}
name={field.name}
onValueChange={(value) => {
onSelectAsset(value);
field.onChange(value);
}}
value={selectedAsset?.id || ''}
placeholder={t('Please select an asset')}
value={selectedAsset?.id}
hasError={Boolean(errors.asset?.message)}
>
<option value="">{t('Please select')}</option>
{assets.filter(isAssetTypeERC20).map((a) => (
<option key={a.id} value={a.id}>
{a.name}
</option>
<Option key={a.id} value={a.id}>
<div className="flex flex-col items-start">
<span>{a.name}</span>
<span className="text-[10px] font-mono">
<span className="text-gray-500">{a.id} -</span> {a.symbol}
</span>
</div>
</Option>
))}
</Select>
</RichSelect>
)}
/>
{errors.asset?.message && (
@@ -96,13 +96,18 @@ export const NetworkSwitcher = () => {
const menuRef = useRef<HTMLButtonElement | null>(null);
return (
<DropdownMenu open={isOpen} onOpenChange={handleOpen}>
<DropdownMenuTrigger
ref={menuRef}
className="flex justify-between items-center"
>
{envTriggerMapping[VEGA_ENV]}
</DropdownMenuTrigger>
<DropdownMenu
open={isOpen}
onOpenChange={handleOpen}
trigger={
<DropdownMenuTrigger
ref={menuRef}
className="flex justify-between items-center"
>
{envTriggerMapping[VEGA_ENV]}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent
align="start"
style={{ minWidth: `${menuRef.current?.offsetWidth || 290}px` }}
@@ -78,6 +78,10 @@ const getBundledEnvironmentValue = (key: EnvKey) => {
return process.env['NX_VEGA_DOCS_URL'];
case 'HOSTED_WALLET_URL':
return process.env['NX_HOSTED_WALLET_URL'];
case 'ETH_LOCAL_PROVIDER_URL':
return process.env['NX_ETH_LOCAL_PROVIDER_URL'];
case 'ETH_WALLET_MNEMONIC':
return process.env['NX_ETH_WALLET_MNEMONIC'];
case 'MAINTENANCE_PAGE':
return (
process.env['MAINTENANCE_PAGE'] || process.env['NX_MAINTENANCE_PAGE']
@@ -50,6 +50,8 @@ const schemaObject = {
}),
HOSTED_WALLET_URL: z.optional(z.string()),
MAINTENANCE_PAGE: z.optional(z.boolean()),
ETH_LOCAL_PROVIDER_URL: z.optional(z.string()),
ETH_WALLET_MNEMONIC: z.optional(z.string()),
};
export const ENV_KEYS = Object.keys(schemaObject) as Array<
@@ -56,7 +56,6 @@ describe('market depth provider update', () => {
];
expect(update(data, delta.slice(0, 1), reload)).toBe(data);
expect(update(data, delta.slice(1, 2), reload)).toBe(data);
expect(mockCaptureException).toBeCalledTimes(2);
});
it('restarts and captureException when there is gap in updates', () => {
@@ -24,11 +24,6 @@ export const update: Update<
continue;
}
if (BigInt(delta.sequenceNumber) <= BigInt(data.depth.sequenceNumber)) {
captureException(
new Error(
`Sequence number from delta is lower or equal to last sequenceNumber for ${data.id}, ${delta.sequenceNumber} <= ${data.depth.sequenceNumber}, update skipped`
)
);
return data;
}
if (delta.previousSequenceNumber !== data.depth.sequenceNumber) {
@@ -1,7 +1,7 @@
import throttle from 'lodash/throttle';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Orderbook } from './orderbook';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { addDecimal, useDataProvider } from '@vegaprotocol/react-helpers';
import { marketDepthProvider } from './market-depth-provider';
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
import type { MarketData } from '@vegaprotocol/market-list';
@@ -16,6 +16,7 @@ import {
mapMarketData,
} from './orderbook-data';
import type { OrderbookData } from './orderbook-data';
import { usePersistedOrderStore } from '@vegaprotocol/orders';
interface OrderbookManagerProps {
marketId: string;
@@ -122,7 +123,7 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
marketDataRef.current = marketData;
useEffect(() => {
const throttleRunnner = updateOrderbookData.current;
const throttleRunner = updateOrderbookData.current;
if (!marketDataRef.current) {
return;
}
@@ -139,7 +140,7 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
setOrderbookData(dataRef.current);
return () => {
throttleRunnner.cancel();
throttleRunner.cancel();
};
}, [data, marketData, resolution]);
@@ -148,6 +149,8 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
flush();
}, [resolution, flush]);
const updatePrice = usePersistedOrderStore((store) => store.updatePrice);
return (
<AsyncRenderer
loading={loading || marketDataLoading || marketLoading}
@@ -160,6 +163,12 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
positionDecimalPlaces={market?.positionDecimalPlaces ?? 0}
resolution={resolution}
onResolutionChange={(resolution: number) => setResolution(resolution)}
onClick={(price?: string | number) => {
if (price) {
const priceValue = addDecimal(price, market?.decimalPlaces ?? 0);
updatePrice(marketId, priceValue);
}
}}
/>
</AsyncRenderer>
);
@@ -21,6 +21,7 @@ interface OrderbookRowProps {
price: string;
relativeAsk?: number;
relativeBid?: number;
onClick?: (price?: string | number) => void;
}
export const OrderbookRow = React.memo(
@@ -37,6 +38,7 @@ export const OrderbookRow = React.memo(
price,
relativeAsk,
relativeBid,
onClick,
}: OrderbookRowProps) => {
return (
<>
@@ -57,6 +59,7 @@ export const OrderbookRow = React.memo(
<PriceCell
testId={`price-${price}`}
value={BigInt(price)}
onClick={() => onClick && onClick(price)}
valueFormatted={addDecimalsFormatNumber(price, decimalPlaces)}
/>
<CumulativeVol
+13 -11
View File
@@ -22,7 +22,7 @@ import {
import * as Schema from '@vegaprotocol/types';
import { OrderbookRow } from './orderbook-row';
import { createRow, getPriceLevel } from './orderbook-data';
import { Icon, Splash } from '@vegaprotocol/ui-toolkit';
import { Checkbox, Icon, Splash } from '@vegaprotocol/ui-toolkit';
import type { OrderbookData, OrderbookRowData } from './orderbook-data';
interface OrderbookProps extends OrderbookData {
@@ -30,6 +30,7 @@ interface OrderbookProps extends OrderbookData {
positionDecimalPlaces: number;
resolution: number;
onResolutionChange: (resolution: number) => void;
onClick?: (price?: string | number) => void;
fillGaps?: boolean;
}
@@ -279,6 +280,7 @@ export const Orderbook = ({
resolution,
fillGaps: initialFillGaps,
onResolutionChange,
onClick,
}: OrderbookProps) => {
const { theme } = useThemeSwitcher();
const scrollElement = useRef<HTMLDivElement>(null);
@@ -533,6 +535,7 @@ export const Orderbook = ({
<OrderbookRow
key={data.price}
price={(BigInt(data.price) / BigInt(resolution)).toString()}
onClick={onClick}
decimalPlaces={decimalPlaces - Math.log10(resolution)}
positionDecimalPlaces={positionDecimalPlaces}
bid={data.bid}
@@ -635,19 +638,18 @@ export const Orderbook = ({
</div>
<div
className="absolute bottom-0 grid grid-cols-4 gap-2 border-t-[1px] border-default mt-2 z-10 bg-white dark:bg-black w-full"
style={{ gridAutoRows: '17px' }}
style={{ gridAutoRows: '20px' }}
ref={footerElement}
>
<div className="col-span-2">
<label className="flex items-center whitespace-nowrap overflow-hidden text-ellipsis">
<input
className="mr-1"
type="checkbox"
checked={fillGaps}
onChange={() => setFillGaps(!fillGaps)}
/>
{t('Show prices with no orders')}
</label>
<Checkbox
name="empty-prices"
checked={fillGaps}
onCheckedChange={() => setFillGaps((curr) => !curr)}
label={
<span className="text-xs">{t('Show prices with no orders')}</span>
}
/>
</div>
<div className="col-start-3">
<select
@@ -5,6 +5,7 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
positionDecimalPlaces
state
tradingMode
lpPriceRange
proposal {
id
rationale {
@@ -58,6 +59,7 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
id
}
markPrice
midPrice
bestBidVolume
bestOfferVolume
bestStaticBidVolume
@@ -10,7 +10,7 @@ export type MarketInfoQueryVariables = Types.Exact<{
}>;
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, data?: { __typename?: 'MarketData', markPrice: string, bestBidVolume: string, bestOfferVolume: string, bestStaticBidVolume: string, bestStaticOfferVolume: string, bestBidPrice: string, bestOfferPrice: string, trigger: Types.AuctionTrigger, openInterest: string, suppliedStake?: string | null, targetStake?: string | null, marketValueProxy: string, market: { __typename?: 'Market', id: string }, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, candlesConnection?: { __typename?: 'CandleDataConnection', edges?: Array<{ __typename?: 'CandleEdge', node: { __typename?: 'Candle', volume: string } } | null> | null } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null } } | null };
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, data?: { __typename?: 'MarketData', markPrice: string, midPrice: string, bestBidVolume: string, bestOfferVolume: string, bestStaticBidVolume: string, bestStaticOfferVolume: string, bestBidPrice: string, bestOfferPrice: string, trigger: Types.AuctionTrigger, openInterest: string, suppliedStake?: string | null, targetStake?: string | null, marketValueProxy: string, market: { __typename?: 'Market', id: string }, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, candlesConnection?: { __typename?: 'CandleDataConnection', edges?: Array<{ __typename?: 'CandleEdge', node: { __typename?: 'Candle', volume: string } } | null> | null } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null } } | null };
export const MarketInfoDocument = gql`
@@ -21,6 +21,7 @@ export const MarketInfoDocument = gql`
positionDecimalPlaces
state
tradingMode
lpPriceRange
proposal {
id
rationale {
@@ -74,6 +75,7 @@ export const MarketInfoDocument = gql`
id
}
markPrice
midPrice
bestBidVolume
bestOfferVolume
bestStaticBidVolume
@@ -2,7 +2,9 @@ import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { useEnvironment } from '@vegaprotocol/environment';
import { totalFeesPercentage } from '@vegaprotocol/market-list';
import {
addDecimalsFormatNumber,
formatNumber,
formatNumberPercentage,
removePaginationWrapper,
t,
useDataProvider,
@@ -182,6 +184,9 @@ export const Info = ({ market, onSelect }: InfoProps) => {
state: Schema.MarketStateMapping[market.state],
};
const assetDecimals =
market.tradableInstrument.instrument.product.settlementAsset.decimals;
const marketSpecPanels = [
{
title: t('Key details'),
@@ -195,9 +200,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
Schema.MarketTradingModeMapping[keyDetails.tradingMode],
marketDecimalPlaces: market.decimalPlaces,
positionDecimalPlaces: market.positionDecimalPlaces,
settlementAssetDecimalPlaces:
market.tradableInstrument.instrument.product.settlementAsset
.decimals,
settlementAssetDecimalPlaces: assetDecimals,
}}
/>
),
@@ -295,10 +298,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
/>
<MarketInfoTable
data={{ referencePrice: trigger.referencePrice }}
decimalPlaces={
market?.tradableInstrument.instrument.product?.settlementAsset
.decimals
}
decimalPlaces={assetDecimals}
/>
</>
),
@@ -324,10 +324,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
suppliedStake: market.data && market.data?.suppliedStake,
marketValueProxy: market.data && market.data.marketValueProxy,
}}
decimalPlaces={
market.tradableInstrument.instrument.product.settlementAsset
.decimals
}
decimalPlaces={assetDecimals}
assetSymbol={assetSymbol}
>
<Link
@@ -340,6 +337,36 @@ export const Info = ({ market, onSelect }: InfoProps) => {
</MarketInfoTable>
),
},
{
title: t('Liquidity price range'),
content: (
<MarketInfoTable
data={{
liquidityPriceRange: formatNumberPercentage(
new BigNumber(market.lpPriceRange).times(100)
),
LPVolumeMin:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.minus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${assetSymbol}`,
LPVolumeMax:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.plus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${assetSymbol}`,
}}
></MarketInfoTable>
),
},
{
title: t('Oracle'),
content: (
@@ -369,7 +396,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
content: (
<div className="">
<ExternalLink
className="mb-2"
className="mb-2 w-full"
href={generatePath(TokenLinks.PROPOSAL_PAGE, {
tokenUrl: VEGA_TOKEN_URL,
proposalId: market.proposal?.id || '',
@@ -383,7 +410,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
{t('View governance proposal')}
</ExternalLink>
<ExternalLink
className="mt-2"
className="w-full"
href={generatePath(TokenLinks.UPDATE_PROPOSAL_PAGE, {
tokenUrl: VEGA_TOKEN_URL,
})}
@@ -92,12 +92,14 @@ export const marketInfoQuery = (
short: '0.008571790367285281',
long: '0.008508132993273576',
},
lpPriceRange: '0.02',
data: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '54b78c1b877e106842ae156332ccec740ad98d6bad43143ac6a029501dd7c6e0',
},
midPrice: '5749',
markPrice: '5749',
suppliedStake: '56767',
marketValueProxy: '677678',
@@ -98,4 +98,7 @@ export const tooltipMapping: Record<string, ReactNode> = {
`The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.`
),
suppliedStake: t('The current amount of liquidity supplied for this market.'),
liquidityPriceRange: t(
`Percentage move up and down from the mid price, which specifies the range of price levels over which automated liquidity provision orders will be deployed.`
),
};
@@ -245,7 +245,7 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
colId="amend"
headerName=""
field="status"
minWidth={150}
minWidth={100}
type="rightAligned"
cellRenderer={({ data, node }: VegaICellRendererParams<Order>) => {
return data && isOrderAmendable(data) ? (
+1
View File
@@ -4,3 +4,4 @@ export * from './use-order-cancel';
export * from './use-order-submit';
export * from './use-order-edit';
export * from './use-order-event';
export * from './use-persisted-order';
@@ -0,0 +1,78 @@
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import produce from 'immer';
import { create } from 'zustand';
import { persist, subscribeWithSelector } from 'zustand/middleware';
import isEqual from 'lodash/isEqual';
import { useEffect } from 'react';
type OrderData = OrderSubmissionBody['orderSubmission'] | null;
type PersistedOrderStore = {
orders: OrderData[];
getOrder: (marketId: string) => OrderData | undefined;
setOrder: (order: OrderData) => void;
clear: () => void;
updatePrice: (marketId: string, price: string) => void;
};
export const usePersistedOrderStore = create<PersistedOrderStore>()(
persist(
subscribeWithSelector((set, get) => ({
orders: [],
getOrder: (marketId: string) => {
const current = get() as PersistedOrderStore;
const persisted = current.orders.find((o) => o?.marketId === marketId);
return persisted;
},
setOrder: (order: OrderData) => {
set(
produce((store: PersistedOrderStore) => {
const persisted = store.orders.find(
(o) => o?.marketId === order?.marketId
);
if (persisted) {
if (!isEqual(persisted, order)) {
Object.assign(persisted, order);
} else {
// NOOP
}
} else {
store.orders.push(order);
}
})
);
},
clear: () => set({ orders: [] }),
updatePrice: (marketId: string, price: string) =>
set(
produce((store: PersistedOrderStore) => {
const persisted = store.orders.find(
(o) => o?.marketId === marketId
);
if (persisted) {
persisted.price = price;
}
})
),
})),
{
name: 'VEGA_DEAL_TICKET_ORDER_STORE',
}
)
);
export const usePersistedOrderStoreSubscription = (
marketId: string,
onOrderChange: (order: NonNullable<OrderData>) => void
) => {
const selector = (state: PersistedOrderStore) =>
state.orders.find((o) => o?.marketId === marketId);
const action = (storedOrder: OrderData | undefined) => {
if (storedOrder) {
onOrderChange(storedOrder);
}
};
const unsubscribe = usePersistedOrderStore.subscribe(selector, action);
useEffect(() => () => unsubscribe(), [unsubscribe]);
};
+4 -7
View File
@@ -1,4 +1,4 @@
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useState } from 'react';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { marginsDataProvider } from './margin-data-provider';
@@ -22,19 +22,16 @@ const getMarketMarginPosition = ({
export const useMarketMargin = (marketId: string) => {
const { pubKey } = useVegaWallet();
const [marginLevel, setMarginLevel] = useState<string>('');
const variables = useMemo(() => {
return { partyId: pubKey || '' };
}, [pubKey]);
const update = useCallback(
({ data }: { data: MarginsQuery['party'] | null }) => {
const marginMarketPosition = getMarketMarginPosition({ data, marketId });
if (marginLevel !== marginMarketPosition?.maintenanceLevel) {
if (marginMarketPosition?.maintenanceLevel) {
setMarginLevel(marginMarketPosition?.maintenanceLevel || '');
}
return true;
},
[marginLevel, setMarginLevel, marketId]
[setMarginLevel, marketId]
);
useDataProvider<
@@ -42,7 +39,7 @@ export const useMarketMargin = (marketId: string) => {
MarginsSubscriptionSubscription['margins']
>({
dataProvider: marginsDataProvider,
variables,
variables: { partyId: pubKey || '' },
skip: !pubKey || !marketId,
update,
});
@@ -1,4 +1,4 @@
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useState } from 'react';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { positionsDataProvider } from './positions-data-providers';
import { useDataProvider } from '@vegaprotocol/react-helpers';
@@ -22,18 +22,15 @@ const getMarketPosition = ({
export const useMarketPositionOpenVolume = (marketId: string) => {
const { pubKey } = useVegaWallet();
const [openVolume, setOpenVolume] = useState<string>('');
const variables = useMemo(() => {
return { partyId: pubKey || '' };
}, [pubKey]);
const update = useCallback(
({ data }: { data: PositionsQuery['party'] | undefined }) => {
const position = getMarketPosition({ data, marketId });
if (openVolume !== position?.openVolume) {
if (position?.openVolume) {
setOpenVolume(position?.openVolume || '');
}
return true;
},
[openVolume, setOpenVolume, marketId]
[setOpenVolume, marketId]
);
useDataProvider<
@@ -41,7 +38,7 @@ export const useMarketPositionOpenVolume = (marketId: string) => {
PositionsSubscriptionSubscription['positions']
>({
dataProvider: positionsDataProvider,
variables,
variables: { partyId: pubKey || '' },
skip: !pubKey || !marketId,
update,
});

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