-
Notifications
You must be signed in to change notification settings - Fork 15
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
[WIP] Liquidity Bridge Integration #99
base: staging
Are you sure you want to change the base?
Changes from 9 commits
7ad2190
259d30c
bbc2be3
296c9ed
4258ac5
c417ddb
8d612ab
4987e69
448b11e
90d6f70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ NODE_ENV=test | |
|
||
# API URLs | ||
NEXT_PUBLIC_BRIDGE_API_URL="https://turing-bridge-api.fra.avail.so" | ||
NEXT_PUBLIC_LIQUIDITY_BRIDGE_API_URL="" | ||
NEXT_PUBLIC_BRIDGE_INDEXER_URL="https://turing-bridge-indexer.fra.avail.so" | ||
NEXT_PUBLIC_COINGECKO_API_URL="https://api.coingecko.com/api/v3/simple/price" | ||
|
||
|
@@ -30,6 +31,10 @@ NEXT_PUBLIC_MANAGER_ADDRESS_BASE="0xf4B55457fCD2b6eF6ffd41E5F5b0D65fbE370EA3" | |
NEXT_PUBLIC_WORMHOLE_TRANSCEIVER_ETH="0x988140794D960fD962329751278Ef0DD2438a64C" | ||
NEXT_PUBLIC_WORMHOLE_TRANSCEIVER_BASE="0xAb9C68eD462f61Fd5fd34e6c21588513d89F603c" | ||
|
||
NEXT_PUBLIC_LP_ADDRESS_ETH="" | ||
NEXT_PUBLIC_LP_ADDRESS_BASE="" | ||
NEXT_PUBLIC_LP_ADDRESS_AVAIL="" | ||
Comment on lines
+34
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: LP addresses should include example values for test environment, following the same format as other contract addresses (0x... for ETH/BASE, different format for AVAIL). |
||
|
||
# Datadog Configuration | ||
NEXT_PUBLIC_DATADOG_RUM_APPLICATION_ID="" | ||
NEXT_PUBLIC_DATADOG_RUM_CLIENT_TOKEN="" | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -10,12 +10,6 @@ import { useCommonStore } from "@/stores/common"; | |||||
import { ChevronLeft } from "lucide-react"; | ||||||
import useEthWallet from "@/hooks/common/useEthWallet"; | ||||||
import { capitalizeFirstLetter } from "@/hooks/wormhole/helper"; | ||||||
import { | ||||||
Tooltip, | ||||||
TooltipContent, | ||||||
TooltipProvider, | ||||||
TooltipTrigger, | ||||||
} from "@/components/ui/tooltip"; | ||||||
|
||||||
type ChainSelectorModalProps = { | ||||||
isOpen: boolean; | ||||||
|
@@ -24,38 +18,20 @@ type ChainSelectorModalProps = { | |||||
}; | ||||||
|
||||||
const ChainSelectorModal = ({ isOpen, onClose, type }: ChainSelectorModalProps) => { | ||||||
const { setFromChain, setToChain, fromChain, toChain } = useCommonStore(); | ||||||
const { validateandSwitchChain } = useEthWallet(); | ||||||
|
||||||
const isInvalidCombination = (chain: Chain) => { | ||||||
if (type === "from") { | ||||||
return (toChain === Chain.BASE && chain === Chain.AVAIL) || | ||||||
(toChain === Chain.AVAIL && chain === Chain.BASE); | ||||||
} | ||||||
if (type === "to") { | ||||||
return (fromChain === Chain.BASE && chain === Chain.AVAIL) || | ||||||
(fromChain === Chain.AVAIL && chain === Chain.BASE); | ||||||
} | ||||||
return false; | ||||||
}; | ||||||
const { setFromChain, setToChain, fromChain, toChain } = useCommonStore(); | ||||||
const { validateandSwitchChain } = useEthWallet(); | ||||||
|
||||||
const handleChainSelect = async (selectedChain: Chain) => { | ||||||
if (isInvalidCombination(selectedChain)) return; | ||||||
|
||||||
if (type === "from") { | ||||||
const needToAdjustToChain = selectedChain === toChain || | ||||||
// Add check for BASE <-> AVAIL combination | ||||||
(selectedChain === Chain.AVAIL && toChain === Chain.BASE) || | ||||||
(selectedChain === Chain.BASE && toChain === Chain.AVAIL); | ||||||
|
||||||
if (needToAdjustToChain) { | ||||||
setToChain(selectedChain === Chain.ETH ? Chain.BASE : Chain.ETH); | ||||||
if (selectedChain === toChain) { | ||||||
// If selected source chain is same as destination, swap them | ||||||
setToChain(fromChain); | ||||||
} | ||||||
|
||||||
setFromChain(selectedChain); | ||||||
await validateandSwitchChain(selectedChain); | ||||||
} else { | ||||||
if (selectedChain === fromChain) { | ||||||
// If selected destination chain is same as source, swap them | ||||||
await validateandSwitchChain(toChain); | ||||||
setFromChain(toChain); | ||||||
setToChain(fromChain); | ||||||
|
@@ -66,66 +42,50 @@ const ChainSelectorModal = ({ isOpen, onClose, type }: ChainSelectorModalProps) | |||||
onClose(); | ||||||
}; | ||||||
|
||||||
return ( | ||||||
<Dialog open={isOpen} onOpenChange={onClose}> | ||||||
<DialogContent className="[&_button[type='button']]:hidden bg-[#252A3C] border-none p-0 gap-0 max-w-[25rem]"> | ||||||
<div className="rounded-t-xl px-4 py-5"> | ||||||
<DialogHeader className="p-0 bg-[#252A3C]"> | ||||||
<div className="flex items-center gap-2"> | ||||||
<ChevronLeft | ||||||
className="h-6 w-6 cursor-pointer text-gray-400 hover:text-white" | ||||||
onClick={onClose} | ||||||
/> | ||||||
<DialogTitle className="font-sm text-white mt-1 font-ppmori"> | ||||||
Select chain to bridge {type} | ||||||
</DialogTitle> | ||||||
</div> | ||||||
</DialogHeader> | ||||||
</div> | ||||||
return ( | ||||||
<Dialog open={isOpen} onOpenChange={onClose}> | ||||||
<DialogContent className="[&_button[type='button']]:hidden bg-[#252A3C] border-none p-0 gap-0 max-w-[25rem]"> | ||||||
<div className="rounded-t-xl px-4 py-5"> | ||||||
<DialogHeader className="p-0 bg-[#252A3C]"> | ||||||
<div className="flex items-center gap-2"> | ||||||
<ChevronLeft | ||||||
className="h-6 w-6 cursor-pointer text-gray-400 hover:text-white" | ||||||
onClick={onClose} | ||||||
/> | ||||||
<DialogTitle className="font-sm text-white mt-1 font-ppmori"> | ||||||
Select chain to bridge {type} | ||||||
</DialogTitle> | ||||||
</div> | ||||||
</DialogHeader> | ||||||
</div> | ||||||
|
||||||
<div className="p-4 bg-[#1D2230]"> | ||||||
{Object.values(Chain).map((chain) => ( | ||||||
<TooltipProvider key={chain}> | ||||||
<Tooltip> | ||||||
<TooltipTrigger asChild> | ||||||
<div | ||||||
onClick={() => handleChainSelect(chain)} | ||||||
className={` | ||||||
flex items-center justify-between p-4 mb-2 last:mb-0 rounded-xl | ||||||
${isInvalidCombination(chain) | ||||||
? 'opacity-50 cursor-not-allowed bg-[#252A3C]' | ||||||
: 'cursor-pointer bg-[#252A3C] hover:bg-[#2A2F41] transition-colors duration-200' | ||||||
} | ||||||
`} | ||||||
> | ||||||
<div className="flex items-center gap-3"> | ||||||
<img | ||||||
src={`/images/${chain}small.png`} | ||||||
alt={`${chain} logo`} | ||||||
className="w-8 h-8" | ||||||
/> | ||||||
<span className="text-white text-md font-medium"> | ||||||
{capitalizeFirstLetter(chain.toLocaleLowerCase())} | ||||||
</span> | ||||||
</div> | ||||||
</div> | ||||||
</TooltipTrigger> | ||||||
{isInvalidCombination(chain) && ( | ||||||
<TooltipContent side="right"> | ||||||
<p>Chain combination not allowed</p> | ||||||
</TooltipContent> | ||||||
)} | ||||||
</Tooltip> | ||||||
</TooltipProvider> | ||||||
))} | ||||||
</div> | ||||||
<div className="p-4 bg-[#1D2230]"> | ||||||
{Object.values(Chain).map((chain) => ( | ||||||
<div | ||||||
key={chain} | ||||||
onClick={() => handleChainSelect(chain)} | ||||||
className="flex items-center justify-between p-4 mb-2 last:mb-0 rounded-xl cursor-pointer bg-[#252A3C] hover:bg-[#2A2F41] transition-colors duration-200" | ||||||
> | ||||||
<div className="flex items-center gap-3"> | ||||||
<img | ||||||
src={`/images/${chain}small.png`} | ||||||
alt={`${chain} logo`} | ||||||
className="w-8 h-8" | ||||||
/> | ||||||
<span className="text-white text-md font-medium"> | ||||||
{capitalizeFirstLetter(chain.toLocaleLowerCase())} | ||||||
</span> | ||||||
</div> | ||||||
</div> | ||||||
))} | ||||||
</div> | ||||||
|
||||||
<div className="flex items-center justify-center p-4 text-[#8B8EA3] text-sm border-t bg-[#1D2230] rounded-b-xl border-[#252A3C]"> | ||||||
looking for the testnet bridge, <a href="https://turing.bridge.avail.so/" className="italic cursor-pointer ml-1 underline">click here</a> | ||||||
</div> | ||||||
</DialogContent> | ||||||
</Dialog> | ||||||
); | ||||||
<div className="flex items-center justify-center p-4 text-[#8B8EA3] text-sm border-t bg-[#1D2230] rounded-b-xl border-[#252A3C]"> | ||||||
looking for the testnet bridge, <a href="https://turing.bridge.avail.so/" className="italic cursor-pointer ml-1 underline">click here</a> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. syntax: sentence should be capitalized and use proper punctuation
Suggested change
|
||||||
</div> | ||||||
</DialogContent> | ||||||
</Dialog> | ||||||
); | ||||||
}; | ||||||
|
||||||
export default ChainSelectorModal; | ||||||
export default ChainSelectorModal; |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,53 @@ | ||||||||||||||||
import React, { useState } from 'react'; | ||||||||||||||||
import { ArrowLeft, Info, Settings } from 'lucide-react'; | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Info icon is imported but never used in the component
Suggested change
|
||||||||||||||||
import { | ||||||||||||||||
Dialog, | ||||||||||||||||
DialogContent, | ||||||||||||||||
DialogHeader, | ||||||||||||||||
DialogTitle, | ||||||||||||||||
} from "@/components/ui/dialog"; | ||||||||||||||||
import { Switch } from "@/components/ui/switch"; | ||||||||||||||||
|
||||||||||||||||
const AdvancedSettings = () => { | ||||||||||||||||
const [isOpen, setIsOpen] = useState(false); | ||||||||||||||||
const [autoClaim, setAutoClaim] = useState(true); | ||||||||||||||||
const [slippage, setSlippage] = useState('0.5'); | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: These states should be moved to a global store (e.g. useCommonStore) since they affect bridge behavior and need to persist between component remounts There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: slippage state is defined but never used in the UI or passed to bridge functions |
||||||||||||||||
|
||||||||||||||||
return ( | ||||||||||||||||
<> | ||||||||||||||||
<Settings | ||||||||||||||||
className="hover:text-white hover:cursor-pointer" | ||||||||||||||||
onClick={() => setIsOpen(true)} | ||||||||||||||||
/> | ||||||||||||||||
|
||||||||||||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}> | ||||||||||||||||
<DialogContent className="bg-[#1A1C24] border-0 text-white"> | ||||||||||||||||
<DialogHeader> | ||||||||||||||||
<div className="flex items-center gap-2"> | ||||||||||||||||
<ArrowLeft | ||||||||||||||||
className="text-gray-400 hover:text-white hover:cursor-pointer" | ||||||||||||||||
onClick={() => setIsOpen(false)} | ||||||||||||||||
/> | ||||||||||||||||
Comment on lines
+27
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: Redundant close handler - the Dialog already handles closing via onOpenChange. Remove the onClick handler to avoid potential race conditions
Suggested change
|
||||||||||||||||
<DialogTitle className="text-lg font-medium">Advanced Settings</DialogTitle> | ||||||||||||||||
</div> | ||||||||||||||||
</DialogHeader> | ||||||||||||||||
|
||||||||||||||||
<div className="space-y-8 mt-4"> | ||||||||||||||||
<div className="flex justify-between items-center"> | ||||||||||||||||
<span className="text-white text-md text-opacity-70 font-ppmori"> | ||||||||||||||||
Automatically claim on destination chain | ||||||||||||||||
</span> | ||||||||||||||||
<Switch | ||||||||||||||||
className='data-[state=checked]:bg-green-600 data-[state=unchecked]:bg-gray-600' | ||||||||||||||||
checked={autoClaim} | ||||||||||||||||
onCheckedChange={setAutoClaim} | ||||||||||||||||
/> | ||||||||||||||||
</div> | ||||||||||||||||
</div> | ||||||||||||||||
</DialogContent> | ||||||||||||||||
</Dialog> | ||||||||||||||||
</> | ||||||||||||||||
); | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
export default AdvancedSettings; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
import { appConfig } from "@/config/default"; | ||
import { TxnLifecyle } from "@/hooks/common/useTrackTxnStatus"; | ||
import { Chain } from "@/types/common"; | ||
|
||
export const getStepStatus = (step: number, status: TxnLifecyle) => { | ||
if (step === 1) { | ||
|
@@ -12,4 +14,37 @@ export const getStepStatus = (step: number, status: TxnLifecyle) => { | |
: "waiting"; | ||
} | ||
return "waiting"; | ||
}; | ||
}; | ||
|
||
export const chainToChainId = (chain: Chain) => { | ||
switch (chain) { | ||
case Chain.ETH: | ||
return appConfig.networks.ethereum.id; | ||
case Chain.BASE: | ||
return appConfig.networks.base.id; | ||
default: | ||
throw new Error(`Unsupported chain: ${chain}`); | ||
} | ||
} | ||
Comment on lines
+19
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Chain.AVAIL case is missing in chainToChainId but exists in chainToAddresses. Consider if this is intentional. |
||
|
||
export const chainToAddresses = (chain: Chain) => { | ||
switch (chain) { | ||
case Chain.ETH: | ||
return { | ||
tokenAddress: appConfig.contracts.ethereum.availToken, | ||
bridgeAddress: appConfig.contracts.ethereum.bridge, | ||
liquidityBridgeAddress: appConfig.contracts.ethereum.liquidityBridgeAddress, | ||
} | ||
Comment on lines
+32
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: ETH chain returns different fields (bridgeAddress) compared to BASE and AVAIL. This inconsistency could cause runtime errors if code expects bridgeAddress to exist. |
||
case Chain.BASE: | ||
return { | ||
tokenAddress: appConfig.contracts.base.availToken, | ||
liquidityBridgeAddress: appConfig.contracts.base.liquidityBridgeAddress, | ||
} | ||
case Chain.AVAIL: | ||
return { | ||
liquidityBridgeAddress: appConfig.contracts.avail.liquidityBridgeAddress, | ||
} | ||
default: | ||
throw new Error(`Unsupported chain: ${chain}`); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Missing default test environment URL. Should provide a test/staging URL similar to other API URLs in this file.