Skip to content
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

Fe/lifi approvals 2 #2876

Merged
merged 8 commits into from
Jul 16, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 145 additions & 68 deletions packages/synapse-interface/pages/lifi/index.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
import Grid from '@tw/Grid'
import { useEffect, useState } from 'react'
import { useAccount, useAccountEffect, useSwitchChain } from 'wagmi'


import { useAccount, useSwitchChain } from 'wagmi'
import { LandingPageWrapper } from '@layouts/LandingPageWrapper'
import StandardPageContainer from '@layouts/StandardPageContainer'
import { getErc20TokenAllowance } from '@/actions/getErc20TokenAllowance'
import { approveToken } from '@/utils/approveToken'
import { TransactionButton } from '@/components/buttons/TransactionButton'
import { useConnectModal } from '@rainbow-me/rainbowkit'
import Image from 'next/image'
import { CHAINS_BY_ID } from '@/constants/chains'

const CHAIN_IDS = [1, 42161, 10]
const LIFI_SPENDER = "0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae"
const CHAIN_IDS = [1, 56, 42161, 10]
const LIFI_SPENDER = '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae'

const MAX_AMOUNT = 100000000000000000000000000000n

const TOKENS = {
USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
USDT: '0xdac17f958d2ee523a2206206994597c13d831ec7',
WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'
} as const;
1: {
// Ethereum Mainnet
USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
USDT: '0xdac17f958d2ee523a2206206994597c13d831ec7',
WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
},
42161: {
// Arbitrum
USDC: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8',
USDT: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
WETH: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
},
10: {
// Optimism
USDC: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
USDT: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
WETH: '0x4200000000000000000000000000000000000006',
},
56: {
// BSC
USDC: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
USDT: '0x55d398326f99059ff775485246999027b3197955',
WETH: '0x4DB5a66E937A9F4473fA95b1cAF1d1E1D62E29EA',
},
}

interface TokenAllowances {
[chainId: number]: {
Expand All @@ -29,6 +51,7 @@ const LifiPage = () => {
const { address, isConnected, chain } = useAccount()
const { chains, switchChain: switchNetwork } = useSwitchChain()
const { openConnectModal } = useConnectModal()
const [hoveredToken, setHoveredToken] = useState(null)

const [allowances, setAllowances] = useState<TokenAllowances>({})

Expand All @@ -40,11 +63,13 @@ const LifiPage = () => {
for (const chainId of CHAIN_IDS) {
newAllowances[chainId] = {}

for (const [tokenName, tokenAddress] of Object.entries(TOKENS)) {
for (const [tokenName, tokenAddress] of Object.entries(
TOKENS[chainId]
)) {
const allowance = await getErc20TokenAllowance({
address,
chainId,
tokenAddress,
tokenAddress: tokenAddress as `0x${string}`,
spender: LIFI_SPENDER,
})
newAllowances[chainId][tokenName] = allowance
Expand All @@ -60,26 +85,24 @@ const LifiPage = () => {
}
}, [address, isConnected])

const handleRevoke = async (chainId: number, tokenName: string, tokenAddress: string) => {
const handleRevoke = async (chainId: number, tokenName: string) => {
if (chain?.id !== chainId) {
await switchNetwork({chainId: chainId})
await switchNetwork({ chainId: chainId })
}
const tokenAddress = TOKENS[chainId][tokenName]
await approveToken(LIFI_SPENDER, chainId, tokenAddress, 0n)
setAllowances(prev => ({
setAllowances((prev) => ({
...prev,
[chainId]: {
...prev[chainId],
[tokenName]: 0n
}
[tokenName]: 0n,
},
}))
}

return (
<LandingPageWrapper>
<StandardPageContainer
connectedChainId={chain?.id}
address={address}
>
<StandardPageContainer connectedChainId={chain?.id} address={address}>
<div className="flex justify-between">
<div>
<div className="text-2xl text-white">
Expand All @@ -88,56 +111,110 @@ const LifiPage = () => {
</div>
</div>
<div className="py-6">
<Grid
cols={{ xs: 1 }}
gap={6}
className="justify-center px-2 py-16 sm:px-6 md:px-8"
>
<div className="pb-3 place-self-center">
<div>
<h3>Li.fi / Jumper is investigating an ongoing exploit, and users should revoke approvals <a className="underline" target="_blank" href="https://x.com/lifiprotocol/status/1813196697641570635">- Li.fi Tweet</a></h3>
<br />
<h3>Check to see if you have any approvals at risk below:</h3>
<br />
{isConnected ? (
CHAIN_IDS.map(chainId => (
<div key={chainId}>
<h4>Chain ID: {chainId}</h4>
{Object.entries(allowances[chainId] || {}).map(([tokenName, allowance]) => (
<div key={tokenName}>
{tokenName} Allowance: {allowance.toString()}
{allowance > 0n && (
<TransactionButton
className="btn btn-primary ml-2"
pendingLabel="Revoking..."
label={`Revoke ${tokenName} Approval`}
onClick={() => handleRevoke(chainId, tokenName, TOKENS[tokenName])}
/>
)}
</div>
))}
</div>
))
) : (
<div className="flex flex-col justify-center h-full p-10">
<TransactionButton
style={{
background: 'linear-gradient(90deg, rgba(128, 0, 255, 0.2) 0%, rgba(255, 0, 191, 0.2) 100%)',
border: '1px solid #9B6DD7',
borderRadius: '4px',
}}
label="Connect wallet to check for approvals"
pendingLabel="Connecting"
onClick={() => new Promise((resolve) => {
<div className="pb-3 place-self-center">
<div>
<h3 className="text-lg">
Li.fi / Jumper is investigating an ongoing exploit, and users
should revoke approvals{' '}
<a
className="underline"
target="_blank"
href="https://x.com/lifiprotocol/status/1813207291778215955"
>
- Li.fi Tweet
</a>
</h3>
<br />
<h3 className="text-lg">
Check to see if you have any approvals at risk below:
</h3>
<br />
{isConnected ? (
CHAIN_IDS.map((chainId) => (
<div key={chainId} className="mb-5 ">
<span className="flex items-center gap-1 mb-2">
<Image
loading="lazy"
src={CHAINS_BY_ID[chainId].chainImg}
alt={`${chain.name} img`}
width="20"
height="20"
className="w-5 h-5 max-w-fit"
/>
<div className="text-xl">
{CHAINS_BY_ID[chainId].name}
</div>
</span>
{Object.entries(allowances[chainId] || {}).map(
([tokenName, allowance]) => {
return (
<div
key={tokenName}
className="flex items-center space-x-8 h-[40px]"
>
<div className="flex justify-between w-1/2">
<div>{tokenName} </div>
<div
onMouseEnter={() =>
setHoveredToken(
allowance > MAX_AMOUNT ? tokenName : null
)
}
onMouseLeave={() => setHoveredToken(null)}
className="hover:cursor-pointer"
>
{allowance > MAX_AMOUNT && <div>UNLIMITED</div>}
{hoveredToken &&
hoveredToken === tokenName &&
allowance > 0n && (
<div className="absolute p-2 mt-4 bg-gray-500">
{allowances[chainId][
hoveredToken
].toString()}
</div>
)}
</div>
</div>
{allowance > 0n ? (
<TransactionButton
className="btn btn-primary rounded-md max-w-[250px] h-[40px]"
pendingLabel="Revoking..."
label={`Revoke ${tokenName} Approval`}
onClick={() => handleRevoke(chainId, tokenName)}
/>
) : (
<div className="w-[250px] flex justify-center">
-
</div>
)}
</div>
)
}
)}
</div>
))
) : (
<div className="flex flex-col justify-center h-full p-10">
<TransactionButton
style={{
background:
'linear-gradient(90deg, rgba(128, 0, 255, 0.2) 0%, rgba(255, 0, 191, 0.2) 100%)',
border: '1px solid #9B6DD7',
borderRadius: '4px',
}}
label="Connect wallet to check for approvals"
pendingLabel="Connecting"
onClick={() =>
new Promise((resolve) => {
openConnectModal()
resolve(true)
})}
/>
</div>
)}
</div>
})
}
/>
</div>
)}
</div>
</Grid>
</div>
</div>
</StandardPageContainer>
</LandingPageWrapper>
Expand Down
Loading