Compare commits

...
23 changed files with 447 additions and 112 deletions
+3
View File
@@ -1,5 +1,8 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Auto-format all files
yarn nx format:write
# Lint all staged files
yarn lint-staged
+3 -3
View File
@@ -1,8 +1,8 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Lint all staged files
yarn nx format:check
# Lint all staged files - this brings more value as pre-commit
# yarn nx format:check
# Test all projects with changes
yarn nx affected -t test --exclude trading
# yarn nx affected -t test --exclude trading
+2 -3
View File
@@ -22,10 +22,9 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=false
NX_REFERRALS=true
# NX_DISABLE_CLOSE_POSITION=false
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_DISABLE_CLOSE_POSITION=true
-1
View File
@@ -23,7 +23,6 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=true
@@ -18,13 +18,35 @@ import { Routes } from '../../lib/links';
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
import { Statistics, useStats } from './referral-statistics';
import { useReferralProgram } from './hooks/use-referral-program';
import { useT } from '../../lib/use-t';
import { ns, useT } from '../../lib/use-t';
import { useFundsAvailable } from './hooks/use-funds-available';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { QUSDTooltip } from './qusd-tooltip';
import { Trans } from 'react-i18next';
const RELOAD_DELAY = 3000;
const SPAM_PROTECTION_ERR = 'SPAM_PROTECTION_ERR';
const SpamProtectionErr = ({
requiredFunds,
}: {
requiredFunds?: string | number | bigint;
}) => {
if (!requiredFunds) return null;
// eslint-disable-next-line react/jsx-no-undef
return (
<Trans
defaults="To protect the network from spam, you must have at least {{requiredFunds}} <0>qUSD</0> of any asset on the network to proceed."
values={{
requiredFunds,
}}
components={[<QUSDTooltip key="qusd" />]}
ns={ns}
/>
);
};
const validateCode = (value: string, t: ReturnType<typeof useT>) => {
const number = +`0x${value}`;
if (!value || value.length !== 64) {
@@ -76,7 +98,6 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
setValue,
setError,
watch,
clearErrors,
} = useForm();
const [params] = useSearchParams();
@@ -90,30 +111,11 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
*/
const validateFundsAvailable = useCallback(() => {
if (requiredFunds && !isEligible) {
const err = t(
'Require minimum of {{requiredFunds}} to join a referral set to protect the network from spam.',
{ replace: { requiredFunds } }
);
const err = SPAM_PROTECTION_ERR;
return err;
}
return true;
}, [isEligible, requiredFunds, t]);
useEffect(() => {
if (codeField) {
const err = validateFundsAvailable();
if (err !== true) {
setStatus('no-funds');
setError('code', {
type: 'required',
message: err,
});
} else {
setStatus(null);
clearErrors('code');
}
}
}, [clearErrors, codeField, isEligible, setError, validateFundsAvailable]);
}, [isEligible, requiredFunds]);
/**
* Validates the set a user tries to apply to.
@@ -138,6 +140,15 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
if (code) setValue('code', code);
}, [params, setValue]);
useEffect(() => {
const err = validateFundsAvailable();
if (err !== true) {
setStatus('no-funds');
} else {
setStatus(null);
}
}, [isEligible, validateFundsAvailable]);
const onSubmit = ({ code }: FieldValues) => {
if (isReadOnly || !pubKey || !code || code.length === 0) {
return;
@@ -321,10 +332,26 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
</label>
<RainbowButton variant="border" {...getButtonProps()} />
</form>
{errors.code && (
<InputError className="overflow-auto break-words">
{errors.code.message?.toString()}
{status === 'no-funds' ? (
<InputError intent="warning" className="overflow-auto break-words">
<span>
<SpamProtectionErr requiredFunds={requiredFunds?.toString()} />
</span>
</InputError>
) : (
errors.code && (
<InputError intent="warning" className="overflow-auto break-words">
{errors.code.message === SPAM_PROTECTION_ERR ? (
<span>
<SpamProtectionErr
requiredFunds={requiredFunds?.toString()}
/>
</span>
) : (
errors.code.message?.toString()
)}
</InputError>
)
)}
</div>
{validateCode(codeField, t) === true && previewLoading && !previewData ? (
@@ -1,7 +1,7 @@
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useFundsAvailableQuery } from './__generated__/FundsAvailable';
import compact from 'lodash/compact';
import sum from 'lodash/sum';
import BigNumber from 'bignumber.js';
/**
* Gets the funds for given public key and required min for
@@ -24,14 +24,16 @@ export const useFundsAvailable = (pubKey?: string) => {
? compact(data.party?.accountsConnection?.edges?.map((e) => e?.node))
: undefined;
const requiredFunds = data
? BigInt(data.networkParameter?.value || '0')
? BigNumber(data.networkParameter?.value || '0')
: undefined;
const sumOfFunds = sum(
fundsAvailable?.filter((fa) => fa.balance).map((fa) => BigInt(fa.balance))
);
const sumOfFunds =
fundsAvailable
?.filter((fa) => fa.balance)
.reduce((sum, fa) => sum.plus(BigNumber(fa.balance)), BigNumber(0)) ||
BigNumber(0);
if (requiredFunds && sumOfFunds >= requiredFunds) {
if (requiredFunds && sumOfFunds.isGreaterThanOrEqualTo(requiredFunds)) {
stopPolling();
}
@@ -41,6 +43,6 @@ export const useFundsAvailable = (pubKey?: string) => {
isEligible:
fundsAvailable != null &&
requiredFunds != null &&
sumOfFunds >= requiredFunds,
sumOfFunds.isGreaterThanOrEqualTo(requiredFunds),
};
};
@@ -15,7 +15,7 @@ export const LandingBanner = () => {
</div>
<div className="pt-20 sm:w-[50%]">
<h1 className="text-6xl font-alpha calt mb-10">
{t('Vega community referral program')}
{t('Vega community referrals')}
</h1>
<p className="text-lg mb-1">
{t(
@@ -0,0 +1,28 @@
import { DocsLinks } from '@vegaprotocol/environment';
import { ExternalLink, Tooltip } from '@vegaprotocol/ui-toolkit';
import { useT } from '../../lib/use-t';
export const QUSDTooltip = () => {
const t = useT();
return (
<Tooltip
description={
<>
<p className="mb-1">
{t(
'qUSD provides a rough USD equivalent of balances across all assets using the value of "Quantum" for that asset'
)}
</p>
{DocsLinks && (
<ExternalLink href={DocsLinks.QUANTUM}>
{t('Find out more')}
</ExternalLink>
)}
</>
}
underline={true}
>
<span>{t('qUSD')}</span>
</Tooltip>
);
};
@@ -4,8 +4,6 @@ import {
VegaIcon,
VegaIconNames,
truncateMiddle,
ExternalLink,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
@@ -28,10 +26,10 @@ import sortBy from 'lodash/sortBy';
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useCurrentEpochInfoQuery } from './hooks/__generated__/Epoch';
import BigNumber from 'bignumber.js';
import { DocsLinks } from '@vegaprotocol/environment';
import { useT, ns } from '../../lib/use-t';
import { Trans } from 'react-i18next';
import { ApplyCodeForm, ApplyCodeFormContainer } from './apply-code-form';
import { QUSDTooltip } from './qusd-tooltip';
export const ReferralStatistics = () => {
const { pubKey } = useVegaWallet();
@@ -121,7 +119,7 @@ export const useStats = ({
: 1;
const finalCommissionValue = isNaN(multiplier)
? baseCommissionValue
: multiplier * baseCommissionValue;
: new BigNumber(multiplier).times(baseCommissionValue).toNumber();
const discountFactorValue = refereeStats?.discountFactor
? Number(refereeStats.discountFactor)
@@ -214,6 +212,7 @@ export const Statistics = ({
).toString(),
}
)}
testId="base-commission-rate"
overrideWithNoProgram={!details}
>
{baseCommissionValue * 100}%
@@ -223,6 +222,7 @@ export const Statistics = ({
const stakingMultiplierTile = (
<StatTile
title={t('Staking multiplier')}
testId="staking-multiplier"
description={
<span
className={classNames({
@@ -242,24 +242,31 @@ export const Statistics = ({
{multiplier || t('None')}
</StatTile>
);
const baseCommissionFormatted = BigNumber(baseCommissionValue)
.times(100)
.toString();
const finalCommissionFormatted = new BigNumber(finalCommissionValue)
.times(100)
.toString();
const finalCommissionTile = (
<StatTile
title={t('Final commission rate')}
description={
!isNaN(multiplier)
? `(${baseCommissionValue * 100}% ⨉ ${multiplier} = ${
finalCommissionValue * 100
}%)`
? `(${baseCommissionFormatted}% ⨉ ${multiplier} = ${finalCommissionFormatted}%)`
: undefined
}
testId="final-commission-rate"
overrideWithNoProgram={!details}
>
{finalCommissionValue * 100}%
{finalCommissionFormatted}%
</StatTile>
);
const numberOfTradersValue = data.referees.length;
const numberOfTradersTile = (
<StatTile title={t('Number of traders')}>{numberOfTradersValue}</StatTile>
<StatTile title={t('Number of traders')} testId="number-of-traders">
{numberOfTradersValue}
</StatTile>
);
const codeTile = (
@@ -274,6 +281,7 @@ export const Statistics = ({
title={t('myVolume', 'My volume (last {{count}} epochs)', {
count: details?.windowLength || DEFAULT_AGGREGATION_DAYS,
})}
testId="my-volume"
overrideWithNoProgram={!details}
>
{compactNumFormat.format(referrerVolumeValue)}
@@ -289,6 +297,7 @@ export const Statistics = ({
count: details?.windowLength || DEFAULT_AGGREGATION_DAYS,
})}
description={<QUSDTooltip />}
testId="total-commission"
>
{getNumberFormat(0).format(Number(totalCommissionValue))}
</StatTile>
@@ -314,6 +323,7 @@ export const Statistics = ({
const currentBenefitTierTile = (
<StatTile
title={t('Current tier')}
testId="current-tier"
description={
nextBenefitTierValue?.tier
? t('(Next tier: {{nextTier}})', {
@@ -329,7 +339,11 @@ export const Statistics = ({
</StatTile>
);
const discountFactorTile = (
<StatTile title={t('Discount')} overrideWithNoProgram={!details}>
<StatTile
title={t('Discount')}
testId="discount"
overrideWithNoProgram={!details}
>
{isApplyCodePreview && benefitTiers.length >= 1
? benefitTiers[0].discountFactor * 100
: discountFactorValue * 100}
@@ -345,23 +359,34 @@ export const Statistics = ({
count: details?.windowLength,
}
)}
testId="combined-volume"
overrideWithNoProgram={!details}
>
{compactNumFormat.format(runningVolumeValue)}
</StatTile>
);
const epochsTile = (
<StatTile title={t('Epochs in set')}>{epochsValue}</StatTile>
<StatTile title={t('Epochs in set')} testId="epochs-in-set">
{epochsValue}
</StatTile>
);
const nextTierVolumeTile = (
<StatTile title={t('Volume to next tier')} overrideWithNoProgram={!details}>
<StatTile
title={t('Volume to next tier')}
testId="vol-to-next-tier"
overrideWithNoProgram={!details}
>
{nextBenefitTierVolumeValue <= 0
? '0'
: compactNumFormat.format(nextBenefitTierVolumeValue)}
</StatTile>
);
const nextTierEpochsTile = (
<StatTile title={t('Epochs to next tier')} overrideWithNoProgram={!details}>
<StatTile
title={t('Epochs to next tier')}
testId="epochs-to-next-tier"
overrideWithNoProgram={!details}
>
{nextBenefitTierEpochsValue <= 0 ? '0' : nextBenefitTierEpochsValue}
</StatTile>
);
@@ -515,28 +540,3 @@ export const RefereesTable = ({
</>
);
};
export const QUSDTooltip = () => {
const t = useT();
return (
<Tooltip
description={
<>
<p className="mb-1">
{t(
'qUSD provides a rough USD equivalent of balances across all assets using the value of "Quantum" for that asset'
)}
</p>
{DocsLinks && (
<ExternalLink href={DocsLinks.QUANTUM}>
{t('Find out more')}
</ExternalLink>
)}
</>
}
underline={true}
>
<span>{t('qUSD')}</span>
</Tooltip>
);
};
@@ -159,11 +159,11 @@ export const TiersContainer = () => {
return (
<>
<h2 className="text-3xl mt-10">{t('Current Program Details')}</h2>
<h2 className="text-3xl mt-10">{t('Current program details')}</h2>
{details?.id && (
<p>
<Trans
defaults="As a result of <0>{{proposal}}</0> the program below is currently active on the Vega network."
defaults="As a result of governance proposal <0>{{proposal}}</0> the program below is currently active on the Vega network."
values={{ proposal: truncateMiddle(details.id) }}
components={[
<ExternalLink
+9 -2
View File
@@ -32,6 +32,7 @@ export const Tile = ({
type StatTileProps = {
title: string;
testId?: string;
description?: ReactNode;
children?: ReactNode;
overrideWithNoProgram?: boolean;
@@ -40,6 +41,7 @@ export const StatTile = ({
title,
description,
children,
testId,
overrideWithNoProgram = false,
}: StatTileProps) => {
if (overrideWithNoProgram) {
@@ -47,10 +49,15 @@ export const StatTile = ({
}
return (
<Tile>
<h3 className="mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100 calt">
<h3
data-testid={testId}
className="mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100 calt"
>
{title}
</h3>
<div className="text-5xl text-left">{children}</div>
<div data-testid={`${testId}-value`} className="text-5xl text-left">
{children}
</div>
{description && (
<div className="text-sm text-left text-vega-clight-100 dark:text-vega-cdark-100">
{description}
@@ -10,6 +10,7 @@ import { positionsDataProvider } from '@vegaprotocol/positions';
import { useGlobalStore } from '../../stores';
const ONBOARDING_STORAGE_KEY = 'vega_onboarding';
export const useOnboardingStore = create<{
dialogOpen: boolean;
walletDialogOpen: boolean;
@@ -20,7 +21,7 @@ export const useOnboardingStore = create<{
}>()(
persist(
(set) => ({
dialogOpen: true,
dialogOpen: false,
walletDialogOpen: false,
dismissed: false,
dismiss: () => set({ dismissed: true }),
@@ -1,18 +1,29 @@
import { useEffect } from 'react';
import { matchPath, useLocation } from 'react-router-dom';
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/environment';
import { WelcomeDialogContent } from './welcome-dialog-content';
import { useOnboardingStore } from './use-get-onboarding-step';
import { VegaConnectDialog } from '@vegaprotocol/wallet';
import { Connectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
import { useT } from '../../lib/use-t';
import { Routes } from '../../lib/links';
import { RiskMessage } from './risk-message';
import { WelcomeDialogContent } from './welcome-dialog-content';
import { useOnboardingStore } from './use-get-onboarding-step';
import { ensureSuffix } from '@vegaprotocol/utils';
/**
* A list of paths on which the welcome dialog should be omitted.
*/
const OMIT_ON_LIST = [ensureSuffix(Routes.REFERRALS, '/*')];
export const WelcomeDialog = () => {
const { pathname } = useLocation();
const t = useT();
const { VEGA_ENV } = useEnvironment();
const dismissed = useOnboardingStore((store) => store.dismissed);
const dialogOpen = useOnboardingStore((store) => store.dialogOpen);
const dismiss = useOnboardingStore((store) => store.dismiss);
const setDialogOpen = useOnboardingStore((store) => store.setDialogOpen);
const walletDialogOpen = useOnboardingStore(
(store) => store.walletDialogOpen
);
@@ -20,6 +31,16 @@ export const WelcomeDialog = () => {
(store) => store.setWalletDialogOpen
);
useEffect(() => {
const shouldOmit = OMIT_ON_LIST.map((path) =>
matchPath(path, pathname)
).some((m) => !!m);
if (dismissed || shouldOmit) return;
setDialogOpen(true);
}, [dismissed, pathname, setDialogOpen]);
const content = walletDialogOpen ? (
<VegaConnectDialog
connectors={Connectors}
@@ -31,7 +52,12 @@ export const WelcomeDialog = () => {
<WelcomeDialogContent />
);
const onClose = walletDialogOpen ? () => setWalletDialogOpen(false) : dismiss;
const onClose = walletDialogOpen
? () => setWalletDialogOpen(false)
: () => {
setDialogOpen(false);
dismiss();
};
const title = walletDialogOpen ? null : (
<span className="font-alpha calt" data-testid="welcome-title">
+29 -4
View File
@@ -6,23 +6,27 @@ from typing import Optional
WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"])
ASSET_NAME = "tDAI"
def wait_for_toast_confirmation(page: Page, timeout: int = 30000):
page.wait_for_function("""
document.querySelector('[data-testid="toast-content"]') &&
document.querySelector('[data-testid="toast-content"]').innerText.includes('AWAITING CONFIRMATION')
""", timeout=timeout)
def create_and_faucet_wallet(
vega: VegaServiceNull,
wallet: WalletConfig,
symbol: Optional[str] = None,
amount: float = 1e4,
):
asset_id = vega.find_asset_id(symbol=symbol if symbol is not None else ASSET_NAME)
asset_id = vega.find_asset_id(
symbol=symbol if symbol is not None else ASSET_NAME)
vega.create_key(wallet.name)
vega.mint(wallet.name, asset_id, amount)
def next_epoch(vega: VegaServiceNull):
forwards = 0
epoch_seq = vega.statistics().epoch_seq
@@ -36,13 +40,34 @@ def next_epoch(vega: VegaServiceNull):
vega.wait_fn(1)
vega.wait_for_total_catchup()
def truncate_middle(market_id, start=6, end=4):
if len(market_id) < 11:
return market_id
return market_id[:start] + '\u2026' + market_id[-end:]
def change_keys(page: Page, vega:VegaServiceNull, key_name):
def change_keys(page: Page, vega: VegaServiceNull, key_name):
page.get_by_test_id("manage-vega-wallet").click()
page.get_by_test_id("key-" + vega.wallet.public_key(key_name)).click()
page.click(f'data-testid=key-{vega.wallet.public_key(key_name)} >> .inline-flex')
page.click(
f'data-testid=key-{vega.wallet.public_key(key_name)} >> .inline-flex')
page.reload()
def forward_time(vega: VegaServiceNull, forward_epoch: bool = False):
vega.wait_fn(1)
vega.wait_for_total_catchup()
if forward_epoch:
next_epoch(vega)
# This is for when the element will initially load but contain an outdated value. It will wait for the element to contain the expected text, returning False after a timeout or exception
def selector_contains_text(page: Page, selector, expected_text, timeout=5000):
try:
page.wait_for_selector(
f'{selector} >> text={expected_text}', timeout=timeout)
return True
except:
return False
+5 -4
View File
@@ -1,6 +1,7 @@
from typing import List, Tuple, Optional
from vega_sim.service import VegaService, PeggedOrder
def submit_order(
vega: VegaService,
wallet_name: str,
@@ -35,7 +36,7 @@ def submit_multiple_orders(
submit_order(vega, wallet_name, market_id, side, volume, price)
def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str):
def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str, buy_vol=99, sell_vol=99, custom_price=None):
vega.submit_simple_liquidity(
key_name=wallet_name,
market_id=market_id,
@@ -51,7 +52,7 @@ def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str):
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1),
wait=False,
time_in_force="TIME_IN_FORCE_GTC",
volume=99,
volume=buy_vol,
)
vega.submit_order(
market_id=market_id,
@@ -61,5 +62,5 @@ def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str):
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1),
wait=False,
time_in_force="TIME_IN_FORCE_GTC",
volume=99,
)
volume=sell_vol,
)
+30 -11
View File
@@ -8,12 +8,17 @@ logger = logging.getLogger()
mint_amount: float = 10e5
market_name = "BTC:DAI_2023"
default_sell_orders = [[1, 110], [1, 105]]
default_buy_orders = [[1, 90], [1, 95]]
def setup_simple_market(
vega: VegaService,
approve_proposal=True,
custom_market_name=market_name,
custom_asset_name="tDAI",
custom_asset_symbol="tDAI",
custom_quantum=1
):
for wallet in wallets:
vega.create_key(wallet.name)
@@ -37,6 +42,7 @@ def setup_simple_market(
symbol=custom_asset_symbol,
decimals=5,
max_faucet_amount=1e10,
quantum=custom_quantum,
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -111,16 +117,17 @@ def setup_simple_successor_market(
return market_id
def setup_opening_auction_market(vega: VegaService, market_id: str = None, **kwargs):
if market_id is None or market_id not in vega.all_markets():
def setup_opening_auction_market(vega: VegaService, market_id: str = None, buy_orders=default_buy_orders, sell_orders=default_sell_orders, add_liquidity=True, **kwargs):
if not market_exists(vega, market_id):
market_id = setup_simple_market(vega, **kwargs)
submit_liquidity(vega, MM_WALLET.name, market_id)
if add_liquidity:
submit_liquidity(vega, MM_WALLET.name, market_id)
submit_multiple_orders(
vega, MM_WALLET.name, market_id, "SIDE_SELL", [[1, 110], [1, 105]]
vega, MM_WALLET.name, market_id, "SIDE_SELL", sell_orders
)
submit_multiple_orders(
vega, MM_WALLET2.name, market_id, "SIDE_BUY", [[1, 90], [1, 95]]
vega, MM_WALLET2.name, market_id, "SIDE_BUY", buy_orders
)
vega.forward("10s")
@@ -130,11 +137,22 @@ def setup_opening_auction_market(vega: VegaService, market_id: str = None, **kwa
return market_id
def setup_continuous_market(vega: VegaService, market_id: str = None, **kwargs):
if market_id is None or market_id not in vega.all_markets():
market_id = setup_opening_auction_market(vega, **kwargs)
def market_exists(vega: VegaService, market_id: str):
if market_id is None:
return False
all_markets = vega.all_markets()
market_ids = [market.id for market in all_markets]
return market_id in market_ids
submit_order(vega, "Key 1", market_id, "SIDE_BUY", 1, 110)
# Add sell orders and buy orders to put on the book
def setup_continuous_market(vega: VegaService, market_id: str = None, buy_orders=default_buy_orders, sell_orders=default_sell_orders, add_liquidity=True, **kwargs):
if not market_exists(vega, market_id) or buy_orders != default_buy_orders or sell_orders != default_sell_orders:
market_id = setup_opening_auction_market(
vega, market_id, buy_orders, sell_orders, add_liquidity, **kwargs)
submit_order(vega, "Key 1", market_id, "SIDE_BUY",
sell_orders[0][0], sell_orders[0][1])
vega.forward("10s")
vega.wait_fn(1)
@@ -142,6 +160,7 @@ def setup_continuous_market(vega: VegaService, market_id: str = None, **kwargs):
return market_id
def setup_perps_market(
vega: VegaService,
custom_asset_name="tDAI",
@@ -210,7 +229,7 @@ def setup_perps_market(
settlement_data_key=TERMINATE_WALLET.name,
funding_payment_frequency_in_seconds=10,
market_decimals=5,
)
)
vega.wait_for_total_catchup()
submit_liquidity(vega, MM_WALLET.name, market_id)
@@ -225,4 +244,4 @@ def setup_perps_market(
vega.wait_fn(1)
vega.wait_for_total_catchup()
return market_id
return market_id
@@ -59,7 +59,7 @@ def test_market_selector_filter(continuous_market, page: Page):
page.get_by_test_id("search-term").fill("btc")
expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(1)
expect(page.locator('[data-testid="market-selector-list"] a').nth(0)).to_have_text(
"BTC:DAI_2023107.50 tDAI1"
"BTC:DAI_2023107.50 tDAI0.00"
)
page.get_by_test_id("search-term").clear()
@@ -84,5 +84,5 @@ def test_market_selector_filter(continuous_market, page: Page):
page.get_by_role("menuitemcheckbox").nth(0).get_by_text("tDAI").click()
expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(1)
expect(page.locator('[data-testid="market-selector-list"] a').nth(0)).to_have_text(
"BTC:DAI_2023107.50 tDAI1"
"BTC:DAI_2023107.50 tDAI0.00"
)
@@ -137,6 +137,6 @@ def test_perps_market_terminated(page: Page, vega: VegaService):
expect(page.get_by_test_id("market-trading-mode")).to_have_text("Trading modeNo trading")
expect(page.get_by_test_id("market-state")).to_have_text("StatusClosed")
expect(page.get_by_test_id("liquidity-supplied")).to_have_text("Liquidity supplied 0.00 (0.00%)")
expect(page.get_by_test_id("market-funding")).to_have_text("Funding Rate / Countdown-Unknown")
expect(page.get_by_test_id("index-price")).to_have_text("Index Price-")
expect(page.get_by_test_id("market-funding")).to_contain_text("Funding Rate / Countdown")
expect(page.get_by_test_id("index-price")).to_contain_text("Index Price")
expect(page.get_by_test_id("deal-ticket-error-message-summary")).to_have_text("This market is closed and not accepting orders")
@@ -0,0 +1,175 @@
import pytest
from playwright.sync_api import Page, expect
from vega_sim.service import VegaService
from conftest import init_vega
from fixtures.market import setup_continuous_market, setup_simple_market
from actions.utils import change_keys, create_and_faucet_wallet, forward_time, selector_contains_text
from actions.vega import submit_order, submit_liquidity
from wallet_config import MM_WALLET, PARTY_A, PARTY_B
SELL_ORDERS = [[1, 111], [1, 111], [1, 112], [1, 112], [
1, 113], [1, 113], [1, 114], [1, 114], [1, 115], [1, 115]]
BUY_ORDERS = [[1, 106], [1, 107], [1, 108]]
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega:
yield vega
@pytest.fixture(scope="module")
def continuous_market(vega):
market = setup_simple_market(vega, custom_quantum=100000)
return setup_continuous_market(vega, market, BUY_ORDERS, SELL_ORDERS, add_liquidity=False)
def generate_referrer_expected_value_dic(expected_base_commission, expected_staking_multiplier, expected_final_commission_rate, expected_volume, expected_num_traders, expected_total_commission):
return {
'[data-testid=my-volume-value]': expected_volume,
'[data-testid=total-commission-value]': expected_total_commission,
'[data-testid=base-commission-rate-value]': expected_base_commission,
'[data-testid=number-of-traders-value]': expected_num_traders,
'[data-testid=final-commission-rate-value]': expected_final_commission_rate,
'[data-testid=staking-multiplier-value]': expected_staking_multiplier
}
def generate_referral_expected_value_dic(expected_volume, expected_tier, expected_discount, expected_epochs, expected_epochs_to_next_tier):
return {
'[data-testid=combined-volume-value]': expected_volume,
'[data-testid=current-tier-value]': expected_tier,
'[data-testid=discount-value]': expected_discount,
'[data-testid=epochs-in-set-value]': expected_epochs,
'[data-testid=epochs-to-next-tier-value]': expected_epochs_to_next_tier
}
def check_tile_values(page: Page, expected_results: dict):
if "referrals" in page.url:
page.reload()
else:
page.goto("/#/referrals/")
for selector, expected_text in expected_results.items():
assert selector_contains_text(
page, selector, expected_text), f"Expected text '{expected_text}' not found in selector '{selector}'"
def create_benefit_tier(minimum_running_notional_taker_volume, minimum_epochs, referral_reward_factor, referral_discount_factor):
return {
"minimum_running_notional_taker_volume": minimum_running_notional_taker_volume,
"minimum_epochs": minimum_epochs,
"referral_reward_factor": referral_reward_factor,
"referral_discount_factor": referral_discount_factor,
}
def create_staking_tier(minimum_staked_tokens, referral_reward_multiplier):
return {
"minimum_staked_tokens": minimum_staked_tokens,
"referral_reward_multiplier": referral_reward_multiplier,
}
def setup_market_and_referral_scheme(vega: VegaService, continuous_market: str, page: Page):
page.goto(f"/#/markets/{continuous_market}")
create_and_faucet_wallet(vega=vega, wallet=PARTY_A)
create_and_faucet_wallet(vega=vega, wallet=PARTY_B)
forward_time(vega)
benefit_tiers = []
staking_tiers = []
for i in range(1, 4):
benefit_tiers.append(create_benefit_tier(
i * 100, i, i * 0.01, i * 0.01))
staking_tiers.append(create_staking_tier(
i * 100, i))
vega.update_referral_program(
proposal_key=MM_WALLET.name,
benefit_tiers=benefit_tiers,
staking_tiers=staking_tiers,
window_length=1,
)
forward_time(vega, True)
vega.create_referral_set(key_name=PARTY_A.name)
forward_time(vega, True)
referral_set_id = list(vega.list_referral_sets().keys())[0]
vega.apply_referral_code(key_name=PARTY_B.name, id=referral_set_id)
tdai_id = vega.find_asset_id(symbol="tDAI")
vega.mint(
"Key 1",
asset=tdai_id,
amount=10e6,
)
vega.mint(
PARTY_B.name,
asset=tdai_id,
amount=10e6,
)
submit_liquidity(vega, MM_WALLET.name, continuous_market, 100, 100)
forward_time(vega)
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaService, page: Page):
setup_market_and_referral_scheme(vega, continuous_market, page)
change_keys(page, vega, PARTY_B.name)
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 1, 115)
forward_time(vega, True)
check_tile_values(page, generate_referral_expected_value_dic(
"110", "1", "1%", "1", "1"))
change_keys(page, vega, PARTY_A.name)
check_tile_values(page, generate_referrer_expected_value_dic(
"1%", "1", "1%", "0", "1", "0"))
change_keys(page, vega, PARTY_B.name)
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 2, 115)
forward_time(vega, True)
check_tile_values(page, generate_referral_expected_value_dic(
"221", "2", "2%", "2", "1"))
change_keys(page, vega, PARTY_A.name)
check_tile_values(page, generate_referrer_expected_value_dic(
"2%", "1", "2%", "0", "1", "0"))
change_keys(page, vega, PARTY_B.name)
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 3, 115)
forward_time(vega, True)
check_tile_values(page, generate_referral_expected_value_dic(
"331", "3", "3%", "3", "0"))
change_keys(page, vega, PARTY_A.name)
check_tile_values(page, generate_referrer_expected_value_dic(
"3%", "1", "3%", "0", "1", "1"))
change_keys(page, vega, PARTY_B.name)
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 1, 115)
forward_time(vega, True)
check_tile_values(page, generate_referral_expected_value_dic(
"110", "1", "1%", "4", "0"))
change_keys(page, vega, PARTY_A.name)
check_tile_values(page, generate_referrer_expected_value_dic(
"1%", "1", "1%", "0", "1", "1"))
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_does_not_move_up_tiers_when_not_enough_epochs(continuous_market, vega: VegaService, page: Page):
setup_market_and_referral_scheme(vega, continuous_market, page)
change_keys(page, vega, PARTY_B.name)
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 2, 115)
forward_time(vega, True)
check_tile_values(page, generate_referral_expected_value_dic(
"221", "1", "1%", "1", "1"))
change_keys(page, vega, PARTY_A.name)
check_tile_values(page, generate_referrer_expected_value_dic(
"2%", "1", "2%", "0", "1", "0"))
+4 -1
View File
@@ -7,6 +7,9 @@ WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"])
MM_WALLET = WalletConfig("market_maker", "pin")
MM_WALLET2 = WalletConfig("market_maker_2", "pin2")
TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs")
GOVERNANCE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs")
GOVERNANCE_WALLET = WalletConfig(
"FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs")
PARTY_A = WalletConfig("party_a", "party_a")
PARTY_B = WalletConfig("party_b", "party_b")
wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET, GOVERNANCE_WALLET]
+1
View File
@@ -279,6 +279,7 @@
"This timestamp is user curated metadata and does not drive any on-chain functionality.": "This timestamp is user curated metadata and does not drive any on-chain functionality.",
"Tier": "Tier",
"to": "to",
"To protect the network from spam, you must have at least {{requiredFunds}} qUSD of any asset on the network to proceed.": "To protect the network from spam, you must have at least {{requiredFunds}} qUSD of any asset on the network to proceed.",
"Toast location": "Toast location",
"Total discount": "Total discount",
"Total distributed": "Total distributed",
+13
View File
@@ -4,6 +4,7 @@ import {
shorten,
titlefy,
stripFullStops,
ensureSuffix,
} from './strings';
describe('truncateByChars', () => {
@@ -88,3 +89,15 @@ describe('stripFullStops', () => {
});
});
});
describe('ensureSuffix', () => {
it.each([
['', 'abc', 'abc'],
['abc', '', 'abc'],
['def', 'abc', 'abcdef'],
['ąę', 'ae', 'aeąę'],
['🥪', '🍞+🔪=', '🍞+🔪=🥪'],
])('ensures "%s" at the end of "%s": "%s"', (suffix, input, expected) => {
expect(ensureSuffix(input, suffix)).toEqual(expected);
});
});
+6
View File
@@ -33,3 +33,9 @@ export function titlefy(words: (string | null | undefined)[]) {
export function stripFullStops(input: string) {
return input.replace(/\./g, '');
}
export function ensureSuffix(input: string, suffix: string) {
const maybeSuffix = input.substring(input.length - suffix.length);
if (maybeSuffix === suffix) return input;
return input + suffix;
}