-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
62d3e13
commit 1f199ec
Showing
8 changed files
with
296 additions
and
181 deletions.
There are no files selected for viewing
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
301 changes: 130 additions & 171 deletions
301
packages/nextjs/app/pools/_components/PoolSelector.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 |
---|---|---|
@@ -1,189 +1,148 @@ | ||
import { useEffect, useState } from "react"; | ||
import { Dispatch, SetStateAction, useState } from "react"; | ||
import { usePathname, useRouter } from "next/navigation"; | ||
import { blo } from "blo"; | ||
import { type Address, isAddress } from "viem"; | ||
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; | ||
import { useScaffoldEventHistory, useScaffoldEventSubscriber } from "~~/hooks/scaffold-eth"; | ||
import { useFactoryHistory } from "~~/hooks/balancer"; | ||
|
||
// TODO: Figure out if this can be fetched from etherscan with contract address? | ||
const FROM_BLOCK_NUMBER = 6278000n; | ||
|
||
/** | ||
* The dropdown selector for internal custom pool and the external pool address input | ||
*/ | ||
export const PoolSelector = ({ | ||
setSelectedPoolAddress, | ||
selectedPoolAddress, | ||
}: { | ||
setSelectedPoolAddress: (_: Address) => void; | ||
type PoolSelectorProps = { | ||
setSelectedPoolAddress: Dispatch<SetStateAction<string | null>>; | ||
selectedPoolAddress: Address | null; | ||
}) => { | ||
}; | ||
|
||
export const PoolSelector = ({ setSelectedPoolAddress, selectedPoolAddress }: PoolSelectorProps) => { | ||
const [inputValue, setInputValue] = useState<string>(""); | ||
const [sumPools, setSumPools] = useState<Address[]>([]); | ||
const [productPools, setProductPools] = useState<Address[]>([]); | ||
|
||
const router = useRouter(); | ||
const pathname = usePathname(); | ||
const isValidAddress = isAddress(inputValue); | ||
const { sumPools, productPools, weightedPools } = useFactoryHistory(); | ||
|
||
// Fetches the history of pools deployed via factory | ||
const { data: sumPoolHistory, isLoading: isLoadingSumPoolHistory } = useScaffoldEventHistory({ | ||
contractName: "ConstantSumFactory", | ||
eventName: "PoolCreated", | ||
fromBlock: FROM_BLOCK_NUMBER, | ||
}); | ||
const poolTypes = [ | ||
{ label: "Constant Sum", addresses: sumPools }, | ||
{ label: "Constant Product", addresses: productPools }, | ||
{ label: "Weighted", addresses: weightedPools }, | ||
]; | ||
|
||
const { data: productPoolHistory, isLoading: isLoadingProductPoolHistory } = useScaffoldEventHistory({ | ||
contractName: "ConstantProductFactory", | ||
eventName: "PoolCreated", | ||
fromBlock: FROM_BLOCK_NUMBER, | ||
}); | ||
return ( | ||
<section className="mb-7"> | ||
<SearchBar | ||
inputValue={inputValue} | ||
setInputValue={setInputValue} | ||
setSelectedPoolAddress={setSelectedPoolAddress} | ||
/> | ||
<div className="flex flex-wrap justify-center gap-3 mt-4"> | ||
{poolTypes.map( | ||
({ label, addresses }) => | ||
addresses.length > 0 && | ||
addresses.map(address => ( | ||
<PoolSelectButton | ||
key={address} | ||
label={label} | ||
address={address} | ||
setInputValue={setInputValue} | ||
selectedPoolAddress={selectedPoolAddress} | ||
setSelectedPoolAddress={setSelectedPoolAddress} | ||
/> | ||
)), | ||
)} | ||
</div> | ||
</section> | ||
); | ||
}; | ||
|
||
type SearchBarProps = { | ||
setSelectedPoolAddress: (_: Address) => void; | ||
inputValue: string; | ||
setInputValue: Dispatch<SetStateAction<string>>; | ||
}; | ||
|
||
// Adds pools deployed using the factories | ||
useScaffoldEventSubscriber({ | ||
contractName: "ConstantSumFactory", | ||
eventName: "PoolCreated", | ||
listener: logs => { | ||
logs.forEach(log => { | ||
const { pool } = log.args; | ||
if (pool) { | ||
setSumPools(pools => [...pools, pool]); | ||
} | ||
}); | ||
}, | ||
}); | ||
const SearchBar = ({ setSelectedPoolAddress, inputValue, setInputValue }: SearchBarProps) => { | ||
const router = useRouter(); | ||
const pathname = usePathname(); | ||
const isValidAddress = isAddress(inputValue); | ||
|
||
// Adds pools deployed using the factories | ||
useScaffoldEventSubscriber({ | ||
contractName: "ConstantProductFactory", | ||
eventName: "PoolCreated", | ||
listener: logs => { | ||
logs.forEach(log => { | ||
const { pool } = log.args; | ||
if (pool) { | ||
setProductPools(pools => [...pools, pool]); | ||
} | ||
}); | ||
}, | ||
}); | ||
return ( | ||
<div className="flex justify-center flex-wrap gap-5 w-full items-center text-xl"> | ||
<form | ||
className="flex flex-wrap items-center gap-5" | ||
onSubmit={event => { | ||
event.preventDefault(); | ||
setSelectedPoolAddress(inputValue); | ||
router.push(`${pathname}?address=${inputValue}`); | ||
setInputValue(""); | ||
}} | ||
> | ||
<div className="relative"> | ||
{inputValue && ( | ||
// eslint-disable-next-line @next/next/no-img-element | ||
<img | ||
alt="" | ||
className="!rounded-full absolute top-1 left-1" | ||
src={blo(inputValue as `0x${string}`)} | ||
width="37" | ||
height="37" | ||
/> | ||
)} | ||
<input | ||
value={inputValue} | ||
onChange={e => setInputValue(e.target.value)} | ||
className={`input input-bordered bg-base-200 text-center h-[44px] w-[355px] sm:w-[550px] ${ | ||
inputValue && "pl-10 pr-14" | ||
}`} | ||
placeholder="Search by pool addresss" | ||
/> | ||
<button | ||
className={`btn btn-sm bg-neutral w-12 absolute top-1.5 right-1.5 border-none ${ | ||
isValidAddress ? "bg-violet-400 hover:bg-violet-400" : "" | ||
}`} | ||
type="submit" | ||
disabled={!isValidAddress} | ||
> | ||
<MagnifyingGlassIcon className="h-5 w-5" /> | ||
</button> | ||
</div> | ||
</form> | ||
</div> | ||
); | ||
}; | ||
|
||
useEffect(() => { | ||
if (!isLoadingSumPoolHistory && !isLoadingProductPoolHistory && sumPoolHistory && productPoolHistory) { | ||
const sumPools = sumPoolHistory | ||
.map(({ args }) => { | ||
if (args.pool && isAddress(args.pool)) return args.pool; | ||
}) | ||
.filter((pool): pool is Address => typeof pool === "string"); | ||
type PoolSelectButtonProps = PoolSelectorProps & { | ||
address: Address; | ||
label: string; | ||
setInputValue: Dispatch<SetStateAction<string>>; | ||
}; | ||
|
||
const productPools = productPoolHistory | ||
.map(({ args }) => { | ||
if (args.pool && isAddress(args.pool)) return args.pool; | ||
}) | ||
.filter((pool): pool is Address => typeof pool === "string"); | ||
setProductPools(productPools); | ||
setSumPools(sumPools); | ||
} | ||
}, [sumPoolHistory, productPoolHistory, isLoadingSumPoolHistory, isLoadingProductPoolHistory]); | ||
const PoolSelectButton = ({ | ||
selectedPoolAddress, | ||
setSelectedPoolAddress, | ||
address, | ||
label, | ||
setInputValue, | ||
}: PoolSelectButtonProps) => { | ||
const router = useRouter(); | ||
const pathname = usePathname(); | ||
|
||
return ( | ||
<section className="mb-7"> | ||
<div className="flex flex-wrap justify-center gap-3 h-12"> | ||
{sumPools.length > 0 && | ||
sumPools.map(pool => { | ||
console.log("selectedPoolAddress", selectedPoolAddress); | ||
console.log("pool", pool); | ||
return ( | ||
<button | ||
key={pool} | ||
className={`btn btn-sm btn-secondary flex relative pl-[35px] border-none font-normal text-lg ${ | ||
selectedPoolAddress === pool | ||
? " text-neutral-700 bg-gradient-to-b from-custom-beige-start to-custom-beige-end to-100%" | ||
: "" | ||
}`} | ||
onClick={() => { | ||
setSelectedPoolAddress(pool); | ||
router.push(`${pathname}?address=${pool}`); | ||
}} | ||
> | ||
{/* eslint-disable-next-line @next/next/no-img-element */} | ||
<img | ||
alt="" | ||
className={`!rounded-full absolute top-0.5 left-1 `} | ||
src={blo(pool as `0x${string}`)} | ||
width="25" | ||
height="25" | ||
/> | ||
Constant Sum | ||
</button> | ||
); | ||
})} | ||
{productPools.length > 0 && | ||
productPools.map(pool => ( | ||
<button | ||
key={pool} | ||
className={`btn btn-sm btn-secondary flex relative pl-[35px] border-none font-normal text-lg ${ | ||
selectedPoolAddress === pool | ||
? "text-neutral-700 bg-gradient-to-b from-custom-beige-start to-custom-beige-end to-100%" | ||
: "" | ||
}`} | ||
onClick={() => { | ||
setSelectedPoolAddress(pool); | ||
router.push(`${pathname}?address=${pool}`); | ||
}} | ||
> | ||
{/* eslint-disable-next-line @next/next/no-img-element */} | ||
<img | ||
alt="" | ||
className={`!rounded-full absolute top-0.5 left-1 `} | ||
src={blo(pool as `0x${string}`)} | ||
width="25" | ||
height="25" | ||
/> | ||
Constant Product | ||
</button> | ||
))} | ||
</div> | ||
<div className="flex justify-center flex-wrap gap-5 w-full items-center text-xl"> | ||
<form | ||
className="flex flex-wrap items-center gap-5" | ||
onSubmit={event => { | ||
event.preventDefault(); | ||
setSelectedPoolAddress(inputValue); | ||
router.push(`${pathname}?address=${inputValue}`); | ||
setInputValue(""); | ||
}} | ||
> | ||
<div className="relative"> | ||
{inputValue && ( | ||
// eslint-disable-next-line @next/next/no-img-element | ||
<img | ||
alt="" | ||
className="!rounded-full absolute top-1 left-1" | ||
src={blo(inputValue as `0x${string}`)} | ||
width="37" | ||
height="37" | ||
/> | ||
)} | ||
<input | ||
value={inputValue} | ||
onChange={e => setInputValue(e.target.value)} | ||
className={`input input-bordered bg-base-200 text-center h-[44px] w-[355px] sm:w-[550px] ${ | ||
inputValue && "pl-10 pr-14" | ||
}`} | ||
placeholder="Search by pool addresss" | ||
/> | ||
<button | ||
className={`btn btn-sm bg-neutral w-12 absolute top-1.5 right-1.5 border-none ${ | ||
isValidAddress ? "bg-violet-400 hover:bg-violet-400" : "" | ||
}`} | ||
type="submit" | ||
disabled={!isValidAddress} | ||
> | ||
<MagnifyingGlassIcon className="h-5 w-5" /> | ||
</button> | ||
</div> | ||
</form> | ||
</div> | ||
</section> | ||
<button | ||
key={address} | ||
className={`btn btn-sm btn-secondary flex relative pl-[35px] border-none font-normal text-lg ${ | ||
selectedPoolAddress === address | ||
? " text-neutral-700 bg-gradient-to-b from-custom-beige-start to-custom-beige-end to-100%" | ||
: "" | ||
}`} | ||
onClick={() => { | ||
setSelectedPoolAddress(address); | ||
setInputValue(address); | ||
router.push(`${pathname}?address=${address}`); | ||
}} | ||
> | ||
{/* eslint-disable-next-line @next/next/no-img-element */} | ||
<img | ||
alt="" | ||
className={`!rounded-full absolute top-0.5 left-1 `} | ||
src={blo(address as `0x${string}`)} | ||
width="25" | ||
height="25" | ||
/> | ||
{label} | ||
</button> | ||
); | ||
}; |
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
Oops, something went wrong.