Skip to content

Commit

Permalink
Locked gno claim 2 (#405)
Browse files Browse the repository at this point in the history
* locked GNO vesting contract addresses and merkle proof data

strip down ABI

* fetch balances

* some bugfixes

* claim flow

* cleanup

* some cosmetics

* chunk up the merkle proof data

* improve profile page fluid section layout

* remove accidentally committed script

* Fix code style issues with Prettier

* work around a bug in the transpiler

* fix layout issue on small screens

* move claim data into extra repo

* use path aliases

* fix duplicate use of transaction confirmation modal, which is not supported

* some minor fixes

* Added vCow locked GNO balance to header button

* Add MOO sounds to the claim from locked GNO transaction

* Add contract link buttons to locked GNO card

* Updated locked GNO contract link

* use new rinkeby contracts (#407)

* Reset confirmed status after some time

* Removed some unnecessary code from the merge

* Fixed some formatting issues

* Add confirmed state for convert vCow to cow

* Fix for tooltip styles

* Fix for failed transactions

* Locked gno claim 2 fixes (#453)

* use new rinkeby contracts

* fix merkle drop contract address on GC

* more specific names

* localize vesting start date

* improve copy

* fix contract address link

* Add cow from locked GNO to combined balance (#456)

* Add cow from locked GNO to combined balance

* Add shimmer effect and handle disconnect

* Locked gno claim fixes (#460)

* 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

* After rebase fixes

* Temporary fix for missing cards on profile page

* Fix for governance card link (#489)

* Fix for convert button

* Fix for locked GNO claim button on wallet change

Co-authored-by: Jan-Felix <[email protected]>
Co-authored-by: Lint Action <[email protected]>
  • Loading branch information
3 people authored Apr 29, 2022
1 parent f68f8dc commit 39e1867
Show file tree
Hide file tree
Showing 27 changed files with 844 additions and 380 deletions.
2 changes: 1 addition & 1 deletion src/custom/abis/TokenDistro.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@
"stateMutability": "nonpayable",
"type": "function"
}
]
]
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export function ActivityDetails(props: {
let inputToken = activityDerivedState?.order?.inputToken || null
let outputToken = activityDerivedState?.order?.outputToken || null

if (enhancedTransaction?.swapVCow) {
if (enhancedTransaction?.swapVCow || enhancedTransaction?.swapLockedGNOvCow) {
inputToken = V_COW[chainId]
outputToken = COW[chainId]
}
Expand Down
41 changes: 35 additions & 6 deletions src/custom/components/CowBalanceButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Trans } from '@lingui/macro'
import styled from 'styled-components/macro'
import styled, { css } from 'styled-components/macro'
import CowProtocolLogo from 'components/CowProtocolLogo'
import { useCombinedBalance } from 'state/cowToken/hooks'
import { ChainId } from 'state/lists/actions/actionsMod'
import { formatMax, formatSmartLocaleAware } from 'utils/format'
import { AMOUNT_PRECISION } from 'constants/index'
import { COW } from 'constants/tokens'

export const Wrapper = styled.div`
export const Wrapper = styled.div<{ isLoading: boolean }>`
${({ theme }) => theme.card.boxShadow};
color: ${({ theme }) => theme.text1};
padding: 0 12px;
Expand All @@ -20,6 +20,35 @@ export const Wrapper = styled.div`
border-radius: 12px;
pointer-events: auto;
${({ theme, isLoading }) =>
isLoading &&
css`
overflow: hidden;
&::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0,
${theme.shimmer1} 20%,
${theme.shimmer2} 60%,
rgba(255, 255, 255, 0)
);
animation: shimmer 2s infinite;
content: '';
}
@keyframes shimmer {
100% {
transform: translateX(100%);
}
}
`}
> b {
margin: 0 0 0 5px;
color: inherit;
Expand Down Expand Up @@ -48,13 +77,13 @@ interface CowBalanceButtonProps {
const COW_DECIMALS = COW[ChainId.MAINNET].decimals

export default function CowBalanceButton({ onClick }: CowBalanceButtonProps) {
const combinedBalance = useCombinedBalance()
const { balance, isLoading } = useCombinedBalance()

const formattedBalance = formatSmartLocaleAware(combinedBalance, AMOUNT_PRECISION)
const formattedMaxBalance = formatMax(combinedBalance, COW_DECIMALS)
const formattedBalance = formatSmartLocaleAware(balance, AMOUNT_PRECISION)
const formattedMaxBalance = formatMax(balance, COW_DECIMALS)

return (
<Wrapper onClick={onClick}>
<Wrapper isLoading={isLoading} onClick={onClick}>
<CowProtocolLogo />
<b title={formattedMaxBalance && `${formattedMaxBalance} (v)COW`}>
<Trans>{formattedBalance || 0}</Trans>
Expand Down
6 changes: 5 additions & 1 deletion src/custom/components/TransactionConfirmationModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ export enum OperationType {
ORDER_SIGN,
ORDER_CANCEL,
CONVERT_VCOW,
CLAIM_VESTED_COW,
}

function getWalletNameLabel(walletType: WalletType): string {
Expand Down Expand Up @@ -384,7 +385,8 @@ function getOperationMessage(operationType: OperationType, chainId: number): str
return 'Revoking token approval'
case OperationType.CONVERT_VCOW:
return 'Converting vCOW to COW'

case OperationType.CLAIM_VESTED_COW:
return 'Claiming vested COW'
default:
return 'Almost there!'
}
Expand All @@ -406,6 +408,8 @@ function getOperationLabel(operationType: OperationType): string {
return t`cancellation`
case OperationType.CONVERT_VCOW:
return t`vCOW conversion`
case OperationType.CLAIM_VESTED_COW:
return t`vested COW claim`
}
}

Expand Down
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',
}
28 changes: 2 additions & 26 deletions src/custom/hooks/useCowBalanceAndSubsidy.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,13 @@
import { useMemo } from 'react'
import { BigNumber } from 'bignumber.js'
import { JSBI } from '@uniswap/sdk'
import { CurrencyAmount } from '@uniswap/sdk-core'

import { getDiscountFromBalance } from 'components/CowSubsidyModal/utils'
import { useVCowData } from 'state/cowToken/hooks'
import { useTokenBalance } from 'state/wallet/hooks'
import { useActiveWeb3React } from 'hooks/web3'

import { COW } from 'constants/tokens'
import { SupportedChainId } from 'constants/chains'
import { useCombinedBalance } from 'state/cowToken/hooks'
import { COW_SUBSIDY_DATA } from 'components/CowSubsidyModal/constants'

const ZERO_BALANCE_SUBSIDY = { subsidy: { tier: 0, discount: COW_SUBSIDY_DATA[0][1] }, balance: undefined }

export default function useCowBalanceAndSubsidy() {
const { account, chainId } = useActiveWeb3React()
// vcow balance
const { total: vCowBalance } = useVCowData()
// Cow balanc
const cowBalance = useTokenBalance(account || undefined, chainId ? COW[chainId] : undefined)

const balance = useMemo(() => {
if (vCowBalance && cowBalance) {
const totalBalance = JSBI.add(vCowBalance.quotient, cowBalance.quotient)

// COW and vCOW safely have the same identifying properties: decimals
// so we make JSBI maths and create a new currency as adding vCow and Cow throws and exception
return CurrencyAmount.fromRawAmount(COW[chainId || SupportedChainId.MAINNET], totalBalance)
} else {
return cowBalance || vCowBalance
}
}, [chainId, cowBalance, vCowBalance])
const { balance } = useCombinedBalance()

return useMemo(() => {
if (!balance || balance?.equalTo('0')) return ZERO_BALANCE_SUBSIDY
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[
"0x00000000cc0b822819f03424dacf9077fdaa58a3",
"0x06d4b89475a53111e8939b1ae8af7e14804a5186",
"0x0f1c44076d4cf58e1458a418393e6be57f3519f9",
"0x1702f2d0df7c99011a690461c34e51bd81cbab48",
"0x1ec30dec8378e6df6988da0e8a2b49b17055f0aa",
"0x26404746cd2228018f86c98f580e5030b06ff3c8",
"0x3047fa36aa687014f3ead30e4873adc3f58467bf",
"0x3812364136fe59a5db130666925be092050bd8ba",
"0x4193e4ca2241d4d16356a94da537e3c3a600678c",
"0x499487f6be895b71cf57881c22d5f6d855fcb8a2",
"0x52077db357420b6999b6b78c46c3ab2fc596fba8",
"0x5aa6a1d420bf8fa45d141af73e9230e1e8c3dc16",
"0x61fde66b0a208be7096ffa314d7cd92f519b2352",
"0x6b504204a85e0231b1a1b1926b9264939f29c65e",
"0x7246a274656e797fab4b02eb1e0581d46f0358e2",
"0x79a074122be96e1fc9bdd32dba04759421d12f90",
"0x817a33e007afb85ec23da7de231c1902cf4686c1",
"0x882289186d7b1b9cd35191780761ee80975f0fd1",
"0x90fa3f3c3a290ee19bbc94dc539dede8e21ce28f",
"0x99c72eb5c22c38137541ef4b9a2fd0316c42b510",
"0xa1e63c0f203df1314153ad6648bd38fd99774d85",
"0xabe8430e3f0beca32915da84e530f81a01379953",
"0xb6271e5a916f3764c5d3387dff14922249d9d70f",
"0xbf8ab1e63a9b883a6b5a396cb5a36138af6e020a",
"0xc789026a0f0b15c532c77405491331997f2b2bbc",
"0xce57ebed9ac38402dcaa44f65a1c9b04e26b8283",
"0xd64c69277d2c842c9ab17683479ac063aa35d4f5",
"0xdfeb9c25186aadf1487979be7da312f95fd55275",
"0xe6f44434c052c00d280bc236c8f58d8a9e42eec8",
"0xee9ec3273c52ea783b86cfefab32b50b1b26fce7",
"0xf6330ad6f2d488f6f29cf45e8fa5bf798e3b8df3",
"0xff36b9cb75c9178841d8b75baf9776bfa59fbe9d"
]
43 changes: 43 additions & 0 deletions src/custom/pages/Profile/LockedGnoVesting/claimData/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { fetchClaim } from '.'

describe('fetchClaim', () => {
it('should fetch the correct amount and merkle proof for 0x01eda16f6a6c3b051ecc63b0d93c8c3a27491dc2 on mainnet', async () => {
const claim = await fetchClaim('0x01eda16f6a6c3b051ecc63b0d93c8c3a27491dc2', 1)
expect(claim?.amount).toEqual('0x03e7eed24f376724a0')
expect(claim?.proof).toEqual([
'0x60d453c5e23c77b881dd9ff7529fcf464edebd491d5ea8bf5e11e23cc6f47480',
'0x0877e91858fac51a42947fbd26999874691a9e888cf34436fa66c0028ab7bbd3',
'0xfe29bdb74e8f410a109fde3f458c927507e618808b8413ed1c5fd323c5c9a628',
'0x6c650faf73c5b63f83db90a590bd33ba56643ebdc302511b31d826d19396816b',
'0x9144ff27437147862213a49a1b7fe83f3a4f7d63a2077f282d4e950fb1fca985',
'0x3c34f4ba3f7f0377e3cf9cf529dab73fd7ff4fe6dfbac59b69f04260b8b33cd9',
'0x91b09c40dbce8d252add3b512793d4b3cfcbe3b46c5c9d6d56ba993ecf770470',
'0xe97c601df9a399f368546575516b533f0438e87866e46e4694687e94d0241218',
'0x1d6a881baea3b1685912b1f308eb9373e808fb3cfff389ffa0cec82e3eb0ea1a',
'0xd84e4549adcf49890f091546b3f873d102368cb115fac46655102eff21a1291b',
])
})

it('should fetch the correct amount and merkle proof for 0x00645dd21310882cc32399abcb54e0a05b3b5d1d on Gnosis Chain', async () => {
const claim = await fetchClaim('0x00645dd21310882cc32399abcb54e0a05b3b5d1d', 100)
expect(claim?.amount).toEqual('0x58b0138dc7e4ccd980')
expect(claim?.proof).toEqual([
'0x65675d39883f109dec07e3d7b4613c412c81ee3311fd093cff00359e52d739c1',
'0x9638aa10de5885b599958cddae3f3ab119f11b61d32e03741198d15c9208e7fb',
'0xf2b9715498991ab98a9e819889268409a351b9754fca9a1b05946f45829699d6',
'0x00c71fda7f57a05619bdc2e6260970a7b4dd726ad0b87717818c33ec28d28e6c',
'0x7631f21c6d503b95f7eb594f0ae219b5743268dd9cbabb71956aeff8ca333083',
'0xb54a34f057a323d8de571f623f8390c02f8d075e1613e8dc8d8c267d8cddb957',
'0xcee373a777b7f6eb5c9b6855ce2fc88aedc4accc4bbb27371f253c23efe3fdf4',
'0xdd4dc16f1f24d8f6f4fe323caeaa275b457b0d9340510eeb80c679b08bc94304',
'0x20a84c8d8e7d15409c6ea4591310bdc57bebe4489b53036d10f9e0467f339600',
'0xd0c880019076ffcd06d15b98b049e82d349323a6da4871484964e2c5b4e446a2',
'0x4b71fa3ace335c8703d193770bcef717053532750939e342c416afef044a3eba',
])
})

it('should return null for ineligible addresses', async () => {
const claim = await fetchClaim('0x0000000000000000000000000000000000000000', 4)
expect(claim).toBe(null)
})
})
60 changes: 60 additions & 0 deletions src/custom/pages/Profile/LockedGnoVesting/claimData/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { SupportedChainId } from 'constants/chains'
import mainnetIndex from './mainnet.json'
import rinkebyIndex from './rinkeby.json'
import gnosisChainIndex from './gnosisChain.json'

interface Claim {
index: number
amount: string
proof: string[]
}

const indexFiles = {
[SupportedChainId.MAINNET]: mainnetIndex,
[SupportedChainId.RINKEBY]: rinkebyIndex,
[SupportedChainId.XDAI]: gnosisChainIndex,
}

const chainNames = {
[SupportedChainId.MAINNET]: 'mainnet',
[SupportedChainId.RINKEBY]: 'rinkeby',
[SupportedChainId.XDAI]: 'gnosisChain',
}

const DISTRO_REPO_BRANCH_NAME = 'main'

export const fetchClaim = async (address: string, chainId: SupportedChainId): Promise<Claim | null> => {
const lowerCaseAddress = address.toLowerCase()

const indexFile = indexFiles[chainId]
const chainName = chainNames[chainId]
const chunkIndex = lookupChunkIndex(indexFile, lowerCaseAddress)
if (chunkIndex === -1) return null // address is lower than the lowest address in the index, which means it's ineligible

const chunk = await fetchChunk(`${chainName}/chunk_${chunkIndex}.json`)
return chunk[lowerCaseAddress] || null
}

// The merkle proof data has been sorted by address in ascending order and then chunked up.
// see: https://github.com/gnosis/locked-gno-cow-merkle-distro/blob/main/chunkClaimData.js
// Our index json gives the first address of each chunk.
// This function returns the chunk index for the given address, or -1 if the address is lower than the lowest address in the index.
const lookupChunkIndex = (
chunkIndexJson: typeof mainnetIndex | typeof rinkebyIndex | typeof gnosisChainIndex,
address: string
) => {
let nextChunkIndex = chunkIndexJson.findIndex((a) => address < a)
if (nextChunkIndex === -1) nextChunkIndex = chunkIndexJson.length
return nextChunkIndex - 1
}

const chunkCache = new Map<string, Promise<Record<string, Claim>>>()
const fetchChunk = (path: string) => {
const promise =
chunkCache.get(path) ??
(fetch(
`https://raw.githubusercontent.com/gnosis/locked-gno-cow-merkle-distro/${DISTRO_REPO_BRANCH_NAME}/${path}`
).then((res) => res.json()) as Promise<Record<string, Claim>>)
chunkCache.set(path, promise)
return promise
}
18 changes: 18 additions & 0 deletions src/custom/pages/Profile/LockedGnoVesting/claimData/mainnet.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
"0x00000081c22fe36e0b47a4f1c67041301f945014",
"0x0d78a4b2657dd319ae1f47ce5e529d03e984cef5",
"0x2059a96525c364560a806ca035a40c9f379ebca9",
"0x32ae635f5136adb181a442cc890be39263bc13c8",
"0x44dde9695027ca6acb7cdf3b361c37056122e4af",
"0x51ffd343d6fecab9e9c5640f0e6a67dec31bc76c",
"0x63829da9dc103b63d984391f7580eddb220ba6d9",
"0x773d161310d07cafc6f767ca24f43e52163b9be6",
"0x8642214d3cb4eb38ee618be37f78dd74a3093869",
"0x98b7a46a33d60f71522115ab7e2ec8f0fc294038",
"0xaa942b60823be690a17576207b819807891d71f6",
"0xbbf7bfc4d9acce27082613e2c14d9fe8d1a54a29",
"0xcb11673592cc6c4b9584f33bbba6c6cf07dde3f7",
"0xdb0b39341290a30510e46e7692195fe16097e0df",
"0xe91f64ca1da165ca8437686b69a022156550837b",
"0xf64c2e6679e089cd1c3e303ca1245d4941747700"
]
Loading

0 comments on commit 39e1867

Please sign in to comment.