diff --git a/components/BannerManager/BannerManager.tsx b/components/BannerManager/BannerManager.tsx
index 1ff199f38..648ced2d2 100644
--- a/components/BannerManager/BannerManager.tsx
+++ b/components/BannerManager/BannerManager.tsx
@@ -5,62 +5,32 @@ import styled from 'styled-components';
import Banner, { BannerType } from 'sections/shared/Layout/Banner';
import { LOCAL_STORAGE_KEYS } from 'constants/storage';
import { ExternalLink } from 'styles/common';
-import { formatShortDateWithTime } from 'utils/formatters/date';
-import { wei } from '@synthetixio/wei';
import useSynthetixQueries from '@synthetixio/queries';
import { EXTERNAL_LINKS } from 'constants/links';
import Connector from 'containers/Connector';
import { isAnyElectionInNomination, isAnyElectionInVoting } from 'utils/governance';
+import { useLiquidation, LiquidationBanner } from './Liquidation';
const BannerManager: FC = () => {
- const { subgraph, useGetLiquidationDataQuery, useGetDebtDataQuery, useGetElectionsPeriodStatus } =
- useSynthetixQueries();
+ const { subgraph, useGetElectionsPeriodStatus } = useSynthetixQueries();
const { L2DefaultProvider, isL2, walletAddress } = Connector.useContainer();
const periodStatusQuery = useGetElectionsPeriodStatus(L2DefaultProvider);
const electionIsInNomination = isAnyElectionInNomination(periodStatusQuery.data);
const electionIsInVoting = isAnyElectionInVoting(periodStatusQuery.data);
- const liquidationData = useGetLiquidationDataQuery(walletAddress);
- const debtData = useGetDebtDataQuery(walletAddress);
-
const feeClaims = subgraph.useGetFeesClaimeds(
{ first: 1, where: { account: walletAddress?.toLowerCase() } },
{ timestamp: true, value: true, rewards: true }
);
-
- const issuanceRatio = debtData?.data?.targetCRatio ?? wei(0);
- const cRatio = debtData?.data?.currentCRatio ?? wei(0);
- const liquidationDeadlineForAccount =
- liquidationData?.data?.liquidationDeadlineForAccount ?? wei(0);
-
- const issuanceRatioPercentage = issuanceRatio.eq(0) ? 0 : 100 / Number(issuanceRatio);
-
const hasClaimHistory = !!feeClaims.data?.length;
- if (!liquidationDeadlineForAccount.eq(0) && cRatio.gt(issuanceRatio)) {
- return (
- ,
- ,
- ,
- ]}
- />
- }
- />
- );
+ const { deadline, hasWarning, ratio } = useLiquidation();
+
+ if (hasWarning) {
+ return ;
}
+
if (electionIsInVoting) {
return (
= ({ ratio, deadline }) => {
+ return (
+ ,
+ ,
+ ,
+ ]}
+ />
+ }
+ />
+ );
+};
+
+const Strong = styled.strong`
+ font-family: ${(props) => props.theme.fonts.condensedBold};
+`;
+
+const StyledExternalLink = styled(ExternalLink)`
+ color: ${(props) => props.theme.colors.white};
+ text-decoration: underline;
+ &:hover {
+ text-decoration: underline;
+ }
+`;
diff --git a/components/BannerManager/Liquidation/index.ts b/components/BannerManager/Liquidation/index.ts
new file mode 100644
index 000000000..ec5bc8ada
--- /dev/null
+++ b/components/BannerManager/Liquidation/index.ts
@@ -0,0 +1,3 @@
+export * from './useLiquidation';
+export * from './useLiquidationOptimismSubgraph';
+export * from './LiquidationBanner';
diff --git a/components/BannerManager/Liquidation/useLiquidation.ts b/components/BannerManager/Liquidation/useLiquidation.ts
new file mode 100644
index 000000000..b86f24bfe
--- /dev/null
+++ b/components/BannerManager/Liquidation/useLiquidation.ts
@@ -0,0 +1,34 @@
+import { wei } from '@synthetixio/wei';
+import useSynthetixQueries from '@synthetixio/queries';
+import Connector from 'containers/Connector';
+import { useLiquidationOptimismSubgraph } from './useLiquidationOptimismSubgraph';
+
+export function useLiquidation() {
+ const { useGetLiquidationDataQuery, useGetDebtDataQuery } = useSynthetixQueries();
+ const { walletAddress } = Connector.useContainer();
+
+ const liquidationData = useGetLiquidationDataQuery(walletAddress);
+ const debtData = useGetDebtDataQuery(walletAddress);
+
+ const issuanceRatio = debtData?.data?.targetCRatio ?? wei(0);
+ const cRatio = debtData?.data?.currentCRatio ?? wei(0);
+ const liquidationDeadlineForAccount =
+ liquidationData?.data?.liquidationDeadlineForAccount ?? wei(0);
+
+ const ratio = issuanceRatio.eq(0) ? 0 : 100 / Number(issuanceRatio);
+
+ const optimismDeadline = useLiquidationOptimismSubgraph();
+ const deadline =
+ optimismDeadline > 0
+ ? optimismDeadline
+ : Number(liquidationDeadlineForAccount.toString()) * 1000;
+
+ const hasWarning =
+ optimismDeadline > 0 || (!liquidationDeadlineForAccount.eq(0) && cRatio.gt(issuanceRatio));
+
+ return {
+ hasWarning,
+ ratio,
+ deadline,
+ };
+}
diff --git a/components/BannerManager/Liquidation/useLiquidationOptimismSubgraph.ts b/components/BannerManager/Liquidation/useLiquidationOptimismSubgraph.ts
new file mode 100644
index 000000000..15f69dd26
--- /dev/null
+++ b/components/BannerManager/Liquidation/useLiquidationOptimismSubgraph.ts
@@ -0,0 +1,86 @@
+import { useQuery, UseQueryOptions } from 'react-query';
+import Connector from 'containers/Connector';
+
+function getEndpoint(networkName: String) {
+ switch (networkName) {
+ case 'kovan-ovm':
+ return 'https://api.thegraph.com/subgraphs/name/noisekit/liquidator-optimism-kovan';
+ case 'mainnet-ovm':
+ return 'https://api.thegraph.com/subgraphs/name/noisekit/liquidator-optimism';
+ default:
+ throw Error(`Called with unsupported network: ${networkName}`);
+ }
+}
+
+const gql = (data: any) => data[0];
+const query = gql`
+ query stakerEntity($id: String!) {
+ stakerEntity(id: $id) {
+ id
+ timestamp
+ status
+ }
+ }
+`;
+
+type StakerEntity = {
+ id: string;
+ timestamp: string;
+ status: string;
+} | null;
+
+export async function fetchLiquidationInfo(walletAddress: string, networkName: string) {
+ const endpoint = getEndpoint(networkName);
+
+ const body = await fetch(endpoint, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ },
+ body: JSON.stringify({
+ query,
+ variables: {
+ id: walletAddress,
+ },
+ }),
+ });
+
+ const {
+ errors,
+ data,
+ }: {
+ errors?: Error[];
+ data: { stakerEntity: StakerEntity };
+ } = await body.json();
+
+ if (errors?.[0]) {
+ throw new Error(errors?.[0]?.message || 'Unknown server error');
+ }
+ return data?.stakerEntity;
+}
+
+export function useLiquidationOptimismInfo(queryOptions?: UseQueryOptions) {
+ const { walletAddress, network } = Connector.useContainer();
+ return useQuery(
+ [walletAddress, network?.name],
+ () => {
+ if (!walletAddress || !network?.name) {
+ throw Error('Missing address or network, query should not run without it');
+ }
+ return fetchLiquidationInfo(walletAddress, network?.name);
+ },
+ {
+ enabled: Boolean(walletAddress && network?.name),
+ ...queryOptions,
+ }
+ );
+}
+
+export function useLiquidationOptimismSubgraph(queryOptions?: UseQueryOptions) {
+ const { data, isSuccess } = useLiquidationOptimismInfo(queryOptions);
+ if (!isSuccess) {
+ return 0;
+ }
+ return data?.status === 'FLAGGED' ? parseInt(data.timestamp, 10) * 1000 : 0;
+}