Skip to content

Commit

Permalink
[CoW Amm Deployer] Check and set fallback handler domain verifier (#598)
Browse files Browse the repository at this point in the history
* chore: remove unused milkman components

* add create amm form

* chore: remove milkmapp related files

* add fallback and domain verifier alert

* fix PR comments

* [CoW Amm Deployer] Create and enable module (#599)

* Update safe app info

* feat: add module creation

* run formatter

* Update apps/cow-amm-deployer/public/manifest.json

Co-authored-by: José Ribeiro <[email protected]>

* fix PR comments

* run formatter

---------

Co-authored-by: José Ribeiro <[email protected]>

---------

Co-authored-by: José Ribeiro <[email protected]>
  • Loading branch information
yvesfracari and ribeirojose authored Feb 23, 2024
1 parent dfdf0de commit c21d49d
Show file tree
Hide file tree
Showing 24 changed files with 1,345 additions and 1,790 deletions.
4 changes: 2 additions & 2 deletions apps/cow-amm-deployer/public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "Milkman",
"description": "UI for Milkman orders",
"name": "CoW AMM Manager",
"description": "UI to manage your CoW AMM",
"iconPath": "favicon.ico"
}
159 changes: 95 additions & 64 deletions apps/cow-amm-deployer/src/app/amms/(components)/CreateAmmForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@ import { useSafeAppsSDK } from "@gnosis.pm/safe-apps-react-sdk";
import { zodResolver } from "@hookform/resolvers/zod";
import { slateDarkA } from "@radix-ui/colors";
import { InfoCircledIcon } from "@radix-ui/react-icons";
import { FieldValues, useForm, UseFormReturn } from "react-hook-form";
import { useEffect, useState } from "react";
import { useForm, UseFormReturn } from "react-hook-form";

import { Button } from "#/components";
import { Input } from "#/components/Input";
import { Select, SelectItem } from "#/components/Select";
import { Spinner } from "#/components/Spinner";
import { Tooltip } from "#/components/Tooltip";
import { Form, FormMessage } from "#/components/ui/form";
import { Label } from "#/components/ui/label";
import { useFallbackState } from "#/hooks/useFallbackState";
import { useRawTxData } from "#/hooks/useRawTxData";
import { createAmmSchema } from "#/lib/schema";
import { ChainId } from "#/utils/chainsPublicClients";
import { createAMMArgs } from "#/lib/transactionFactory";
import { ChainId, publicClientsFromIds } from "#/utils/chainsPublicClients";
import { cowTokenList } from "#/utils/cowTokenList";

import { PRICE_ORACLES } from "../utils/type";
import { FALLBACK_STATES, PRICE_ORACLES } from "../utils/type";
import { FallbackAndDomainWarning } from "./FallbackAndDomainWarning";
import { IToken, TokenSelect } from "./TokenSelect";

const getDefaultData = (chainId: ChainId) => {
Expand All @@ -32,7 +38,7 @@ const getDefaultData = (chainId: ChainId) => {

export function CreateAmmForm() {
const {
safe: { chainId },
safe: { chainId, safeAddress },
} = useSafeAppsSDK();
const form = useForm<typeof createAmmSchema._type>({
resolver: zodResolver(createAmmSchema),
Expand All @@ -44,72 +50,97 @@ export function CreateAmmForm() {
watch,
formState: { errors },
} = form;
const { fallbackState, domainSeparator } = useFallbackState();
const { sendTransactions } = useRawTxData();
const [confirmedFallbackSetup, setConfirmedFallbackSetup] = useState(false);

const token0 = watch("token0");
const token1 = watch("token1");

const onSubmit = (data: FieldValues) => {
// TODO: Implement this
// eslint-disable-next-line no-console
console.log("submit", data);
const onSubmit = async (data: typeof createAmmSchema._type) => {
const publicClient = publicClientsFromIds[chainId as ChainId];
await createAMMArgs(data, publicClient).then((txArgs) => {
sendTransactions(txArgs);
});
};

useEffect(() => {
if (fallbackState && domainSeparator) {
setValue("domainSeparator", domainSeparator);
setValue("fallbackSetupState", fallbackState);
}
}, [fallbackState, setValue]);

useEffect(() => {
setValue("safeAddress", safeAddress);
}, [safeAddress, setValue]);

if (!fallbackState || !domainSeparator) {
return <Spinner />;
}

return (
<Form {...form} onSubmit={onSubmit} className="flex flex-col gap-y-6 p-9">
<div>
<div className="flex flex-col h-fit justify-between gap-y-7">
<div className="flex h-fit justify-between gap-x-7">
<div className="w-full flex flex-col">
<TokenSelect
onSelectToken={(token: IToken) => {
setValue("token0", {
decimals: token.decimals,
address: token.address,
symbol: token.symbol,
});
}}
label="First Token"
tokenType="sell"
selectedToken={token0 ?? undefined}
/>
{errors.token0 && (
<FormMessage className="mt-1 h-6 text-sm text-tomato10">
<span>{errors.token0.message}</span>
</FormMessage>
)}
</div>
<div className="w-full flex flex-col">
<TokenSelect
onSelectToken={(token: IToken) => {
setValue("token1", {
decimals: token.decimals,
address: token.address,
symbol: token.symbol,
});
}}
label="Second Token"
tokenType="sell"
selectedToken={token1 ?? undefined}
/>
{errors.token1 && (
<FormMessage className="mt-1 h-6 text-sm text-tomato10">
<span>{errors.token1.message}</span>
</FormMessage>
)}
</div>
</div>
<Input
label="Minimum traded first token"
type="number"
step={10 ** -token0.decimals}
name="minTradedToken0"
<Form {...form} onSubmit={onSubmit} className="flex flex-col gap-y-3 p-9">
<div className="flex h-fit justify-between gap-x-7">
<div className="w-full flex flex-col">
<TokenSelect
onSelectToken={(token: IToken) => {
setValue("token0", {
decimals: token.decimals,
address: token.address,
symbol: token.symbol,
});
}}
label="First Token"
selectedToken={token0 ?? undefined}
/>
<PriceOracleFields form={form} />
<div className="flex justify-center gap-x-5">
<Button type="submit" className="w-full">
<span>Create AMM</span>
</Button>
</div>
{errors.token0 && (
<FormMessage className="mt-1 h-6 text-sm text-tomato10">
<span>{errors.token0.message}</span>
</FormMessage>
)}
</div>
<div className="w-full flex flex-col">
<TokenSelect
onSelectToken={(token: IToken) => {
setValue("token1", {
decimals: token.decimals,
address: token.address,
symbol: token.symbol,
});
}}
label="Second Token"
selectedToken={token1 ?? undefined}
/>
{errors.token1 && (
<FormMessage className="mt-1 h-6 text-sm text-tomato10">
<span>{errors.token1.message}</span>
</FormMessage>
)}
</div>
</div>
<Input
label="Minimum amount of the first token to be traded on each order"
type="number"
step={10 ** -token0.decimals}
name="minTradedToken0"
/>
<PriceOracleFields form={form} />
<FallbackAndDomainWarning
confirmedFallbackSetup={confirmedFallbackSetup}
setConfirmedFallbackSetup={setConfirmedFallbackSetup}
/>
<div className="flex justify-center gap-x-5">
<Button
type="submit"
className="w-full"
disabled={
fallbackState != FALLBACK_STATES.HAS_DOMAIN_VERIFIER &&
!confirmedFallbackSetup
}
>
<span>Create AMM</span>
</Button>
</div>
</Form>
);
Expand All @@ -130,7 +161,7 @@ function PriceOracleFields({
const priceOracle = watch("priceOracle");

return (
<div className="flex flex-col justify-between gap-y-7">
<div className="flex flex-col justify-between gap-y-3">
<div>
<div className="flex gap-x-2 items-center">
<Label>Price checker</Label>
Expand Down Expand Up @@ -164,7 +195,7 @@ function PriceOracleFields({
<Input label="Balancer Pool Id" {...register("balancerPoolId")} />
)}
{priceOracle === PRICE_ORACLES.UNI && (
<Input label="Uniswap V2 Pool Id" {...register("uniswapV2Pair")} />
<Input label="Uniswap V2 Pool Address" {...register("uniswapV2Pair")} />
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { AlertCard } from "#/components/AlertCard";
import { Checkbox } from "#/components/Checkbox";

export function FallbackAndDomainWarning({
confirmedFallbackSetup,
setConfirmedFallbackSetup,
}: {
confirmedFallbackSetup: boolean;
setConfirmedFallbackSetup: (value: boolean) => void;
}) {
return (
<AlertCard style="warning" title="Fallback Setting">
<Checkbox
id="set"
checked={confirmedFallbackSetup}
onChange={() => setConfirmedFallbackSetup(!confirmedFallbackSetup)}
label="Approve fallback and domain verifier setup"
/>
</AlertCard>
);
}
31 changes: 2 additions & 29 deletions apps/cow-amm-deployer/src/app/amms/(components)/TokenSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import Table from "#/components/Table";
import { Toast } from "#/components/Toast";
import { useSafeBalances } from "#/hooks/useSafeBalances";
import { ChainId, publicClientsFromIds } from "#/utils/chainsPublicClients";
import { cowTokenList } from "#/utils/cowTokenList";

export interface IToken {
address: string;
Expand All @@ -29,13 +28,11 @@ export interface IToken {

export function TokenSelect({
onSelectToken,
tokenType,
selectedToken,
label,
disabeld = false,
}: {
onSelectToken: (token: IToken) => void;
tokenType: "sell" | "buy";
selectedToken?: IToken;
label: string;
disabeld?: boolean;
Expand All @@ -62,9 +59,7 @@ export function TokenSelect({

return (
<Dialog
content={
<TokenModal onSelectToken={handleSelectToken} tokenType={tokenType} />
}
content={<TokenModal onSelectToken={handleSelectToken} />}
isOpen={open}
setIsOpen={setOpen}
>
Expand Down Expand Up @@ -123,35 +118,13 @@ export function TokenSelectButton({

function TokenModal({
onSelectToken,
tokenType,
}: {
onSelectToken: (token: TokenBalance) => void;
tokenType: "sell" | "buy";
}) {
const {
safe: { chainId },
} = useSafeAppsSDK();
const [tokens, setTokens] = useState<(TokenBalance | undefined)[]>(
tokenType === "buy"
? cowTokenList
.filter((token) => token.chainId === chainId)
.map((token) => {
return {
balance: "0",
fiatBalance: "0",
fiatConversion: "0",
tokenInfo: {
address: token.address,
decimals: token.decimals,
name: token.name,
symbol: token.symbol,
logoUri: token.logoURI,
type: TokenType.ERC20,
},
};
})
: [],
);
const [tokens, setTokens] = useState<(TokenBalance | undefined)[]>([]);

const { assets, loaded } = useSafeBalances();
useEffect(() => {
Expand Down
3 changes: 3 additions & 0 deletions apps/cow-amm-deployer/src/app/amms/[network]/new/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Layout({ children }: { children: React.ReactNode }) {
return children;
}
6 changes: 6 additions & 0 deletions apps/cow-amm-deployer/src/app/amms/utils/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ export enum PRICE_ORACLES {
BALANCER = "Balancer",
UNI = "Uniswap",
}

export enum FALLBACK_STATES {
HAS_DOMAIN_VERIFIER = "HAS_DOMAIN_VERIFIER",
HAS_EXTENSIBLE_FALLBACK = "HAS_EXTENSIBLE_FALLBACK",
HAS_NOTHING = "HAS_NOTHING",
}
2 changes: 1 addition & 1 deletion apps/cow-amm-deployer/src/components/AlertCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function AlertCard({
children?: React.ReactNode;
}) {
return (
<div className="max-w-md" role="alert">
<div className="w-full justify-center" role="alert">
<div
className={cn(
"font-bold text-slate12 rounded-t px-4 py-2 mt-1",
Expand Down
5 changes: 1 addition & 4 deletions apps/cow-amm-deployer/src/components/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ export function Checkbox({ id, checked, onChange, label }: ICheckbox) {
<CheckIcon />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
<label
htmlFor={id}
className="pl-[15px] text-[15px] leading-8 text-white"
>
<label htmlFor={id} className="pl-[15px] text-[15px] leading-8">
{label}
</label>
</div>
Expand Down
Loading

0 comments on commit c21d49d

Please sign in to comment.