Skip to content
This repository has been archived by the owner on Jun 24, 2022. It is now read-only.

Commit

Permalink
Claim dynamic prices (#2182)
Browse files Browse the repository at this point in the history
* Added dependecy on @gnosis.pm/cow-token

* Renamed vCOW abi function wethPrice to nativeTokenPrice

* Importing ClaimType from cow-token contract package

* Refactoring imports on claim/hooks

* Revert "Renamed vCOW abi function wethPrice to nativeTokenPrice"

This reverts commit c1ec593.

* New hooks for price fetching from the contract

* Using new hooks and removing hardcoded price values

* Added TODO about new price fn name

* Revert "Refactoring imports on claim/hooks"

This reverts commit 50bafab.

* Revert "Importing ClaimType from cow-token contract package"

This reverts commit b2c79f0.

* Revert "Added dependecy on @gnosis.pm/cow-token"

This reverts commit f0bbea4.

Co-authored-by: Leandro <[email protected]>
  • Loading branch information
alfetopito and Leandro authored Jan 18, 2022
1 parent 45c0b23 commit 1c15ca5
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 33 deletions.
101 changes: 75 additions & 26 deletions src/custom/state/claim/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ms from 'ms.macro'
import { CurrencyAmount, Price, Token } from '@uniswap/sdk-core'
import { TransactionResponse } from '@ethersproject/providers'
import { parseUnits } from '@ethersproject/units'
import { BigNumber } from '@ethersproject/bignumber'

import { VCow as VCowType } from 'abis/types'

Expand Down Expand Up @@ -63,17 +64,6 @@ const ONE_VCOW = CurrencyAmount.fromRawAmount(
V_COW[SupportedChainId.RINKEBY],
parseUnits('1', V_COW[SupportedChainId.RINKEBY].decimals).toString()
)
// TODO: these values came from the test contract, might be different on real deployment
// Network variable price
export const NATIVE_TOKEN_PRICE: { [chain in SupportedChainId]: string } = {
[SupportedChainId.MAINNET]: '37500000000000', // '0.0000375' WETH (18 decimals) per vCOW, in wei
[SupportedChainId.RINKEBY]: '37500000000000', // assuming Rinkeby has same price as Mainnet
[SupportedChainId.XDAI]: '150000000000000000', // TODO: wild guess, wxDAI is same price as USDC
}

// Same on all networks. Actually, likely available only on Mainnet (and Rinkeby)
export const GNO_PRICE = '375000000000000' // '0.000375' GNO (18 decimals) per vCOW, in atoms
export const USDC_PRICE = '150000' // '0.15' USDC (6 decimals) per vCOW, in atoms

// Constants regarding investment time windows
const INVESTMENT_TIME = ms`2 weeks`
Expand Down Expand Up @@ -339,6 +329,55 @@ export function useClaimTimeInfo(): ClaimTimeInfo {
return { deployment, investmentDeadline, airdropDeadline, isInvestmentWindowOpen, isAirdropWindowOpen }
}

export function useNativeTokenPrice(): string | null {
// TODO: rename fn to `nativeTokenPrice` and revert e7197dd27287ff1460a7d7af22734cae938b8c83
// when there's a new deployment
return _useVCowPriceForToken('wethPrice')
}

export function useGnoPrice(): string | null {
return _useVCowPriceForToken('gnoPrice')
}

export function useUsdcPrice(): string | null {
return _useVCowPriceForToken('usdcPrice')
}

/**
* Generic hook for fetching contract value for the many prices
*/
function _useVCowPriceForToken(priceFnName: 'wethPrice' | 'gnoPrice' | 'usdcPrice'): string | null {
const { chainId } = useActiveWeb3React()
const vCowContract = useVCowContract()

const [price, setPrice] = useState<string | null>(null)

useEffect(() => {
if (!chainId || !vCowContract) {
return
}
console.debug(`_useVCowPriceForToken::fetching price for `, priceFnName)

vCowContract[priceFnName]().then((price: BigNumber) => setPrice(price.toString()))
}, [chainId, priceFnName, vCowContract])

return price
}

export type VCowPrices = {
native: string | null
gno: string | null
usdc: string | null
}

export function useVCowPrices(): VCowPrices {
const native = useNativeTokenPrice()
const gno = useGnoPrice()
const usdc = useUsdcPrice()

return useMemo(() => ({ native, gno, usdc }), [gno, native, usdc])
}

/**
* Helper function that checks whether selected investment options are still available
*
Expand Down Expand Up @@ -378,6 +417,7 @@ export function useClaimCallback(account: string | null | undefined): {
const { chainId, account: connectedAccount } = useActiveWeb3React()
const claims = useUserAvailableClaims(account)
const vCowContract = useVCowContract()
const nativeTokenPrice = useNativeTokenPrice()

const { isInvestmentWindowOpen, isAirdropWindowOpen } = useClaimTimeInfo()

Expand Down Expand Up @@ -421,14 +461,21 @@ export function useClaimCallback(account: string | null | undefined): {
!connectedAccount ||
!chainId ||
!vCowContract ||
!vCowToken
!vCowToken ||
!nativeTokenPrice
) {
throw new Error("Not initialized, can't claim")
}

_validateClaimable(claims, claimInput, isInvestmentWindowOpen, isAirdropWindowOpen)

const { args, totalClaimedAmount } = _getClaimManyArgs({ claimInput, claims, account, connectedAccount, chainId })
const { args, totalClaimedAmount } = _getClaimManyArgs({
claimInput,
claims,
account,
connectedAccount,
nativeTokenPrice,
})

if (!args) {
throw new Error('There were no valid claims selected')
Expand Down Expand Up @@ -461,6 +508,7 @@ export function useClaimCallback(account: string | null | undefined): {
connectedAccount,
isAirdropWindowOpen,
isInvestmentWindowOpen,
nativeTokenPrice,
vCowContract,
vCowToken,
]
Expand All @@ -474,7 +522,7 @@ type GetClaimManyArgsParams = {
claims: UserClaims
account: string
connectedAccount: string
chainId: SupportedChainId
nativeTokenPrice: string
}

type ClaimManyFnArgs = Parameters<VCowType['claimMany']>
Expand All @@ -492,7 +540,7 @@ function _getClaimManyArgs({
claims,
account,
connectedAccount,
chainId,
nativeTokenPrice,
}: GetClaimManyArgsParams): GetClaimManyArgsResult {
// Arrays are named according to contract parameters
// For more info, check https://github.com/gnosis/gp-v2-token/blob/main/src/contracts/mixins/MerkleDistributor.sol#L123
Expand Down Expand Up @@ -532,7 +580,7 @@ function _getClaimManyArgs({

merkleProofs.push(claim.proof)
// only used on UserOption
const value = _getClaimValue(claim, claimedAmount, chainId)
const value = _getClaimValue(claim, claimedAmount, nativeTokenPrice)
sendEth.push(value) // TODO: verify ETH balance < input.amount ?

// sum of claimedAmounts for the toast notification
Expand Down Expand Up @@ -615,16 +663,14 @@ function _hasNoInputOrInputIsGreaterThanClaimAmount(
* vCowAmount * wethPrice / 10^18
* See https://github.com/gnosis/gp-v2-token/blob/main/src/contracts/mixins/Claiming.sol#L314-L320
*/
function _getClaimValue(claim: UserClaimData, vCowAmount: string, chainId: SupportedChainId): string {
function _getClaimValue(claim: UserClaimData, vCowAmount: string, nativeTokenPrice: string): string {
if (claim.type !== ClaimType.UserOption) {
return '0'
}

const price = NATIVE_TOKEN_PRICE[chainId]

// Why InAtomsSquared? because we are multiplying vCowAmount (which is in atoms == * 10**18)
// by the price (which is also in atoms == * 10**18)
const claimValueInAtomsSquared = JSBI.multiply(JSBI.BigInt(vCowAmount), JSBI.BigInt(price))
const claimValueInAtomsSquared = JSBI.multiply(JSBI.BigInt(vCowAmount), JSBI.BigInt(nativeTokenPrice))
// Then it's divided by 10**18 to return the value in the native currency atoms
return JSBI.divide(claimValueInAtomsSquared, DENOMINATOR).toString()
}
Expand Down Expand Up @@ -749,22 +795,25 @@ export function useClaimState() {
export function useUserEnhancedClaimData(account: Account): EnhancedUserClaimData[] {
const { available } = useClassifiedUserClaims(account)
const { chainId: preCheckChainId } = useActiveWeb3React()
const native = useNativeTokenPrice()
const gno = useGnoPrice()
const usdc = useUsdcPrice()

const sorted = useMemo(() => available.sort(_sortTypes), [available])

return useMemo(() => {
const chainId = supportedChainId(preCheckChainId)
if (!chainId) return []
if (!chainId || !native || !gno || !usdc) return []

return sorted.map((claim) => _enhanceClaimData(claim, chainId))
}, [preCheckChainId, sorted])
return sorted.map((claim) => _enhanceClaimData(claim, chainId, { native, gno, usdc }))
}, [gno, native, preCheckChainId, sorted, usdc])
}

function _sortTypes(a: UserClaimData, b: UserClaimData): number {
return Number(isFreeClaim(b.type)) - Number(isFreeClaim(a.type))
}

function _enhanceClaimData(claim: UserClaimData, chainId: SupportedChainId): EnhancedUserClaimData {
function _enhanceClaimData(claim: UserClaimData, chainId: SupportedChainId, prices: VCowPrices): EnhancedUserClaimData {
const claimAmount = CurrencyAmount.fromRawAmount(ONE_VCOW.currency, claim.amount)

const data: EnhancedUserClaimData = {
Expand All @@ -773,11 +822,11 @@ function _enhanceClaimData(claim: UserClaimData, chainId: SupportedChainId): Enh
claimAmount,
}

const tokenAndAmount = claimTypeToTokenAmount(claim.type, chainId)
const tokenAndAmount = claimTypeToTokenAmount(claim.type, chainId, prices)

// Free claims will have tokenAndAmount === undefined
// If it's not a free claim, store the price and calculate cost in investment token
if (tokenAndAmount) {
if (tokenAndAmount?.amount) {
data.price = _getPrice(tokenAndAmount)
// get the currency amount using the price base currency (remember price was inverted)
data.currencyAmount = CurrencyAmount.fromRawAmount(data.price.baseCurrency, claim.amount)
Expand Down
12 changes: 5 additions & 7 deletions src/custom/state/claim/hooks/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ import {
ClaimType,
ClaimTypePriceMap,
FREE_CLAIM_TYPES,
GNO_PRICE,
NATIVE_TOKEN_PRICE,
PAID_CLAIM_TYPES,
RepoClaims,
TypeToPriceMapper,
USDC_PRICE,
UserClaims,
VCowPrices,
} from 'state/claim/hooks/index'

/**
Expand Down Expand Up @@ -164,14 +162,14 @@ export type PaidClaimTypeToPriceMap = {
/**
* Helper function to get vCow price based on claim type and chainId
*/
export function claimTypeToTokenAmount(type: ClaimType, chainId: SupportedChainId) {
export function claimTypeToTokenAmount(type: ClaimType, chainId: SupportedChainId, prices: VCowPrices) {
switch (type) {
case ClaimType.GnoOption:
return { token: GNO[chainId], amount: GNO_PRICE }
return { token: GNO[chainId], amount: prices.gno as string }
case ClaimType.Investor:
return { token: USDC_BY_CHAIN[chainId], amount: USDC_PRICE }
return { token: USDC_BY_CHAIN[chainId], amount: prices.usdc as string }
case ClaimType.UserOption:
return { token: GpEther.onChain(chainId), amount: NATIVE_TOKEN_PRICE[chainId] }
return { token: GpEther.onChain(chainId), amount: prices.native as string }
default:
return undefined
}
Expand Down

0 comments on commit 1c15ca5

Please sign in to comment.