Compare commits

..
97 changed files with 2024 additions and 1978 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Related issues 🔗
Closes #[Issue number here]
Issue: #[Issue number here]
# Description
-1
View File
@@ -15,7 +15,6 @@ on:
- types
- utils
- i18n
- wallet
jobs:
publish:
+1 -1
View File
@@ -77,7 +77,7 @@
"executor": "nx:run-commands",
"options": {
"commands": [
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/spec-update-v0.72.0-preview.2/specs/v0.72.0-preview.2/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.72.3/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
]
}
},
@@ -0,0 +1,25 @@
fragment ExplorerStopOrderFields on StopOrder {
id
status
createdAt
trigger {
... on StopOrderPrice {
price
}
... on StopOrderTrailingPercentOffset {
trailingPercentOffset
}
}
createdAt
ocoLinkId
triggerDirection
order {
id
}
}
query ExplorerStopOrder($stopOrderId: ID!) {
stopOrder(id: $stopOrderId) {
...ExplorerStopOrderFields
}
}
@@ -0,0 +1,70 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerStopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, status: Types.StopOrderStatus, createdAt: any, ocoLinkId?: string | null, triggerDirection: Types.StopOrderTriggerDirection, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, order?: { __typename?: 'Order', id: string } | null };
export type ExplorerStopOrderQueryVariables = Types.Exact<{
stopOrderId: Types.Scalars['ID'];
}>;
export type ExplorerStopOrderQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, status: Types.StopOrderStatus, createdAt: any, ocoLinkId?: string | null, triggerDirection: Types.StopOrderTriggerDirection, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, order?: { __typename?: 'Order', id: string } | null } | null };
export const ExplorerStopOrderFieldsFragmentDoc = gql`
fragment ExplorerStopOrderFields on StopOrder {
id
status
createdAt
trigger {
... on StopOrderPrice {
price
}
... on StopOrderTrailingPercentOffset {
trailingPercentOffset
}
}
createdAt
ocoLinkId
triggerDirection
order {
id
}
}
`;
export const ExplorerStopOrderDocument = gql`
query ExplorerStopOrder($stopOrderId: ID!) {
stopOrder(id: $stopOrderId) {
...ExplorerStopOrderFields
}
}
${ExplorerStopOrderFieldsFragmentDoc}`;
/**
* __useExplorerStopOrderQuery__
*
* To run a query within a React component, call `useExplorerStopOrderQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerStopOrderQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerStopOrderQuery({
* variables: {
* stopOrderId: // value for 'stopOrderId'
* },
* });
*/
export function useExplorerStopOrderQuery(baseOptions: Apollo.QueryHookOptions<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>(ExplorerStopOrderDocument, options);
}
export function useExplorerStopOrderLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>(ExplorerStopOrderDocument, options);
}
export type ExplorerStopOrderQueryHookResult = ReturnType<typeof useExplorerStopOrderQuery>;
export type ExplorerStopOrderLazyQueryHookResult = ReturnType<typeof useExplorerStopOrderLazyQuery>;
export type ExplorerStopOrderQueryResult = Apollo.QueryResult<ExplorerStopOrderQuery, ExplorerStopOrderQueryVariables>;
@@ -33,7 +33,7 @@ describe('Order TX Summary component', () => {
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
});
it('Renders nothing if the order passed lacks a price', () => {
it('Renders "Market Price" if the order passed lacks a price', () => {
const o: Order = {
marketId: '123',
side: 'SIDE_BUY',
@@ -41,7 +41,7 @@ describe('Order TX Summary component', () => {
size: '10',
};
const res = renderComponent(o);
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
expect(res.queryByText('Market Price')).toBeInTheDocument();
});
it('Renders nothing if the order has an unspecified side', () => {
@@ -3,6 +3,7 @@ import type { components } from '../../../types/explorer';
import PriceInMarket from '../price-in-market/price-in-market';
import { sideText } from '../order-details/lib/order-labels';
import SizeInMarket from '../size-in-market/size-in-market';
import { t } from '@vegaprotocol/i18n';
export type OrderSummaryProps = {
order: components['schemas']['v1OrderSubmission'];
@@ -20,7 +21,6 @@ const OrderTxSummary = ({ order }: OrderSummaryProps) => {
if (
!order ||
!order.marketId ||
!order.price ||
!order.side ||
order.side === 'SIDE_UNSPECIFIED'
) {
@@ -36,10 +36,14 @@ const OrderTxSummary = ({ order }: OrderSummaryProps) => {
'-'
)}
&nbsp;<i className="text-xs">@</i>&nbsp;
<PriceInMarket
marketId={order.marketId}
price={order.price}
></PriceInMarket>
{order.price ? (
<PriceInMarket
marketId={order.marketId}
price={order.price}
></PriceInMarket>
) : (
t('Market Price')
)}
</div>
);
};
@@ -0,0 +1,139 @@
import { t } from '@vegaprotocol/i18n';
import type { components } from '../../../../../types/explorer';
import { formatNumberPercentage } from '@vegaprotocol/utils';
import BigNumber from 'bignumber.js';
import PriceInMarket from '../../../price-in-market/price-in-market';
import StopOrderTriggerSummary from './stop-order-trigger';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import fromUnixTime from 'date-fns/fromUnixTime';
const wrapperClasses =
'flex-1 max-w-xs items-center border border-vega-light-200 dark:border-vega-dark-150 rounded-md pv-2 ph-5 mb-5';
export type StopOrderType = 'RisesAbove' | 'FallsBelow' | 'OCO';
type V1OrderSetup = components['schemas']['v1StopOrderSetup'];
interface StopOrderSetupProps extends V1OrderSetup {
type: StopOrderType;
deterministicId: string;
}
export function getExpiryTypeLabel(
expiryStrategy: V1OrderSetup['expiryStrategy']
): string {
switch (expiryStrategy) {
case 'EXPIRY_STRATEGY_CANCELS':
return t('Cancels');
case 'EXPIRY_STRATEGY_SUBMIT':
return t('Submit');
}
return expiryStrategy || t('Unknown');
}
export interface ExpiryTriggerProps {
trailingPercentOffset?: string;
price?: string;
marketId?: string;
}
export function ExpiryTrigger({
trailingPercentOffset,
price,
marketId,
}: ExpiryTriggerProps) {
if (price && marketId) {
return <PriceInMarket price={price} marketId={marketId} />;
}
if (trailingPercentOffset) {
return (
<span>
{formatNumberPercentage(new BigNumber(trailingPercentOffset))}{' '}
(trailing)
</span>
);
}
return null;
}
export function getMovePrefix(
type: StopOrderType,
trailingPercentOffset?: string
): string {
if (type === 'RisesAbove') {
if (trailingPercentOffset) {
return '+';
} else {
return '>';
}
} else {
if (trailingPercentOffset) {
return '-';
} else {
return '<';
}
}
}
export const TypeLabel = {
RisesAbove: t('Rises above ↗'),
FallsBelow: t('Falls below ↘'),
OCO: '',
};
/**
*/
export const StopOrderSetup = ({
type,
price,
orderSubmission,
expiresAt,
expiryStrategy,
trailingPercentOffset,
deterministicId,
}: StopOrderSetupProps) => {
let d = 'Unknown';
try {
d = expiresAt
? fromUnixTime(parseInt(expiresAt) / 1000000000).toLocaleString()
: t('Unknown');
} catch (e) {
d = t('Unknown');
}
return (
<div className={wrapperClasses}>
<div className="mb-12 lg:mb-0">
<div className="bg-slate-100 text-slate-900 px-6 py-2 md:px-6 flex">
<div className="flex-1">
<strong className="font-bold mb-1">{TypeLabel[type]} </strong>
<p className=" font-xs mb-0">
{getMovePrefix(type, trailingPercentOffset)}
<ExpiryTrigger
trailingPercentOffset={trailingPercentOffset}
price={price}
marketId={orderSubmission?.marketId}
/>
</p>
</div>
{expiresAt && expiryStrategy ? (
<div className="flex-1">
<strong className="font-bold mb-1">{t('Expiry Type')}</strong>
<p className=" font-xs mb-0">
<Tooltip description={<span>{d}</span>}>
<span>{getExpiryTypeLabel(expiryStrategy)}</span>
</Tooltip>
</p>
</div>
) : null}
</div>
<StopOrderTriggerSummary
id={deterministicId}
orderSubmission={orderSubmission}
/>
</div>
</div>
);
};
@@ -0,0 +1,108 @@
import { StopOrderStatus } from '@vegaprotocol/types';
import { useExplorerStopOrderQuery } from '../../../order-details/__generated__/StopOrder';
import type { ExplorerStopOrderQuery } from '../../../order-details/__generated__/StopOrder';
import { t } from '@vegaprotocol/i18n';
import { Icon } from '@vegaprotocol/ui-toolkit';
import type { IconName } from '@vegaprotocol/ui-toolkit';
import OrderTxSummary from '../../../order-summary/order-tx-summary';
import type { components } from '../../../../../types/explorer';
import type { ApolloError } from '@apollo/client';
export const StatusLabel: Record<StopOrderStatus, string> = {
[StopOrderStatus.STATUS_CANCELLED]: t('Cancelled'),
[StopOrderStatus.STATUS_EXPIRED]: t('Expired'),
[StopOrderStatus.STATUS_PENDING]: t('Pending'),
[StopOrderStatus.STATUS_REJECTED]: t('Rejected'),
[StopOrderStatus.STATUS_STOPPED]: t('Stopped'),
[StopOrderStatus.STATUS_TRIGGERED]: t('Triggered'),
[StopOrderStatus.STATUS_UNSPECIFIED]: t('Status unknown'),
};
export const StatusIcon: Record<StopOrderStatus, IconName> = {
[StopOrderStatus.STATUS_CANCELLED]: 'disable',
[StopOrderStatus.STATUS_EXPIRED]: 'outdated',
[StopOrderStatus.STATUS_PENDING]: 'circle',
[StopOrderStatus.STATUS_REJECTED]: 'cross',
[StopOrderStatus.STATUS_STOPPED]: 'stop',
[StopOrderStatus.STATUS_TRIGGERED]: 'tick',
[StopOrderStatus.STATUS_UNSPECIFIED]: 'help',
};
export const StatusMidColor: Record<StopOrderStatus, string> = {
[StopOrderStatus.STATUS_CANCELLED]: 'bg-red-100 text-red-900',
[StopOrderStatus.STATUS_EXPIRED]: 'bg-red-100 text-red-900',
[StopOrderStatus.STATUS_PENDING]: 'bg-yellow-100 text-yellow-900',
[StopOrderStatus.STATUS_REJECTED]: 'bg-red-100 text-red-900',
[StopOrderStatus.STATUS_STOPPED]: 'bg-red-100 text-red-900',
[StopOrderStatus.STATUS_TRIGGERED]: 'bg-green-100 text-green-900',
[StopOrderStatus.STATUS_UNSPECIFIED]: 'bg-yellow-100 text-yellow-900',
};
export const StatusBottomColor: Record<StopOrderStatus, string> = {
[StopOrderStatus.STATUS_CANCELLED]: 'bg-red-50 text-red-900 line-through',
[StopOrderStatus.STATUS_EXPIRED]: 'bg-red-50 text-red-900 line-through',
[StopOrderStatus.STATUS_PENDING]: 'bg-yellow-50 text-yellow-900',
[StopOrderStatus.STATUS_REJECTED]: 'bg-red-50 text-red-900 line-through',
[StopOrderStatus.STATUS_STOPPED]: 'bg-red-50 text-red-900 line-through',
[StopOrderStatus.STATUS_TRIGGERED]: 'bg-green-50 text-green-900',
[StopOrderStatus.STATUS_UNSPECIFIED]:
'bg-yellow-50 text-yellow-900 line-through',
};
export function getStopOrderTriggerStatus(
data?: ExplorerStopOrderQuery,
error?: ApolloError
) {
if (data && data.stopOrder) {
return data.stopOrder.status;
}
return StopOrderStatus.STATUS_UNSPECIFIED;
}
export interface StopOrderTriggerSummaryProps {
id: string;
orderSubmission?: components['schemas']['v1OrderSubmission'];
}
/**
*/
const StopOrderTriggerSummary = ({
id,
orderSubmission,
}: StopOrderTriggerSummaryProps) => {
const { data, error } = useExplorerStopOrderQuery({
variables: { stopOrderId: id },
});
const status = getStopOrderTriggerStatus(data, error);
return (
<>
<div
className={`${StatusMidColor[status]} px-3 py-2 md:px-6 flex space-x-4`}
>
<p className="m-0 p-0 align-top">
<Icon
size={6}
name={StatusIcon[status]}
className="inline-block mr-2"
/>
<span className="align-top">{StatusLabel[status]}</span>
</p>
</div>
<div
className={`${StatusBottomColor[status]} px-3 py-2 md:px-6 flex space-x-4`}
>
{orderSubmission && (
<p className="text-vega-grey-400 strike">
<OrderTxSummary order={orderSubmission} />
</p>
)}
</div>
</>
);
};
export default StopOrderTriggerSummary;
@@ -26,6 +26,11 @@ export const sharedHeaderProps = {
className: 'align-top',
};
const Labels: Record<BlockExplorerTransactionResult['type'], string> = {
'Stop Orders Submission': 'Stop Order',
'Stop Orders Cancellation': 'Cancel Stop Order',
};
/**
* These rows are shown for every transaction type, providing a consistent set of rows for the top
* of a transaction details row. The order is relatively arbitrary but felt right - it might need to
@@ -44,12 +49,14 @@ export const TxDetailsShared = ({
const time: string = blockData?.result.block.header.time || '';
const height: string = blockData?.result.block.header.height || txData.block;
const type = Labels[txData.type] || txData.type;
return (
<>
{hideTypeRow === false ? (
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
<TableCell>{txData.type}</TableCell>
<TableCell>{type}</TableCell>
</TableRow>
) : null}
<TableRow modifier="bordered">
@@ -31,6 +31,8 @@ const AccountType: Record<AccountTypes, string> = {
ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: 'LP Received Fees',
ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: 'Market Proposers',
ACCOUNT_TYPE_HOLDING: 'Holding',
ACCOUNT_TYPE_LIQUIDITY_FEES_BONUS_DISTRIBUTION: 'Bonus Distribution',
ACCOUNT_TYPE_LP_LIQUIDITY_FEES: 'LP Liquidity Fees',
};
interface TransferParticipantsProps {
@@ -27,6 +27,7 @@ import { TxDetailsNodeAnnounce } from './tx-node-announce';
import { TxDetailsStateVariable } from './tx-state-variable-proposal';
import { TxProposal } from './tx-proposal';
import { TxDetailsTransfer } from './tx-transfer';
import { TxDetailsStopOrderSubmission } from './tx-stop-order-submission';
interface TxDetailsWrapperProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -116,6 +117,8 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
return TxDetailsUndelegate;
case 'State Variable Proposal':
return TxDetailsStateVariable;
case 'Stop Orders Submission':
return TxDetailsStopOrderSubmission;
case 'Transfer Funds':
return TxDetailsTransfer;
default:
@@ -0,0 +1,148 @@
import { t } from '@vegaprotocol/i18n';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import { MarketLink } from '../../links/';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import {
getStopOrderIds,
stopOrdersSignatureToDeterministicId,
} from '../lib/deterministic-ids';
import type { components } from '../../../../types/explorer';
import { StopOrderSetup } from './order/stop-order-setup';
type StopOrderSetup = components['schemas']['v1StopOrderSetup'];
interface TxDetailsOrderProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
export function getStopTypeLabel(
risesAbove: StopOrderSetup | undefined,
fallsBelow: StopOrderSetup | undefined
): string {
if (risesAbove && fallsBelow) {
return t('OCO (One Cancels Other)');
} else if (fallsBelow) {
return t('Falls Below ↘');
} else if (risesAbove) {
return t('Rises Above ↗');
} else {
return t('Stop Order');
}
}
export interface StopMarketIdProps {
risesAbove: StopOrderSetup | undefined;
fallsBelow: StopOrderSetup | undefined;
showMarketName?: boolean;
}
export function StopMarketId({
risesAbove,
fallsBelow,
showMarketName = false,
}: StopMarketIdProps) {
const raMarketId = risesAbove?.orderSubmission?.marketId;
const fbMarketId = fallsBelow?.orderSubmission?.marketId;
if (raMarketId && fbMarketId) {
if (raMarketId === fbMarketId) {
return <MarketLink id={raMarketId} showMarketName={showMarketName} />;
} else {
return (
<>
<MarketLink id={raMarketId} showMarketName={showMarketName} />,
<MarketLink id={fbMarketId} showMarketName={showMarketName} />
</>
);
}
} else if (raMarketId) {
return <MarketLink id={raMarketId} showMarketName={showMarketName} />;
} else if (fbMarketId) {
return <MarketLink id={fbMarketId} showMarketName={showMarketName} />;
} else {
return '-';
}
}
/**
*/
export const TxDetailsStopOrderSubmission = ({
txData,
pubKey,
blockData,
}: TxDetailsOrderProps) => {
if (!txData || !txData.command.stopOrdersSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const tx: components['schemas']['v1StopOrdersSubmission'] =
txData.command.stopOrdersSubmission;
const orderIds = stopOrdersSignatureToDeterministicId(
txData?.signature?.value
);
const { risesAboveId, fallsBelowId } = getStopOrderIds(
orderIds,
tx.risesAbove,
tx.fallsBelow
);
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
<TableRow modifier="bordered">
<TableCell>{t('Market ID')}</TableCell>
<TableCell>
<StopMarketId
risesAbove={tx.risesAbove}
fallsBelow={tx.fallsBelow}
/>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Market')}</TableCell>
<TableCell>
<StopMarketId
risesAbove={tx.risesAbove}
fallsBelow={tx.fallsBelow}
showMarketName={true}
/>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Trigger')}</TableCell>
<TableCell>
{getStopTypeLabel(tx.risesAbove, tx.fallsBelow)}
</TableCell>
</TableRow>
</TableWithTbody>
<div className="flex gap-2">
{tx.fallsBelow && fallsBelowId && (
<StopOrderSetup
type={'FallsBelow'}
{...tx.fallsBelow}
deterministicId={fallsBelowId}
/>
)}
{tx.risesAbove && risesAboveId && (
<StopOrderSetup
type={'RisesAbove'}
{...tx.risesAbove}
deterministicId={risesAboveId}
/>
)}
</div>
</>
);
};
@@ -1,4 +1,8 @@
import { hexToString, txSignatureToDeterministicId } from './deterministic-ids';
import {
hexToString,
txSignatureToDeterministicId,
stopOrdersSignatureToDeterministicId,
} from './deterministic-ids';
it('txSignatureToDeterministicId Turns a known signature in to a known deterministic ID', () => {
const signature =
@@ -20,3 +24,24 @@ it('hexToString encodes a known good value as bytes', () => {
const res = hexToString(hex);
expect(res).toEqual([14, 221]);
});
describe('stopOrdersSignatureToDeterministicId', () => {
it('should return empty object if no signature is provided', () => {
const result = stopOrdersSignatureToDeterministicId();
expect(result.length).toEqual(0);
});
it('should return valid deterministic ids if a signature is provided', () => {
const signature = 'deadb33f';
const result = stopOrdersSignatureToDeterministicId(signature);
expect(result.length).toEqual(2);
expect(result[0]).toBe(
'4c45b67a8c08cbf1982883a75beaf309bf172461d04bd427623d6cd3d9ab0e91'
);
expect(result[1]).toBe(
'afe7509ff90d8f26339a0ab81e4d3e1fb6c4d44e94419aa5e13ae7659d894da1'
);
});
});
@@ -1,4 +1,6 @@
import type { components } from '../../../../types/explorer';
import { sha3_256 } from 'js-sha3';
type StopOrderSetup = components['schemas']['v1StopOrderSetup'];
/**
* Encodes a string as bytes
@@ -37,3 +39,58 @@ export function txSignatureToDeterministicId(signature: string): string {
return hash.hex();
}
/**
* Given a stop order signature string, returns the deterministic IDs of both potential
* Stop Orders. A stop order is not an order per se, but a trigger for an order. The order
* created by the stop order will have another ID based on the market event that hit the
* trigger, and as such is not deterministic.
*
* @param signature
* @returns string[]
*/
export function stopOrdersSignatureToDeterministicId(
signature?: string
): string[] {
if (!signature) {
return [];
}
const firstId = txSignatureToDeterministicId(signature);
return [firstId, txSignatureToDeterministicId(firstId)];
}
export type stopSignatures = {
risesAboveId: string | undefined;
fallsBelowId: string | undefined;
};
/**
* In 0.72.10 the way stop order IDs are determined is a little tricky. It will be stabilised
* in a future release.
* @param deterministicIds Output of stopORdersSignatureToDeterministicId
* @param risesAbove Stop order setup
* @param fallsBelow Stop order setup
* @returns Object containing the deterministic IDs of the stop orders
*/
export function getStopOrderIds(
deterministicIds: string[],
risesAbove: StopOrderSetup | undefined,
fallsBelow: StopOrderSetup | undefined
) {
if (risesAbove && fallsBelow) {
return {
risesAboveId: deterministicIds[0],
fallsBelowId: deterministicIds[1],
};
} else if (!fallsBelow && risesAbove) {
return {
risesAboveId: deterministicIds[0],
};
} else {
return {
fallsBelowId: deterministicIds[0] || undefined,
risesAboveId: deterministicIds[1] || undefined,
};
}
}
@@ -34,6 +34,7 @@ export type FilterOption =
| 'Protocol Upgrade'
| 'Register new Node'
| 'State Variable Proposal'
| 'Stop Orders Submission'
| 'Stop Orders Cancellation'
| 'Submit Oracle Data'
| 'Submit Order'
@@ -54,6 +55,7 @@ export const PrimaryFilterOptions: FilterOption[] = [
'Delegate',
'Liquidity Provision Order',
'Proposal',
'Stop Orders Submission',
'Stop Orders Cancellation',
'Submit Oracle Data',
'Submit Order',
@@ -50,6 +50,25 @@ const displayString: StringMap = {
'Stop Orders Cancellation': 'Cancel stop',
};
export function getLabelForStopOrderType(
orderType: string,
command: components['schemas']['v1InputData']
): string {
if (command.stopOrdersSubmission) {
if (
command.stopOrdersSubmission.risesAbove &&
command.stopOrdersSubmission.fallsBelow
) {
return 'Stop ⇅';
} else if (command.stopOrdersSubmission.risesAbove) {
return 'Stop ↗';
} else if (command.stopOrdersSubmission.fallsBelow) {
return 'Stop ↘';
}
}
return 'Stop';
}
export function getLabelForOrderType(
orderType: string,
command: components['schemas']['v1InputData']
@@ -185,6 +204,9 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
} else if (type === 'Order' && command) {
type = getLabelForOrderType(orderType, command);
colours = 'text-white dark-text-white bg-vega-blue dark:bg-vega-blue';
} else if (type === 'Stop' && command) {
type = getLabelForStopOrderType(orderType, command);
colours = 'text-white dark-text-white bg-vega-blue dark:bg-vega-blue';
}
if (type === 'Vote on Proposal') {
@@ -55,7 +55,7 @@ export const TxsInfiniteList = ({
}
return (
<div className="overflow-scroll">
<div>
<table className={className} data-testid="transactions-list">
<thead>
<tr className="w-full mb-3 text-vega-dark-300 uppercase text-left">
@@ -1,4 +1,34 @@
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
edges {
node {
externalData {
data {
signers {
signer {
... on ETHAddress {
address
}
... on PubKey {
key
}
}
}
data {
name
value
}
matchedSpecIds
broadcastAt
}
}
}
}
}
}
fragment ExplorerOracleDataSource on OracleSpec {
...ExplorerOracleDataConnection
dataSourceSpec {
spec {
id
@@ -34,6 +64,7 @@ fragment ExplorerOracleDataSource on OracleSpec {
key {
name
type
numberDecimalPlaces
}
conditions {
value
@@ -49,38 +80,6 @@ fragment ExplorerOracleDataSource on OracleSpec {
}
}
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
pageInfo {
hasNextPage
}
edges {
node {
externalData {
data {
signers {
signer {
... on ETHAddress {
address
}
... on PubKey {
key
}
}
}
data {
name
value
}
matchedSpecIds
broadcastAt
}
}
}
}
}
}
query ExplorerOracleSpecs {
oracleSpecsConnection(pagination: { first: 50 }) {
pageInfo {
@@ -97,6 +96,5 @@ query ExplorerOracleSpecs {
query ExplorerOracleSpecById($id: ID!) {
oracleSpec(oracleSpecId: $id) {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}
+38 -41
View File
@@ -3,24 +3,55 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } } };
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } } } } | null> | null } | null };
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
export const ExplorerOracleDataConnectionFragmentDoc = gql`
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
edges {
node {
externalData {
data {
signers {
signer {
... on ETHAddress {
address
}
... on PubKey {
key
}
}
}
data {
name
value
}
matchedSpecIds
broadcastAt
}
}
}
}
}
}
`;
export const ExplorerOracleDataSourceFragmentDoc = gql`
fragment ExplorerOracleDataSource on OracleSpec {
...ExplorerOracleDataConnection
dataSourceSpec {
spec {
id
@@ -56,6 +87,7 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
key {
name
type
numberDecimalPlaces
}
conditions {
value
@@ -70,40 +102,7 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
}
}
}
`;
export const ExplorerOracleDataConnectionFragmentDoc = gql`
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
pageInfo {
hasNextPage
}
edges {
node {
externalData {
data {
signers {
signer {
... on ETHAddress {
address
}
... on PubKey {
key
}
}
}
data {
name
value
}
matchedSpecIds
broadcastAt
}
}
}
}
}
}
`;
${ExplorerOracleDataConnectionFragmentDoc}`;
export const ExplorerOracleSpecsDocument = gql`
query ExplorerOracleSpecs {
oracleSpecsConnection(pagination: {first: 50}) {
@@ -149,11 +148,9 @@ export const ExplorerOracleSpecByIdDocument = gql`
query ExplorerOracleSpecById($id: ID!) {
oracleSpec(oracleSpecId: $id) {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}
${ExplorerOracleDataSourceFragmentDoc}
${ExplorerOracleDataConnectionFragmentDoc}`;
${ExplorerOracleDataSourceFragmentDoc}`;
/**
* __useExplorerOracleSpecByIdQuery__
@@ -1,23 +1,23 @@
import { render } from '@testing-library/react';
import { OracleData } from './oracle-data';
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
import { type ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
function renderComponent(data: ExplorerOracleDataConnectionFragment) {
type DataConnection = ExplorerOracleDataConnectionFragment['dataConnection'];
function renderComponent(
data: ExplorerOracleDataConnectionFragment['dataConnection']
) {
return <OracleData data={data} />;
}
describe('Oracle Data view', () => {
it('Renders nothing when data is null', () => {
const res = render(
renderComponent(null as unknown as ExplorerOracleDataConnectionFragment)
);
const res = render(renderComponent(null as unknown as DataConnection));
expect(res.container).toBeEmptyDOMElement();
});
it('Renders nothing when dataConnection is empty', () => {
const res = render(
renderComponent({} as ExplorerOracleDataConnectionFragment)
);
const res = render(renderComponent({} as DataConnection));
expect(res.container).toBeEmptyDOMElement();
});
it('Renders nothing when dataConnection has no edges', () => {
@@ -26,7 +26,7 @@ describe('Oracle Data view', () => {
dataConnection: {
edges: null,
},
} as ExplorerOracleDataConnectionFragment)
} as DataConnection)
);
expect(res.container).toBeEmptyDOMElement();
});
@@ -37,7 +37,7 @@ describe('Oracle Data view', () => {
dataConnection: {
edges: [],
},
} as unknown as ExplorerOracleDataConnectionFragment)
} as unknown as DataConnection)
);
expect(res.container).toBeEmptyDOMElement();
});
@@ -47,20 +47,18 @@ describe('Oracle Data view', () => {
it('Renders details component when there is data', () => {
const res = render(
renderComponent({
dataConnection: {
edges: [
{
node: {
externalData: {
data: {
broadcastAt: '2022-01-01',
},
edges: [
{
node: {
externalData: {
data: {
broadcastAt: '2022-01-01',
},
},
},
],
},
} as ExplorerOracleDataConnectionFragment)
},
],
} as DataConnection)
);
expect(res.getByText('Broadcast data')).toBeInTheDocument();
});
@@ -4,7 +4,7 @@ import filter from 'recursive-key-filter';
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
interface OracleDataTypeProps {
data: ExplorerOracleDataConnectionFragment;
data: ExplorerOracleDataConnectionFragment['dataConnection'];
}
/**
@@ -14,12 +14,7 @@ interface OracleDataTypeProps {
* that Does The Job, rather than because it's good.
*/
export function OracleData({ data }: OracleDataTypeProps) {
if (
!data ||
!data.dataConnection ||
!data.dataConnection.edges?.length ||
data.dataConnection.edges.length > 1
) {
if (!data || !data.edges?.length || data.edges.length > 1) {
return null;
}
@@ -27,7 +22,7 @@ export function OracleData({ data }: OracleDataTypeProps) {
<details data-testid="oracle-data">
<summary>{t('Broadcast data')}</summary>
<ul>
{data.dataConnection.edges.map((d) => {
{data.edges.map((d) => {
if (!d) {
return null;
}
@@ -27,8 +27,8 @@ export function OracleMarkets({ id }: OracleMarketsProps) {
const m = markets.find((m) => {
const p = m.tradableInstrument.instrument.product;
if (
p.dataSourceSpecForSettlementData.id === id ||
p.dataSourceSpecForTradingTermination.id === id
p?.dataSourceSpecForSettlementData?.id === id ||
p?.dataSourceSpecForTradingTermination?.id === id
) {
return true;
}
@@ -36,15 +36,9 @@ export function OracleMarkets({ id }: OracleMarketsProps) {
});
if (m && m.id) {
const type =
id ===
m.tradableInstrument.instrument.product.dataSourceSpecForSettlementData
.id
? 'Settlement for'
: 'Termination for';
return (
<TableRow modifier="bordered">
<TableHeader scope="row">{type}</TableHeader>
<TableHeader scope="row">{getLabel(id, m)}</TableHeader>
<TableCell modifier="bordered" data-testid={`m-${m.id}`}>
<MarketLink id={m.id} />
</TableCell>
@@ -61,3 +55,14 @@ export function OracleMarkets({ id }: OracleMarketsProps) {
</TableRow>
);
}
export function getLabel(
id: string,
m: ExplorerOracleForMarketsMarketFragment | null
): string {
const settlementId =
m?.tradableInstrument?.instrument?.product?.dataSourceSpecForSettlementData
?.id || null;
return id === settlementId ? 'Settlement for' : 'Termination for';
}
@@ -22,7 +22,7 @@ export type SourceType =
interface OracleDetailsProps {
id: string;
dataSource: ExplorerOracleDataSourceFragment;
dataConnection?: ExplorerOracleDataConnectionFragment;
dataConnection: ExplorerOracleDataConnectionFragment['dataConnection'];
// Defaults to false. Hides the count of 'broadcasts' this oracle has seen
showBroadcasts?: boolean;
}
@@ -41,8 +41,7 @@ export const OracleDetails = ({
showBroadcasts = false,
}: OracleDetailsProps) => {
const sourceType = dataSource.dataSourceSpec.spec.data.sourceType;
const reportsCount: number =
dataConnection?.dataConnection.edges?.length || 0;
const reportsCount: number = dataConnection.edges?.length || 0;
return (
<div>
@@ -34,11 +34,15 @@ const Oracles = () => {
if (!id) {
return null;
}
const dataConnection = o?.node.dataConnection;
return (
<div id={id} key={id} className="mb-10">
<OracleDetails
id={id}
dataSource={o?.node}
dataConnection={dataConnection}
showBroadcasts={false}
/>
<details>
@@ -39,7 +39,7 @@ export const Oracle = () => {
<OracleDetails
id={id || ''}
dataSource={data?.oracleSpec}
dataConnection={data?.oracleSpec}
dataConnection={data?.oracleSpec.dataConnection}
showBroadcasts={true}
/>
<details>
+5 -1
View File
@@ -920,6 +920,8 @@ export interface components {
* - ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: Per asset reward account for fees received by liquidity providers
* - ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: Per asset reward account for market proposers when the market goes above some trading threshold
* - ACCOUNT_TYPE_HOLDING: Per asset account for holding in-flight unfilled orders' funds
* - ACCOUNT_TYPE_LP_LIQUIDITY_FEES: Network controlled liquidity provider's account, per market, to hold accrued liquidity fees.
* - ACCOUNT_TYPE_LIQUIDITY_FEES_BONUS_DISTRIBUTION: Network controlled liquidity fees bonus distribution account, per market.
* @default ACCOUNT_TYPE_UNSPECIFIED
* @enum {string}
*/
@@ -941,7 +943,9 @@ export interface components {
| 'ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES'
| 'ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES'
| 'ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS'
| 'ACCOUNT_TYPE_HOLDING';
| 'ACCOUNT_TYPE_HOLDING'
| 'ACCOUNT_TYPE_LP_LIQUIDITY_FEES'
| 'ACCOUNT_TYPE_LIQUIDITY_FEES_BONUS_DISTRIBUTION';
/** Vega representation of an external asset */
readonly vegaAssetDetails: {
/** @description Vega built-in asset. */
-4
View File
@@ -20,10 +20,6 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
NX_SUCCESSOR_MARKETS=true
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
#Test configuration variables
CYPRESS_FAIRGROUND=false
CYPRESS_VEGA_URL=http://localhost:3008/graphql
@@ -11,6 +11,9 @@ import {
getDateFormatForSpecifiedDays,
getProposalFromTitle,
getProposalInformationFromTable,
goToMakeNewProposal,
governanceProposalType,
longProposalDescription,
proposalChangeType,
submitUniqueRawProposal,
validateProposalDetailsDiff,
@@ -43,7 +46,6 @@ const proposalDetailsTitle = 'proposal-title';
const proposalDetailsDescription = 'proposal-description';
const openProposals = 'open-proposals';
const viewProposalButton = 'view-proposal-btn';
const proposalDescriptionToggle = 'proposal-description-toggle';
const voteBreakdownToggle = 'vote-breakdown-toggle';
const proposalTermsToggle = 'proposal-json-toggle';
const marketDataToggle = 'proposal-market-data-toggle';
@@ -71,10 +73,13 @@ describe(
// 3001-VOTE-050 3001-VOTE-054 3001-VOTE-055 3002-PROP-019
it('Newly created raw proposal details - shows proposal title and full description', function () {
const proposalDescription =
'I propose that everyone evaluate the following IPFS document and vote Yes if they agree. bafybeigwwctpv37xdcwacqxvekr6e4kaemqsrv34em6glkbiceo3fcy4si';
const proposalDetails = longProposalDescription;
createRawProposal();
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({
proposalTitle: 'raw proposal with long description',
proposalDescription: proposalDetails,
});
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
cy.getByTestId(openProposals).within(() => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
@@ -85,12 +90,17 @@ describe(
'contain.text',
rawProposal.rationale.title
);
cy.getByTestId(proposalDescriptionToggle).click();
cy.getByTestId('proposal-description-toggle');
cy.getByTestId(proposalDetailsDescription)
.find('p')
.should('have.text', proposalDescription);
});
cy.getByTestId(proposalDetailsDescription).within(() => {
cy.get('p').should('not.have.text', 'Hyperlink text');
cy.getByTestId('show-more-btn').click();
cy.get('p')
.invoke('text')
.should('have.have.length', 2194) // Full description is displayed
.and('contain', 'Hyperlink text');
cy.get('a').should('have.attr', 'href');
});
// 3001-VOTE-008
getProposalInformationFromTable('ID')
.invoke('text')
@@ -361,34 +371,6 @@ describe(
stakingPageDisassociateAllTokens();
});
it('Error message should be displayed if error returned from wallet when voting', function () {
const errorMsg =
'Application error: party has already submitted the maximum number of transactions of this type per epoch (3)';
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
);
cy.intercept('POST', '/api/v2/requests', {
jsonrpc: '2.0',
error: {
code: 2001,
message: 'Application error',
data: 'party has already submitted the maximum number of transactions of this type per epoch (3)',
},
id: '-PK5EGmErnjLhAmzMeclC',
});
cy.contains('Vote breakdown').should('be.visible', { timeout: 10000 });
cy.getByTestId('vote-buttons').contains('for').click();
cy.getByTestId('dialog-title').should(
'have.text',
'Transaction failed'
);
cy.getByTestId('Error').should('have.text', errorMsg);
});
});
it('Able to see successor market details with new and updated values', function () {
cy.createMarket();
cy.reload();
@@ -425,7 +407,8 @@ describe(
// 3003-PMAN-011 3003-PMAN-012
cy.getByTestId(marketDataToggle).click();
cy.getByTestId('proposal-market-data').within(() => {
cy.contains('Key details').click();
// Assert that all toggles are removed
cy.getByTestId('accordion-toggle').should('not.exist');
validateProposalDetailsDiff(
'Name',
proposalChangeType.UPDATED,
@@ -449,7 +432,6 @@ describe(
'Opening auction'
);
cy.contains('Instrument').click();
validateProposalDetailsDiff(
'Market Name',
proposalChangeType.UPDATED,
@@ -457,7 +439,6 @@ describe(
'Test market 1'
);
cy.contains('Metadata').click();
validateProposalDetailsDiff(
'Sector',
proposalChangeType.UPDATED,
@@ -274,3 +274,6 @@ export enum proposalChangeType {
UPDATED = 'Updated',
ADDED = 'Added',
}
export const longProposalDescription =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam bibendum orci augue, vel imperdiet augue ultrices sed. In hac habitasse platea dictumst. Sed eget elit vitae nisl tincidunt faucibus. Donec pellentesque mauris nec viverra blandit. Aenean eros diam, tempor eu luctus nec, rhoncus non massa. Mauris libero diam, mattis et enim ut, lobortis pharetra elit. Phasellus vel metus accumsan, rhoncus tellus finibus, blandit mi. In sollicitudin ex ac tortor ornare interdum. Sed est ipsum, vestibulum eget dolor vel, porta luctus elit. Fusce justo nibh, placerat eget sollicitudin eleifend, rhoncus id lorem. Fusce vitae magna vel urna faucibus accumsan quis id purus.\nPraesent convallis dolor sed ante ultricies tempor. Proin sed risus ut libero euismod semper. Duis quis quam sed lacus viverra blandit vel scelerisque diam. Donec interdum, ipsum eget imperdiet ornare, risus augue faucibus lectus, ullamcorper scelerisque erat sapien in purus. Nunc molestie tincidunt felis dignissim vestibulum. Quisque quis ornare enim, non dignissim lectus. Mauris mollis, massa ut maximus consectetur, sem mi lobortis quam, vel malesuada eros tortor nec ex. Cras ac nunc sed erat malesuada varius a quis nulla. Curabitur cursus nec sem sit amet aliquet. Ut tristique tortor neque, a dignissim lectus dictum vel. Praesent sollicitudin bibendum vulputate.\nAenean bibendum tristique diam laoreet posuere. Curabitur ornare lectus ut diam ultricies, ut sodales eros lacinia. Maecenas mauris turpis, gravida non arcu ac, interdum auctor sapien. Vestibulum sed tortor quam. Interdum et malesuada fames ac ante ipsum primis in faucibus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec finibus pulvinar magna, non laoreet lectus molestie nec. Vestibulum tempus mattis vehicula. Praesent in orci lectus. In commodo sollicitudin lacus, et lobortis eros placerat vitae. Proin mi libero, feugiat id pretium posuere, rhoncus ut augue. Cras massa tortor, rutrum sed ex vitae, posuere pretium augue. Donec pellentesque suscipit dignissim. Vivamus convallis a odio vitae sodales. Nullam non eleifend mauris, sed iaculis lectus. Cras facilisis justo at ante.\n[Hyperlink text](https://dweb.link/ipfs/bafybeigwwctpv37xdcwacqxvekr6e4kaemqsrv34em6glkbiceo3fcy4si)';
-2
View File
@@ -22,8 +22,6 @@ NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
#Test configuration variables
CYPRESS_FAIRGROUND=false
+4 -37
View File
@@ -41,7 +41,6 @@ import {
AppFailure,
NodeSwitcherDialog,
useNodeSwitcherStore,
DocsLinks,
} from '@vegaprotocol/environment';
import { ENV } from './config';
import type { InMemoryCacheConfig } from '@apollo/client';
@@ -110,17 +109,8 @@ const Web3Container = ({
store.connectors,
store.initialize,
]);
const {
ETHEREUM_PROVIDER_URL,
ETH_LOCAL_PROVIDER_URL,
ETH_WALLET_MNEMONIC,
VEGA_ENV,
VEGA_URL,
VEGA_EXPLORER_URL,
CHROME_EXTENSION_URL,
MOZILLA_EXTENSION_URL,
VEGA_WALLET_URL,
} = useEnvironment();
const { ETHEREUM_PROVIDER_URL, ETH_LOCAL_PROVIDER_URL, ETH_WALLET_MNEMONIC } =
useEnvironment();
useEffect(() => {
if (chainId) {
return initializeConnectors(
@@ -149,33 +139,10 @@ const Web3Container = ({
return <SplashLoader />;
}
if (
!VEGA_URL ||
!VEGA_WALLET_URL ||
!VEGA_EXPLORER_URL ||
!DocsLinks ||
!CHROME_EXTENSION_URL ||
!MOZILLA_EXTENSION_URL
) {
return null;
}
return (
<Web3Provider connectors={connectors}>
<Web3Connector connectors={connectors} chainId={Number(chainId)}>
<VegaWalletProvider
config={{
network: VEGA_ENV,
vegaUrl: VEGA_URL,
vegaWalletServiceUrl: VEGA_WALLET_URL,
links: {
explorer: VEGA_EXPLORER_URL,
concepts: DocsLinks?.VEGA_WALLET_CONCEPTS_URL,
chromeExtensionUrl: CHROME_EXTENSION_URL,
mozillaExtensionUrl: MOZILLA_EXTENSION_URL,
},
}}
>
<VegaWalletProvider>
<ContractsProvider>
<AppLoader>
<BalanceManager>
@@ -308,7 +275,7 @@ const AppContainer = () => {
<Router>
<ScrollToTop />
<AppStateProvider>
<div className="min-h-full text-white grid">
<div className="grid min-h-full text-white">
<NodeGuard
skeleton={<div>{t('Loading')}</div>}
failure={
+5 -4
View File
@@ -1,4 +1,4 @@
import { FLAGS } from '@vegaprotocol/environment';
import { ENV } from '@vegaprotocol/environment';
import {
JsonRpcConnector,
ViewConnector,
@@ -13,9 +13,10 @@ export const jsonRpc = new JsonRpcConnector();
export const injected = new InjectedConnector();
export const view = new ViewConnector(urlParams.get('address'));
export const snap = FLAGS.METAMASK_SNAPS
? new SnapConnector(DEFAULT_SNAP_ID)
: undefined;
export const snap = new SnapConnector(
ENV.VEGA_URL ? new URL(ENV.VEGA_URL).origin : undefined,
DEFAULT_SNAP_ID
);
export const Connectors = {
injected,
@@ -1,7 +1,6 @@
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import { VegaWalletProvider } from '@vegaprotocol/wallet';
import type { VegaWalletConfig } from '@vegaprotocol/wallet';
import { render, screen } from '@testing-library/react';
import { generateProposal } from '../../test-helpers/generate-proposals';
import { Proposal } from './proposal';
@@ -44,23 +43,11 @@ jest.mock('../list-asset', () => ({
ListAsset: () => <div data-testid="proposal-list-asset"></div>,
}));
const vegaWalletConfig: VegaWalletConfig = {
network: 'TESTNET',
vegaUrl: 'https://vega.xyz',
vegaWalletServiceUrl: 'https://wallet.vega.xyz',
links: {
explorer: 'explorer',
concepts: 'concepts',
chromeExtensionUrl: 'chrome',
mozillaExtensionUrl: 'mozilla',
},
};
const renderComponent = (proposal: ProposalQuery['proposal']) => {
render(
<MemoryRouter>
<MockedProvider>
<VegaWalletProvider config={vegaWalletConfig}>
<VegaWalletProvider>
<Proposal
restData={{}}
proposal={proposal as ProposalQuery['proposal']}
@@ -4,7 +4,6 @@ import { VoteButtons } from './vote-buttons';
import { VoteState } from './use-user-vote';
import { ProposalState } from '@vegaprotocol/types';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { mockWalletContext } from '../../test-helpers/mocks';
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
import { MockedProvider } from '@apollo/react-testing';
@@ -68,7 +67,7 @@ describe('Vote buttons', () => {
disconnect: jest.fn(),
selectPubKey: jest.fn(),
connector: null,
} as unknown as VegaWalletContextShape;
};
render(
<AppStateProvider>
@@ -114,7 +114,6 @@ describe('Raw proposal form', () => {
{
pubKey,
sendTx: mockSendTx,
links: { explorer: 'explorer' },
} as unknown as VegaWalletContextShape
}
>
@@ -1,7 +1,7 @@
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
import type { MockedResponse } from '@apollo/client/testing';
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
import type { PubKey, VegaWalletContextShape } from '@vegaprotocol/wallet';
import type { PubKey } from '@vegaprotocol/wallet';
import type { VoteValue } from '@vegaprotocol/types';
import type { UserVoteQuery } from '../components/vote-details/__generated__/Vote';
import { UserVoteDocument } from '../components/vote-details/__generated__/Vote';
@@ -21,7 +21,7 @@ export const mockWalletContext = {
disconnect: jest.fn(),
selectPubKey: jest.fn(),
connector: null,
} as unknown as VegaWalletContextShape;
};
const mockEthereumConfig = {
network_id: '3',
@@ -1,59 +1,25 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { ledgerEntriesQuery } from '@vegaprotocol/mock';
import { partyAssetsQuery } from '@vegaprotocol/mock';
describe('Portfolio page', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.mockTradingPage();
cy.mockGQL((req) => {
aliasGQLQuery(req, 'LedgerEntries', ledgerEntriesQuery());
aliasGQLQuery(req, 'PartyAssets', partyAssetsQuery());
});
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
});
describe('Ledger entries', () => {
it('List should be properly rendered', () => {
it('Download form should be properly rendered', () => {
// 7007-LEEN-001
cy.visit('/#/portfolio');
cy.getByTestId('"Ledger entries"').click();
const headers = [
'Sender',
'Account type',
'Market',
'Receiver',
'Account type',
'Market',
'Transfer type',
'Quantity',
'Asset',
'Sender account balance',
'Receiver account balance',
'Vega time',
];
cy.getByTestId('tab-ledger-entries').within(($headers) => {
cy.wrap($headers)
.get('.ag-header-cell-text')
.each(($header, i) => {
cy.wrap($header).should('have.text', headers[i]);
});
.getByTestId('ledger-download-button')
.should('be.visible');
});
cy.get(
'[data-testid="tab-ledger-entries"] .ag-center-cols-container .ag-row'
).should('have.length', ledgerEntriesQuery().ledgerEntries.edges.length);
});
it('account filters should be callable', () => {
cy.visit('/#/portfolio');
cy.getByTestId('"Ledger entries"').click();
cy.get('[role="columnheader"][col-id="fromAccountType"]').realHover();
cy.get(
'[role="columnheader"][col-id="fromAccountType"] .ag-header-cell-menu-button'
).click();
cy.get('fieldset.ag-simple-filter-body-wrapper')
.should('be.visible')
.within((fields) => {
cy.wrap(fields).find('label').should('have.length', 18);
});
cy.getByTestId('"Ledger entries"').click();
cy.get('fieldset.ag-simple-filter-body-wrapper').should('not.exist');
});
});
});
-2
View File
@@ -12,8 +12,6 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
# Cosmic elevator flags
@@ -1,7 +1,6 @@
import type { InMemoryCacheConfig } from '@apollo/client';
import {
AppFailure,
DocsLinks,
NetworkLoader,
NodeGuard,
useEnvironment,
@@ -18,32 +17,16 @@ export const DynamicLoader = dynamic(() => import('../preloader/preloader'), {
});
export const AppLoader = ({ children }: { children: ReactNode }) => {
const {
error,
VEGA_URL,
VEGA_ENV,
VEGA_WALLET_URL,
VEGA_EXPLORER_URL,
MAINTENANCE_PAGE,
MOZILLA_EXTENSION_URL,
CHROME_EXTENSION_URL,
} = useEnvironment();
const { error, VEGA_URL, MAINTENANCE_PAGE } = useEnvironment((store) => ({
error: store.error,
VEGA_URL: store.VEGA_URL,
MAINTENANCE_PAGE: store.MAINTENANCE_PAGE,
}));
if (MAINTENANCE_PAGE) {
return <MaintenancePage />;
}
if (
!VEGA_URL ||
!VEGA_WALLET_URL ||
!VEGA_EXPLORER_URL ||
!CHROME_EXTENSION_URL ||
!MOZILLA_EXTENSION_URL ||
!DocsLinks
) {
return null;
}
return (
<NetworkLoader
cache={cacheConfig}
@@ -57,21 +40,7 @@ export const AppLoader = ({ children }: { children: ReactNode }) => {
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
>
<Web3Provider>
<VegaWalletProvider
config={{
network: VEGA_ENV,
vegaUrl: VEGA_URL,
vegaWalletServiceUrl: VEGA_WALLET_URL,
links: {
explorer: VEGA_EXPLORER_URL,
concepts: DocsLinks.VEGA_WALLET_CONCEPTS_URL,
chromeExtensionUrl: CHROME_EXTENSION_URL,
mozillaExtensionUrl: MOZILLA_EXTENSION_URL,
},
}}
>
{children}
</VegaWalletProvider>
<VegaWalletProvider>{children}</VegaWalletProvider>
</Web3Provider>
</NodeGuard>
</NetworkLoader>
@@ -1,23 +1,30 @@
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import { LedgerManager } from '@vegaprotocol/ledger';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { LedgerExportForm } from '@vegaprotocol/ledger';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { useEnvironment } from '@vegaprotocol/environment';
import type { PartyAssetFieldsFragment } from '@vegaprotocol/assets';
import { usePartyAssetsQuery } from '@vegaprotocol/assets';
export const LedgerContainer = () => {
const VEGA_URL = useEnvironment((store) => store.VEGA_URL);
const { pubKey } = useVegaWallet();
const gridStore = useLedgerStore((store) => store.gridStore);
const updateGridStore = useLedgerStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
const { data, loading } = usePartyAssetsQuery({
variables: { partyId: pubKey || '' },
skip: !pubKey,
});
const assets = (data?.party?.accountsConnection?.edges ?? [])
.map<PartyAssetFieldsFragment>(
(item) => item?.node?.asset ?? ({} as PartyAssetFieldsFragment)
)
.reduce((aggr, item) => {
if ('id' in item && 'symbol' in item) {
aggr[item.id as string] = item.symbol as string;
}
return aggr;
}, {} as Record<string, string>);
if (!pubKey) {
return (
<Splash>
@@ -26,11 +33,31 @@ export const LedgerContainer = () => {
);
}
return <LedgerManager partyId={pubKey} gridProps={gridStoreCallbacks} />;
};
if (!VEGA_URL) {
return (
<Splash>
<p>{t('Environment not configured')}</p>
</Splash>
);
}
const useLedgerStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_ledger_store',
})
);
if (loading) {
return (
<div className="relative flex items-center justify-center w-full h-full">
<Loader />
</div>
);
}
if (!Object.keys(assets).length) {
return (
<Splash>
<p>{t('No ledger entries to export')}</p>
</Splash>
);
}
return (
<LedgerExportForm partyId={pubKey} vegaUrl={VEGA_URL} assets={assets} />
);
};
@@ -9,8 +9,7 @@ import {
} from './sidebar';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { VegaWalletProvider } from '@vegaprotocol/wallet';
jest.mock('../node-health', () => ({
NodeHealthContainer: () => <span data-testid="node-health" />,
@@ -32,20 +31,16 @@ jest.mock('../welcome-dialog', () => ({
GetStarted: () => <div data-testid="get-started" />,
}));
const walletContext = {
pubKeys: [{ publicKey: 'pubkey' }],
} as VegaWalletContextShape;
describe('Sidebar', () => {
it.each(['/markets/all', '/portfolio'])(
'does not render ticket and info',
(path) => {
render(
<VegaWalletContext.Provider value={walletContext}>
<VegaWalletProvider>
<MemoryRouter initialEntries={[path]}>
<Sidebar />
</MemoryRouter>
</VegaWalletContext.Provider>
</VegaWalletProvider>
);
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
@@ -63,11 +58,11 @@ describe('Sidebar', () => {
it('renders ticket and info on market pages', () => {
render(
<VegaWalletContext.Provider value={walletContext}>
<VegaWalletProvider>
<MemoryRouter initialEntries={['/markets/ABC']}>
<Sidebar />
</MemoryRouter>
</VegaWalletContext.Provider>
</VegaWalletProvider>
);
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
@@ -84,11 +79,11 @@ describe('Sidebar', () => {
it('renders selected state', async () => {
render(
<VegaWalletContext.Provider value={walletContext}>
<VegaWalletProvider>
<MemoryRouter initialEntries={['/markets/ABC']}>
<Sidebar />
</MemoryRouter>
</VegaWalletContext.Provider>
</VegaWalletProvider>
);
const settingsButton = screen.getByTestId(ViewType.Settings);
@@ -112,13 +107,13 @@ describe('Sidebar', () => {
describe('SidebarContent', () => {
it('renders the correct content', () => {
const { container } = render(
<VegaWalletContext.Provider value={walletContext}>
<VegaWalletProvider>
<MemoryRouter initialEntries={['/markets/ABC']}>
<Routes>
<Route path="/markets/:marketId" element={<SidebarContent />} />
</Routes>
</MemoryRouter>
</VegaWalletContext.Provider>
</VegaWalletProvider>
);
expect(container).toBeEmptyDOMElement();
@@ -138,13 +133,13 @@ describe('SidebarContent', () => {
it('closes sidebar if market id is required but not present', () => {
const { container } = render(
<VegaWalletContext.Provider value={walletContext}>
<VegaWalletProvider>
<MemoryRouter initialEntries={['/portfolio']}>
<Routes>
<Route path="/portfolio" element={<SidebarContent />} />
</Routes>
</MemoryRouter>
</VegaWalletContext.Provider>
</VegaWalletProvider>
);
act(() => {
@@ -29,7 +29,6 @@ interface Props {
}
const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
const { CHROME_EXTENSION_URL, MOZILLA_EXTENSION_URL } = useEnvironment();
const navigate = useNavigate();
const [, setOnboardingViewed] = useLocalStorage(
constants.ONBOARDING_VIEWED_KEY
@@ -47,13 +46,7 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
openVegaWalletDialog();
};
if (step === OnboardingStep.ONBOARDING_WALLET_STEP) {
return (
<GetWalletButton
className="justify-between"
chromeExtensionUrl={CHROME_EXTENSION_URL}
mozillaExtensionUrl={MOZILLA_EXTENSION_URL}
/>
);
return <GetWalletButton className="justify-between" />;
} else if (step === OnboardingStep.ONBOARDING_CONNECT_STEP) {
buttonText = t('Connect');
} else if (step === OnboardingStep.ONBOARDING_DEPOSIT_STEP) {
+5 -4
View File
@@ -1,4 +1,4 @@
import { FLAGS } from '@vegaprotocol/environment';
import { ENV } from '@vegaprotocol/environment';
import {
JsonRpcConnector,
ViewConnector,
@@ -18,9 +18,10 @@ if (typeof window !== 'undefined') {
view = new ViewConnector();
}
export const snap = FLAGS.METAMASK_SNAPS
? new SnapConnector(DEFAULT_SNAP_ID)
: undefined;
export const snap = new SnapConnector(
ENV.VEGA_URL ? new URL(ENV.VEGA_URL).origin : undefined,
DEFAULT_SNAP_ID
);
export const Connectors = {
injected,
+23
View File
@@ -24,3 +24,26 @@ query Assets {
}
}
}
fragment PartyAssetFields on Asset {
id
name
symbol
status
}
query PartyAssets($partyId: ID!) {
party(id: $partyId) {
id
accountsConnection {
edges {
node {
type
asset {
...PartyAssetFields
}
}
}
}
}
}
+63 -1
View File
@@ -10,6 +10,15 @@ export type AssetsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type AssetsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string, status: Types.AssetStatus, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } } | null> | null } | null };
export type PartyAssetFieldsFragment = { __typename?: 'Asset', id: string, name: string, symbol: string, status: Types.AssetStatus };
export type PartyAssetsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type PartyAssetsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, asset: { __typename?: 'Asset', id: string, name: string, symbol: string, status: Types.AssetStatus } } } | null> | null } | null } | null };
export const AssetListFieldsFragmentDoc = gql`
fragment AssetListFields on Asset {
id
@@ -28,6 +37,14 @@ export const AssetListFieldsFragmentDoc = gql`
status
}
`;
export const PartyAssetFieldsFragmentDoc = gql`
fragment PartyAssetFields on Asset {
id
name
symbol
status
}
`;
export const AssetsDocument = gql`
query Assets {
assetsConnection {
@@ -65,4 +82,49 @@ export function useAssetsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<Ass
}
export type AssetsQueryHookResult = ReturnType<typeof useAssetsQuery>;
export type AssetsLazyQueryHookResult = ReturnType<typeof useAssetsLazyQuery>;
export type AssetsQueryResult = Apollo.QueryResult<AssetsQuery, AssetsQueryVariables>;
export type AssetsQueryResult = Apollo.QueryResult<AssetsQuery, AssetsQueryVariables>;
export const PartyAssetsDocument = gql`
query PartyAssets($partyId: ID!) {
party(id: $partyId) {
id
accountsConnection {
edges {
node {
type
asset {
...PartyAssetFields
}
}
}
}
}
}
${PartyAssetFieldsFragmentDoc}`;
/**
* __usePartyAssetsQuery__
*
* To run a query within a React component, call `usePartyAssetsQuery` and pass it any options that fit your needs.
* When your component renders, `usePartyAssetsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = usePartyAssetsQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function usePartyAssetsQuery(baseOptions: Apollo.QueryHookOptions<PartyAssetsQuery, PartyAssetsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<PartyAssetsQuery, PartyAssetsQueryVariables>(PartyAssetsDocument, options);
}
export function usePartyAssetsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PartyAssetsQuery, PartyAssetsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<PartyAssetsQuery, PartyAssetsQueryVariables>(PartyAssetsDocument, options);
}
export type PartyAssetsQueryHookResult = ReturnType<typeof usePartyAssetsQuery>;
export type PartyAssetsLazyQueryHookResult = ReturnType<typeof usePartyAssetsLazyQuery>;
export type PartyAssetsQueryResult = Apollo.QueryResult<PartyAssetsQuery, PartyAssetsQueryVariables>;
+47
View File
@@ -0,0 +1,47 @@
import merge from 'lodash/merge';
import type { PartyAssetsQuery } from './__generated__/Assets';
import * as Types from '@vegaprotocol/types';
import type { PartialDeep } from 'type-fest';
export const partyAssetsQuery = (
override?: PartialDeep<PartyAssetsQuery>
): PartyAssetsQuery => {
const defaultAssets: PartyAssetsQuery = {
party: {
__typename: 'Party',
id: 'partyId',
accountsConnection: {
edges: partyAccountFields.map((node) => ({
__typename: 'AccountEdge',
node,
})),
},
},
};
return merge(defaultAssets, override);
};
const partyAccountFields = [
{
__typename: 'AccountBalance',
type: Types.AccountType.ACCOUNT_TYPE_MARGIN,
asset: {
__typename: 'Asset',
id: 'asset-id',
symbol: 'tEURO',
name: 'Euro',
status: Types.AssetStatus.STATUS_ENABLED,
},
},
{
__typename: 'AccountBalance',
type: Types.AccountType.ACCOUNT_TYPE_MARGIN,
asset: {
__typename: 'Asset',
id: 'asset-id-2',
symbol: 'tDAI',
name: 'DAI',
status: Types.AssetStatus.STATUS_ENABLED,
},
},
] as const;
+1 -1
View File
@@ -2,6 +2,7 @@
export * from '../accounts/src/lib/accounts.mock';
export * from '../assets/src/lib/asset.mock';
export * from '../assets/src/lib/assets.mock';
export * from '../assets/src/lib/party-assets.mock';
export * from '../candles-chart/src/lib/candles.mock';
export * from '../candles-chart/src/lib/chart.mock';
export * from '../deal-ticket/src/hooks/estimate-order.mock';
@@ -10,7 +11,6 @@ export * from '../environment/src/utils/node.mock';
export * from '../environment/src/components/node-guard/node-guard.mock';
export * from '../fills/src/lib/fills.mock';
export * from '../proposals/src/lib/proposals-data-provider/proposals.mock';
export * from '../ledger/src/lib/ledger-entries.mock';
export * from '../market-depth/src/lib/market-depth.mock';
export * from '../markets/src/lib/components/market-info/market-info.mock';
export * from '../markets/src/lib/market-candles.mock';
+1 -2
View File
@@ -1,2 +1 @@
export * from './lib/ledger-manager';
export * from './lib/__generated__/LedgerEntries';
export * from './lib/ledger-export-form';
-47
View File
@@ -1,47 +0,0 @@
fragment LedgerEntry on AggregatedLedgerEntry {
vegaTime
quantity
assetId
transferType
toAccountType
toAccountMarketId
toAccountPartyId
toAccountBalance
fromAccountType
fromAccountMarketId
fromAccountPartyId
fromAccountBalance
}
query LedgerEntries(
$partyId: ID!
$pagination: Pagination
$dateRange: DateRange
$fromAccountType: [AccountType!]
$toAccountType: [AccountType!]
) {
ledgerEntries(
filter: {
FromAccountFilter: {
partyIds: [$partyId]
accountTypes: $fromAccountType
}
ToAccountFilter: { partyIds: [$partyId], accountTypes: $toAccountType }
}
pagination: $pagination
dateRange: $dateRange
) {
edges {
node {
...LedgerEntry
}
cursor
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
-88
View File
@@ -1,88 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type LedgerEntryFragment = { __typename?: 'AggregatedLedgerEntry', vegaTime: any, quantity: string, assetId?: string | null, transferType?: Types.TransferType | null, toAccountType?: Types.AccountType | null, toAccountMarketId?: string | null, toAccountPartyId?: string | null, toAccountBalance: string, fromAccountType?: Types.AccountType | null, fromAccountMarketId?: string | null, fromAccountPartyId?: string | null, fromAccountBalance: string };
export type LedgerEntriesQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
pagination?: Types.InputMaybe<Types.Pagination>;
dateRange?: Types.InputMaybe<Types.DateRange>;
fromAccountType?: Types.InputMaybe<Array<Types.AccountType> | Types.AccountType>;
toAccountType?: Types.InputMaybe<Array<Types.AccountType> | Types.AccountType>;
}>;
export type LedgerEntriesQuery = { __typename?: 'Query', ledgerEntries: { __typename?: 'AggregatedLedgerEntriesConnection', edges: Array<{ __typename?: 'AggregatedLedgerEntriesEdge', cursor: string, node: { __typename?: 'AggregatedLedgerEntry', vegaTime: any, quantity: string, assetId?: string | null, transferType?: Types.TransferType | null, toAccountType?: Types.AccountType | null, toAccountMarketId?: string | null, toAccountPartyId?: string | null, toAccountBalance: string, fromAccountType?: Types.AccountType | null, fromAccountMarketId?: string | null, fromAccountPartyId?: string | null, fromAccountBalance: string } } | null>, pageInfo: { __typename?: 'PageInfo', startCursor: string, endCursor: string, hasNextPage: boolean, hasPreviousPage: boolean } } };
export const LedgerEntryFragmentDoc = gql`
fragment LedgerEntry on AggregatedLedgerEntry {
vegaTime
quantity
assetId
transferType
toAccountType
toAccountMarketId
toAccountPartyId
toAccountBalance
fromAccountType
fromAccountMarketId
fromAccountPartyId
fromAccountBalance
}
`;
export const LedgerEntriesDocument = gql`
query LedgerEntries($partyId: ID!, $pagination: Pagination, $dateRange: DateRange, $fromAccountType: [AccountType!], $toAccountType: [AccountType!]) {
ledgerEntries(
filter: {FromAccountFilter: {partyIds: [$partyId], accountTypes: $fromAccountType}, ToAccountFilter: {partyIds: [$partyId], accountTypes: $toAccountType}}
pagination: $pagination
dateRange: $dateRange
) {
edges {
node {
...LedgerEntry
}
cursor
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
${LedgerEntryFragmentDoc}`;
/**
* __useLedgerEntriesQuery__
*
* To run a query within a React component, call `useLedgerEntriesQuery` and pass it any options that fit your needs.
* When your component renders, `useLedgerEntriesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useLedgerEntriesQuery({
* variables: {
* partyId: // value for 'partyId'
* pagination: // value for 'pagination'
* dateRange: // value for 'dateRange'
* fromAccountType: // value for 'fromAccountType'
* toAccountType: // value for 'toAccountType'
* },
* });
*/
export function useLedgerEntriesQuery(baseOptions: Apollo.QueryHookOptions<LedgerEntriesQuery, LedgerEntriesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<LedgerEntriesQuery, LedgerEntriesQueryVariables>(LedgerEntriesDocument, options);
}
export function useLedgerEntriesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<LedgerEntriesQuery, LedgerEntriesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<LedgerEntriesQuery, LedgerEntriesQueryVariables>(LedgerEntriesDocument, options);
}
export type LedgerEntriesQueryHookResult = ReturnType<typeof useLedgerEntriesQuery>;
export type LedgerEntriesLazyQueryHookResult = ReturnType<typeof useLedgerEntriesLazyQuery>;
export type LedgerEntriesQueryResult = Apollo.QueryResult<LedgerEntriesQuery, LedgerEntriesQueryVariables>;
@@ -1,77 +0,0 @@
import type { Asset } from '@vegaprotocol/assets';
import { assetsMapProvider } from '@vegaprotocol/assets';
import type { Market } from '@vegaprotocol/markets';
import { marketsMapProvider } from '@vegaprotocol/markets';
import {
makeDataProvider,
makeDerivedDataProvider,
} from '@vegaprotocol/data-provider';
import type {
LedgerEntriesQuery,
LedgerEntriesQueryVariables,
LedgerEntryFragment,
} from './__generated__/LedgerEntries';
import { LedgerEntriesDocument } from './__generated__/LedgerEntries';
export type LedgerEntry = LedgerEntryFragment & {
asset: Asset | null | undefined;
marketSender: Market | null | undefined;
marketReceiver: Market | null | undefined;
};
type Edge = LedgerEntriesQuery['ledgerEntries']['edges'][number];
const isLedgerEntryEdge = (entry: Edge): entry is NonNullable<Edge> =>
entry !== null;
const getData = (responseData: LedgerEntriesQuery | null) => {
return (
responseData?.ledgerEntries?.edges
.filter(isLedgerEntryEdge)
.map((edge) => edge.node) || []
);
};
const ledgerEntriesOnlyProvider = makeDataProvider<
LedgerEntriesQuery,
ReturnType<typeof getData>,
never,
never,
LedgerEntriesQueryVariables
>({
query: LedgerEntriesDocument,
getData,
additionalContext: {
isEnlargedTimeout: true,
},
});
export const ledgerEntriesProvider = makeDerivedDataProvider<
LedgerEntry[],
never,
LedgerEntriesQueryVariables
>(
[
ledgerEntriesOnlyProvider,
(callback, client) => assetsMapProvider(callback, client, undefined),
(callback, client) => marketsMapProvider(callback, client, undefined),
],
(partsData) => {
const entries = partsData[0] as ReturnType<typeof getData>;
const assets = partsData[1] as Record<string, Asset>;
const markets = partsData[2] as Record<string, Market>;
return entries.map((entry) => {
const asset = entry.assetId
? (assets as Record<string, Asset>)[entry.assetId]
: null;
const marketSender = entry.fromAccountMarketId
? markets[entry.fromAccountMarketId]
: null;
const marketReceiver = entry.toAccountMarketId
? markets[entry.toAccountMarketId]
: null;
return { ...entry, asset, marketSender, marketReceiver };
});
}
);
-247
View File
@@ -1,247 +0,0 @@
import type {
LedgerEntriesQuery,
LedgerEntryFragment,
} from './__generated__/LedgerEntries';
import * as Schema from '@vegaprotocol/types';
import type { PartialDeep } from 'type-fest';
import merge from 'lodash/merge';
export const ledgerEntriesQuery = (
override?: PartialDeep<LedgerEntriesQuery>
): LedgerEntriesQuery => {
const defaultResult: LedgerEntriesQuery = {
__typename: 'Query',
ledgerEntries: {
__typename: 'AggregatedLedgerEntriesConnection',
edges: ledgerEntries.map((node) => ({
__typename: 'AggregatedLedgerEntriesEdge',
node,
cursor: 'cursor-1',
})),
pageInfo: {
startCursor:
'eyJ2ZWdhX3RpbWUiOiIyMDIyLTExLTIzVDE3OjI3OjU2LjczNDM2NFoifQ==',
endCursor:
'eyJ2ZWdhX3RpbWUiOiIyMDIyLTExLTIzVDEzOjExOjE2LjU0NjM2M1oifQ==',
hasNextPage: false,
hasPreviousPage: false,
__typename: 'PageInfo',
},
},
};
return merge(defaultResult, override);
};
export const ledgerEntries: LedgerEntryFragment[] = [
{
vegaTime: '1669224476734364000',
quantity: '0',
assetId: 'asset-id',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_HIGH,
toAccountType: Schema.AccountType.ACCOUNT_TYPE_EXTERNAL,
toAccountMarketId: 'market-1',
toAccountPartyId: 'network',
fromAccountType: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
fromAccountMarketId: 'market-0',
fromAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '1669221452175594000',
quantity: '0',
assetId: 'asset-id-2',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_HIGH,
toAccountType: Schema.AccountType.ACCOUNT_TYPE_EXTERNAL,
toAccountMarketId: 'market-0',
toAccountPartyId: 'network',
fromAccountType: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
fromAccountMarketId: 'market-2',
fromAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '1669209347054198000',
quantity: '0',
assetId: 'asset-id',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_HIGH,
toAccountType: Schema.AccountType.ACCOUNT_TYPE_EXTERNAL,
toAccountMarketId: 'market-3',
toAccountPartyId: 'network',
fromAccountType: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
fromAccountMarketId: 'market-2',
fromAccountPartyId: 'sender party id',
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '1669209345512806000',
quantity: '0',
assetId: 'asset-id',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_HIGH,
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '1669209316163397000',
quantity: '0',
assetId: 'asset-id-2',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_HIGH,
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '1669209299051286000',
quantity: '1326783',
assetId: 'asset-id-2',
transferType: Schema.TransferType.TRANSFER_TYPE_MTM_WIN,
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '1669209151328614000',
quantity: '0',
assetId: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_HIGH,
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '1669209117655380000',
quantity: '0',
assetId: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_HIGH,
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '1669209082788024000',
quantity: '1326783',
assetId: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
transferType: Schema.TransferType.TRANSFER_TYPE_MTM_WIN,
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '1669209076546363000',
quantity: '0',
assetId: 'cee709223217281d7893b650850ae8ee8a18b7539b5658f9b4cc24de95dd18ad',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_HIGH,
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '2022-11-24T13:36:42.13989Z',
quantity: '9078407730948615',
assetId: 'cee709223217281d7893b650850ae8ee8a18b7539b5658f9b4cc24de95dd18ad',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_LOW,
toAccountType: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
toAccountMarketId: null,
toAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
fromAccountType: Schema.AccountType.ACCOUNT_TYPE_MARGIN,
fromAccountMarketId:
'0942d767cb2cb5a795e14216e8e53c2b6f75e46dc1732c5aeda8a5aba4ad193d',
fromAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '2022-11-24T13:35:49.257039Z',
quantity: '263142253070974',
assetId: 'cee709223217281d7893b650850ae8ee8a18b7539b5658f9b4cc24de95dd18ad',
transferType: Schema.TransferType.TRANSFER_TYPE_MARGIN_LOW,
toAccountType: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
toAccountMarketId: null,
toAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
fromAccountType: Schema.AccountType.ACCOUNT_TYPE_MARGIN,
fromAccountMarketId:
'0942d767cb2cb5a795e14216e8e53c2b6f75e46dc1732c5aeda8a5aba4ad193d',
fromAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '2022-11-24T12:41:22.054428Z',
quantity: '1000000000',
assetId: '4e4e80abff30cab933b8c4ac6befc618372eb76b2cbddc337eff0b4a3a4d25b8',
transferType: Schema.TransferType.TRANSFER_TYPE_DEPOSIT,
toAccountType: Schema.AccountType.ACCOUNT_TYPE_EXTERNAL,
toAccountMarketId: null,
toAccountPartyId: 'network',
fromAccountType: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
fromAccountMarketId: null,
fromAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '2022-11-24T12:39:11.516154Z',
quantity: '1000000000000',
assetId: 'c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d',
transferType: Schema.TransferType.TRANSFER_TYPE_DEPOSIT,
toAccountType: Schema.AccountType.ACCOUNT_TYPE_EXTERNAL,
toAccountMarketId: null,
toAccountPartyId: 'network',
fromAccountType: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
fromAccountMarketId: null,
fromAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '2022-11-24T12:37:26.832226Z',
quantity: '10000000000000000000000',
assetId: 'c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d',
transferType: Schema.TransferType.TRANSFER_TYPE_DEPOSIT,
toAccountType: Schema.AccountType.ACCOUNT_TYPE_EXTERNAL,
toAccountMarketId: null,
toAccountPartyId: 'network',
fromAccountType: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
fromAccountMarketId: null,
fromAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
{
vegaTime: '2022-11-24T12:24:52.844901Z',
quantity: '49390000000000000000000',
assetId: 'c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d',
transferType: Schema.TransferType.TRANSFER_TYPE_DEPOSIT,
toAccountType: Schema.AccountType.ACCOUNT_TYPE_EXTERNAL,
toAccountMarketId: null,
toAccountPartyId: 'network',
fromAccountType: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
fromAccountMarketId: null,
fromAccountPartyId:
'2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
__typename: 'AggregatedLedgerEntry',
toAccountBalance: '0',
fromAccountBalance: '0',
},
];
@@ -0,0 +1,215 @@
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { createDownloadUrl, LedgerExportForm } from './ledger-export-form';
import { formatForInput, toNanoSeconds } from '@vegaprotocol/utils';
const vegaUrl = 'https://vega-url.co.uk/querystuff';
const mockResponse = {
headers: { get: jest.fn() },
blob: () => '',
};
global.fetch = jest.fn().mockResolvedValue(mockResponse);
const assetsMock = {
['a'.repeat(64)]: 'symbol asset-id',
['b'.repeat(64)]: 'symbol asset-id-2',
};
describe('LedgerExportForm', () => {
const partyId = 'c'.repeat(64);
afterEach(() => {
jest.clearAllMocks();
});
beforeAll(() => {
jest.useFakeTimers().setSystemTime(new Date('2023-08-10T10:10:10.000Z'));
});
afterAll(() => {
jest.useRealTimers();
});
it('should be properly rendered', async () => {
render(
<LedgerExportForm
partyId={partyId}
vegaUrl={vegaUrl}
assets={assetsMock}
/>
);
expect(screen.getByText('symbol asset-id')).toBeInTheDocument();
// userEvent does not work with faked timers
fireEvent.click(screen.getByTestId('ledger-download-button'));
expect(screen.getByTestId('download-spinner')).toBeInTheDocument();
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith(
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
Object.keys(assetsMock)[0]
}&dateRange.startTimestamp=1691057410000000000`
);
});
await waitFor(() => {
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
});
});
it('assetID should be properly change request url', async () => {
render(
<LedgerExportForm
partyId={partyId}
vegaUrl={vegaUrl}
assets={assetsMock}
/>
);
expect(screen.getByText('symbol asset-id')).toBeInTheDocument();
fireEvent.change(screen.getByTestId('select-ledger-asset'), {
target: { value: Object.keys(assetsMock)[1] },
});
expect(screen.getByText('symbol asset-id-2')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('ledger-download-button'));
expect(screen.getByTestId('download-spinner')).toBeInTheDocument();
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith(
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
Object.keys(assetsMock)[1]
}&dateRange.startTimestamp=1691057410000000000`
);
});
await waitFor(() => {
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
});
});
it('date-from should properly change request url', async () => {
const newDate = new Date(1691230210000);
render(
<LedgerExportForm
partyId={partyId}
vegaUrl={vegaUrl}
assets={assetsMock}
/>
);
expect(screen.getByLabelText('Date from')).toBeInTheDocument();
fireEvent.change(screen.getByLabelText('Date from'), {
target: { value: formatForInput(newDate) },
});
expect(screen.getByTestId('date-from')).toHaveValue(
`${formatForInput(newDate)}.000`
);
fireEvent.click(screen.getByTestId('ledger-download-button'));
expect(screen.getByTestId('download-spinner')).toBeInTheDocument();
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith(
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
Object.keys(assetsMock)[0]
}&dateRange.startTimestamp=${toNanoSeconds(newDate)}`
);
});
await waitFor(() => {
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
});
});
it('date-to should properly change request url', async () => {
const newDate = new Date(1691230210000);
render(
<LedgerExportForm
partyId={partyId}
vegaUrl={vegaUrl}
assets={assetsMock}
/>
);
expect(screen.getByLabelText('Date to')).toBeInTheDocument();
fireEvent.change(screen.getByLabelText('Date to'), {
target: { value: formatForInput(newDate) },
});
expect(screen.getByTestId('date-to')).toHaveValue(
`${formatForInput(newDate)}.000`
);
fireEvent.click(screen.getByTestId('ledger-download-button'));
await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith(
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${
Object.keys(assetsMock)[0]
}&dateRange.startTimestamp=1691057410000000000&dateRange.endTimestamp=${toNanoSeconds(
newDate
)}`
);
});
await waitFor(() => {
expect(screen.queryByTestId('download-spinner')).not.toBeInTheDocument();
});
});
});
describe('createDownloadUrl', () => {
const fromTimestamp = 1690848000000;
const toTimestamp = 1691107200000;
const args = {
protohost: 'https://vega-url.co.uk',
partyId: 'a'.repeat(64),
assetId: 'b'.repeat(64),
dateFrom: new Date(fromTimestamp).toISOString(),
};
it('formats url with without an end date', () => {
expect(createDownloadUrl(args)).toEqual(
`${args.protohost}/api/v2/ledgerentry/export?partyId=${
args.partyId
}&assetId=${args.assetId}&dateRange.startTimestamp=${toNanoSeconds(
args.dateFrom
)}`
);
});
it('formats url with with an end date', () => {
const dateTo = new Date(toTimestamp).toISOString();
expect(
createDownloadUrl({
...args,
dateTo,
})
).toEqual(
`${args.protohost}/api/v2/ledgerentry/export?partyId=${
args.partyId
}&assetId=${args.assetId}&dateRange.startTimestamp=${toNanoSeconds(
args.dateFrom
)}&dateRange.endTimestamp=${toNanoSeconds(dateTo)}`
);
});
it('should throw if invalid args are provided', () => {
// invalid url
expect(() => {
// @ts-ignore override z.infer type
createDownloadUrl({ ...args, protohost: 'foo' });
}).toThrow();
// invalid partyId
expect(() => {
// @ts-ignore override z.infer type
createDownloadUrl({ ...args, partyId: 'z'.repeat(64) });
}).toThrow();
});
});
+172
View File
@@ -0,0 +1,172 @@
import { useRef, useState } from 'react';
import { z } from 'zod';
import {
Button,
Loader,
TradingFormGroup,
TradingInput,
TradingSelect,
} from '@vegaprotocol/ui-toolkit';
import { toNanoSeconds, VEGA_ID_REGEX } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { localLoggerFactory } from '@vegaprotocol/logger';
import { formatForInput } from '@vegaprotocol/utils';
import { subDays } from 'date-fns';
const DEFAULT_EXPORT_FILE_NAME = 'ledger_entries.csv';
const getProtoHost = (vegaurl: string) => {
const loc = new URL(vegaurl);
return `${loc.protocol}//${loc.host}`;
};
const downloadSchema = z.object({
protohost: z.string().url().nonempty(),
partyId: z.string().regex(VEGA_ID_REGEX).nonempty(),
assetId: z.string().regex(VEGA_ID_REGEX).nonempty(),
dateFrom: z.string().nonempty(),
dateTo: z.string().optional(),
});
export const createDownloadUrl = (args: z.infer<typeof downloadSchema>) => {
// check args from form inputs
downloadSchema.parse(args);
const params = new URLSearchParams();
params.append('partyId', args.partyId);
params.append('assetId', args.assetId);
params.append('dateRange.startTimestamp', toNanoSeconds(args.dateFrom));
if (args.dateTo) {
params.append('dateRange.endTimestamp', toNanoSeconds(args.dateTo));
}
const url = new URL(args.protohost);
url.pathname = '/api/v2/ledgerentry/export';
url.search = params.toString();
return url.toString();
};
interface Props {
partyId: string;
vegaUrl: string;
assets: Record<string, string>;
}
export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => {
const now = useRef(new Date());
const [dateFrom, setDateFrom] = useState(() => {
return formatForInput(subDays(now.current, 7));
});
const [dateTo, setDateTo] = useState('');
const maxFromDate = formatForInput(new Date(dateTo || now.current));
const maxToDate = formatForInput(now.current);
const [isDownloading, setIsDownloading] = useState(false);
const [assetId, setAssetId] = useState(Object.keys(assets)[0]);
const protohost = getProtoHost(vegaUrl);
const disabled = Boolean(!assetId || isDownloading);
const assetDropDown = (
<TradingSelect
id="select-ledger-asset"
value={assetId}
onChange={(e) => {
setAssetId(e.target.value);
}}
className="w-full"
data-testid="select-ledger-asset"
disabled={isDownloading}
>
{Object.keys(assets).map((assetKey) => (
<option key={assetKey} value={assetKey}>
{assets[assetKey]}
</option>
))}
</TradingSelect>
);
const startDownload = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
const link = createDownloadUrl({
protohost,
partyId,
assetId,
dateFrom,
dateTo,
});
setIsDownloading(true);
const resp = await fetch(link);
const { headers } = resp;
const nameHeader = headers.get('content-disposition');
const filename = nameHeader?.split('=').pop() ?? DEFAULT_EXPORT_FILE_NAME;
const blob = await resp.blob();
if (blob) {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
} catch (err) {
localLoggerFactory({ application: 'ledger' }).error('Download file', err);
} finally {
setIsDownloading(false);
}
};
if (!protohost || Object.keys(assets).length === 0) {
return null;
}
return (
<form onSubmit={startDownload} className="p-4 w-[350px]">
<h2 className="mb-4">{t('Export ledger entries')}</h2>
<TradingFormGroup label={t('Select asset')} labelFor="asset">
{assetDropDown}
</TradingFormGroup>
<TradingFormGroup label={t('Date from')} labelFor="date-from">
<TradingInput
type="datetime-local"
data-testid="date-from"
id="date-from"
value={dateFrom}
onChange={(e) => setDateFrom(e.target.value)}
disabled={disabled}
max={maxFromDate}
/>
</TradingFormGroup>
<TradingFormGroup label={t('Date to')} labelFor="date-to">
<TradingInput
type="datetime-local"
data-testid="date-to"
id="date-to"
value={dateTo}
onChange={(e) => setDateTo(e.target.value)}
disabled={disabled}
max={maxToDate}
/>
</TradingFormGroup>
<div className="relative text-sm" title={t('Download all to .csv file')}>
{isDownloading && (
<div
className="absolute flex items-center justify-center w-full h-full"
data-testid="download-spinner"
>
<Loader size="small" />
</div>
)}
<Button
variant="primary"
fill
disabled={disabled}
type="submit"
data-testid="ledger-download-button"
>
{t('Download')}
</Button>
</div>
</form>
);
};
@@ -1,85 +0,0 @@
import { act, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LedgerExportLink } from './ledger-export-link';
import * as Types from '@vegaprotocol/types';
import { ledgerEntries } from './ledger-entries.mock';
import type { LedgerEntry } from './ledger-entries-data-provider';
const VEGA_URL = 'https://vega-url.co.uk/querystuff';
const mockEnvironment = jest.fn(() => VEGA_URL);
jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: jest.fn(() => mockEnvironment()),
}));
const asset = {
id: 'assetID',
name: 'assetName',
symbol: 'assetSymbol',
decimals: 1,
quantum: '1',
status: Types.AssetStatus,
source: {
__typename: 'ERC20',
contractAddress: 'contractAddres',
lifetimeLimit: 'lifetimeLimit',
withdrawThreshold: 'withdraw',
},
};
describe('LedgerExportLink', () => {
const partyId = 'partyId';
const entries = ledgerEntries.map((entry) => {
return {
...entry,
asset: entry.assetId
? {
...asset,
id: entry.assetId,
name: `name ${entry.assetId}`,
symbol: `symbol ${entry.assetId}`,
}
: null,
marketSender: null,
marketReceiver: null,
} as LedgerEntry;
});
it('should be properly rendered', async () => {
render(<LedgerExportLink partyId={partyId} entries={entries} />);
await waitFor(() => {
expect(screen.getByRole('link')).toBeInTheDocument();
expect(screen.getByRole('link')).toHaveAttribute(
'href',
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=asset-id`
);
expect(
screen.getByRole('button', { name: /^symbol asset-id/ })
).toBeInTheDocument();
});
});
it('should be properly change link url', async () => {
render(<LedgerExportLink partyId={partyId} entries={entries} />);
await waitFor(() => {
expect(
screen.getByRole('button', { name: /^symbol asset-id/ })
).toBeInTheDocument();
});
act(() => {
userEvent.click(screen.getByRole('button', { name: /^symbol asset-id/ }));
});
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
act(() => {
userEvent.click(
screen.getByRole('menuitem', { name: /^symbol asset-id-2/ })
);
});
await waitFor(() => {
expect(screen.getByRole('link')).toHaveAttribute(
'href',
`https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=asset-id-2`
);
});
});
});
@@ -1,71 +0,0 @@
import type { LedgerEntry } from './ledger-entries-data-provider';
import { useMemo, useState } from 'react';
import { useEnvironment } from '@vegaprotocol/environment';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Link,
Button,
} from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
const getProtoHost = (vegaurl: string) => {
const loc = new URL(vegaurl);
return `${loc.protocol}//${loc.host}`;
};
export const LedgerExportLink = ({
partyId,
entries,
}: {
partyId: string;
entries: LedgerEntry[];
}) => {
const assets = entries.reduce((aggr, item) => {
if (item.asset && !(item.asset.id in aggr)) {
aggr[item.asset.id] = item.asset.symbol;
}
return aggr;
}, {} as Record<string, string>);
const [assetId, setAssetId] = useState(Object.keys(assets)[0]);
const VEGA_URL = useEnvironment((store) => store.VEGA_URL);
const protohost = VEGA_URL ? getProtoHost(VEGA_URL) : '';
const assetDropDown = useMemo(() => {
return (
<DropdownMenu
trigger={<DropdownMenuTrigger>{assets[assetId]}</DropdownMenuTrigger>}
>
<DropdownMenuContent>
{Object.keys(assets).map((assetKey) => (
<DropdownMenuItem
key={assetKey}
onSelect={() => setAssetId(assetKey)}
>
{assets[assetKey]}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}, [assetId, assets]);
if (!protohost || !entries || entries.length === 0) {
return null;
}
return (
<div className="flex shrink items-stretch gap-2 p-2">
<div className="flex items-center">Export all</div>
{assetDropDown}
<Link
className="text-sm"
title={t('Download all to .csv file')}
href={`${protohost}/api/v2/ledgerentry/export?partyId=${partyId}&assetId=${assetId}`}
>
<Button size="sm">{t('Download')}</Button>
</Link>
</div>
);
};
-73
View File
@@ -1,73 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type * as Schema from '@vegaprotocol/types';
import type { FilterChangedEvent } from 'ag-grid-community';
import { useCallback, useState, useMemo } from 'react';
import { subDays, formatRFC3339 } from 'date-fns';
import { ledgerEntriesProvider } from './ledger-entries-data-provider';
import type { LedgerEntriesQueryVariables } from './__generated__/LedgerEntries';
import { LedgerTable } from './ledger-table';
import { useDataProvider } from '@vegaprotocol/data-provider';
import type * as Types from '@vegaprotocol/types';
import { LedgerExportLink } from './ledger-export-link';
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
export interface Filter {
vegaTime?: {
value: Schema.DateRange;
};
fromAccountType?: { value: Types.AccountType[] };
toAccountType?: { value: Types.AccountType[] };
}
const defaultFilter = {
vegaTime: {
value: { start: formatRFC3339(subDays(Date.now(), 7)) },
},
};
export const LedgerManager = ({
partyId,
gridProps,
}: {
partyId: string;
gridProps: ReturnType<typeof useDataGridEvents>;
}) => {
const [filter, setFilter] = useState<Filter>(defaultFilter);
const variables = useMemo<LedgerEntriesQueryVariables>(
() => ({
partyId,
dateRange: filter?.vegaTime?.value,
pagination: {
first: 10,
},
}),
[partyId, filter?.vegaTime?.value]
);
const { data, error } = useDataProvider({
dataProvider: ledgerEntriesProvider,
variables,
skip: !variables.partyId,
});
const onFilterChanged = useCallback(
(event: FilterChangedEvent) => {
const updatedFilter = { ...defaultFilter, ...event.api.getFilterModel() };
setFilter(updatedFilter);
gridProps.onFilterChanged(event);
},
[gridProps]
);
return (
<div className="h-full relative">
<LedgerTable
rowData={data}
overlayNoRowsTemplate={error ? error.message : t('No entries')}
{...gridProps}
onFilterChanged={onFilterChanged}
/>
{data && <LedgerExportLink entries={data} partyId={partyId} />}
</div>
);
};
-215
View File
@@ -1,215 +0,0 @@
import {
addDecimalsFormatNumber,
fromNanoSeconds,
getDateTimeFormat,
truncateByChars,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type {
VegaValueFormatterParams,
TypedDataAgGrid,
} from '@vegaprotocol/datagrid';
import {
AgGridLazy as AgGrid,
DateRangeFilter,
MarketNameCell,
SetFilter,
} from '@vegaprotocol/datagrid';
import type * as Types from '@vegaprotocol/types';
import type { ColDef } from 'ag-grid-community';
import type { Market } from '@vegaprotocol/markets';
import {
AccountTypeMapping,
DescriptionTransferTypeMapping,
TransferTypeMapping,
} from '@vegaprotocol/types';
import type { LedgerEntry } from './ledger-entries-data-provider';
import { useMemo } from 'react';
import { formatRFC3339, subDays } from 'date-fns';
export const TransferTooltipCellComponent = ({
value,
}: {
value: Types.TransferType;
}) => {
return (
<p className="max-w-sm px-4 py-2 z-20 rounded text-sm break-word">
{value ? DescriptionTransferTypeMapping[value] : ''}
</p>
);
};
const defaultValue = { start: formatRFC3339(subDays(Date.now(), 7)) };
const dateRangeFilterParams = {
maxNextDays: 0,
defaultValue,
};
const defaultColDef = {
resizable: true,
sortable: true,
tooltipComponent: TransferTooltipCellComponent,
filterParams: {
...dateRangeFilterParams,
buttons: ['reset'],
},
};
type LedgerEntryProps = TypedDataAgGrid<LedgerEntry>;
export const LedgerTable = (props: LedgerEntryProps) => {
const columnDefs = useMemo<ColDef[]>(
() => [
{
headerName: t('Sender'),
field: 'fromAccountPartyId',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountPartyId'>) =>
truncateByChars(value || ''),
},
{
headerName: t('Account type'),
filter: SetFilter,
filterParams: {
set: AccountTypeMapping,
},
field: 'fromAccountType',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountType'>) =>
value ? AccountTypeMapping[value] : '-',
},
{
headerName: t('Market'),
field: 'marketSender.tradableInstrument.instrument.code',
cellRenderer: ({
value,
data,
}: VegaValueFormatterParams<
LedgerEntry,
'marketSender.tradableInstrument.instrument.code'
>) =>
value && (
<MarketNameCell value={value} data={data?.marketSender as Market} />
),
},
{
headerName: t('Receiver'),
field: 'toAccountPartyId',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountPartyId'>) =>
truncateByChars(value || ''),
},
{
headerName: t('Account type'),
filter: SetFilter,
filterParams: {
set: AccountTypeMapping,
},
field: 'toAccountType',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountType'>) =>
value ? AccountTypeMapping[value] : '-',
},
{
headerName: t('Market'),
field: 'marketReceiver.tradableInstrument.instrument.code',
cellRenderer: ({
value,
data,
}: VegaValueFormatterParams<
LedgerEntry,
'marketReceiver.tradableInstrument.instrument.code'
>) =>
value && (
<MarketNameCell
value={value}
data={data?.marketReceiver as Market}
/>
),
},
{
headerName: t('Transfer type'),
field: 'transferType',
tooltipField: 'transferType',
filter: SetFilter,
filterParams: {
set: TransferTypeMapping,
},
valueFormatter: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'transferType'>) =>
value ? TransferTypeMapping[value] : '',
},
{
headerName: t('Quantity'),
field: 'quantity',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'quantity'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
},
{
headerName: t('Asset'),
field: 'assetId',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'asset'>) =>
data?.asset?.symbol || '',
},
{
headerName: t('Sender account balance'),
field: 'fromAccountBalance',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountBalance'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
},
{
headerName: t('Receiver account balance'),
field: 'toAccountBalance',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountBalance'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
},
{
headerName: t('Vega time'),
field: 'vegaTime',
valueFormatter: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'vegaTime'>) =>
value ? getDateTimeFormat().format(fromNanoSeconds(value)) : '-',
filterParams: dateRangeFilterParams,
filter: DateRangeFilter,
},
],
[]
);
return (
<AgGrid
tooltipShowDelay={500}
defaultColDef={defaultColDef}
columnDefs={columnDefs}
{...props}
/>
);
};
+2
View File
@@ -4414,6 +4414,8 @@ export type StopOrderFilter = {
dateRange?: InputMaybe<DateRange>;
/** Zero or more expiry strategies to filter by */
expiryStrategy?: InputMaybe<Array<StopOrderExpiryStrategy>>;
/** Filter for live stop orders only */
liveOnly?: InputMaybe<Scalars['Boolean']>;
/** Zero or more market IDs to filter by */
markets?: InputMaybe<Array<Scalars['ID']>>;
/** Zero or more party IDs to filter by */
@@ -68,7 +68,11 @@ export const ShowMore = ({
{!expanded && (
<div className="mt-1 text-center">
<Button size={'sm'} onClick={() => setExpanded(true)}>
<Button
size={'sm'}
onClick={() => setExpanded(true)}
data-testid="show-more-btn"
>
{t('Show more')}
</Button>
</div>
+2 -1
View File
@@ -15,8 +15,9 @@ export const ethereumAddress = (value: string) => {
return true;
};
export const VEGA_ID_REGEX = /^[A-Fa-f0-9]{64}$/i;
export const vegaPublicKey = (value: string) => {
if (!/^[A-Fa-f0-9]{64}$/i.test(value)) {
if (!VEGA_ID_REGEX.test(value)) {
return t('Invalid Vega key');
}
return true;
+1 -5
View File
@@ -1,8 +1,4 @@
{
"name": "@vegaprotocol/wallet",
"version": "0.0.1",
"peerDependencies": {
"react": "18.2.0",
"react-dom": "18.2.0"
}
"version": "0.0.1"
}
+1 -2
View File
@@ -13,10 +13,9 @@
"tsConfig": "libs/wallet/tsconfig.lib.json",
"project": "libs/wallet/package.json",
"entryFile": "libs/wallet/src/index.ts",
"external": ["react", "react-dom", "react/jsx-runtime"],
"external": ["react/jsx-runtime"],
"rollupConfig": "@nx/react/plugins/bundle-rollup",
"compiler": "babel",
"format": ["esm", "cjs"],
"assets": [
{
"glob": "libs/wallet/README.md",
@@ -1,77 +0,0 @@
export const ChromeIcon = () => {
return (
<svg
width="28"
height="28"
viewBox="0 0 32 32"
fill="none"
data-testid="chrome-logo"
>
<g clipPath="url(#clip0_3681_24659)">
<path
d="M15.9987 9.99963L26.3893 9.99964C25.3364 8.17534 23.8217 6.6604 21.9976 5.60716C20.1735 4.55391 18.1042 3.99949 15.9979 3.99964C13.8915 3.99979 11.8223 4.5545 9.99837 5.608C8.1744 6.66149 6.65995 8.17664 5.6073 10.0011L10.8026 18.9996L10.8072 18.9984C10.2787 18.0871 9.99984 17.0525 9.99865 15.999C9.99747 14.9454 10.274 13.9102 10.8005 12.9977C11.3269 12.0851 12.0847 11.3275 12.9973 10.8011C13.9099 10.2748 14.9451 9.99832 15.9987 9.99963Z"
fill="url(#paint0_linear_3681_24659)"
/>
<path
d="M21.1974 18.9989L16.0021 27.9974C18.1084 27.9977 20.1777 27.4435 22.0019 26.3904C23.8261 25.3373 25.3409 23.8224 26.3939 21.9982C27.447 20.174 28.0012 18.1047 28.0008 15.9983C28.0004 13.892 27.4455 11.8228 26.3918 9.99898L16.0012 9.99899L15.9999 10.0036C17.0534 10.0016 18.0889 10.2773 19.0018 10.8031C19.9148 11.3288 20.673 12.0859 21.2001 12.9981C21.7272 13.9103 22.0044 14.9454 22.004 15.9989C22.0035 17.0524 21.7253 18.0872 21.1974 18.9989Z"
fill="url(#paint1_linear_3681_24659)"
/>
<path
d="M10.8044 19.0016L5.60914 10.0031C4.5557 11.8271 4.00106 13.8963 4.00098 16.0026C4.0009 18.109 4.55539 20.1782 5.60869 22.0023C6.66199 23.8264 8.17698 25.341 10.0013 26.3938C11.8257 27.4467 13.895 28.0007 16.0014 28.0001L21.1967 19.0015L21.1933 18.9981C20.6683 19.9115 19.9118 20.6703 19 21.1981C18.0882 21.7259 17.0534 22.004 15.9999 22.0043C14.9464 22.0047 13.9114 21.7273 12.9992 21.2001C12.0871 20.6729 11.3301 19.9146 10.8044 19.0016Z"
fill="url(#paint2_linear_3681_24659)"
/>
<path
d="M16 22C19.3137 22 22 19.3137 22 16C22 12.6863 19.3137 10 16 10C12.6863 10 10 12.6863 10 16C10 19.3137 12.6863 22 16 22Z"
fill="white"
/>
<path
d="M16 20.75C18.6234 20.75 20.75 18.6234 20.75 16C20.75 13.3766 18.6234 11.25 16 11.25C13.3766 11.25 11.25 13.3766 11.25 16C11.25 18.6234 13.3766 20.75 16 20.75Z"
fill="#1A73E8"
/>
</g>
<defs>
<linearGradient
id="paint0_linear_3681_24659"
x1="25.093"
y1="9.25084"
x2="14.702"
y2="27.2485"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#D93025" />
<stop offset="1" stopColor="#EA4335" />
</linearGradient>
<linearGradient
id="paint1_linear_3681_24659"
x1="27.073"
y1="11.4997"
x2="6.29104"
y2="11.4997"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#FCC934" />
<stop offset="1" stopColor="#FBBC04" />
</linearGradient>
<linearGradient
id="paint2_linear_3681_24659"
x1="17.2992"
y1="27.2508"
x2="6.90819"
y2="9.25305"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#1E8E3E" />
<stop offset="1" stopColor="#34A853" />
</linearGradient>
<clipPath id="clip0_3681_24659">
<rect
width="24"
height="24"
fill="white"
transform="translate(4 4)"
/>
</clipPath>
</defs>
</svg>
);
};
@@ -1,3 +1,4 @@
import { ExternalLinks, useEnvironment } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import {
ExternalLink,
@@ -6,15 +7,12 @@ import {
} from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import { MozillaIcon } from './mozilla-icon';
import { ChromeIcon } from './chrome-icon';
import { useVegaWallet } from '../use-vega-wallet';
export const ConnectDialogTitle = ({ children }: { children: ReactNode }) => {
return (
<h1
data-testid="wallet-dialog-title"
className="mb-6 text-2xl uppercase font-alpha calt"
className="text-2xl uppercase mb-6 font-alpha calt"
>
{children}
</h1>
@@ -26,7 +24,6 @@ export const ConnectDialogContent = ({ children }: { children: ReactNode }) => {
};
export const ConnectDialogFooter = () => {
const { links } = useVegaWallet();
const wrapperClasses = classNames(
'flex justify-center gap-4 mt-4',
'px-4 md:px-8 pt-4 md:pt-6',
@@ -35,37 +32,335 @@ export const ConnectDialogFooter = () => {
);
return (
<footer className={wrapperClasses}>
<ExternalLink href={links.about} className="underline">
<ExternalLink
href={ExternalLinks.VEGA_WALLET_URL_ABOUT}
className="underline"
>
{t('About the Vega wallet')}{' '}
<VegaIcon name={VegaIconNames.ARROW_TOP_RIGHT} />
</ExternalLink>
{' | '}
<ExternalLink href={links.browserList} className="underline">
{t('Supported browsers')}{' '}
<VegaIcon name={VegaIconNames.ARROW_TOP_RIGHT} />
</ExternalLink>
{ExternalLinks.VEGA_WALLET_BROWSER_LIST && (
<>
{' | '}
<ExternalLink
href={ExternalLinks.VEGA_WALLET_BROWSER_LIST}
className="underline"
>
{t('Supported browsers')}{' '}
<VegaIcon name={VegaIconNames.ARROW_TOP_RIGHT} />
</ExternalLink>
</>
)}
</footer>
);
};
export const BrowserIcon = ({
chromeExtensionUrl,
mozillaExtensionUrl,
}: {
chromeExtensionUrl: string;
mozillaExtensionUrl: string;
}) => {
export const ChromeIcon = () => {
return (
<svg
width="28"
height="28"
viewBox="0 0 32 32"
fill="none"
data-testid="chrome-logo"
>
<g clipPath="url(#clip0_3681_24659)">
<path
d="M15.9987 9.99963L26.3893 9.99964C25.3364 8.17534 23.8217 6.6604 21.9976 5.60716C20.1735 4.55391 18.1042 3.99949 15.9979 3.99964C13.8915 3.99979 11.8223 4.5545 9.99837 5.608C8.1744 6.66149 6.65995 8.17664 5.6073 10.0011L10.8026 18.9996L10.8072 18.9984C10.2787 18.0871 9.99984 17.0525 9.99865 15.999C9.99747 14.9454 10.274 13.9102 10.8005 12.9977C11.3269 12.0851 12.0847 11.3275 12.9973 10.8011C13.9099 10.2748 14.9451 9.99832 15.9987 9.99963Z"
fill="url(#paint0_linear_3681_24659)"
/>
<path
d="M21.1974 18.9989L16.0021 27.9974C18.1084 27.9977 20.1777 27.4435 22.0019 26.3904C23.8261 25.3373 25.3409 23.8224 26.3939 21.9982C27.447 20.174 28.0012 18.1047 28.0008 15.9983C28.0004 13.892 27.4455 11.8228 26.3918 9.99898L16.0012 9.99899L15.9999 10.0036C17.0534 10.0016 18.0889 10.2773 19.0018 10.8031C19.9148 11.3288 20.673 12.0859 21.2001 12.9981C21.7272 13.9103 22.0044 14.9454 22.004 15.9989C22.0035 17.0524 21.7253 18.0872 21.1974 18.9989Z"
fill="url(#paint1_linear_3681_24659)"
/>
<path
d="M10.8044 19.0016L5.60914 10.0031C4.5557 11.8271 4.00106 13.8963 4.00098 16.0026C4.0009 18.109 4.55539 20.1782 5.60869 22.0023C6.66199 23.8264 8.17698 25.341 10.0013 26.3938C11.8257 27.4467 13.895 28.0007 16.0014 28.0001L21.1967 19.0015L21.1933 18.9981C20.6683 19.9115 19.9118 20.6703 19 21.1981C18.0882 21.7259 17.0534 22.004 15.9999 22.0043C14.9464 22.0047 13.9114 21.7273 12.9992 21.2001C12.0871 20.6729 11.3301 19.9146 10.8044 19.0016Z"
fill="url(#paint2_linear_3681_24659)"
/>
<path
d="M16 22C19.3137 22 22 19.3137 22 16C22 12.6863 19.3137 10 16 10C12.6863 10 10 12.6863 10 16C10 19.3137 12.6863 22 16 22Z"
fill="white"
/>
<path
d="M16 20.75C18.6234 20.75 20.75 18.6234 20.75 16C20.75 13.3766 18.6234 11.25 16 11.25C13.3766 11.25 11.25 13.3766 11.25 16C11.25 18.6234 13.3766 20.75 16 20.75Z"
fill="#1A73E8"
/>
</g>
<defs>
<linearGradient
id="paint0_linear_3681_24659"
x1="25.093"
y1="9.25084"
x2="14.702"
y2="27.2485"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#D93025" />
<stop offset="1" stopColor="#EA4335" />
</linearGradient>
<linearGradient
id="paint1_linear_3681_24659"
x1="27.073"
y1="11.4997"
x2="6.29104"
y2="11.4997"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#FCC934" />
<stop offset="1" stopColor="#FBBC04" />
</linearGradient>
<linearGradient
id="paint2_linear_3681_24659"
x1="17.2992"
y1="27.2508"
x2="6.90819"
y2="9.25305"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#1E8E3E" />
<stop offset="1" stopColor="#34A853" />
</linearGradient>
<clipPath id="clip0_3681_24659">
<rect
width="24"
height="24"
fill="white"
transform="translate(4 4)"
/>
</clipPath>
</defs>
</svg>
);
};
export const MozillaIcon = () => {
return (
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
data-testid="mozilla-logo"
>
<g clipPath="url(#clip0_3681_24667)">
<path
d="M22.4398 7.79786C21.9502 6.62017 20.9585 5.34873 20.1798 4.94687C20.8136 6.1893 21.1804 7.43561 21.3205 8.36575C21.3205 8.36758 21.3212 8.37212 21.3227 8.3845C20.0489 5.20951 17.889 3.92926 16.1252 1.1417C16.0361 1.00075 15.9468 0.85942 15.8598 0.710452C15.8155 0.634372 15.7742 0.556628 15.7358 0.477389C15.6625 0.335913 15.606 0.186371 15.5674 0.0317953C15.5676 0.0244774 15.5652 0.017323 15.5604 0.0117374C15.5557 0.00615177 15.549 0.00253868 15.5418 0.00160783C15.5349 -0.000373182 15.5275 -0.000373182 15.5206 0.00160783C15.519 0.00217033 15.5167 0.00399845 15.515 0.0046547C15.5125 0.00563908 15.5094 0.00788908 15.5068 0.0093422C15.508 0.0076547 15.5107 0.00385783 15.5115 0.0029672C12.6816 1.66028 11.7216 4.72614 11.6334 6.26003C10.5034 6.33772 9.42293 6.75406 8.53298 7.45478C8.43981 7.37608 8.3424 7.30253 8.24119 7.23447C7.98442 6.33615 7.97351 5.38538 8.20959 4.4814C7.05234 5.00833 6.15225 5.84125 5.49787 6.57672H5.49267C5.04609 6.01108 5.07759 4.14517 5.10305 3.75559C5.0977 3.73145 4.76991 3.92575 4.72697 3.95505C4.3329 4.23633 3.96449 4.55194 3.62606 4.89817C3.24095 5.28871 2.88907 5.71069 2.57409 6.15972C2.57409 6.16028 2.57377 6.16094 2.57358 6.1615C2.57358 6.16089 2.57391 6.16028 2.57409 6.15972C1.8497 7.18625 1.33595 8.34618 1.06252 9.57245C1.05712 9.59687 1.05258 9.62219 1.04733 9.6468C1.02614 9.74598 0.949828 10.2421 0.936469 10.3499C0.935438 10.3582 0.934969 10.3662 0.933984 10.3745C0.835324 10.8874 0.774224 11.4069 0.751172 11.9287C0.751172 11.9479 0.75 11.967 0.75 11.9862C0.750187 18.2072 5.79394 23.2501 12.0154 23.2501C17.5872 23.2501 22.2135 19.2053 23.1192 13.8924C23.1383 13.7482 23.1536 13.6033 23.1704 13.4578C23.3943 11.5261 23.1456 9.49572 22.4398 7.79786ZM9.45562 16.6148C9.50831 16.6399 9.55781 16.6675 9.61191 16.6916C9.61416 16.6931 9.61725 16.6949 9.61955 16.6963C9.56449 16.67 9.50984 16.6428 9.45562 16.6148ZM21.3236 8.38726L21.3221 8.37634C21.3227 8.38033 21.3234 8.3845 21.324 8.38848L21.3236 8.38726Z"
fill="url(#paint0_linear_3681_24667)"
/>
<path
d="M22.4397 7.79776C21.9501 6.62007 20.9584 5.34864 20.1797 4.94678C20.8135 6.1892 21.1803 7.43551 21.3204 8.36565C21.3204 8.36293 21.321 8.3679 21.3221 8.37625C21.3227 8.38023 21.3234 8.3844 21.324 8.38839C22.3869 11.2698 21.8078 14.1999 20.9734 15.9903C19.6825 18.7607 16.5571 21.5999 11.6652 21.4614C6.37978 21.3117 1.7235 17.39 0.854297 12.2536C0.695906 11.4436 0.854297 11.0323 0.933984 10.3746C0.836906 10.8816 0.799922 11.0281 0.751172 11.9289C0.751172 11.9481 0.75 11.9671 0.75 11.9864C0.750094 18.2071 5.79384 23.25 12.0153 23.25C17.5871 23.25 22.2134 19.2052 23.1191 13.8923C23.1382 13.7481 23.1535 13.6032 23.1703 13.4577C23.3942 11.526 23.1455 9.49562 22.4397 7.79776Z"
fill="url(#paint1_radial_3681_24667)"
/>
<path
d="M22.4397 7.79776C21.9501 6.62007 20.9584 5.34864 20.1797 4.94678C20.8135 6.1892 21.1803 7.43551 21.3204 8.36565C21.3204 8.36293 21.321 8.3679 21.3221 8.37625C21.3227 8.38023 21.3234 8.3844 21.324 8.38839C22.3869 11.2698 21.8078 14.1999 20.9734 15.9903C19.6825 18.7607 16.5571 21.5999 11.6652 21.4614C6.37978 21.3117 1.7235 17.39 0.854297 12.2536C0.695906 11.4436 0.854297 11.0323 0.933984 10.3746C0.836906 10.8816 0.799922 11.0281 0.751172 11.9289C0.751172 11.9481 0.75 11.9671 0.75 11.9864C0.750094 18.2071 5.79384 23.25 12.0153 23.25C17.5871 23.25 22.2134 19.2052 23.1191 13.8923C23.1382 13.7481 23.1535 13.6032 23.1703 13.4577C23.3942 11.526 23.1455 9.49562 22.4397 7.79776Z"
fill="url(#paint2_radial_3681_24667)"
/>
<path
d="M16.965 9.12184C16.9896 9.13909 17.0119 9.15625 17.035 9.1734C16.7523 8.67164 16.4002 8.21224 15.9892 7.80878C12.4874 4.3074 15.071 0.216811 15.5067 0.00906055C15.5079 0.00737305 15.5106 0.00357617 15.5114 0.00268555C12.6815 1.66 11.7215 4.72586 11.6333 6.25975C11.7646 6.25065 11.8954 6.23964 12.029 6.23964C14.1408 6.23964 15.9801 7.40073 16.965 9.12184Z"
fill="url(#paint3_radial_3681_24667)"
/>
<path
d="M12.0361 9.82097C12.0176 10.1012 11.0276 11.0675 10.6814 11.0675C7.47799 11.0675 6.95801 13.0051 6.95801 13.0051C7.0999 14.6368 8.23587 15.9805 9.61165 16.6915C9.67441 16.7239 9.73793 16.7532 9.80149 16.7822C9.91047 16.8304 10.0208 16.8756 10.1324 16.9175C10.6041 17.0845 11.0982 17.1798 11.5982 17.2002C17.2129 17.4635 18.3007 10.488 14.2488 8.4623C15.2864 8.28183 16.3635 8.69916 16.965 9.12169C15.9801 7.40072 14.1408 6.23962 12.0291 6.23962C11.8955 6.23962 11.7647 6.25064 11.6334 6.25973C10.5033 6.33742 9.42291 6.75376 8.53296 7.45448C8.70471 7.5998 8.89859 7.79405 9.30705 8.19642C10.0712 8.94956 12.0319 9.72947 12.0361 9.82097Z"
fill="url(#paint4_radial_3681_24667)"
/>
<path
d="M12.0361 9.82097C12.0176 10.1012 11.0276 11.0675 10.6814 11.0675C7.47799 11.0675 6.95801 13.0051 6.95801 13.0051C7.0999 14.6368 8.23587 15.9805 9.61165 16.6915C9.67441 16.7239 9.73793 16.7532 9.80149 16.7822C9.91047 16.8304 10.0208 16.8756 10.1324 16.9175C10.6041 17.0845 11.0982 17.1798 11.5982 17.2002C17.2129 17.4635 18.3007 10.488 14.2488 8.4623C15.2864 8.28183 16.3635 8.69916 16.965 9.12169C15.9801 7.40072 14.1408 6.23962 12.0291 6.23962C11.8955 6.23962 11.7647 6.25064 11.6334 6.25973C10.5033 6.33742 9.42291 6.75376 8.53296 7.45448C8.70471 7.5998 8.89859 7.79405 9.30705 8.19642C10.0712 8.94956 12.0319 9.72947 12.0361 9.82097Z"
fill="url(#paint5_radial_3681_24667)"
/>
<path
d="M8.00739 7.07982C8.08584 7.13043 8.16368 7.18199 8.24087 7.23451C7.98411 6.33619 7.97319 5.38542 8.20928 4.48145C7.05203 5.00837 6.15193 5.84129 5.49756 6.57676C5.5517 6.57521 7.18571 6.54582 8.00739 7.07982Z"
fill="url(#paint6_radial_3681_24667)"
/>
<path
d="M0.853976 12.2536C1.72318 17.3901 6.37946 21.3118 11.6649 21.4614C16.5568 21.5999 19.6822 18.7605 20.9731 15.9904C21.8075 14.1997 22.3866 11.2701 21.3237 8.38842L21.3233 8.3872L21.3218 8.37628C21.3206 8.36793 21.3199 8.36296 21.3201 8.36568C21.3201 8.36751 21.3208 8.37206 21.3223 8.38443C21.7219 10.9935 20.3947 13.5212 18.3199 15.2302L18.3137 15.2449C14.271 18.5366 10.4024 17.2309 9.61913 16.6964C9.56408 16.67 9.50939 16.6427 9.45507 16.6147C7.0981 15.4884 6.12441 13.3411 6.3332 11.4996C4.34302 11.4996 3.66441 9.82101 3.66441 9.82101C3.66441 9.82101 5.45124 8.54699 7.8062 9.65503C9.98729 10.6813 12.0356 9.8211 12.0359 9.82101C12.0317 9.72951 10.071 8.9496 9.30667 8.19651C8.89824 7.79414 8.70432 7.60012 8.53257 7.45457C8.4394 7.37587 8.34198 7.30232 8.24077 7.23426C8.16349 7.18188 8.08566 7.13031 8.00729 7.07957C7.18566 6.54557 5.5516 6.57496 5.49746 6.57637H5.49226C5.04568 6.01073 5.07718 4.14482 5.10263 3.75524C5.09729 3.7311 4.76949 3.9254 4.72655 3.9547C4.33248 4.23598 3.96408 4.55159 3.62565 4.89782C3.24052 5.28846 2.88865 5.71053 2.57368 6.15965C2.57368 6.16021 2.57335 6.16087 2.57316 6.16143C2.57316 6.16082 2.57349 6.16021 2.57368 6.15965C1.84929 7.18619 1.33553 8.34611 1.0621 9.57238C1.05671 9.59681 0.656632 11.3462 0.853976 12.2536Z"
fill="url(#paint7_radial_3681_24667)"
/>
<path
d="M15.9894 7.80883C16.4004 8.21229 16.7525 8.67169 17.0352 9.17345C17.0972 9.22005 17.1552 9.2665 17.2043 9.31183C19.7582 11.665 18.4201 14.9931 18.3203 15.2303C20.395 13.5212 21.7222 10.9936 21.3227 8.38445C20.0489 5.20951 17.8889 3.92926 16.1251 1.1417C16.0361 1.00075 15.9468 0.85942 15.8598 0.710452C15.8155 0.634372 15.7741 0.556628 15.7357 0.477389C15.6625 0.335913 15.6059 0.186371 15.5673 0.0317953C15.5676 0.0244774 15.5651 0.017323 15.5604 0.0117374C15.5556 0.00615177 15.549 0.00253868 15.5417 0.00160783C15.5348 -0.000373182 15.5275 -0.000373182 15.5206 0.00160783C15.519 0.00217033 15.5166 0.00399845 15.515 0.0046547C15.5125 0.00563908 15.5093 0.00788908 15.5067 0.0093422C15.0712 0.216905 12.4876 4.3075 15.9894 7.80883Z"
fill="url(#paint8_radial_3681_24667)"
/>
<path
d="M17.2043 9.31181C17.1551 9.26648 17.0972 9.22003 17.0352 9.17343C17.0123 9.15618 16.9898 9.13903 16.9652 9.12187C16.3637 8.69934 15.2866 8.28201 14.2489 8.46248C18.3008 10.4881 17.2131 17.4637 11.5984 17.2004C11.0984 17.18 10.6043 17.0847 10.1326 16.9177C10.021 16.8757 9.91066 16.8306 9.80166 16.7824C9.7381 16.7534 9.67458 16.7241 9.61182 16.6917C9.61407 16.6932 9.61716 16.6949 9.61946 16.6964C10.4027 17.2307 14.2713 18.5365 18.314 15.2448L18.3202 15.2302C18.42 14.9932 19.7581 11.665 17.2043 9.31181Z"
fill="url(#paint9_radial_3681_24667)"
/>
<path
d="M6.95794 13.0051C6.95794 13.0051 7.47793 11.0675 10.6813 11.0675C11.0276 11.0675 12.0177 10.1012 12.036 9.82096C12.0543 9.54074 9.98756 10.6812 7.80633 9.65497C5.45138 8.54694 3.66455 9.82096 3.66455 9.82096C3.66455 9.82096 4.34316 11.4995 6.33333 11.4995C6.1246 13.341 7.09828 15.4886 9.45521 16.6147C9.50789 16.6399 9.55739 16.6674 9.61149 16.6915C8.2358 15.9805 7.09983 14.6368 6.95794 13.0051Z"
fill="url(#paint10_radial_3681_24667)"
/>
<path
d="M22.4396 7.79786C21.95 6.62017 20.9583 5.34873 20.1796 4.94687C20.8134 6.1893 21.1802 7.43561 21.3203 8.36575C21.3203 8.36758 21.321 8.37212 21.3225 8.3845C20.0487 5.20951 17.8888 3.92926 16.125 1.1417C16.0359 1.00075 15.9466 0.85942 15.8596 0.710452C15.8153 0.634372 15.774 0.556628 15.7356 0.477389C15.6623 0.335913 15.6058 0.186371 15.5672 0.0317953C15.5675 0.0244774 15.565 0.017323 15.5602 0.0117374C15.5555 0.00615177 15.5489 0.00253868 15.5416 0.00160783C15.5347 -0.000373182 15.5274 -0.000373182 15.5205 0.00160783C15.5189 0.00217033 15.5165 0.00399845 15.5148 0.0046547C15.5123 0.00563908 15.5092 0.00788908 15.5066 0.0093422C15.5078 0.0076547 15.5105 0.00385783 15.5113 0.0029672C12.6814 1.66028 11.7214 4.72614 11.6332 6.26003C11.7645 6.25094 11.8953 6.23992 12.0289 6.23992C14.1408 6.23992 15.9801 7.40101 16.9649 9.12198C16.3634 8.69945 15.2863 8.28212 14.2486 8.46259C18.3005 10.4882 17.2127 17.4638 11.598 17.2005C11.098 17.1801 10.6039 17.0848 10.1322 16.9178C10.0207 16.8758 9.91032 16.8307 9.80133 16.7825C9.73777 16.7535 9.67425 16.7242 9.61148 16.6918C9.61373 16.6933 9.61683 16.6951 9.61912 16.6965C9.56407 16.67 9.50938 16.6427 9.45506 16.6148C9.50775 16.6399 9.55725 16.6675 9.61134 16.6916C8.23556 15.9806 7.09959 14.6369 6.9577 13.0052C6.9577 13.0052 7.47769 11.0676 10.6811 11.0676C11.0274 11.0676 12.0174 10.1013 12.0358 9.82108C12.0316 9.72958 10.0709 8.94967 9.30656 8.19658C8.89814 7.7942 8.70422 7.60019 8.53247 7.45464C8.43929 7.37594 8.34188 7.30239 8.24067 7.23433C7.98391 6.33601 7.97299 5.38524 8.20908 4.48126C7.05183 5.00819 6.15173 5.84111 5.49736 6.57658H5.49216C5.04558 6.01094 5.07708 4.14503 5.10253 3.75545C5.09719 3.73131 4.76939 3.92561 4.72645 3.9549C4.33238 4.23619 3.96398 4.5518 3.62555 4.89803C3.24054 5.28863 2.88878 5.71066 2.57391 6.15972C2.57391 6.16028 2.57358 6.16094 2.57339 6.1615C2.57339 6.16089 2.57372 6.16028 2.57391 6.15972C1.84952 7.18625 1.33576 8.34618 1.06233 9.57245C1.05694 9.59687 1.05239 9.62219 1.04714 9.6468C1.02595 9.74598 0.930609 10.2493 0.917297 10.3572C0.916266 10.3655 0.918281 10.349 0.917297 10.3572C0.830351 10.8773 0.774875 11.4022 0.751172 11.929C0.751172 11.9482 0.75 11.9672 0.75 11.9865C0.75 18.2072 5.79375 23.2501 12.0152 23.2501C17.587 23.2501 22.2133 19.2053 23.119 13.8924C23.1381 13.7482 23.1534 13.6033 23.1702 13.4578C23.3941 11.5261 23.1454 9.49572 22.4396 7.79786ZM21.322 8.37634C21.3226 8.38033 21.3233 8.3845 21.3239 8.38848L21.3235 8.38726L21.322 8.37634Z"
fill="url(#paint11_linear_3681_24667)"
/>
</g>
<defs>
<linearGradient
id="paint0_linear_3681_24667"
x1="20.3814"
y1="3.60386"
x2="2.29295"
y2="21.0528"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.05" stopColor="#FFF44F" />
<stop offset="0.37" stopColor="#FF980E" />
<stop offset="0.53" stopColor="#FF3647" />
<stop offset="0.7" stopColor="#E31587" />
</linearGradient>
<radialGradient
id="paint1_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(16.3404 2.59171) scale(23.0401 23.4281)"
>
<stop offset="0.13" stopColor="#FFBD4F" />
<stop offset="0.28" stopColor="#FF980E" />
<stop offset="0.47" stopColor="#FF3750" />
<stop offset="0.78" stopColor="#EB0878" />
<stop offset="0.86" stopColor="#E50080" />
</radialGradient>
<radialGradient
id="paint2_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(9.65968 12.2681) scale(23.6161 23.4281)"
>
<stop offset="0.3" stopColor="#960E18" />
<stop offset="0.35" stopColor="#B11927" stopOpacity="0.74" />
<stop offset="0.43" stopColor="#DB293D" stopOpacity="0.34" />
<stop offset="0.5" stopColor="#F5334B" stopOpacity="0.09" />
<stop offset="0.53" stopColor="#FF3750" stopOpacity="0" />
</radialGradient>
<radialGradient
id="paint3_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(14.2261 -1.0978) scale(7.56236 12.839)"
>
<stop offset="0.13" stopColor="#FFF44F" />
<stop offset="0.53" stopColor="#FF980E" />
</radialGradient>
<radialGradient
id="paint4_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(9.2356 18.3163) scale(10.007 10.9678)"
>
<stop offset="0.35" stopColor="#3A8EE6" />
<stop offset="0.67" stopColor="#9059FF" />
<stop offset="1" stopColor="#C139E6" />
</radialGradient>
<radialGradient
id="paint5_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(10.9455 9.859) scale(5.31373 6.47101)"
>
<stop offset="0.21" stopColor="#9059FF" stopOpacity="0" />
<stop offset="0.97" stopColor="#6E008B" stopOpacity="0.6" />
</radialGradient>
<radialGradient
id="paint6_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(11.2585 1.72838) scale(7.95561 7.98388)"
>
<stop offset="0.1" stopColor="#FFE226" />
<stop offset="0.79" stopColor="#FF7139" />
</radialGradient>
<radialGradient
id="paint7_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(18.5242 -3.50921) scale(37.9818 31.8836)"
>
<stop offset="0.11" stopColor="#FFF44F" />
<stop offset="0.46" stopColor="#FF980E" />
<stop offset="0.72" stopColor="#FF3647" />
<stop offset="0.9" stopColor="#E31587" />
</radialGradient>
<radialGradient
id="paint8_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4.41888 7.02007) rotate(77.3946) scale(12.0503 52.1278)"
>
<stop stopColor="#FFF44F" />
<stop offset="0.3" stopColor="#FF980E" />
<stop offset="0.57" stopColor="#FF3647" />
<stop offset="0.74" stopColor="#E31587" />
</radialGradient>
<radialGradient
id="paint9_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(11.3407 4.60001) scale(21.8071 21.424)"
>
<stop offset="0.14" stopColor="#FFF44F" />
<stop offset="0.48" stopColor="#FF980E" />
<stop offset="0.66" stopColor="#FF3647" />
<stop offset="0.9" stopColor="#E31587" />
</radialGradient>
<radialGradient
id="paint10_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(17.0005 5.8529) scale(26.2114 23.4492)"
>
<stop offset="0.09" stopColor="#FFF44F" />
<stop offset="0.63" stopColor="#FF980E" />
</radialGradient>
<linearGradient
id="paint11_linear_3681_24667"
x1="18.75"
y1="3.25511"
x2="4.28552"
y2="19.0592"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.17" stopColor="#FFF44F" stopOpacity="0.8" />
<stop offset="0.6" stopColor="#FFF44F" stopOpacity="0" />
</linearGradient>
<clipPath id="clip0_3681_24667">
<rect width="24" height="24" fill="white" />
</clipPath>
</defs>
</svg>
);
};
export const BrowserIcon = () => {
const { MOZILLA_EXTENSION_URL, CHROME_EXTENSION_URL } = useEnvironment();
const isItChrome = window.navigator.userAgent.includes('Chrome');
const isItMozilla =
window.navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
return (
<div className="absolute top-0 flex items-center h-8 right-1">
<div className="absolute right-1 top-0 h-8 flex items-center">
{!isItChrome && !isItMozilla ? (
<>
<a href={mozillaExtensionUrl} target="_blank" rel="noreferrer">
<a href={MOZILLA_EXTENSION_URL} target="_blank" rel="noreferrer">
<MozillaIcon />
</a>{' '}
<a href={chromeExtensionUrl} target="_blank" rel="noreferrer">
<a href={CHROME_EXTENSION_URL} target="_blank" rel="noreferrer">
<ChromeIcon />
</a>
</>
@@ -1,10 +1,12 @@
import { act, fireEvent, render, screen } from '@testing-library/react';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type { VegaWalletConfig } from '../provider';
import { VegaWalletProvider } from '../provider';
import { VegaConnectDialog, CLOSE_DELAY } from './connect-dialog';
import { useVegaWalletDialogStore } from './vega-wallet-dialog-store';
import {
VegaConnectDialog,
CLOSE_DELAY,
useVegaWalletDialogStore,
} from './connect-dialog';
import type { VegaConnectDialogProps } from '..';
import {
ClientErrors,
@@ -13,6 +15,7 @@ import {
ViewConnector,
WalletError,
} from '../connectors';
import { useEnvironment } from '@vegaprotocol/environment';
import type { ChainIdQuery } from './__generated__/ChainId';
import { ChainIdDocument } from './__generated__/ChainId';
import {
@@ -25,14 +28,26 @@ import {
const mockUpdateDialogOpen = jest.fn();
const mockCloseVegaDialog = jest.fn();
jest.mock('@vegaprotocol/environment');
let mockIsDesktopRunning = true;
jest.mock('../use-is-wallet-service-running', () => ({
useIsWalletServiceRunning: jest
.fn()
.mockImplementation(() => mockIsDesktopRunning),
}));
// @ts-ignore ignore mock implementation
useEnvironment.mockImplementation(() => ({
VEGA_ENV: 'TESTNET',
VEGA_URL: 'https://vega-node.url',
VEGA_NETWORKS: JSON.stringify({}),
VEGA_WALLET_URL: mockVegaWalletUrl,
GIT_BRANCH: 'test',
GIT_COMMIT_HASH: 'abcdef',
GIT_ORIGIN_URL: 'https://github.com/test/repo',
HOSTED_WALLET_URL: mockHostedWalletUrl,
}));
let defaultProps: VegaConnectDialogProps;
const INITIAL_KEY = 'some-key';
@@ -44,9 +59,7 @@ const connectors = {
jsonRpc,
view,
injected,
snap: undefined,
};
beforeEach(() => {
jest.clearAllMocks();
defaultProps = {
@@ -60,24 +73,12 @@ beforeEach(() => {
});
});
const mockVegaWalletUrl = 'http://mock.wallet.com';
const mockHostedWalletUrl = 'http://mock.hosted.com';
const mockChainId = 'chain-id';
const defaultConfig: VegaWalletConfig = {
network: 'TESTNET',
vegaUrl: 'https://vega.xyz',
vegaWalletServiceUrl: 'https://vegaservice.xyz',
links: {
explorer: 'explorer-link',
concepts: 'concepts-link',
chromeExtensionUrl: 'chrome-link',
mozillaExtensionUrl: 'mozilla-link',
},
};
function generateJSX(
props?: Partial<VegaConnectDialogProps>,
config?: Partial<VegaWalletConfig>
) {
function generateJSX(props?: Partial<VegaConnectDialogProps>) {
const chainIdMock: MockedResponse<ChainIdQuery> = {
request: {
query: ChainIdDocument,
@@ -92,7 +93,7 @@ function generateJSX(
};
return (
<MockedProvider mocks={[chainIdMock]}>
<VegaWalletProvider config={{ ...defaultConfig, ...config }}>
<VegaWalletProvider>
<VegaConnectDialog {...defaultProps} {...props} />
</VegaWalletProvider>
</MockedProvider>
@@ -1,4 +1,5 @@
import classNames from 'classnames';
import { create } from 'zustand';
import {
Dialog,
Intent,
@@ -13,7 +14,7 @@ import type { ReactNode } from 'react';
import { useCallback, useState } from 'react';
import type { WalletClientError } from '@vegaprotocol/wallet-client';
import { t } from '@vegaprotocol/i18n';
import type { Connectors, VegaConnector } from '../connectors';
import type { VegaConnector } from '../connectors';
import {
DEFAULT_SNAP_ID,
InjectedConnector,
@@ -24,6 +25,7 @@ import {
} from '../connectors';
import { JsonRpcConnectorForm } from './json-rpc-connector-form';
import { ViewConnectorForm } from './view-connector-form';
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
import {
BrowserIcon,
ConnectDialogContent,
@@ -40,10 +42,9 @@ import { InjectedConnectorForm } from './injected-connector-form';
import { isBrowserWalletInstalled } from '../utils';
import { useIsWalletServiceRunning } from '../use-is-wallet-service-running';
import { useIsSnapRunning } from '../use-is-snap-running';
import { useVegaWalletDialogStore } from './vega-wallet-dialog-store';
export const CLOSE_DELAY = 1700;
type Connectors = { [key: string]: VegaConnector };
export type WalletType = 'injected' | 'jsonRpc' | 'view' | 'snap';
export interface VegaConnectDialogProps {
@@ -51,6 +52,23 @@ export interface VegaConnectDialogProps {
riskMessage?: ReactNode;
}
export interface VegaWalletDialogStore {
vegaWalletDialogOpen: boolean;
updateVegaWalletDialog: (open: boolean) => void;
openVegaWalletDialog: () => void;
closeVegaWalletDialog: () => void;
}
export const useVegaWalletDialogStore = create<VegaWalletDialogStore>()(
(set) => ({
vegaWalletDialogOpen: false,
updateVegaWalletDialog: (open: boolean) =>
set({ vegaWalletDialogOpen: open }),
openVegaWalletDialog: () => set({ vegaWalletDialogOpen: true }),
closeVegaWalletDialog: () => set({ vegaWalletDialogOpen: false }),
})
);
export const VegaConnectDialog = ({
connectors,
riskMessage,
@@ -105,12 +123,12 @@ const ConnectDialogContainer = ({
appChainId: string;
riskMessage?: ReactNode;
}) => {
const { vegaUrl, vegaWalletServiceUrl } = useVegaWallet();
const { VEGA_WALLET_URL } = useEnvironment();
const closeDialog = useVegaWalletDialogStore(
(store) => store.closeVegaWalletDialog
);
const [selectedConnector, setSelectedConnector] = useState<VegaConnector>();
const [walletUrl, setWalletUrl] = useState(vegaWalletServiceUrl);
const [walletUrl, setWalletUrl] = useState(VEGA_WALLET_URL || '');
const reset = useCallback(() => {
setSelectedConnector(undefined);
@@ -129,6 +147,7 @@ const ConnectDialogContainer = ({
const handleSelect = (type: WalletType) => {
const connector = connectors[type];
connector.url = walletUrl;
if (!connector) {
// we should never get here unless connectors are not configured correctly
@@ -140,28 +159,21 @@ const ConnectDialogContainer = ({
// Immediately connect on selection if jsonRpc is selected, we can't do this
// for rest because we need to show an authentication form
if (connector instanceof JsonRpcConnector) {
connector.url = walletUrl;
jsonRpcConnect(connector, appChainId);
} else if (connector instanceof InjectedConnector) {
injectedConnect(connector, appChainId);
} else if (connector instanceof SnapConnector) {
// Set the nodeAddress to send tx's to, normally this is handled by
// the vega wallet
connector.nodeAddress = new URL(vegaUrl).origin;
} else if (
connector instanceof InjectedConnector ||
connector instanceof SnapConnector
) {
injectedConnect(connector, appChainId);
}
};
const isDesktopWalletRunning = useIsWalletServiceRunning(
walletUrl,
connectors['jsonRpc'],
connectors,
appChainId
);
const isSnapRunning = useIsSnapRunning(
DEFAULT_SNAP_ID,
Boolean(connectors['snap'])
);
const isSnapRunning = useIsSnapRunning(DEFAULT_SNAP_ID);
return (
<>
@@ -178,7 +190,6 @@ const ConnectDialogContainer = ({
/>
) : (
<ConnectorList
connectors={connectors}
walletUrl={walletUrl}
setWalletUrl={setWalletUrl}
onSelect={handleSelect}
@@ -193,34 +204,29 @@ const ConnectDialogContainer = ({
};
const ConnectorList = ({
connectors,
onSelect,
walletUrl,
setWalletUrl,
isDesktopWalletRunning,
isSnapRunning,
}: {
connectors: Connectors;
onSelect: (type: WalletType) => void;
walletUrl: string;
setWalletUrl: (value: string) => void;
isDesktopWalletRunning: boolean | null;
isSnapRunning: boolean | null;
}) => {
const { pubKey, links } = useVegaWallet();
const { pubKey } = useVegaWallet();
const title = isBrowserWalletInstalled()
? t('Connect Vega wallet')
: t('Get a Vega wallet');
const extendedText = (
<>
<div className="flex items-center justify-center w-full h-full text-base gap-1">
<div className="w-full h-full flex justify-center items-center gap-1 text-base">
{t('Connect')}
</div>
<BrowserIcon
chromeExtensionUrl={links.chromeExtensionUrl}
mozillaExtensionUrl={links.mozillaExtensionUrl}
/>
<BrowserIcon />
</>
);
@@ -241,23 +247,20 @@ const ConnectorList = ({
onClick={() => onSelect('injected')}
/>
) : (
<GetWalletButton
chromeExtensionUrl={links.chromeExtensionUrl}
mozillaExtensionUrl={links.mozillaExtensionUrl}
/>
<GetWalletButton />
)}
</div>
{connectors['snap'] !== undefined ? (
{FLAGS.METAMASK_SNAPS ? (
<div>
{isSnapRunning ? (
<ConnectionOption
type="snap"
text={
<>
<div className="flex items-center justify-center w-full h-full text-base gap-1">
<div className="w-full h-full flex justify-center items-center gap-1 text-base">
{t('Connect via Vega MetaMask Snap')}
</div>
<div className="absolute top-0 flex items-center h-8 right-1">
<div className="absolute right-1 top-0 h-8 flex items-center">
<VegaIcon name={VegaIconNames.METAMASK} size={24} />
</div>
</>
@@ -271,10 +274,10 @@ const ConnectorList = ({
type="snap"
text={
<>
<div className="flex items-center justify-center w-full h-full text-base gap-1">
<div className="w-full h-full flex justify-center items-center gap-1 text-base">
{t('Install Vega MetaMask Snap')}
</div>
<div className="absolute top-0 flex items-center h-8 right-1">
<div className="absolute right-1 top-0 h-8 flex items-center">
<VegaIcon name={VegaIconNames.METAMASK} size={24} />
</div>
</>
@@ -371,43 +374,30 @@ const SelectedForm = ({
throw new Error('No connector selected');
};
export const GetWalletButton = ({
chromeExtensionUrl,
mozillaExtensionUrl,
className,
}: {
chromeExtensionUrl?: string;
mozillaExtensionUrl?: string;
className?: string;
}) => {
export const GetWalletButton = ({ className }: { className?: string }) => {
const { MOZILLA_EXTENSION_URL, CHROME_EXTENSION_URL } = useEnvironment();
const isItChrome = window.navigator.userAgent.includes('Chrome');
const isItMozilla =
window.navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
const onClick = () => {
if (isItMozilla) {
window.open(mozillaExtensionUrl, '_blank');
window.open(MOZILLA_EXTENSION_URL, '_blank');
return;
}
if (isItChrome) {
window.open(chromeExtensionUrl, '_blank');
window.open(CHROME_EXTENSION_URL, '_blank');
}
};
const buttonContent = (
<>
<div className="flex items-center justify-center text-base gap-1">
<div className="flex items-center justify-center gap-1 text-base">
{t('Get the Vega Wallet')}
<Pill size="xxs" intent={Intent.Info}>
ALPHA
</Pill>
</div>
{chromeExtensionUrl && mozillaExtensionUrl && (
<BrowserIcon
chromeExtensionUrl={chromeExtensionUrl}
mozillaExtensionUrl={mozillaExtensionUrl}
/>
)}
<BrowserIcon />
</>
);
@@ -462,7 +452,7 @@ const ConnectionOption = ({
icon={icon}
fill
>
<span className="flex items-center justify-center text-base">{text}</span>
<span className="flex justify-center items-center text-base">{text}</span>
</TradingButton>
);
};
@@ -518,7 +508,7 @@ const CustomUrlInput = ({
onClick={() => onSelect('jsonRpc')}
/>
{isDesktopWalletRunning !== null && (
<p className="pt-2 mb-6 text-sm">
<p className="mb-6 text-sm pt-2">
{isDesktopWalletRunning ? (
<button
className="underline text-default"
-1
View File
@@ -1,3 +1,2 @@
export * from './connect-dialog';
export * from './view-as-dialog';
export * from './vega-wallet-dialog-store';
@@ -14,6 +14,7 @@ import type { JsonRpcConnector } from '../connectors';
import { ClientErrors } from '../connectors';
import { ConnectDialogTitle } from './connect-dialog-elements';
import { Status } from '../use-json-rpc-connect';
import { DocsLinks } from '@vegaprotocol/environment';
import { useVegaWallet } from '../use-vega-wallet';
import { setAcknowledged } from '../storage';
@@ -155,7 +156,6 @@ const Error = ({
appChainId: string;
onTryAgain: () => void;
}) => {
const { links } = useVegaWallet();
let title = t('Something went wrong');
let text: ReactNode | undefined = t('An unknown error occurred');
let tryAgain: ReactNode | null = (
@@ -182,9 +182,11 @@ const Error = ({
<>
{capitalize(error.message)}
{'. '}
<Link href={links.concepts}>
{t('Read the docs to troubleshoot')}
</Link>
{DocsLinks && (
<Link href={DocsLinks.VEGA_WALLET_CONCEPTS_URL}>
{t('Read the docs to troubleshoot')}
</Link>
)}
</>
);
} else if (error.code === ServiceErrors.REQUEST_PROCESSING) {
@@ -1,214 +0,0 @@
export const MozillaIcon = () => {
return (
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
data-testid="mozilla-logo"
>
<g clipPath="url(#clip0_3681_24667)">
<path
d="M22.4398 7.79786C21.9502 6.62017 20.9585 5.34873 20.1798 4.94687C20.8136 6.1893 21.1804 7.43561 21.3205 8.36575C21.3205 8.36758 21.3212 8.37212 21.3227 8.3845C20.0489 5.20951 17.889 3.92926 16.1252 1.1417C16.0361 1.00075 15.9468 0.85942 15.8598 0.710452C15.8155 0.634372 15.7742 0.556628 15.7358 0.477389C15.6625 0.335913 15.606 0.186371 15.5674 0.0317953C15.5676 0.0244774 15.5652 0.017323 15.5604 0.0117374C15.5557 0.00615177 15.549 0.00253868 15.5418 0.00160783C15.5349 -0.000373182 15.5275 -0.000373182 15.5206 0.00160783C15.519 0.00217033 15.5167 0.00399845 15.515 0.0046547C15.5125 0.00563908 15.5094 0.00788908 15.5068 0.0093422C15.508 0.0076547 15.5107 0.00385783 15.5115 0.0029672C12.6816 1.66028 11.7216 4.72614 11.6334 6.26003C10.5034 6.33772 9.42293 6.75406 8.53298 7.45478C8.43981 7.37608 8.3424 7.30253 8.24119 7.23447C7.98442 6.33615 7.97351 5.38538 8.20959 4.4814C7.05234 5.00833 6.15225 5.84125 5.49787 6.57672H5.49267C5.04609 6.01108 5.07759 4.14517 5.10305 3.75559C5.0977 3.73145 4.76991 3.92575 4.72697 3.95505C4.3329 4.23633 3.96449 4.55194 3.62606 4.89817C3.24095 5.28871 2.88907 5.71069 2.57409 6.15972C2.57409 6.16028 2.57377 6.16094 2.57358 6.1615C2.57358 6.16089 2.57391 6.16028 2.57409 6.15972C1.8497 7.18625 1.33595 8.34618 1.06252 9.57245C1.05712 9.59687 1.05258 9.62219 1.04733 9.6468C1.02614 9.74598 0.949828 10.2421 0.936469 10.3499C0.935438 10.3582 0.934969 10.3662 0.933984 10.3745C0.835324 10.8874 0.774224 11.4069 0.751172 11.9287C0.751172 11.9479 0.75 11.967 0.75 11.9862C0.750187 18.2072 5.79394 23.2501 12.0154 23.2501C17.5872 23.2501 22.2135 19.2053 23.1192 13.8924C23.1383 13.7482 23.1536 13.6033 23.1704 13.4578C23.3943 11.5261 23.1456 9.49572 22.4398 7.79786ZM9.45562 16.6148C9.50831 16.6399 9.55781 16.6675 9.61191 16.6916C9.61416 16.6931 9.61725 16.6949 9.61955 16.6963C9.56449 16.67 9.50984 16.6428 9.45562 16.6148ZM21.3236 8.38726L21.3221 8.37634C21.3227 8.38033 21.3234 8.3845 21.324 8.38848L21.3236 8.38726Z"
fill="url(#paint0_linear_3681_24667)"
/>
<path
d="M22.4397 7.79776C21.9501 6.62007 20.9584 5.34864 20.1797 4.94678C20.8135 6.1892 21.1803 7.43551 21.3204 8.36565C21.3204 8.36293 21.321 8.3679 21.3221 8.37625C21.3227 8.38023 21.3234 8.3844 21.324 8.38839C22.3869 11.2698 21.8078 14.1999 20.9734 15.9903C19.6825 18.7607 16.5571 21.5999 11.6652 21.4614C6.37978 21.3117 1.7235 17.39 0.854297 12.2536C0.695906 11.4436 0.854297 11.0323 0.933984 10.3746C0.836906 10.8816 0.799922 11.0281 0.751172 11.9289C0.751172 11.9481 0.75 11.9671 0.75 11.9864C0.750094 18.2071 5.79384 23.25 12.0153 23.25C17.5871 23.25 22.2134 19.2052 23.1191 13.8923C23.1382 13.7481 23.1535 13.6032 23.1703 13.4577C23.3942 11.526 23.1455 9.49562 22.4397 7.79776Z"
fill="url(#paint1_radial_3681_24667)"
/>
<path
d="M22.4397 7.79776C21.9501 6.62007 20.9584 5.34864 20.1797 4.94678C20.8135 6.1892 21.1803 7.43551 21.3204 8.36565C21.3204 8.36293 21.321 8.3679 21.3221 8.37625C21.3227 8.38023 21.3234 8.3844 21.324 8.38839C22.3869 11.2698 21.8078 14.1999 20.9734 15.9903C19.6825 18.7607 16.5571 21.5999 11.6652 21.4614C6.37978 21.3117 1.7235 17.39 0.854297 12.2536C0.695906 11.4436 0.854297 11.0323 0.933984 10.3746C0.836906 10.8816 0.799922 11.0281 0.751172 11.9289C0.751172 11.9481 0.75 11.9671 0.75 11.9864C0.750094 18.2071 5.79384 23.25 12.0153 23.25C17.5871 23.25 22.2134 19.2052 23.1191 13.8923C23.1382 13.7481 23.1535 13.6032 23.1703 13.4577C23.3942 11.526 23.1455 9.49562 22.4397 7.79776Z"
fill="url(#paint2_radial_3681_24667)"
/>
<path
d="M16.965 9.12184C16.9896 9.13909 17.0119 9.15625 17.035 9.1734C16.7523 8.67164 16.4002 8.21224 15.9892 7.80878C12.4874 4.3074 15.071 0.216811 15.5067 0.00906055C15.5079 0.00737305 15.5106 0.00357617 15.5114 0.00268555C12.6815 1.66 11.7215 4.72586 11.6333 6.25975C11.7646 6.25065 11.8954 6.23964 12.029 6.23964C14.1408 6.23964 15.9801 7.40073 16.965 9.12184Z"
fill="url(#paint3_radial_3681_24667)"
/>
<path
d="M12.0361 9.82097C12.0176 10.1012 11.0276 11.0675 10.6814 11.0675C7.47799 11.0675 6.95801 13.0051 6.95801 13.0051C7.0999 14.6368 8.23587 15.9805 9.61165 16.6915C9.67441 16.7239 9.73793 16.7532 9.80149 16.7822C9.91047 16.8304 10.0208 16.8756 10.1324 16.9175C10.6041 17.0845 11.0982 17.1798 11.5982 17.2002C17.2129 17.4635 18.3007 10.488 14.2488 8.4623C15.2864 8.28183 16.3635 8.69916 16.965 9.12169C15.9801 7.40072 14.1408 6.23962 12.0291 6.23962C11.8955 6.23962 11.7647 6.25064 11.6334 6.25973C10.5033 6.33742 9.42291 6.75376 8.53296 7.45448C8.70471 7.5998 8.89859 7.79405 9.30705 8.19642C10.0712 8.94956 12.0319 9.72947 12.0361 9.82097Z"
fill="url(#paint4_radial_3681_24667)"
/>
<path
d="M12.0361 9.82097C12.0176 10.1012 11.0276 11.0675 10.6814 11.0675C7.47799 11.0675 6.95801 13.0051 6.95801 13.0051C7.0999 14.6368 8.23587 15.9805 9.61165 16.6915C9.67441 16.7239 9.73793 16.7532 9.80149 16.7822C9.91047 16.8304 10.0208 16.8756 10.1324 16.9175C10.6041 17.0845 11.0982 17.1798 11.5982 17.2002C17.2129 17.4635 18.3007 10.488 14.2488 8.4623C15.2864 8.28183 16.3635 8.69916 16.965 9.12169C15.9801 7.40072 14.1408 6.23962 12.0291 6.23962C11.8955 6.23962 11.7647 6.25064 11.6334 6.25973C10.5033 6.33742 9.42291 6.75376 8.53296 7.45448C8.70471 7.5998 8.89859 7.79405 9.30705 8.19642C10.0712 8.94956 12.0319 9.72947 12.0361 9.82097Z"
fill="url(#paint5_radial_3681_24667)"
/>
<path
d="M8.00739 7.07982C8.08584 7.13043 8.16368 7.18199 8.24087 7.23451C7.98411 6.33619 7.97319 5.38542 8.20928 4.48145C7.05203 5.00837 6.15193 5.84129 5.49756 6.57676C5.5517 6.57521 7.18571 6.54582 8.00739 7.07982Z"
fill="url(#paint6_radial_3681_24667)"
/>
<path
d="M0.853976 12.2536C1.72318 17.3901 6.37946 21.3118 11.6649 21.4614C16.5568 21.5999 19.6822 18.7605 20.9731 15.9904C21.8075 14.1997 22.3866 11.2701 21.3237 8.38842L21.3233 8.3872L21.3218 8.37628C21.3206 8.36793 21.3199 8.36296 21.3201 8.36568C21.3201 8.36751 21.3208 8.37206 21.3223 8.38443C21.7219 10.9935 20.3947 13.5212 18.3199 15.2302L18.3137 15.2449C14.271 18.5366 10.4024 17.2309 9.61913 16.6964C9.56408 16.67 9.50939 16.6427 9.45507 16.6147C7.0981 15.4884 6.12441 13.3411 6.3332 11.4996C4.34302 11.4996 3.66441 9.82101 3.66441 9.82101C3.66441 9.82101 5.45124 8.54699 7.8062 9.65503C9.98729 10.6813 12.0356 9.8211 12.0359 9.82101C12.0317 9.72951 10.071 8.9496 9.30667 8.19651C8.89824 7.79414 8.70432 7.60012 8.53257 7.45457C8.4394 7.37587 8.34198 7.30232 8.24077 7.23426C8.16349 7.18188 8.08566 7.13031 8.00729 7.07957C7.18566 6.54557 5.5516 6.57496 5.49746 6.57637H5.49226C5.04568 6.01073 5.07718 4.14482 5.10263 3.75524C5.09729 3.7311 4.76949 3.9254 4.72655 3.9547C4.33248 4.23598 3.96408 4.55159 3.62565 4.89782C3.24052 5.28846 2.88865 5.71053 2.57368 6.15965C2.57368 6.16021 2.57335 6.16087 2.57316 6.16143C2.57316 6.16082 2.57349 6.16021 2.57368 6.15965C1.84929 7.18619 1.33553 8.34611 1.0621 9.57238C1.05671 9.59681 0.656632 11.3462 0.853976 12.2536Z"
fill="url(#paint7_radial_3681_24667)"
/>
<path
d="M15.9894 7.80883C16.4004 8.21229 16.7525 8.67169 17.0352 9.17345C17.0972 9.22005 17.1552 9.2665 17.2043 9.31183C19.7582 11.665 18.4201 14.9931 18.3203 15.2303C20.395 13.5212 21.7222 10.9936 21.3227 8.38445C20.0489 5.20951 17.8889 3.92926 16.1251 1.1417C16.0361 1.00075 15.9468 0.85942 15.8598 0.710452C15.8155 0.634372 15.7741 0.556628 15.7357 0.477389C15.6625 0.335913 15.6059 0.186371 15.5673 0.0317953C15.5676 0.0244774 15.5651 0.017323 15.5604 0.0117374C15.5556 0.00615177 15.549 0.00253868 15.5417 0.00160783C15.5348 -0.000373182 15.5275 -0.000373182 15.5206 0.00160783C15.519 0.00217033 15.5166 0.00399845 15.515 0.0046547C15.5125 0.00563908 15.5093 0.00788908 15.5067 0.0093422C15.0712 0.216905 12.4876 4.3075 15.9894 7.80883Z"
fill="url(#paint8_radial_3681_24667)"
/>
<path
d="M17.2043 9.31181C17.1551 9.26648 17.0972 9.22003 17.0352 9.17343C17.0123 9.15618 16.9898 9.13903 16.9652 9.12187C16.3637 8.69934 15.2866 8.28201 14.2489 8.46248C18.3008 10.4881 17.2131 17.4637 11.5984 17.2004C11.0984 17.18 10.6043 17.0847 10.1326 16.9177C10.021 16.8757 9.91066 16.8306 9.80166 16.7824C9.7381 16.7534 9.67458 16.7241 9.61182 16.6917C9.61407 16.6932 9.61716 16.6949 9.61946 16.6964C10.4027 17.2307 14.2713 18.5365 18.314 15.2448L18.3202 15.2302C18.42 14.9932 19.7581 11.665 17.2043 9.31181Z"
fill="url(#paint9_radial_3681_24667)"
/>
<path
d="M6.95794 13.0051C6.95794 13.0051 7.47793 11.0675 10.6813 11.0675C11.0276 11.0675 12.0177 10.1012 12.036 9.82096C12.0543 9.54074 9.98756 10.6812 7.80633 9.65497C5.45138 8.54694 3.66455 9.82096 3.66455 9.82096C3.66455 9.82096 4.34316 11.4995 6.33333 11.4995C6.1246 13.341 7.09828 15.4886 9.45521 16.6147C9.50789 16.6399 9.55739 16.6674 9.61149 16.6915C8.2358 15.9805 7.09983 14.6368 6.95794 13.0051Z"
fill="url(#paint10_radial_3681_24667)"
/>
<path
d="M22.4396 7.79786C21.95 6.62017 20.9583 5.34873 20.1796 4.94687C20.8134 6.1893 21.1802 7.43561 21.3203 8.36575C21.3203 8.36758 21.321 8.37212 21.3225 8.3845C20.0487 5.20951 17.8888 3.92926 16.125 1.1417C16.0359 1.00075 15.9466 0.85942 15.8596 0.710452C15.8153 0.634372 15.774 0.556628 15.7356 0.477389C15.6623 0.335913 15.6058 0.186371 15.5672 0.0317953C15.5675 0.0244774 15.565 0.017323 15.5602 0.0117374C15.5555 0.00615177 15.5489 0.00253868 15.5416 0.00160783C15.5347 -0.000373182 15.5274 -0.000373182 15.5205 0.00160783C15.5189 0.00217033 15.5165 0.00399845 15.5148 0.0046547C15.5123 0.00563908 15.5092 0.00788908 15.5066 0.0093422C15.5078 0.0076547 15.5105 0.00385783 15.5113 0.0029672C12.6814 1.66028 11.7214 4.72614 11.6332 6.26003C11.7645 6.25094 11.8953 6.23992 12.0289 6.23992C14.1408 6.23992 15.9801 7.40101 16.9649 9.12198C16.3634 8.69945 15.2863 8.28212 14.2486 8.46259C18.3005 10.4882 17.2127 17.4638 11.598 17.2005C11.098 17.1801 10.6039 17.0848 10.1322 16.9178C10.0207 16.8758 9.91032 16.8307 9.80133 16.7825C9.73777 16.7535 9.67425 16.7242 9.61148 16.6918C9.61373 16.6933 9.61683 16.6951 9.61912 16.6965C9.56407 16.67 9.50938 16.6427 9.45506 16.6148C9.50775 16.6399 9.55725 16.6675 9.61134 16.6916C8.23556 15.9806 7.09959 14.6369 6.9577 13.0052C6.9577 13.0052 7.47769 11.0676 10.6811 11.0676C11.0274 11.0676 12.0174 10.1013 12.0358 9.82108C12.0316 9.72958 10.0709 8.94967 9.30656 8.19658C8.89814 7.7942 8.70422 7.60019 8.53247 7.45464C8.43929 7.37594 8.34188 7.30239 8.24067 7.23433C7.98391 6.33601 7.97299 5.38524 8.20908 4.48126C7.05183 5.00819 6.15173 5.84111 5.49736 6.57658H5.49216C5.04558 6.01094 5.07708 4.14503 5.10253 3.75545C5.09719 3.73131 4.76939 3.92561 4.72645 3.9549C4.33238 4.23619 3.96398 4.5518 3.62555 4.89803C3.24054 5.28863 2.88878 5.71066 2.57391 6.15972C2.57391 6.16028 2.57358 6.16094 2.57339 6.1615C2.57339 6.16089 2.57372 6.16028 2.57391 6.15972C1.84952 7.18625 1.33576 8.34618 1.06233 9.57245C1.05694 9.59687 1.05239 9.62219 1.04714 9.6468C1.02595 9.74598 0.930609 10.2493 0.917297 10.3572C0.916266 10.3655 0.918281 10.349 0.917297 10.3572C0.830351 10.8773 0.774875 11.4022 0.751172 11.929C0.751172 11.9482 0.75 11.9672 0.75 11.9865C0.75 18.2072 5.79375 23.2501 12.0152 23.2501C17.587 23.2501 22.2133 19.2053 23.119 13.8924C23.1381 13.7482 23.1534 13.6033 23.1702 13.4578C23.3941 11.5261 23.1454 9.49572 22.4396 7.79786ZM21.322 8.37634C21.3226 8.38033 21.3233 8.3845 21.3239 8.38848L21.3235 8.38726L21.322 8.37634Z"
fill="url(#paint11_linear_3681_24667)"
/>
</g>
<defs>
<linearGradient
id="paint0_linear_3681_24667"
x1="20.3814"
y1="3.60386"
x2="2.29295"
y2="21.0528"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.05" stopColor="#FFF44F" />
<stop offset="0.37" stopColor="#FF980E" />
<stop offset="0.53" stopColor="#FF3647" />
<stop offset="0.7" stopColor="#E31587" />
</linearGradient>
<radialGradient
id="paint1_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(16.3404 2.59171) scale(23.0401 23.4281)"
>
<stop offset="0.13" stopColor="#FFBD4F" />
<stop offset="0.28" stopColor="#FF980E" />
<stop offset="0.47" stopColor="#FF3750" />
<stop offset="0.78" stopColor="#EB0878" />
<stop offset="0.86" stopColor="#E50080" />
</radialGradient>
<radialGradient
id="paint2_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(9.65968 12.2681) scale(23.6161 23.4281)"
>
<stop offset="0.3" stopColor="#960E18" />
<stop offset="0.35" stopColor="#B11927" stopOpacity="0.74" />
<stop offset="0.43" stopColor="#DB293D" stopOpacity="0.34" />
<stop offset="0.5" stopColor="#F5334B" stopOpacity="0.09" />
<stop offset="0.53" stopColor="#FF3750" stopOpacity="0" />
</radialGradient>
<radialGradient
id="paint3_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(14.2261 -1.0978) scale(7.56236 12.839)"
>
<stop offset="0.13" stopColor="#FFF44F" />
<stop offset="0.53" stopColor="#FF980E" />
</radialGradient>
<radialGradient
id="paint4_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(9.2356 18.3163) scale(10.007 10.9678)"
>
<stop offset="0.35" stopColor="#3A8EE6" />
<stop offset="0.67" stopColor="#9059FF" />
<stop offset="1" stopColor="#C139E6" />
</radialGradient>
<radialGradient
id="paint5_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(10.9455 9.859) scale(5.31373 6.47101)"
>
<stop offset="0.21" stopColor="#9059FF" stopOpacity="0" />
<stop offset="0.97" stopColor="#6E008B" stopOpacity="0.6" />
</radialGradient>
<radialGradient
id="paint6_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(11.2585 1.72838) scale(7.95561 7.98388)"
>
<stop offset="0.1" stopColor="#FFE226" />
<stop offset="0.79" stopColor="#FF7139" />
</radialGradient>
<radialGradient
id="paint7_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(18.5242 -3.50921) scale(37.9818 31.8836)"
>
<stop offset="0.11" stopColor="#FFF44F" />
<stop offset="0.46" stopColor="#FF980E" />
<stop offset="0.72" stopColor="#FF3647" />
<stop offset="0.9" stopColor="#E31587" />
</radialGradient>
<radialGradient
id="paint8_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4.41888 7.02007) rotate(77.3946) scale(12.0503 52.1278)"
>
<stop stopColor="#FFF44F" />
<stop offset="0.3" stopColor="#FF980E" />
<stop offset="0.57" stopColor="#FF3647" />
<stop offset="0.74" stopColor="#E31587" />
</radialGradient>
<radialGradient
id="paint9_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(11.3407 4.60001) scale(21.8071 21.424)"
>
<stop offset="0.14" stopColor="#FFF44F" />
<stop offset="0.48" stopColor="#FF980E" />
<stop offset="0.66" stopColor="#FF3647" />
<stop offset="0.9" stopColor="#E31587" />
</radialGradient>
<radialGradient
id="paint10_radial_3681_24667"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(17.0005 5.8529) scale(26.2114 23.4492)"
>
<stop offset="0.09" stopColor="#FFF44F" />
<stop offset="0.63" stopColor="#FF980E" />
</radialGradient>
<linearGradient
id="paint11_linear_3681_24667"
x1="18.75"
y1="3.25511"
x2="4.28552"
y2="19.0592"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.17" stopColor="#FFF44F" stopOpacity="0.8" />
<stop offset="0.6" stopColor="#FFF44F" stopOpacity="0" />
</linearGradient>
<clipPath id="clip0_3681_24667">
<rect width="24" height="24" fill="white" />
</clipPath>
</defs>
</svg>
);
};
@@ -1,18 +0,0 @@
import { create } from 'zustand';
export interface VegaWalletDialogStore {
vegaWalletDialogOpen: boolean;
updateVegaWalletDialog: (open: boolean) => void;
openVegaWalletDialog: () => void;
closeVegaWalletDialog: () => void;
}
export const useVegaWalletDialogStore = create<VegaWalletDialogStore>()(
(set) => ({
vegaWalletDialogOpen: false,
updateVegaWalletDialog: (open: boolean) =>
set({ vegaWalletDialogOpen: open }),
openVegaWalletDialog: () => set({ vegaWalletDialogOpen: true }),
closeVegaWalletDialog: () => set({ vegaWalletDialogOpen: false }),
})
);
-12
View File
@@ -1,17 +1,5 @@
import type { InjectedConnector } from './injected-connector';
import type { JsonRpcConnector } from './json-rpc-connector';
import type { SnapConnector } from './snap-connector';
import type { ViewConnector } from './view-connector';
export * from './injected-connector';
export * from './json-rpc-connector';
export * from './snap-connector';
export * from './vega-connector';
export * from './view-connector';
export type Connectors = {
jsonRpc: JsonRpcConnector | undefined;
injected: InjectedConnector | undefined;
snap: SnapConnector | undefined;
view: ViewConnector | undefined;
};
+2 -3
View File
@@ -147,9 +147,8 @@ export class SnapConnector implements VegaConnector {
snapId: string | undefined = undefined;
nodeAddress: string | undefined = undefined;
// note we cannot set nodeAddress in the constructor because the
// trading app will not know what the vega url is until the app runs
constructor(snapId = DEFAULT_SNAP_ID) {
constructor(nodeAddress?: string, snapId = DEFAULT_SNAP_ID) {
this.nodeAddress = nodeAddress;
this.snapId = snapId;
}
-19
View File
@@ -7,15 +7,6 @@ import type {
} from './connectors';
export interface VegaWalletContextShape {
/** Current connected network */
network: string;
/** Url of current connected node */
vegaUrl: string;
/** Url of running wallet service */
vegaWalletServiceUrl: string;
/** If the current connector does not support signing transactions */
isReadOnly: boolean;
/** The current select public key */
@@ -46,16 +37,6 @@ export interface VegaWalletContextShape {
/** Acknowledge disclaimer */
acknowledgeNeeded?: boolean;
/** Useful links for wallet users */
links: {
explorer: string;
about: string;
concepts: string;
browserList: string;
chromeExtensionUrl: string;
mozillaExtensionUrl: string;
};
}
export const VegaWalletContext = createContext<
+3 -4
View File
@@ -1,18 +1,17 @@
export * from './connectors';
export * from './context';
export * from './use-vega-transaction';
export * from './use-vega-wallet';
export * from './connectors';
export * from './use-vega-transaction';
export * from './use-vega-transaction-manager';
export * from './use-vega-transaction-store';
export * from './use-vega-transaction-updater';
export * from './use-transaction-result';
export * from './use-eager-connect';
export * from './manage-dialog';
export * from './vega-transaction-dialog';
export * from './provider';
export * from './connect-dialog';
export * from './utils';
export * from './storage';
export * from './types';
export * from './vega-transaction-dialog';
export * from './__generated__/TransactionResult';
export * from './__generated__/WithdrawalApproval';
@@ -7,7 +7,7 @@ import {
Intent,
Icon,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '../use-vega-wallet';
import { useVegaWallet } from '..';
export interface VegaManageDialogProps {
dialogOpen: boolean;
@@ -38,13 +38,13 @@ export const VegaManageDialog = ({
className="mb-2 last:mb-0"
>
<div
className="flex justify-between text-sm gap-4"
className="flex gap-4 justify-between text-sm"
data-testid={isSelected ? 'selected-key' : ''}
>
<p data-testid="vega-public-key-full">
{truncateByChars(pk.publicKey)}
</p>
<div className="flex ml-auto gap-4">
<div className="flex gap-4 ml-auto">
{!isSelected && (
<button
onClick={() => {
+28 -30
View File
@@ -2,32 +2,19 @@ import { act, renderHook } from '@testing-library/react';
import type { Transaction } from './connectors';
import { ViewConnector, JsonRpcConnector } from './connectors';
import { useVegaWallet } from './use-vega-wallet';
import type { VegaWalletConfig } from './provider';
import { VegaWalletProvider } from './provider';
import { LocalStorage } from '@vegaprotocol/utils';
import type { ReactNode } from 'react';
import { WALLET_KEY } from './storage';
import * as Environment from '@vegaprotocol/environment';
import * as ReactHelpers from '@vegaprotocol/react-helpers';
const jsonRpcConnector = new JsonRpcConnector();
const viewConnector = new ViewConnector();
const defaultConfig: VegaWalletConfig = {
network: 'TESTNET',
vegaUrl: 'https://vega.xyz',
vegaWalletServiceUrl: 'https://vegaservice.xyz',
links: {
explorer: 'explorer-link',
concepts: 'concepts-link',
chromeExtensionUrl: 'chrome-link',
mozillaExtensionUrl: 'mozilla-link',
},
};
const setup = (config?: Partial<VegaWalletConfig>) => {
const setup = () => {
const wrapper = ({ children }: { children: ReactNode }) => (
<VegaWalletProvider config={{ ...defaultConfig, ...config }}>
{children}
</VegaWalletProvider>
<VegaWalletProvider>{children}</VegaWalletProvider>
);
return renderHook(() => useVegaWallet(), { wrapper });
};
@@ -64,9 +51,6 @@ describe('VegaWalletProvider', () => {
// Default state
expect(result.current).toEqual({
network: defaultConfig.network,
vegaUrl: defaultConfig.vegaUrl,
vegaWalletServiceUrl: defaultConfig.vegaWalletServiceUrl,
acknowledgeNeeded: false,
pubKey: null,
pubKeys: null,
@@ -76,11 +60,6 @@ describe('VegaWalletProvider', () => {
disconnect: expect.any(Function),
sendTx: expect.any(Function),
fetchPubKeys: expect.any(Function || undefined),
links: {
about: expect.any(String),
browserList: expect.any(String),
...defaultConfig.links,
},
});
// Connect
@@ -112,7 +91,17 @@ describe('VegaWalletProvider', () => {
const { result } = setup();
// Default state
expect(result.current.pubKey).toEqual(null);
expect(result.current).toEqual({
acknowledgeNeeded: false,
pubKey: null,
pubKeys: null,
isReadOnly: false,
selectPubKey: expect.any(Function),
connect: expect.any(Function),
disconnect: expect.any(Function),
sendTx: expect.any(Function),
fetchPubKeys: expect.any(Function),
});
// Connect
await act(async () => {
@@ -139,7 +128,6 @@ describe('VegaWalletProvider', () => {
result.current.connect(jsonRpcConnector);
result.current.selectPubKey(mockPubKeys[0].publicKey);
});
expect(result.current.pubKey).toBe(mockPubKeys[0].publicKey);
// Disconnect
@@ -166,12 +154,22 @@ describe('VegaWalletProvider', () => {
});
it('acknowledgeNeeded will set on', async () => {
jest
.spyOn(Environment, 'useEnvironment')
.mockReturnValue({ VEGA_ENV: 'MAINNET' });
jest.spyOn(ReactHelpers, 'useLocalStorage').mockImplementation(() => [
'',
() => {
/**/
},
() => {
/**/
},
]);
jest
.spyOn(viewConnector, 'connect')
.mockImplementation(() => Promise.resolve(mockPubKeys));
const { result } = setup({ network: 'MAINNET' });
const { result } = setup();
expect(result.current.acknowledgeNeeded).toBe(true);
await act(async () => {
+4 -46
View File
@@ -11,45 +11,14 @@ import type {
import { VegaWalletContext } from './context';
import { WALLET_KEY, WALLET_RISK_ACCEPTED_KEY } from './storage';
import { ViewConnector } from './connectors';
import { useEnvironment, Networks } from '@vegaprotocol/environment';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
type Networks =
| 'MAINNET'
| 'MAINNET_MIRROR'
| 'TESTNET'
| 'VALIDATOR_TESTNET'
| 'STAGNET1'
| 'DEVNET'
| 'CUSTOM';
interface VegaWalletLinks {
explorer: string;
concepts: string;
chromeExtensionUrl: string;
mozillaExtensionUrl: string;
}
export interface VegaWalletConfig {
network: Networks;
vegaUrl: string;
vegaWalletServiceUrl: string;
links: VegaWalletLinks;
}
const ExternalLinks = {
VEGA_WALLET_URL_ABOUT: 'https://vega.xyz/wallet/#overview',
VEGA_WALLET_BROWSER_LIST: '',
};
interface VegaWalletProviderProps {
children: ReactNode;
config: VegaWalletConfig;
}
export const VegaWalletProvider = ({
children,
config,
}: VegaWalletProviderProps) => {
export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
// Current selected pubKey
const [pubKey, setPubKey] = useState<string | null>(null);
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
@@ -140,23 +109,13 @@ export const VegaWalletProvider = ({
return connector.current.sendTx(pubkey, transaction);
}, []);
const { VEGA_ENV } = useEnvironment();
const [riskAcceptedValue] = useLocalStorage(WALLET_RISK_ACCEPTED_KEY);
const acknowledgeNeeded =
config.network === 'MAINNET' && riskAcceptedValue !== 'true';
VEGA_ENV === Networks.MAINNET && riskAcceptedValue !== 'true';
const contextValue = useMemo<VegaWalletContextShape>(() => {
return {
vegaUrl: config.vegaUrl,
vegaWalletServiceUrl: config.vegaWalletServiceUrl,
network: config.network,
links: {
explorer: config.links.explorer,
about: ExternalLinks.VEGA_WALLET_URL_ABOUT,
browserList: ExternalLinks.VEGA_WALLET_BROWSER_LIST,
concepts: config.links.concepts,
chromeExtensionUrl: config.links.chromeExtensionUrl,
mozillaExtensionUrl: config.links.mozillaExtensionUrl,
},
isReadOnly,
pubKey,
pubKeys,
@@ -168,7 +127,6 @@ export const VegaWalletProvider = ({
acknowledgeNeeded,
};
}, [
config,
isReadOnly,
pubKey,
pubKeys,
-15
View File
@@ -1,15 +0,0 @@
export enum VegaTxStatus {
Default = 'Default',
Requested = 'Requested',
Pending = 'Pending',
Error = 'Error',
Complete = 'Complete',
}
export interface VegaTxState {
status: VegaTxStatus;
error: Error | null;
txHash: string | null;
signature: string | null;
dialogOpen: boolean;
}
+23 -16
View File
@@ -1,13 +1,16 @@
import type { SnapConnector } from './';
import { useVegaWallet } from './';
import { useEffect, useState } from 'react';
import { InjectedConnector } from './connectors/injected-connector';
import { SnapConnector } from './connectors/snap-connector';
import type { VegaConnector } from './connectors/vega-connector';
import { getConfig } from './storage';
import type { Connectors } from './connectors';
import { useVegaWallet } from './use-vega-wallet';
import { useEnvironment } from '@vegaprotocol/environment';
export function useEagerConnect(connectors: Connectors) {
export function useEagerConnect(Connectors: {
[connector: string]: VegaConnector;
}) {
const { VEGA_URL } = useEnvironment();
const [connecting, setConnecting] = useState(true);
const { vegaUrl, connect, acknowledgeNeeded } = useVegaWallet();
const { connect, acknowledgeNeeded } = useVegaWallet();
useEffect(() => {
const attemptConnect = async () => {
@@ -20,7 +23,7 @@ export function useEagerConnect(connectors: Connectors) {
// Use the connector string in local storage to find the right connector to auto
// connect to
const connector = connectors[cfg.connector];
const connector = Connectors[cfg.connector];
// Developer hasn't provided this connector
if (!connector) {
@@ -30,16 +33,20 @@ export function useEagerConnect(connectors: Connectors) {
);
return;
}
try {
if (connector instanceof InjectedConnector) {
await connector.connectWallet();
await connect(connector);
} else if (connector instanceof SnapConnector) {
connector.nodeAddress = new URL(vegaUrl).origin;
await connect(connector);
if (cfg.connector === 'injected') {
const injectedInstance = Connectors[cfg.connector];
// @ts-ignore only injected wallet has connectWallet method
await injectedInstance.connectWallet();
await connect(injectedInstance);
} else if (cfg.connector === 'snap') {
const snapInstance = Connectors[cfg.connector] as SnapConnector;
if (VEGA_URL) {
snapInstance.nodeAddress = new URL(VEGA_URL).origin;
await connect(snapInstance);
}
} else {
await connect(connector);
await connect(Connectors[cfg.connector]);
}
} catch {
console.warn(`Failed to connect with connector: ${cfg.connector}`);
@@ -51,7 +58,7 @@ export function useEagerConnect(connectors: Connectors) {
if (typeof window !== 'undefined') {
attemptConnect();
}
}, [connect, connectors, acknowledgeNeeded, vegaUrl]);
}, [connect, Connectors, acknowledgeNeeded, VEGA_URL]);
return connecting;
}
+13 -17
View File
@@ -1,28 +1,17 @@
import { act, renderHook, waitFor } from '@testing-library/react';
import { Status, useInjectedConnector } from './use-injected-connector';
import type { ReactNode } from 'react';
import type { VegaWalletConfig } from './provider';
import { VegaWalletProvider } from './provider';
import { InjectedConnector } from './connectors';
import { mockBrowserWallet } from './test-helpers';
import { useEnvironment } from '@vegaprotocol/environment';
import { Networks } from '@vegaprotocol/environment';
const defaultConfig: VegaWalletConfig = {
network: 'TESTNET',
vegaUrl: 'https://vega.xyz',
vegaWalletServiceUrl: 'https://vegaservice.xyz',
links: {
explorer: 'explorer-link',
concepts: 'concepts-link',
chromeExtensionUrl: 'chrome-link',
mozillaExtensionUrl: 'mozilla-link',
},
};
jest.mock('@vegaprotocol/environment');
const setup = (callback = jest.fn(), config?: Partial<VegaWalletConfig>) => {
const setup = (callback = jest.fn()) => {
const wrapper = ({ children }: { children: ReactNode }) => (
<VegaWalletProvider config={{ ...defaultConfig, ...config }}>
{children}
</VegaWalletProvider>
<VegaWalletProvider>{children}</VegaWalletProvider>
);
return renderHook(() => useInjectedConnector(callback), { wrapper });
};
@@ -30,6 +19,10 @@ const setup = (callback = jest.fn(), config?: Partial<VegaWalletConfig>) => {
const injected = new InjectedConnector();
describe('useInjectedConnector', () => {
beforeEach(() => {
// @ts-ignore useEnvironment has been mocked
useEnvironment.mockImplementation(() => ({ VEGA_ENV: Networks.TESTNET }));
});
it('attempts connection', async () => {
const { result } = setup();
expect(typeof result.current.connect).toBe('function');
@@ -91,8 +84,11 @@ describe('useInjectedConnector', () => {
it('connects when aknowledgement required', async () => {
const callback = jest.fn();
// @ts-ignore useEnvironment has been mocked
useEnvironment.mockImplementation(() => ({ VEGA_ENV: Networks.MAINNET }));
const vega = mockBrowserWallet();
const { result } = setup(callback, { network: 'MAINNET' });
const { result } = setup(callback);
act(() => {
result.current.connect(injected, '1'); // default mock chainId is '1'
+6 -4
View File
@@ -6,6 +6,7 @@ import {
} from './connectors';
import { InjectedConnector } from './connectors';
import { useVegaWallet } from './use-vega-wallet';
import { useEnvironment } from '@vegaprotocol/environment';
export enum Status {
Idle = 'Idle',
@@ -17,9 +18,10 @@ export enum Status {
}
export const useInjectedConnector = (onConnect: () => void) => {
const { vegaUrl, connect, acknowledgeNeeded } = useVegaWallet();
const { connect, acknowledgeNeeded } = useVegaWallet();
const [status, setStatus] = useState(Status.Idle);
const [error, setError] = useState<Error | null>(null);
const { VEGA_URL } = useEnvironment();
const attemptConnect = useCallback(
async (
@@ -34,10 +36,10 @@ export const useInjectedConnector = (onConnect: () => void) => {
if (!('ethereum' in window)) {
throw SnapConnectorErrors.ETHEREUM_UNDEFINED;
}
if (!vegaUrl) {
if (!VEGA_URL) {
throw SnapConnectorErrors.NODE_ADDRESS_NOT_SET;
}
connector.nodeAddress = new URL(vegaUrl).origin;
connector.nodeAddress = new URL(VEGA_URL).origin;
}
setStatus(Status.GettingChainId);
@@ -69,7 +71,7 @@ export const useInjectedConnector = (onConnect: () => void) => {
setStatus(Status.Error);
}
},
[vegaUrl, acknowledgeNeeded, connect, onConnect]
[VEGA_URL, acknowledgeNeeded, connect, onConnect]
);
return {
+2 -7
View File
@@ -3,25 +3,20 @@ import { getSnap } from './connectors';
const INTERVAL = 2_000;
export const useIsSnapRunning = (snapId: string, shouldCheck: boolean) => {
export const useIsSnapRunning = (snapId: string) => {
const [running, setRunning] = useState(false);
useEffect(() => {
if (!shouldCheck) return;
const checkState = async () => {
const snap = await getSnap(snapId);
setRunning(!!snap);
};
const i = setInterval(() => {
checkState();
}, INTERVAL);
checkState();
return () => {
clearInterval(i);
};
}, [snapId, shouldCheck]);
}, [snapId]);
return running;
};
@@ -1,21 +1,20 @@
import type { VegaConnector } from './connectors';
import { useCallback, useEffect, useState } from 'react';
import type { JsonRpcConnector } from './connectors';
import { ClientErrors } from './connectors';
export const useIsWalletServiceRunning = (
url: string,
connector: JsonRpcConnector | undefined,
connectors: { [key: string]: VegaConnector },
appChainId: string
) => {
const [run, setRun] = useState<boolean | null>(null);
const checkState = useCallback(async () => {
if (!connector) return false;
const connector = connectors['jsonRpc'] as JsonRpcConnector;
if (url && url !== connector.url) {
connector.url = url;
}
try {
await connector.checkCompat();
const chainIdResult = await connector.getChainId();
@@ -26,11 +25,9 @@ export const useIsWalletServiceRunning = (
return false;
}
return true;
}, [connector, url, appChainId]);
}, [connectors, url, appChainId]);
useEffect(() => {
if (!connector) return;
let interval: NodeJS.Timeout;
checkState().then((value) => {
setRun(value);
@@ -41,7 +38,7 @@ export const useIsWalletServiceRunning = (
return () => {
clearInterval(interval);
};
}, [checkState, connector]);
}, [checkState]);
return run;
};
@@ -2,12 +2,13 @@ import { useVegaTransactionManager } from './use-vega-transaction-manager';
import { renderHook } from '@testing-library/react';
import waitForNextTick from 'flush-promises';
import type { TransactionResponse } from './connectors/vega-connector';
import { VegaTxStatus } from './types';
import type {
VegaTransactionStore,
VegaStoredTxState,
} from './use-vega-transaction-store';
import { VegaTxStatus } from './use-vega-transaction';
const mockSendTx = jest.fn<Promise<Partial<TransactionResponse> | null>, []>();
const pubKey = 'pubKey';
@@ -1,26 +1,9 @@
import { useVegaWallet } from './use-vega-wallet';
import { useEffect, useRef } from 'react';
import { WalletError } from './connectors';
import type { WalletError } from './connectors';
import { ClientErrors } from './connectors';
import { VegaTxStatus, orderErrorResolve } from './use-vega-transaction';
import { useVegaTransactionStore } from './use-vega-transaction-store';
import {
WalletClientError,
WalletHttpError,
} from '@vegaprotocol/wallet-client';
import { VegaTxStatus } from './types';
const orderErrorResolve = (err: Error | unknown): Error => {
if (err instanceof WalletClientError || err instanceof WalletError) {
return err;
} else if (err instanceof WalletHttpError) {
return ClientErrors.UNKNOWN;
} else if (err instanceof TypeError) {
return ClientErrors.NO_SERVICE;
} else if (err instanceof Error) {
return err;
}
return ClientErrors.UNKNOWN;
};
export const useVegaTransactionManager = () => {
const { sendTx, pubKey, disconnect } = useVegaWallet();
@@ -1,4 +1,5 @@
import { useVegaTransactionStore } from './use-vega-transaction-store';
import { VegaTxStatus } from './use-vega-transaction';
import type { VegaStoredTxState } from './use-vega-transaction-store';
import type {
OrderAmendmentBody,
@@ -11,7 +12,6 @@ import {
OrderType,
Side,
} from '@vegaprotocol/types';
import { VegaTxStatus } from './types';
jest.mock('./utils', () => ({
...jest.requireActual('./utils'),
@@ -13,6 +13,8 @@ import {
import { determineId } from './utils';
import { create } from 'zustand';
import type { VegaTxState } from './use-vega-transaction';
import { VegaTxStatus } from './use-vega-transaction';
import type {
TransactionEventFieldsFragment,
WithdrawalBusEventFieldsFragment,
@@ -21,9 +23,6 @@ import type {
import type { WithdrawalApprovalQuery } from './__generated__/WithdrawalApproval';
import { subscribeWithSelector } from 'zustand/middleware';
import type { VegaTxState } from './types';
import { VegaTxStatus } from './types';
export interface VegaStoredTxState extends VegaTxState {
id: number;
createdAt: Date;
@@ -34,7 +33,6 @@ export interface VegaStoredTxState extends VegaTxState {
withdrawalApproval?: WithdrawalApprovalQuery['erc20WithdrawalApproval'];
order?: OrderTxUpdateFieldsFragment;
}
export interface VegaTransactionStore {
transactions: (VegaStoredTxState | undefined)[];
create: (tx: Transaction, order?: OrderTxUpdateFieldsFragment) => number;
@@ -2,13 +2,15 @@ import { act, renderHook } from '@testing-library/react';
import type { VegaWalletContextShape } from './context';
import { VegaWalletContext } from './context';
import type { ReactNode } from 'react';
import { initialState, useVegaTransaction } from './use-vega-transaction';
import {
initialState,
useVegaTransaction,
VegaTxStatus,
} from './use-vega-transaction';
import type { Transaction } from './connectors';
import { WalletError } from './connectors';
import { VegaTxStatus } from './types';
const mockPubKey = '0x123';
const defaultWalletContext = {
pubKey: null,
pubKeys: [],
@@ -18,7 +20,7 @@ const defaultWalletContext = {
disconnect: jest.fn(),
selectPubKey: jest.fn(),
connector: null,
} as unknown as VegaWalletContextShape;
};
function setup(context?: Partial<VegaWalletContextShape>) {
const wrapper = ({ children }: { children: ReactNode }) => (
+16 -2
View File
@@ -11,8 +11,6 @@ import type { Intent } from '@vegaprotocol/ui-toolkit';
import type { Transaction } from './connectors';
import { WalletError } from './connectors';
import { ClientErrors } from './connectors';
import type { VegaTxState } from './types';
import { VegaTxStatus } from './types';
export interface DialogProps {
intent?: Intent;
@@ -21,6 +19,22 @@ export interface DialogProps {
content?: VegaTransactionContentMap;
}
export enum VegaTxStatus {
Default = 'Default',
Requested = 'Requested',
Pending = 'Pending',
Error = 'Error',
Complete = 'Complete',
}
export interface VegaTxState {
status: VegaTxStatus;
error: Error | null;
txHash: string | null;
signature: string | null;
dialogOpen: boolean;
}
export const initialState = {
status: VegaTxStatus.Default,
error: null,
+1 -2
View File
@@ -1,6 +1,5 @@
import { useCallback, useContext } from 'react';
import { useVegaWalletDialogStore } from './connect-dialog/vega-wallet-dialog-store';
import { VegaWalletContext } from './context';
import { useVegaWalletDialogStore, VegaWalletContext } from '.';
export function useVegaWallet() {
const context = useContext(VegaWalletContext);
@@ -1,8 +1,6 @@
import { render, screen } from '@testing-library/react';
import { WalletError } from '../connectors';
import type { VegaWalletContextShape } from '../context';
import { VegaWalletContext } from '../context';
import { VegaTxStatus } from '../types';
import { VegaTxStatus } from '../use-vega-transaction';
import type { VegaTransactionDialogProps } from './vega-transaction-dialog';
import { VegaTransactionDialog } from './vega-transaction-dialog';
@@ -18,12 +16,6 @@ jest.mock('@vegaprotocol/environment', () => ({
}));
describe('VegaTransactionDialog', () => {
const walletContext = {
network: 'TESTNET',
links: {
explorer: 'explorer',
},
} as VegaWalletContextShape;
let props: VegaTransactionDialogProps;
beforeEach(() => {
@@ -41,11 +33,7 @@ describe('VegaTransactionDialog', () => {
});
it('requested', () => {
render(
<VegaWalletContext.Provider value={walletContext}>
<VegaTransactionDialog {...props} />
</VegaWalletContext.Provider>
);
render(<VegaTransactionDialog {...props} />);
expect(screen.getByTestId('dialog-title')).toHaveTextContent(/confirm/i);
expect(screen.getByTestId(VegaTxStatus.Requested)).toHaveTextContent(
/please open your wallet/i
@@ -57,16 +45,14 @@ describe('VegaTransactionDialog', () => {
it('pending', () => {
render(
<VegaWalletContext.Provider value={walletContext}>
<VegaTransactionDialog
{...props}
transaction={{
...props.transaction,
txHash: 'tx-hash',
status: VegaTxStatus.Pending,
}}
/>
</VegaWalletContext.Provider>
<VegaTransactionDialog
{...props}
transaction={{
...props.transaction,
txHash: 'tx-hash',
status: VegaTxStatus.Pending,
}}
/>
);
expect(screen.getByTestId('dialog-title')).toHaveTextContent(/awaiting/i);
expect(screen.getByTestId(VegaTxStatus.Pending)).toHaveTextContent(
@@ -77,16 +63,14 @@ describe('VegaTransactionDialog', () => {
it('error', () => {
render(
<VegaWalletContext.Provider value={walletContext}>
<VegaTransactionDialog
{...props}
transaction={{
...props.transaction,
error: new WalletError('rejected', 1),
status: VegaTxStatus.Error,
}}
/>
</VegaWalletContext.Provider>
<VegaTransactionDialog
{...props}
transaction={{
...props.transaction,
error: new WalletError('rejected', 1),
status: VegaTxStatus.Error,
}}
/>
);
expect(screen.getByTestId('dialog-title')).toHaveTextContent(/failed/i);
expect(screen.getByTestId(VegaTxStatus.Error)).toHaveTextContent(
@@ -96,16 +80,14 @@ describe('VegaTransactionDialog', () => {
it('default complete', () => {
render(
<VegaWalletContext.Provider value={walletContext}>
<VegaTransactionDialog
{...props}
transaction={{
...props.transaction,
txHash: 'tx-hash',
status: VegaTxStatus.Complete,
}}
/>
</VegaWalletContext.Provider>
<VegaTransactionDialog
{...props}
transaction={{
...props.transaction,
txHash: 'tx-hash',
status: VegaTxStatus.Complete,
}}
/>
);
expect(screen.getByTestId('dialog-title')).toHaveTextContent(/complete/i);
expect(screen.getByTestId(VegaTxStatus.Complete)).toHaveTextContent(
@@ -123,18 +105,16 @@ describe('VegaTransactionDialog', () => {
[status]: <div>{text}</div>,
};
render(
<VegaWalletContext.Provider value={walletContext}>
<VegaTransactionDialog
{...props}
transaction={{
...props.transaction,
txHash: 'tx-hash',
status: status as VegaTxStatus,
}}
title={title}
content={content}
/>
</VegaWalletContext.Provider>
<VegaTransactionDialog
{...props}
transaction={{
...props.transaction,
txHash: 'tx-hash',
status: status as VegaTxStatus,
}}
title={title}
content={content}
/>
);
expect(screen.getByTestId('dialog-title')).toHaveTextContent(title);
expect(screen.getByText(text)).toBeInTheDocument();
@@ -147,7 +127,7 @@ describe('VegaTransactionDialog', () => {
);
expect(screen.getByTestId('tx-block-explorer')).toHaveAttribute(
'href',
`${walletContext.links.explorer}/txs/0x${txHash}`
`https://test.explorer.vega.network/txs/0x${txHash}`
);
}
});
@@ -1,10 +1,10 @@
import type { ReactNode } from 'react';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
import type { ReactNode } from 'react';
import { WalletClientError } from '@vegaprotocol/wallet-client';
import type { VegaTxState } from '../types';
import { VegaTxStatus } from '../types';
import { useVegaWallet } from '../use-vega-wallet';
import type { VegaTxState } from '../use-vega-transaction';
import { VegaTxStatus } from '../use-vega-transaction';
export type VegaTransactionContentMap = {
[C in VegaTxStatus]?: JSX.Element;
@@ -86,7 +86,7 @@ interface VegaDialogProps {
* Default dialog content
*/
export const VegaDialog = ({ transaction }: VegaDialogProps) => {
const { links, network } = useVegaWallet();
const { VEGA_EXPLORER_URL, VEGA_ENV } = useEnvironment();
let content = null;
if (transaction.status === VegaTxStatus.Requested) {
@@ -97,9 +97,9 @@ export const VegaDialog = ({ transaction }: VegaDialogProps) => {
'Please open your wallet application and confirm or reject the transaction'
)}
</p>
{network !== 'MAINNET' && (
{VEGA_ENV !== Networks.MAINNET && (
<p data-testid="testnet-transaction-info">
{t('[This is %s transaction only]').replace('%s', network)}
{t('[This is %s transaction only]').replace('%s', VEGA_ENV)}
</p>
)}
</>
@@ -125,7 +125,7 @@ export const VegaDialog = ({ transaction }: VegaDialogProps) => {
<a
className="underline"
data-testid="tx-block-explorer"
href={`${links.explorer}/txs/0x${transaction.txHash}`}
href={`${VEGA_EXPLORER_URL}/txs/0x${transaction.txHash}`}
target="_blank"
rel="noreferrer"
>
@@ -146,7 +146,7 @@ export const VegaDialog = ({ transaction }: VegaDialogProps) => {
<a
className="underline"
data-testid="tx-block-explorer"
href={`${links.explorer}/txs/0x${transaction.txHash}`}
href={`${VEGA_EXPLORER_URL}/txs/0x${transaction.txHash}`}
target="_blank"
rel="noreferrer"
>
+1
View File
@@ -139,6 +139,7 @@
"@storybook/react-webpack5": "7.0.24",
"@svgr/rollup": "^8.0.1",
"@svgr/webpack": "^6.1.2",
"@swc/core": "^1.2.173",
"@swc/jest": "0.2.20",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "14.0.0",
+11
View File
@@ -0,0 +1,11 @@
# Ledger entries
## Ledger entries tab in portfolio
When I enter on ledger entries tab in portfolio page
- **Must** see a form for download report file (<a name="7007-LEEN-001" href="#7007-LEEN-001">7007-LEEN-001</a>)
- in the form **Must** see a dropdown for select an asset, in which reports will be downloaded (<a name="7007-LEEN-002" href="#7007-LEEN-002">7007-LEEN-002</a>)
- in the form **Must** see inputs for select time period, in which reports will be downloaded (<a name="7007-LEEN-003" href="#7007-LEEN-003">7007-LEEN-003</a>)
- default preselected period **Must** be the last 7 days (<a name="7007-LEEN-004" href="#7007-LEEN-004">7007-LEEN-004</a>)
- during download a loader component **Must** be visible and all interactive elements in the form **Must** be disabled (<a name="7007-LEEN-005" href="#7007-LEEN-005">7007-LEEN-005</a>)
+66
View File
@@ -7565,6 +7565,72 @@
"@svgr/plugin-jsx" "8.0.1"
"@svgr/plugin-svgo" "8.0.1"
"@swc/core-darwin-arm64@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.56.tgz#a846f7fdbc274aa7d299568031dde025d44a3b4c"
integrity sha512-DZcu7BzDaLEdWHabz9DRTP0yEBLqkrWmskFcD5BX0lGAvoIvE4duMnAqi5F2B3X7630QioHRCYFoRw2WkeE3Cw==
"@swc/core-darwin-x64@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.56.tgz#793865f14f07f5eff6a6e6764aa6c766983fa007"
integrity sha512-VH5saqYFasdRXJy6RAT+MXm0+IjkMZvOkohJwUei+oA65cKJofQwrJ1jZro8yOJFYvUSI3jgNRGsdBkmo/4hMw==
"@swc/core-linux-arm-gnueabihf@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.56.tgz#6942e9a4f6086da66d995c46c2ab2b0f7fa86a1d"
integrity sha512-LWwPo6NnJkH01+ukqvkoNIOpMdw+Zundm4vBeicwyVrkP+mC3kwVfi03TUFpQUz3kRKdw/QEnxGTj+MouCPbtw==
"@swc/core-linux-arm64-gnu@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.56.tgz#21c3f8083c2d0a8028a07bf42cdd2a345e5108e9"
integrity sha512-GzsUy/4egJ4cMlxbM+Ub7AMi5CKAc+pxBxrh8MUPQbyStW8jGgnQsJouTnGy0LHawtdEnsCOl6PcO6OgvktXuQ==
"@swc/core-linux-arm64-musl@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.56.tgz#87c9781f0814bca65074f357e5cfdcdd7fb09845"
integrity sha512-9gxL09BIiAv8zY0DjfnFf19bo8+P4T9tdhzPwcm+1yPJcY5yr1+YFWLNFzz01agtOj6VlZ2/wUJTaOfdjjtc+A==
"@swc/core-linux-x64-gnu@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.56.tgz#55edae77d1c6a2b6230abbc79bccd175cb7c2208"
integrity sha512-n0ORNknl50vMRkll3BDO1E4WOqY6iISlPV1ZQCRLWQ6YQ2q8/WAryBxc2OAybcGHBUFkxyACpJukeU1QZ/9tNw==
"@swc/core-linux-x64-musl@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.56.tgz#45bf5a6611350bcbe3bdd1eac6f5fcfe69502137"
integrity sha512-r+D34WLAOAlJtfw1gaVWpHRwCncU9nzW9i7w9kSw4HpWYnHJOz54jLGSEmNsrhdTCz1VK2ar+V2ktFUsrlGlDA==
"@swc/core-win32-arm64-msvc@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.56.tgz#28853170b9e4f57a793d617aab75ef4b9c4b8a7f"
integrity sha512-29Yt75Is6X24z3x8h/xZC1HnDPkPpyLH9mDQiM6Cuc0I9mVr1XSriPEUB2N/awf5IE4SA8c+3IVq1DtKWbkJIw==
"@swc/core-win32-ia32-msvc@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.56.tgz#dfa120fece0741c6ccc24885d4c155c9593a956e"
integrity sha512-mplp0zbYDrcHtfvkniXlXdB04e2qIjz2Gq/XHKr4Rnc6xVORJjjXF91IemXKpavx2oZYJws+LNJL7UFQ8jyCdQ==
"@swc/core-win32-x64-msvc@1.3.56":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.56.tgz#23b601b9afcdc435e2e4d66767ac5fa6bb50d640"
integrity sha512-zp8MBnrw/bjdLenO/ifYzHrImSjKunqL0C2IF4LXYNRfcbYFh2NwobsVQMZ20IT0474lKRdlP8Oxdt+bHuXrzA==
"@swc/core@^1.2.173":
version "1.3.56"
resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.56.tgz#b12e98bc9ef39dc6092e71ea922f8073f7bec50f"
integrity sha512-yz/EeXT+PMZucUNrYceRUaTfuNS4IIu5EDZSOlvCEvm4jAmZi7CYH1B/kvzEzoAOzr7zkQiDPNJftcQXLkjbjA==
optionalDependencies:
"@swc/core-darwin-arm64" "1.3.56"
"@swc/core-darwin-x64" "1.3.56"
"@swc/core-linux-arm-gnueabihf" "1.3.56"
"@swc/core-linux-arm64-gnu" "1.3.56"
"@swc/core-linux-arm64-musl" "1.3.56"
"@swc/core-linux-x64-gnu" "1.3.56"
"@swc/core-linux-x64-musl" "1.3.56"
"@swc/core-win32-arm64-msvc" "1.3.56"
"@swc/core-win32-ia32-msvc" "1.3.56"
"@swc/core-win32-x64-msvc" "1.3.56"
"@swc/helpers@0.4.14":
version "0.4.14"
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"