Skip to content

Commit

Permalink
Locked gno claim fixes (#460)
Browse files Browse the repository at this point in the history
* Added some fixes

* Update combined balance calculation

* Added comments for locked GNO timestamps

* Move locked GNO start date to const file

* Moved some other locked GNO dates to const file

* Added some PR updates

* Small update
  • Loading branch information
nenadV91 committed Apr 28, 2022
1 parent 407f9f5 commit 6878a5a
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 47 deletions.
12 changes: 10 additions & 2 deletions src/custom/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ export const GP_VAULT_RELAYER: Partial<Record<number, string>> = {
export const V_COW_CONTRACT_ADDRESS: Record<number, string> = {
[ChainId.MAINNET]: '0xd057b63f5e69cf1b929b356b579cba08d7688048',
[ChainId.XDAI]: '0xc20C9C13E853fc64d054b73fF21d3636B2d97eaB',
[ChainId.RINKEBY]: '0x9386177e95A853070076Df2403b9D547D653126D', // <- TODO: change these at some point after testing is done
[ChainId.RINKEBY]: '0x9386177e95A853070076Df2403b9D547D653126D',
}

export const COW_CONTRACT_ADDRESS: Record<number, string> = {
[ChainId.MAINNET]: '0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB',
[ChainId.XDAI]: '0x177127622c4A00F3d409B75571e12cB3c8973d3c',
[ChainId.RINKEBY]: '0xbdf1e19f8c78A77fb741b44EbA5e4c0C8DBAeF91', // <- TODO: change these at some point after testing is done
[ChainId.RINKEBY]: '0xbdf1e19f8c78A77fb741b44EbA5e4c0C8DBAeF91',
}

// See https://github.com/gnosis/gp-v2-contracts/commit/821b5a8da213297b0f7f1d8b17c893c5627020af#diff-12bbbe13cd5cf42d639e34a39d8795021ba40d3ee1e1a8282df652eb161a11d6R13
Expand Down Expand Up @@ -141,3 +141,11 @@ export const WAITING_TIME_RECONNECT_LAST_PROVIDER = 15000 // 15s
// COWSWAP = new quote endpoint
// LEGACY = price racing logic (checking 0x, gp, paraswap, etc)
export const DEFAULT_GP_PRICE_STRATEGY = 'COWSWAP'

// Start date of COW vesting for locked GNO
export const LOCKED_GNO_VESTING_START_DATE = new Date('02-11-2022 13:05:15 GMT')

// These values match the vesting contract configuration (see contract's `cliffTime` and `duration` fields).
// They are fixed and will never change.
export const LOCKED_GNO_VESTING_START_TIME = 1644584715000
export const LOCKED_GNO_VESTING_DURATION = 126144000000 // 4 years
16 changes: 16 additions & 0 deletions src/custom/constants/tokens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,19 @@ export const ADDRESS_IMAGE_OVERRIDE = {
[V_COW_TOKEN_MAINNET.address]: vCowLogo,
[COW_TOKEN_MAINNET.address]: cowLogo,
}

/**
* Addresses related to COW vesting for Locked GNO
* These are used in src/custom/pages/Profile/LockedGnoVesting hooks and index files
*/
export const MERKLE_DROP_CONTRACT_ADDRESSES: Record<number, string> = {
[SupportedChainId.MAINNET]: '0x64646f112FfD6F1B7533359CFaAF7998F23C8c40',
[SupportedChainId.RINKEBY]: '0x5444c4AFb2ec7f7367C10F7732b8558650c5899F',
[SupportedChainId.XDAI]: '0x3d610e917130f9D036e85A030596807f57e11093',
}

export const TOKEN_DISTRO_CONTRACT_ADDRESSES: Record<number, string> = {
[SupportedChainId.MAINNET]: '0x68FFAaC7A431f276fe73604C127Bd78E49070c92',
[SupportedChainId.RINKEBY]: '0xeBA8CE5b23c054f1511F8fF5114d848329B8258d',
[SupportedChainId.XDAI]: '0x3d610e917130f9D036e85A030596807f57e11093',
}
33 changes: 12 additions & 21 deletions src/custom/pages/Profile/LockedGnoVesting/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { ContractTransaction } from '@ethersproject/contracts'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useActiveWeb3React } from 'hooks/web3'
Expand All @@ -12,29 +12,19 @@ import { COW as COW_TOKENS } from 'constants/tokens'
import { SupportedChainId } from 'constants/chains'
import { OperationType } from 'components/TransactionConfirmationModal'
import { fetchClaim } from './claimData'
import { MERKLE_DROP_CONTRACT_ADDRESSES, TOKEN_DISTRO_CONTRACT_ADDRESSES } from 'constants/tokens'
import { LOCKED_GNO_VESTING_START_TIME, LOCKED_GNO_VESTING_DURATION } from 'constants/index'

// We just generally use the mainnet version. We don't read from the contract anyways so the address doesn't matter
const COW = COW_TOKENS[SupportedChainId.MAINNET]

export const MERKLE_DROP_CONTRACT_ADDRESSES: Record<number, string> = {
[SupportedChainId.MAINNET]: '0x64646f112FfD6F1B7533359CFaAF7998F23C8c40',
[SupportedChainId.RINKEBY]: '0x5444c4AFb2ec7f7367C10F7732b8558650c5899F',
[SupportedChainId.XDAI]: '0x48D8566887F8c7d99757CE29c2cD39962bfd9547',
}

const useMerkleDropContract = () => useContract<MerkleDrop>(MERKLE_DROP_CONTRACT_ADDRESSES, MERKLE_DROP_ABI, true)

export const TOKEN_DISTRO_CONTRACT_ADDRESSES: Record<number, string> = {
[SupportedChainId.MAINNET]: '0x68FFAaC7A431f276fe73604C127Bd78E49070c92',
[SupportedChainId.RINKEBY]: '0xeBA8CE5b23c054f1511F8fF5114d848329B8258d',
[SupportedChainId.XDAI]: '0x3d610e917130f9D036e85A030596807f57e11093',
}

const useTokenDistroContract = () => useContract<TokenDistro>(TOKEN_DISTRO_CONTRACT_ADDRESSES, TOKEN_DISTRO_ABI, true)

export const useAllocation = (): CurrencyAmount<Token> => {
const { chainId, account } = useActiveWeb3React()
const [allocation, setAllocation] = useState(CurrencyAmount.fromRawAmount(COW, 0))
const initialAllocation = useRef(CurrencyAmount.fromRawAmount(COW, 0))
const [allocation, setAllocation] = useState(initialAllocation.current)

useEffect(() => {
let canceled = false
Expand All @@ -44,26 +34,27 @@ export const useAllocation = (): CurrencyAmount<Token> => {
setAllocation(CurrencyAmount.fromRawAmount(COW, claim?.amount ?? 0))
}
})
} else {
setAllocation(initialAllocation.current)
}
return () => {
canceled = true
}
}, [chainId, account])
}, [chainId, account, initialAllocation])

return allocation
}

const START_TIME = 1644584715000
const DURATION = 126144000000

export const useCowFromLockedGnoBalances = () => {
const { account } = useActiveWeb3React()
const allocated = useAllocation()
const vested = allocated.multiply(Math.min(Date.now() - START_TIME, DURATION)).divide(DURATION)
const vested = allocated
.multiply(Math.min(Date.now() - LOCKED_GNO_VESTING_START_TIME, LOCKED_GNO_VESTING_DURATION))
.divide(LOCKED_GNO_VESTING_DURATION)

const tokenDistro = useTokenDistroContract()
const { result, loading } = useSingleCallResult(allocated.greaterThan(0) ? tokenDistro : null, 'balances', [
account || undefined,
account ?? undefined,
])
const claimed = useMemo(() => CurrencyAmount.fromRawAmount(COW, result ? result.claimed.toString() : 0), [result])

Expand Down
5 changes: 3 additions & 2 deletions src/custom/pages/Profile/LockedGnoVesting/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import { getBlockExplorerUrl } from 'utils'
import { formatDateWithTimezone } from 'utils/time'
import { SupportedChainId as ChainId } from 'constants/chains'
import { useActiveWeb3React } from 'hooks/web3'
import { MERKLE_DROP_CONTRACT_ADDRESSES, TOKEN_DISTRO_CONTRACT_ADDRESSES } from 'pages/Profile/LockedGnoVesting/hooks'
import { MERKLE_DROP_CONTRACT_ADDRESSES, TOKEN_DISTRO_CONTRACT_ADDRESSES } from 'constants/tokens'
import { LOCKED_GNO_VESTING_START_DATE } from 'constants/index'
import { useCowFromLockedGnoBalances, useClaimCowFromLockedGnoCallback } from './hooks'

enum ClaimStatus {
Expand Down Expand Up @@ -128,7 +129,7 @@ const LockedGnoVesting: React.FC<Props> = ({ openModal, closeModal }: Props) =>
<div>
<p>
<strong>COW vesting from the GNO lock</strong> is vested linearly over four years, starting on{' '}
{formatDateWithTimezone(new Date('02-11-2022 13:05:15 GMT'))}.
{formatDateWithTimezone(LOCKED_GNO_VESTING_START_DATE)}.
</p>
<p>Each time you claim, you will receive the entire claimable amount.</p>
</div>
Expand Down
55 changes: 38 additions & 17 deletions src/custom/pages/Profile/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { Txt } from 'assets/styles/styled'
import {
FlexCol,
Expand Down Expand Up @@ -57,9 +57,13 @@ import { Link } from 'react-router-dom'
import CopyHelper from 'components/Copy'
import { SwapVCowStatus } from 'state/cowToken/actions'
import LockedGnoVesting from './LockedGnoVesting'
import useBlockNumber from 'lib/hooks/useBlockNumber'

const COW_DECIMALS = COW[ChainId.MAINNET].decimals

// Number of blocks to wait before we re-enable the swap COW -> vCOW button after confirmation
const BLOCKS_TO_WAIT = 2

export default function Profile() {
const referralLink = useReferralLink()
const { account, chainId = ChainId.MAINNET, library } = useActiveWeb3React()
Expand All @@ -69,6 +73,10 @@ export default function Profile() {
const hasOrders = useHasOrders(account)
const selectedAddress = useAddress()

const blockNumber = useBlockNumber()
const [confirmationBlock, setConfirmationBlock] = useState<undefined | number>(undefined)
const [shouldUpdate, setShouldUpdate] = useState<boolean>(false)

const setSwapVCowStatus = useSetSwapVCowStatus()
const swapVCowStatus = useSwapVCowStatus()

Expand All @@ -78,29 +86,31 @@ export default function Profile() {
// vCow balance values
const { unvested, vested, total, isLoading: isVCowLoading } = useVCowData()

// Boolean flags
const hasVestedBalance = vested && !vested.equalTo(0)
const hasVCowBalance = total && !total.equalTo(0)

const isSwapPending = swapVCowStatus === SwapVCowStatus.SUBMITTED
const isSwapInitial = swapVCowStatus === SwapVCowStatus.INITIAL
const isSwapConfirmed = swapVCowStatus === SwapVCowStatus.CONFIRMED
const isSwapDisabled = Boolean(
!hasVestedBalance || !isSwapInitial || isSwapPending || isSwapConfirmed || shouldUpdate
)

const cowBalance = formatSmartLocaleAware(cow, AMOUNT_PRECISION) || '0'
const cowBalanceMax = formatMax(cow, COW_DECIMALS) || '0'
const vCowBalanceVested = formatSmartLocaleAware(vested, AMOUNT_PRECISION) || '0'
const vCowBalanceVestedMax = vested ? formatMax(vested, COW_DECIMALS) : '0'
const vCowBalanceVested = formatSmartLocaleAware(shouldUpdate ? undefined : vested, AMOUNT_PRECISION) || '0'
const vCowBalanceVestedMax = vested ? formatMax(shouldUpdate ? undefined : vested, COW_DECIMALS) : '0'
const vCowBalanceUnvested = formatSmartLocaleAware(unvested, AMOUNT_PRECISION) || '0'
const vCowBalance = formatSmartLocaleAware(total, AMOUNT_PRECISION) || '0'
const vCowBalanceMax = total ? formatMax(total, COW_DECIMALS) : '0'

const hasVestedBalance = vested && !vested.equalTo(0)
const hasVCowBalance = total && !total.equalTo(0)

// Init modal hooks
const { handleSetError, handleCloseError, ErrorModal } = useErrorModal()
const { TransactionConfirmationModal, openModal, closeModal } = useTransactionConfirmationModal(
OperationType.CONVERT_VCOW
)

// Boolean flags
const isSwapPending = swapVCowStatus === SwapVCowStatus.SUBMITTED
const isSwapInitial = swapVCowStatus === SwapVCowStatus.INITIAL
const isSwapConfirmed = swapVCowStatus === SwapVCowStatus.CONFIRMED
const isSwapDisabled = Boolean(!hasVestedBalance || !isSwapInitial || isSwapPending || isSwapConfirmed)

// Handle swaping
const { swapCallback } = useSwapVCowCallback({
openModal,
Expand Down Expand Up @@ -185,13 +195,24 @@ export default function Profile() {
return content
}, [isSwapConfirmed, isSwapPending])

// Fixes the issue with change in status after swap confirmation
// Makes sure to wait 2 blocks after confirmation to enable the swap button again
useEffect(() => {
if (isSwapConfirmed && hasVestedBalance) {
setTimeout(() => {
setSwapVCowStatus(SwapVCowStatus.INITIAL)
}, 5000)
if (isSwapConfirmed && !confirmationBlock) {
setConfirmationBlock(blockNumber)
setShouldUpdate(true)
}

if (!confirmationBlock || !blockNumber) {
return
}

if (isSwapConfirmed && blockNumber - confirmationBlock > BLOCKS_TO_WAIT && hasVestedBalance) {
setSwapVCowStatus(SwapVCowStatus.INITIAL)
setConfirmationBlock(undefined)
setShouldUpdate(false)
}
}, [hasVestedBalance, isSwapConfirmed, setSwapVCowStatus, vested])
}, [blockNumber, confirmationBlock, hasVestedBalance, isSwapConfirmed, setSwapVCowStatus, shouldUpdate])

const currencyCOW = COW[chainId]

Expand Down
5 changes: 3 additions & 2 deletions src/custom/state/claim/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { VCow as VCowType } from 'abis/types'

import { useVCowContract } from 'hooks/useContract'
import { useActiveWeb3React } from 'hooks/web3'
import { useSingleContractMultipleData } from 'state/multicall/hooks'
import { useSingleContractMultipleData } from 'lib/hooks/multicall'
import { useTransactionAdder } from 'state/enhancedTransactions/hooks'

import { GpEther, V_COW } from 'constants/tokens'
Expand Down Expand Up @@ -59,6 +59,7 @@ import { AMOUNT_PRECISION } from 'constants/index'
import useIsMounted from 'hooks/useIsMounted'
import { ChainId } from '@uniswap/sdk'
import { ClaimInfo } from 'state/claim/reducer'
import { CallState } from '@uniswap/redux-multicall'

export { useUserClaimData, useUserHasAvailableClaim } from '@src/state/claim/hooks'

Expand Down Expand Up @@ -164,7 +165,7 @@ export function useClassifiedUserClaims(account: Account, optionalChainId?: Supp

let isContractCallLoading = false

results.forEach((result, index) => {
results.forEach((result: CallState, index: number) => {
const claim = userClaims[index]

// Use the loading state from the multicall results
Expand Down
14 changes: 11 additions & 3 deletions src/custom/state/cowToken/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { setSwapVCowStatus, SwapVCowStatus } from './actions'
import { OperationType } from 'components/TransactionConfirmationModal'
import { APPROVE_GAS_LIMIT_DEFAULT } from 'hooks/useApproveCallback/useApproveCallbackMod'
import { useTokenBalance } from 'state/wallet/hooks'
import { useAllocation } from 'pages/Profile/LockedGnoVesting/hooks'
import { useCowFromLockedGnoBalances } from 'pages/Profile/LockedGnoVesting/hooks'
import { SupportedChainId } from 'constants/chains'
import JSBI from 'jsbi'

Expand Down Expand Up @@ -170,9 +170,17 @@ export function useCowBalance() {
export function useCombinedBalance() {
const { chainId, account } = useActiveWeb3React()
const { total: vCowBalance } = useVCowData()
const lockedGnoBalance = useAllocation()
const { allocated, claimed } = useCowFromLockedGnoBalances()
const cowBalance = useCowBalance()

const lockedGnoBalance = useMemo(() => {
if (!allocated || !claimed) {
return
}

return JSBI.subtract(allocated.quotient, claimed.quotient)
}, [allocated, claimed])

return useMemo(() => {
let tmpBalance = JSBI.BigInt(0)

Expand All @@ -182,7 +190,7 @@ export function useCombinedBalance() {

if (account) {
if (vCowBalance) tmpBalance = JSBI.add(tmpBalance, vCowBalance.quotient)
if (lockedGnoBalance) tmpBalance = JSBI.add(tmpBalance, lockedGnoBalance.quotient)
if (lockedGnoBalance) tmpBalance = JSBI.add(tmpBalance, lockedGnoBalance)
if (cowBalance) tmpBalance = JSBI.add(tmpBalance, cowBalance.quotient)
}

Expand Down

0 comments on commit 6878a5a

Please sign in to comment.