-
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.
[CoW AMM Deployer] Integrate chainlink contract into the application (#…
…677) * add custom oracle feature * chore: add bleu ui * remove unused context * chore: use bleu ui toaster * refactor: separete price oracle inputs into new files * chore: add npm token on github pnpm setup action * wip: move .npmrc to repo root * add sushi gql * chore: remove rainbowkit colors from milkman project * chore: add missing dependencies on balancer tools * add npm_token input on all workflows * add input into setup-pnpm workflow * chore: run formatter * add sushiswap pricechecker component * run lint * chore: rename @bleu-fi/ui to @bleu/ui due to org name change * remove bleu-ui from tsconfig * chore: use try and catch patern and change tooltip texts * add chainlink price oracle and rename other price oracle components * add chainlink information on manager page * fix: load current parameters on cow amm edit * fix: error message overload
- Loading branch information
1 parent
4640988
commit 968becb
Showing
17 changed files
with
1,117 additions
and
23 deletions.
There are no files selected for viewing
74 changes: 74 additions & 0 deletions
74
apps/cow-amm-deployer/src/app/manager/(components)/ChainlinkPriceInformation.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,74 @@ | ||
import { useSafeAppsSDK } from "@gnosis.pm/safe-apps-react-sdk"; | ||
import Link from "next/link"; | ||
import { useEffect, useState } from "react"; | ||
import { Address } from "viem"; | ||
import { gnosis, sepolia } from "viem/chains"; | ||
|
||
import { priceFeedAbi } from "#/lib/abis/priceFeed"; | ||
import { ICowAmm } from "#/lib/types"; | ||
import { ChainId, publicClientsFromIds } from "#/utils/chainsPublicClients"; | ||
|
||
export function ChainlinkPriceInformation({ cowAmm }: { cowAmm: ICowAmm }) { | ||
const { safe } = useSafeAppsSDK(); | ||
const [priceFeed0Link, setPriceFeed0Link] = useState<string>(); | ||
const [priceFeed1Link, setPriceFeed1Link] = useState<string>(); | ||
|
||
const fetchPriceFeedLinks = async () => { | ||
const [priceFeed0Link, priceFeed1Link] = await Promise.all([ | ||
getPriceFeedLink( | ||
safe.chainId as ChainId, | ||
cowAmm.priceOracleData.chainlinkPriceFeed0, | ||
), | ||
getPriceFeedLink( | ||
safe.chainId as ChainId, | ||
cowAmm.priceOracleData.chainlinkPriceFeed1, | ||
), | ||
]); | ||
setPriceFeed0Link(priceFeed0Link); | ||
setPriceFeed1Link(priceFeed1Link); | ||
}; | ||
|
||
useEffect(() => { | ||
fetchPriceFeedLinks(); | ||
}, []); | ||
return ( | ||
<div className="flex flex-row gap-x-1 items-start hover:text-foreground/90"> | ||
<span>Using price information from Chainlink</span> | ||
{priceFeed0Link && ( | ||
<Link | ||
href={priceFeed0Link} | ||
target="_blank" | ||
className="text-primary hover:text-primary/80 text-xs" | ||
> | ||
1 | ||
</Link> | ||
)} | ||
{priceFeed1Link && ( | ||
<Link | ||
href={priceFeed1Link} | ||
target="_blank" | ||
className="text-primary hover:text-primary/80 text-xs" | ||
> | ||
2 | ||
</Link> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
export async function getPriceFeedLink(chainId: ChainId, address?: Address) { | ||
if (!address) return; | ||
if (chainId === sepolia.id) return; | ||
const publicClient = publicClientsFromIds[chainId]; | ||
const priceFeedDescription = (await publicClient.readContract({ | ||
address: address, | ||
abi: priceFeedAbi, | ||
functionName: "description", | ||
})) as string; | ||
const priceFeedPageName = priceFeedDescription | ||
.replace(" / ", "-") | ||
.toLowerCase(); | ||
const chainName = chainId === gnosis.id ? "xdai" : "ethereum"; | ||
|
||
return `https://data.chain.link/feeds/${chainName}/mainnet/${priceFeedPageName}`; | ||
} |
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
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
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
87 changes: 87 additions & 0 deletions
87
apps/cow-amm-deployer/src/app/new/(components)/ChainlinkForm.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,87 @@ | ||
"use client"; | ||
|
||
import { toast } from "@bleu/ui"; | ||
import { useSafeAppsSDK } from "@gnosis.pm/safe-apps-react-sdk"; | ||
import { UseFormReturn } from "react-hook-form"; | ||
|
||
import { Input } from "#/components/Input"; | ||
import { CHAINS_ORACLE_ROUTER_FACTORY } from "#/lib/chainlinkPriceFeedRouter"; | ||
import { ammFormSchema } from "#/lib/schema"; | ||
import { ChainId } from "#/utils/chainsPublicClients"; | ||
|
||
const TOOLTIP_PRICE_FEED_TEXT = | ||
"The address of the Chainlink price feed that will be used as the price oracle for the second token. Both price feeds have to have the same token as base. Click on the load button to try to find a valid Chainlink price feed pair."; | ||
|
||
const TOOLTIP_PRICE_FEED_LINK = "https://data.chain.link/feeds"; | ||
|
||
export function ChainlinkForm({ | ||
form, | ||
}: { | ||
form: UseFormReturn<typeof ammFormSchema._type>; | ||
}) { | ||
const { register, setValue, watch } = form; | ||
const { | ||
safe: { chainId }, | ||
} = useSafeAppsSDK(); | ||
|
||
const token0 = watch("token0"); | ||
const token1 = watch("token1"); | ||
return ( | ||
<div className="flex flex-col gap-y-1"> | ||
<div className="flex h-fit justify-between gap-x-7"> | ||
<div className="w-full"> | ||
<Input | ||
{...register("chainlinkPriceFeed0")} | ||
label="First Token Price Feed" | ||
tooltipText={TOOLTIP_PRICE_FEED_TEXT} | ||
tooltipLink={TOOLTIP_PRICE_FEED_LINK} | ||
/> | ||
</div> | ||
<div className="w-full"> | ||
<Input | ||
{...register("chainlinkPriceFeed1")} | ||
label="Second Token Price Feed" | ||
tooltipText={TOOLTIP_PRICE_FEED_TEXT} | ||
tooltipLink={TOOLTIP_PRICE_FEED_LINK} | ||
/> | ||
</div> | ||
</div> | ||
<button | ||
type="button" | ||
className="flex flex-row outline-none hover:text-highlight text-xs my-1" | ||
onClick={async () => { | ||
try { | ||
const oracleRouterFactory = | ||
CHAINS_ORACLE_ROUTER_FACTORY[chainId as ChainId]; | ||
const oracleRouter = new oracleRouterFactory({ | ||
chainId: chainId as ChainId, | ||
token0, | ||
token1, | ||
}); | ||
|
||
const { priceFeedToken0, priceFeedToken1 } = | ||
await oracleRouter.findRoute(); | ||
setValue("chainlinkPriceFeed0", priceFeedToken0); | ||
setValue("chainlinkPriceFeed1", priceFeedToken1); | ||
} catch (e) { | ||
toast({ | ||
title: "Price feed not found", | ||
description: | ||
"No Chainlink price feed pair found for these tokens", | ||
variant: "destructive", | ||
}); | ||
} | ||
}} | ||
> | ||
Load Chainlink Price Feeds | ||
</button> | ||
<Input | ||
{...register("chainlinkTimeThresholdInHours")} | ||
type="number" | ||
label="Maximum time since last price feed update (hours)" | ||
tooltipText="If the price feed is older than this value, the order will be rejected" | ||
defaultValue={24} | ||
/> | ||
</div> | ||
); | ||
} |
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
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
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
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
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
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,80 @@ | ||
export const priceFeedAbi = [ | ||
{ | ||
inputs: [ | ||
{ internalType: "uint8", name: "_decimals", type: "uint8" }, | ||
{ internalType: "string", name: "_description", type: "string" }, | ||
{ internalType: "uint256", name: "_version", type: "uint256" }, | ||
{ internalType: "int256", name: "_price", type: "int256" }, | ||
], | ||
stateMutability: "nonpayable", | ||
type: "constructor", | ||
}, | ||
{ | ||
inputs: [], | ||
name: "decimals", | ||
outputs: [{ internalType: "uint8", name: "", type: "uint8" }], | ||
stateMutability: "view", | ||
type: "function", | ||
}, | ||
{ | ||
inputs: [], | ||
name: "description", | ||
outputs: [{ internalType: "string", name: "", type: "string" }], | ||
stateMutability: "view", | ||
type: "function", | ||
}, | ||
{ | ||
inputs: [{ internalType: "uint80", name: "", type: "uint80" }], | ||
name: "getRoundData", | ||
outputs: [ | ||
{ internalType: "uint80", name: "roundId", type: "uint80" }, | ||
{ internalType: "int256", name: "currentPrice", type: "int256" }, | ||
{ internalType: "uint256", name: "startedAt", type: "uint256" }, | ||
{ internalType: "uint256", name: "currentUpdatedAt", type: "uint256" }, | ||
{ internalType: "uint80", name: "answeredInRound", type: "uint80" }, | ||
], | ||
stateMutability: "view", | ||
type: "function", | ||
}, | ||
{ | ||
inputs: [], | ||
name: "latestRoundData", | ||
outputs: [ | ||
{ internalType: "uint80", name: "roundId", type: "uint80" }, | ||
{ internalType: "int256", name: "answer", type: "int256" }, | ||
{ internalType: "uint256", name: "startedAt", type: "uint256" }, | ||
{ internalType: "uint256", name: "currentUpdatedAt", type: "uint256" }, | ||
{ internalType: "uint80", name: "answeredInRound", type: "uint80" }, | ||
], | ||
stateMutability: "view", | ||
type: "function", | ||
}, | ||
{ | ||
inputs: [], | ||
name: "price", | ||
outputs: [{ internalType: "int256", name: "", type: "int256" }], | ||
stateMutability: "view", | ||
type: "function", | ||
}, | ||
{ | ||
inputs: [{ internalType: "int256", name: "_price", type: "int256" }], | ||
name: "setPrice", | ||
outputs: [], | ||
stateMutability: "nonpayable", | ||
type: "function", | ||
}, | ||
{ | ||
inputs: [], | ||
name: "updatedAt", | ||
outputs: [{ internalType: "uint256", name: "", type: "uint256" }], | ||
stateMutability: "view", | ||
type: "function", | ||
}, | ||
{ | ||
inputs: [], | ||
name: "version", | ||
outputs: [{ internalType: "uint256", name: "", type: "uint256" }], | ||
stateMutability: "view", | ||
type: "function", | ||
}, | ||
]; |
Oops, something went wrong.