Compare commits
39
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd0b29c251 | ||
|
|
6471be323d | ||
|
|
3e9aeb2f2f | ||
|
|
e83a7915ad | ||
|
|
d2b2d16c9c | ||
|
|
16b2360da7 | ||
|
|
1adbc2dbd7 | ||
|
|
d6f39049bd | ||
|
|
1d06be8f4e | ||
|
|
5eba8fe28f | ||
|
|
2ba0e9a1b2 | ||
|
|
43d3754c64 | ||
|
|
aae5c44fa4 | ||
|
|
878bed9c7a | ||
|
|
6f9f432c90 | ||
|
|
3f01b93159 | ||
|
|
473c244d7b | ||
|
|
d6a32f5090 | ||
|
|
71540a90fb | ||
|
|
cefdbe6a3c | ||
|
|
3928dd5c0e | ||
|
|
1521bab4c4 | ||
|
|
b3036d520f | ||
|
|
429a5a23d3 | ||
|
|
5b02fd5d54 | ||
|
|
b8309a76e7 | ||
|
|
e8ae085c06 | ||
|
|
fdcd24847c | ||
|
|
e054db39b5 | ||
|
|
a01e48d508 | ||
|
|
0d96c487d9 | ||
|
|
011e4e4cec | ||
|
|
d6e2432955 | ||
|
|
18c034b910 | ||
|
|
849cbb43c4 | ||
|
|
cd34cb181f | ||
|
|
97b3f4decf | ||
|
|
0465fb8ca9 | ||
|
|
22af89e78c |
@@ -113,15 +113,19 @@ jobs:
|
||||
preview_governance="not deployed"
|
||||
preview_trading="not deployed"
|
||||
preview_explorer="not deployed"
|
||||
if [[ $affected == *"governance"* ]]; then
|
||||
preview_tools="not deployed"
|
||||
if echo "$affected" | grep -q governance; then
|
||||
echo "Governance is affected"
|
||||
projects_e2e+='"governance-e2e" '
|
||||
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
|
||||
fi
|
||||
if [[ $affected == *"trading"* ]]; then
|
||||
if echo "$affected" | grep -q trading; then
|
||||
echo "Trading is affected"
|
||||
projects_e2e+='"trading-e2e" '
|
||||
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
|
||||
fi
|
||||
if [[ $affected == *"explorer"* ]]; then
|
||||
if echo "$affected" | grep -q explorer; then
|
||||
echo "Explorer is affected"
|
||||
projects_e2e+='"explorer-e2e" '
|
||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
||||
fi
|
||||
@@ -131,13 +135,30 @@ jobs:
|
||||
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
|
||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
||||
fi
|
||||
projects="$(echo $projects_e2e | sed 's|-e2e||g')"
|
||||
if echo "$affected" | grep -q multisig-signer; then
|
||||
echo "Tools are affected"
|
||||
# tools are only applicable to check previews or deploy from develop to mainnet
|
||||
if [[ "${{ github.event_name }}" = "pull_request" ]]; then
|
||||
echo "Deploying tools on preview"
|
||||
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
|
||||
projects+=' "multisig-signer" '
|
||||
fi
|
||||
if [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
||||
echo "Deploying tools on s3"
|
||||
projects+=' "multisig-signer" '
|
||||
fi
|
||||
fi
|
||||
projects_e2e=${projects_e2e%?}
|
||||
projects_e2e=[${projects_e2e// /,}]
|
||||
projects=${projects%?}
|
||||
projects=[${projects// /,}]
|
||||
echo PROJECTS_E2E=$projects_e2e >> $GITHUB_ENV
|
||||
echo PROJECTS=$(echo $projects_e2e | sed 's|-e2e||g') >> $GITHUB_ENV
|
||||
echo PROJECTS=$projects >> $GITHUB_ENV
|
||||
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
|
||||
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
|
||||
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
|
||||
echo PREVIEW_TOOLS=$preview_tools >> $GITHUB_ENV
|
||||
|
||||
outputs:
|
||||
projects: ${{ env.PROJECTS }}
|
||||
@@ -145,11 +166,12 @@ jobs:
|
||||
preview_governance: ${{ env.PREVIEW_GOVERNANCE }}
|
||||
preview_trading: ${{ env.PREVIEW_TRADING }}
|
||||
preview_explorer: ${{ env.PREVIEW_EXPLORER }}
|
||||
preview_tools: ${{ env.PREVIEW_TOOLS }}
|
||||
|
||||
cypress:
|
||||
needs: lint-test-build
|
||||
name: '(CI) cypress'
|
||||
if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
|
||||
if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }}
|
||||
uses: ./.github/workflows/cypress-run.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -203,6 +225,12 @@ jobs:
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
if [[ "${{ needs.lint-test-build.outputs.preview_tools }}" =~ $regex ]]; then
|
||||
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_tools }}"; do
|
||||
echo "waiting for tools preview"
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
|
||||
- name: Create comment
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
@@ -214,6 +242,7 @@ jobs:
|
||||
* governance: ${{ needs.lint-test-build.outputs.preview_governance }}
|
||||
* explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
|
||||
* trading: ${{ needs.lint-test-build.outputs.preview_trading }}
|
||||
* tools: ${{ needs.lint-test-build.outputs.preview_tools }}
|
||||
|
||||
# Report single result at the end, to avoid mess with required checks in PR
|
||||
cypress-check:
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
project: ${{ fromJSON(inputs.projects) }}
|
||||
name: ${{ matrix.project }}
|
||||
runs-on: self-hosted-runner
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 100
|
||||
steps:
|
||||
# Checks if skip cache was requested
|
||||
- name: Set skip-nx-cache flag
|
||||
|
||||
@@ -70,6 +70,10 @@ jobs:
|
||||
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
|
||||
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
||||
envName="stagnet1"
|
||||
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then
|
||||
envName="mainnet"
|
||||
bucketName="tools.vega.xyz"
|
||||
fi
|
||||
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
|
||||
envName="mainnet"
|
||||
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ "${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }}" = "true" ]]; then
|
||||
@@ -78,10 +82,14 @@ jobs:
|
||||
|
||||
if [[ "${envName}" = "mainnet" ]]; then
|
||||
domain="vega.xyz"
|
||||
bucketName="${{ matrix.app }}.${domain}"
|
||||
if [[ -z "${bucketName}" ]]; then
|
||||
bucketName="${{ matrix.app }}.${domain}"
|
||||
fi
|
||||
elif [[ "${envName}" = "testnet" ]]; then
|
||||
domain="fairground.wtf"
|
||||
bucketName="${{ matrix.app }}.${domain}"
|
||||
if [[ -z "${bucketName}" ]]; then
|
||||
bucketName="${{ matrix.app }}.${domain}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "${bucketName}" ]]; then
|
||||
|
||||
@@ -15,6 +15,7 @@ NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases/tag/
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_ASSETS=1
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import { PriceMonitoringBoundsInfoPanel } from '@vegaprotocol/markets';
|
||||
import {
|
||||
LiquidityInfoPanel,
|
||||
LiquidityMonitoringParametersInfoPanel,
|
||||
@@ -39,133 +40,69 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
return [];
|
||||
};
|
||||
|
||||
const oraclePanels = isEqual(
|
||||
const showTwoOracles = isEqual(
|
||||
getSigners(settlementData),
|
||||
getSigners(terminationData)
|
||||
)
|
||||
? [
|
||||
{
|
||||
title: t('Settlement Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel
|
||||
noBorder={false}
|
||||
market={market}
|
||||
type="settlementData"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Termination Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel
|
||||
noBorder={false}
|
||||
market={market}
|
||||
type="termination"
|
||||
/>
|
||||
),
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: t('Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel
|
||||
noBorder={false}
|
||||
market={market}
|
||||
type="settlementData"
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
);
|
||||
|
||||
const panels = [
|
||||
{
|
||||
title: t('Key details'),
|
||||
content: <KeyDetailsInfoPanel noBorder={false} market={market} />,
|
||||
},
|
||||
{
|
||||
title: t('Instrument'),
|
||||
content: <InstrumentInfoPanel noBorder={false} market={market} />,
|
||||
},
|
||||
{
|
||||
title: t('Settlement asset'),
|
||||
content: <SettlementAssetInfoPanel market={market} noBorder={false} />,
|
||||
},
|
||||
{
|
||||
title: t('Metadata'),
|
||||
content: <MetadataInfoPanel noBorder={false} market={market} />,
|
||||
},
|
||||
{
|
||||
title: t('Risk model'),
|
||||
content: <RiskModelInfoPanel noBorder={false} market={market} />,
|
||||
},
|
||||
{
|
||||
title: t('Risk parameters'),
|
||||
content: <RiskParametersInfoPanel noBorder={false} market={market} />,
|
||||
},
|
||||
{
|
||||
title: t('Risk factors'),
|
||||
content: <RiskFactorsInfoPanel noBorder={false} market={market} />,
|
||||
},
|
||||
...(market.priceMonitoringSettings?.parameters?.triggers || []).map(
|
||||
(trigger, i) => ({
|
||||
title: t(`Price monitoring trigger ${i + 1}`),
|
||||
content: <MarketInfoTable noBorder={false} data={trigger} />,
|
||||
})
|
||||
),
|
||||
...(market.data?.priceMonitoringBounds || []).map((trigger, i) => ({
|
||||
title: t(`Price monitoring bound ${i + 1}`),
|
||||
content: (
|
||||
<>
|
||||
<MarketInfoTable
|
||||
noBorder={false}
|
||||
data={{
|
||||
maxValidPrice: trigger.maxValidPrice,
|
||||
minValidPrice: trigger.minValidPrice,
|
||||
}}
|
||||
decimalPlaces={market.decimalPlaces}
|
||||
/>
|
||||
<MarketInfoTable
|
||||
noBorder={false}
|
||||
data={{ referencePrice: trigger.referencePrice }}
|
||||
decimalPlaces={
|
||||
market.tradableInstrument.instrument.product.settlementAsset
|
||||
.decimals
|
||||
}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
})),
|
||||
{
|
||||
title: t('Liquidity monitoring parameters'),
|
||||
content: (
|
||||
<LiquidityMonitoringParametersInfoPanel
|
||||
noBorder={false}
|
||||
market={market}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Liquidity'),
|
||||
content: <LiquidityInfoPanel market={market} noBorder={false} />,
|
||||
},
|
||||
{
|
||||
title: t('Liquidity price range'),
|
||||
content: (
|
||||
<LiquidityPriceRangeInfoPanel market={market} noBorder={false} />
|
||||
),
|
||||
},
|
||||
...oraclePanels,
|
||||
];
|
||||
const headerClassName = 'font-alpha calt text-xl mt-4 border-b-2 pb-2';
|
||||
|
||||
return (
|
||||
<>
|
||||
{panels.map((p) => (
|
||||
<div key={p.title} className="mb-3">
|
||||
<h2 className="font-alpha calt text-xl">{p.title}</h2>
|
||||
{p.content}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className={headerClassName}>{t('Key details')}</h2>
|
||||
<KeyDetailsInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Instrument')}</h2>
|
||||
<InstrumentInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Settlement asset')}</h2>
|
||||
<SettlementAssetInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Metadata')}</h2>
|
||||
<MetadataInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Risk model')}</h2>
|
||||
<RiskModelInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Risk parameters')}</h2>
|
||||
<RiskParametersInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Risk factors')}</h2>
|
||||
<RiskFactorsInfoPanel market={market} />
|
||||
{(market.data?.priceMonitoringBounds || []).map((trigger, i) => (
|
||||
<>
|
||||
<h2 className={headerClassName}>
|
||||
{t('Price monitoring bounds %s', [(i + 1).toString()])}
|
||||
</h2>
|
||||
<PriceMonitoringBoundsInfoPanel
|
||||
market={market}
|
||||
triggerIndex={i + 1}
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
{(market.priceMonitoringSettings?.parameters?.triggers || []).map(
|
||||
(trigger, i) => (
|
||||
<>
|
||||
<h2 className={headerClassName}>
|
||||
{t('Price monitoring settings %s', [(i + 1).toString()])}
|
||||
</h2>
|
||||
<MarketInfoTable data={trigger} key={i} />
|
||||
</>
|
||||
)
|
||||
)}
|
||||
<h2 className={headerClassName}>{t('Liquidity monitoring')}</h2>
|
||||
<LiquidityMonitoringParametersInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Liquidity')}</h2>
|
||||
<LiquidityInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Liquidity price range')}</h2>
|
||||
<LiquidityPriceRangeInfoPanel market={market} />
|
||||
{showTwoOracles ? (
|
||||
<>
|
||||
<h2 className={headerClassName}>{t('Settlement oracle')}</h2>
|
||||
<OracleInfoPanel market={market} type="settlementData" />
|
||||
<h2 className={headerClassName}>{t('Termination oracle')}</h2>
|
||||
<OracleInfoPanel market={market} type="termination" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h2 className={headerClassName}>{t('Oracle')}</h2>
|
||||
<OracleInfoPanel market={market} type="settlementData" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+6
-4
@@ -5,12 +5,13 @@ export const ErrorCodes = new Map([
|
||||
[51, 'Transaction failed validation'],
|
||||
[60, 'Transaction could not be decoded'],
|
||||
[70, 'Error'],
|
||||
[71, 'Partial success/error'],
|
||||
[80, 'Unknown command'],
|
||||
[89, 'Rejected as spam'],
|
||||
[0, 'Success'],
|
||||
]);
|
||||
|
||||
export const successCodes = new Set([0]);
|
||||
export const successCodes = new Set([0, 71]);
|
||||
|
||||
interface ChainResponseCodeProps {
|
||||
code: number;
|
||||
@@ -29,11 +30,12 @@ export const ChainResponseCode = ({
|
||||
error,
|
||||
}: ChainResponseCodeProps) => {
|
||||
const isSuccess = successCodes.has(code);
|
||||
|
||||
const successColour =
|
||||
code === 71 ? 'fill-vega-orange' : 'fill-vega-green-600';
|
||||
const icon = isSuccess ? (
|
||||
<Icon name="tick-circle" className="fill-vega-green-550" />
|
||||
<Icon name="tick-circle" className={successColour} />
|
||||
) : (
|
||||
<Icon name="cross" className="fill-vega-pink-550" />
|
||||
<Icon name="cross" className="fill-vega-pink-600" />
|
||||
);
|
||||
const label = ErrorCodes.get(code) || 'Unknown response code';
|
||||
|
||||
|
||||
@@ -160,6 +160,7 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
||||
vote={command?.voteSubmission?.value === 'VALUE_YES'}
|
||||
yesText="Proposal vote"
|
||||
noText="Proposal vote"
|
||||
useVoteColour={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,4 +34,17 @@ describe('Vote TX icon', () => {
|
||||
const no = render(<VoteIcon vote={false} />);
|
||||
expect(no.getByRole('img')).toHaveAttribute('aria-label', 'delete icon');
|
||||
});
|
||||
|
||||
it('useVoteColour prop can be used to override coloured background', () => {
|
||||
const no = render(<VoteIcon vote={false} />);
|
||||
expect(no.container.children[0]).toHaveClass('bg-vega-pink-550');
|
||||
|
||||
const monochromeNo = render(
|
||||
<VoteIcon vote={false} useVoteColour={false} />
|
||||
);
|
||||
expect(monochromeNo.container.children[0]).not.toHaveClass(
|
||||
'bg-vega-pink-550'
|
||||
);
|
||||
expect(monochromeNo.container.children[0]).toHaveClass('bg-vega-dark-200');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,6 +8,32 @@ export interface VoteIconProps {
|
||||
yesText?: string;
|
||||
// Defaults to 'Against', but can be any text
|
||||
noText?: string;
|
||||
// If set to false the background will not be coloured
|
||||
useVoteColour?: boolean;
|
||||
}
|
||||
|
||||
function getBgColour(useVoteColour: boolean, vote: boolean) {
|
||||
if (useVoteColour === false) {
|
||||
return 'bg-vega-dark-200';
|
||||
}
|
||||
|
||||
return vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
|
||||
}
|
||||
|
||||
function getFillColour(useVoteColour: boolean, vote: boolean) {
|
||||
if (useVoteColour === false) {
|
||||
return 'white';
|
||||
}
|
||||
|
||||
return vote ? 'vega-green-300' : 'vega-pink-300';
|
||||
}
|
||||
|
||||
function getTextColour(useVoteColour: boolean, vote: boolean) {
|
||||
if (useVoteColour === false) {
|
||||
return 'white';
|
||||
}
|
||||
|
||||
return vote ? 'vega-green-200' : 'vega-pink-200';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -18,14 +44,15 @@ export interface VoteIconProps {
|
||||
*/
|
||||
export function VoteIcon({
|
||||
vote,
|
||||
useVoteColour = true,
|
||||
yesText = 'For',
|
||||
noText = 'Against',
|
||||
}: VoteIconProps) {
|
||||
const label = vote ? yesText : noText;
|
||||
const bg = vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
|
||||
const icon: IconName = vote ? 'tick-circle' : 'delete';
|
||||
const fill = vote ? 'vega-green-300' : 'vega-pink-300';
|
||||
const text = vote ? 'vega-green-200' : 'vega-pink-200';
|
||||
const bg = getBgColour(useVoteColour, vote);
|
||||
const fill = getFillColour(useVoteColour, vote);
|
||||
const text = getTextColour(useVoteColour, vote);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -103,8 +103,6 @@ describe(
|
||||
it('Newly created freeform proposal details - shows proposed and closing dates', function () {
|
||||
const proposalTitle = generateFreeFormProposalTitle();
|
||||
const proposalTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
// const currentDate = new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
|
||||
// const proposedDate = new Date(currentDate.getTime() + 60000)
|
||||
|
||||
submitUniqueRawProposal({
|
||||
proposalTitle: proposalTitle,
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
createUpdateNetworkProposalTxBody,
|
||||
createFreeFormProposalTxBody,
|
||||
} from '../../support/proposal.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||
|
||||
@@ -43,6 +44,7 @@ context(
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
ethereumWalletConnect();
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('1');
|
||||
navigateTo(navigation.proposals);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
closeDialog,
|
||||
dissociateFromSecondWalletKey,
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
@@ -15,7 +16,11 @@ import {
|
||||
governanceProposalType,
|
||||
voteForProposal,
|
||||
} from '../../support/governance.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
|
||||
import {
|
||||
ensureSpecifiedUnstakedTokensAreAssociated,
|
||||
stakingPageAssociateTokens,
|
||||
stakingPageDisassociateAllTokens,
|
||||
} from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
@@ -100,9 +105,9 @@ context(
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
validateDialogContentMsg('PROPOSAL_ERROR_CLOSE_TIME_TOO_SOON');
|
||||
});
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
validateDialogContentMsg('PROPOSAL_ERROR_CLOSE_TIME_TOO_SOON');
|
||||
});
|
||||
|
||||
// 3007-PNEC-001 3007-PNEC-003
|
||||
@@ -182,13 +187,17 @@ context(
|
||||
'have.text',
|
||||
'Proposal will fail if enactment is earlier than the voting deadline'
|
||||
);
|
||||
cy.get(proposalDownloadBtn).click();
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-network-param-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-network-param-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
validateFeedBackMsg(
|
||||
'Invalid params: proposal_submission.terms.closing_timestamp (cannot be after enactment time)'
|
||||
@@ -207,13 +216,17 @@ context(
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.wrap(getDownloadedProposalJsonPath('vega-new-market-proposal-')).then(
|
||||
(filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003
|
||||
}
|
||||
);
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-new-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Unable to submit new market proposal with missing/invalid fields', function () {
|
||||
@@ -233,13 +246,17 @@ context(
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.wrap(getDownloadedProposalJsonPath('vega-new-market-proposal-')).then(
|
||||
(filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
}
|
||||
);
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-new-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
validateFeedBackMsg(errorMsg);
|
||||
@@ -248,6 +265,9 @@ context(
|
||||
// Will fail if run after 'Able to submit update market proposal and vote for proposal'
|
||||
// 3002-PROP-022
|
||||
it('Unable to submit update market proposal without equity-like share in the market', function () {
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click(); // switch to second wallet pub key
|
||||
stakingPageAssociateTokens('1');
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
|
||||
cy.get(newProposalTitle).type('Test update market proposal - rejected');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
@@ -259,17 +279,25 @@ context(
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
|
||||
validateDialogContentMsg('PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('1');
|
||||
closeDialog();
|
||||
ethereumWalletConnect();
|
||||
stakingPageDisassociateAllTokens();
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
||||
});
|
||||
|
||||
// 3002-PROP-020
|
||||
@@ -291,13 +319,17 @@ context(
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
validateFeedBackMsg(
|
||||
@@ -335,13 +367,17 @@ context(
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get('@EnactedMarketId').then((marketId) => {
|
||||
cy.contains(String(marketId).slice(0, 6))
|
||||
@@ -390,14 +426,17 @@ context(
|
||||
cy.get(minVoteDeadline).click();
|
||||
cy.get(minValidationDeadline).click();
|
||||
cy.get(minEnactDeadline).click();
|
||||
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.wrap(getDownloadedProposalJsonPath('vega-new-asset-proposal-')).then(
|
||||
(filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false }); // 3005-PASN-003
|
||||
}
|
||||
);
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-new-asset-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false }); // 3005-PASN-003
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
closeDialog();
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
@@ -433,13 +472,17 @@ context(
|
||||
enterUpdateAssetProposalDetails();
|
||||
cy.get(minVoteDeadline).click();
|
||||
cy.get(minEnactDeadline).click();
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-asset-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-asset-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.get(proposalType)
|
||||
@@ -471,13 +514,17 @@ context(
|
||||
enterUpdateAssetProposalDetails();
|
||||
cy.get(maxVoteDeadline).click();
|
||||
cy.get(maxEnactDeadline).click();
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-asset-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-asset-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Unable to submit edit asset proposal with missing/invalid fields', function () {
|
||||
@@ -525,6 +572,13 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
after('Disassociate from second wallet key if present', function () {
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
ethereumWalletConnect();
|
||||
dissociateFromSecondWalletKey();
|
||||
});
|
||||
|
||||
function validateDialogContentMsg(expectedMsg: string) {
|
||||
cy.getByTestId('dialog-content')
|
||||
.last()
|
||||
|
||||
@@ -86,11 +86,12 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
});
|
||||
|
||||
it('Newly created proposals list - shows title and portion of summary', function () {
|
||||
const proposalPath = '/proposals/new-market-raw.json';
|
||||
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
const proposalPath = 'src/fixtures/proposals/new-market-raw.json';
|
||||
const proposalTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
enactmentTimestamp: proposalTimestamp,
|
||||
closingTimestamp: proposalTimestamp,
|
||||
}); // 3001-VOTE-052
|
||||
// 3001-VOTE-008
|
||||
// 3001-VOTE-034
|
||||
|
||||
@@ -29,13 +29,17 @@ context('rewards - flow', { tags: '@slow' }, function () {
|
||||
turnTelemetryOff();
|
||||
cy.visit('/');
|
||||
waitForSpinner();
|
||||
depositAsset(vegaAssetAddress, '1000', 18);
|
||||
ethereumWalletConnect();
|
||||
cy.connectVegaWallet();
|
||||
depositAsset(vegaAssetAddress, '1000', 18);
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'contain.text',
|
||||
'Collateral'
|
||||
);
|
||||
vegaWalletTeardown();
|
||||
cy.associateTokensToVegaWallet('6000');
|
||||
cy.VegaWalletTopUpRewardsPool(30, 200);
|
||||
navigateTo(navigation.validators);
|
||||
cy.VegaWalletTopUpRewardsPool();
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'6,000.0',
|
||||
|
||||
@@ -109,6 +109,7 @@ context(
|
||||
cy.getByTestId(userStake, epochTimeout)
|
||||
.first()
|
||||
.should('have.text', '2.00');
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('total-stake').first().realHover();
|
||||
cy.getByTestId('staked-by-user-tooltip')
|
||||
.first()
|
||||
@@ -379,6 +380,7 @@ context(
|
||||
});
|
||||
|
||||
it('Disassociating some tokens - prioritizes unstaked tokens', function () {
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
stakingPageAssociateTokens('3');
|
||||
verifyUnstakedBalance(3.0);
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
@@ -485,6 +487,7 @@ context(
|
||||
});
|
||||
|
||||
afterEach('Teardown Wallet', function () {
|
||||
navigateTo(navigation.home);
|
||||
vegaWalletTeardown();
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ const ethWalletContainer = '[data-testid="ethereum-wallet"]';
|
||||
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
|
||||
const vegaWalletUnstakedBalance =
|
||||
'[data-testid="vega-wallet-balance-unstaked"]';
|
||||
const currencyTitle = '[data-testid="currency-title"]:visible';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
|
||||
const ethWalletAssociateButton = '[data-testid="associate-btn"]:visible';
|
||||
@@ -39,7 +38,7 @@ const associatedKey = '[data-testid="associated-key"]';
|
||||
const associatedAmount = '[data-testid="associated-amount"]';
|
||||
const associateCompleteText = '[data-testid="transaction-complete-body"]';
|
||||
const disassociationWarning = '[data-testid="disassociation-warning"]';
|
||||
const vegaWallet = '[data-testid="vega-wallet"]';
|
||||
const vegaWallet = 'aside [data-testid="vega-wallet"]';
|
||||
|
||||
context(
|
||||
'Token association flow - with eth and vega wallets connected',
|
||||
@@ -79,27 +78,15 @@ context(
|
||||
//0005-ETXN-003
|
||||
//0005-ETXN-005
|
||||
stakingPageAssociateTokens('2', { skipConfirmation: true });
|
||||
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
|
||||
|
||||
// 0005-ETXN-002
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
|
||||
@@ -114,12 +101,11 @@ context(
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('6,002.00');
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
stakingPageDisassociateTokens('2');
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '0.00');
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
|
||||
cy.get(
|
||||
'[data-testid="eth-wallet-associated-balances"]:visible',
|
||||
txTimeout
|
||||
@@ -132,38 +118,26 @@ context(
|
||||
stakingPageAssociateTokens('1001', { approve: true });
|
||||
verifyEthWalletAssociatedBalance('1,001.00');
|
||||
verifyEthWalletTotalAssociatedBalance('7,001.00');
|
||||
cy.get(vegaWallet)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'1,001.00'
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'1,001.00'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to disassociate a partial amount of tokens currently associated', function () {
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
stakingPageDisassociateTokens('1');
|
||||
verifyEthWalletAssociatedBalance('1.0');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
1.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 1.0);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to disassociate all tokens - using max', function () {
|
||||
@@ -171,15 +145,11 @@ context(
|
||||
const warningText =
|
||||
'Warning: Any tokens that have been nominated to a node will sacrifice rewards they are due for the current epoch. If you do not wish to sacrifice these, you should remove stake from a node at the end of an epoch before disassociation.';
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
cy.get(ethWalletDissociateButton).click();
|
||||
cy.get(disassociationWarning).should('contain', warningText);
|
||||
stakingPageDisassociateAllTokens();
|
||||
@@ -197,14 +167,9 @@ context(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
0.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 0.0);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to associate and disassociate vesting contract tokens', function () {
|
||||
@@ -219,32 +184,22 @@ context(
|
||||
type: 'contract',
|
||||
skipConfirmation: true,
|
||||
});
|
||||
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
stakingPageDisassociateTokens('1', {
|
||||
type: 'contract',
|
||||
skipConfirmation: true,
|
||||
});
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
validateWalletCurrency('Pending association', '1.00');
|
||||
validateWalletCurrency('Total associated after pending', '1.00');
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
|
||||
verifyEthWalletAssociatedBalance('1.0');
|
||||
verifyEthWalletTotalAssociatedBalance('1.0');
|
||||
});
|
||||
@@ -256,6 +211,7 @@ context(
|
||||
// 1004-ASSO-022
|
||||
stakingPageAssociateTokens('21', { type: 'wallet' });
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
stakingPageAssociateTokens('37', { type: 'contract' });
|
||||
cy.get(vestingContractSection)
|
||||
.first()
|
||||
@@ -275,28 +231,18 @@ context(
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 21);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
58
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 58);
|
||||
});
|
||||
stakingPageDisassociateTokens('6', { type: 'contract' });
|
||||
cy.get(vestingContractSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 31);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
52
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52);
|
||||
});
|
||||
navigateTo(navigation.validators);
|
||||
stakingPageDisassociateTokens('9', { type: 'wallet' });
|
||||
cy.get(vegaInWalletSection)
|
||||
@@ -304,14 +250,9 @@ context(
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 12);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
43
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 43);
|
||||
});
|
||||
});
|
||||
|
||||
it('Not able to associate more tokens than owned', function () {
|
||||
@@ -328,11 +269,9 @@ context(
|
||||
// 1004-ASSO-004
|
||||
it('Pending association outside of app is shown', function () {
|
||||
vegaWalletAssociate('2');
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
});
|
||||
|
||||
@@ -341,11 +280,9 @@ context(
|
||||
cy.wrap(validateWalletCurrency('Associated', '2.00')).then(() => {
|
||||
vegaWalletDisassociate('2');
|
||||
});
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '0.00');
|
||||
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
});
|
||||
|
||||
@@ -364,14 +301,9 @@ context(
|
||||
Cypress.env('vegaWalletPublicKey2')
|
||||
);
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(associateCompleteText).should(
|
||||
'have.text',
|
||||
`Vega key ${Cypress.env(
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { stakingPageDisassociateAllTokens } from './staking.functions';
|
||||
|
||||
const tokenDropDown = 'state-trigger';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
|
||||
export enum navigation {
|
||||
section = 'nav',
|
||||
home = '[href="/"]',
|
||||
vesting = '[href="/token/redeem"]',
|
||||
validators = '[href="/validators"]',
|
||||
rewards = '[href="/rewards"]',
|
||||
@@ -18,6 +21,7 @@ export function convertTokenValueToNumber(subject: string) {
|
||||
}
|
||||
|
||||
const topLevelRoutes = [
|
||||
navigation.home,
|
||||
navigation.proposals,
|
||||
navigation.validators,
|
||||
navigation.rewards,
|
||||
@@ -97,3 +101,25 @@ export function turnTelemetryOff() {
|
||||
win.localStorage.setItem('vega_telemetry_on', 'false')
|
||||
);
|
||||
}
|
||||
|
||||
export function dissociateFromSecondWalletKey() {
|
||||
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
||||
cy.getByTestId('vega-in-wallet')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId('eth-wallet-associated-balances')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId('associated-key')
|
||||
.invoke('text')
|
||||
.as('associatedPubKey');
|
||||
});
|
||||
});
|
||||
cy.get('@associatedPubKey').then((associatedPubKey) => {
|
||||
if (associatedPubKey == secondWalletKey) {
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
||||
stakingPageDisassociateAllTokens();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,29 +59,29 @@ export function submitUniqueRawProposal(proposalFields: {
|
||||
submit?: boolean;
|
||||
}) {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
let proposalBodyPath = '/proposals/raw.json';
|
||||
let proposalBodyPath = 'src/fixtures/proposals/raw.json';
|
||||
if (proposalFields.proposalBody) {
|
||||
proposalBodyPath = proposalFields.proposalBody;
|
||||
}
|
||||
cy.readFile(proposalBodyPath).then((rawProposal) => {
|
||||
if (!proposalFields.proposalBody) {
|
||||
if (proposalFields.proposalTitle) {
|
||||
rawProposal.rationale.title = proposalFields.proposalTitle;
|
||||
cy.wrap(proposalFields.proposalTitle).as('proposalTitle');
|
||||
}
|
||||
if (proposalFields.proposalDescription) {
|
||||
rawProposal.rationale.description = proposalFields.proposalDescription;
|
||||
}
|
||||
if (proposalFields.closingTimestamp) {
|
||||
rawProposal.terms.closingTimestamp = proposalFields.closingTimestamp;
|
||||
} else {
|
||||
const minTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
rawProposal.terms.closingTimestamp = minTimeStamp;
|
||||
}
|
||||
if (proposalFields.enactmentTimestamp) {
|
||||
rawProposal.terms.enactmentTimestamp =
|
||||
proposalFields.enactmentTimestamp;
|
||||
}
|
||||
if (proposalFields.proposalTitle) {
|
||||
rawProposal.rationale.title = proposalFields.proposalTitle;
|
||||
cy.wrap(proposalFields.proposalTitle).as('proposalTitle');
|
||||
}
|
||||
if (proposalFields.proposalDescription) {
|
||||
rawProposal.rationale.description = proposalFields.proposalDescription;
|
||||
}
|
||||
if (proposalFields.closingTimestamp) {
|
||||
rawProposal.terms.closingTimestamp = proposalFields.closingTimestamp;
|
||||
} else if (
|
||||
!proposalFields.closingTimestamp &&
|
||||
!proposalFields.proposalBody
|
||||
) {
|
||||
const minTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
rawProposal.terms.closingTimestamp = minTimeStamp;
|
||||
}
|
||||
if (proposalFields.enactmentTimestamp) {
|
||||
rawProposal.terms.enactmentTimestamp = proposalFields.enactmentTimestamp;
|
||||
}
|
||||
|
||||
const proposalPayload = JSON.stringify(rawProposal);
|
||||
|
||||
@@ -236,8 +236,8 @@ export function validateWalletCurrency(
|
||||
currencyTitle: string,
|
||||
expectedAmount: string
|
||||
) {
|
||||
cy.get("[data-testid='currency-title']")
|
||||
.contains(currencyTitle)
|
||||
cy.get("[data-testid='currency-title']", txTimeout)
|
||||
.contains(currencyTitle, txTimeout)
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
|
||||
@@ -19,7 +19,7 @@ const ethStakingBridgeContractAddress = Cypress.env(
|
||||
);
|
||||
const ethProviderUrl = Cypress.env('ethProviderUrl');
|
||||
const getAccount = (number = 0) => `m/44'/60'/0'/0/${number}`;
|
||||
const transactionTimeout = 100000;
|
||||
const transactionTimeout = { timeout: 100000, log: false };
|
||||
const Erc20BridgeAddress = '0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54';
|
||||
|
||||
const provider = new ethers.providers.JsonRpcProvider({ url: ethProviderUrl });
|
||||
@@ -43,10 +43,7 @@ export async function depositAsset(
|
||||
const faucet = new Token(assetEthAddress, signer);
|
||||
cy.wrap(
|
||||
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
|
||||
{
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}
|
||||
transactionTimeout
|
||||
).then(() => {
|
||||
const collateralBridge = new CollateralBridge(Erc20BridgeAddress, signer);
|
||||
cy.wrap(
|
||||
@@ -55,7 +52,7 @@ export async function depositAsset(
|
||||
amount + '0'.repeat(decimalPlaces),
|
||||
'0x' + vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
transactionTimeout
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -79,13 +76,13 @@ export async function vegaWalletTeardown() {
|
||||
}
|
||||
});
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
cy.get(associatedAmountInWallet, {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.should('have.length', 1, { timeout: transactionTimeout })
|
||||
.contains('0.00', {
|
||||
timeout: transactionTimeout,
|
||||
});
|
||||
cy.get(associatedAmountInWallet, transactionTimeout).should(
|
||||
'have.length',
|
||||
1
|
||||
);
|
||||
cy.get(associatedAmountInWallet)
|
||||
.first(transactionTimeout)
|
||||
.should('have.text', '0.00');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -109,7 +106,7 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
cy.highlight('Tearing down staking tokens from vega wallet if present');
|
||||
cy.wrap(
|
||||
stakingBridgeContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
|
||||
{ timeout: transactionTimeout }
|
||||
transactionTimeout
|
||||
).then((stakeBalance) => {
|
||||
if (Number(stakeBalance) != 0) {
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
@@ -122,31 +119,25 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
String(stakeBalance),
|
||||
vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout }
|
||||
transactionTimeout
|
||||
);
|
||||
cy.wrap(
|
||||
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
|
||||
{
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}
|
||||
transactionTimeout
|
||||
).then((vestingAmount) => {
|
||||
if (Number(vestingAmount) != 0) {
|
||||
cy.contains('Associated', {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
cy.contains('Associated', transactionTimeout)
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.getByTestId('currency-value', {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.should('have.length', 1)
|
||||
cy.getByTestId('currency-value', transactionTimeout)
|
||||
.first()
|
||||
.invoke('text')
|
||||
.as('displayedAmount');
|
||||
cy.get('@displayedAmount', {
|
||||
timeout: transactionTimeout,
|
||||
}).should('not.eq', $associatedAmount);
|
||||
cy.get('@displayedAmount', transactionTimeout).should(
|
||||
'not.eq',
|
||||
$associatedAmount
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -158,14 +149,14 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
|
||||
async function vegaWalletTeardownVesting(vestingContract: TokenVesting) {
|
||||
cy.highlight('Tearing down vesting tokens from vega wallet if present');
|
||||
cy.wrap(vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey), {
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}).then((vestingAmount) => {
|
||||
cy.wrap(
|
||||
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
|
||||
transactionTimeout
|
||||
).then((vestingAmount) => {
|
||||
if (Number(vestingAmount) != 0) {
|
||||
cy.wrap(
|
||||
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
|
||||
{ timeout: transactionTimeout }
|
||||
transactionTimeout
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https:/
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||
NX_VEGA_EXPLORER_URL=#
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
|
||||
|
||||
@@ -7,7 +7,7 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https:/
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './multisig-incorrect-notice';
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { MultisigIncorrectNotice } from './multisig-incorrect-notice';
|
||||
|
||||
jest.mock('@vegaprotocol/web3', () => ({
|
||||
useEthereumConfig: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
useEnvironment: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('MultisigIncorrectNotice', () => {
|
||||
it('renders correctly when config is provided', () => {
|
||||
(useEthereumConfig as jest.Mock).mockReturnValue({
|
||||
config: {
|
||||
multisig_control_contract: {
|
||||
address: '0x1234',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
(useEnvironment as unknown as jest.Mock).mockReturnValue({
|
||||
ETHERSCAN_URL: 'https://etherscan.io',
|
||||
});
|
||||
|
||||
render(<MultisigIncorrectNotice />);
|
||||
|
||||
expect(screen.getByTestId('multisig-contract-link')).toHaveAttribute(
|
||||
'href',
|
||||
'https://etherscan.io/address/0x1234'
|
||||
);
|
||||
expect(screen.getByTestId('multisig-contract-link')).toHaveAttribute(
|
||||
'title',
|
||||
'0x1234'
|
||||
);
|
||||
expect(
|
||||
screen.getByTestId('multisig-validators-learn-more')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render when config is not provided', () => {
|
||||
(useEthereumConfig as jest.Mock).mockReturnValue({
|
||||
config: null,
|
||||
});
|
||||
|
||||
const { container } = render(<MultisigIncorrectNotice />);
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
});
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Callout, Intent, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||
|
||||
export const MultisigIncorrectNotice = () => {
|
||||
const { t } = useTranslation();
|
||||
const { config } = useEthereumConfig();
|
||||
const { ETHERSCAN_URL } = useEnvironment();
|
||||
|
||||
if (!config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contract = config[
|
||||
'multisig_control_contract' as keyof EthereumConfig
|
||||
] as {
|
||||
address: string;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-10">
|
||||
<Callout intent={Intent.Warning}>
|
||||
<div>
|
||||
<Link
|
||||
title={contract.address}
|
||||
href={`${ETHERSCAN_URL}/address/${contract.address}`}
|
||||
target="_blank"
|
||||
data-testid="multisig-contract-link"
|
||||
>
|
||||
{t('multisigContractLink')}
|
||||
</Link>{' '}
|
||||
{t('multisigContractIncorrect')}
|
||||
</div>
|
||||
|
||||
<div className="mt-2">
|
||||
<Link
|
||||
href={DocsLinks?.VALIDATOR_SCORES_REWARDS}
|
||||
target="_blank"
|
||||
data-testid="multisig-validators-learn-more"
|
||||
>
|
||||
{t('learnMore')}
|
||||
</Link>
|
||||
</div>
|
||||
</Callout>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -116,7 +116,7 @@
|
||||
"Showing tranches with <{{trancheMinimum}} VEGA, click to hide these tranches": "Showing tranches with ≤{{trancheMinimum}} $VEGA, click to hide these tranches",
|
||||
"Not showing tranches with <{{trancheMinimum}} VEGA, click to show all tranches": "Not showing tranches with ≤{{trancheMinimum}} $VEGA, click to show all tranches",
|
||||
"the holder": "the holder",
|
||||
"We couldn't seem to load your data.": "We couldn't seem to load your data.",
|
||||
"Your data couldn't be loaded": "Your data couldn't be loaded",
|
||||
"Vesting VEGA": "Vesting VEGA",
|
||||
"All the tokens in this tranche are locked and can not be redeemed yet.": "All the tokens in this tranche are locked and can not be redeemed yet.",
|
||||
"Redeem unlocked VEGA from tranche {{id}}": "Redeem unlocked $VEGA from tranche {{id}}",
|
||||
@@ -728,7 +728,7 @@
|
||||
"ThisWillSetEnactmentDeadlineTo": "This will set the enactment date to",
|
||||
"ThisWillSetValidationDeadlineTo": "This will set the validation deadline to",
|
||||
"Hours": "hours",
|
||||
"ThisWillAdd2MinutesToAllowTimeToConfirmInWallet": "Note: we add 2 minutes of extra time when you choose the minimum value. This gives you time to confirm the proposal in your wallet.",
|
||||
"ThisWillAdd2MinutesToAllowTimeToConfirmInWallet": "Note: 2 minutes of extra time are added when you choose the minimum value. This gives you time to confirm the proposal in your wallet.",
|
||||
"ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline": "Proposal will fail if enactment is earlier than the voting deadline",
|
||||
"SelectAMarketToChange": "Select a market to change",
|
||||
"MarketName": "Market name",
|
||||
@@ -821,5 +821,8 @@
|
||||
"disclaimer2": "The Vega Governance App is free, public and open source software. Software upgrades may contain bugs or security vulnerabilities that might result in loss of functionality.",
|
||||
"disclaimer3": "The Vega Governance App uses data obtained from nodes on the Vega Blockchain. The developers of the Vega Governance App do not operate or run the Vega Blockchain or any other blockchain.",
|
||||
"disclaimer4": "The Vega Governance App is provided “as is”. The developers of the Vega Governance App make no representations or warranties of any kind, whether express or implied, statutory or otherwise regarding the Vega Governance App. They disclaim all warranties of merchantability, quality, fitness for purpose. They disclaim all warranties that the Vega Governance App is free of harmful components or errors.",
|
||||
"disclaimer5": "No developer of the Vega Governance App accepts any responsibility for, or liability to users in connection with their use of the Vega Governance App."
|
||||
"disclaimer5": "No developer of the Vega Governance App accepts any responsibility for, or liability to users in connection with their use of the Vega Governance App.",
|
||||
"multisigContractLink": "Ethereum Multisig Contract",
|
||||
"multisigContractIncorrect": "is incorrectly configured. Validator and delegator rewards will be penalised until this is resolved.",
|
||||
"learnMore": "Learn more"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
getMultisigStatusInfo,
|
||||
MultisigStatus,
|
||||
} from './get-multisig-status-info';
|
||||
import type { PreviousEpochQuery } from '../routes/staking/__generated__/PreviousEpoch';
|
||||
|
||||
const createNode = (id: string, multisigScore: string) => ({
|
||||
node: {
|
||||
id,
|
||||
stakedTotal: '1000',
|
||||
rewardScore: { multisigScore },
|
||||
},
|
||||
});
|
||||
|
||||
describe('getMultisigStatus', () => {
|
||||
it('should return MultisigStatus.noNodes when no nodes are present', () => {
|
||||
const result = getMultisigStatusInfo({
|
||||
epoch: { id: '1', validatorsConnection: { edges: [] } },
|
||||
} as PreviousEpochQuery);
|
||||
expect(result).toEqual({
|
||||
multisigStatus: MultisigStatus.noNodes,
|
||||
showMultisigStatusError: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return MultisigStatus.correct when all nodes have multisigScore of 1', () => {
|
||||
const result = getMultisigStatusInfo({
|
||||
epoch: {
|
||||
id: '1',
|
||||
validatorsConnection: {
|
||||
edges: [createNode('1', '1'), createNode('2', '1')],
|
||||
},
|
||||
},
|
||||
} as PreviousEpochQuery);
|
||||
expect(result).toEqual({
|
||||
multisigStatus: MultisigStatus.correct,
|
||||
showMultisigStatusError: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return MultisigStatus.nodeNeedsRemoving when all nodes have multisigScore of 0', () => {
|
||||
const result = getMultisigStatusInfo({
|
||||
epoch: {
|
||||
id: '1',
|
||||
validatorsConnection: {
|
||||
edges: [createNode('1', '0'), createNode('2', '0')],
|
||||
},
|
||||
},
|
||||
} as PreviousEpochQuery);
|
||||
expect(result).toEqual({
|
||||
multisigStatus: MultisigStatus.nodeNeedsRemoving,
|
||||
showMultisigStatusError: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return MultisigStatus.nodeNeedsAdding when some nodes have multisigScore of 0 and others have 1', () => {
|
||||
const result = getMultisigStatusInfo({
|
||||
epoch: {
|
||||
id: '1',
|
||||
validatorsConnection: {
|
||||
edges: [createNode('1', '0'), createNode('2', '1')],
|
||||
},
|
||||
},
|
||||
} as PreviousEpochQuery);
|
||||
expect(result).toEqual({
|
||||
multisigStatus: MultisigStatus.nodeNeedsAdding,
|
||||
showMultisigStatusError: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import type { PreviousEpochQuery } from '../routes/staking/__generated__/PreviousEpoch';
|
||||
|
||||
export enum MultisigStatus {
|
||||
'correct' = 'correct',
|
||||
'nodeNeedsAdding' = 'nodeNeedsAdding',
|
||||
'nodeNeedsRemoving' = 'nodeNeedsRemoving ',
|
||||
'noNodes' = 'noNodes',
|
||||
}
|
||||
|
||||
export const getMultisigStatusInfo = (
|
||||
previousEpochData: PreviousEpochQuery
|
||||
) => {
|
||||
let status = MultisigStatus.noNodes;
|
||||
|
||||
const allNodesInPreviousEpoch = removePaginationWrapper(
|
||||
previousEpochData?.epoch.validatorsConnection?.edges
|
||||
);
|
||||
|
||||
const hasZero = allNodesInPreviousEpoch.some(
|
||||
(node) => Number(node?.rewardScore?.multisigScore) === 0
|
||||
);
|
||||
const hasOne = allNodesInPreviousEpoch.some(
|
||||
(node) => Number(node?.rewardScore?.multisigScore) === 1
|
||||
);
|
||||
|
||||
if (hasZero && hasOne) {
|
||||
// If any individual node has 0 it means that node is missing from the multisig and needs to be added
|
||||
status = MultisigStatus.nodeNeedsAdding;
|
||||
} else if (hasZero) {
|
||||
// If all nodes have 0 it means there is an incorrect address in the multisig that needs to be removed
|
||||
status = MultisigStatus.nodeNeedsRemoving;
|
||||
} else if (allNodesInPreviousEpoch.length > 0) {
|
||||
// If all nodes have 1 it means the multisig is correct
|
||||
status = MultisigStatus.correct;
|
||||
}
|
||||
|
||||
return {
|
||||
showMultisigStatusError: status !== MultisigStatus.correct,
|
||||
multisigStatus: status,
|
||||
};
|
||||
};
|
||||
+1
-1
@@ -146,7 +146,7 @@ describe('Proposal form vote, validation and enactment deadline', () => {
|
||||
it('should show the correct datetimes', () => {
|
||||
renderComponent();
|
||||
// Should be adding 2 mins to the vote deadline as the minimum is set by
|
||||
// default, and we add 2 mins for wallet confirmation
|
||||
// default, and 2 mins are added for wallet confirmation
|
||||
expect(screen.getByTestId('voting-date')).toHaveTextContent(
|
||||
'2022-01-01T01:02:00.000Z'
|
||||
);
|
||||
|
||||
@@ -23,6 +23,9 @@ import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { ConnectToSeeRewards } from '../connect-to-see-rewards';
|
||||
import { EpochTotalRewards } from '../epoch-total-rewards/epoch-total-rewards';
|
||||
import { usePreviousEpochQuery } from '../../staking/__generated__/PreviousEpoch';
|
||||
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
|
||||
import { MultisigIncorrectNotice } from '../../../components/multisig-incorrect-notice';
|
||||
|
||||
type RewardsView = 'total' | 'individual';
|
||||
|
||||
@@ -41,12 +44,25 @@ export const RewardsPage = () => {
|
||||
|
||||
useRefreshAfterEpoch(epochData?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
const { data: previousEpochData } = usePreviousEpochQuery({
|
||||
variables: {
|
||||
epochId: (Number(epochData?.epoch.id) - 1).toString(),
|
||||
},
|
||||
skip: !epochData?.epoch.id,
|
||||
});
|
||||
|
||||
const multisigStatus = previousEpochData
|
||||
? getMultisigStatusInfo(previousEpochData)
|
||||
: undefined;
|
||||
|
||||
const {
|
||||
params,
|
||||
loading: paramsLoading,
|
||||
error: paramsError,
|
||||
} = useNetworkParams([NetworkParams.reward_staking_delegation_payoutDelay]);
|
||||
|
||||
console.log('params', params);
|
||||
|
||||
const payoutDuration = useMemo(() => {
|
||||
if (!params) {
|
||||
return 0;
|
||||
@@ -78,14 +94,18 @@ export const RewardsPage = () => {
|
||||
)}
|
||||
</p>
|
||||
|
||||
{payoutDuration ? (
|
||||
{multisigStatus?.showMultisigStatusError ? (
|
||||
<MultisigIncorrectNotice />
|
||||
) : null}
|
||||
|
||||
{!multisigStatus?.showMultisigStatusError && payoutDuration ? (
|
||||
<div className="my-8">
|
||||
<Callout
|
||||
title={t('rewardsCallout', {
|
||||
duration: formatDistance(new Date(0), payoutDuration),
|
||||
})}
|
||||
headingLevel={3}
|
||||
intent={Intent.Warning}
|
||||
intent={Intent.Primary}
|
||||
>
|
||||
<p className="mb-0">{t('rewardsCalloutDetail')}</p>
|
||||
</Callout>
|
||||
|
||||
@@ -7,6 +7,8 @@ import { ValidatorTables } from './validator-tables';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { ENV } from '../../../config';
|
||||
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
|
||||
import { MultisigIncorrectNotice } from '../../../components/multisig-incorrect-notice';
|
||||
|
||||
export const EpochData = () => {
|
||||
// errorPolicy due to vegaprotocol/vega issue 5898
|
||||
@@ -46,12 +48,20 @@ export const EpochData = () => {
|
||||
userStakingRefetch();
|
||||
});
|
||||
|
||||
const multisigStatus = previousEpochData
|
||||
? getMultisigStatusInfo(previousEpochData)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={nodesLoading || userStakingLoading}
|
||||
error={nodesError || userStakingError}
|
||||
data={nodesData}
|
||||
>
|
||||
{multisigStatus?.showMultisigStatusError ? (
|
||||
<MultisigIncorrectNotice />
|
||||
) : null}
|
||||
|
||||
{nodesData?.epoch &&
|
||||
nodesData.epoch.timestamps.start &&
|
||||
nodesData?.epoch.timestamps.expiry && (
|
||||
|
||||
@@ -5,4 +5,4 @@ NX_VEGA_ENV=DEVNET
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||
NX_VEGA_EXPLORER_URL=#
|
||||
|
||||
@@ -3,3 +3,6 @@ NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/maste
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
|
||||
NX_VEGA_ENV=MAINNET
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
// #region consts
|
||||
const asset = 'asset';
|
||||
const assetDetailsDialog = 'dialog-content';
|
||||
const assetRow = 'key-value-table-row';
|
||||
const contractAddress = '7_value';
|
||||
const dialogCloseBtn = 'close-asset-details-dialog';
|
||||
const dialogCloseX = 'dialog-close';
|
||||
const dialogTitle = 'dialog-title';
|
||||
|
||||
const indicesWithLabelTooltips = [4, 5, 6, 7, 8, 9, 11, 12, 13, 14];
|
||||
const indicesWithValueTooltips = [1, 6];
|
||||
|
||||
const labelValueToolTipPairs = [
|
||||
{
|
||||
label: 'ID',
|
||||
value: 'asset-id',
|
||||
},
|
||||
{
|
||||
label: 'Type',
|
||||
value: 'ERC20',
|
||||
valueToolTip: 'An asset originated from an Ethereum ERC20 Token',
|
||||
},
|
||||
{
|
||||
label: 'Name',
|
||||
value: 'Euro',
|
||||
},
|
||||
{
|
||||
label: 'Symbol',
|
||||
value: 'tEURO',
|
||||
},
|
||||
{
|
||||
label: 'Decimals',
|
||||
value: '5',
|
||||
labelTooltip: 'Number of decimal / precision handled by this asset',
|
||||
},
|
||||
{
|
||||
label: 'Quantum',
|
||||
value: '0.00001',
|
||||
labelTooltip: 'The minimum economically meaningful amount of the asset',
|
||||
},
|
||||
{
|
||||
label: 'Status',
|
||||
value: 'Enabled',
|
||||
labelTooltip: 'The status of the asset in the Vega network',
|
||||
valueToolTip: 'Asset can be used on the Vega network',
|
||||
},
|
||||
{
|
||||
label: 'Contract address',
|
||||
value: '0x0158031158Bb4dF2AD02eAA31e8963E84EA978a4 ',
|
||||
labelTooltip:
|
||||
'The address of the contract for the token, on the ethereum network',
|
||||
},
|
||||
{
|
||||
label: 'Withdrawal threshold',
|
||||
value: '0.0005',
|
||||
labelTooltip:
|
||||
"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",
|
||||
},
|
||||
{
|
||||
label: 'Lifetime limit',
|
||||
value: '1,230.00',
|
||||
labelTooltip:
|
||||
'The lifetime deposit limit per address. Note: this is a temporary measure that can be changed or removed through governance',
|
||||
},
|
||||
{ label: '', value: '' },
|
||||
{
|
||||
label: 'Infrastructure fee account balance',
|
||||
value: '0.00001',
|
||||
labelTooltip: 'The infrastructure fee account in this asset',
|
||||
},
|
||||
{
|
||||
label: 'Global reward pool account balance',
|
||||
value: '0.00002',
|
||||
labelTooltip: 'The global rewards acquired in this asset',
|
||||
},
|
||||
{
|
||||
label: 'Maker paid fees account balance',
|
||||
value: '0.00003',
|
||||
labelTooltip:
|
||||
'The rewards acquired based on the fees paid to makers in this asset',
|
||||
},
|
||||
{
|
||||
label: 'Maker received fees account balance',
|
||||
value: '0.00004',
|
||||
labelTooltip:
|
||||
'The rewards acquired based on fees received for being a maker on trades',
|
||||
},
|
||||
{
|
||||
label: 'Liquidity provision fee reward account balance',
|
||||
value: '0.00005',
|
||||
labelTooltip:
|
||||
'The rewards acquired based on the liquidity provision fees in this asset',
|
||||
},
|
||||
{
|
||||
label: 'Market proposer reward account balance',
|
||||
value: '0.00006',
|
||||
labelTooltip:
|
||||
'The rewards acquired based on the market proposer reward in this asset',
|
||||
},
|
||||
];
|
||||
//endregion
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
|
||||
const visitPortfolioAndClickAsset = (assetName: string) => {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(asset).contains(assetName).click();
|
||||
};
|
||||
|
||||
const testTooltip = (index: number, testId: string, tooltip: string) => {
|
||||
cy.getByTestId(`${index}_${testId}`).realHover();
|
||||
cy.get('[role="tooltip"]').find('div').should('have.text', tooltip);
|
||||
cy.getByTestId(dialogTitle).click();
|
||||
};
|
||||
|
||||
describe('assets', { tags: '@smoke', testIsolation: true }, () => {
|
||||
it('asset details', () => {
|
||||
visitPortfolioAndClickAsset('tBTC');
|
||||
|
||||
cy.getByTestId(assetRow).each((element, index) => {
|
||||
if (index === 10) {
|
||||
return;
|
||||
}
|
||||
const { label, value, labelTooltip, valueToolTip } =
|
||||
labelValueToolTipPairs[index];
|
||||
// 6501-ASSE-001
|
||||
// 6501-ASSE-002
|
||||
// 6501-ASSE-003
|
||||
// 6501-ASSE-004
|
||||
// 6501-ASSE-005
|
||||
// 6501-ASSE-006
|
||||
// 6501-ASSE-007
|
||||
// 6501-ASSE-008
|
||||
// 6501-ASSE-009
|
||||
// 6501-ASSE-010
|
||||
// 6501-ASSE-011
|
||||
cy.getByTestId(`${index}_label`).should('have.text', label);
|
||||
cy.getByTestId(`${index}_value`).should('have.text', value);
|
||||
|
||||
// 6501-ASSE-012
|
||||
if (indicesWithLabelTooltips.includes(index)) {
|
||||
if (labelTooltip) {
|
||||
testTooltip(index, 'label', labelTooltip);
|
||||
}
|
||||
}
|
||||
if (indicesWithValueTooltips.includes(index)) {
|
||||
if (valueToolTip) {
|
||||
testTooltip(index, 'value', valueToolTip);
|
||||
}
|
||||
}
|
||||
});
|
||||
// 6501-ASSE-013
|
||||
cy.getByTestId(dialogCloseX).click();
|
||||
cy.document().then((doc) => {
|
||||
expect(doc.querySelector(assetDetailsDialog)).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
it('ERC20 Contract address', () => {
|
||||
visitPortfolioAndClickAsset('tBTC');
|
||||
cy.getByTestId(contractAddress).within(() => {
|
||||
// 6501-ASSE-014
|
||||
cy.getByTestId('external-link')
|
||||
.should('have.attr', 'target', '_blank')
|
||||
.should('have.text', '0x0158031158Bb4dF2AD02eAA31e8963E84EA978a4');
|
||||
});
|
||||
// 6501-ASSE-013
|
||||
cy.getByTestId(dialogCloseBtn).click();
|
||||
cy.document().then((doc) => {
|
||||
expect(doc.querySelector(assetDetailsDialog)).to.not.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -114,6 +114,8 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
});
|
||||
|
||||
it('can key to key transfers', function () {
|
||||
// 1003-TRAN-023
|
||||
// 1003-TRAN-006
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
|
||||
cy.getByTestId(collateralTab).click();
|
||||
@@ -199,7 +201,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
.first()
|
||||
.should('contain.text', 'Operational')
|
||||
.next()
|
||||
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
|
||||
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname)
|
||||
.next()
|
||||
.then(($el) => {
|
||||
const blockHeight = parseInt($el.text());
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
const dialogContent = 'dialog-content';
|
||||
const nodeHealth = 'node-health';
|
||||
const statusIncidentsLink = 'footer [data-testid=external-link]';
|
||||
|
||||
describe.skip('home', { tags: '@regression' }, () => {
|
||||
describe('home', { tags: '@regression' }, () => {
|
||||
before(() => {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.mockTradingPage();
|
||||
@@ -12,34 +13,17 @@ describe.skip('home', { tags: '@regression' }, () => {
|
||||
describe('footer', () => {
|
||||
it('shows current block height', () => {
|
||||
// 0006-NETW-004
|
||||
// 0006-NETW-005
|
||||
// 0006-NETW-008
|
||||
// 0006-NETW-009
|
||||
// 0006-NETW-011
|
||||
|
||||
cy.intercept('POST', 'http://localhost:3008/graphql', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.setDelay(3001);
|
||||
});
|
||||
});
|
||||
|
||||
cy.getByTestId(nodeHealth)
|
||||
.children()
|
||||
.first()
|
||||
.should('contain.text', 'Warning delay ( >3 sec)');
|
||||
|
||||
cy.intercept('POST', 'http://localhost:3008/graphql', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.setDelay(1);
|
||||
});
|
||||
});
|
||||
|
||||
cy.getByTestId(nodeHealth)
|
||||
.children()
|
||||
.first()
|
||||
.should('contain.text', 'Operational', { timeout: 10000 })
|
||||
.should('contain.text', 'Operational', {
|
||||
timeout: 10000,
|
||||
})
|
||||
.next()
|
||||
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
|
||||
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname)
|
||||
.next()
|
||||
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
|
||||
});
|
||||
@@ -82,12 +66,9 @@ describe.skip('home', { tags: '@regression' }, () => {
|
||||
.focus()
|
||||
.type(new URL(Cypress.env('VEGA_URL')).origin + '/graphql');
|
||||
cy.getByTestId('connect').click();
|
||||
cy.getByTestId(nodeHealth)
|
||||
.children()
|
||||
.first()
|
||||
.should('contain.text', 'Operational');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Network switcher', () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
@@ -97,10 +78,21 @@ describe.skip('home', { tags: '@regression' }, () => {
|
||||
|
||||
// 0006-NETW-002
|
||||
// 0006-NETW-003
|
||||
it('switch to fairground network', () => {
|
||||
cy.getByTestId('network-switcher').click();
|
||||
// 0006-NETW-011
|
||||
it('switch to fairground network and check status & incidents link', () => {
|
||||
cy.getByTestId('navigation')
|
||||
.find('[data-testid="network-switcher"]')
|
||||
.click();
|
||||
cy.getByTestId('network-item').contains('Fairground testnet').click();
|
||||
cy.get('[aria-haspopup="menu"]').should('contain.text', 'Fairground');
|
||||
cy.url().should('include', 'fairground.wtf');
|
||||
cy.contains('Continue').click();
|
||||
cy.get(statusIncidentsLink)
|
||||
.children('span')
|
||||
.should('have.text', 'Mainnet status & incidents');
|
||||
cy.get(statusIncidentsLink)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://blog.vega.xyz/tagged/vega-incident-reports');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const rowSelector =
|
||||
'[data-testid="tab-all-markets"] .ag-center-cols-container .ag-row';
|
||||
|
||||
describe('markets all table', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage().then(() => {
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/all');
|
||||
});
|
||||
});
|
||||
|
||||
it('can see table headers', () => {
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
const headers = [
|
||||
'Market',
|
||||
'Description',
|
||||
'Trading mode',
|
||||
'Status',
|
||||
'Best bid',
|
||||
'Best offer',
|
||||
'Mark price',
|
||||
'Settlement asset',
|
||||
'',
|
||||
];
|
||||
cy.getByTestId('tab-all-markets').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('markets tab should be rendered properly', () => {
|
||||
cy.get('[data-testid="All markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'active'
|
||||
);
|
||||
cy.get('[data-testid="Proposed markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'inactive'
|
||||
);
|
||||
cy.get('[data-testid="Closed markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'inactive'
|
||||
);
|
||||
});
|
||||
it('renders markets correctly', () => {
|
||||
// 6001-MARK-035
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="tradableInstrument.instrument.code"]')
|
||||
.should('have.text', 'SOLUSD');
|
||||
|
||||
// 6001-MARK-036
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="tradableInstrument.instrument.name"]')
|
||||
.should('have.text', 'SUSPENDED MARKET');
|
||||
|
||||
// 6001-MARK-037
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="tradingMode"]')
|
||||
.should('have.text', 'Continuous');
|
||||
|
||||
// 6001-MARK-038
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="state"]')
|
||||
.should('have.text', 'Active');
|
||||
|
||||
// 6001-MARK-039
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="data.bestBidPrice"]')
|
||||
.should('have.text', '0.00');
|
||||
|
||||
// 6001-MARK-040
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="data.bestOfferPrice"]')
|
||||
.should('have.text', '0.00');
|
||||
|
||||
// 6001-MARK-041
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="data.markPrice"]')
|
||||
.should('have.text', '84.41');
|
||||
|
||||
// 6001-MARK-042
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(
|
||||
'[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"]'
|
||||
)
|
||||
.should('have.text', 'XYZalpha');
|
||||
|
||||
// 6001-MARK-043
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(
|
||||
'[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"] button'
|
||||
)
|
||||
.click();
|
||||
cy.getByTestId('dialog-title').should('have.text', 'Asset details - tEURO');
|
||||
cy.getByTestId('close-asset-details-dialog').click();
|
||||
});
|
||||
|
||||
it('can open row actions', () => {
|
||||
// 6001-MARK-044
|
||||
cy.get('.ag-pinned-right-cols-container')
|
||||
.find('[col-id="market-actions"]')
|
||||
.first()
|
||||
.find('button')
|
||||
.click();
|
||||
|
||||
// 6001-MARK-045
|
||||
const dropdownContent = '[data-testid="market-actions-content"]';
|
||||
const dropdownContentItem = '[role="menuitem"]';
|
||||
cy.get(dropdownContent)
|
||||
.find(dropdownContentItem)
|
||||
.eq(0)
|
||||
// Cannot click the copy button as it falls back to window.prompt, blocking the test.
|
||||
.should('have.text', 'Copy Market ID');
|
||||
|
||||
// 6001-MARK-046
|
||||
cy.get(dropdownContent)
|
||||
.find(dropdownContentItem)
|
||||
.eq(1)
|
||||
.find('a')
|
||||
.then(($el) => {
|
||||
const href = $el.attr('href');
|
||||
expect(/\/markets\/market-1/.test(href || '')).to.equal(true);
|
||||
})
|
||||
.should('have.text', 'View on Explorer');
|
||||
|
||||
// 6001-MARK-047
|
||||
cy.get(dropdownContent)
|
||||
.find(dropdownContentItem)
|
||||
.eq(2)
|
||||
.should('have.text', 'View asset');
|
||||
cy.getByTestId('market-actions-content').click();
|
||||
});
|
||||
|
||||
it('able to open and sort full market list - market page', () => {
|
||||
const ExpectedSortedMarkets = [
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'SOLUSD',
|
||||
];
|
||||
cy.get('[data-testid="All markets"]').click({ force: true });
|
||||
cy.url().should('eq', Cypress.config('baseUrl') + '/#/markets/all');
|
||||
cy.contains('AAPL.MF21').should('be.visible');
|
||||
cy.get('.ag-header-cell-label').contains('Market').click(); // sort by market name
|
||||
for (let i = 0; i < ExpectedSortedMarkets.length; i++) {
|
||||
cy.get(`[row-index=${i}]`)
|
||||
.find('[col-id="tradableInstrument.instrument.code"]')
|
||||
.should('have.text', ExpectedSortedMarkets[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -55,12 +55,8 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
validateMarketDataRow(3, 'Quote Unit', 'BTC');
|
||||
});
|
||||
|
||||
// TODO: fix this test
|
||||
// New volume check logic, added by https://github.com/vegaprotocol/frontend-monorepo/pull/3870 has caused the
|
||||
// 24hr volume assertion to fail as it now reads 'Unknown'
|
||||
it.skip('market volume displayed', () => {
|
||||
it('market volume displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Market volume').click();
|
||||
validateMarketDataRow(0, '24 Hour Volume', '1');
|
||||
validateMarketDataRow(1, 'Open Interest', '-');
|
||||
validateMarketDataRow(2, 'Best Bid Volume', '1');
|
||||
validateMarketDataRow(3, 'Best Offer Volume', '3');
|
||||
|
||||
@@ -40,26 +40,26 @@ describe('markets selector', { tags: '@smoke' }, () => {
|
||||
{
|
||||
code: 'SOLUSD',
|
||||
markPrice: '84.41XYZalpha',
|
||||
change: '+200.00%',
|
||||
vol: '324h vol',
|
||||
change: '',
|
||||
vol: '0.0024h vol',
|
||||
},
|
||||
{
|
||||
code: 'ETHBTC.QM21',
|
||||
markPrice: '46,126.90058tBTC',
|
||||
change: '+200.00%',
|
||||
vol: '324h vol',
|
||||
change: '',
|
||||
vol: '0.0024h vol',
|
||||
},
|
||||
{
|
||||
code: 'BTCUSD.MF21',
|
||||
markPrice: '46,126.90058tDAI',
|
||||
change: '+200.00%',
|
||||
vol: '324h vol',
|
||||
change: '',
|
||||
vol: '0.0024h vol',
|
||||
},
|
||||
{
|
||||
code: 'AAPL.MF21',
|
||||
markPrice: '46,126.90058tUSDC',
|
||||
change: '+200.00%',
|
||||
vol: '324h vol',
|
||||
change: '',
|
||||
vol: '0.0024h vol',
|
||||
},
|
||||
];
|
||||
cy.getByTestId(list)
|
||||
@@ -80,7 +80,7 @@ describe('markets selector', { tags: '@smoke' }, () => {
|
||||
market.change
|
||||
);
|
||||
// 6001-MARK-025
|
||||
expect(item.find('[data-testid="sparkline-svg"]')).to.exist;
|
||||
expect(item.find('[data-testid="sparkline-svg"]')).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
import { checkSorting } from '@vegaprotocol/cypress';
|
||||
|
||||
const rowSelector =
|
||||
'[data-testid="tab-proposed-markets"] .ag-center-cols-container .ag-row';
|
||||
|
||||
describe('markets proposed table', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage().then(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('can see table headers', () => {
|
||||
const headers = [
|
||||
'Market',
|
||||
'Description',
|
||||
'Settlement asset',
|
||||
'State',
|
||||
'Voting',
|
||||
'Closing date',
|
||||
'Enactment date',
|
||||
'',
|
||||
];
|
||||
cy.getByTestId('tab-proposed-markets').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
// 6001-MARK-049
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="market"]')
|
||||
.should('have.text', 'ETHUSD');
|
||||
|
||||
// 6001-MARK-050
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="description"]')
|
||||
.should('have.text', 'ETHUSD');
|
||||
|
||||
// 6001-MARK-051
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="asset"]')
|
||||
.should('have.text', 'tDAI TEST');
|
||||
|
||||
// 6001-MARK-052
|
||||
// 6001-MARK-053
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="state"]')
|
||||
.should('have.text', 'Open');
|
||||
|
||||
// 6001-MARK-054
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="voting"]')
|
||||
.should('have.text', '');
|
||||
|
||||
// 6001-MARK-056
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="closing-date"]')
|
||||
.should('not.be.empty');
|
||||
|
||||
// 6001-MARK-057
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="enactment-date"]')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('can open row actions', () => {
|
||||
// 6001-MARK-058
|
||||
cy.get('.ag-pinned-right-cols-container')
|
||||
.find('[col-id="proposal-actions"]')
|
||||
.first()
|
||||
.find('button')
|
||||
.click();
|
||||
|
||||
const dropdownContent = '[data-testid="market-actions-content"]';
|
||||
const dropdownContentItem = '[role="menuitem"]';
|
||||
|
||||
// 6001-MARK-059
|
||||
cy.get(dropdownContent)
|
||||
.find(dropdownContentItem)
|
||||
.eq(0)
|
||||
.find('a')
|
||||
.should('have.text', 'View proposal')
|
||||
.and(
|
||||
'have.attr',
|
||||
'href',
|
||||
`${Cypress.env(
|
||||
'VEGA_TOKEN_URL'
|
||||
)}/proposals/e9ec6d5c46a7e7bcabf9ba7a893fa5a5eeeec08b731f06f7a6eb7bf0e605b829`
|
||||
);
|
||||
cy.getByTestId('market-actions-content').click();
|
||||
});
|
||||
|
||||
// 6001-MARK-060
|
||||
it('can see proposed market link', () => {
|
||||
cy.getByTestId('tab-proposed-markets')
|
||||
.find('[data-testid="external-link"]')
|
||||
.should('have.length', 11)
|
||||
.last()
|
||||
.should('have.text', 'Propose a new market')
|
||||
.and(
|
||||
'have.attr',
|
||||
'href',
|
||||
`${Cypress.env('VEGA_TOKEN_URL')}/proposals/propose/new-market`
|
||||
);
|
||||
});
|
||||
it('proposed markets tab should be sorted properly', () => {
|
||||
cy.get('[data-testid="Proposed markets"]').click({ force: true });
|
||||
const marketColDefault = [
|
||||
'ETHUSD',
|
||||
'LINKUSD',
|
||||
'ETHUSD',
|
||||
'ETHDAI.MF21',
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'TSLA.QM21',
|
||||
'AAVEDAI.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'UNIDAI.MF21',
|
||||
];
|
||||
const marketColAsc = [
|
||||
'AAPL.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'ETHDAI.MF21',
|
||||
'ETHUSD',
|
||||
'ETHUSD',
|
||||
'LINKUSD',
|
||||
'TSLA.QM21',
|
||||
'UNIDAI.MF21',
|
||||
];
|
||||
const marketColDesc = [
|
||||
'UNIDAI.MF21',
|
||||
'TSLA.QM21',
|
||||
'LINKUSD',
|
||||
'ETHUSD',
|
||||
'ETHUSD',
|
||||
'ETHDAI.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'BTCUSD.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'AAPL.MF21',
|
||||
];
|
||||
checkSorting('market', marketColDefault, marketColAsc, marketColDesc);
|
||||
|
||||
const stateColDefault = [
|
||||
'Open',
|
||||
'Passed',
|
||||
'Waiting for Node Vote',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Waiting for Node Vote',
|
||||
'Open',
|
||||
];
|
||||
const stateColAsc = [
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Waiting for Node Vote',
|
||||
'Waiting for Node Vote',
|
||||
];
|
||||
const stateColDesc = [
|
||||
'Waiting for Node Vote',
|
||||
'Waiting for Node Vote',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
];
|
||||
checkSorting('state', stateColDefault, stateColAsc, stateColDesc);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { aliasGQLQuery, checkSorting } from '@vegaprotocol/cypress';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { marketsQuery } from '@vegaprotocol/mock';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
|
||||
@@ -16,147 +16,7 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
cy.get('[data-testid^="market-link-"]').should('not.be.empty');
|
||||
cy.getByTestId('price').invoke('text').should('not.be.empty');
|
||||
cy.getByTestId('settlement-asset').should('not.be.empty');
|
||||
cy.getByTestId('price-change-percentage').should('not.be.empty');
|
||||
cy.getByTestId('price-change').should('not.be.empty');
|
||||
});
|
||||
|
||||
it('able to open and sort full market list - market page', () => {
|
||||
const ExpectedSortedMarkets = [
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'SOLUSD',
|
||||
];
|
||||
cy.url().should('eq', Cypress.config('baseUrl') + '/#/markets/all');
|
||||
cy.contains('AAPL.MF21').should('be.visible');
|
||||
cy.get('.ag-header-cell-label').contains('Market').click(); // sort by market name
|
||||
for (let i = 0; i < ExpectedSortedMarkets.length; i++) {
|
||||
cy.get(`[row-index=${i}]`)
|
||||
.find('[col-id="tradableInstrument.instrument.code"]')
|
||||
.should('have.text', ExpectedSortedMarkets[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it('proposed markets tab should be rendered properly', () => {
|
||||
cy.get('[data-testid="All markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'active'
|
||||
);
|
||||
cy.get('[data-testid="Proposed markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'inactive'
|
||||
);
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
cy.get('[data-testid="Proposed markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'active'
|
||||
);
|
||||
cy.getByTestId('tab-proposed-markets').should('be.visible');
|
||||
cy.get('.ag-body-viewport .ag-center-cols-container .ag-row').should(
|
||||
'have.length',
|
||||
10
|
||||
);
|
||||
cy.getByTestId('tab-proposed-markets')
|
||||
.find('[data-testid="external-link"]')
|
||||
.should('have.length', 11)
|
||||
.last()
|
||||
.should('have.text', 'Propose a new market')
|
||||
.and(
|
||||
'have.attr',
|
||||
'href',
|
||||
`${Cypress.env('VEGA_TOKEN_URL')}/proposals/propose/new-market`
|
||||
);
|
||||
});
|
||||
|
||||
it('proposed markets tab should be sorted properly', () => {
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
const marketColDefault = [
|
||||
'ETHUSD',
|
||||
'LINKUSD',
|
||||
'ETHUSD',
|
||||
'ETHDAI.MF21',
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'TSLA.QM21',
|
||||
'AAVEDAI.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'UNIDAI.MF21',
|
||||
];
|
||||
const marketColAsc = [
|
||||
'AAPL.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'ETHDAI.MF21',
|
||||
'ETHUSD',
|
||||
'ETHUSD',
|
||||
'LINKUSD',
|
||||
'TSLA.QM21',
|
||||
'UNIDAI.MF21',
|
||||
];
|
||||
const marketColDesc = [
|
||||
'UNIDAI.MF21',
|
||||
'TSLA.QM21',
|
||||
'LINKUSD',
|
||||
'ETHUSD',
|
||||
'ETHUSD',
|
||||
'ETHDAI.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'BTCUSD.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'AAPL.MF21',
|
||||
];
|
||||
checkSorting('market', marketColDefault, marketColAsc, marketColDesc);
|
||||
|
||||
const stateColDefault = [
|
||||
'Open',
|
||||
'Passed',
|
||||
'Waiting for Node Vote',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Waiting for Node Vote',
|
||||
'Open',
|
||||
];
|
||||
const stateColAsc = [
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Waiting for Node Vote',
|
||||
'Waiting for Node Vote',
|
||||
];
|
||||
const stateColDesc = [
|
||||
'Waiting for Node Vote',
|
||||
'Waiting for Node Vote',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
];
|
||||
checkSorting('state', stateColDefault, stateColAsc, stateColDesc);
|
||||
});
|
||||
|
||||
it.skip('opening auction subsets should be properly displayed', () => {
|
||||
it('opening auction subsets should be properly displayed', () => {
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
|
||||
@@ -19,6 +19,7 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
// 7001-COLL-005
|
||||
// 7001-COLL-006
|
||||
// 7001-COLL-007
|
||||
// 1003-TRAN-001
|
||||
|
||||
const tradingAccountRowId = '[row-id="t-0"]';
|
||||
cy.getByTestId('Collateral').click();
|
||||
@@ -62,6 +63,7 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
.click();
|
||||
cy.getByTestId('deposit').should('be.visible');
|
||||
cy.getByTestId('withdraw').should('be.visible');
|
||||
cy.getByTestId('transfer').should('be.visible');
|
||||
cy.getByTestId('breakdown').should('be.visible');
|
||||
cy.getByTestId('Collateral').click({ force: true });
|
||||
});
|
||||
@@ -122,25 +124,6 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId(dialogClose).click();
|
||||
});
|
||||
|
||||
it('sorting usage breakdown columns should work well', () => {
|
||||
// 7001-COLL-010
|
||||
cy.getByTestId('breakdown').contains('1.01').click();
|
||||
cy.getByTestId('usage-breakdown')
|
||||
.find('[col-id="type"]')
|
||||
.eq(1)
|
||||
.should('have.text', 'Margin');
|
||||
cy.getByTestId('usage-breakdown')
|
||||
.find('[col-id="type"]')
|
||||
.eq(2)
|
||||
.should('have.text', 'Margin');
|
||||
cy.getByTestId('usage-breakdown')
|
||||
.find('[col-id="type"]')
|
||||
.eq(3)
|
||||
.should('have.text', 'General');
|
||||
|
||||
cy.getByTestId(dialogClose).click();
|
||||
});
|
||||
|
||||
describe('sorting by ag-grid columns should work well', () => {
|
||||
before(() => {
|
||||
const dialogs = Cypress.$('[data-testid="dialog-close"]:visible');
|
||||
|
||||
@@ -88,7 +88,8 @@ describe(
|
||||
.pop()
|
||||
?.toLowerCase()} and not accepting orders`
|
||||
);
|
||||
cy.getByTestId('place-order').should('be.disabled');
|
||||
// 7002-SORD-060
|
||||
cy.getByTestId('place-order').should('be.enabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -77,7 +77,8 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
it('must warn if order size input has too many digits after the decimal place', function () {
|
||||
// 7002-SORD-016
|
||||
cy.getByTestId(orderSizeField).clear().type('1.234');
|
||||
cy.getByTestId(placeOrderBtn).should('be.disabled');
|
||||
// 7002-SORD-060
|
||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
||||
cy.getByTestId('dealticket-error-message-size-market').should(
|
||||
'have.text',
|
||||
'Size must be whole numbers for this market'
|
||||
@@ -86,12 +87,13 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
|
||||
it('must warn if order size is set to 0', function () {
|
||||
cy.getByTestId(orderSizeField).clear().type('0');
|
||||
cy.getByTestId(placeOrderBtn).should('be.disabled');
|
||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
||||
cy.getByTestId('dealticket-error-message-size-market').should(
|
||||
'have.text',
|
||||
'Size cannot be lower than 1'
|
||||
);
|
||||
});
|
||||
|
||||
it('must have total margin available', () => {
|
||||
// 7001-COLL-011
|
||||
cy.getByTestId('tab-ticket')
|
||||
@@ -100,9 +102,8 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
.within(() => {
|
||||
cy.get('[data-state="closed"]').should(
|
||||
'have.text',
|
||||
'Total margin available'
|
||||
'Total margin available100,000.01 tDAI'
|
||||
);
|
||||
cy.get('.text-neutral-500').should('have.text', '100,000.01 tDAI');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,13 +23,14 @@ describe(
|
||||
});
|
||||
|
||||
it('should show an error if your balance is zero', () => {
|
||||
cy.getByTestId('place-order').should('be.disabled');
|
||||
// 7002-SORD-060
|
||||
cy.getByTestId('place-order').should('be.enabled');
|
||||
// 7002-SORD-003
|
||||
cy.getByTestId('dealticket-error-message-zero-balance').should(
|
||||
'have.text',
|
||||
'You need ' +
|
||||
'tDAI' +
|
||||
' in your wallet to trade in this market.See all your collateral.Make a deposit'
|
||||
' in your wallet to trade in this market. See all your collateral.Make a deposit'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').should('exist');
|
||||
});
|
||||
|
||||
@@ -34,9 +34,9 @@ describe('suspended market validation', { tags: '@regression' }, () => {
|
||||
|
||||
it('should show warning for market order', function () {
|
||||
cy.getByTestId(toggleMarket).click();
|
||||
cy.getByTestId(placeOrderBtn).should('not.be.disabled');
|
||||
// 7002-SORD-060
|
||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
||||
cy.getByTestId(placeOrderBtn).click();
|
||||
cy.getByTestId(placeOrderBtn).should('be.disabled');
|
||||
cy.getByTestId('dealticket-error-message-type').should(
|
||||
'have.text',
|
||||
'This market is in auction until it reaches sufficient liquidity. Only limit orders are permitted when market is in auction'
|
||||
@@ -59,7 +59,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
|
||||
cy.getByTestId(orderTIFDropDown).select(
|
||||
TIFlist.filter((item) => item.code === 'FOK')[0].value
|
||||
);
|
||||
cy.getByTestId(placeOrderBtn).should('be.disabled');
|
||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
||||
cy.getByTestId('dealticket-error-message-tif').should(
|
||||
'have.text',
|
||||
'This market is in auction until it reaches sufficient liquidity. Until the auction ends, you can only place GFA, GTT, or GTC limit orders'
|
||||
|
||||
@@ -98,23 +98,14 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
|
||||
},
|
||||
},
|
||||
];
|
||||
const marketData = marketsDataQuery();
|
||||
const edges = marketData.marketsConnection?.edges.map((market) => {
|
||||
const replace =
|
||||
market.node.data?.market.id === 'market-2' ? null : market.node.data;
|
||||
return { ...market, node: { ...market.node, data: replace } };
|
||||
});
|
||||
const overrides = {
|
||||
...marketData,
|
||||
marketsConnection: { ...marketData.marketsConnection, edges },
|
||||
marketsConnection: { edges: [] },
|
||||
};
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'MarketsData', overrides, errors);
|
||||
});
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.get('.pointer-events-none.absolute.inset-0').contains(
|
||||
'Something went wrong:'
|
||||
);
|
||||
cy.get('[data-testid="tab-positions"]').contains('no market data');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,95 +1,141 @@
|
||||
import { selectAsset } from '../support/helpers';
|
||||
|
||||
const formFieldError = 'input-error-text';
|
||||
const toAddressField = '[name="toAddress"]';
|
||||
// #region consts
|
||||
const amountField = 'input[name="amount"]';
|
||||
const submitTransferBtn = '[type="submit"]';
|
||||
const transferForm = 'transfer-form';
|
||||
const errorText = 'input-error-text';
|
||||
const openTransferDialog = 'open-transfer-dialog';
|
||||
const amountShortName = 'input[name="amount"] + div + span.text-xs';
|
||||
const assetSelection = 'select-asset';
|
||||
const assetBalance = 'asset-balance';
|
||||
const assetOption = 'rich-select-option';
|
||||
const closeDialog = 'dialog-close';
|
||||
const dialogTitle = 'dialog-title';
|
||||
const dialogTransferText = 'dialog-transfer-text';
|
||||
const dropdownMenu = 'dropdown-menu';
|
||||
const errorText = 'input-error-text';
|
||||
const formFieldError = 'input-error-text';
|
||||
const includeTransferFeeRadioBtn = 'include-transfer-fee';
|
||||
const keyID = '[data-testid="dialog-transfer-text"] > .rounded-md';
|
||||
const manageVegaWallet = 'manage-vega-wallet';
|
||||
const openTransferDialog = 'open-transfer-dialog';
|
||||
const submitTransferBtn = '[type="submit"]';
|
||||
const toAddressField = '[name="toAddress"]';
|
||||
const totalTransferfee = 'total-transfer-fee';
|
||||
const transfer = 'transfer';
|
||||
const transferAmount = 'transfer-amount';
|
||||
const transferForm = 'transfer-form';
|
||||
const transferFee = 'transfer-fee';
|
||||
const walletTransfer = 'wallet-transfer';
|
||||
|
||||
const ASSET_SEPOLIA_TBTC = 2;
|
||||
const ASSET_EURO = 1;
|
||||
const ASSET_SEPOLIA_TBTC = 2;
|
||||
|
||||
const toastContent = 'toast-content';
|
||||
const collateralTab = 'Collateral';
|
||||
const toastCloseBtn = 'toast-close';
|
||||
const toastContent = 'toast-content';
|
||||
// #endregion
|
||||
|
||||
describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId('Trading').first().click();
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(dropdownMenu).first().click();
|
||||
cy.getByTestId(transfer).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
cy.mockVegaWalletTransaction();
|
||||
});
|
||||
|
||||
it('transfer fees tooltips', () => {
|
||||
// 1003-TRAN-015
|
||||
// 1003-TRAN-016
|
||||
// 1003-TRAN-017
|
||||
// 1003-TRAN-018
|
||||
// 1003-TRAN-019
|
||||
cy.getByTestId(transferForm);
|
||||
cy.contains('Enter manually').click();
|
||||
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.type('1', { delay: 100, force: true });
|
||||
|
||||
/// Check Include Transfer Fee tooltip
|
||||
cy.get('label[for="include-transfer-fee"] div').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(dialogTitle).click();
|
||||
|
||||
//Check Transfer Fee tooltip
|
||||
cy.contains('div', 'Transfer fee').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(dialogTitle).click();
|
||||
|
||||
//Check Amount to be transferred tooltip
|
||||
cy.contains('div', 'Amount to be transferred').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(dialogTitle).click();
|
||||
|
||||
//Check Total amount (with fee) tooltip
|
||||
cy.contains('div', 'Total amount (with fee)').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('transfer fees', () => {
|
||||
// 1003-TRAN-020
|
||||
// 1003-TRAN-021
|
||||
// 1003-TRAN-022
|
||||
// 1003-TRAN-023
|
||||
cy.getByTestId(transferForm);
|
||||
cy.contains('Enter manually').click();
|
||||
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(includeTransferFeeRadioBtn).should('be.disabled');
|
||||
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.type('1', { delay: 100, force: true });
|
||||
|
||||
cy.getByTestId(transferFee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '0.01');
|
||||
cy.getByTestId(transferAmount)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '1.00');
|
||||
cy.getByTestId(totalTransferfee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '1.01');
|
||||
cy.getByTestId(includeTransferFeeRadioBtn).click();
|
||||
cy.getByTestId(transferFee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '0.01');
|
||||
cy.getByTestId(transferAmount)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '0.99');
|
||||
cy.getByTestId(totalTransferfee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '1.00');
|
||||
});
|
||||
});
|
||||
describe(
|
||||
'transfer form validation and transfer from options',
|
||||
{ tags: '@smoke' },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
});
|
||||
|
||||
it('empty fields', () => {
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
|
||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
||||
// only 2 despite 3 fields because the ethereum address will be auto populated
|
||||
cy.getByTestId(formFieldError).should('have.length', 3);
|
||||
});
|
||||
it('min amount', () => {
|
||||
// 1002-WITH-010
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.get(amountField).clear().type('0');
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'Value is below minimum'
|
||||
);
|
||||
});
|
||||
it('max amount', () => {
|
||||
selectAsset(ASSET_EURO); // Will be above maximum because the vega wallet doesn't have any collateral
|
||||
cy.get(amountField).clear().type('1001', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'You cannot transfer more than your available collateral'
|
||||
);
|
||||
});
|
||||
|
||||
it('can start transfer from vega wallet', () => {
|
||||
cy.getByTestId(closeDialog).click();
|
||||
cy.getByTestId('manage-vega-wallet').click();
|
||||
cy.getByTestId('wallet-transfer').should('have.text', 'Transfer').click();
|
||||
cy.getByTestId(dialogTransferText).should(
|
||||
'contain.text',
|
||||
'Transfer funds to another Vega key from 02ecea…342f65 If you are at all unsure, stop and seek advice.'
|
||||
);
|
||||
});
|
||||
|
||||
it('can start transfer from trading collateral table', () => {
|
||||
cy.getByTestId(closeDialog).click();
|
||||
cy.getByTestId('Trading').first().click();
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).should('not.exist');
|
||||
cy.getByTestId('Portfolio').eq(0).click();
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
cy.getByTestId(dialogTransferText).should(
|
||||
'contain.text',
|
||||
'Transfer funds to another Vega key from 02ecea…342f65 If you are at all unsure, stop and seek advice.'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe(
|
||||
'withdraw actions',
|
||||
'transfer form validation',
|
||||
{ tags: '@regression', testIsolation: true },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
@@ -99,43 +145,141 @@ describe(
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
cy.getByTestId(manageVegaWallet).click();
|
||||
cy.getByTestId(walletTransfer).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
cy.mockVegaWalletTransaction();
|
||||
});
|
||||
|
||||
it('key to key transfers by select key', function () {
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.getByTestId(transferForm).find(toAddressField).select(1);
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm).find(amountField).type('1', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Awaiting confirmation'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
it('transfer Text', () => {
|
||||
// 1003-TRAN-003
|
||||
cy.getByTestId(dialogTransferText)
|
||||
.should('exist')
|
||||
.get(keyID)
|
||||
.invoke('text')
|
||||
.should('match', /[\w.]{6}…[\w.]{6}/);
|
||||
});
|
||||
|
||||
it('key to key transfers by enter manual key', function () {
|
||||
it('invalid vega key validation', () => {
|
||||
//1003-TRAN-013
|
||||
//1003-TRAN-004
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.contains('Enter manually').click();
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type(
|
||||
'7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535'
|
||||
);
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm).find(amountField).type('1', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(toAddressField).type('asd');
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
cy.getByTestId(formFieldError).should('contain.text', 'Invalid Vega key');
|
||||
cy.contains('label', 'Vega key').should('be.visible');
|
||||
cy.contains('label', 'Asset').should('be.visible');
|
||||
cy.contains('label', 'Amount').should('be.visible');
|
||||
});
|
||||
|
||||
it('empty fields', () => {
|
||||
// 1003-TRAN-012
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
||||
cy.getByTestId(formFieldError).should('have.length', 3);
|
||||
});
|
||||
|
||||
it('min amount', () => {
|
||||
// 1002-WITH-010
|
||||
// 1003-TRAN-014
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.get(amountField).clear().type('0');
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'Awaiting confirmation'
|
||||
'Value is below minimum'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
});
|
||||
|
||||
it('max amount', () => {
|
||||
// 1003-TRAN-002
|
||||
// 1003-TRAN-011
|
||||
// 1003-TRAN-002
|
||||
selectAsset(ASSET_EURO); // Will be above maximum because the vega wallet doesn't have any collateral
|
||||
cy.get(amountField).clear().type('1001', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'You cannot transfer more than your available collateral'
|
||||
);
|
||||
cy.getByTestId(closeDialog).click();
|
||||
});
|
||||
}
|
||||
);
|
||||
describe('withdraw actions', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
cy.mockVegaWalletTransaction();
|
||||
});
|
||||
|
||||
it('key to key transfers by select key', () => {
|
||||
// 1003-TRAN-001
|
||||
// 1003-TRAN-006
|
||||
// 1003-TRAN-007
|
||||
// 1003-TRAN-008
|
||||
// 1003-TRAN-009
|
||||
// 1003-TRAN-010
|
||||
// 1003-TRAN-023
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.getByTestId(transferForm).find(toAddressField).select(1);
|
||||
|
||||
cy.getByTestId(assetSelection).click();
|
||||
cy.getByTestId(assetOption);
|
||||
cy.getByTestId(assetBalance).should('not.be.empty');
|
||||
cy.getByTestId(assetOption).should('have.length.gt', 4);
|
||||
|
||||
let optionText: string;
|
||||
cy.getByTestId(assetOption)
|
||||
.eq(2)
|
||||
.invoke('text')
|
||||
.then((text: string) => {
|
||||
optionText = text;
|
||||
cy.getByTestId(assetOption).eq(2).click();
|
||||
cy.getByTestId(assetSelection).should('have.text', optionText);
|
||||
});
|
||||
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.type('1', { delay: 100, force: true });
|
||||
|
||||
cy.getByTestId(transferForm).find(amountShortName).should('not.be.empty');
|
||||
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Awaiting confirmation'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
});
|
||||
|
||||
it('key to key transfers by enter manual key', () => {
|
||||
//1003-TRAN-005
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.contains('Enter manually').click();
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.type('1', { delay: 100, force: true });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Awaiting confirmation'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
||||
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_DOCS_URL=#
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
|
||||
@@ -11,14 +11,12 @@ import {
|
||||
formatNumberPercentage,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { updateGridData } from '@vegaprotocol/datagrid';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
Tab,
|
||||
Tabs,
|
||||
Link as UiToolkitLink,
|
||||
@@ -26,14 +24,13 @@ import {
|
||||
ExternalLink,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { memo, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { Header, HeaderStat, HeaderTitle } from '../../components/header';
|
||||
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { IGetRowsParams } from 'ag-grid-community';
|
||||
|
||||
import type { LiquidityProvisionData, Filter } from '@vegaprotocol/liquidity';
|
||||
import type { Filter } from '@vegaprotocol/liquidity';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
|
||||
@@ -74,21 +71,12 @@ export const LiquidityContainer = ({
|
||||
}) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const { data: market } = useMarket(marketId);
|
||||
const dataRef = useRef<LiquidityProvisionData[] | null>(null);
|
||||
|
||||
// To be removed when liquidityProvision subscriptions are working
|
||||
useReloadLiquidityData(marketId);
|
||||
|
||||
const update = useCallback(
|
||||
({ data }: { data: LiquidityProvisionData[] | null }) => {
|
||||
return updateGridData(dataRef, data, gridRef);
|
||||
},
|
||||
[gridRef]
|
||||
);
|
||||
|
||||
const { data, loading, error } = useDataProvider({
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: lpAggregatedDataProvider,
|
||||
update,
|
||||
variables: { marketId: marketId || '', filter },
|
||||
skip: !marketId,
|
||||
});
|
||||
@@ -103,36 +91,16 @@ export const LiquidityContainer = ({
|
||||
]);
|
||||
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
||||
|
||||
const getRows = useCallback(
|
||||
async ({ successCallback, startRow, endRow }: IGetRowsParams) => {
|
||||
const rowsThisBlock = dataRef.current
|
||||
? dataRef.current.slice(startRow, endRow)
|
||||
: [];
|
||||
const lastRow = dataRef.current ? dataRef.current.length : 0;
|
||||
successCallback(rowsThisBlock, lastRow);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-full relative">
|
||||
<LiquidityTable
|
||||
ref={gridRef}
|
||||
datasource={{ getRows }}
|
||||
rowModelType="infinite"
|
||||
rowData={data}
|
||||
symbol={symbol}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
stakeToCcyVolume={stakeToCcyVolume}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No data')}
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={data}
|
||||
noDataMessage={t('No liquidity provisions')}
|
||||
noDataCondition={(data) => !data?.length}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,25 +6,32 @@ import { MemoryRouter } from 'react-router-dom';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type {
|
||||
MarketCandlesQuery,
|
||||
MarketCandlesQueryVariables,
|
||||
MarketDataUpdateFieldsFragment,
|
||||
MarketDataUpdateSubscription,
|
||||
} from '@vegaprotocol/markets';
|
||||
import { MarketCandlesDocument } from '@vegaprotocol/markets';
|
||||
import { MarketDataUpdateDocument } from '@vegaprotocol/markets';
|
||||
import {
|
||||
AuctionTrigger,
|
||||
Interval,
|
||||
MarketState,
|
||||
MarketTradingMode,
|
||||
} from '@vegaprotocol/types';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { subDays } from 'date-fns';
|
||||
|
||||
describe('MarketSelectorItem', () => {
|
||||
const yesterday = new Date();
|
||||
yesterday.setHours(yesterday.getHours() - 20);
|
||||
const market = createMarketFragment({
|
||||
id: 'market-0',
|
||||
decimalPlaces: 2,
|
||||
// @ts-ignore fragment doesn't contain candles
|
||||
candles: [
|
||||
{ close: '5', volume: '50' },
|
||||
{ close: '10', volume: '50' },
|
||||
{ close: '5', volume: '50', periodStart: yesterday.toISOString() },
|
||||
{ close: '10', volume: '50', periodStart: yesterday.toISOString() },
|
||||
],
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
@@ -36,6 +43,7 @@ describe('MarketSelectorItem', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const marketData: MarketDataUpdateFieldsFragment = {
|
||||
__typename: 'ObservableMarketData',
|
||||
marketId: market.id,
|
||||
@@ -63,26 +71,32 @@ describe('MarketSelectorItem', () => {
|
||||
trigger: AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED,
|
||||
priceMonitoringBounds: null,
|
||||
};
|
||||
const mock: MockedResponse<MarketDataUpdateSubscription> = {
|
||||
request: {
|
||||
query: MarketDataUpdateDocument,
|
||||
variables: {
|
||||
marketId: market.id,
|
||||
},
|
||||
|
||||
const candles = [
|
||||
{
|
||||
open: '5',
|
||||
close: '5',
|
||||
high: '5',
|
||||
low: '5',
|
||||
volume: '50',
|
||||
periodStart: yesterday.toISOString(),
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
marketsData: [marketData],
|
||||
},
|
||||
{
|
||||
open: '10',
|
||||
close: '10',
|
||||
high: '10',
|
||||
low: '10',
|
||||
volume: '50',
|
||||
periodStart: yesterday.toISOString(),
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
const mockOnSelect = jest.fn();
|
||||
|
||||
const renderJsx = () => {
|
||||
const renderJsx = (mocks: MockedResponse[]) => {
|
||||
return render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider mocks={[mock]}>
|
||||
<MockedProvider mocks={mocks}>
|
||||
<MarketSelectorItem
|
||||
market={market}
|
||||
currentMarketId={market.id}
|
||||
@@ -94,11 +108,66 @@ describe('MarketSelectorItem', () => {
|
||||
);
|
||||
};
|
||||
|
||||
let dateSpy: jest.SpyInstance;
|
||||
const ts = 1685577600000; // 2023-06-01
|
||||
|
||||
beforeAll(() => {
|
||||
dateSpy = jest.spyOn(Date, 'now').mockImplementation(() => ts);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
dateSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('renders market information', async () => {
|
||||
const symbol =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
|
||||
renderJsx();
|
||||
const mock: MockedResponse<MarketDataUpdateSubscription> = {
|
||||
request: {
|
||||
query: MarketDataUpdateDocument,
|
||||
variables: {
|
||||
marketId: market.id,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
marketsData: [marketData],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const since = subDays(Date.now(), 5).toISOString();
|
||||
const variables: MarketCandlesQueryVariables = {
|
||||
marketId: market.id,
|
||||
interval: Interval.INTERVAL_I1H,
|
||||
since,
|
||||
};
|
||||
const mockCandles: MockedResponse<MarketCandlesQuery> = {
|
||||
request: {
|
||||
query: MarketCandlesDocument,
|
||||
variables,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
candlesConnection: {
|
||||
edges: candles.map((c) => ({
|
||||
node: c,
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderJsx([mock, mockCandles]);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
// link renders and is styled
|
||||
@@ -106,18 +175,17 @@ describe('MarketSelectorItem', () => {
|
||||
|
||||
expect(link).toHaveClass('ring-1');
|
||||
|
||||
expect(screen.getByTitle('24h vol')).toHaveTextContent('100');
|
||||
expect(screen.getByTitle('24h vol')).toHaveTextContent('0.00');
|
||||
expect(screen.getByTitle(symbol)).toHaveTextContent('-');
|
||||
|
||||
// candles are loaded immediately
|
||||
expect(screen.getByTestId('market-item-change')).toHaveTextContent(
|
||||
'+100.00%'
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTitle('24h vol')).toHaveTextContent('100');
|
||||
expect(screen.getByTitle(symbol)).toHaveTextContent(
|
||||
addDecimalsFormatNumber(marketData.markPrice, market.decimalPlaces)
|
||||
);
|
||||
expect(screen.getByTestId('market-item-change')).toHaveTextContent(
|
||||
'+100.00%'
|
||||
);
|
||||
});
|
||||
|
||||
await userEvent.click(link);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
import type { CSSProperties, ReactNode } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from '@vegaprotocol/utils';
|
||||
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
|
||||
import { calcCandleVolume } from '@vegaprotocol/markets';
|
||||
import { useCandles } from '@vegaprotocol/markets';
|
||||
import { useMarketDataUpdateSubscription } from '@vegaprotocol/markets';
|
||||
import { Sparkline } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
@@ -80,8 +81,9 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
|
||||
: '';
|
||||
|
||||
const instrument = market.tradableInstrument.instrument;
|
||||
const { oneDayCandles } = useCandles({ marketId: market.id });
|
||||
|
||||
const vol = market.candles ? calcCandleVolume(market.candles) : '0';
|
||||
const vol = oneDayCandles ? calcCandleVolume(oneDayCandles) : '0';
|
||||
const volume =
|
||||
vol && vol !== '0'
|
||||
? addDecimalsFormatNumber(vol, market.positionDecimalPlaces)
|
||||
@@ -111,20 +113,20 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
|
||||
value={price}
|
||||
label={instrument.product.settlementAsset.symbol}
|
||||
/>
|
||||
<div className="relative">
|
||||
{market.candles && (
|
||||
<PriceChange candles={market.candles.map((c) => c.close)} />
|
||||
<div className="relative text-xs p-1">
|
||||
{oneDayCandles && (
|
||||
<PriceChange candles={oneDayCandles.map((c) => c.close)} />
|
||||
)}
|
||||
|
||||
<div
|
||||
// absolute so height is not larger than price change value
|
||||
className="absolute right-0 bottom-0 w-[120px]"
|
||||
>
|
||||
{market.candles && (
|
||||
{oneDayCandles && (
|
||||
<Sparkline
|
||||
width={120}
|
||||
height={20}
|
||||
data={market.candles.filter(Boolean).map((c) => Number(c.close))}
|
||||
data={oneDayCandles.map((c) => Number(c.close))}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -133,7 +135,13 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const DataRow = ({ value, label }: { value: string; label: string }) => {
|
||||
const DataRow = ({
|
||||
value,
|
||||
label,
|
||||
}: {
|
||||
value: string | ReactNode;
|
||||
label: string;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className="text-ellipsis whitespace-nowrap overflow-hidden leading-tight"
|
||||
|
||||
@@ -296,7 +296,7 @@ const MainGrid = memo(
|
||||
</TradeGridChild>
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
preferredSize={sizesMiddle[2] || 430}
|
||||
preferredSize={sizesMiddle[2] || 300}
|
||||
minSize={200}
|
||||
>
|
||||
<TradeGridChild>
|
||||
|
||||
@@ -53,7 +53,7 @@ export const useMarketSelectorList = ({
|
||||
});
|
||||
|
||||
if (sort === Sort.None) {
|
||||
// Sort by market state primarilly and AtoZ secondarilly
|
||||
// Sort by market state primarily and AtoZ secondarily
|
||||
return orderBy(
|
||||
markets,
|
||||
[
|
||||
|
||||
@@ -27,7 +27,6 @@ import type { ColDef } from 'ag-grid-community';
|
||||
import { SettlementDateCell } from './settlement-date-cell';
|
||||
import { SettlementPriceCell } from './settlement-price-cell';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
type SettlementAsset =
|
||||
MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset'];
|
||||
@@ -55,7 +54,6 @@ export const Closed = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const {
|
||||
data: marketData,
|
||||
loading,
|
||||
error,
|
||||
reload,
|
||||
} = useDataProvider({
|
||||
@@ -117,21 +115,20 @@ export const Closed = () => {
|
||||
});
|
||||
return (
|
||||
<div className="h-full relative">
|
||||
<ClosedMarketsDataGrid rowData={rowData} />
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={marketData}
|
||||
noDataMessage={t('No markets')}
|
||||
reload={reload}
|
||||
/>
|
||||
</div>
|
||||
<ClosedMarketsDataGrid rowData={rowData} error={error} reload={reload} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
|
||||
const ClosedMarketsDataGrid = ({
|
||||
rowData,
|
||||
error,
|
||||
reload,
|
||||
}: {
|
||||
rowData: Row[];
|
||||
error: Error | undefined;
|
||||
reload: () => void;
|
||||
}) => {
|
||||
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
|
||||
const colDefs = useMemo(() => {
|
||||
const cols: ColDef[] = [
|
||||
@@ -315,7 +312,7 @@ const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
|
||||
resizable: true,
|
||||
minWidth: 100,
|
||||
}}
|
||||
overlayNoRowsTemplate="No data"
|
||||
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
||||
storeKey="closedMarkets"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -11,7 +11,7 @@ import type { AgGridReact } from 'ag-grid-react';
|
||||
export const DepositsContainer = () => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const { data, loading, error, reload } = useDataProvider({
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: depositsProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
@@ -23,21 +23,10 @@ export const DepositsContainer = () => {
|
||||
<div className="h-full relative">
|
||||
<DepositsTable
|
||||
rowData={data || []}
|
||||
suppressLoadingOverlay
|
||||
suppressNoRowsOverlay
|
||||
ref={gridRef}
|
||||
{...bottomPlaceholderProps}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
data={data}
|
||||
loading={loading}
|
||||
error={error}
|
||||
noDataCondition={(data) => !(data && data.length)}
|
||||
noDataMessage={t('No deposits')}
|
||||
reload={reload}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{!isReadOnly && (
|
||||
<div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
withdrawalProvider,
|
||||
useWithdrawalDialog,
|
||||
@@ -11,7 +11,7 @@ import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
|
||||
export const WithdrawalsContainer = () => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const { data, loading, error, reload } = useDataProvider({
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: withdrawalProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
@@ -24,19 +24,8 @@ export const WithdrawalsContainer = () => {
|
||||
<WithdrawalsTable
|
||||
data-testid="withdrawals-history"
|
||||
rowData={data}
|
||||
suppressLoadingOverlay
|
||||
suppressNoRowsOverlay
|
||||
overlayNoRowsTemplate={error ? error.message : t('No withdrawals')}
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
data={data}
|
||||
loading={loading}
|
||||
error={error}
|
||||
noDataCondition={(data) => !(data && data.length)}
|
||||
noDataMessage={t('No withdrawals')}
|
||||
reload={reload}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{!isReadOnly && (
|
||||
<div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { renderHook, act, waitFor } from '@testing-library/react';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { SentryInit, SentryClose } from '@vegaprotocol/utils';
|
||||
import { SentryInit, SentryClose } from '@vegaprotocol/logger';
|
||||
import { STORAGE_KEY, useTelemetryApproval } from './use-telemetry-approval';
|
||||
|
||||
const mockSetValue = jest.fn();
|
||||
const mockRemoveValue = jest.fn();
|
||||
jest.mock('@vegaprotocol/utils');
|
||||
jest.mock('@vegaprotocol/logger');
|
||||
jest.mock('@vegaprotocol/react-helpers', () => ({
|
||||
...jest.requireActual('@vegaprotocol/react-helpers'),
|
||||
useLocalStorage: jest
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useCallback } from 'react';
|
||||
import { SentryInit, SentryClose } from '@vegaprotocol/utils';
|
||||
import { SentryInit, SentryClose } from '@vegaprotocol/logger';
|
||||
import { ENV } from '../config';
|
||||
export const STORAGE_KEY = 'vega_telemetry_approval';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ENV } from './lib/config/env';
|
||||
import { LocalStorage, SentryInit } from '@vegaprotocol/utils';
|
||||
import { LocalStorage, SentryInit } from '@vegaprotocol/logger';
|
||||
import { STORAGE_KEY } from './lib/hooks/use-telemetry-approval';
|
||||
|
||||
const { dsn, envName } = ENV;
|
||||
|
||||
@@ -3,6 +3,7 @@ server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
add_header 'Cache-Control' 'max-age=60';
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { assetsProvider } from '@vegaprotocol/assets';
|
||||
import { marketsProvider } from '@vegaprotocol/markets';
|
||||
import { assetsMapProvider } from '@vegaprotocol/assets';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { marketsMapProvider } from '@vegaprotocol/markets';
|
||||
import {
|
||||
makeDataProvider,
|
||||
makeDerivedDataProvider,
|
||||
} from '@vegaprotocol/data-provider';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { Market } from '@vegaprotocol/markets';
|
||||
import produce from 'immer';
|
||||
|
||||
import {
|
||||
@@ -20,7 +21,6 @@ import type {
|
||||
AccountEventsSubscription,
|
||||
AccountsQueryVariables,
|
||||
} from './__generated__/Accounts';
|
||||
import type { Market } from '@vegaprotocol/markets';
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
|
||||
const AccountType = Schema.AccountType;
|
||||
@@ -45,9 +45,9 @@ export const getId = (
|
||||
? `${account.type}-${account.asset.id}-${account.market?.id || 'null'}`
|
||||
: `${account.type}-${account.assetId}-${account.marketId || 'null'}`;
|
||||
|
||||
export type Account = Omit<AccountFieldsFragment, 'market' | 'asset'> & {
|
||||
market?: Market | null;
|
||||
export type Account = Omit<AccountFieldsFragment, 'asset' | 'market'> & {
|
||||
asset: Asset;
|
||||
market?: Market | null;
|
||||
};
|
||||
|
||||
const update = (
|
||||
@@ -170,31 +170,22 @@ export const accountsDataProvider = makeDerivedDataProvider<
|
||||
>(
|
||||
[
|
||||
accountsOnlyDataProvider,
|
||||
(callback, client) => marketsProvider(callback, client, undefined),
|
||||
(callback, client) => assetsProvider(callback, client, undefined),
|
||||
(callback, client) => marketsMapProvider(callback, client, undefined),
|
||||
(callback, client) => assetsMapProvider(callback, client, undefined),
|
||||
],
|
||||
([accounts, markets, assets]): Account[] | null => {
|
||||
return accounts
|
||||
? accounts
|
||||
.map((account: AccountFieldsFragment) => {
|
||||
const market = markets.find(
|
||||
(market: Market) => market.id === account.market?.id
|
||||
);
|
||||
const asset = assets.find(
|
||||
(asset: Asset) => asset.id === account.asset?.id
|
||||
);
|
||||
const asset = (assets as Record<string, Asset>)[account.asset.id];
|
||||
const market =
|
||||
account.market?.id &&
|
||||
(markets as Record<string, Asset>)[account.market?.id];
|
||||
if (asset) {
|
||||
return {
|
||||
...account,
|
||||
partyId: account.party?.id,
|
||||
asset: {
|
||||
...asset,
|
||||
},
|
||||
market: market
|
||||
? {
|
||||
...market,
|
||||
}
|
||||
: null,
|
||||
asset,
|
||||
market,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
@@ -212,3 +203,18 @@ export const aggregatedAccountsDataProvider = makeDerivedDataProvider<
|
||||
[accountsDataProvider],
|
||||
(parts) => parts[0] && getAccountData(parts[0] as Account[])
|
||||
);
|
||||
|
||||
export const aggregatedAccountDataProvider = makeDerivedDataProvider<
|
||||
AccountFields,
|
||||
never,
|
||||
AccountsQueryVariables & { assetId: string }
|
||||
>(
|
||||
[
|
||||
(callback, client, { partyId }) =>
|
||||
aggregatedAccountsDataProvider(callback, client, { partyId }),
|
||||
],
|
||||
(parts, { assetId }) =>
|
||||
(parts[0] as AccountFields[]).find(
|
||||
(account) => account.asset.id === assetId
|
||||
) || null
|
||||
);
|
||||
|
||||
@@ -97,7 +97,7 @@ describe('AccountManager', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('splash loading should be displayed', async () => {
|
||||
it('loading should be displayed', async () => {
|
||||
mockedUseDataProvider.mockImplementation((args) => {
|
||||
return {
|
||||
loading: true,
|
||||
@@ -113,15 +113,6 @@ describe('AccountManager', () => {
|
||||
/>
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(
|
||||
(content, element) =>
|
||||
Boolean(
|
||||
element?.className.endsWith('flex items-center justify-center')
|
||||
) && content.startsWith('Loading')
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
expect(await screen.findByText('Loading...')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,51 @@
|
||||
import { useRef, useMemo, memo } from 'react';
|
||||
import { useRef, memo, useCallback, useState } from 'react';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { aggregatedAccountsDataProvider } from './accounts-data-provider';
|
||||
import type { AccountFields } from './accounts-data-provider';
|
||||
import {
|
||||
aggregatedAccountsDataProvider,
|
||||
aggregatedAccountDataProvider,
|
||||
} from './accounts-data-provider';
|
||||
import type { PinnedAsset } from './accounts-table';
|
||||
import { AccountTable } from './accounts-table';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
||||
import BreakdownTable from './breakdown-table';
|
||||
|
||||
const AccountBreakdown = ({
|
||||
assetId,
|
||||
partyId,
|
||||
}: {
|
||||
assetId: string;
|
||||
partyId: string;
|
||||
}) => {
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: aggregatedAccountDataProvider,
|
||||
variables: { partyId, assetId },
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className="h-[35vh] w-full m-auto flex flex-col"
|
||||
data-testid="usage-breakdown"
|
||||
>
|
||||
<h1 className="text-xl mb-4">
|
||||
{data?.asset?.symbol} {t('usage breakdown')}
|
||||
</h1>
|
||||
{data && (
|
||||
<p className="mb-2 text-sm">
|
||||
{t('You have %s %s in total.', [
|
||||
addDecimalsFormatNumber(data.total, data.asset.decimals),
|
||||
data.asset.symbol,
|
||||
])}
|
||||
</p>
|
||||
)}
|
||||
<BreakdownTable data={data?.breakdown || null} domLayout="autoHeight" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface AccountManagerProps {
|
||||
partyId: string;
|
||||
@@ -30,10 +69,40 @@ export const AccountManager = ({
|
||||
storeKey,
|
||||
}: AccountManagerProps) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||
const { data, loading, error, reload } = useDataProvider({
|
||||
const [breakdownAssetId, setBreakdownAssetId] = useState<string>();
|
||||
const update = useCallback(
|
||||
({ data }: { data: AccountFields[] | null }) => {
|
||||
if (!data || !gridRef.current?.api) {
|
||||
return false;
|
||||
}
|
||||
const pinnedAssetRowData =
|
||||
pinnedAsset && data.find((d) => d.asset.id === pinnedAsset.id);
|
||||
|
||||
if (pinnedAssetRowData) {
|
||||
const pinnedTopRow = gridRef.current.api.getPinnedTopRow(0);
|
||||
if (
|
||||
pinnedTopRow?.data?.balance === '0' &&
|
||||
pinnedAssetRowData.balance !== '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (!isEqual(pinnedTopRow?.data, pinnedAssetRowData)) {
|
||||
gridRef.current.api.setPinnedTopRowData([pinnedAssetRowData]);
|
||||
}
|
||||
}
|
||||
gridRef.current.api.setRowData(
|
||||
pinnedAssetRowData
|
||||
? data?.filter((d) => d !== pinnedAssetRowData)
|
||||
: data
|
||||
);
|
||||
return true;
|
||||
},
|
||||
[gridRef, pinnedAsset]
|
||||
);
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: aggregatedAccountsDataProvider,
|
||||
variables,
|
||||
variables: { partyId },
|
||||
update,
|
||||
});
|
||||
const bottomPlaceholderProps = useBottomPlaceholder({
|
||||
gridRef,
|
||||
@@ -44,27 +113,30 @@ export const AccountManager = ({
|
||||
<div className="relative h-full">
|
||||
<AccountTable
|
||||
ref={gridRef}
|
||||
rowData={error ? [] : data}
|
||||
rowData={data}
|
||||
onClickAsset={onClickAsset}
|
||||
onClickDeposit={onClickDeposit}
|
||||
onClickWithdraw={onClickWithdraw}
|
||||
onClickBreakdown={setBreakdownAssetId}
|
||||
isReadOnly={isReadOnly}
|
||||
suppressLoadingOverlay
|
||||
suppressNoRowsOverlay
|
||||
pinnedAsset={pinnedAsset}
|
||||
storeKey={storeKey}
|
||||
{...bottomPlaceholderProps}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No accounts')}
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
data={data}
|
||||
noDataCondition={(data) => !(data && data.length)}
|
||||
error={error}
|
||||
loading={loading}
|
||||
noDataMessage={pinnedAsset ? ' ' : t('No accounts')}
|
||||
reload={reload}
|
||||
/>
|
||||
</div>
|
||||
<Dialog
|
||||
size="medium"
|
||||
open={Boolean(breakdownAssetId)}
|
||||
onChange={(isOpen) => {
|
||||
if (!isOpen) {
|
||||
setBreakdownAssetId(undefined);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{breakdownAssetId && (
|
||||
<AccountBreakdown assetId={breakdownAssetId} partyId={partyId} />
|
||||
)}
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,13 +10,6 @@ const singleRow = {
|
||||
balance: '125600000',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'BTCUSD Monthly (30 Jun 2022)',
|
||||
},
|
||||
},
|
||||
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
|
||||
},
|
||||
asset: {
|
||||
@@ -136,13 +129,6 @@ describe('AccountsTable', () => {
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'BTCUSD Monthly (30 Jun 2022)',
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'ACCOUNT_TYPE_MARGIN',
|
||||
used: '125600000',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { forwardRef, useCallback, useMemo, useState } from 'react';
|
||||
import { forwardRef, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
isNumeric,
|
||||
@@ -11,28 +11,25 @@ import type {
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { COL_DEFS } from '@vegaprotocol/datagrid';
|
||||
import {
|
||||
Button,
|
||||
ButtonLink,
|
||||
Dialog,
|
||||
Button,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
AgGridLazy as AgGrid,
|
||||
CenteredGridCellWrapper,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import type {
|
||||
IDatasource,
|
||||
IGetRowsParams,
|
||||
RowHeightParams,
|
||||
RowNode,
|
||||
RowHeightParams,
|
||||
} from 'ag-grid-community';
|
||||
import type { AgGridReact, AgGridReactProps } from 'ag-grid-react';
|
||||
import BreakdownTable from './breakdown-table';
|
||||
import type { AccountFields } from './accounts-data-provider';
|
||||
import type { Asset } from '@vegaprotocol/types';
|
||||
import { CenteredGridCellWrapper } from '@vegaprotocol/datagrid';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import classNames from 'classnames';
|
||||
import { AccountsActionsDropdown } from './accounts-actions-dropdown';
|
||||
@@ -96,34 +93,42 @@ export interface AccountTableProps extends AgGridReactProps {
|
||||
onClickAsset: (assetId: string) => void;
|
||||
onClickWithdraw?: (assetId: string) => void;
|
||||
onClickDeposit?: (assetId: string) => void;
|
||||
onClickBreakdown?: (assetId: string) => void;
|
||||
isReadOnly: boolean;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
storeKey?: string;
|
||||
}
|
||||
|
||||
export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
||||
({ onClickAsset, onClickWithdraw, onClickDeposit, ...props }, ref) => {
|
||||
const [openBreakdown, setOpenBreakdown] = useState(false);
|
||||
const [row, setRow] = useState<AccountFields>();
|
||||
const pinnedAssetId = props.pinnedAsset?.id;
|
||||
|
||||
(
|
||||
{
|
||||
onClickAsset,
|
||||
onClickWithdraw,
|
||||
onClickDeposit,
|
||||
onClickBreakdown,
|
||||
rowData,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const pinnedAsset = useMemo(() => {
|
||||
const currentPinnedAssetRow = props.rowData?.find(
|
||||
(row) => row.asset.id === pinnedAssetId
|
||||
if (!props.pinnedAsset) {
|
||||
return;
|
||||
}
|
||||
const currentPinnedAssetRow = rowData?.find(
|
||||
(row) => row.asset.id === props.pinnedAsset?.id
|
||||
);
|
||||
if (!currentPinnedAssetRow) {
|
||||
if (props.pinnedAsset) {
|
||||
return {
|
||||
asset: props.pinnedAsset,
|
||||
available: '0',
|
||||
used: '0',
|
||||
total: '0',
|
||||
balance: '0',
|
||||
};
|
||||
}
|
||||
return {
|
||||
asset: props.pinnedAsset,
|
||||
available: '0',
|
||||
used: '0',
|
||||
total: '0',
|
||||
balance: '0',
|
||||
};
|
||||
}
|
||||
return currentPinnedAssetRow;
|
||||
}, [pinnedAssetId, props.pinnedAsset, props.rowData]);
|
||||
}, [props.pinnedAsset, rowData]);
|
||||
|
||||
const { getRowHeight } = props;
|
||||
|
||||
@@ -131,225 +136,192 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
||||
(params: RowHeightParams) => {
|
||||
if (
|
||||
params.node.rowPinned &&
|
||||
params.data.asset.id === pinnedAssetId &&
|
||||
params.data.asset.id === props.pinnedAsset?.id &&
|
||||
new BigNumber(params.data.total).isLessThanOrEqualTo(0)
|
||||
) {
|
||||
return 32;
|
||||
}
|
||||
return getRowHeight ? getRowHeight(params) : undefined;
|
||||
},
|
||||
[pinnedAssetId, getRowHeight]
|
||||
[props.pinnedAsset?.id, getRowHeight]
|
||||
);
|
||||
|
||||
const accountForPinnedAsset = props?.rowData?.find(
|
||||
(a) => a.asset.id === pinnedAssetId
|
||||
);
|
||||
const showDepositButton = accountForPinnedAsset
|
||||
? new BigNumber(accountForPinnedAsset.total).isLessThanOrEqualTo(0)
|
||||
: true;
|
||||
const showDepositButton = pinnedAsset?.balance === '0';
|
||||
|
||||
return (
|
||||
<>
|
||||
<AgGrid
|
||||
{...props}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
overlayNoRowsTemplate={t('No accounts')}
|
||||
getRowId={({
|
||||
data,
|
||||
}: {
|
||||
data: AccountFields & { isLastPlaceholder?: boolean; id?: string };
|
||||
}) => (data.isLastPlaceholder && data.id ? data.id : data.asset.id)}
|
||||
ref={ref}
|
||||
tooltipShowDelay={500}
|
||||
rowData={props.rowData?.filter(
|
||||
(data) => data.asset.id !== pinnedAssetId
|
||||
<AgGrid
|
||||
{...props}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
overlayNoRowsTemplate={t('No accounts')}
|
||||
getRowId={({
|
||||
data,
|
||||
}: {
|
||||
data: AccountFields & { isLastPlaceholder?: boolean; id?: string };
|
||||
}) => (data.isLastPlaceholder && data.id ? data.id : data.asset.id)}
|
||||
ref={ref}
|
||||
tooltipShowDelay={500}
|
||||
rowData={rowData?.filter(
|
||||
(data) => data.asset.id !== props.pinnedAsset?.id
|
||||
)}
|
||||
defaultColDef={{
|
||||
resizable: true,
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
sortable: true,
|
||||
comparator: accountValuesComparator,
|
||||
}}
|
||||
getRowHeight={getPinnedAssetRowHeight}
|
||||
pinnedTopRowData={pinnedAsset ? [pinnedAsset] : undefined}
|
||||
>
|
||||
<AgGridColumn
|
||||
headerName={t('Asset')}
|
||||
field="asset.symbol"
|
||||
headerTooltip={t(
|
||||
'Asset is the collateral that is deposited into the Vega protocol.'
|
||||
)}
|
||||
defaultColDef={{
|
||||
resizable: true,
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
sortable: true,
|
||||
comparator: accountValuesComparator,
|
||||
cellRenderer={({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<AccountFields, 'asset.symbol'>) => {
|
||||
return (
|
||||
<ButtonLink
|
||||
data-testid="asset"
|
||||
onClick={() => {
|
||||
if (data) {
|
||||
onClickAsset(data.asset.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</ButtonLink>
|
||||
);
|
||||
}}
|
||||
getRowHeight={getPinnedAssetRowHeight}
|
||||
pinnedTopRowData={pinnedAsset ? [pinnedAsset] : undefined}
|
||||
>
|
||||
<AgGridColumn
|
||||
headerName={t('Asset')}
|
||||
field="asset.symbol"
|
||||
headerTooltip={t(
|
||||
'Asset is the collateral that is deposited into the Vega protocol.'
|
||||
)}
|
||||
cellRenderer={({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<AccountFields, 'asset.symbol'>) => {
|
||||
return (
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Used')}
|
||||
type="rightAligned"
|
||||
field="used"
|
||||
headerTooltip={t(
|
||||
'Currently allocated to a market as margin or bond. Check the breakdown for details.'
|
||||
)}
|
||||
cellRenderer={({
|
||||
data,
|
||||
value,
|
||||
}: VegaICellRendererParams<AccountFields, 'used'>) => {
|
||||
if (!data) return null;
|
||||
const percentageUsed = percentageValue(value, data.total);
|
||||
const valueFormatted = formatWithAssetDecimals(data, value);
|
||||
|
||||
return data.breakdown ? (
|
||||
<>
|
||||
<ButtonLink
|
||||
data-testid="asset"
|
||||
data-testid="breakdown"
|
||||
onClick={() => {
|
||||
if (data) {
|
||||
onClickAsset(data.asset.id);
|
||||
}
|
||||
onClickBreakdown && onClickBreakdown(data.asset.id);
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
<span>{valueFormatted}</span>
|
||||
</ButtonLink>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Used')}
|
||||
type="rightAligned"
|
||||
field="used"
|
||||
headerTooltip={t(
|
||||
'Currently allocated to a market as margin or bond. Check the breakdown for details.'
|
||||
)}
|
||||
cellRenderer={({
|
||||
data,
|
||||
value,
|
||||
}: VegaICellRendererParams<AccountFields, 'used'>) => {
|
||||
if (!data) return null;
|
||||
const percentageUsed = percentageValue(value, data.total);
|
||||
const valueFormatted = formatWithAssetDecimals(data, value);
|
||||
<span
|
||||
className={classNames(
|
||||
colorClass(percentageUsed),
|
||||
'ml-2 inline-block w-14'
|
||||
)}
|
||||
>
|
||||
{percentageUsed.toFixed(2)}%
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span>{valueFormatted}</span>
|
||||
<span className="ml-2 inline-block w-14 text-neutral-500 dark:text-neutral-400">
|
||||
0.00%
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Available')}
|
||||
field="available"
|
||||
type="rightAligned"
|
||||
headerTooltip={t(
|
||||
'Deposited on the network, but not allocated to a market. Free to use for placing orders or providing liquidity.'
|
||||
)}
|
||||
cellRenderer={({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<AccountFields, 'available'>) => {
|
||||
const percentageUsed = percentageValue(data?.used, data?.total);
|
||||
|
||||
return data.breakdown ? (
|
||||
<>
|
||||
<ButtonLink
|
||||
data-testid="breakdown"
|
||||
return (
|
||||
<span className={colorClass(percentageUsed, true)}>
|
||||
{formatWithAssetDecimals(data, value)}
|
||||
</span>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Total')}
|
||||
type="rightAligned"
|
||||
field="total"
|
||||
headerTooltip={t(
|
||||
'The total amount of each asset on this key. Includes used and available collateral.'
|
||||
)}
|
||||
valueFormatter={({
|
||||
data,
|
||||
}: VegaValueFormatterParams<AccountFields, 'total'>) =>
|
||||
formatWithAssetDecimals(data, data?.total)
|
||||
}
|
||||
/>
|
||||
<AgGridColumn
|
||||
colId="accounts-actions"
|
||||
field="asset.id"
|
||||
{...COL_DEFS.actions}
|
||||
minWidth={showDepositButton ? 130 : COL_DEFS.actions.minWidth}
|
||||
maxWidth={showDepositButton ? 130 : COL_DEFS.actions.maxWidth}
|
||||
cellRenderer={({
|
||||
value: assetId,
|
||||
node,
|
||||
}: VegaICellRendererParams<AccountFields, 'asset.id'>) => {
|
||||
if (!assetId) return null;
|
||||
if (node.rowPinned && node.data?.balance === '0') {
|
||||
return (
|
||||
<CenteredGridCellWrapper className="h-[30px] justify-end py-1">
|
||||
<Button
|
||||
size="xs"
|
||||
variant="primary"
|
||||
data-testid="deposit"
|
||||
onClick={() => {
|
||||
setOpenBreakdown(!openBreakdown);
|
||||
setRow(data);
|
||||
onClickDeposit && onClickDeposit(assetId);
|
||||
}}
|
||||
>
|
||||
<span>{valueFormatted}</span>
|
||||
</ButtonLink>
|
||||
<span
|
||||
className={classNames(
|
||||
colorClass(percentageUsed),
|
||||
'ml-2 inline-block w-14'
|
||||
)}
|
||||
>
|
||||
{percentageUsed.toFixed(2)}%
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span>{valueFormatted}</span>
|
||||
<span className="ml-2 inline-block w-14 text-neutral-500 dark:text-neutral-400">
|
||||
0.00%
|
||||
</span>
|
||||
</>
|
||||
<VegaIcon name={VegaIconNames.DEPOSIT} /> {t('Deposit')}
|
||||
</Button>
|
||||
</CenteredGridCellWrapper>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Available')}
|
||||
field="available"
|
||||
type="rightAligned"
|
||||
headerTooltip={t(
|
||||
'Deposited on the network, but not allocated to a market. Free to use for placing orders or providing liquidity.'
|
||||
)}
|
||||
cellRenderer={({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<AccountFields, 'available'>) => {
|
||||
const percentageUsed = percentageValue(data?.used, data?.total);
|
||||
|
||||
return (
|
||||
<span className={colorClass(percentageUsed, true)}>
|
||||
{formatWithAssetDecimals(data, value)}
|
||||
</span>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Total')}
|
||||
type="rightAligned"
|
||||
field="total"
|
||||
headerTooltip={t(
|
||||
'The total amount of each asset on this key. Includes used and available collateral.'
|
||||
)}
|
||||
valueFormatter={({
|
||||
data,
|
||||
}: VegaValueFormatterParams<AccountFields, 'total'>) =>
|
||||
formatWithAssetDecimals(data, data?.total)
|
||||
}
|
||||
/>
|
||||
<AgGridColumn
|
||||
colId="accounts-actions"
|
||||
{...COL_DEFS.actions}
|
||||
minWidth={showDepositButton ? 130 : COL_DEFS.actions.minWidth}
|
||||
maxWidth={showDepositButton ? 130 : COL_DEFS.actions.maxWidth}
|
||||
cellRenderer={({
|
||||
data,
|
||||
}: VegaICellRendererParams<AccountFields>) => {
|
||||
if (!data) return null;
|
||||
else {
|
||||
if (showDepositButton && data.asset.id === pinnedAssetId) {
|
||||
return (
|
||||
<CenteredGridCellWrapper className="h-[30px] justify-end py-1">
|
||||
<Button
|
||||
size="xs"
|
||||
variant="primary"
|
||||
data-testid="deposit"
|
||||
onClick={() => {
|
||||
onClickDeposit && onClickDeposit(data.asset.id);
|
||||
}}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.DEPOSIT} /> {t('Deposit')}
|
||||
</Button>
|
||||
</CenteredGridCellWrapper>
|
||||
);
|
||||
return props.isReadOnly ? null : (
|
||||
<AccountsActionsDropdown
|
||||
assetId={assetId}
|
||||
assetContractAddress={
|
||||
node.data?.asset.source?.__typename === 'ERC20'
|
||||
? node.data.asset.source.contractAddress
|
||||
: undefined
|
||||
}
|
||||
return (
|
||||
!props.isReadOnly && (
|
||||
<AccountsActionsDropdown
|
||||
assetId={data.asset.id}
|
||||
assetContractAddress={
|
||||
data.asset.source?.__typename === 'ERC20'
|
||||
? data.asset.source.contractAddress
|
||||
: undefined
|
||||
}
|
||||
onClickDeposit={() => {
|
||||
onClickDeposit && onClickDeposit(data.asset.id);
|
||||
}}
|
||||
onClickWithdraw={() => {
|
||||
onClickWithdraw && onClickWithdraw(data.asset.id);
|
||||
}}
|
||||
onClickBreakdown={() => {
|
||||
setOpenBreakdown(!openBreakdown);
|
||||
setRow(data);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</AgGrid>
|
||||
<Dialog size="medium" open={openBreakdown} onChange={setOpenBreakdown}>
|
||||
<div
|
||||
className="h-[35vh] w-full m-auto flex flex-col"
|
||||
data-testid="usage-breakdown"
|
||||
>
|
||||
<h1 className="text-xl mb-4">
|
||||
{row?.asset?.symbol} {t('usage breakdown')}
|
||||
</h1>
|
||||
{row && (
|
||||
<p className="mb-2 text-sm">
|
||||
{t('You have %s %s in total.', [
|
||||
addDecimalsFormatNumber(row.total, row.asset.decimals),
|
||||
row.asset.symbol,
|
||||
])}
|
||||
</p>
|
||||
)}
|
||||
<BreakdownTable
|
||||
data={row?.breakdown || null}
|
||||
domLayout="autoHeight"
|
||||
/>
|
||||
</div>
|
||||
</Dialog>
|
||||
</>
|
||||
onClickDeposit={() => {
|
||||
onClickDeposit && onClickDeposit(assetId);
|
||||
}}
|
||||
onClickWithdraw={() => {
|
||||
onClickWithdraw && onClickWithdraw(assetId);
|
||||
}}
|
||||
onClickBreakdown={() => {
|
||||
onClickBreakdown && onClickBreakdown(assetId);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</AgGrid>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { createClient as createWSClient } from 'graphql-ws';
|
||||
import { onError } from '@apollo/client/link/error';
|
||||
import { RetryLink } from '@apollo/client/link/retry';
|
||||
import ApolloLinkTimeout from 'apollo-link-timeout';
|
||||
import { localLoggerFactory } from '@vegaprotocol/utils';
|
||||
import { localLoggerFactory } from '@vegaprotocol/logger';
|
||||
import { useHeaderStore } from './header-store';
|
||||
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
|
||||
@@ -31,6 +31,29 @@ export const assetsProvider = makeDataProvider<
|
||||
getData,
|
||||
});
|
||||
|
||||
export const assetsMapProvider = makeDerivedDataProvider<
|
||||
Record<string, Asset>,
|
||||
never,
|
||||
undefined
|
||||
>(
|
||||
[(callback, client) => assetsProvider(callback, client, undefined)],
|
||||
([assets]) => {
|
||||
return ((assets as ReturnType<typeof getData>) || []).reduce(
|
||||
(assets, asset) => {
|
||||
assets[asset.id] = asset;
|
||||
return assets;
|
||||
},
|
||||
{} as Record<string, Asset>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const useAssetsMapProvider = () =>
|
||||
useDataProvider({
|
||||
dataProvider: assetsMapProvider,
|
||||
variables: undefined,
|
||||
});
|
||||
|
||||
export const enabledAssetsProvider = makeDerivedDataProvider<
|
||||
ReturnType<typeof getData>,
|
||||
never
|
||||
|
||||
@@ -7,44 +7,54 @@ declare global {
|
||||
namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
VegaWalletTopUpRewardsPool(
|
||||
transferStartEpoch: number,
|
||||
transferEndEpoch: number
|
||||
): void;
|
||||
VegaWalletTopUpRewardsPool(): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function addVegaWalletTopUpRewardsPool() {
|
||||
Cypress.Commands.add(
|
||||
'VegaWalletTopUpRewardsPool',
|
||||
(transferStartEpoch, transferEndEpoch) => {
|
||||
const vegaWalletUrl = Cypress.env('VEGA_WALLET_URL');
|
||||
const token = Cypress.env('VEGA_WALLET_API_TOKEN');
|
||||
const vegaPubKey = Cypress.env('VEGA_PUBLIC_KEY');
|
||||
const assetAddress =
|
||||
'b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b';
|
||||
Cypress.Commands.add('VegaWalletTopUpRewardsPool', () => {
|
||||
let transferStartEpoch = 0;
|
||||
let transferEndEpoch = 0;
|
||||
const vegaWalletUrl = Cypress.env('VEGA_WALLET_URL');
|
||||
const token = Cypress.env('VEGA_WALLET_API_TOKEN');
|
||||
const vegaPubKey = Cypress.env('VEGA_PUBLIC_KEY');
|
||||
const assetAddress =
|
||||
'b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b';
|
||||
|
||||
createWalletClient(vegaWalletUrl, token);
|
||||
cy.getByTestId('epoch-countdown')
|
||||
.within(() => {
|
||||
cy.get('h3')
|
||||
.invoke('text')
|
||||
.then((epochText) => {
|
||||
transferStartEpoch = Number(epochText.replace('Epoch', '')) + 5;
|
||||
transferEndEpoch = transferStartEpoch + 100;
|
||||
|
||||
const transactionBody: TransferBody = {
|
||||
transfer: {
|
||||
fromAccountType: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
toAccountType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
to: '0000000000000000000000000000000000000000000000000000000000000000',
|
||||
asset: assetAddress,
|
||||
amount: '1000000000000000000',
|
||||
recurring: {
|
||||
factor: '1',
|
||||
startEpoch: transferStartEpoch,
|
||||
endEpoch: transferEndEpoch,
|
||||
console.log(transferStartEpoch);
|
||||
console.log(transferEndEpoch);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
createWalletClient(vegaWalletUrl, token);
|
||||
|
||||
const transactionBody: TransferBody = {
|
||||
transfer: {
|
||||
fromAccountType: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
toAccountType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
to: '0000000000000000000000000000000000000000000000000000000000000000',
|
||||
asset: assetAddress,
|
||||
amount: '1000000000000000000',
|
||||
recurring: {
|
||||
factor: '1',
|
||||
startEpoch: transferStartEpoch,
|
||||
endEpoch: transferEndEpoch,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
cy.highlight('Topping up rewards pool');
|
||||
cy.highlight('Topping up rewards pool');
|
||||
|
||||
sendVegaTx(vegaPubKey, transactionBody);
|
||||
}
|
||||
);
|
||||
sendVegaTx(vegaPubKey, transactionBody);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useColumnSizes } from './use-column-sizes';
|
||||
import classNames from 'classnames';
|
||||
|
||||
@@ -23,6 +24,8 @@ export const AgGridThemed = ({
|
||||
rowHeight: 22,
|
||||
headerHeight: 22,
|
||||
enableCellTextSelection: true,
|
||||
overlayLoadingTemplate: t('Loading...'),
|
||||
overlayNoRowsTemplate: t('No data'),
|
||||
};
|
||||
|
||||
const wrapperClasses = classNames('vega-ag-grid', {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { forwardRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { getDecimalSeparator, isNumeric } from '@vegaprotocol/utils';
|
||||
|
||||
interface NumericCellProps {
|
||||
value: number | bigint | null | undefined;
|
||||
valueFormatted: string;
|
||||
testId?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -12,7 +14,7 @@ interface NumericCellProps {
|
||||
* use, right aligned, monospace and decimals deemphasised
|
||||
*/
|
||||
export const NumericCell = forwardRef<HTMLSpanElement, NumericCellProps>(
|
||||
({ value, valueFormatted, testId }, ref) => {
|
||||
({ value, valueFormatted, testId, className }, ref) => {
|
||||
if (!isNumeric(value)) {
|
||||
return (
|
||||
<span ref={ref} data-testid={testId}>
|
||||
@@ -29,7 +31,10 @@ export const NumericCell = forwardRef<HTMLSpanElement, NumericCellProps>(
|
||||
return (
|
||||
<span
|
||||
ref={ref}
|
||||
className="font-mono relative text-black dark:text-white whitespace-nowrap overflow-hidden text-ellipsis text-right rtl-dir"
|
||||
className={classNames(
|
||||
'font-mono relative text-black dark:text-white whitespace-nowrap overflow-hidden text-ellipsis text-right rtl-dir',
|
||||
className
|
||||
)}
|
||||
data-testid={testId}
|
||||
title={valueFormatted}
|
||||
>
|
||||
|
||||
@@ -16,7 +16,7 @@ export const OrderTypeCell = ({
|
||||
data: order,
|
||||
onClick,
|
||||
}: OrderTypeCellProps) => {
|
||||
const id = order ? order.market.id : '';
|
||||
const id = order?.market?.id ?? '';
|
||||
|
||||
const label = useMemo(() => {
|
||||
if (!order) {
|
||||
|
||||
@@ -6,11 +6,15 @@ export interface IPriceCellProps {
|
||||
valueFormatted: string;
|
||||
testId?: string;
|
||||
onClick?: (price?: string | number) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const PriceCell = memo(
|
||||
forwardRef<HTMLSpanElement, IPriceCellProps>(
|
||||
({ value, valueFormatted, testId, onClick }: IPriceCellProps, ref) => {
|
||||
(
|
||||
{ value, valueFormatted, testId, onClick, className }: IPriceCellProps,
|
||||
ref
|
||||
) => {
|
||||
if (!isNumeric(value)) {
|
||||
return (
|
||||
<span data-testid="price" ref={ref}>
|
||||
@@ -27,6 +31,7 @@ export const PriceCell = memo(
|
||||
value={value}
|
||||
valueFormatted={valueFormatted}
|
||||
testId={testId || 'price'}
|
||||
className={className}
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
|
||||
@@ -4,17 +4,18 @@ import type {
|
||||
ValueFormatterParams,
|
||||
ValueGetterParams,
|
||||
} from 'ag-grid-community';
|
||||
import type { IDatasource, IGetRowsParams } from 'ag-grid-community';
|
||||
import type { IDatasource, IGetRowsParams, RowNode } from 'ag-grid-community';
|
||||
import type { AgGridReactProps } from 'ag-grid-react';
|
||||
|
||||
type Field = string | readonly string[];
|
||||
|
||||
type RowHelper<TObj, TRow, TField extends Field> = Omit<
|
||||
TObj,
|
||||
'data' | 'value'
|
||||
'data' | 'value' | 'node'
|
||||
> & {
|
||||
data?: TRow;
|
||||
value?: Get<TRow, TField>;
|
||||
node: (Omit<RowNode, 'data'> & { data?: TRow }) | null;
|
||||
};
|
||||
|
||||
export type VegaValueFormatterParams<TRow, TField extends Field> = RowHelper<
|
||||
@@ -29,10 +30,10 @@ export type VegaValueGetterParams<TRow, TField extends Field> = RowHelper<
|
||||
TField
|
||||
>;
|
||||
|
||||
export type VegaICellRendererParams<
|
||||
TRow,
|
||||
TField extends Field = string
|
||||
> = RowHelper<ICellRendererParams, TRow, TField>;
|
||||
export type VegaICellRendererParams<TRow, TField extends Field = string> = Omit<
|
||||
RowHelper<ICellRendererParams, TRow, TField>,
|
||||
'node'
|
||||
> & { node: NonNullable<RowHelper<ICellRendererParams, TRow, TField>['node']> };
|
||||
|
||||
export interface GetRowsParams<T> extends IGetRowsParams {
|
||||
successCallback(rowsThisBlock: T[], lastRow?: number): void;
|
||||
|
||||
@@ -31,6 +31,7 @@ export const MarginWarning = ({ margin, balance, asset }: Props) => {
|
||||
text: t(`Deposit ${asset.symbol}`),
|
||||
action: () => openDepositDialog(asset.id),
|
||||
dataTestId: 'deal-ticket-deposit-dialog-button',
|
||||
size: 'sm',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -21,10 +21,14 @@ export const ZeroBalanceError = ({
|
||||
testId="dealticket-error-message-zero-balance"
|
||||
message={
|
||||
<>
|
||||
You need {asset.symbol} in your wallet to trade in this market.
|
||||
{t(
|
||||
'You need %s in your wallet to trade in this market. ',
|
||||
asset.symbol
|
||||
)}
|
||||
{onClickCollateral && (
|
||||
<>
|
||||
See all your <Link onClick={onClickCollateral}>collateral</Link>.
|
||||
{t('See all your')}{' '}
|
||||
<Link onClick={onClickCollateral}>collateral</Link>.
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
@@ -33,7 +37,7 @@ export const ZeroBalanceError = ({
|
||||
text: t(`Make a deposit`),
|
||||
action: () => openDepositDialog(asset.id),
|
||||
dataTestId: 'deal-ticket-deposit-dialog-button',
|
||||
size: 'md',
|
||||
size: 'sm',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { ButtonVariant } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
variant: ButtonVariant;
|
||||
}
|
||||
|
||||
export const DealTicketButton = ({ disabled, variant }: Props) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const isDisabled = !pubKey || isReadOnly || disabled;
|
||||
export const DealTicketButton = ({ variant }: Props) => {
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<Button
|
||||
variant={variant}
|
||||
fill
|
||||
type="submit"
|
||||
disabled={isDisabled}
|
||||
data-testid="place-order"
|
||||
>
|
||||
<Button variant={variant} fill type="submit" data-testid="place-order">
|
||||
{t('Place order')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -33,24 +33,35 @@ export const DealTicketFeeDetails = (props: FeeDetails) => {
|
||||
const details = getFeeDetailsValues(props);
|
||||
return (
|
||||
<div>
|
||||
{details.map(({ label, value, labelDescription, symbol, indent }) => (
|
||||
<div
|
||||
key={typeof label === 'string' ? label : 'value-dropdown'}
|
||||
className={classnames(
|
||||
'text-xs mt-2 flex justify-between items-center gap-4 flex-wrap',
|
||||
{ 'ml-2': indent }
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<Tooltip description={labelDescription}>
|
||||
<div>{label}</div>
|
||||
{details.map(
|
||||
({
|
||||
label,
|
||||
value,
|
||||
labelDescription,
|
||||
symbol,
|
||||
indent,
|
||||
formattedValue,
|
||||
}) => (
|
||||
<div
|
||||
key={typeof label === 'string' ? label : 'value-dropdown'}
|
||||
className={classnames(
|
||||
'text-xs mt-2 flex justify-between items-center gap-4 flex-wrap',
|
||||
{ 'ml-2': indent }
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<Tooltip description={labelDescription}>
|
||||
<div>{label}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Tooltip description={`${value ?? '-'} ${symbol || ''}`}>
|
||||
<div className="text-neutral-500 dark:text-neutral-300">{`${
|
||||
formattedValue ?? '-'
|
||||
} ${symbol || ''}`}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="text-neutral-500 dark:text-neutral-300">{`${
|
||||
value ?? '-'
|
||||
} ${symbol || ''}`}</div>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -42,6 +42,9 @@ describe('DealTicket', () => {
|
||||
it('should display ticket defaults', () => {
|
||||
const { container } = render(generateJsx());
|
||||
|
||||
// place order button should always be enabled
|
||||
expect(screen.getByTestId('place-order')).toBeEnabled();
|
||||
|
||||
// Assert defaults are used
|
||||
expect(
|
||||
screen.getByTestId(`order-type-${Schema.OrderType.TYPE_MARKET}`)
|
||||
|
||||
@@ -217,6 +217,8 @@ export const DealTicket = ({
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// No error found above clear the error in case it was active on a previous render
|
||||
clearErrors('summary');
|
||||
}, [
|
||||
marketState,
|
||||
@@ -480,7 +482,6 @@ export const DealTicket = ({
|
||||
onClickCollateral={onClickCollateral}
|
||||
/>
|
||||
<DealTicketButton
|
||||
disabled={Object.keys(errors).length >= 1 || isReadOnly}
|
||||
variant={
|
||||
order.side === Schema.Side.SIDE_BUY ? 'ternary' : 'secondary'
|
||||
}
|
||||
@@ -562,7 +563,7 @@ const SummaryMessage = memo(
|
||||
text: t('Connect wallet'),
|
||||
action: openVegaWalletDialog,
|
||||
dataTestId: 'order-connect-wallet',
|
||||
size: 'md',
|
||||
size: 'sm',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { formatRange, formatValue } from './use-fee-deal-ticket-details';
|
||||
|
||||
describe('useFeeDealTicketDetails', () => {
|
||||
it.each([
|
||||
{ v: 123000, d: 5, o: '1.23' },
|
||||
{ v: 123000, d: 3, o: '123.00' },
|
||||
{ v: 123000, d: 1, o: '12,300.0' },
|
||||
{ v: 123001000, d: 2, o: '1,230,010.00' },
|
||||
{ v: 123001, d: 2, o: '1,230.01' },
|
||||
{
|
||||
v: '123456789123456789',
|
||||
d: 10,
|
||||
o: '12,345,678.91234568',
|
||||
},
|
||||
])('formats values correctly', ({ v, d, o }) => {
|
||||
expect(formatValue(v, d)).toStrictEqual(o);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ v: 123000, d: 5, o: '1.23', q: '0.1' },
|
||||
{ v: 123000, d: 3, o: '123.00', q: '0.1' },
|
||||
{ v: 123000, d: 1, o: '12,300.00', q: '0.1' },
|
||||
{ v: 123001000, d: 2, o: '1,230,010.00', q: '0.1' },
|
||||
{ v: 123001, d: 2, o: '1,230', q: '100' },
|
||||
{ v: 123001, d: 2, o: '1,230.01', q: '0.1' },
|
||||
{
|
||||
v: '123456789123456789',
|
||||
d: 10,
|
||||
o: '12,345,678.9123457',
|
||||
q: '0.00003846',
|
||||
},
|
||||
])(
|
||||
'formats with formatValue with quantum given number correctly',
|
||||
({ v, d, o, q }) => {
|
||||
expect(formatValue(v.toString(), d, q)).toStrictEqual(o);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
{ min: 123000, max: 12300011111, d: 5, o: '1.23 - 123,000.111', q: '0.1' },
|
||||
{
|
||||
min: 123000,
|
||||
max: 12300011111,
|
||||
d: 3,
|
||||
o: '123.00 - 12,300,011.111',
|
||||
q: '0.1',
|
||||
},
|
||||
{
|
||||
min: 123000,
|
||||
max: 12300011111,
|
||||
d: 1,
|
||||
o: '12,300.00 - 1,230,001,111.10',
|
||||
q: '0.1',
|
||||
},
|
||||
{
|
||||
min: 123001000,
|
||||
max: 12300011111,
|
||||
d: 2,
|
||||
o: '1,230,010 - 123,000,111',
|
||||
q: '100',
|
||||
},
|
||||
])(
|
||||
'formats with formatValue with quantum given number correctly',
|
||||
({ min, max, d, o, q }) => {
|
||||
expect(formatRange(min, max, d, q)).toStrictEqual(o);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -1,5 +1,9 @@
|
||||
import { FeesBreakdown } from '@vegaprotocol/markets';
|
||||
import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
addDecimalsFormatNumberQuantum,
|
||||
isNumeric,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { Market } from '@vegaprotocol/markets';
|
||||
@@ -52,21 +56,25 @@ export interface FeeDetails {
|
||||
}
|
||||
|
||||
const emptyValue = '-';
|
||||
const formatValue = (
|
||||
|
||||
export const formatValue = (
|
||||
value: string | number | null | undefined,
|
||||
formatDecimals: number
|
||||
formatDecimals: number,
|
||||
quantum?: string
|
||||
): string => {
|
||||
return isNumeric(value)
|
||||
? addDecimalsFormatNumber(value, formatDecimals)
|
||||
: emptyValue;
|
||||
if (!isNumeric(value)) return emptyValue;
|
||||
if (!quantum) return addDecimalsFormatNumber(value, formatDecimals);
|
||||
return addDecimalsFormatNumberQuantum(value, formatDecimals, quantum);
|
||||
};
|
||||
const formatRange = (
|
||||
|
||||
export const formatRange = (
|
||||
min: string | number | null | undefined,
|
||||
max: string | number | null | undefined,
|
||||
formatDecimals: number
|
||||
formatDecimals: number,
|
||||
quantum?: string
|
||||
) => {
|
||||
const minFormatted = formatValue(min, formatDecimals);
|
||||
const maxFormatted = formatValue(max, formatDecimals);
|
||||
const minFormatted = formatValue(min, formatDecimals, quantum);
|
||||
const maxFormatted = formatValue(max, formatDecimals, quantum);
|
||||
if (minFormatted !== maxFormatted) {
|
||||
return `${minFormatted} - ${maxFormatted}`;
|
||||
}
|
||||
@@ -93,9 +101,12 @@ export const getFeeDetailsValues = ({
|
||||
BigInt(generalAccountBalance || '0') + BigInt(marginAccountBalance || '0');
|
||||
const assetDecimals =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.decimals;
|
||||
const quantum =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.quantum;
|
||||
const details: {
|
||||
label: string;
|
||||
value?: string | null;
|
||||
formattedValue?: string | null;
|
||||
symbol: string;
|
||||
indent?: boolean;
|
||||
labelDescription?: React.ReactNode;
|
||||
@@ -103,6 +114,7 @@ export const getFeeDetailsValues = ({
|
||||
{
|
||||
label: t('Notional'),
|
||||
value: formatValue(notionalSize, assetDecimals),
|
||||
formattedValue: formatValue(notionalSize, assetDecimals, quantum),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: NOTIONAL_SIZE_TOOLTIP_TEXT(assetSymbol),
|
||||
},
|
||||
@@ -111,6 +123,9 @@ export const getFeeDetailsValues = ({
|
||||
value:
|
||||
feeEstimate?.totalFeeAmount &&
|
||||
`~${formatValue(feeEstimate?.totalFeeAmount, assetDecimals)}`,
|
||||
formattedValue:
|
||||
feeEstimate?.totalFeeAmount &&
|
||||
`~${formatValue(feeEstimate?.totalFeeAmount, assetDecimals, quantum)}`,
|
||||
labelDescription: (
|
||||
<>
|
||||
<span>
|
||||
@@ -154,6 +169,12 @@ export const getFeeDetailsValues = ({
|
||||
}
|
||||
details.push({
|
||||
label: t('Margin required'),
|
||||
formattedValue: formatRange(
|
||||
marginRequiredBestCase,
|
||||
marginRequiredWorstCase,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
value: formatRange(
|
||||
marginRequiredBestCase,
|
||||
marginRequiredWorstCase,
|
||||
@@ -172,12 +193,13 @@ export const getFeeDetailsValues = ({
|
||||
details.push({
|
||||
indent: true,
|
||||
label: t('Total margin available'),
|
||||
formattedValue: formatValue(totalMarginAvailable, assetDecimals, quantum),
|
||||
value: formatValue(totalMarginAvailable, assetDecimals),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: TOTAL_MARGIN_AVAILABLE(
|
||||
formatValue(generalAccountBalance, assetDecimals),
|
||||
formatValue(marginAccountBalance, assetDecimals),
|
||||
formatValue(currentMaintenanceMargin, assetDecimals),
|
||||
formatValue(generalAccountBalance, assetDecimals, quantum),
|
||||
formatValue(marginAccountBalance, assetDecimals, quantum),
|
||||
formatValue(currentMaintenanceMargin, assetDecimals, quantum),
|
||||
assetSymbol
|
||||
),
|
||||
});
|
||||
@@ -203,6 +225,16 @@ export const getFeeDetailsValues = ({
|
||||
: '0',
|
||||
assetDecimals
|
||||
),
|
||||
formattedValue: formatRange(
|
||||
deductionFromCollateralBestCase > 0
|
||||
? deductionFromCollateralBestCase.toString()
|
||||
: '0',
|
||||
deductionFromCollateralWorstCase > 0
|
||||
? deductionFromCollateralWorstCase.toString()
|
||||
: '0',
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT(assetSymbol),
|
||||
});
|
||||
@@ -214,6 +246,12 @@ export const getFeeDetailsValues = ({
|
||||
marginEstimate?.worstCase.initialLevel,
|
||||
assetDecimals
|
||||
),
|
||||
formattedValue: formatRange(
|
||||
marginEstimate?.bestCase.initialLevel,
|
||||
marginEstimate?.worstCase.initialLevel,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: EST_TOTAL_MARGIN_TOOLTIP_TEXT,
|
||||
});
|
||||
@@ -223,9 +261,11 @@ export const getFeeDetailsValues = ({
|
||||
value: formatValue(marginAccountBalance, assetDecimals),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: MARGIN_ACCOUNT_TOOLTIP_TEXT,
|
||||
formattedValue: formatValue(marginAccountBalance, assetDecimals, quantum),
|
||||
});
|
||||
|
||||
let liquidationPriceEstimate = emptyValue;
|
||||
let liquidationPriceEstimateFormatted;
|
||||
|
||||
if (liquidationEstimate) {
|
||||
const liquidationEstimateBestCaseIncludingBuyOrders = BigInt(
|
||||
@@ -262,11 +302,24 @@ export const getFeeDetailsValues = ({
|
||||
).toString(),
|
||||
assetDecimals
|
||||
);
|
||||
liquidationPriceEstimateFormatted = formatRange(
|
||||
(liquidationEstimateBestCase < liquidationEstimateWorstCase
|
||||
? liquidationEstimateBestCase
|
||||
: liquidationEstimateWorstCase
|
||||
).toString(),
|
||||
(liquidationEstimateBestCase > liquidationEstimateWorstCase
|
||||
? liquidationEstimateBestCase
|
||||
: liquidationEstimateWorstCase
|
||||
).toString(),
|
||||
assetDecimals,
|
||||
quantum
|
||||
);
|
||||
}
|
||||
|
||||
details.push({
|
||||
label: t('Liquidation price estimate'),
|
||||
value: liquidationPriceEstimate,
|
||||
formattedValue: liquidationPriceEstimateFormatted,
|
||||
symbol: assetSymbol,
|
||||
labelDescription: LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT,
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
|
||||
symbol: 'tDAI',
|
||||
name: 'tDAI',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
|
||||
|
||||
export const validateMarketState = (state: Schema.MarketState) => {
|
||||
export const validateMarketState = (state: MarketState) => {
|
||||
if (
|
||||
[
|
||||
Schema.MarketState.STATE_SETTLED,
|
||||
Schema.MarketState.STATE_REJECTED,
|
||||
Schema.MarketState.STATE_TRADING_TERMINATED,
|
||||
Schema.MarketState.STATE_CANCELLED,
|
||||
Schema.MarketState.STATE_CLOSED,
|
||||
MarketState.STATE_SETTLED,
|
||||
MarketState.STATE_REJECTED,
|
||||
MarketState.STATE_TRADING_TERMINATED,
|
||||
MarketState.STATE_CANCELLED,
|
||||
MarketState.STATE_CLOSED,
|
||||
].includes(state)
|
||||
) {
|
||||
return t(
|
||||
@@ -16,7 +16,7 @@ export const validateMarketState = (state: Schema.MarketState) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (state === Schema.MarketState.STATE_PROPOSED) {
|
||||
if (state === MarketState.STATE_PROPOSED) {
|
||||
return t(
|
||||
`This market is ${marketTranslations(
|
||||
state
|
||||
@@ -27,11 +27,11 @@ export const validateMarketState = (state: Schema.MarketState) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const marketTranslations = (marketState: Schema.MarketState) => {
|
||||
const marketTranslations = (marketState: MarketState) => {
|
||||
switch (marketState) {
|
||||
case Schema.MarketState.STATE_TRADING_TERMINATED:
|
||||
case MarketState.STATE_TRADING_TERMINATED:
|
||||
return t('terminated');
|
||||
default:
|
||||
return t(Schema.MarketStateMapping[marketState]).toLowerCase();
|
||||
return t(MarketStateMapping[marketState]).toLowerCase();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||
|
||||
export const validateMarketTradingMode = (
|
||||
marketTradingMode: Schema.MarketTradingMode
|
||||
marketTradingMode: MarketTradingMode
|
||||
) => {
|
||||
if (marketTradingMode === Schema.MarketTradingMode.TRADING_MODE_NO_TRADING) {
|
||||
if (marketTradingMode === MarketTradingMode.TRADING_MODE_NO_TRADING) {
|
||||
return t('Trading terminated');
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
useIsExemptDepositor,
|
||||
} from './use-get-deposit-maximum';
|
||||
import { useGetDepositedAmount } from './use-get-deposited-amount';
|
||||
import { isAssetTypeERC20, localLoggerFactory } from '@vegaprotocol/utils';
|
||||
import { isAssetTypeERC20 } from '@vegaprotocol/utils';
|
||||
import { localLoggerFactory } from '@vegaprotocol/logger';
|
||||
import { useAccountBalance } from '@vegaprotocol/accounts';
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useCallback } from 'react';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
import { addDecimal, localLoggerFactory } from '@vegaprotocol/utils';
|
||||
import { addDecimal } from '@vegaprotocol/utils';
|
||||
import { localLoggerFactory } from '@vegaprotocol/logger';
|
||||
|
||||
export const useGetAllowance = (
|
||||
contract: Token | null,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useCallback } from 'react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
import { addDecimal, localLoggerFactory } from '@vegaprotocol/utils';
|
||||
import { addDecimal } from '@vegaprotocol/utils';
|
||||
import { localLoggerFactory } from '@vegaprotocol/logger';
|
||||
import type { CollateralBridge } from '@vegaprotocol/smart-contracts';
|
||||
|
||||
export const useGetDepositMaximum = (
|
||||
|
||||
@@ -3,7 +3,8 @@ import { ethers } from 'ethers';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
import { addDecimal, localLoggerFactory } from '@vegaprotocol/utils';
|
||||
import { addDecimal } from '@vegaprotocol/utils';
|
||||
import { localLoggerFactory } from '@vegaprotocol/logger';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
|
||||
export const useGetDepositedAmount = (asset: Asset | undefined) => {
|
||||
|
||||
@@ -72,6 +72,7 @@ export const DocsLinks = VEGA_DOCS_URL
|
||||
POSITION_RESOLUTION: `${VEGA_DOCS_URL}/concepts/trading-on-vega/market-protections#position-resolution`,
|
||||
LIQUIDITY: `${VEGA_DOCS_URL}/concepts/liquidity/provision`,
|
||||
WITHDRAWAL_LIMITS: `${VEGA_DOCS_URL}/concepts/assets/deposits-withdrawals#withdrawal-limits`,
|
||||
VALIDATOR_SCORES_REWARDS: `${VEGA_DOCS_URL}/concepts/vega-chain/validator-scores-and-rewards`,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { localLoggerFactory } from '@vegaprotocol/utils';
|
||||
import { localLoggerFactory } from '@vegaprotocol/logger';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import z from 'zod';
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import compact from 'lodash/compact';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { useRef } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { FillsTable } from './fills-table';
|
||||
import type { BodyScrollEvent, BodyScrollEndEvent } from 'ag-grid-community';
|
||||
import { useFillsList } from './use-fills-list';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
||||
|
||||
@@ -22,80 +21,30 @@ export const FillsManager = ({
|
||||
}: FillsManagerProps) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const scrolledToTop = useRef(true);
|
||||
const {
|
||||
data,
|
||||
error,
|
||||
loading,
|
||||
addNewRows,
|
||||
getRows,
|
||||
reload,
|
||||
makeBottomPlaceholders,
|
||||
} = useFillsList({
|
||||
const { data, error } = useFillsList({
|
||||
partyId,
|
||||
marketId,
|
||||
gridRef,
|
||||
scrolledToTop,
|
||||
});
|
||||
|
||||
const checkBottomPlaceholder = useCallback(() => {
|
||||
const rowCont = gridRef.current?.api?.getModel().getRowCount() ?? 0;
|
||||
const lastRowIndex = gridRef.current?.api?.getLastDisplayedRow();
|
||||
if (lastRowIndex && rowCont - 1 === lastRowIndex) {
|
||||
const lastrow = gridRef.current?.api.getDisplayedRowAtIndex(lastRowIndex);
|
||||
lastrow?.setRowHeight(50);
|
||||
makeBottomPlaceholders(lastrow?.data);
|
||||
gridRef.current?.api.onRowHeightChanged();
|
||||
gridRef.current?.api.refreshInfiniteCache();
|
||||
}
|
||||
}, [makeBottomPlaceholders]);
|
||||
const bottomPlaceholderProps = useBottomPlaceholder({
|
||||
gridRef,
|
||||
});
|
||||
|
||||
const onBodyScrollEnd = useCallback(
|
||||
(event: BodyScrollEndEvent) => {
|
||||
if (event.top === 0) {
|
||||
addNewRows();
|
||||
}
|
||||
checkBottomPlaceholder();
|
||||
},
|
||||
[addNewRows, checkBottomPlaceholder]
|
||||
);
|
||||
|
||||
const onBodyScroll = useCallback((event: BodyScrollEvent) => {
|
||||
scrolledToTop.current = event.top <= 0;
|
||||
}, []);
|
||||
|
||||
const { isFullWidthRow, fullWidthCellRenderer, rowClassRules, getRowHeight } =
|
||||
useBottomPlaceholder({
|
||||
gridRef,
|
||||
});
|
||||
const fills = compact(data).map((e) => e.node);
|
||||
|
||||
return (
|
||||
<div className="h-full relative">
|
||||
<FillsTable
|
||||
ref={gridRef}
|
||||
rowData={fills}
|
||||
partyId={partyId}
|
||||
rowModelType="infinite"
|
||||
datasource={{ getRows }}
|
||||
onBodyScrollEnd={onBodyScrollEnd}
|
||||
onBodyScroll={onBodyScroll}
|
||||
onMarketClick={onMarketClick}
|
||||
suppressLoadingOverlay
|
||||
suppressNoRowsOverlay
|
||||
isFullWidthRow={isFullWidthRow}
|
||||
fullWidthCellRenderer={fullWidthCellRenderer}
|
||||
rowClassRules={rowClassRules}
|
||||
getRowHeight={getRowHeight}
|
||||
storeKey={storeKey}
|
||||
{...bottomPlaceholderProps}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No fills')}
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={data}
|
||||
noDataMessage={t('No fills')}
|
||||
noDataCondition={(data) => !(data && data.length)}
|
||||
reload={reload}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -75,6 +75,7 @@ export const generateFill = (override?: PartialDeep<Trade>) => {
|
||||
name: 'assset-id',
|
||||
symbol: 'SYM',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
},
|
||||
quoteName: '',
|
||||
dataSourceSpecForTradingTermination: {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import type { FilterChangedEvent } from 'ag-grid-community';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { subDays, formatRFC3339 } from 'date-fns';
|
||||
import type { AggregatedLedgerEntriesNode } from './ledger-entries-data-provider';
|
||||
import { useLedgerEntriesDataProvider } from './ledger-entries-data-provider';
|
||||
import { LedgerTable } from './ledger-table';
|
||||
import type * as Types from '@vegaprotocol/types';
|
||||
@@ -27,9 +25,8 @@ const defaultFilter = {
|
||||
export const LedgerManager = ({ partyId }: { partyId: string }) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const [filter, setFilter] = useState<Filter>(defaultFilter);
|
||||
const [dataCount, setDataCount] = useState(0);
|
||||
|
||||
const { data, error, loading, reload } = useLedgerEntriesDataProvider({
|
||||
const { data, error } = useLedgerEntriesDataProvider({
|
||||
partyId,
|
||||
filter,
|
||||
gridRef,
|
||||
@@ -39,16 +36,9 @@ export const LedgerManager = ({ partyId }: { partyId: string }) => {
|
||||
const updatedFilter = { ...defaultFilter, ...event.api.getFilterModel() };
|
||||
setFilter(updatedFilter);
|
||||
}, []);
|
||||
const extractNodesDecorator = useCallback(
|
||||
(data: AggregatedLedgerEntriesNode[] | null, loading: boolean) =>
|
||||
data && !loading ? data.map((item) => item.node) : null,
|
||||
[]
|
||||
);
|
||||
|
||||
const extractedData = extractNodesDecorator(data, loading);
|
||||
useEffect(() => {
|
||||
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
||||
}, [extractedData]);
|
||||
// allow passing undefined to grid so that loading state is shown
|
||||
const extractedData = data?.map((item) => item.node);
|
||||
|
||||
return (
|
||||
<div className="h-full relative">
|
||||
@@ -56,20 +46,11 @@ export const LedgerManager = ({ partyId }: { partyId: string }) => {
|
||||
ref={gridRef}
|
||||
rowData={extractedData}
|
||||
onFilterChanged={onFilterChanged}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No entries')}
|
||||
/>
|
||||
{extractedData && (
|
||||
<LedgerExportLink entries={extractedData} partyId={partyId} />
|
||||
)}
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={data}
|
||||
noDataMessage={t('No entries')}
|
||||
noDataCondition={() => !dataCount}
|
||||
reload={reload}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nrwl/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# logger
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test logger` to execute the unit tests via [Jest](https://jestjs.io).
|
||||
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'logger',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/logger',
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "@vegaprotocol/logger",
|
||||
"version": "0.0.1",
|
||||
"type": "commonjs"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/logger/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/js:tsc",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"format": ["esm", "cjs"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/logger",
|
||||
"main": "libs/logger/src/index.ts",
|
||||
"tsConfig": "libs/logger/tsconfig.lib.json",
|
||||
"assets": ["libs/logger/*.md"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["libs/logger/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/libs/logger"],
|
||||
"options": {
|
||||
"jestConfig": "libs/logger/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user