Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,14 @@
## Overview

Monarch is an unofficial user interface designed for composing custom lending strategies on [Morpho Blue](https://github.com/morpho-org/morpho-blue). It enables you to compose your own lending strategies by bundling multiple markets, each with your defined risk parameters. Access Morpho Blue directly without intermediaries, maintaining full control over your lending positions.

## Core Features

🔍 **Market Analysis**: Easily evaluate risk of markets with real-time data, interactive graphs, and oracle breakdown

💱 **Smart Reallocation**: Move funds seamlessly between markets to maximize yield

📊 **Performance Report**: Generate detailed earnings reports from any selected period, aggregating all market positions into a single view

🤖 **Automation** (Beta): Delegate operations to [Monarch Agents](https://github.com/monarch-xyz/monarch-agents) to automate reallocations

2 changes: 1 addition & 1 deletion app/history/components/HistoryContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function HistoryContent({ account }: { account: string }) {
return (
<div className="flex flex-col justify-between font-zen">
<Header />
<div className="container gap-8" style={{ padding: '0 5%' }}>
<div className="container gap-8 px-[5%]">
<h1 className="py-4 font-zen text-2xl">Transaction History</h1>

{loading ? (
Expand Down
8 changes: 4 additions & 4 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ export default function RootLayout({ children }: { children: React.ReactNode })
return (
<html lang="en" className={`${zen.variable} ${inter.variable} ${monospace.variable}`}>
<body>
<ClientProviders>
<Providers>
<Providers>
<ClientProviders>
<OnchainProviders>
{children}
<RiskNotificationModal />
</OnchainProviders>
</Providers>
</ClientProviders>
</ClientProviders>
</Providers>
</body>
<GoogleAnalytics />
</html>
Expand Down
2 changes: 1 addition & 1 deletion app/markets/components/markets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ export default function Markets() {
return (
<div className="flex w-full flex-col justify-between font-zen">
<Header />
<div className="container h-full gap-8" style={{ padding: '0 5%' }}>
<div className="container h-full gap-8 px-[5%]">
<h1 className="py-8 font-zen"> Markets </h1>

{showSupplyModal && (
Expand Down
8 changes: 4 additions & 4 deletions app/positions/components/PositionsContent.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
'use client';

import { useMemo, useState } from 'react';
import { Name } from '@coinbase/onchainkit/identity';
import Link from 'next/link';
import { useParams } from 'next/navigation';
import { FaHistory, FaPlus, FaCircle } from 'react-icons/fa';
import { TbReport } from 'react-icons/tb';
import { useAccount } from 'wagmi';
import { Avatar } from '@/components/Avatar/Avatar';
import { Button } from '@/components/common/Button';
import { Name } from '@/components/common/Name';
import Header from '@/components/layout/header/Header';
import EmptyScreen from '@/components/Status/EmptyScreen';
import LoadingScreen from '@/components/Status/LoadingScreen';
Expand Down Expand Up @@ -45,9 +45,9 @@ export default function Positions() {
return (
<div className="flex flex-col justify-between font-zen">
<Header />
<div className="container gap-8" style={{ padding: '0 5%' }}>
<div className="mb-4 flex items-center">
<h1 className="font-zen text-2xl">Portfolio</h1>
<div className="container h-full gap-8 px-[5%]">
<div className="pb-4">
<h1 className="font-zen">Portfolio</h1>
</div>
<div className="flex flex-col items-center justify-between pb-4 sm:flex-row">
<div className="flex items-start gap-4">
Expand Down
1 change: 1 addition & 0 deletions app/positions/components/onboarding/AssetSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useMemo } from 'react';
// import { Tooltip } from '@nextui-org/tooltip';
import { motion } from 'framer-motion';
import Image from 'next/image';
import Link from 'next/link';
Expand Down
8 changes: 7 additions & 1 deletion app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import { ThemeProvider as NextThemesProvider } from 'next-themes';

export function Providers({ children }: { children: React.ReactNode }) {
return (
<NextThemesProvider disableTransitionOnChange attribute="class">
<NextThemesProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
themes={['light', 'dark']}
>
<NextUIProvider>{children}</NextUIProvider>
</NextThemesProvider>
);
Expand Down
11 changes: 4 additions & 7 deletions app/rewards/components/RewardContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ export default function Rewards() {
const { loading, markets } = useMarkets();
const { rewards, distributions, loading: loadingRewards } = useUserRewards(account);

console.log('distributions', distributions);

const marketRewards = useMemo(() => filterMarketRewards(rewards), [rewards]);
const uniformRewards = useMemo(() => filterUniformRewards(rewards), [rewards]);

Expand Down Expand Up @@ -53,11 +51,10 @@ export default function Rewards() {
return (
<div className="flex flex-col justify-between font-zen">
<Header />

<div className="container mt-4 gap-8" style={{ padding: '0 5%' }}>
<div className="pb-8">
<div className="font-zen text-3xl">Rewards</div>
<div className="pt-4 text-base text-gray-500">
<div className="container h-full gap-8 px-[5%]">
<div className="pb-4">
<h1 className="font-zen">Reward</h1>
<div className="pt-4 text-secondary">
Morpho offers multiple reward programs to incentivize user participation. Choose a
program type below to see more details.
</div>
Expand Down
8 changes: 4 additions & 4 deletions app/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ export default function SettingsPage() {
return (
<div className="flex w-full flex-col justify-between font-zen">
<Header />
<div className="container h-full gap-8" style={{ padding: '0 5%' }}>
<div className="container h-full gap-8 px-[5%]">
<h1 className="py-8 font-zen">Settings</h1>

<div className="flex flex-col gap-6">
{/* Transaction Settings Section */}
<div className="flex flex-col gap-4">
<h2 className="text-xl font-semibold text-primary">Transaction Settings</h2>
<h2 className="text font-monospace text-secondary">Transaction Settings</h2>

<div className="bg-surface rounded p-6">
<div className="flex items-center justify-between">
Expand All @@ -48,8 +48,8 @@ export default function SettingsPage() {
</div>

{/* Filter Settings Section */}
<div className="flex flex-col gap-4">
<h2 className="text-xl font-semibold text-primary">Filter Settings</h2>
<div className="flex flex-col gap-4 pt-4">
<h2 className="text font-monospace text-secondary">Filter Settings</h2>

<div className="bg-surface flex flex-col gap-6 rounded p-6">
{/* Group related settings with a subtle separator */}
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"test:coverage:open": "yarn test:coverage && open coverage/lcov-report/index.html"
},
"dependencies": {
"@coinbase/onchainkit": "0.10.0",
"@coinbase/wallet-sdk": "^3.9.1",
"@nextui-org/accordion": "^2.0.35",
"@nextui-org/button": "^2.0.34",
Expand Down
15 changes: 9 additions & 6 deletions src/OnchainProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,22 @@ if (!projectId) {

const wagmiConfig = createWagmiConfig(projectId);

/**
* Monarch
* TODO Docs ~~~
*/
function OnchainProviders({ children }: Props) {
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<RainbowKitProvider
theme={{
lightMode: lightTheme(),
darkMode: darkTheme(),
lightMode: lightTheme({
accentColor: '#f45f2d',
borderRadius: 'small',
}),
darkMode: darkTheme({
accentColor: '#f45f2d',
borderRadius: 'small',
}),
}}
modalSize="compact"
>
{children}
</RainbowKitProvider>
Expand Down
1 change: 0 additions & 1 deletion src/components/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const sizeClasses = {
const variantStyles = {
default: (isSelected: boolean) => [
isSelected ? 'bg-hovered hover:bg-surface z-10' : 'bg-surface hover:bg-hovered',
'border border-divider',
'shadow-sm',
],
primary: (isSelected: boolean) => [
Expand Down
2 changes: 1 addition & 1 deletion src/components/SearchOrConnect/SearchOrConnect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function SearchOrConnect({ path }: { path: string }) {
return (
<div className="flex flex-col justify-between font-zen">
<Header />
<div className="container items-center justify-center gap-8" style={{ padding: '0 5%' }}>
<div className="container items-center justify-center gap-8 px-[5%]">
<div className="flex justify-center py-14">
<div className="w-full items-center rounded-md p-12 text-center text-lg text-secondary">
Connect wallet or search an account to continue.
Expand Down
23 changes: 23 additions & 0 deletions src/components/common/Name.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';

import { useEnsName } from 'wagmi';

type NameProps = {
address: `0x${string}`;
className?: string;
};

export function Name({ address, className = '' }: NameProps) {
const { data: ensName, isLoading } = useEnsName({
address,
chainId: 1,
});
Comment thread
antoncoding marked this conversation as resolved.

if (isLoading) {
return <span className={className}>...</span>;
}

return (
<span className={className}>{ensName ?? `${address.slice(0, 6)}...${address.slice(-4)}`}</span>
);
}
11 changes: 4 additions & 7 deletions src/components/layout/header/AccountConnect.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ConnectButton } from '@rainbow-me/rainbowkit';
import '@rainbow-me/rainbowkit/styles.css';
import { Button } from '@/components/common';
import { AccountDropdown } from './AccountDropdown';

/**
Expand Down Expand Up @@ -34,13 +35,9 @@ function AccountConnect() {
{(() => {
if (!connected) {
return (
<button
onClick={openConnectModal}
type="button"
className="inline-flex h-10 flex-grow items-center justify-center gap-2 rounded bg-white px-4 py-2"
>
<div className="text-sm font-medium leading-normal text-black">Connect</div>
</button>
<Button onClick={openConnectModal} type="button" variant="cta">
Connect
</Button>
);
}

Expand Down
4 changes: 3 additions & 1 deletion src/components/layout/header/AccountDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { useCallback, useState } from 'react';
import { Name } from '@coinbase/onchainkit/identity';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { ExitIcon, ExternalLinkIcon } from '@radix-ui/react-icons';
import { clsx } from 'clsx';
Expand All @@ -8,6 +9,7 @@ import { usePathname } from 'next/navigation';
import { FiSettings } from 'react-icons/fi';
import { useAccount, useDisconnect } from 'wagmi';
import { Avatar } from '@/components/Avatar/Avatar';
import { Name } from '@/components/common/Name';
import { getSlicedAddress } from '@/utils/address';
import { getExplorerURL } from '@/utils/external';

Expand Down
20 changes: 0 additions & 20 deletions src/components/layout/header/Navbar.test.tsx

This file was deleted.

21 changes: 16 additions & 5 deletions src/components/layout/header/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
'use client';

import { useEffect, useState } from 'react';
import { clsx } from 'clsx';
import Image from 'next/image';
import NextLink from 'next/link';
import { usePathname } from 'next/navigation';
import { useTheme } from 'next-themes';
import { FaRegMoon, FaSun } from 'react-icons/fa';
import { FaRegMoon } from 'react-icons/fa';
import { LuSunMedium } from 'react-icons/lu';

import { useAccount } from 'wagmi';
import logo from '../../imgs/logo.png';
import AccountConnect from './AccountConnect';
Expand Down Expand Up @@ -62,6 +65,15 @@ export function NavbarTitle() {
export function Navbar() {
const { theme, setTheme } = useTheme();
const { address } = useAccount();
const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

const toggleTheme = () => {
setTheme(theme === 'dark' ? 'light' : 'dark');
};

return (
<nav className="bg-surface flex h-full w-full items-center justify-between rounded px-4 font-zen">
Expand All @@ -74,17 +86,16 @@ export function Navbar() {
<NavbarLink href={`/rewards/${address ?? ''}`} matchKey="/rewards">
Rewards
</NavbarLink>
{/* <NavbarLink href="/settings/faq">FAQ</NavbarLink> */}
</div>

<div className="flex items-center gap-4">
<button
type="button"
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
className="hover:bg-hover flex h-10 w-10 items-center justify-center rounded-full"
onClick={toggleTheme}
className="flex h-10 w-10 items-center justify-center rounded-full"
aria-label="Toggle theme"
>
{theme === 'dark' ? <FaSun size={20} /> : <FaRegMoon size={20} />}
{mounted && (theme === 'dark' ? <LuSunMedium size={24} /> : <FaRegMoon size={20} />)}
</button>
<AccountConnect />
</div>
Expand Down
31 changes: 0 additions & 31 deletions src/components/layout/header/NavbarMobile.test.tsx

This file was deleted.

2 changes: 0 additions & 2 deletions src/hooks/useTransactionWithToast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ export function useTransactionWithToast({
hash,
});

console.log('isConfirmed', isConfirmed, toastId, hash);

const onClick = useCallback(() => {
if (hash) {
// if chainId is not supported, use 1
Expand Down
Loading