diff --git a/src/constants/abacus.ts b/src/constants/abacus.ts
index f9e7b69..1e7623f 100644
--- a/src/constants/abacus.ts
+++ b/src/constants/abacus.ts
@@ -119,6 +119,8 @@ export const InputSelectionOption = Abacus.exchange.dydx.abacus.output.input.Sel
// ------ Wallet ------ //
export type Wallet = Abacus.exchange.dydx.abacus.output.Wallet;
export type AccountBalance = Abacus.exchange.dydx.abacus.output.AccountBalance;
+export type TradingRewards = Abacus.exchange.dydx.abacus.output.TradingRewards;
+export type HistoricalTradingReward = Abacus.exchange.dydx.abacus.output.HistoricalTradingReward;
export type Subaccount = Abacus.exchange.dydx.abacus.output.Subaccount;
export type SubaccountPosition = Abacus.exchange.dydx.abacus.output.SubaccountPosition;
export type SubaccountOrder = Abacus.exchange.dydx.abacus.output.SubaccountOrder;
diff --git a/src/lib/abacus/stateNotification.ts b/src/lib/abacus/stateNotification.ts
index 216b7b7..b8b328c 100644
--- a/src/lib/abacus/stateNotification.ts
+++ b/src/lib/abacus/stateNotification.ts
@@ -29,6 +29,7 @@ import {
setSubaccount,
setTransfers,
setWallet,
+ setTradingRewards,
} from '@/state/account';
import { setApiState } from '@/state/app';
@@ -96,6 +97,12 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol {
}
}
+ if (changes.has(Changes.tradingRewards)) {
+ if (updatedState.account?.tradingRewards) {
+ dispatch(setTradingRewards(updatedState.account?.tradingRewards));
+ }
+ }
+
if (changes.has(Changes.configs)) {
dispatch(setConfigs(updatedState.configs));
}
diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx
index 2f9a0bd..5117cd5 100644
--- a/src/pages/Profile.tsx
+++ b/src/pages/Profile.tsx
@@ -26,7 +26,6 @@ import { useAccounts, useStringGetter, useTokenConfigs } from '@/hooks';
import { getOnboardingState } from '@/state/accountSelectors';
import { openDialog } from '@/state/dialogs';
-import abacusStateManager from '@/lib/abacus';
import { isTruthy } from '@/lib/isTruthy';
import { truncateAddress } from '@/lib/wallet';
diff --git a/src/pages/rewards/RewardsPage.tsx b/src/pages/rewards/RewardsPage.tsx
index f69078a..73331cc 100644
--- a/src/pages/rewards/RewardsPage.tsx
+++ b/src/pages/rewards/RewardsPage.tsx
@@ -24,6 +24,7 @@ import { DYDXBalancePanel } from './DYDXBalancePanel';
import { MigratePanel } from './MigratePanel';
import { LaunchIncentivesPanel } from './LaunchIncentivesPanel';
import { RewardsHelpPanel } from './RewardsHelpPanel';
+import { TradingRewardsSummaryPanel } from './TradingRewardsSummaryPanel';
const RewardsPage = () => {
const dispatch = useDispatch();
@@ -50,50 +51,49 @@ const RewardsPage = () => {
{stringGetter({ key: STRING_KEYS.TRADING_REWARDS })}
)}
- {import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && }
+
+ {import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && }
- {isTablet ? (
-
- ) : (
-
-
-
-
- )}
+
+
+ {isTablet && Help} />}
+ Reward History} />
+
-
- {stringGetter({ key: STRING_KEYS.GOVERNANCE })}
- }
- slotRight={panelArrow}
- onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
- >
-
- {stringGetter({ key: STRING_KEYS.GOVERNANCE_DESCRIPTION })}
- e.stopPropagation()}>
- {stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
-
-
-
+ {isNotTablet && (
+
+ {stringGetter({ key: STRING_KEYS.GOVERNANCE })}
+ }
+ slotRight={panelArrow}
+ onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
+ >
+
+ {stringGetter({ key: STRING_KEYS.GOVERNANCE_DESCRIPTION })}
+ e.stopPropagation()}>
+ {stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
+
+
+
- {stringGetter({ key: STRING_KEYS.STAKING })}
- }
- slotRight={panelArrow}
- onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
- >
-
- {stringGetter({ key: STRING_KEYS.STAKING_DESCRIPTION })}
- e.stopPropagation()}>
- {stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
-
-
-
-
+ {stringGetter({ key: STRING_KEYS.STAKING })}}
+ slotRight={panelArrow}
+ onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
+ >
+
+ {stringGetter({ key: STRING_KEYS.STAKING_DESCRIPTION })}
+ e.stopPropagation()}>
+ {stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
+
+
+
-
+ Help} />
+
+ )}
+
);
};
@@ -104,7 +104,6 @@ const Styled: Record = {};
Styled.Page = styled.div`
${layoutMixins.contentContainerPage}
- gap: 1.5rem;
padding: 2rem;
align-items: center;
@@ -138,6 +137,54 @@ Styled.MobileHeader = styled.header`
Styled.Panel = styled(Panel)`
height: fit-content;
+
+ font: var(--font-large-medium);
+ color: var(--color-text-2);
+ background-color: var(--color-layer-2);
+`;
+
+Styled.GridLayout = styled.div`
+ --gap: 1.5rem;
+ display: grid;
+ grid-template-columns: 2fr 1fr;
+ gap: var(--gap);
+
+ > * {
+ gap: var(--gap);
+ }
+
+ grid-template-areas:
+ 'migrate migrate'
+ 'incentives balance'
+ 'rewards other';
+
+ @media ${breakpoints.tablet} {
+ --gap: 1rem;
+ grid-template-columns: 1fr;
+ grid-template-areas:
+ 'incentives'
+ 'rewards';
+ }
+`;
+
+Styled.MigratePanel = styled(MigratePanel)`
+ grid-area: migrate;
+`;
+
+Styled.LaunchIncentivesPanel = styled(LaunchIncentivesPanel)`
+ grid-area: incentives;
+`;
+Styled.DYDXBalancePanel = styled(DYDXBalancePanel)`
+ grid-area: balance;
+`;
+
+Styled.TradingRewardsColumn = styled.div`
+ grid-area: rewards;
+ ${layoutMixins.flexColumn}
+`;
+Styled.OtherColumn = styled.div`
+ grid-area: other;
+ ${layoutMixins.flexColumn}
`;
Styled.Title = styled.h3`
@@ -158,20 +205,6 @@ Styled.Description = styled.div`
}
`;
-Styled.PanelRow = styled.div`
- ${layoutMixins.gridEqualColumns}
- gap: 1.5rem;
-
- @media ${breakpoints.tablet} {
- grid-auto-flow: row;
- grid-template-columns: 1fr;
- }
-`;
-
-Styled.PanelRowIncentivesAndBalance = styled(Styled.PanelRow)`
- grid-template-columns: 2fr 1fr;
-`;
-
Styled.IconButton = styled(IconButton)`
color: var(--color-text-0);
--color-border: var(--color-layer-6);
diff --git a/src/pages/rewards/TradingRewardsSummaryPanel.tsx b/src/pages/rewards/TradingRewardsSummaryPanel.tsx
new file mode 100644
index 0000000..236d183
--- /dev/null
+++ b/src/pages/rewards/TradingRewardsSummaryPanel.tsx
@@ -0,0 +1,119 @@
+import styled, { AnyStyledComponent } from 'styled-components';
+import { shallowEqual, useSelector } from 'react-redux';
+
+import { STRING_KEYS } from '@/constants/localization';
+import breakpoints from '@/styles/breakpoints';
+import { layoutMixins } from '@/styles/layoutMixins';
+import { useStringGetter, useTokenConfigs } from '@/hooks';
+
+import { AssetIcon } from '@/components/AssetIcon';
+import { Details } from '@/components/Details';
+import { Output, OutputType } from '@/components/Output';
+import { Panel } from '@/components/Panel';
+
+import { getHistoricalTradingRewards } from '@/state/accountSelectors';
+
+export const TradingRewardsSummaryPanel = () => {
+ const stringGetter = useStringGetter();
+ const historicalTradingRewards = useSelector(getHistoricalTradingRewards, shallowEqual);
+ const currentWeekTradingReward = historicalTradingRewards?.get('WEEKLY')?.firstOrNull()?.amount;
+ const { chainTokenLabel } = useTokenConfigs();
+
+ return (
+
+ Trading Rewards Summary
+
+ }
+ >
+
+
+ {stringGetter({ key: STRING_KEYS.THIS_WEEK })}
+
+ ),
+ value: (
+ }
+ type={OutputType.Asset}
+ value={currentWeekTradingReward}
+ />
+ ),
+ },
+ // TODO(@aforaleka): add all-time when supported
+ ]}
+ />
+
+
+ );
+};
+
+const Styled: Record = {};
+
+Styled.Panel = styled(Panel)`
+ --panel-paddingX: 1.5rem;
+ --panel-paddingY: 1.25rem;
+
+ @media ${breakpoints.tablet} {
+ --panel-paddingY: 1.5rem;
+ }
+`;
+
+Styled.Header = styled.div`
+ ${layoutMixins.spacedRow}
+ gap: 1rem;
+ padding: var(--panel-paddingY) var(--panel-paddingX) 0;
+`;
+
+Styled.Title = styled.h3`
+ ${layoutMixins.inlineRow}
+ font: var(--font-medium-book);
+ color: var(--color-text-2);
+
+ img {
+ font-size: 1.5rem;
+ }
+`;
+
+Styled.Content = styled.div`
+ ${layoutMixins.flexColumn}
+ gap: 0.75rem;
+`;
+
+Styled.TradingRewardsDetails = styled(Details)`
+ --details-item-backgroundColor: var(--color-layer-6);
+
+ grid-template-columns: 1fr; // TODO(@aforaleka): change to 1fr 1fr when all-time is supported
+ gap: 1rem;
+
+ > div {
+ gap: 1rem;
+
+ padding: 1rem;
+
+ border-radius: 0.75em;
+ background-color: var(--color-layer-5);
+ }
+
+ dt {
+ width: 100%;
+ }
+
+ output {
+ color: var(--color-text-2);
+ font: var(--font-large-book);
+ }
+`;
+
+Styled.Label = styled.div`
+ ${layoutMixins.spacedRow}
+
+ font: var(--font-base-book);
+ color: var(--color-text-1);
+`;
diff --git a/src/state/account.ts b/src/state/account.ts
index aae7b2d..57d7a54 100644
--- a/src/state/account.ts
+++ b/src/state/account.ts
@@ -13,6 +13,7 @@ import type {
HistoricalPnlPeriods,
SubAccountHistoricalPNLs,
UsageRestriction,
+ TradingRewards,
} from '@/constants/abacus';
import { OnboardingGuard, OnboardingState } from '@/constants/account';
@@ -24,6 +25,7 @@ import { getLocalStorage } from '@/lib/localStorage';
export type AccountState = {
balances?: Record;
stakingBalances?: Record;
+ tradingRewards?: TradingRewards;
wallet?: Nullable;
walletType?: WalletType;
@@ -179,6 +181,9 @@ export const accountSlice = createSlice({
setStakingBalances: (state, action: PayloadAction>) => {
state.stakingBalances = action.payload;
},
+ setTradingRewards: (state, action: PayloadAction) => {
+ state.tradingRewards = action.payload;
+ },
addUncommittedOrderClientId: (state, action: PayloadAction) => {
state.uncommittedOrderClientIds.push(action.payload);
},
@@ -206,6 +211,7 @@ export const {
viewedOrders,
setBalances,
setStakingBalances,
+ setTradingRewards,
addUncommittedOrderClientId,
removeUncommittedOrderClientId,
} = accountSlice.actions;
diff --git a/src/state/accountSelectors.ts b/src/state/accountSelectors.ts
index 9594b46..49c9d32 100644
--- a/src/state/accountSelectors.ts
+++ b/src/state/accountSelectors.ts
@@ -343,6 +343,17 @@ export const getBalances = (state: RootState) => state.account?.balances;
* */
export const getStakingBalances = (state: RootState) => state.account?.stakingBalances;
+/**
+ * @returns account all time trading rewards
+ */
+export const getTotalTradingRewards = (state: RootState) => state.account?.tradingRewards?.total;
+
+/**
+ * @returns account trading rewards aggregated by period
+ */
+export const getHistoricalTradingRewards = (state: RootState) =>
+ state.account?.tradingRewards?.historical;
+
/**
* @returns UsageRestriction of the current session
*/