Compare commits

..
46 changed files with 712 additions and 660 deletions
+1 -2
View File
@@ -1,3 +1,2 @@
* @vegaprotocol/frontend
* @vegaprotocol/frontend-qa
* @vegaprotocol/frontend @vegaprotocol/frontend-qa
*.graphql @vegaprotocol/core
+31 -1
View File
@@ -718,6 +718,8 @@
"UpdateAssetProposal": "Update asset proposal",
"NewFreeformProposal": "New freeform proposal",
"NewRawProposal": "New proposal",
"NewTransferProposal": "New transfer proposal",
"CancelTransferProposal": "Cancel transfer proposal",
"MARKET_STATE_UPDATE_TYPE_RESUME": "Resume market",
"MARKET_STATE_UPDATE_TYPE_SUSPEND": "Suspend market",
"MARKET_STATE_UPDATE_TYPE_TERMINATE": "Terminate market",
@@ -738,6 +740,8 @@
"UpdateVolumeDiscountProgram": "Update volume discount program",
"NewAsset": "New asset",
"UpdateAsset": "Update asset",
"NewTransfer": "New transfer",
"CancelTransfer": "Cancel transfer",
"AssetID": "Asset ID",
"Freeform": "Freeform",
"RawProposal": "Let me choose (raw proposal)",
@@ -914,5 +918,31 @@
"EndOfProgramTimestamp": "End of program",
"EndOfProgramTimestampDescription": "Time after which when the current epoch ends, the programs will end and benefits will be disabled.",
"BenefitTierVolumeDiscountFactor": "Volume discount factor",
"BenefitTierVolumeDiscountFactorDescription": "Discount given to those in this benefit tier"
"BenefitTierVolumeDiscountFactorDescription": "Discount given to those in this benefit tier",
"ACCOUNT_TYPE_INSURANCE": "Insurance account",
"ACCOUNT_TYPE_GLOBAL_INSURANCE": "Global insurance account",
"ACCOUNT_TYPE_SETTLEMENT": "Settlement account",
"ACCOUNT_TYPE_MARGIN": "Margin account ",
"ACCOUNT_TYPE_GENERAL": "General account",
"ACCOUNT_TYPE_FEES_INFRASTRUCTURE": "Infrastructure fees account",
"ACCOUNT_TYPE_FEES_LIQUIDITY": "Liquidity fees account",
"ACCOUNT_TYPE_FEES_MAKER": "Maker fees account",
"ACCOUNT_TYPE_BOND": "Bond account",
"ACCOUNT_TYPE_EXTERNAL": "External account",
"ACCOUNT_TYPE_GLOBAL_REWARD": "Global reward account",
"ACCOUNT_TYPE_PENDING_TRANSFERS": "Pending transfers account",
"ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES": "Maker paid fees reward account",
"ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES": "Maker received fees reward account",
"ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES": "Liquidity provider received fees reward account",
"ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS": "Market proposers reward account",
"ACCOUNT_TYPE_HOLDING": "Holding account",
"ACCOUNT_TYPE_LP_LIQUIDITY_FEES": "Liquidity provider fees account",
"ACCOUNT_TYPE_NETWORK_TREASURY": "Network treasury account",
"ACCOUNT_TYPE_VESTING_REWARDS": "Vesting rewards account",
"ACCOUNT_TYPE_VESTED_REWARDS": "Vested rewards account",
"ACCOUNT_TYPE_REWARD_AVERAGE_POSITION": "Average position reward account",
"ACCOUNT_TYPE_REWARD_RELATIVE_RETURN": "Relative return reward account",
"ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY": "Return volatility reward account",
"ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING": "Validator ranking reward account",
"ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD": "Pending fee referral reward account"
}
@@ -263,7 +263,10 @@ const NewTransferSummary = ({ proposalId }: { proposalId?: string | null }) => {
return (
<span>
{GovernanceTransferKindMapping[details.kind.__typename]}{' '}
{t('transfer from')} <Lozenge>{truncateMiddle(details.source)}</Lozenge>{' '}
{t('transfer from')}{' '}
<Lozenge>
{truncateMiddle(details.source) || t(details.sourceType)}
</Lozenge>{' '}
{t('to')} <Lozenge>{truncateMiddle(details.destination)}</Lozenge>
</span>
);
@@ -63,44 +63,42 @@ const mockReferralProposal = generateProposal({
terms: {
change: {
__typename: 'UpdateReferralProgram',
changes: {
benefitTiers: [
{
minimumEpochs: 6,
minimumRunningNotionalTakerVolume: '10000',
referralDiscountFactor: '0.001',
referralRewardFactor: '0.001',
},
{
minimumEpochs: 24,
minimumRunningNotionalTakerVolume: '500000',
referralDiscountFactor: '0.005',
referralRewardFactor: '0.005',
},
{
minimumEpochs: 48,
minimumRunningNotionalTakerVolume: '1000000',
referralDiscountFactor: '0.01',
referralRewardFactor: '0.01',
},
],
endOfProgramTimestamp: '2026-10-03T10:34:34Z',
windowLength: 3,
stakingTiers: [
{
minimumStakedTokens: '1',
referralRewardMultiplier: '1',
},
{
minimumStakedTokens: '2',
referralRewardMultiplier: '2',
},
{
minimumStakedTokens: '5',
referralRewardMultiplier: '3',
},
],
},
benefitTiers: [
{
minimumEpochs: 6,
minimumRunningNotionalTakerVolume: '10000',
referralDiscountFactor: '0.001',
referralRewardFactor: '0.001',
},
{
minimumEpochs: 24,
minimumRunningNotionalTakerVolume: '500000',
referralDiscountFactor: '0.005',
referralRewardFactor: '0.005',
},
{
minimumEpochs: 48,
minimumRunningNotionalTakerVolume: '1000000',
referralDiscountFactor: '0.01',
referralRewardFactor: '0.01',
},
],
endOfProgram: '2026-10-03T10:34:34Z',
windowLength: 3,
stakingTiers: [
{
minimumStakedTokens: '1',
referralRewardMultiplier: '1',
},
{
minimumStakedTokens: '2',
referralRewardMultiplier: '2',
},
{
minimumStakedTokens: '5',
referralRewardMultiplier: '3',
},
],
},
},
});
@@ -132,7 +130,6 @@ describe('<ProposalReferralProgramDetails />', () => {
terms: {
change: {
__typename: 'UpdateReferralProgram',
changes: {},
},
},
});
@@ -54,11 +54,10 @@ export const ProposalReferralProgramDetails = ({
return null;
}
const benefitTiers = proposal?.terms?.change?.changes?.benefitTiers;
const stakingTiers = proposal?.terms?.change?.changes?.stakingTiers;
const windowLength = proposal?.terms?.change?.changes?.windowLength;
const endOfProgramTimestamp =
proposal?.terms?.change?.changes?.endOfProgramTimestamp;
const benefitTiers = proposal?.terms?.change?.benefitTiers;
const stakingTiers = proposal?.terms?.change?.stakingTiers;
const windowLength = proposal?.terms?.change?.windowLength;
const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgram;
if (
!benefitTiers &&
@@ -30,6 +30,8 @@ export const useProposalNetworkParams = ({
NetworkParams.governance_proposal_freeform_requiredParticipation,
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority,
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredParticipation,
NetworkParams.governance_proposal_transfer_requiredParticipation,
NetworkParams.governance_proposal_transfer_requiredMajority,
]);
const fallback = {
@@ -111,6 +113,14 @@ export const useProposalNetworkParams = ({
params.governance_proposal_VolumeDiscountProgram_requiredParticipation
),
};
case 'NewTransfer':
case 'CancelTransfer':
return {
requiredMajority: params.governance_proposal_transfer_requiredMajority,
requiredParticipation: new BigNumber(
params.governance_proposal_transfer_requiredParticipation
),
};
default:
return fallback;
}
@@ -47,19 +47,17 @@ fragment UpdateReferralProgram on Proposal {
terms {
change {
... on UpdateReferralProgram {
changes {
benefitTiers {
minimumEpochs
minimumRunningNotionalTakerVolume
referralDiscountFactor
referralRewardFactor
}
endOfProgramTimestamp
windowLength
stakingTiers {
minimumStakedTokens
referralRewardMultiplier
}
benefitTiers {
minimumEpochs
minimumRunningNotionalTakerVolume
referralDiscountFactor
referralRewardFactor
}
endOfProgram: endOfProgramTimestamp
windowLength
stakingTiers {
minimumStakedTokens
referralRewardMultiplier
}
}
}
File diff suppressed because one or more lines are too long
@@ -47,19 +47,17 @@ fragment UpdateReferralPrograms on Proposal {
terms {
change {
... on UpdateReferralProgram {
changes {
benefitTiers {
minimumEpochs
minimumRunningNotionalTakerVolume
referralDiscountFactor
referralRewardFactor
}
endOfProgramTimestamp
windowLength
stakingTiers {
minimumStakedTokens
referralRewardMultiplier
}
benefitTiers {
minimumEpochs
minimumRunningNotionalTakerVolume
referralDiscountFactor
referralRewardFactor
}
endOfProgram: endOfProgramTimestamp
windowLength
stakingTiers {
minimumStakedTokens
referralRewardMultiplier
}
}
}
@@ -7,7 +7,7 @@ export type NewMarketProductFieldsFragment = { __typename?: 'Proposal', terms: {
export type UpdateMarketStatesFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: string, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
export type UpdateVolumeDiscountProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } } };
@@ -21,7 +21,7 @@ export type ProposalsQueryVariables = Types.Exact<{
}>;
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: string, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
export const NewMarketProductFieldsFragmentDoc = gql`
fragment NewMarketProductFields on Proposal {
@@ -75,19 +75,17 @@ export const UpdateReferralProgramsFragmentDoc = gql`
terms {
change {
... on UpdateReferralProgram {
changes {
benefitTiers {
minimumEpochs
minimumRunningNotionalTakerVolume
referralDiscountFactor
referralRewardFactor
}
endOfProgramTimestamp
windowLength
stakingTiers {
minimumStakedTokens
referralRewardMultiplier
}
benefitTiers {
minimumEpochs
minimumRunningNotionalTakerVolume
referralDiscountFactor
referralRewardFactor
}
endOfProgram: endOfProgramTimestamp
windowLength
stakingTiers {
minimumStakedTokens
referralRewardMultiplier
}
}
}
@@ -1,201 +0,0 @@
import { removeDecimal } from '@vegaprotocol/cypress';
import { ethers } from 'ethers';
import { connectEthereumWallet } from '../support/ethereum-wallet';
import { selectAsset } from '../support/helpers';
const assetSelectField = 'select[name="asset"]';
const toAddressField = 'input[name="to"]';
const amountField = 'input[name="amount"]';
const formFieldError = 'input-error-text';
const ASSET_EURO = 1;
describe('deposit form validation', { tags: '@smoke' }, () => {
function openDepositForm() {
cy.mockWeb3Provider();
cy.mockSubscription();
cy.mockTradingPage();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.getByTestId('Deposits').click();
cy.getByTestId('deposit-button').click();
connectEthereumWallet('MetaMask');
cy.wait('@Assets');
}
before(() => {
openDepositForm();
});
it('handles empty fields', () => {
cy.getByTestId('deposit-submit').click();
cy.getByTestId(formFieldError).should('contain.text', 'Required');
// once Ethereum wallet is connected and key selected the only field that will
// error is the asset select
cy.getByTestId(formFieldError).should('have.length', 1);
cy.get('[data-testid="input-error-text"][aria-describedby="asset"]').should(
'have.length',
1
);
});
it('unable to select assets not enabled', () => {
// Assets not enabled in mocks
cy.get(assetSelectField + ' option:contains(Asset 2)').should('not.exist');
cy.get(assetSelectField + ' option:contains(Asset 3)').should('not.exist');
cy.get(assetSelectField + ' option:contains(Asset 4)').should('not.exist');
});
it('invalid public key when entering address manually', () => {
cy.getByTestId('enter-pubkey-manually').click();
cy.get(toAddressField).clear().type('INVALID_DEPOSIT_TO_ADDRESS');
cy.get(`[data-testid="${formFieldError}"][aria-describedby="to"]`).should(
'have.text',
'Invalid Vega key'
);
});
it('invalid amount', () => {
mockWeb3DepositCalls({
allowance: '1000',
depositLifetimeLimit: '1000',
balance: '800',
deposited: '0',
dps: 5,
});
// Deposit amount smaller than minimum viable for selected asset
// Select an amount so that we have a known decimal places value to work with
selectAsset(ASSET_EURO);
cy.get(amountField)
.clear()
.type('0.00000000000000000000000000000000001')
.next(`[data-testid="${formFieldError}"]`)
.should('have.text', 'Value is below minimum');
});
it('insufficient funds', () => {
// 1001-DEPO-004
mockWeb3DepositCalls({
allowance: '1000',
depositLifetimeLimit: '1000',
balance: '800',
deposited: '0',
dps: 5,
});
cy.get(amountField)
.clear()
.type('850')
.next(`[data-testid="${formFieldError}"]`)
.should(
'have.text',
"You can't deposit more than you have in your Ethereum wallet, 800 tEURO"
);
});
});
describe('deposit actions', { tags: '@smoke' }, () => {
before(() => {
cy.mockWeb3Provider();
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/markets/market-1');
});
it.skip('Deposit to trade is visible', () => {
cy.getByTestId('Collateral').click();
cy.get('[row-id="asset-id"]').contains('tEURO').should('be.visible');
cy.contains('[data-testid="deposit"]', 'Deposit').should('be.visible');
cy.contains('[data-testid="deposit"]', 'Deposit').click();
cy.getByTestId('deposit-submit').should('be.visible');
});
});
function mockWeb3DepositCalls({
allowance,
depositLifetimeLimit,
balance,
deposited,
dps,
}: {
allowance: string;
depositLifetimeLimit: string;
balance: string;
deposited: string;
dps: number;
}) {
const assetContractAddress = '0x0158031158bb4df2ad02eaa31e8963e84ea978a4';
const collateralBridgeAddress = '0x7fe27d970bc8afc3b11cc8d9737bfb66b1efd799';
const toResult = (value: string, dps: number) => {
const rawValue = removeDecimal(value, dps);
return ethers.utils.hexZeroPad(
ethers.utils.hexlify(parseInt(rawValue)),
32
);
};
cy.intercept('POST', 'http://localhost:8545', (req) => {
// Mock chainId call
if (req.body.method === 'eth_chainId') {
req.alias = 'eth_chainId';
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: '0xaa36a7', // 11155111 for sepolia chain id
});
}
// Mock deposited amount
if (req.body.method === 'eth_getStorageAt') {
req.alias = 'eth_getStorageAt';
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: toResult(deposited, dps),
});
}
if (req.body.method === 'eth_call') {
// Mock approved amount for asset on collateral bridge
if (
req.body.params[0].to === assetContractAddress &&
req.body.params[0].data ===
'0xdd62ed3e000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f0000000000000000000000007fe27d970bc8afc3b11cc8d9737bfb66b1efd799'
) {
req.alias = 'eth_call_allowance';
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: toResult(allowance, dps),
});
}
// Mock balance of asset in Ethereum wallet
else if (
req.body.params[0].to === assetContractAddress &&
req.body.params[0].data ===
'0x70a08231000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f'
) {
req.alias = 'eth_call_balanceOf';
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: toResult(balance, dps),
});
}
// Mock deposit lifetime limit
else if (
req.body.params[0].to === collateralBridgeAddress &&
req.body.params[0].data ===
'0x354a897a0000000000000000000000000158031158bb4df2ad02eaa31e8963e84ea978a4'
) {
req.alias = 'eth_call_get_deposit_maximum'; // deposit lifetime limit
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: toResult(depositLifetimeLimit, dps),
});
}
}
});
}
@@ -1,5 +0,0 @@
import MarketPage from '../market';
export const ClosedMarketPage = () => {
return <MarketPage closed />;
};
@@ -1 +0,0 @@
export { ClosedMarketPage as default } from './closed-market';
@@ -0,0 +1,48 @@
import { t } from '@vegaprotocol/i18n';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import {
SidebarButton,
SidebarDivider,
ViewType,
} from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const LiquiditySidebar = () => {
const currentRouteId = useGetCurrentRouteId();
return (
<>
<SidebarButton
view={ViewType.Deposit}
icon={VegaIconNames.DEPOSIT}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Withdraw}
icon={VegaIconNames.WITHDRAW}
tooltip={t('Withdraw')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Transfer}
icon={VegaIconNames.TRANSFER}
tooltip={t('Transfer')}
routeId={currentRouteId}
/>
<SidebarDivider />
<SidebarButton
view={ViewType.Order}
icon={VegaIconNames.TICKET}
tooltip={t('Order')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Info}
icon={VegaIconNames.BREAKDOWN}
tooltip={t('Market specification')}
routeId={currentRouteId}
/>
</>
);
};
@@ -43,7 +43,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
const asset = getAsset(market);
return (
<div className="flex gap-8">
<>
<HeaderStat heading={t('Mark Price')} testId="market-price">
<MarketMarkPrice
marketId={market.id}
@@ -144,7 +144,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
</HeaderStat>
)}
<MarketProposalNotification marketId={market.id} />
</div>
</>
);
};
+4 -19
View File
@@ -9,10 +9,9 @@ import { useGlobalStore, usePageTitleStore } from '../../stores';
import { TradeGrid } from './trade-grid';
import { TradePanels } from './trade-panels';
import { useNavigate, useParams } from 'react-router-dom';
import { Links, Routes } from '../../lib/links';
import { Links } from '../../lib/links';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { MarketState } from '@vegaprotocol/types';
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
return markPrice && decimalPlaces
@@ -57,7 +56,7 @@ const TitleUpdater = ({
return null;
};
export const MarketPage = ({ closed }: { closed?: boolean }) => {
export const MarketPage = () => {
const { marketId } = useParams();
const navigate = useNavigate();
const currentRouteId = useGetCurrentRouteId();
@@ -70,25 +69,11 @@ export const MarketPage = ({ closed }: { closed?: boolean }) => {
const { data, loading } = useMarket(marketId);
useEffect(() => {
if (
data?.state &&
[
MarketState.STATE_SETTLED,
MarketState.STATE_TRADING_TERMINATED,
].includes(data.state) &&
currentRouteId !== Routes.CLOSED_MARKETS &&
marketId
) {
navigate(Links.CLOSED_MARKETS(marketId));
}
}, [data?.state, currentRouteId, navigate, marketId]);
useEffect(() => {
if (data?.id && data.id !== lastMarketId && !closed) {
update({ marketId: data.id });
}
}, [update, lastMarketId, data?.id, closed]);
}, [update, lastMarketId, data?.id]);
useEffect(() => {
if (largeScreen && view === undefined) {
@@ -97,7 +82,7 @@ export const MarketPage = ({ closed }: { closed?: boolean }) => {
currentRouteId
);
}
}, [setViews, view, currentRouteId, largeScreen, closed]);
}, [setViews, view, currentRouteId, largeScreen]);
const pinnedAsset = data && getAsset(data);
@@ -73,10 +73,19 @@ const MainGrid = memo(
{market &&
market.tradableInstrument.instrument.product.__typename ===
'Perpetual' ? (
<Tab id="funding" name={t('Funding')}>
<Tab id="funding-history" name={t('Funding history')}>
<TradingViews.funding.component marketId={marketId} />
</Tab>
) : null}
{market &&
market.tradableInstrument.instrument.product.__typename ===
'Perpetual' ? (
<Tab id="funding-payments" name={t('Funding payments')}>
<TradingViews.fundingPayments.component
marketId={marketId}
/>
</Tab>
) : null}
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
@@ -139,9 +148,6 @@ const MainGrid = memo(
<Tab id="fills" name={t('Fills')}>
<TradingViews.fills.component />
</Tab>
<Tab id="funding-payments" name={t('Funding payments')}>
<TradingViews.fundingPayments.component />
</Tab>
<Tab
id="accounts"
name={t('Collateral')}
@@ -363,12 +363,12 @@ describe('Closed', () => {
const container = within(
document.querySelector('.ag-center-cols-container') as HTMLElement
);
const cell = container.getAllByRole('gridcell', {
name: (_name, element) => element.getAttribute('col-id') === 'code',
})[0];
expect(within(cell).getByTestId('stack-cell-secondary')).toHaveTextContent(
'PRNT'
);
const cells = await container.findAllByRole('gridcell');
const cell = cells.find((el) => el.getAttribute('col-id') === 'code');
expect(
within(cell as HTMLElement).getByTestId('stack-cell-secondary')
).toHaveTextContent('PRNT');
});
});
+9 -6
View File
@@ -1,3 +1,4 @@
import type { CellClickedEvent } from 'ag-grid-community';
import compact from 'lodash/compact';
import { isAfter } from 'date-fns';
import type {
@@ -5,6 +6,7 @@ import type {
VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { useMemo } from 'react';
import { t } from '@vegaprotocol/i18n';
import type { Asset } from '@vegaprotocol/types';
@@ -17,13 +19,11 @@ import {
import { closedMarketsWithDataProvider, getAsset } from '@vegaprotocol/markets';
import type { DataSourceFilterFragment } from '@vegaprotocol/markets';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { SettlementDateCell } from './settlement-date-cell';
import { SettlementPriceCell } from './settlement-price-cell';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { MarketCodeCell } from './market-code-cell';
import { MarketActionsDropdown } from './market-table-actions';
import type { CellClickedEvent } from 'ag-grid-community';
import { useClosedMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
type SettlementAsset = Pick<
Asset,
@@ -127,7 +127,7 @@ const ClosedMarketsDataGrid = ({
rowData: Row[];
error: Error | undefined;
}) => {
const handleOnSelect = useClosedMarketClickHandler();
const handleOnSelect = useMarketClickHandler();
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
const colDefs = useMemo(() => {
@@ -302,8 +302,11 @@ const ClosedMarketsDataGrid = ({
return;
}
// @ts-ignore metaKey exists
handleOnSelect(data.id, event ? event.metaKey : false);
handleOnSelect(
data.id,
// @ts-ignore metaKey exists
event ? event.metaKey : false
);
}}
/>
);
@@ -0,0 +1,67 @@
import { Route, Routes, useParams } from 'react-router-dom';
import { MarketState } from '@vegaprotocol/types';
import { t } from '@vegaprotocol/i18n';
import { useMarket } from '@vegaprotocol/markets';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import {
SidebarButton,
SidebarDivider,
ViewType,
} from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const MarketsSidebar = () => {
const { marketId } = useParams();
const currentRouteId = useGetCurrentRouteId();
const { data } = useMarket(marketId);
const active =
data &&
[MarketState.STATE_ACTIVE, MarketState.STATE_PENDING].includes(data.state);
return (
<>
<SidebarButton
view={ViewType.Deposit}
icon={VegaIconNames.DEPOSIT}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Withdraw}
icon={VegaIconNames.WITHDRAW}
tooltip={t('Withdraw')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Transfer}
icon={VegaIconNames.TRANSFER}
tooltip={t('Transfer')}
routeId={currentRouteId}
/>
<Routes>
<Route
path=":marketId"
element={
<>
<SidebarDivider />
{active && (
<SidebarButton
view={ViewType.Order}
icon={VegaIconNames.TICKET}
tooltip={t('Order')}
routeId={currentRouteId}
/>
)}
<SidebarButton
view={ViewType.Info}
icon={VegaIconNames.BREAKDOWN}
tooltip={t('Market specification')}
routeId={currentRouteId}
/>
</>
}
/>
</Routes>
</>
);
};
@@ -0,0 +1,31 @@
import { t } from '@vegaprotocol/i18n';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { SidebarButton, ViewType } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const PortfolioSidebar = () => {
const currentRouteId = useGetCurrentRouteId();
return (
<>
<SidebarButton
view={ViewType.Deposit}
icon={VegaIconNames.DEPOSIT}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Withdraw}
icon={VegaIconNames.WITHDRAW}
tooltip={t('Withdraw')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Transfer}
icon={VegaIconNames.TRANSFER}
tooltip={t('Transfer')}
routeId={currentRouteId}
/>
</>
);
};
@@ -9,7 +9,11 @@ import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
export const FundingPaymentsContainer = () => {
export const FundingPaymentsContainer = ({
marketId,
}: {
marketId?: string;
}) => {
const onMarketClick = useMarketClickHandler(true);
const { pubKey } = useVegaWallet();
@@ -33,6 +37,7 @@ export const FundingPaymentsContainer = () => {
return (
<FundingPaymentsManager
partyId={pubKey}
marketId={marketId}
onMarketClick={onMarketClick}
gridProps={gridStoreCallbacks}
/>
+3 -3
View File
@@ -17,11 +17,11 @@ export const Header = ({ title, children }: TradeMarketHeaderProps) => {
);
return (
<header className={headerClasses}>
<div className="hidden lg:flex flex-col justify-center items-start pl-3 lg:pl-4 pt-2 xl:pb-2 pb-0">
<div className="flex-col items-start justify-center hidden pt-2 pb-0 pl-3 lg:flex lg:pl-4 xl:pb-2">
{title}
</div>
<div data-testid="header-summary" className="min-w-0">
<div className="px-3 lg:px-4 py-2 flex flex-nowrap gap-4 items-center text-xs overflow-x-auto">
<div className="flex items-center px-3 py-2 overflow-x-auto text-xs lg:px-4 flex-nowrap gap-6">
{children}
</div>
</div>
@@ -70,7 +70,7 @@ export const HeaderTitle = ({ children }: { children: ReactNode }) => {
return (
<h1
data-testid="header-title"
className="flex gap-4 items-center text-lg whitespace-nowrap xl:pr-4 xl:border-r border-default"
className="flex items-center text-lg gap-4 whitespace-nowrap xl:pr-4 xl:border-r border-default"
>
{children}
</h1>
@@ -1,11 +1,16 @@
import { Outlet, Routes, Route } from 'react-router-dom';
import type { ReactNode } from 'react';
import { Outlet } from 'react-router-dom';
import { Sidebar, SidebarContent, useSidebar } from '../sidebar';
import classNames from 'classnames';
import { MarketHeader } from '../market-header';
import { LiquidityHeader } from '../liquidity-header';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const LayoutWithSidebar = () => {
export const LayoutWithSidebar = ({
header,
sidebar,
}: {
header?: ReactNode;
sidebar?: ReactNode;
}) => {
const currentRouteId = useGetCurrentRouteId();
const views = useSidebar((store) => store.views);
const sidebarView = views[currentRouteId] || null;
@@ -20,16 +25,7 @@ export const LayoutWithSidebar = () => {
return (
<div className={gridClasses}>
<div className="col-span-full">
<Routes>
<Route path="markets/:marketId" element={<MarketHeader />} />
<Route
path="markets/all/closed/:marketId"
element={<MarketHeader />}
/>
<Route path="liquidity/:marketId" element={<LiquidityHeader />} />
</Routes>
</div>
<div className="col-span-full">{header}</div>
<main
className={classNames('col-start-1 col-end-1', {
'lg:col-end-3': !sidebarOpen,
@@ -40,9 +36,12 @@ export const LayoutWithSidebar = () => {
</main>
<aside
// min-h-0 is needed as this element is part of a grid, we want the content to be scrollable, without it it will push the grid element taller
className={classNames('col-start-1 lg:col-start-2 min-h-0', {
hidden: !sidebarOpen,
})}
className={classNames(
'col-start-1 lg:col-start-2 min-h-0 lg:border-l border-default lg:bg-vega-clight-800 dark:lg:bg-vega-cdark-800',
{
hidden: !sidebarOpen,
}
)}
>
<SidebarContent />
</aside>
@@ -54,7 +53,7 @@ export const LayoutWithSidebar = () => {
'lg:row-start-2 lg:row-span-full lg:col-start-3'
)}
>
<Sidebar />
<Sidebar options={sidebar} />
</div>
</div>
);
@@ -1,6 +1,6 @@
import { Popover, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { Header, HeaderTitle } from '../header';
import { useParams } from 'react-router-dom';
import { Route, Routes, useParams } from 'react-router-dom';
import { MarketSelector } from '../../components/market-selector/market-selector';
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
import { useMarket, useMarketList } from '@vegaprotocol/markets';
@@ -19,36 +19,44 @@ export const MarketHeader = () => {
if (!data) return null;
return (
<Header
title={
<Popover
open={open}
onChange={setOpen}
trigger={
<HeaderTitle>
<span>
{data.tradableInstrument.instrument.code}
<span className="ml-2 text-xs uppercase text-muted">
{' '}
{data.tradableInstrument.instrument.product.__typename &&
ProductTypeShortName[
data.tradableInstrument.instrument.product.__typename
]}{' '}
</span>
</span>
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
</HeaderTitle>
}
alignOffset={-10}
>
<MarketSelector
currentMarketId={marketId}
onSelect={() => setOpen(false)}
/>
</Popover>
}
>
<MarketHeaderStats market={data} />
</Header>
<Routes>
<Route
path=":marketId"
element={
<Header
title={
<Popover
open={open}
onChange={setOpen}
trigger={
<HeaderTitle>
<span>
{data.tradableInstrument.instrument.code}
<span className="ml-1 text-xs uppercase text-muted">
{data.tradableInstrument.instrument.product
.__typename &&
ProductTypeShortName[
data.tradableInstrument.instrument.product
.__typename
]}
</span>
</span>
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
</HeaderTitle>
}
alignOffset={-10}
>
<MarketSelector
currentMarketId={marketId}
onSelect={() => setOpen(false)}
/>
</Popover>
}
>
<MarketHeaderStats market={data} />
</Header>
}
/>
</Routes>
);
};
@@ -63,7 +63,7 @@ describe('Navbar', () => {
const expectedLinks = [
['/', ''],
['/markets/all', 'Markets'],
['/markets', 'Markets'],
[`/markets/${marketId}`, 'Trading'],
['/portfolio', 'Portfolio'],
['/referrals', 'Referrals'],
@@ -80,7 +80,7 @@ describe('Navbar', () => {
});
it('Markets page route should not match empty market page', () => {
renderComponent(['/markets/all']);
renderComponent(['/markets']);
expect(screen.getByRole('link', { name: 'Markets' })).toHaveClass('active');
expect(screen.getByRole('link', { name: 'Trading' })).not.toHaveClass(
'active'
@@ -96,7 +96,7 @@ describe('Navbar', () => {
const menu = within(menuEl);
const expectedLinks = [
['/markets/all', 'Markets'],
['/markets', 'Markets'],
[`/markets/${marketId}`, 'Trading'],
['/portfolio', 'Portfolio'],
['/referrals', 'Referrals'],
@@ -264,6 +264,7 @@ const NavbarLink = ({
<N.Link asChild={true}>
<NavLink
to={to}
end={true}
className={classNames(
'block lg:flex lg:h-full flex-col justify-center',
'px-6 py-2 lg:p-0 text-lg lg:text-sm',
@@ -3,14 +3,12 @@ import { Filter, OrderListManager } from '@vegaprotocol/orders';
import { t } from '@vegaprotocol/i18n';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
} from '../../lib/hooks/use-market-click-handler';
import { useNavigateWithMeta } from '../../lib/hooks/use-market-click-handler';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { DataGridStore } from '../../stores/datagrid-store-slice';
import { OrderStatus } from '@vegaprotocol/types';
import { Links } from '../../lib/links';
export const FilterStatusValue = {
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
@@ -32,8 +30,7 @@ const AUTO_SIZE_COLUMNS = ['instrument-code'];
export const OrdersContainer = ({ filter }: OrderContainerProps) => {
const { pubKey, isReadOnly } = useVegaWallet();
const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler();
const navigate = useNavigateWithMeta();
const { gridState, updateGridState } = useOrderListGridState(filter);
const gridStoreCallbacks = useDataGridEvents(
gridState,
@@ -51,8 +48,12 @@ export const OrdersContainer = ({ filter }: OrderContainerProps) => {
<OrderListManager
partyId={pubKey}
filter={filter}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
onMarketClick={(marketId, metaKey) => {
navigate(Links.MARKET(marketId), metaKey);
}}
onOrderTypeClick={(marketId, metaKey) =>
navigate(Links.LIQUIDITY(marketId), metaKey)
}
isReadOnly={isReadOnly}
gridProps={gridStoreCallbacks}
/>
@@ -38,79 +38,68 @@ const walletContext = {
} as VegaWalletContextShape;
describe('Sidebar', () => {
it.each(['/markets/all', '/portfolio'])(
'does not render ticket and info',
(path) => {
render(
<VegaWalletContext.Provider value={walletContext}>
<MemoryRouter initialEntries={[path]}>
<Sidebar />
</MemoryRouter>
</VegaWalletContext.Provider>
);
beforeEach(() => {
useSidebar.setState({ views: {} });
});
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Withdraw)).toBeInTheDocument();
expect(screen.getByTestId('node-health')).toBeInTheDocument();
// no order or info sidebars
expect(screen.queryByTestId(ViewType.Order)).not.toBeInTheDocument();
expect(screen.queryByTestId(ViewType.Info)).not.toBeInTheDocument();
}
);
it('renders ticket and info on market pages', () => {
it('renders options prop', () => {
render(
<VegaWalletContext.Provider value={walletContext}>
<MemoryRouter initialEntries={['/markets/ABC']}>
<Sidebar />
<MemoryRouter>
<Sidebar options={<div data-testid="options" />} />
</MemoryRouter>
</VegaWalletContext.Provider>
);
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Withdraw)).toBeInTheDocument();
expect(screen.getByTestId('node-health')).toBeInTheDocument();
// order and info sidebars are shown
expect(screen.getByTestId(ViewType.Order)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Info)).toBeInTheDocument();
expect(screen.getByTestId('options')).toBeInTheDocument();
});
it('renders selected state', async () => {
const routeId = '/markets/ABC';
render(
<VegaWalletContext.Provider value={walletContext}>
<MemoryRouter initialEntries={['/markets/ABC']}>
<Sidebar />
<MemoryRouter initialEntries={[routeId]}>
<Sidebar
options={
<SidebarButton
view={ViewType.Deposit}
icon={VegaIconNames.DEPOSIT}
tooltip="Deposit"
routeId={'/markets/:marketId'}
/>
}
/>
</MemoryRouter>
</VegaWalletContext.Provider>
);
const settingsButton = screen.getByTestId(ViewType.Settings);
const orderButton = screen.getByTestId(ViewType.Order);
const depositButton = screen.getByTestId(ViewType.Deposit);
// select settings first
await userEvent.click(settingsButton);
expect(settingsButton).toHaveClass('bg-vega-yellow text-black');
// switch to order
await userEvent.click(orderButton);
// switch to deposit
await userEvent.click(depositButton);
expect(settingsButton).not.toHaveClass('bg-vega-yellow text-black');
expect(orderButton).toHaveClass('bg-vega-yellow text-black');
expect(depositButton).toHaveClass('bg-vega-yellow text-black');
// close order
await userEvent.click(orderButton);
expect(orderButton).not.toHaveClass('bg-vega-yellow text-black');
await userEvent.click(depositButton);
expect(depositButton).not.toHaveClass('bg-vega-yellow text-black');
});
});
describe('SidebarContent', () => {
beforeEach(() => {
useSidebar.setState({ views: {} });
});
it('renders the correct content', () => {
const { container } = render(
<VegaWalletContext.Provider value={walletContext}>
+4 -98
View File
@@ -1,7 +1,7 @@
import classNames from 'classnames';
import type { ReactNode } from 'react';
import { useEffect } from 'react';
import { Route, Routes, useParams } from 'react-router-dom';
import { useParams } from 'react-router-dom';
import { create } from 'zustand';
import { TransferContainer } from '@vegaprotocol/accounts';
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
@@ -50,108 +50,14 @@ type SidebarView =
type: ViewType.Settings;
};
const MarketSidebarButtons = () => {
const currentRouteId = useGetCurrentRouteId();
return (
<>
<SidebarButton
view={ViewType.Order}
icon={VegaIconNames.TICKET}
tooltip={t('Order')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Info}
icon={VegaIconNames.BREAKDOWN}
tooltip={t('Market specification')}
routeId={currentRouteId}
/>
</>
);
};
const AssetSidebarButtons = () => {
const currentRouteId = useGetCurrentRouteId();
return (
<>
<SidebarButton
view={ViewType.Deposit}
icon={VegaIconNames.DEPOSIT}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Withdraw}
icon={VegaIconNames.WITHDRAW}
tooltip={t('Withdraw')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Transfer}
icon={VegaIconNames.TRANSFER}
tooltip={t('Transfer')}
routeId={currentRouteId}
/>
</>
);
};
export const Sidebar = () => {
export const Sidebar = ({ options }: { options?: ReactNode }) => {
const currentRouteId = useGetCurrentRouteId();
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen);
const { pubKeys } = useVegaWallet();
return (
<div className="flex h-full p-1 lg:flex-col gap-2" data-testid="sidebar">
<nav className={navClasses}>
<Routes>
<Route path="markets/all" element={<AssetSidebarButtons />} />
<Route path="portfolio">
<Route
// Show deposit/withdraw/transfer sidebar options only on portflio dashboard (index)
index={true}
element={<AssetSidebarButtons />}
/>
</Route>
<Route
path="markets/:marketId"
element={
<>
<AssetSidebarButtons />
<SidebarDivider />
<MarketSidebarButtons />
</>
}
/>
<Route
path="liquidity/:marketId"
element={
<>
<AssetSidebarButtons />
<SidebarDivider />
<MarketSidebarButtons />
</>
}
/>
<Route
path="markets/all/closed/:marketId"
element={
<>
<AssetSidebarButtons />
<SidebarDivider />
<SidebarButton
view={ViewType.Info}
icon={VegaIconNames.BREAKDOWN}
tooltip={t('Market specification')}
routeId={currentRouteId}
/>
</>
}
/>
</Routes>
</nav>
{options && <nav className={navClasses}>{options}</nav>}
<nav className={classNames(navClasses, 'ml-auto lg:mt-auto lg:ml-0')}>
<SidebarButton
view={ViewType.ViewAs}
@@ -234,7 +140,7 @@ export const SidebarButton = ({
);
};
const SidebarDivider = () => {
export const SidebarDivider = () => {
return (
<div
className="w-px h-4 bg-vega-clight-600 dark:bg-vega-cdark-600 lg:w-4 lg:h-px"
@@ -1,5 +1,4 @@
import { useNavigate } from 'react-router-dom';
import { useCallback } from 'react';
import { Links } from '../../lib/links';
export const useMarketClickHandler = (replace = false) => {
@@ -15,17 +14,10 @@ export const useMarketClickHandler = (replace = false) => {
};
};
export const useMarketLiquidityClickHandler = () => {
return useCallback((selectedId: string, metaKey?: boolean) => {
window.open(`/#/liquidity/${selectedId}`, metaKey ? '_blank' : '_self');
}, []);
};
export const useClosedMarketClickHandler = (replace = false) => {
export const useNavigateWithMeta = (replace = false) => {
const navigate = useNavigate();
return (selectedId: string, metaKey?: boolean) => {
const link = Links.CLOSED_MARKETS(selectedId);
return (link: string, metaKey?: boolean) => {
if (metaKey) {
window.open(`/#${link}`, '_blank');
} else {
+1 -4
View File
@@ -4,8 +4,7 @@ import trimEnd from 'lodash/trimEnd';
// href creation
export const Routes = {
HOME: '/',
MARKETS: '/markets/all',
CLOSED_MARKETS: '/markets/all/closed/:marketId',
MARKETS: '/markets',
MARKET: '/markets/:marketId',
LIQUIDITY: '/liquidity/:marketId',
PORTFOLIO: '/portfolio',
@@ -29,8 +28,6 @@ export const Links: ConsoleLinks = {
MARKET: (marketId: string) =>
trimEnd(Routes.MARKET.replace(':marketId', marketId)),
MARKETS: () => Routes.MARKETS,
CLOSED_MARKETS: (marketId: string) =>
trimEnd(Routes.CLOSED_MARKETS.replace(':marketId', marketId)),
PORTFOLIO: () => Routes.PORTFOLIO,
LIQUIDITY: (marketId: string) =>
trimEnd(Routes.LIQUIDITY.replace(':marketId', marketId)),
+73 -67
View File
@@ -1,5 +1,5 @@
import type { RouteObject } from 'react-router-dom';
import { Navigate, Outlet, useRoutes } from 'react-router-dom';
import { Navigate, useRoutes } from 'react-router-dom';
import { lazy, Suspense } from 'react';
import { t } from '@vegaprotocol/i18n';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
@@ -13,7 +13,7 @@ import { Assets } from '../client-pages/assets';
import { Deposit } from '../client-pages/deposit';
import { Withdraw } from '../client-pages/withdraw';
import { Transfer } from '../client-pages/transfer';
import { Routes } from '../lib/links';
import { Routes as AppRoutes } from '../lib/links';
import { LayoutWithSky } from '../client-pages/referrals/layout';
import { Referrals } from '../client-pages/referrals/referrals';
import { ReferralStatistics } from '../client-pages/referrals/referral-statistics';
@@ -22,11 +22,15 @@ import { CreateCodeContainer } from '../client-pages/referrals/create-code-form'
import { NotFound as ReferralNotFound } from '../client-pages/referrals/error-boundary';
import { compact } from 'lodash';
import { FLAGS } from '@vegaprotocol/environment';
import { LiquidityHeader } from '../components/liquidity-header';
import { MarketHeader } from '../components/market-header';
import { PortfolioSidebar } from '../client-pages/portfolio/portfolio-sidebar';
import { LiquiditySidebar } from '../client-pages/liquidity/liquidity-sidebar';
import { MarketsSidebar } from '../client-pages/markets/markets-sidebar';
// These must remain dynamically imported as pennant cannot be compiled by nextjs due to ESM
// Using dynamic imports is a workaround for this until pennant is published as ESM
const MarketPage = lazy(() => import('../client-pages/market'));
const ClosedMarketPage = lazy(() => import('../client-pages/closed-market'));
const Portfolio = lazy(() => import('../client-pages/portfolio'));
const NotFound = () => (
@@ -36,18 +40,21 @@ const NotFound = () => (
);
export const routerConfig: RouteObject[] = compact([
// Pages that don't use the LayoutWithSidebar must come first
// to ensure they are matched before the catch all route '/*'
{
index: true,
element: <Home />,
id: AppRoutes.HOME,
},
{
path: 'disclaimer',
element: <LayoutCentered />,
id: Routes.DISCLAIMER,
id: AppRoutes.DISCLAIMER,
children: [{ index: true, element: <Disclaimer /> }],
},
// Referrals routing (the pages should be available if the feature flag is on)
FLAGS.REFERRALS
? {
path: Routes.REFERRALS,
path: AppRoutes.REFERRALS,
element: <LayoutWithSky />,
children: [
{
@@ -58,11 +65,11 @@ export const routerConfig: RouteObject[] = compact([
element: <ReferralStatistics />,
},
{
path: Routes.REFERRALS_CREATE_CODE,
path: AppRoutes.REFERRALS_CREATE_CODE,
element: <CreateCodeContainer />,
},
{
path: Routes.REFERRALS_APPLY_CODE,
path: AppRoutes.REFERRALS_APPLY_CODE,
element: <ApplyCodeForm />,
},
],
@@ -74,75 +81,74 @@ export const routerConfig: RouteObject[] = compact([
],
}
: undefined,
// All other pages will use the sidebar
{
path: '/*',
element: <LayoutWithSidebar />,
path: 'markets/*',
element: (
<LayoutWithSidebar
header={<MarketHeader />}
sidebar={<MarketsSidebar />}
/>
),
children: [
{
index: true,
element: <Home />,
id: Routes.HOME,
element: <MarketsPage />,
id: AppRoutes.MARKETS,
},
{
path: 'markets',
element: <Outlet />,
children: [
{
index: true,
element: <Navigate to="all" />,
},
{
path: 'all',
element: <MarketsPage />,
id: Routes.MARKETS,
},
{
path: ':marketId',
element: <MarketPage />,
id: Routes.MARKET,
},
{
path: 'all/closed/:marketId',
element: <ClosedMarketPage />,
id: Routes.CLOSED_MARKETS,
},
],
path: 'all',
element: <Navigate to="/markets" />,
},
{
path: 'portfolio',
element: <Outlet />,
children: [
{
index: true,
element: <Portfolio />,
id: Routes.PORTFOLIO,
},
{
path: 'assets',
element: <Assets />,
id: Routes.ASSETS,
children: [
{ index: true, element: <Navigate to="deposit" /> },
{ path: 'deposit', element: <Deposit />, id: Routes.DEPOSIT },
{ path: 'withdraw', element: <Withdraw />, id: Routes.WITHDRAW },
{ path: 'transfer', element: <Transfer />, id: Routes.TRANSFER },
],
},
],
},
{
path: 'liquidity/:marketId',
element: <Liquidity />,
id: Routes.LIQUIDITY,
},
// NotFound page is here so its caught within parent '/*' route
{
path: '*',
element: <NotFound />,
path: ':marketId',
element: <MarketPage />,
id: AppRoutes.MARKET,
},
],
},
{
path: 'portfolio/*',
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
children: [
{
index: true,
element: <Portfolio />,
id: AppRoutes.PORTFOLIO,
},
{
path: 'assets',
element: <Assets />,
id: AppRoutes.ASSETS,
children: [
{ index: true, element: <Navigate to="deposit" /> },
{ path: 'deposit', element: <Deposit />, id: AppRoutes.DEPOSIT },
{ path: 'withdraw', element: <Withdraw />, id: AppRoutes.WITHDRAW },
{ path: 'transfer', element: <Transfer />, id: AppRoutes.TRANSFER },
],
},
],
},
{
path: 'liquidity/*',
element: (
<LayoutWithSidebar
header={<LiquidityHeader />}
sidebar={<LiquiditySidebar />}
/>
),
id: AppRoutes.LIQUIDITY,
children: [
{
path: ':marketId',
element: <Liquidity />,
},
],
},
{
path: '*',
element: <NotFound />,
},
]);
export const ClientRouter = () => {
@@ -45,6 +45,28 @@ const secondRow = {
} as AccountFields;
const multiRowData = [singleRow, secondRow];
const zeroBalanceRow = {
__typename: 'AccountBalance',
type: Types.AccountType.ACCOUNT_TYPE_MARGIN,
balance: '0',
market: {
__typename: 'Market',
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
},
asset: {
__typename: 'Asset',
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
symbol: 'tBTC',
decimals: 5,
},
available: '0',
used: '0',
total: '0',
} as AccountFields;
const zeroBalanceRowData = [zeroBalanceRow];
const onClickDepositMock = jest.fn();
describe('AccountsTable', () => {
it('should render correct columns', async () => {
// 7001-COLL-001
@@ -71,6 +93,27 @@ describe('AccountsTable', () => {
).toEqual(expectedHeaders);
});
it('should render deposit button', async () => {
render(
<AccountTable
rowData={zeroBalanceRowData}
onClickAsset={() => null}
isReadOnly={false}
onClickDeposit={onClickDepositMock}
pinnedAsset={{
decimals: 5,
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
symbol: 'tBTC',
name: 'tBTC',
}}
/>
);
const depositButton = screen.getByTestId('deposit');
expect(depositButton).toBeVisible();
await userEvent.click(depositButton);
expect(onClickDepositMock).toHaveBeenCalled();
});
it('should apply correct formatting', async () => {
const { container } = render(
<AccountTable
+25 -4
View File
@@ -4,9 +4,10 @@ import {
} from '@vegaprotocol/data-provider';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { AssetsDocument } from './__generated__/Assets';
import * as Schema from '@vegaprotocol/types';
import { AssetStatus } from '@vegaprotocol/types';
import type { AssetsQuery } from './__generated__/Assets';
import type { Asset } from './asset-data-provider';
import { DENY_LIST } from './constants';
export interface BuiltinAssetSource {
__typename: 'BuiltinAsset';
@@ -48,6 +49,7 @@ export const assetsMapProvider = makeDerivedDataProvider<
}
);
/** Returns a record of assets by id */
export const useAssetsMapProvider = () =>
useDataProvider({
dataProvider: assetsMapProvider,
@@ -58,13 +60,32 @@ export const enabledAssetsProvider = makeDerivedDataProvider<
ReturnType<typeof getData>,
never
>([assetsProvider], ([assets]) =>
(assets as ReturnType<typeof getData>).filter(
(a) => a.status === Schema.AssetStatus.STATUS_ENABLED
)
(assets as ReturnType<typeof getData>)
.filter((a) => a.status === AssetStatus.STATUS_ENABLED)
.filter((a) => {
const env = process.env['NX_VEGA_ENV'];
if (!env || !DENY_LIST[env]) return true;
if (DENY_LIST[env].includes(a.id)) {
return false;
}
return true;
})
);
/** Returns all assets */
export const useAssetsDataProvider = () =>
useDataProvider({
dataProvider: assetsProvider,
variables: undefined,
});
/** Returns assets that are enabled and are not on the deny list */
export const useEnabledAssets = () => {
return useDataProvider({
dataProvider: enabledAssetsProvider,
variables: undefined,
});
};
+63
View File
@@ -1,4 +1,67 @@
import { t } from '@vegaprotocol/i18n';
export const WITHDRAW_THRESHOLD_TOOLTIP_TEXT = t(
"The maximum you can withdraw instantly. There's no limit on the size of a withdrawal, but all withdrawals over the threshold will have a delay time added to them"
);
// List of defunct and no longer used assets that were created for various testnets
export const DENY_LIST: Record<string, string[]> = {
TESTNET: [
'07f8ce4e3df6c649f3fd794112f545ede9fbd9cfa76a9e380a7502a8d525e4db',
'0b87ac58d4af7fc11c8b417153fcb62631cfd9643835ef28db3f5a1caef0b379',
'169fd651dd1303fc6f3e1dee8349439140cf467a190bc9a04bff3e617509cfd4',
'16ae5dbb1fd7aa2ddef725703bfe66b3647a4da7b844bfdd04e985756f53d9d6',
'177e8f6c25a955bd18475084b99b2b1d37f28f3dec393fab7755a7e69c3d8c3b',
'20ffe5b1f70364a6b976007d96d306345bc7b880256ddffbbea2c590096ef375',
'2792b6ead78a812bd0169b2adbe9c2ea5b8c21510e40b93bf8d2cba6a0838e90',
'27c0229ab9edc6a3ba6a3e9716c1b324d6515a3168004256879e2de7c240f453',
'285923fed8c66ffb416b163e8ec72d3a87b9b8e2570e7ee7fe97d7092a918bc8',
'2af02165ef5ea1d1284b4251117eaae45faa961ec918e3f69b66903a18822387',
'2bf406160d06280d92412605eb397d08142736f4e17967e404dc892da278e9da',
'373d90d97bd3f97f0c13cf449926cfd7f1ed5f3746c3091e8dd894d60bf5eac8',
'3c7d6bd83dac3174923ba65f3af01f42858e267ca5b722fe2b0b3fec14e98ec2',
'434b502cabb7a9a36d3036a599d589537da293d8bbab224692629351a7926249',
'43bb50d6ba0a871088014cb204079ace4811970ce3d52b15896f6e7a88f900b7',
'46e9d23142762db57751b1b507e15747fe9478de504cbc4a0bffd828c39e3776',
'4b6957c6a184b002db80c7a4dd811d0e70866be4a4c8fffa4894e78aa132b407',
'4cbe1ad4cf232fb2232176e3f440f6e6fdc3ba7b1d95a99cb69116b9e379eae8',
'4e4e80abff30cab933b8c4ac6befc618372eb76b2cbddc337eff0b4a3a4d25b8',
'51eed0c17e3287996ba6006ce56de2591550ebfad46631f3a0129886967420e1',
'522e09779d984ae77b60036f4ecd80f0491f5f0c8cc386a0712b78a020ab62e6',
'5c950d4cfe1aaf9174c4358ee5df03f7e6bc556eac06a012cf6c07db154e20c4',
'5d8eb4e4e385b85c5c11f47111f315d5d201e04f4ef5e294763bf51036974b91',
'5dd5c226e944b4c3df3e8dbbbc473e72fb134f4c5b82a1968cc3cf7c1b40fe9e',
'638b4dbe4f3ce5b81546bef29108e8374a7293012c6189ca00f287f8c62a98a5',
'644c3a6e258382542a108c4dca16fae93657b6ff40238b2bb55854a125316052',
'71ea74ef1aa7582bcdd4ef843b3e79584ded4d16e692f3946cac4c9d61aed4f2',
'72f051ea66686ae004086f1ad086866f720f25896319abf3427cae101a58d985',
'827bbacde0dd5bfec857933dbbf642147f2afe6a9db0a543839b1b5239ca6786',
'83f4970aa1aeab85d01d8a976cbfd0fb3afedf391e18197e8c94c4021b3dffa1',
'84a20b554854c9af10ef84d308795dfae455834d1a207798292d66e19fd72c92',
'84fff099818dc4f5319477f0812b4341565cfb32ccf735beede734e386b8108f',
'85771250f0aa84727a1c01ba8926e59217ca13a7706ba03cf2ad95abbbc2e3bc',
'8a246b15bf185065d3f41a710f3eea0fbbe8754a8464501cf4a6d253d69c6423',
'8e5553ee4e95db51523640d1d03b00c8ab31dd69d787e169b0d958ef52e19a0a',
'92747340d922e9cda5bde17c46af8b409e47d6342f3b9c8fb058a34c6206d373',
'9b43c6791aebed5f7b38d91907539173aa278de88294b6422f3dd8282cd2c6e2',
'9e0bb9bd7ea2ec51efcdc98b432b6f0b055b2ed7973cfac9b44899d6e6c5deab',
'a0cf0835cb5af7bd03c56d12d846eed81c182eab36ea61c074961c115da9c8c4',
'a26dc4f6dea91e80d42e4736da7fe3c42f6275df9c4dca48be3ef96850192242',
'a4c945aea50ef321eabf8a3bcf0b8bb74288452c4638876ee4df5fb59be4a0ec',
'ae28c1043e79bdb95cae0e9919d4436157d14b183e24ab4572793812f3d89222',
'b0cf8be4e4d1d2ac5156c2298aa42edc7a74d25b7838d06e2279e94018a47f3f',
'b6d11705cb456768ff8271074dd8adcd78bd042a27fc205e9b52f9f945f42ea1',
'be478570584211521890fb65a41bded4d2bf29beb0b61ac3cc3b97f828a27b22',
'befde6d78ea1c996ae1d959342730220e274992097aeeae00c908a971314e6ae',
'bf5b9464d154ffeff57f05f90b223c1045f0754befd01a9a42c99382115e7833',
'd2239a7c92bc5c72a959a73ccee7727ba0966f4d88a33d71dbbc26e3f171555d',
'd3f9cf6e1711595b4588366a96b725fc039f72a8a28b2e4c1b630016dd2eed39',
'd9a9ab00bb79ce7dca7f6329ab627dcea2e53e5c16b85bf4aa8c3980b76fad57',
'dd73f7d1e721ef5cba95f454a3019521456c1b43b97084ad29e3e6509148d45d',
'eb30d55e90e1f9e5c4727d6fa2a5a8cd36ab9ae9738eb8f3faf53e2bee4861ee',
'ede4076aef07fd79502d14326c54ab3911558371baaf697a19d077f4f89de399',
'f481abe5700c6940ca996adfa9d07c9d9f07a486a7ce9cdb2e6ee0fa6450fe31',
'f7355ec52730c2e6ba02216da8501b4b6934cd363511e9d47ae5b97056d02703',
'fdf0ec118d98393a7702cf72e46fc87ad680b152f64b2aac59e093ac2d688fbb',
],
};
@@ -37,6 +37,7 @@ import {
AccordionChevron,
AccordionPanel,
Intent,
ExternalLink,
Pill,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
@@ -385,7 +386,21 @@ export const DealTicketMarginDetails = ({
value={liquidationPriceEstimateRange}
formattedValue={liquidationPriceEstimate}
symbol={quoteName}
labelDescription={LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT}
labelDescription={
<>
<span>{LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT}</span>{' '}
<span>
{t('For full details please see ')}
<ExternalLink
href={
'https://github.com/vegaprotocol/specs/blob/master/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md'
}
>
{t('liquidation price estimate documentation.')}
</ExternalLink>
</span>
</>
}
/>
{partyId && (
<AccountBreakdownDialog
+1 -1
View File
@@ -60,7 +60,7 @@ export const EST_FEES_TOOLTIP_TEXT = t(
);
export const LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT = t(
'This is an approximation (or a range) for the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.'
'This is an approximation for the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.'
);
export const EST_SLIPPAGE = t(
+2 -6
View File
@@ -1,18 +1,14 @@
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { AsyncRendererInline } from '@vegaprotocol/ui-toolkit';
import { DepositManager } from './deposit-manager';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { enabledAssetsProvider } from '@vegaprotocol/assets';
import { useEnabledAssets } from '@vegaprotocol/assets';
/**
* Fetches data required for the Deposit page
*/
export const DepositContainer = ({ assetId }: { assetId?: string }) => {
const { VEGA_ENV } = useEnvironment();
const { data, loading, error } = useDataProvider({
dataProvider: enabledAssetsProvider,
variables: undefined,
});
const { data, error, loading } = useEnabledAssets();
return (
<AsyncRendererInline data={data} loading={loading} error={error}>
+1 -1
View File
@@ -148,7 +148,7 @@ describe('Deposit form', () => {
});
fireEvent.submit(screen.getByTestId('deposit-form'));
// 1001-DEPO-004
expect(
await screen.findByText(
"You can't deposit more than you have in your Ethereum wallet, 5"
@@ -6,8 +6,12 @@ fragment FundingPaymentFields on FundingPayment {
timestamp
}
query FundingPayments($partyId: ID!, $pagination: Pagination) {
fundingPayments(partyId: $partyId, pagination: $pagination) {
query FundingPayments($partyId: ID!, $pagination: Pagination, $marketId: ID) {
fundingPayments(
partyId: $partyId
pagination: $pagination
marketId: $marketId
) {
edges {
node {
...FundingPaymentFields
@@ -8,6 +8,7 @@ export type FundingPaymentFieldsFragment = { __typename?: 'FundingPayment', mark
export type FundingPaymentsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
pagination?: Types.InputMaybe<Types.Pagination>;
marketId?: Types.InputMaybe<Types.Scalars['ID']>;
}>;
@@ -23,8 +24,8 @@ export const FundingPaymentFieldsFragmentDoc = gql`
}
`;
export const FundingPaymentsDocument = gql`
query FundingPayments($partyId: ID!, $pagination: Pagination) {
fundingPayments(partyId: $partyId, pagination: $pagination) {
query FundingPayments($partyId: ID!, $pagination: Pagination, $marketId: ID) {
fundingPayments(partyId: $partyId, pagination: $pagination, marketId: $marketId) {
edges {
node {
...FundingPaymentFields
@@ -55,6 +56,7 @@ export const FundingPaymentsDocument = gql`
* variables: {
* partyId: // value for 'partyId'
* pagination: // value for 'pagination'
* marketId: // value for 'marketId'
* },
* });
*/
@@ -8,12 +8,14 @@ import { fundingPaymentsWithMarketProvider } from './funding-payments-data-provi
interface FundingPaymentsManagerProps {
partyId: string;
marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
gridProps: ReturnType<typeof useDataGridEvents>;
}
export const FundingPaymentsManager = ({
partyId,
marketId,
onMarketClick,
gridProps,
}: FundingPaymentsManagerProps) => {
@@ -27,7 +29,7 @@ export const FundingPaymentsManager = ({
}
return false;
},
variables: { partyId },
variables: { partyId, marketId },
});
return (
@@ -128,6 +128,26 @@ export const NetworkParams = {
'governance_proposal_VolumeDiscountProgram_requiredMajority',
governance_proposal_VolumeDiscountProgram_requiredParticipation:
'governance_proposal_VolumeDiscountProgram_requiredParticipation',
governance_proposal_transfer_maxAmount:
'governance_proposal_transfer_maxAmount',
governance_proposal_transfer_maxClose:
'governance_proposal_transfer_maxClose',
governance_proposal_transfer_maxEnact:
'governance_proposal_transfer_maxEnact',
governance_proposal_transfer_maxFraction:
'governance_proposal_transfer_maxFraction',
governance_proposal_transfer_minClose:
'governance_proposal_transfer_minClose',
governance_proposal_transfer_minEnact:
'governance_proposal_transfer_minEnact',
governance_proposal_transfer_minProposerBalance:
'governance_proposal_transfer_minProposerBalance',
governance_proposal_transfer_minVoterBalance:
'governance_proposal_transfer_minVoterBalance',
governance_proposal_transfer_requiredMajority:
'governance_proposal_transfer_requiredMajority',
governance_proposal_transfer_requiredParticipation:
'governance_proposal_transfer_requiredParticipation',
validators_delegation_minAmount: 'validators_delegation_minAmount',
spam_protection_minimumWithdrawalQuantumMultiple:
'spam_protection_minimumWithdrawalQuantumMultiple',
+8 -2
View File
@@ -104,7 +104,13 @@ describe('TradesTable', () => {
const [firstDateCell, secondDateCell] = createdAtCells;
// Compare the times as strings
expect(firstDateCell.textContent).toBe('6:59:00 PM');
expect(secondDateCell.textContent).toBe('7:00:00 PM');
// Note: I've changed it form literal strings to this because of the
// locale mismatch. Mocking the format also resulted with strange "space" diff.
expect(firstDateCell.textContent).toBe(
getTimeFormat().format(new Date(trade2.createdAt))
);
expect(secondDateCell.textContent).toBe(
getTimeFormat().format(new Date(trade.createdAt))
);
});
});
+20 -7
View File
@@ -4605,6 +4605,8 @@ export type QueryreferralFeeStatsArgs = {
assetId?: InputMaybe<Scalars['ID']>;
epoch?: InputMaybe<Scalars['Int']>;
marketId?: InputMaybe<Scalars['ID']>;
referee?: InputMaybe<Scalars['ID']>;
referrer?: InputMaybe<Scalars['ID']>;
};
@@ -4620,9 +4622,9 @@ export type QueryreferralSetRefereesArgs = {
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QueryreferralSetStatsArgs = {
epoch?: InputMaybe<Scalars['Int']>;
id: Scalars['ID'];
pagination?: InputMaybe<Pagination>;
partyId?: InputMaybe<Scalars['ID']>;
setId?: InputMaybe<Scalars['ID']>;
};
@@ -4882,6 +4884,10 @@ export type ReferralSetStats = {
referralSetRunningNotionalTakerVolume: Scalars['String'];
/** Reward factor applied to the party. */
rewardFactor: Scalars['String'];
/** The proportion of the referees taker fees to be rewarded to the referrer. */
rewardsFactorMultiplier: Scalars['String'];
/** The multiplier applied to the referral reward factor when calculating referral rewards due to the referrer. */
rewardsMultiplier: Scalars['String'];
};
/** Connection type for retrieving cursor-based paginated referral set statistics information */
@@ -6143,7 +6149,18 @@ export type UpdateProductConfiguration = UpdateFutureProduct | UpdatePerpetualPr
export type UpdateReferralProgram = {
__typename?: 'UpdateReferralProgram';
changes: ReferralProgram;
/** Defined tiers in increasing order. First element will give Tier 1, second element will give Tier 2, etc. */
benefitTiers: Array<BenefitTier>;
/** Timestamp as RFC3339, after which when the current epoch ends, the programs will end and benefits will be disabled. */
endOfProgramTimestamp: Scalars['String'];
/**
* Defined staking tiers in increasing order. First element will give Tier 1,
* second element will give Tier 2, and so on. Determines the level of
* benefit a party can expect based on their staking.
*/
stakingTiers: Array<StakingTier>;
/** Number of epochs over which to evaluate a referral set's running volume. */
windowLength: Scalars['Int'];
};
/** Update an existing spot market on Vega */
@@ -6175,11 +6192,7 @@ export type UpdateVolumeDiscountProgram = {
benefitTiers: Array<VolumeBenefitTier>;
/** The end time of the program */
endOfProgramTimestamp: Scalars['Timestamp'];
/** ID of the proposal that created the discount program */
id: Scalars['ID'];
/** The current version of the volume discount program */
version: Scalars['Int'];
/** The window legnth to consider for the volume discount program */
/** The window length to consider for the volume discount program */
windowLength: Scalars['Int'];
};