Skip to content

Commit

Permalink
feat: vault monitoring (#1106)
Browse files Browse the repository at this point in the history
  • Loading branch information
tyleroooo authored Oct 3, 2024
1 parent 1499473 commit 14278a4
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 6 deletions.
19 changes: 19 additions & 0 deletions src/constants/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,25 @@ export const AnalyticsEvents = unionize(
assetSymbol?: string;
assetName?: string;
}>(),

// vaults
ClickViewVaultFromPositionCard: ofType<{}>(),
ClickViewVaultFromOverview: ofType<{}>(),

EnterValidVaultAmountForm: ofType<{}>(),
VaultFormPreviewStep: ofType<{ operation: 'DEPOSIT' | 'WITHDRAW'; amount: number }>(),
AttemptVaultOperation: ofType<{
operation: 'DEPOSIT' | 'WITHDRAW';
amount: number;
slippage: number | null | undefined;
}>(),
VaultOperationPreAborted: ofType<{ operation: 'DEPOSIT' | 'WITHDRAW'; amount: number }>(),
SuccessfulVaultOperation: ofType<{
operation: 'DEPOSIT' | 'WITHDRAW';
amount: number;
amountDiff: number | null | undefined;
}>(),
VaultOperationProtocolError: ofType<{ operation: 'DEPOSIT' | 'WITHDRAW' }>(),
},
{ tag: 'type' as const, value: 'payload' as const }
);
Expand Down
10 changes: 10 additions & 0 deletions src/hooks/vaultsHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import {
VaultFormAction,
VaultFormData,
} from '@/constants/abacus';
import { AnalyticsEvents } from '@/constants/analytics';
import { STRING_KEYS, StringGetterFunction } from '@/constants/localization';
import { timeUnits } from '@/constants/time';

import { selectSubaccountStateForVaults } from '@/state/accountCalculators';
import { getVaultForm, selectVaultFormStateExceptAmount } from '@/state/vaultSelectors';

import abacusStateManager from '@/lib/abacus';
import { track } from '@/lib/analytics/analytics';
import { assertNever } from '@/lib/assertNever';
import { MustBigNumber } from '@/lib/numbers';
import { safeStringifyForAbacusParsing } from '@/lib/stringifyHelpers';
Expand Down Expand Up @@ -230,12 +232,20 @@ const VAULT_FORM_AMOUNT_DEBOUNCE_MS = 500;
const useVaultFormAmountDebounced = () => {
const amount = useAppSelector((state) => state.vaults.vaultForm.amount);
const debouncedAmount = useDebounce(amount, VAULT_FORM_AMOUNT_DEBOUNCE_MS);

useEffect(() => {
if (MustBigNumber(debouncedAmount).gt(0)) {
track(AnalyticsEvents.EnterValidVaultAmountForm());
}
}, [debouncedAmount]);

// if the user goes back to the beginning, use that value for calculations
// this fixes an issue where the validation logic would show an error for a second after submission
// when we reset the value
if (amount === '') {
return amount;
}

return debouncedAmount;
};

Expand Down
3 changes: 3 additions & 0 deletions src/pages/portfolio/AccountOverviewSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { shallowEqual } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';

import { AnalyticsEvents } from '@/constants/analytics';
import { ButtonAction } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { AppRoute } from '@/constants/routes';
Expand All @@ -21,6 +22,7 @@ import { WithLabel } from '@/components/WithLabel';
import { getSubaccount } from '@/state/accountSelectors';
import { useAppSelector } from '@/state/appTypes';

import { track } from '@/lib/analytics/analytics';
import { runIf } from '@/lib/do';
import { isTruthy } from '@/lib/isTruthy';
import { testFlags } from '@/lib/testFlags';
Expand Down Expand Up @@ -54,6 +56,7 @@ export const AccountOverviewSection = () => {
const totalValue = runIf(equity?.current, (e) => e + (vaultBalance ?? 0));

const handleViewVault = useCallback(() => {
track(AnalyticsEvents.ClickViewVaultFromOverview());
navigate(`${AppRoute.Vault}`, {
state: { from: AppRoute.Portfolio },
});
Expand Down
3 changes: 3 additions & 0 deletions src/pages/portfolio/Positions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';

import { AnalyticsEvents } from '@/constants/analytics';
import { STRING_KEYS } from '@/constants/localization';
import { AppRoute, PortfolioRoute } from '@/constants/routes';

Expand All @@ -19,6 +20,7 @@ import { PositionsTable, PositionsTableColumnKey } from '@/views/tables/Position

import { calculateShouldRenderActionsInPositionsTable } from '@/state/accountCalculators';

import { track } from '@/lib/analytics/analytics';
import { isTruthy } from '@/lib/isTruthy';

import { MaybeUnopenedIsolatedPositionsPanel } from '../trade/UnopenedIsolatedPositions';
Expand All @@ -41,6 +43,7 @@ export const Positions = () => {
}, [navigate]);

const handleViewVault = useCallback(() => {
track(AnalyticsEvents.ClickViewVaultFromPositionCard());
navigate(`${AppRoute.Vault}`, {
state: { from: AppRoute.Portfolio },
});
Expand Down
61 changes: 55 additions & 6 deletions src/pages/vaults/VaultDepositWithdrawForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import styled, { css } from 'styled-components';
import tw from 'twin.macro';

import { AlertType } from '@/constants/alerts';
import { AnalyticsEvents } from '@/constants/analytics';
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
Expand Down Expand Up @@ -52,6 +53,7 @@ import {
setVaultFormSlippageAck,
} from '@/state/vaults';

import { track } from '@/lib/analytics/analytics';
import { dd } from '@/lib/analytics/datadog';
import { assertNever } from '@/lib/assertNever';
import { runFn } from '@/lib/do';
Expand Down Expand Up @@ -169,6 +171,13 @@ export const VaultDepositWithdrawForm = ({
if (isSubmitting) {
return;
}
track(
AnalyticsEvents.AttemptVaultOperation({
amount: MustBigNumber(amount).toNumber(),
operation,
slippage: validationResponse.summaryData.estimatedSlippage,
})
);
setIsSubmitting(true);
try {
const { submissionData } = validationResponse;
Expand All @@ -180,6 +189,12 @@ export const VaultDepositWithdrawForm = ({
title: stringGetter({ key: STRING_KEYS.MEGAVAULT_CANT_SUBMIT }),
body: stringGetter({ key: STRING_KEYS.MEGAVAULT_CANT_SUBMIT_BODY }),
});
track(
AnalyticsEvents.VaultOperationPreAborted({
amount: MustBigNumber(amount).toNumber(),
operation,
})
);
dd.error('Megavault deposit blocked, invalid validation response amount', {
deposit: submissionData?.deposit,
});
Expand All @@ -192,6 +207,13 @@ export const VaultDepositWithdrawForm = ({

await depositToMegavault(cachedAmount);

track(
AnalyticsEvents.SuccessfulVaultOperation({
amount: MustBigNumber(amount).toNumber(),
operation,
amountDiff: undefined,
})
);
notify({
title: stringGetter({ key: STRING_KEYS.MEGAVAULT_DEPOSIT_SUCCESSFUL }),
slotTitleLeft: <$SmallIcon iconName={IconName.CheckCircle} />,
Expand All @@ -218,6 +240,12 @@ export const VaultDepositWithdrawForm = ({
title: stringGetter({ key: STRING_KEYS.MEGAVAULT_CANT_SUBMIT }),
body: stringGetter({ key: STRING_KEYS.MEGAVAULT_CANT_SUBMIT_BODY }),
});
track(
AnalyticsEvents.VaultOperationPreAborted({
amount: MustBigNumber(amount).toNumber(),
operation,
})
);
dd.error('Megavault withdraw blocked, invalid validation response values', {
withdraw: submissionData?.withdraw,
});
Expand All @@ -228,16 +256,25 @@ export const VaultDepositWithdrawForm = ({
return;
}

const preEstimate = validationResponse.summaryData.estimatedAmountReceived;
const result = await withdrawFromMegavault(
submissionData?.withdraw?.shares,
submissionData?.withdraw?.minAmount
);

const events = (result as IndexedTx).events;
const events = (result as IndexedTx)?.events;
const actualAmount = events
.find((e) => e.type === 'withdraw_from_megavault')
?.find((e) => e.type === 'withdraw_from_megavault')
?.attributes.find((a) => a.key === 'redeemed_quote_quantums')?.value;

const realAmountReceived = MustBigNumber(actualAmount).div(QUANTUM_MULTIPLIER).toNumber();

track(
AnalyticsEvents.SuccessfulVaultOperation({
amount: realAmountReceived,
operation,
amountDiff: Math.abs((preEstimate ?? 0) - (realAmountReceived ?? 0)),
})
);
notify({
slotTitleLeft: <$SmallIcon iconName={IconName.CheckCircle} />,
title: stringGetter({ key: STRING_KEYS.MEGAVAULT_WITHDRAWAL_SUCCESSFUL }),
Expand All @@ -251,7 +288,7 @@ export const VaultDepositWithdrawForm = ({
<Output
tw="inline-block text-color-text-1"
type={OutputType.Fiat}
value={MustBigNumber(actualAmount).div(QUANTUM_MULTIPLIER)}
value={realAmountReceived}
/>
),
},
Expand Down Expand Up @@ -281,7 +318,11 @@ export const VaultDepositWithdrawForm = ({
: STRING_KEYS.MEGAVAULT_WITHDRAWAL_FAILED_BODY,
}),
});

track(
AnalyticsEvents.VaultOperationProtocolError({
operation,
})
);
dd.error('Megavault transaction failed', { ...validationResponse.submissionData }, e);
// eslint-disable-next-line no-console
console.error('Error submitting megavault transaction', e);
Expand Down Expand Up @@ -573,7 +614,15 @@ export const VaultDepositWithdrawForm = ({
<Button
type={ButtonType.Button}
action={ButtonAction.Secondary}
onClick={() => dispatch(setVaultFormConfirmationStep(false))}
onClick={() => {
track(
AnalyticsEvents.VaultFormPreviewStep({
amount: MustBigNumber(amount).toNumber(),
operation,
})
);
dispatch(setVaultFormConfirmationStep(false));
}}
tw="pl-1 pr-1"
>
{stringGetter({ key: STRING_KEYS.EDIT })}
Expand Down
1 change: 1 addition & 0 deletions src/pages/vaults/VaultPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const VaultPage = () => {
useDocumentTitle(stringGetter({ key: STRING_KEYS.MEGAVAULT }));

const { isTablet } = useBreakpoints();

if (isTablet) {
// one column, reordered, static positioned deposit buttons
return (
Expand Down

0 comments on commit 14278a4

Please sign in to comment.