-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'cowamm-next' into jose/cow-270-create-transaction-pre-e…
…xecute-post-execute-flow
- Loading branch information
Showing
73 changed files
with
1,427 additions
and
3,101 deletions.
There are no files selected for viewing
89 changes: 89 additions & 0 deletions
89
apps/cow-amm-deployer/src/app/[userId]/amms/(components)/AmmsTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
"use client"; | ||
|
||
import { formatNumber } from "@bleu/ui"; | ||
import { useRouter } from "next/navigation"; | ||
|
||
import Table from "#/components/Table"; | ||
import { TokenInfo } from "#/components/TokenInfo"; | ||
import { ICoWAmmOverview } from "#/lib/fetchAmmData"; | ||
|
||
export function AmmsTable({ | ||
standaloneAmmData, | ||
userId, | ||
}: { | ||
standaloneAmmData: ICoWAmmOverview[]; | ||
userId: string; | ||
}) { | ||
const router = useRouter(); | ||
|
||
return ( | ||
<Table | ||
color="foreground" | ||
shade="darkWithBorder" | ||
classNames="overflow-y-auto text-background" | ||
> | ||
<Table.HeaderRow> | ||
<Table.HeaderCell>Token pair</Table.HeaderCell> | ||
<Table.HeaderCell> | ||
<span className="sr-only">Token pair</span> | ||
</Table.HeaderCell> | ||
<Table.HeaderCell>Value</Table.HeaderCell> | ||
<Table.HeaderCell>Status</Table.HeaderCell> | ||
<Table.HeaderCell>Updated at</Table.HeaderCell> | ||
</Table.HeaderRow> | ||
<Table.Body> | ||
{standaloneAmmData.length === 0 ? ( | ||
<Table.BodyRow> | ||
<Table.BodyCell colSpan={5} classNames="text-center" padding="py-2"> | ||
<span className="text-base">No AMMs created yet</span> | ||
</Table.BodyCell> | ||
</Table.BodyRow> | ||
) : ( | ||
standaloneAmmData.map((amm) => ( | ||
<Table.BodyRow | ||
key={amm.id} | ||
onClick={() => { | ||
router.push(`/${userId}/amms/${amm.id}`); | ||
}} | ||
classNames="hover:cursor-pointer hover:bg-foreground/50" | ||
> | ||
<Table.BodyCell padding="px-1"> | ||
<span className="text-base"> | ||
<TokenInfo token={amm.token0} /> | ||
</span> | ||
</Table.BodyCell> | ||
<Table.BodyCell padding="px-1"> | ||
<span className="text-base"> | ||
<TokenInfo token={amm.token1} /> | ||
</span> | ||
</Table.BodyCell> | ||
<Table.BodyCell> | ||
<span className="text-base"> | ||
$ {formatNumber(amm.totalUsdValue, 2)} | ||
</span> | ||
</Table.BodyCell> | ||
<Table.BodyCell> | ||
{amm.disabled ? ( | ||
<span className="bg-highlight rounded-full p-2 text-base"> | ||
Paused | ||
</span> | ||
) : ( | ||
<span className="bg-success rounded-full p-2 text-base"> | ||
Active | ||
</span> | ||
)} | ||
</Table.BodyCell> | ||
<Table.BodyCell> | ||
<span className="text-base"> | ||
{new Date( | ||
(amm.order.blockTimestamp as number) * 1000, | ||
).toLocaleString()} | ||
</span> | ||
</Table.BodyCell> | ||
</Table.BodyRow> | ||
)) | ||
)} | ||
</Table.Body> | ||
</Table> | ||
); | ||
} |
200 changes: 200 additions & 0 deletions
200
apps/cow-amm-deployer/src/app/[userId]/amms/[id]/(components)/EditAMMForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
"use client"; | ||
|
||
import { toast } from "@bleu/ui"; | ||
import { zodResolver } from "@hookform/resolvers/zod"; | ||
import { brownDark } from "@radix-ui/colors"; | ||
import { InfoCircledIcon } from "@radix-ui/react-icons"; | ||
import { useRouter } from "next/navigation"; | ||
import { useForm, UseFormReturn, useWatch } from "react-hook-form"; | ||
import { Address, formatUnits } from "viem"; | ||
|
||
import { Button } from "#/components"; | ||
import { BalancerWeightedForm } from "#/components/BalancerWeightedForm"; | ||
import { ChainlinkForm } from "#/components/ChainlinkForm"; | ||
import { CustomOracleForm } from "#/components/CustomOracleForm"; | ||
import { Input } from "#/components/Input"; | ||
import { SelectInput } from "#/components/SelectInput"; | ||
import { SushiForm } from "#/components/SushiForm"; | ||
import { TokenInfo } from "#/components/TokenInfo"; | ||
import { Tooltip } from "#/components/Tooltip"; | ||
import { | ||
Accordion, | ||
AccordionContent, | ||
AccordionItem, | ||
AccordionTrigger, | ||
} from "#/components/ui/accordion"; | ||
import { Form, FormMessage } from "#/components/ui/form"; | ||
import { Label } from "#/components/ui/label"; | ||
import { UniswapV2Form } from "#/components/UniswapV2Form"; | ||
import { useRawTxData } from "#/hooks/useRawTxData"; | ||
import { ICowAmm } from "#/lib/fetchAmmData"; | ||
import { ammEditSchema } from "#/lib/schema"; | ||
import { buildTxEditAMMArgs } from "#/lib/transactionFactory"; | ||
import { PRICE_ORACLES, PriceOraclesValue } from "#/lib/types"; | ||
import { cn } from "#/lib/utils"; | ||
|
||
export function EditAMMForm({ | ||
cowAmmData, | ||
submitButtonText, | ||
}: { | ||
cowAmmData: ICowAmm; | ||
submitButtonText: string; | ||
}) { | ||
const router = useRouter(); | ||
|
||
const form = useForm<typeof ammEditSchema._type>({ | ||
// @ts-ignore | ||
resolver: zodResolver(ammEditSchema), | ||
defaultValues: { | ||
safeAddress: cowAmmData.user.address, | ||
chainId: cowAmmData.chainId, | ||
token0: cowAmmData.token0, | ||
token1: cowAmmData.token1, | ||
minTradedToken0: Number( | ||
formatUnits(cowAmmData.minTradedToken0, cowAmmData.token0.decimals), | ||
), | ||
priceOracle: cowAmmData.decodedPriceOracleData[0], | ||
balancerPoolId: cowAmmData.decodedPriceOracleData[1].balancerPoolId, | ||
uniswapV2Pair: cowAmmData.decodedPriceOracleData[1].uniswapV2PairAddress, | ||
sushiV2Pair: cowAmmData.decodedPriceOracleData[1].sushiSwapPairAddress, | ||
chainlinkPriceFeed0: | ||
cowAmmData.decodedPriceOracleData[1].chainlinkPriceFeed0, | ||
chainlinkPriceFeed1: | ||
cowAmmData.decodedPriceOracleData[1].chainlinkPriceFeed1, | ||
chainlinkTimeThresholdInHours: | ||
cowAmmData.decodedPriceOracleData[1].chainlinkTimeThresholdInHours, | ||
customPriceOracleAddress: | ||
cowAmmData.decodedPriceOracleData[1].customPriceOracleAddress, | ||
customPriceOracleData: | ||
cowAmmData.decodedPriceOracleData[1].customPriceOracleData, | ||
}, | ||
}); | ||
|
||
const { | ||
formState: { errors, isSubmitting }, | ||
} = form; | ||
const { sendTransactions } = useRawTxData(); | ||
|
||
const onSubmit = async (data: typeof ammEditSchema._type) => { | ||
const txArgs = buildTxEditAMMArgs({ | ||
data: data, | ||
ammAddress: cowAmmData.order.owner as Address, | ||
}); | ||
|
||
try { | ||
await sendTransactions(txArgs); | ||
router.push(`/${cowAmmData.user.id}/amms/${cowAmmData.id}`); | ||
} catch { | ||
toast({ | ||
title: `Transaction failed`, | ||
description: "An error occurred while processing the transaction.", | ||
variant: "destructive", | ||
}); | ||
} | ||
}; | ||
|
||
return ( | ||
<Form {...form} onSubmit={onSubmit} className="flex flex-col gap-y-3"> | ||
<div className="flex flex-col w-full"> | ||
<span className="mb-2 h-5 block text-sm">Token Pair</span> | ||
<div className="flex h-fit gap-x-7"> | ||
<TokenInfo token={cowAmmData.token0} /> | ||
<TokenInfo token={cowAmmData.token1} /> | ||
</div> | ||
</div> | ||
<PriceOracleFields form={form} /> | ||
<Accordion className="w-full" type="single" collapsible> | ||
<AccordionItem value="advancedOptions" key="advancedOption"> | ||
<AccordionTrigger | ||
className={cn( | ||
errors.minTradedToken0 ? "text-destructive" : "", | ||
"pt-0", | ||
)} | ||
> | ||
Advanced Options | ||
</AccordionTrigger> | ||
<AccordionContent> | ||
<Input | ||
label="Minimum first token amount on each order" | ||
type="number" | ||
step={10 ** -cowAmmData.token0.decimals} | ||
name="minTradedToken0" | ||
tooltipText="This parameter is used to not overload the CoW Orderbook with small orders. By default, 10 dollars worth of the first token will be the minimum amount for each order." | ||
/> | ||
</AccordionContent> | ||
</AccordionItem> | ||
</Accordion> | ||
|
||
<div className="flex justify-center gap-x-5 mt-2"> | ||
<Button | ||
loading={isSubmitting} | ||
variant="highlight" | ||
type="submit" | ||
className="w-full" | ||
disabled={isSubmitting} | ||
> | ||
<span>{submitButtonText}</span> | ||
</Button> | ||
</div> | ||
</Form> | ||
); | ||
} | ||
|
||
function PriceOracleFields({ | ||
form, | ||
}: { | ||
form: UseFormReturn<typeof ammEditSchema._type>; | ||
}) { | ||
const { | ||
setValue, | ||
control, | ||
formState: { errors }, | ||
} = form; | ||
|
||
const priceOracle = useWatch({ control, name: "priceOracle" }); | ||
|
||
return ( | ||
<div className="flex flex-col gap-y-3"> | ||
<div className="flex flex-col gap-x-7"> | ||
<div className="w-full"> | ||
<div className="flex gap-x-2 mb-2"> | ||
<Label>Price Oracle Source</Label> | ||
<Tooltip | ||
content={ | ||
"The AMM relies on price oracle exclusively for generating orders that will plausibly be settled in the current market conditions" | ||
} | ||
> | ||
<InfoCircledIcon className="size-4" color={brownDark.brown8} /> | ||
</Tooltip> | ||
</div> | ||
<SelectInput | ||
name="priceOracle" | ||
options={Object.values(PRICE_ORACLES).map((value) => ({ | ||
id: value, | ||
value, | ||
}))} | ||
onValueChange={(priceOracle) => { | ||
setValue("priceOracle", priceOracle as PriceOraclesValue); | ||
}} | ||
placeholder={priceOracle} | ||
/> | ||
{errors.priceOracle && ( | ||
<FormMessage className="text-sm text-destructive w-full"> | ||
<p className="text-wrap"> | ||
{errors.priceOracle.message as string} | ||
</p> | ||
</FormMessage> | ||
)} | ||
</div> | ||
</div> | ||
|
||
{priceOracle === PRICE_ORACLES.BALANCER && ( | ||
<BalancerWeightedForm form={form} /> | ||
)} | ||
{priceOracle === PRICE_ORACLES.UNI && <UniswapV2Form form={form} />} | ||
{priceOracle === PRICE_ORACLES.CUSTOM && <CustomOracleForm form={form} />} | ||
{priceOracle === PRICE_ORACLES.SUSHI && <SushiForm form={form} />} | ||
{priceOracle === PRICE_ORACLES.CHAINLINK && <ChainlinkForm form={form} />} | ||
</div> | ||
); | ||
} |
File renamed without changes.
File renamed without changes.
66 changes: 66 additions & 0 deletions
66
apps/cow-amm-deployer/src/app/[userId]/amms/[id]/(components)/TradingControlButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
"use client"; | ||
|
||
import { toast } from "@bleu/ui"; | ||
import { useSafeAppsSDK } from "@gnosis.pm/safe-apps-react-sdk"; | ||
import { PlayIcon, StopIcon } from "@radix-ui/react-icons"; | ||
import Link from "next/link"; | ||
import React from "react"; | ||
|
||
import { Button } from "#/components/Button"; | ||
import { useRawTxData } from "#/hooks/useRawTxData"; | ||
import { ICowAmm } from "#/lib/fetchAmmData"; | ||
import { DisableCoWAMMArgs, TRANSACTION_TYPES } from "#/lib/transactionFactory"; | ||
import { ChainId } from "#/utils/chainsPublicClients"; | ||
|
||
export function TradingControlButton({ ammData }: { ammData: ICowAmm }) { | ||
if (ammData.disabled) { | ||
return <EnableAMMButton ammData={ammData} />; | ||
} | ||
return <DisableAmmButton ammData={ammData} />; | ||
} | ||
|
||
function EnableAMMButton({ ammData }: { ammData: ICowAmm }) { | ||
return ( | ||
<Link href={`/${ammData.user.id}/amms/${ammData.id}/enable`}> | ||
<Button className="flex items-center gap-1 py-3 px-6"> | ||
<PlayIcon /> | ||
Enable trading | ||
</Button> | ||
</Link> | ||
); | ||
} | ||
|
||
function DisableAmmButton({ ammData }: { ammData: ICowAmm }) { | ||
const { | ||
safe: { chainId }, | ||
} = useSafeAppsSDK(); | ||
const { sendTransactions } = useRawTxData(); | ||
|
||
async function onDisableAMM() { | ||
const txArgs = { | ||
type: TRANSACTION_TYPES.DISABLE_COW_AMM, | ||
amm: ammData.order.owner, | ||
chainId: chainId as ChainId, | ||
} as DisableCoWAMMArgs; | ||
try { | ||
await sendTransactions([txArgs]); | ||
} catch { | ||
toast({ | ||
title: `Transaction failed`, | ||
description: "An error occurred while processing the transaction.", | ||
variant: "destructive", | ||
}); | ||
} | ||
} | ||
|
||
return ( | ||
<Button | ||
className="flex items-center gap-1 py-3 px-6" | ||
variant="destructive" | ||
onClick={onDisableAMM} | ||
> | ||
<StopIcon /> | ||
Disable trading | ||
</Button> | ||
); | ||
} |
21 changes: 21 additions & 0 deletions
21
apps/cow-amm-deployer/src/app/[userId]/amms/[id]/edit/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { FormPageWrapper } from "#/components/FormPageWrapper"; | ||
import { fetchAmmData } from "#/lib/fetchAmmData"; | ||
|
||
import { EditAMMForm } from "../(components)/EditAMMForm"; | ||
|
||
export default async function Page({ | ||
params, | ||
}: { | ||
params: { userId: string; id: `0x${string}` }; | ||
}) { | ||
const ammData = await fetchAmmData(params.id); | ||
|
||
return ( | ||
<FormPageWrapper | ||
formTitle="Edit AMM" | ||
backHref={`/${params.userId}/amms/${params.id}`} | ||
> | ||
<EditAMMForm cowAmmData={ammData} submitButtonText="Edit AMM" /> | ||
</FormPageWrapper> | ||
); | ||
} |
Oops, something went wrong.