Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update: supply modal #5

Merged
merged 2 commits into from
Jan 15, 2025
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
107 changes: 64 additions & 43 deletions src/components/element/participate/Interact.client.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { Opportunity } from "@merkl/api";
import type { InteractionTarget } from "@merkl/api/dist/src/modules/v4/interaction/interaction.model";
import {
Box,
Button,
type ButtonProps,
Checkbox,
Collapsible,
Divider,
Dropdown,
Group,
Icon,
PrimitiveTag,
Expand All @@ -19,6 +18,7 @@ import { useMemo, useState } from "react";
import useBalances from "../../../hooks/useBalances";
import useInteractionTransaction from "../../../hooks/useInteractionTransaction";
import Token from "../token/Token";
import TransactionOverview from "../transaction/TransactionOverview";

export type InteractProps = {
opportunity: Opportunity;
Expand All @@ -42,7 +42,7 @@ export default function Interact({
target,
disabled,
}: InteractProps) {
const { chainId, switchChain, address: user, sponsorTransactions, setSponsorTransactions } = useWalletContext();
const { chainId, switchChain, address: user } = useWalletContext();
const {
transaction,
reload,
Expand Down Expand Up @@ -90,6 +90,15 @@ export default function Interact({
</>
),
});
else if (transaction.approved && !transaction.transaction)
createProps({
disabled: true,
children: (
<>
<Icon remix="RiProhibitedLine" /> An error occured
</>
),
});

// biome-ignore lint/suspicious/noExplicitAny: <explanation>
if (buttonProps) return <Button {...(buttonProps as any)} />;
Expand Down Expand Up @@ -145,59 +154,71 @@ export default function Interact({
if (target.provider === "enso")
return (
<>
<Icon src="https://framerusercontent.com/images/19ye5oms8sG6XHF1K8p03vLNkg.png" /> Enso
<Dropdown
content={
<Group className="flex-col max-w-[42ch]">
<Text size="sm">
Enso provides abstract on-chain actions, shortcuts and routes that allows dApps to find the best
routes to interact with other protocols.
</Text>
<Divider look="soft" horizontal />
<Group className="flex-col">
<Button to={"https://www.enso.build/"} size="xs" look="soft">
<Icon remix="RiArrowRightLine" /> Visit Enso
</Button>
</Group>
</Group>
}>
<PrimitiveTag size="sm">
<Icon src="https://framerusercontent.com/images/19ye5oms8sG6XHF1K8p03vLNkg.png" /> Enso
</PrimitiveTag>
</Dropdown>
</>
);
if (target.provider === "zap")
return (
<>
<Icon src="https://docs.kyberswap.com/~gitbook/image?url=https%3A%2F%2F1368568567-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fw1XgQJc40kVeGUIxgI7c%252Ficon%252FYl1TDE5MQwDPbEsfCerK%252Fimage%2520%281%29.png%3Falt%3Dmedia%26token%3D3f984a53-8b11-4d1b-b550-193d82610e7b&width=32&dpr=1&quality=100&sign=a7af3e95&sv=2" />{" "}
Zap
<Dropdown
content={
<Group className="flex-col max-w-[42ch]">
<Text size="sm">
Zap enables users to effortlessly add liquidity into any concentrated liquidity protocol using any
tokens, thanks to the KyberSwap aggregator.
</Text>
<Divider look="soft" horizontal />
<Group className="flex-col">
<Button
to={"https://docs.kyberswap.com/kyberswap-solutions/kyberswap-zap-as-a-service"}
size="xs"
look="soft">
<Icon remix="RiArrowRightLine" /> Visit Kyberswap Zap
</Button>
</Group>
</Group>
}>
<PrimitiveTag size="sm">
<Icon src="https://docs.kyberswap.com/~gitbook/image?url=https%3A%2F%2F1368568567-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fw1XgQJc40kVeGUIxgI7c%252Ficon%252FYl1TDE5MQwDPbEsfCerK%252Fimage%2520%281%29.png%3Falt%3Dmedia%26token%3D3f984a53-8b11-4d1b-b550-193d82610e7b&width=32&dpr=1&quality=100&sign=a7af3e95&sv=2" />{" "}
Kyberswap Zap
</PrimitiveTag>
</Dropdown>
</>
);
}, [target]);

const canTransactionBeSponsored = opportunity.chainId === 324;
const [settingsCollapsed, setSettingsCollapsed] = useState<boolean>(false);

return (
<>
<Space size="sm" />
<Box content="sm" className="w-full !gap-0 !bg-main-2" look="base">
<Group className="w-full flex-nowrap">
<Group className="grow items-center">
{amount && inputToken && (
<Text className="flex animate-drop grow flex-nowrap items-center gap-md" size={6}>
Supply
<Token key={amount} className="animate-drop" token={inputToken} amount={amount} format="price" /> with{" "}
{providerIcon}
</Text>
)}
</Group>
<PrimitiveTag
onClick={() => setSettingsCollapsed(o => !o)}
size="sm"
look="base"
className="flex flex-nowrap gap-md">
<Icon remix="RiSettings3Line" />
<Icon
data-state={!settingsCollapsed ? "closed" : "opened"}
className={"transition duration-150 ease-out data-[state=opened]:rotate-180"}
remix="RiArrowDownSLine"
/>
</PrimitiveTag>
</Group>
<Collapsible state={[settingsCollapsed]}>
<Space size="md" />
{canTransactionBeSponsored && (
<Group className="justify-between w-full items-center">
<Text>Gasless</Text>
<Checkbox size="sm" state={[sponsorTransactions, setSponsorTransactions]} />
</Group>
)}
{settings}
</Collapsible>
</Box>
<TransactionOverview settings={settings} allowTxSponsoring={canTransactionBeSponsored}>
{amount && inputToken && (
<Text className="flex animate-drop grow flex-nowrap items-center gap-md" size={6}>
Supply
<Token key={amount} className="animate-drop" token={inputToken} amount={amount} format="price" /> with{" "}
{providerIcon}
</Text>
)}
</TransactionOverview>
<Space size="xl" />
{currentInteraction}
</>
Expand Down
38 changes: 29 additions & 9 deletions src/components/element/participate/Participate.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Opportunity } from "@merkl/api";
import { useLocation } from "@remix-run/react";
import { Button, Group, Icon, Input, PrimitiveTag, Text, Value } from "dappkit";
import { Collapsible } from "dappkit";
import { Box, Collapsible } from "dappkit";
import { useWalletContext } from "dappkit";
import { Fmt } from "dappkit";
import { Suspense, useMemo, useState } from "react";
Expand Down Expand Up @@ -43,6 +43,7 @@ export default function Participate({
const { link } = useOpportunity(opportunity);
const location = useLocation();
const isOnOpportunityPage = location.pathname.includes("/opportunities/");
const [success, setSuccess] = useState(false);

const { connected } = useWalletContext();

Expand Down Expand Up @@ -118,7 +119,10 @@ export default function Participate({
/>
<Suspense>
<Interact
onSuccess={() => setAmount(undefined)}
onSuccess={() => {
setAmount(undefined);
setSuccess(true);
}}
disabled={!loading && !targets?.length}
target={targets?.[0]}
slippage={slippage}
Expand Down Expand Up @@ -180,20 +184,36 @@ export default function Participate({
</Button>
</Group>
)}
{!!I18n.trad.get.pages.home.depositInformation && (
<Group className="rounded-md p-md bg-main-5 flex-nowrap items-start">
<Icon remix="RiInformation2Fill" className="text-lg text-accent-11 flex-shrink-0" />
<Text look="bold" size="xs">
{I18n.trad.get.pages.home.depositInformation}
</Text>
</Group>

{!loading && !!interactor && (
<Box look="soft" className="gap-xs bg-main-5">
<Group className="flex flex-nowrap">
<Icon coloring={"warn"} remix="RiErrorWarningFill" className="text-accent-11 flex-shrink-0" />
<Text size="sm">{I18n.trad.get.pages.home.depositInformation}</Text>
</Group>
</Box>
)}
{loading && (
<Group className="w-full justify-center">
<Icon remix="RiLoader2Line" className="animate-spin" />
</Group>
)}
<Collapsible state={[!!interactor]}>{interactor}</Collapsible>
<Collapsible state={[success]}>
<Box look="soft" className="gap-xs bg-main-5">
<Group>
<Icon coloring={"good"} remix="RiCheckboxCircleFill" className="text-accent-12" />
<Text look="bold" className="font-bold">
Deposit successful !
</Text>
</Group>
<Text size="sm">
Your liquidity is now earning rewards (if any are currently being distributed to this opportunity). You'll
soon be able to claim them directly from your dashboard. You can monitor your positions and withdraw your
liquidity anytime directly through the protocol app.
</Text>
</Box>
</Collapsible>
</>
);
}
3 changes: 2 additions & 1 deletion src/components/element/rewards/ClaimRewardsChainTableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Fmt } from "dappkit";
import { useMemo, useState } from "react";
import merklConfig from "../../../config";
import useReward from "../../../hooks/resources/useReward";
import { UserService } from "../../../modules/user/user.service";
import Tag from "../Tag";
import { ClaimRewardsChainRow } from "./ClaimRewardsChainTable";
import { ClaimRewardsTokenTable } from "./ClaimRewardsTokenTable";
Expand All @@ -30,7 +31,7 @@ export default function ClaimRewardsChainTableRow({
const [selectedTokens, setSelectedTokens] = useState<Set<string>>(new Set<string>());

const { address: user, chainId, switchChain } = useWalletContext();
const isUserRewards = useMemo(() => user === from, [user, from]);
const isUserRewards = useMemo(() => UserService.isSame(user, from), [user, from]);
const isAbleToClaim = useMemo(
() => isUserRewards && !reward.rewards.every(({ amount, claimed }) => amount === claimed),
[isUserRewards, reward],
Expand Down
78 changes: 78 additions & 0 deletions src/components/element/transaction/TransactionOverview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
Box,
Button,
Checkbox,
Collapsible,
Divider,
Dropdown,
Group,
Icon,
PrimitiveTag,
Space,
Text,
useWalletContext,
} from "dappkit";
import { type ReactNode, useState } from "react";

export interface TransactionOverviewProps {
gas?: number;
allowTxSponsoring?: boolean;
settings?: ReactNode;
children?: ReactNode;
}

export default function TransactionOverview({ allowTxSponsoring, settings, children }: TransactionOverviewProps) {
const { sponsorTransactions, setSponsorTransactions } = useWalletContext();
const [settingsCollapsed, setSettingsCollapsed] = useState<boolean>(false);

return (
<Box content="sm" className="w-full !gap-0 !bg-main-2" look="base">
<Group className="w-full flex-nowrap">
<Group className="grow items-center">{children}</Group>
<PrimitiveTag
onClick={() => setSettingsCollapsed(o => !o)}
size="sm"
look="base"
className="flex flex-nowrap gap-md">
<Icon remix="RiSettings3Line" />
<Icon
data-state={!settingsCollapsed ? "closed" : "opened"}
className={"transition duration-150 ease-out data-[state=opened]:rotate-180"}
remix="RiArrowDownSLine"
/>
</PrimitiveTag>
</Group>
<Collapsible state={[settingsCollapsed, setSettingsCollapsed]}>
<Space size="md" />
{allowTxSponsoring && (
<Group className="justify-between w-full items-center">
<Text className="flex flex-nowrap gap-md items-center">
Sponsor with{" "}
<Dropdown
content={
<Group className="flex-col max-w-[42ch]">
<Text size="sm">
Zyfi leverages ZKSync's native account abstraction to allow dApps simplify gas management for
dApps users.
</Text>
<Divider look="soft" horizontal />
<Group className="flex-col">
<Button to={"https://www.zyfi.org/"} size="xs" look="soft">
<Icon remix="RiArrowRightLine" /> Visit Zyfi
</Button>
</Group>
</Group>
}>
<PrimitiveTag size="sm">
<Icon src="https://www.zyfi.org/favicon.ico" /> Zyfi
</PrimitiveTag>
</Dropdown>
</Text>
<Checkbox size="sm" state={[sponsorTransactions, setSponsorTransactions]} />
</Group>
)}
{settings}
</Collapsible>
</Box>
);
}
3 changes: 2 additions & 1 deletion src/modules/user/routes/user.$address.header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import useReward from "../../../hooks/resources/useReward";
import useRewards from "../../../hooks/resources/useRewards";
import { RewardService } from "../../../modules/reward/reward.service";
import { TokenService } from "../../../modules/token/token.service";
import { UserService } from "../user.service";

export async function loader({ params: { address }, request }: LoaderFunctionArgs) {
if (!address || !isAddress(address)) throw "";
Expand Down Expand Up @@ -63,7 +64,7 @@ export default function Index() {
const reward = useMemo(() => rawRewards.find(({ chain: { id } }) => id === chainId), [chainId, rawRewards]);
const { claimTransaction } = useReward(reward, user);

const isUserRewards = useMemo(() => user === address, [user, address]);
const isUserRewards = useMemo(() => UserService.isSame(user, address), [user, address]);
const isAbleToClaim = useMemo(
() => isUserRewards && reward && !reward.rewards.every(({ amount, claimed }) => amount === claimed),
[isUserRewards, reward],
Expand Down
14 changes: 14 additions & 0 deletions src/modules/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { isAddressEqual } from "viem";

export abstract class UserService {
/**
* Compares addresses to check if they are equal
* @notice needed because addresse can be checksum or not
* @param a address
* @param b address
*/
static isSame(a?: string, b?: string): boolean {
if (a?.startsWith("0x") && b.startsWith("0x")) return isAddressEqual(a as `0x${string}`, b as `0x${string}`);
return false;
}
}
Loading