diff --git a/libs/oeth/shared/.env b/.env
similarity index 100%
rename from libs/oeth/shared/.env
rename to .env
diff --git a/apps/oeth/src/App.tsx b/apps/oeth/src/App.tsx
index 95a4c00dc..0b44fde71 100644
--- a/apps/oeth/src/App.tsx
+++ b/apps/oeth/src/App.tsx
@@ -1,11 +1,8 @@
import { Container, CssBaseline, Stack } from '@mui/material';
-import { registerChart } from '@origin/shared/providers';
import { Outlet } from 'react-router-dom';
import { Topnav } from './components/Topnav';
-registerChart();
-
export const App = () => {
return (
<>
diff --git a/apps/oeth/src/main.tsx b/apps/oeth/src/main.tsx
index e6cbe3e94..2076a176c 100644
--- a/apps/oeth/src/main.tsx
+++ b/apps/oeth/src/main.tsx
@@ -6,7 +6,11 @@ import * as ReactDOM from 'react-dom/client';
import { Experimental_CssVarsProvider as CssVarsProvider } from '@mui/material';
import { chains, queryClient, wagmiConfig } from '@origin/oeth/shared';
-import { CurveProvider, NotificationsProvider } from '@origin/shared/providers';
+import {
+ CurveProvider,
+ NotificationsProvider,
+ registerChart,
+} from '@origin/shared/providers';
import { theme } from '@origin/shared/theme';
import { composeContexts } from '@origin/shared/utils';
import { darkTheme, RainbowKitProvider } from '@rainbow-me/rainbowkit';
@@ -22,6 +26,8 @@ import { routes } from './routes';
// https://github.com/dai-shi/proxy-compare/pull/8
setAutoFreeze(false);
+registerChart();
+
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
);
diff --git a/libs/oeth/history/src/components/APYContainer.tsx b/libs/oeth/history/src/components/APYContainer.tsx
index f3a05dfcc..9e95389ac 100644
--- a/libs/oeth/history/src/components/APYContainer.tsx
+++ b/libs/oeth/history/src/components/APYContainer.tsx
@@ -1,19 +1,31 @@
-import { Divider, Stack, Typography } from '@mui/material';
-import { valueFormat } from '@origin/shared/utils';
+import { Divider, Skeleton, Stack, Typography } from '@mui/material';
+import { tokens } from '@origin/shared/contracts';
+import { balanceFormat } from '@origin/shared/utils';
import { useIntl } from 'react-intl';
-import { useAccount } from 'wagmi';
+import { useAccount, useBalance } from 'wagmi';
+import { usePendingYield } from '../hooks';
import { useHistoryTableQuery } from '../queries.generated';
+import type { StackProps } from '@mui/material';
+
export function APYContainer() {
+ const intl = useIntl();
const { address, isConnected } = useAccount();
- const { data } = useHistoryTableQuery(
- { addressId: address?.toLowerCase(), offset: 0 },
+ const { data: oethBalance, isLoading: oethLoading } = useBalance({
+ address,
+ token: tokens.mainnet.OETH.address,
+ watch: true,
+ });
+ const { data: earnings, isLoading: earningsLoading } = useHistoryTableQuery(
+ { address: address?.toLowerCase(), offset: 0 },
{
enabled: isConnected,
},
);
- const intl = useIntl();
+ const { data: pendingYield, isLoading: pendingYieldLoading } =
+ usePendingYield(tokens.mainnet.OETH);
+
return (
);
}
-interface Props {
+type ValueContainerProps = {
label: string;
- value: number;
-}
+ value: string;
+ isLoading?: boolean;
+} & StackProps;
-function ValueContainer(props: Props) {
- const intl = useIntl();
+function ValueContainer({
+ label,
+ value,
+ isLoading,
+ ...rest
+}: ValueContainerProps) {
return (
-
+
- {props.label}
+ {label}
- {intl.formatNumber(props.value, valueFormat)}
+ {isLoading ? : value}
);
diff --git a/libs/oeth/history/src/components/HistoryCard.tsx b/libs/oeth/history/src/components/HistoryCard.tsx
index d87e068ee..65678ec3d 100644
--- a/libs/oeth/history/src/components/HistoryCard.tsx
+++ b/libs/oeth/history/src/components/HistoryCard.tsx
@@ -1,51 +1,31 @@
import { useState } from 'react';
import { Box, Button, Divider, Stack, Typography } from '@mui/material';
-import { graphqlClient } from '@origin/oeth/shared';
-import { useQuery } from '@tanstack/react-query';
import { useIntl } from 'react-intl';
import { useAccount } from 'wagmi';
-import {
- HistoryTableDocument,
- HistoryTableWithFiltersDocument,
-} from '../queries.generated';
+import { useHistoryTableWithFiltersQuery } from '../queries.generated';
import { ExportData } from './ExportData';
import { HistoryFilters } from './Filters';
import { HistoryTable } from './HistoryTable';
-import type { HistoryTableQuery } from '../queries.generated';
-
const PAGE_SIZE = 20;
export function HistoryCard() {
+ const intl = useIntl();
const [page, setPage] = useState(0);
const [filters, setFilters] = useState([]);
const { address, isConnected } = useAccount();
- const { data, isFetching } = useQuery(
- ['history-table', address, filters, page],
- () => {
- return graphqlClient<
- HistoryTableQuery,
- { addressId: string; filters?: string[]; offset: number }
- >(
- filters.length ? HistoryTableWithFiltersDocument : HistoryTableDocument,
- {
- addressId: address?.toLowerCase(),
- filters: filters.length ? filters : undefined,
- offset: page * PAGE_SIZE,
- },
- )();
- },
-
+ const { data, isFetching } = useHistoryTableWithFiltersQuery(
{
- enabled: isConnected,
+ address: address.toLowerCase(),
+ filters: filters.length ? filters : undefined,
+ offset: page * PAGE_SIZE,
},
+ { enabled: isConnected },
);
- const intl = useIntl();
-
return (
+ useQuery({
+ queryKey: ['usePendingYield', token.symbol],
+ queryFn: async () => {
+ const { address, isConnected } = getAccount();
+
+ if (!isConnected) {
+ return 0;
+ }
+
+ // TODO endpoint for next userCredits is not yet available on subsquid
+ const [balance, data] = await Promise.all([
+ fetchBalance({ address, token: token.address }),
+ queryClient.fetchQuery({
+ queryKey: useHistoryTableQuery.getKey({ address, offset: 0 }),
+ queryFn: useHistoryTableQuery.fetcher({
+ address: address.toLowerCase(),
+ offset: 0,
+ }),
+ }),
+ ]);
+
+ if (isNilOrEmpty(data?.addressById) || balance?.value === 0n) return 0;
+
+ return 0;
+ },
+ });
diff --git a/libs/oeth/history/src/queries.generated.tsx b/libs/oeth/history/src/queries.generated.ts
similarity index 55%
rename from libs/oeth/history/src/queries.generated.tsx
rename to libs/oeth/history/src/queries.generated.ts
index 0082ab230..501572792 100644
--- a/libs/oeth/history/src/queries.generated.tsx
+++ b/libs/oeth/history/src/queries.generated.ts
@@ -4,7 +4,7 @@ import { useQuery } from '@tanstack/react-query';
import type * as Types from '@origin/oeth/shared';
import type { UseQueryOptions } from '@tanstack/react-query';
export type HistoryTableQueryVariables = Types.Exact<{
- addressId: Types.Scalars['String']['input'];
+ address: Types.Scalars['String']['input'];
offset: Types.Scalars['Int']['input'];
}>;
@@ -29,7 +29,7 @@ export type HistoryTableQuery = {
};
export type HistoryTableWithFiltersQueryVariables = Types.Exact<{
- addressId: Types.Scalars['String']['input'];
+ address: Types.Scalars['String']['input'];
offset: Types.Scalars['Int']['input'];
filters?: Types.InputMaybe<
Array | Types.Scalars['String']['input']
@@ -44,6 +44,7 @@ export type HistoryTableWithFiltersQuery = {
earned: number;
isContract: boolean;
rebasingOption: string;
+ credits: any;
lastUpdated: any;
history: Array<{
__typename?: 'History';
@@ -56,9 +57,16 @@ export type HistoryTableWithFiltersQuery = {
} | null;
};
+export type HistoryApyQueryVariables = Types.Exact<{ [key: string]: never }>;
+
+export type HistoryApyQuery = {
+ __typename?: 'Query';
+ apies: Array<{ __typename?: 'APY'; apy7DayAvg: number; apy30DayAvg: number }>;
+};
+
export const HistoryTableDocument = `
- query HistoryTable($addressId: String!, $offset: Int!) {
- addressById(id: $addressId) {
+ query HistoryTable($address: String!, $offset: Int!) {
+ addressById(id: $address) {
balance
earned
isContract
@@ -89,13 +97,28 @@ export const useHistoryTableQuery = <
),
options,
);
+
+useHistoryTableQuery.getKey = (variables: HistoryTableQueryVariables) => [
+ 'HistoryTable',
+ variables,
+];
+useHistoryTableQuery.fetcher = (
+ variables: HistoryTableQueryVariables,
+ options?: RequestInit['headers'],
+) =>
+ graphqlClient(
+ HistoryTableDocument,
+ variables,
+ options,
+ );
export const HistoryTableWithFiltersDocument = `
- query HistoryTableWithFilters($addressId: String!, $offset: Int!, $filters: [String!]) {
- addressById(id: $addressId) {
+ query HistoryTableWithFilters($address: String!, $offset: Int!, $filters: [String!]) {
+ addressById(id: $address) {
balance
earned
isContract
rebasingOption
+ credits
lastUpdated
history(
limit: 20
@@ -127,3 +150,47 @@ export const useHistoryTableWithFiltersQuery = <
>(HistoryTableWithFiltersDocument, variables),
options,
);
+
+useHistoryTableWithFiltersQuery.getKey = (
+ variables: HistoryTableWithFiltersQueryVariables,
+) => ['HistoryTableWithFilters', variables];
+useHistoryTableWithFiltersQuery.fetcher = (
+ variables: HistoryTableWithFiltersQueryVariables,
+ options?: RequestInit['headers'],
+) =>
+ graphqlClient<
+ HistoryTableWithFiltersQuery,
+ HistoryTableWithFiltersQueryVariables
+ >(HistoryTableWithFiltersDocument, variables, options);
+export const HistoryApyDocument = `
+ query HistoryApy {
+ apies(limit: 1, orderBy: timestamp_DESC) {
+ apy7DayAvg
+ apy30DayAvg
+ }
+}
+ `;
+export const useHistoryApyQuery = (
+ variables?: HistoryApyQueryVariables,
+ options?: UseQueryOptions,
+) =>
+ useQuery(
+ variables === undefined ? ['HistoryApy'] : ['HistoryApy', variables],
+ graphqlClient(
+ HistoryApyDocument,
+ variables,
+ ),
+ options,
+ );
+
+useHistoryApyQuery.getKey = (variables?: HistoryApyQueryVariables) =>
+ variables === undefined ? ['HistoryApy'] : ['HistoryApy', variables];
+useHistoryApyQuery.fetcher = (
+ variables?: HistoryApyQueryVariables,
+ options?: RequestInit['headers'],
+) =>
+ graphqlClient(
+ HistoryApyDocument,
+ variables,
+ options,
+ );
diff --git a/libs/oeth/history/src/queries.graphql b/libs/oeth/history/src/queries.graphql
index 676841d7e..856707099 100644
--- a/libs/oeth/history/src/queries.graphql
+++ b/libs/oeth/history/src/queries.graphql
@@ -1,9 +1,10 @@
-query HistoryTable($addressId: String!, $offset: Int!) {
- addressById(id: $addressId) {
+query HistoryTable($address: String!, $offset: Int!) {
+ addressById(id: $address) {
balance
earned
isContract
rebasingOption
+ credits
lastUpdated
history(limit: 20, orderBy: timestamp_DESC, offset: $offset) {
type
@@ -16,11 +17,11 @@ query HistoryTable($addressId: String!, $offset: Int!) {
}
query HistoryTableWithFilters(
- $addressId: String!
+ $address: String!
$offset: Int!
$filters: [String!]
) {
- addressById(id: $addressId) {
+ addressById(id: $address) {
balance
earned
isContract
@@ -40,3 +41,10 @@ query HistoryTableWithFilters(
}
}
}
+
+query HistoryApy {
+ apies(limit: 1, orderBy: timestamp_DESC) {
+ apy7DayAvg
+ apy30DayAvg
+ }
+}
diff --git a/libs/oeth/redeem/src/views/RedeemView.tsx b/libs/oeth/redeem/src/views/RedeemView.tsx
index 02603585a..bac846849 100644
--- a/libs/oeth/redeem/src/views/RedeemView.tsx
+++ b/libs/oeth/redeem/src/views/RedeemView.tsx
@@ -78,7 +78,6 @@ function RedeemViewWrapped() {
return (
(query: string, variables?: TVariables) =>
+ (
+ query: string,
+ variables?: TVariables,
+ options?: RequestInit['headers'],
+ ) =>
async () => {
const res = await axiosInstance({
url: '/graphql',
@@ -14,6 +18,7 @@ export const graphqlClient =
headers: {
'Content-Type': 'application/json',
},
+ ...options,
data: { query, variables },
});
diff --git a/libs/oeth/shared/src/components/ApyHeader.tsx b/libs/oeth/shared/src/components/ApyHeader.tsx
deleted file mode 100644
index 65ecd5595..000000000
--- a/libs/oeth/shared/src/components/ApyHeader.tsx
+++ /dev/null
@@ -1,234 +0,0 @@
-import { useState } from 'react';
-
-import {
- alpha,
- Box,
- Divider,
- IconButton,
- Menu,
- MenuItem,
- Stack,
- Typography,
-} from '@mui/material';
-import { Icon } from '@origin/shared/components';
-import { tokens } from '@origin/shared/contracts';
-import { useIntl } from 'react-intl';
-import { useAccount, useBalance } from 'wagmi';
-
-const days = [7, 30];
-
-export function ApyHeader() {
- const intl = useIntl();
- const [selectedPeriod, setSelectedPeriod] = useState(30);
- const [anchorEl, setAnchorEl] = useState(null);
- const { address } = useAccount();
- const { data: oethBalance } = useBalance({
- address,
- token: tokens.mainnet.OUSD.address,
- watch: true,
- });
-
- const handleClose = () => {
- setAnchorEl(null);
- };
-
- return (
- <>
-
-
-
-
- {intl.formatMessage(
- { defaultMessage: '{days} day trailing APY' },
- { days: selectedPeriod },
- )}
-
-
-
- {intl.formatNumber(6.71 / 100, {
- minimumFractionDigits: 2,
- style: 'percent',
- })}
-
- setAnchorEl(e.currentTarget)}
- sx={{
- backgroundColor: (theme) =>
- alpha(theme.palette.common.white, 0.15),
- marginInlineStart: 1,
- alignSelf: 'center',
- position: 'relative',
- height: '1rem',
- width: '1rem',
- borderRadius: '100%',
- top: '-2px',
- }}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
-
-function ValueContainer({
- text,
- value,
- icon,
-}: {
- text: string;
- value: string;
- icon?: string;
-}) {
- return (
-
-
- {text}
-
-
- {icon ? (
-
- ) : undefined}
-
- {value}
-
-
- );
-}
diff --git a/libs/oeth/shared/src/components/index.tsx b/libs/oeth/shared/src/components/index.ts
similarity index 68%
rename from libs/oeth/shared/src/components/index.tsx
rename to libs/oeth/shared/src/components/index.ts
index 89385b39e..45b4fb9d0 100644
--- a/libs/oeth/shared/src/components/index.tsx
+++ b/libs/oeth/shared/src/components/index.ts
@@ -1,3 +1,2 @@
-export * from './ApyHeader';
export * from './AccountPopover';
export * from './GasPopover';
diff --git a/libs/oeth/swap/src/components/ApyChart.tsx b/libs/oeth/swap/src/components/ApyChart.tsx
new file mode 100644
index 000000000..968a89d5e
--- /dev/null
+++ b/libs/oeth/swap/src/components/ApyChart.tsx
@@ -0,0 +1,348 @@
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+
+import {
+ Box,
+ Button,
+ Menu,
+ MenuItem,
+ Skeleton,
+ Stack,
+ Typography,
+ useTheme,
+} from '@mui/material';
+import { Card } from '@origin/shared/components';
+import { isNilOrEmpty } from '@origin/shared/utils';
+import { ascend, last, prop, sort } from 'ramda';
+import { Line } from 'react-chartjs-2';
+import { defineMessage, useIntl } from 'react-intl';
+
+import { useApiesQuery } from '../queries.generated';
+
+import type { StackProps } from '@mui/material';
+import type { Chart, ChartData, ChartOptions, Plugin } from 'chart.js';
+
+const limitOptions = [
+ { label: defineMessage({ defaultMessage: '1W' }), value: 7 },
+ { label: defineMessage({ defaultMessage: '1M' }), value: 30 },
+ { label: defineMessage({ defaultMessage: '6M' }), value: 180 },
+ { label: defineMessage({ defaultMessage: '1YR' }), value: 365 },
+ { label: defineMessage({ defaultMessage: 'All' }), value: undefined },
+];
+
+const trailingOptions = [
+ { label: defineMessage({ defaultMessage: '30 days trailing' }), value: 30 },
+ { label: defineMessage({ defaultMessage: '7 days trailing' }), value: 7 },
+];
+
+export const ApyChart = (props: StackProps) => {
+ const intl = useIntl();
+ const theme = useTheme();
+ const chartRef = useRef>(null);
+ const [apy, setApy] = useState(0);
+ const [timestamp, setTimestamp] = useState(null);
+ const [limit, setLimit] = useState(limitOptions[0]);
+ const [trailing, setTrailing] = useState(trailingOptions[0]);
+ const [anchorEl, setAnchorEl] = useState(null);
+ const { data: apies, isLoading: apiesLoading } = useApiesQuery(
+ {
+ limit: limit.value,
+ },
+ {
+ select: (data) => sort(ascend(prop('timestamp')), data.apies),
+ },
+ );
+
+ useEffect(() => {
+ if (!isNilOrEmpty(apies)) {
+ setApy(
+ trailing.value === 30
+ ? last(apies).apy30DayAvg
+ : last(apies).apy7DayAvg,
+ );
+ setTimestamp(last(apies).timestamp);
+ }
+ }, [apies, trailing.value]);
+
+ useEffect(() => {
+ if (chartRef.current) {
+ chartRef.current.options.scales.x['time'].unit =
+ isNilOrEmpty(limit.value) || limit.value > 30 ? 'month' : 'day';
+ chartRef.current.update();
+ }
+ }, [limit.value]);
+
+ const handleMouseLeave = useCallback(() => {
+ setApy(
+ trailing.value === 30 ? last(apies).apy30DayAvg : last(apies).apy7DayAvg,
+ );
+ setTimestamp(last(apies).timestamp);
+ }, [apies, trailing.value]);
+
+ const hoverline: Plugin = {
+ id: 'hoverline',
+ afterDatasetDraw: (chart, args, options) => {
+ const {
+ ctx,
+ tooltip,
+ chartArea: { top, bottom },
+ scales: { x },
+ } = chart;
+
+ if (
+ !isNilOrEmpty(tooltip?.['_active']) &&
+ !isNilOrEmpty(tooltip?.dataPoints?.[0]?.parsed?.x)
+ ) {
+ const xCoor = x.getPixelForValue(tooltip.dataPoints[0].parsed.x);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = theme.palette.grey[200];
+ ctx.setLineDash([3, 3]);
+ ctx.moveTo(xCoor, top);
+ ctx.lineTo(xCoor, bottom);
+ ctx.stroke();
+ ctx.restore();
+ }
+ },
+ };
+
+ const chartData = useMemo>(
+ () => ({
+ datasets: [
+ {
+ data: apies?.map((item) => ({
+ x: item.timestamp,
+ y: trailing.value === 7 ? item.apy7DayAvg : item.apy30DayAvg,
+ })),
+ },
+ ],
+ }),
+ [apies, trailing.value],
+ );
+
+ const chartOptions = useMemo>(
+ () => ({
+ responsive: true,
+ maintainAspectRatio: false,
+ interaction: {
+ intersect: false,
+ },
+ events: ['mousemove', 'mouseout', 'mouseenter'],
+ datasets: {
+ line: {
+ backgroundColor: 'transparent',
+ pointRadius: 0,
+ pointHoverRadius: 0,
+ borderWidth: 2,
+ tension: 0.4,
+ },
+ },
+ borderColor: (ctx) => {
+ const gradient = ctx.chart.ctx.createLinearGradient(
+ 0,
+ 0,
+ ctx.chart.width,
+ ctx.chart.height,
+ );
+ gradient.addColorStop(0, theme.palette.primary.main);
+ gradient.addColorStop(1, theme.palette.primary.dark);
+
+ return gradient;
+ },
+ layout: {
+ padding: 0,
+ },
+ scales: {
+ y: {
+ display: false,
+ grid: {
+ display: false,
+ },
+ },
+ x: {
+ type: 'time',
+ time: {
+ round: 'day',
+ },
+ border: { display: false },
+ grid: {
+ display: false,
+ },
+ ticks: {
+ align: 'inner',
+ maxRotation: 0,
+ padding: 4,
+ font: {
+ size: 11,
+ family: 'Inter',
+ weight: '400',
+ },
+ },
+ },
+ },
+ title: {
+ display: false,
+ },
+ plugins: {
+ legend: {
+ display: false,
+ },
+ tooltip: {
+ backgroundColor: 'transparent',
+ titleColor: 'transparent',
+ bodyColor: 'transparent',
+ borderColor: 'transparent',
+ footerColor: 'transparent',
+ borderWidth: 0,
+ padding: 0,
+ boxHeight: 0,
+ boxWidth: 0,
+ mode: 'index',
+ },
+ },
+ onHover: (event, elements, chart) => {
+ if (!isNilOrEmpty(chart?.tooltip?.dataPoints?.[0]?.parsed)) {
+ setTimestamp(chart.tooltip.dataPoints[0].parsed.x);
+ setApy(chart.tooltip.dataPoints[0].parsed.y);
+ }
+ },
+ }),
+ [theme.palette.primary.dark, theme.palette.primary.main],
+ );
+
+ return (
+
+
+ {intl.formatMessage({ defaultMessage: 'APY' })}
+
+
+ {limitOptions.map((d) => (
+
+ ))}
+
+
+ }
+ >
+
+
+
+ {apiesLoading ? (
+
+ ) : (
+
+ {intl.formatNumber(apy, {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ })}
+ %
+
+ )}
+ {apiesLoading ? (
+
+ ) : (
+
+ {intl.formatDate(new Date(timestamp), {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric',
+ })}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/libs/oeth/swap/src/queries.generated.ts b/libs/oeth/swap/src/queries.generated.ts
new file mode 100644
index 000000000..4c5ec1771
--- /dev/null
+++ b/libs/oeth/swap/src/queries.generated.ts
@@ -0,0 +1,51 @@
+import { graphqlClient } from '@origin/oeth/shared';
+import { useQuery } from '@tanstack/react-query';
+
+import type * as Types from '@origin/oeth/shared';
+import type { UseQueryOptions } from '@tanstack/react-query';
+export type ApiesQueryVariables = Types.Exact<{
+ limit?: Types.InputMaybe;
+}>;
+
+export type ApiesQuery = {
+ __typename?: 'Query';
+ apies: Array<{
+ __typename?: 'APY';
+ id: string;
+ timestamp: any;
+ apy7DayAvg: number;
+ apy30DayAvg: number;
+ }>;
+};
+
+export const ApiesDocument = `
+ query Apies($limit: Int) {
+ apies(limit: $limit, orderBy: timestamp_DESC) {
+ id
+ timestamp
+ apy7DayAvg
+ apy30DayAvg
+ }
+}
+ `;
+export const useApiesQuery = (
+ variables?: ApiesQueryVariables,
+ options?: UseQueryOptions,
+) =>
+ useQuery(
+ variables === undefined ? ['Apies'] : ['Apies', variables],
+ graphqlClient(ApiesDocument, variables),
+ options,
+ );
+
+useApiesQuery.getKey = (variables?: ApiesQueryVariables) =>
+ variables === undefined ? ['Apies'] : ['Apies', variables];
+useApiesQuery.fetcher = (
+ variables?: ApiesQueryVariables,
+ options?: RequestInit['headers'],
+) =>
+ graphqlClient(
+ ApiesDocument,
+ variables,
+ options,
+ );
diff --git a/libs/oeth/swap/src/queries.graphql b/libs/oeth/swap/src/queries.graphql
new file mode 100644
index 000000000..5e2258a0b
--- /dev/null
+++ b/libs/oeth/swap/src/queries.graphql
@@ -0,0 +1,8 @@
+query Apies($limit: Int) {
+ apies(limit: $limit, orderBy: timestamp_DESC) {
+ id
+ timestamp
+ apy7DayAvg
+ apy30DayAvg
+ }
+}
diff --git a/libs/oeth/swap/src/views/SwapView.tsx b/libs/oeth/swap/src/views/SwapView.tsx
index 756fcc0db..a9490a550 100644
--- a/libs/oeth/swap/src/views/SwapView.tsx
+++ b/libs/oeth/swap/src/views/SwapView.tsx
@@ -10,13 +10,14 @@ import {
Stack,
Typography,
} from '@mui/material';
-import { ApyHeader, GasPopover } from '@origin/oeth/shared';
+import { GasPopover } from '@origin/oeth/shared';
import { Card, TokenInput } from '@origin/shared/components';
import { ConnectedButton, usePrices } from '@origin/shared/providers';
import { composeContexts, isNilOrEmpty } from '@origin/shared/utils';
import { useIntl } from 'react-intl';
import { useAccount, useBalance } from 'wagmi';
+import { ApyChart } from '../components/ApyChart';
import { SwapRoute } from '../components/SwapRoute';
import { TokenSelectModal } from '../components/TokenSelectModal';
import { routeActionLabel } from '../constants';
@@ -154,7 +155,7 @@ function SwapViewWrapped() {
return (
<>
-
+
({
+ color: theme.palette.primary.contrastText,
'&.Mui-selected': {
backgroundColor: 'transparent',
color: theme.palette.text.secondary,
diff --git a/libs/shared/utils/src/formatters.ts b/libs/shared/utils/src/formatters.ts
index 3e02d1058..b75382e95 100644
--- a/libs/shared/utils/src/formatters.ts
+++ b/libs/shared/utils/src/formatters.ts
@@ -20,6 +20,11 @@ export const quantityFormat: FormatNumberOptions = {
maximumFractionDigits: 4,
};
+export const balanceFormat: FormatNumberOptions = {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+};
+
// Format map [value, maxDigits]
const mappings = [
[10000000, 0],
diff --git a/package.json b/package.json
index e8fd301f1..5bfd3eb8a 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,8 @@
"@wagmi/core": "^1.4.1",
"axios": "^1.5.0",
"chart.js": "^4.4.0",
+ "chartjs-adapter-date-fns": "^3.0.0",
+ "date-fns": "^2.30.0",
"graphql": "^16.8.0",
"immer": "^10.0.2",
"ramda": "^0.29.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index de0bdc9ab..54e8e07a5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -35,6 +35,12 @@ dependencies:
chart.js:
specifier: ^4.4.0
version: 4.4.0
+ chartjs-adapter-date-fns:
+ specifier: ^3.0.0
+ version: 3.0.0(chart.js@4.4.0)(date-fns@2.30.0)
+ date-fns:
+ specifier: ^2.30.0
+ version: 2.30.0
graphql:
specifier: ^16.8.0
version: 16.8.0
@@ -1703,12 +1709,6 @@ packages:
resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
dev: true
- /@babel/runtime@7.22.10:
- resolution: {integrity: sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==}
- engines: {node: '>=6.9.0'}
- dependencies:
- regenerator-runtime: 0.14.0
-
/@babel/runtime@7.22.15:
resolution: {integrity: sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==}
engines: {node: '>=6.9.0'}
@@ -1833,7 +1833,7 @@ packages:
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
dependencies:
'@babel/helper-module-imports': 7.22.15
- '@babel/runtime': 7.22.10
+ '@babel/runtime': 7.22.15
'@emotion/hash': 0.9.1
'@emotion/memoize': 0.8.1
'@emotion/serialize': 1.1.2
@@ -6439,7 +6439,7 @@ packages:
engines: {node: '>=14'}
dependencies:
'@babel/code-frame': 7.22.13
- '@babel/runtime': 7.22.10
+ '@babel/runtime': 7.22.15
'@types/aria-query': 5.0.1
aria-query: 5.1.3
chalk: 4.1.2
@@ -8425,7 +8425,7 @@ packages:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'}
dependencies:
- '@babel/runtime': 7.22.10
+ '@babel/runtime': 7.22.15
cosmiconfig: 7.1.0
resolve: 1.22.4
dev: false
@@ -8885,6 +8885,16 @@ packages:
'@kurkle/color': 0.3.2
dev: false
+ /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.0)(date-fns@2.30.0):
+ resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==}
+ peerDependencies:
+ chart.js: '>=2.8.0'
+ date-fns: '>=2.0.0'
+ dependencies:
+ chart.js: 4.4.0
+ date-fns: 2.30.0
+ dev: false
+
/check-error@1.0.2:
resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
dev: true
@@ -9309,6 +9319,13 @@ packages:
resolution: {integrity: sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==}
dev: true
+ /date-fns@2.30.0:
+ resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
+ engines: {node: '>=0.11'}
+ dependencies:
+ '@babel/runtime': 7.22.15
+ dev: false
+
/de-indent@1.0.2:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
dev: true