Skip to content

Commit

Permalink
feat: [IXSPD1-2285] lock IXS page integrates with smart contract (#3047)
Browse files Browse the repository at this point in the history
* feat: [IXSPD1-2285] lock IXS page integrates with smart contract

* feat: show locking power relies on duration and token amount

* fix: refactor, handle process section

* fix: get token balance by wagmi
  • Loading branch information
galvin96 authored Jan 10, 2025
1 parent 276be4e commit 39c6883
Show file tree
Hide file tree
Showing 16 changed files with 264 additions and 108 deletions.
1 change: 1 addition & 0 deletions src/abis/voting-escrow.json

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/assets/images/dex-v2/documents.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions src/assets/images/dex-v2/loading.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions src/constants/addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { FACTORY_ADDRESS } from '@ixswap1/v2-sdk'
import { constructSameAddressMap } from '../utils/constructSameAddressMap'
import { SupportedChainId } from './chains'
import { isProd } from 'utils/isEnvMode'
import { Address } from 'viem'

type ContractAddressRecord = Record<number, string>

Expand Down Expand Up @@ -75,7 +76,7 @@ export const TGE_CHAINS_WITH_KYC = ENV_SUPPORTED_TGE_CHAINS || [
SUPPORTED_TGE_CHAINS.BASE_SEPOLIA,
]
// the rest are same as kovan for now
export const IXS_ADDRESS: { [key: number]: string } = {
export const IXS_ADDRESS: { [key: number]: Address } = {
[1]: '0x73d7c860998CA3c01Ce8c808F5577d94d545d1b4',
[4]: '0xA1997c88a60dCe7BF92A3644DA21e1FfC8F96dC2',
[3]: '0xA1997c88a60dCe7BF92A3644DA21e1FfC8F96dC2',
Expand Down Expand Up @@ -223,5 +224,5 @@ export const LBP_FACTORY_ADDRESS = {
} as Record<number, string>

export const VOTING_ESCROW_ADDRESS = {
[84532]: '0x72Dc60450Daf5c72d2b6b9a52f4Db7321Ffdb7Ad', // base sepolia
[84532]: '0xc55d5Ed2f931dac22d5b578130Dd96f9CfAFaCB1', // base sepolia
} as ContractAddressRecord
14 changes: 10 additions & 4 deletions src/hooks/useContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import LAUNCHPAD_INVESTMENT_ABI from 'abis/launchpad-investment.json'
import LBP_ABI from 'abis/LiquiidtyBoostrapPool.json'
import LBP_FACTORY_ABI from 'abis/LiquidityBoostrapPoolFactory.json'
import PAYOUT_AIRDROP_ABI from 'abis/payout-airdrop.json'
import VOTING_ESCROW_ABI from 'abis/voting-escrow.json'
import {
ARGENT_WALLET_DETECTOR_ADDRESS,
ENS_REGISTRAR_ADDRESSES,
Expand All @@ -49,6 +50,7 @@ import {
SWAP_ROUTER_ADDRESS,
IXSALE_ADDRESS,
PAYOUT_AIRDROP_PROXY_ADDRESS,
VOTING_ESCROW_ADDRESS,
} from 'constants/addresses'
import { useMemo } from 'react'
import { getContract } from 'utils'
Expand Down Expand Up @@ -150,17 +152,17 @@ export function useWETHContract(withSignerIfPossible?: boolean) {
}

export function useArgentWalletDetectorContract() {
// @ts-ignore
// @ts-ignore
return useContract<ArgentWalletDetector>(ARGENT_WALLET_DETECTOR_ADDRESS, ARGENT_WALLET_DETECTOR_ABI, false)
}

export function useENSRegistrarContract(withSignerIfPossible?: boolean) {
// @ts-ignore
// @ts-ignore
return useContract<EnsRegistrar>(ENS_REGISTRAR_ADDRESSES, ENS_ABI, withSignerIfPossible)
}

export function useENSResolverContract(address: string | undefined, withSignerIfPossible?: boolean) {
// @ts-ignore
// @ts-ignore
return useContract<EnsPublicResolver>(address, ENS_PUBLIC_RESOLVER_ABI, withSignerIfPossible)
}

Expand All @@ -183,7 +185,7 @@ export function useLiquidityRouterContract(): Contract | null {
return useContract(LIQUIDITY_ROUTER_ADDRESS, IIxsV2LiquidityRouterABI, true)
}
export function useMulticall2Contract() {
// @ts-ignore
// @ts-ignore
return useContract<Multicall2>(MULTICALL2_ADDRESSES, MULTICALL_ABI, false) as Multicall2
}
export function useBurnWSecContract(address: string | undefined) {
Expand Down Expand Up @@ -218,3 +220,7 @@ export function useLBPContract(contractAddress: string) {
export function useLBPFactory(contractAddress: string) {
return useContract(contractAddress, LBP_FACTORY_ABI, true)
}

export function useVotingEscrowContract() {
return useContract(VOTING_ESCROW_ADDRESS, VOTING_ESCROW_ABI, true)
}
56 changes: 54 additions & 2 deletions src/pages/DexV2/Lock/LockProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,68 @@
import { Currency } from "@ixswap1/sdk-core"
import { createContext, PropsWithChildren, useContext, useState } from "react"
import { createContext, PropsWithChildren, useCallback, useContext, useEffect, useMemo, useState } from "react"
import { utils } from 'ethers'
import { useVotingEscrowContract } from "hooks/useContract"
import useIXSCurrency from "hooks/useIXSCurrency"
import { useTransactionAdder } from "state/transactions/hooks"
import { safeParseUnits } from "utils/formatCurrencyAmount"
import { WEEK } from "./constants"
import { ApprovalState, useAllowance } from "hooks/useApproveCallback"
import { useWeb3React } from "hooks/useWeb3React"
import { IXS_ADDRESS, VOTING_ESCROW_ADDRESS } from 'constants/addresses'

export type UseLockResult = ReturnType<typeof _useLock>
export const LockContext = createContext<UseLockResult | null>(null)

export function _useLock() {
const [userInput, setUserInput] = useState('')
const [duration, setDuration] = useState(604800) // 7 days
const [locking, setLocking] = useState(false)
const [locked, setLocked] = useState(false)

const votingEscrowContract = useVotingEscrowContract()
const addTransaction = useTransactionAdder()
const currency = useIXSCurrency()

const handleLock = useCallback(async () => {
setLocking(true)
try {
const tx = await votingEscrowContract?.createLock(
safeParseUnits(+userInput, currency?.decimals),
duration,
)
await tx.wait()

if (!tx.hash) return
setLocked(true)
addTransaction(tx, {
summary: `Lock ${userInput} IXS in ${ Math.round(duration / WEEK) } weeks`,
})
} finally {
setLocking(false)
}
}, [userInput, duration, votingEscrowContract, addTransaction, currency])

const { chainId } = useWeb3React()
const [approvalState, approve] = useAllowance(
IXS_ADDRESS[chainId],
utils.parseUnits(userInput || '0', currency?.decimals),
VOTING_ESCROW_ADDRESS[chainId]
)

const step = useMemo(() => {
if (locked) return 3
if (locking) return 2
if (approvalState === ApprovalState.PENDING || approvalState === ApprovalState.APPROVED) return 1
return 0
}, [approvalState, locking, locked])

return {
userInput, setUserInput,
duration, setDuration,
locking,
locked,
handleLock,
step,
approvalState, approve,
}
}

Expand Down
18 changes: 13 additions & 5 deletions src/pages/DexV2/Lock/components/CurrencyInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import { Input as NumericalInput } from 'components/NumericalInput'
import { useActiveWeb3React } from 'hooks/web3'
import { Trans } from '@lingui/macro'
import useTheme from 'hooks/useTheme'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { formatCurrencySymbol } from 'utils/formatCurrencySymbol'
import { useCurrencyBalance } from 'state/wallet/hooks'
import { useCurrencyBalanceV2 } from 'state/wallet/hooks'
import { TYPE } from 'theme'
import { AssetLogo } from 'components/CurrencyInputPanel/AssetLogo'
import { FiatValue } from 'components/CurrencyInputPanel/FiatValue'
import { Flex } from 'rebass'
import { Button } from '@mui/material'
import { IXS_ADDRESS } from 'constants/addresses'
import { DEFAULT_CHAIN_ID } from 'config'
import { formatAmount } from 'utils/formatCurrencyAmount'

interface CurrencyInputPanelProps {
value: string
Expand All @@ -33,8 +35,14 @@ export default function CurrencyInputPanel({
priceImpact,
...rest
}: CurrencyInputPanelProps) {
const { account } = useActiveWeb3React()
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
const { account, chainId } = useActiveWeb3React()
const currencyBalance = useCurrencyBalanceV2({
account,
chainId,
currency: IXS_ADDRESS[chainId ?? DEFAULT_CHAIN_ID],
})
const selectedCurrencyBalance = currencyBalance?.formatted

const theme = useTheme()
const decimals = currency?.tokenInfo?.decimals || 18
const onChangeInput = (val: string) => {
Expand Down Expand Up @@ -93,7 +101,7 @@ export default function CurrencyInputPanel({
<Trans>
Balance:
<span style={{ color: '#292933', fontWeight: 600, marginLeft: 4 }}>
{formatCurrencyAmount(selectedCurrencyBalance, decimals)} {currency.symbol}
{formatAmount(+selectedCurrencyBalance)}
</span>
</Trans>
) : null}
Expand Down
17 changes: 13 additions & 4 deletions src/pages/DexV2/Lock/components/DurationSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Slider } from "@mui/material"
import { Box, Button, Flex } from "rebass"
import { Box, Flex } from "rebass"
import styled from "styled-components"
import { TYPE } from "theme"
import { useLock } from "../LockProvider"

const WEEK = 604800
import { FOUR_YEARS_IN_SECONDS, WEEK } from "../constants"
import { formatAmount } from "utils/formatCurrencyAmount"

const DurationSlider: React.FC = () => {
const {
userInput,
duration,
setDuration,
} = useLock()
Expand All @@ -34,12 +35,14 @@ const DurationSlider: React.FC = () => {
}, ]

const weekToShow = Math.round(duration / WEEK)
const durationLabel = `${weekToShow} ${weekToShow > 1 ? 'weeks' : 'week'}`
const votingPower = +userInput * duration / FOUR_YEARS_IN_SECONDS

return (
<Box>
<Flex justifyContent="space-between" alignItems="center" mb={2}>
<TYPE.subHeader1 color='blue5'>Lock Time</TYPE.subHeader1>
<TYPE.label>{weekToShow} {weekToShow > 1 ? 'weeks' : 'week'}</TYPE.label>
<TYPE.label>{durationLabel}</TYPE.label>
</Flex>

<StyledSlider
Expand All @@ -64,6 +67,12 @@ const DurationSlider: React.FC = () => {
</TYPE.subHeader1>
))}
</Flex>

<Box mt={4}>
<TYPE.body3>
Locking for <strong>{durationLabel}</strong> for {formatAmount(votingPower, 2)} veIXS voting power.
</TYPE.body3>
</Box>
</Box>
)
}
Expand Down
Loading

0 comments on commit 39c6883

Please sign in to comment.