From 84a384d6eb6f5b646437b847792afd9cceae8400 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 14 Jan 2022 15:45:17 +0000 Subject: [PATCH] [Claim refactor] - Split InvestmentFlow and minor slider impl. (#2144) * split InvestmentFlow and move to folder * Combined with another PR * Remove hardcoded address * rename Investment > InvestmentFlow * Small PR updates Co-authored-by: nenadV91 --- src/custom/pages/Claim/InvestmentFlow.tsx | 216 ------------------ .../Claim/InvestmentFlow/InvestOption.tsx | 159 +++++++++++++ .../pages/Claim/InvestmentFlow/index.tsx | 149 ++++++++++++ src/custom/pages/Claim/index.tsx | 2 +- 4 files changed, 309 insertions(+), 217 deletions(-) delete mode 100644 src/custom/pages/Claim/InvestmentFlow.tsx create mode 100644 src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx create mode 100644 src/custom/pages/Claim/InvestmentFlow/index.tsx diff --git a/src/custom/pages/Claim/InvestmentFlow.tsx b/src/custom/pages/Claim/InvestmentFlow.tsx deleted file mode 100644 index d0c1bf2d4..000000000 --- a/src/custom/pages/Claim/InvestmentFlow.tsx +++ /dev/null @@ -1,216 +0,0 @@ -import { - InvestFlow, - InvestContent, - InvestTokenGroup, - InvestInput, - InvestAvailableBar, - InvestSummary, - InvestFlowValidation, - InvestTokenSubtotal, - StepIndicator, - Steps, - TokenLogo, -} from 'pages/Claim/styled' -import CowProtocolLogo from 'components/CowProtocolLogo' -import { useClaimState } from 'state/claim/hooks' -import { ClaimCommonTypes } from './types' -import { ClaimStatus } from 'state/claim/actions' -import { useActiveWeb3React } from 'hooks/web3' -import { ApprovalState } from 'hooks/useApproveCallback' -import { CheckCircle } from 'react-feather' -import Row from 'components/Row' - -type InvestmentFlowProps = Pick & { - isAirdropOnly: boolean - approveState: ApprovalState - approveCallback: () => void -} - -export default function InvestmentFlow({ - hasClaims, - isAirdropOnly, - approveState, - approveCallback, -}: InvestmentFlowProps) { - const { account } = useActiveWeb3React() - - const { activeClaimAccount, claimStatus, isInvestFlowActive, investFlowStep } = useClaimState() - - if ( - !activeClaimAccount || // no connected account - !hasClaims || // no claims - !isInvestFlowActive || // not on correct step (account change in mid step) - claimStatus !== ClaimStatus.DEFAULT || // not in default claim state - isAirdropOnly // is only for airdrop - ) { - return null - } - - return ( - - -

- {investFlowStep === 0 - ? 'Claiming vCOW is a two step process' - : investFlowStep === 1 - ? 'Set allowance to Buy vCOW' - : 'Confirm transaction to claim all vCOW'} -

- -
  • Allowances: Approve all tokens to be used for investment.
  • -
  • Submit and confirm the transaction to claim vCOW
  • -
    -
    - - {/* Invest flow: Step 1 > Set allowances and investment amounts */} - {investFlowStep === 1 ? ( - -

    - Your account can participate in the investment of vCOW. Each investment opportunity will allow you to invest - up to a predefined maximum amount of tokens{' '} -

    - -
    - - - - -

    Buy vCOW with GNO

    -
    - - - - - Price 16.66 vCoW per GNO - - - Token approval - - {approveState === ApprovalState.NOT_APPROVED ? ( - 'GNO not approved' - ) : ( - - GNO approved - - )} - - {approveState === ApprovalState.NOT_APPROVED && ( - - )} - - - Max. investment available 2,500.04 GNO - - - Available investment used - - - -
    - - Balance: 10,583.34 GNO - {/* Button should use the max possible amount the user can invest, considering their balance + max investment allowed */} - - - - Receive: 32,432.54 vCOW - {/* Insufficient balance validation error */} - - Insufficient balance to invest. Adjust the amount or go back to remove this investment option. - -
    -
    -
    -
    - - -
    - - - - -

    Buy vCOW with ETH

    -
    - - - - - Price 16.66 vCoW per ETH - - - Token approval - - - Not required for ETH! - - - - - Max. investment available 2,500.04 ETH - - - Available investment used - - - -
    - - Balance: 10,583.34 ETH - {/* Button should use the max possible amount the user can invest, considering their balance + max investment allowed */} - - - - Receive: 32,432.54 vCOW - {/* Insufficient balance validation error */} - - Insufficient balance to invest. Adjust the amount or go back to remove this investment option. - -
    -
    -
    -
    - - - {activeClaimAccount} will receive: 4,054,671.28 vCOW based on investment(s) - - - Approve all investment tokens before continuing -
    - ) : null} - - {/* Invest flow: Step 2 > Review summary */} - {investFlowStep === 2 ? ( - - 1. Claim airdrop: {activeClaimAccount} receives 13,120.50 vCOW (Note: please make sure you intend to claim and - send vCOW to the mentioned account) -
    -
    - 2. Claim and invest: Investing with account: {account} (connected account). Investing: 1343 GNO (50% of - available investing opportunity) and 32 ETH (30% of available investing opportunity) -
    -
    - 3. Receive vCOW claims on account {activeClaimAccount}: 23,947.6 vCOW - available NOW! and 120,567.12 vCOW - - Vested linearly 4 years
    -
    -
    -

    Ready to claim your vCOW?

    -

    - What will happen? By sending this Ethereum transaction, you will be investing tokens from the - connected account and exchanging them for vCOW tokens that will be received by the claiming account - specified above. -

    -

    - Can I modify the invested amounts or invest partial amounts later? No. Once you send the transaction, - you cannot increase or reduce the investment. Investment oportunities can only be exercised once. -

    -
    - ) : null} -
    - ) -} diff --git a/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx b/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx new file mode 100644 index 000000000..8a0d136ce --- /dev/null +++ b/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx @@ -0,0 +1,159 @@ +import { useCallback, useMemo } from 'react' +import styled from 'styled-components/macro' +import CowProtocolLogo from 'components/CowProtocolLogo' +import { formatUnits, parseUnits } from '@ethersproject/units' +import { CurrencyAmount } from '@uniswap/sdk-core' + +import { InvestTokenGroup, TokenLogo, InvestSummary, InvestInput } from '../styled' +import { formatSmart } from 'utils/format' +import Row from 'components/Row' +import { CheckCircle } from 'react-feather' +import { InvestOptionProps } from '.' +import { ApprovalState } from 'hooks/useApproveCallback' +import { useCurrencyBalance } from 'state/wallet/hooks' +import { useActiveWeb3React } from 'hooks/web3' + +const RangeSteps = styled.div` + display: flex; + align-items: center; + width: 100%; + justify-content: space-between; +` + +const RangeStep = styled.button` + background: none; + border: none; + font-size: 0.8rem; + cursor: pointer; + color: blue; + padding: 0; +` + +const INVESTMENT_STEPS = [0, 25, 50, 75, 100] + +export default function InvestOption({ approveState, approveCallback, updateInvestAmount, claim }: InvestOptionProps) { + const { currencyAmount, price, cost: maxCost, investedAmount } = claim + + const { account } = useActiveWeb3React() + + const token = currencyAmount?.currency + + const balance = useCurrencyBalance(account || undefined, token) + + const handlePercentChange = (event: React.ChangeEvent) => { + console.log(event.target.value) + } + + const handleStepChange = (value: number) => { + console.log(value) + } + + const onMaxClick = useCallback(() => { + if (!maxCost || !balance) { + return + } + + const amount = maxCost.greaterThan(balance) ? balance : maxCost + // store the value as a string to prevent unnecessary re-renders + const investAmount = formatUnits(amount.quotient.toString(), balance.currency.decimals) + + updateInvestAmount(claim.index, investAmount) + }, [balance, claim.index, maxCost, updateInvestAmount]) + + const vCowAmount = useMemo(() => { + if (!token || !price) { + return + } + + const investA = CurrencyAmount.fromRawAmount(token, parseUnits(investedAmount, token.decimals).toString()) + return investA.multiply(price) + }, [investedAmount, price, token]) + + return ( + +
    + + + + +

    Buy vCOW with {currencyAmount?.currency?.symbol}

    +
    + + + + + Price{' '} + + {formatSmart(price)} vCoW per {currencyAmount?.currency?.symbol} + + + + Token approval + + {approveState === ApprovalState.NOT_APPROVED ? ( + `${currencyAmount?.currency?.symbol} not approved` + ) : ( + + {currencyAmount?.currency?.symbol} approved{' '} + + + )} + + {approveState === ApprovalState.NOT_APPROVED && ( + + )} + + + Max. investment available{' '} + + {formatSmart(maxCost) || '0'} {currencyAmount?.currency?.symbol} + + + + Available investment used + +
    + + {INVESTMENT_STEPS.map((step: number) => ( + handleStepChange(step)} key={step}> + {step}% + + ))} + + + +
    +
    +
    + +
    + + Balance:{' '} + + {formatSmart(balance)} {currencyAmount?.currency?.symbol} + + {/* Button should use the max possible amount the user can invest, considering their balance + max investment allowed */} + + + + Receive: {formatSmart(vCowAmount) || 0} vCOW + {/* Insufficient balance validation error */} + + Insufficient balance to invest. Adjust the amount or go back to remove this investment option. + +
    +
    +
    +
    + ) +} diff --git a/src/custom/pages/Claim/InvestmentFlow/index.tsx b/src/custom/pages/Claim/InvestmentFlow/index.tsx new file mode 100644 index 000000000..de45f8de2 --- /dev/null +++ b/src/custom/pages/Claim/InvestmentFlow/index.tsx @@ -0,0 +1,149 @@ +import { useEffect, useState, useCallback } from 'react' +import { + InvestFlow, + InvestContent, + InvestFlowValidation, + InvestTokenSubtotal, + StepIndicator, + Steps, +} from 'pages/Claim/styled' +import { useClaimState, useUserEnhancedClaimData } from 'state/claim/hooks' +import { ClaimCommonTypes, EnhancedUserClaimData } from '../types' +import { ClaimStatus } from 'state/claim/actions' +import { useActiveWeb3React } from 'hooks/web3' +import { ApprovalState } from 'hooks/useApproveCallback' +import InvestOption from './InvestOption' + +type InvestmentFlowProps = Pick & { + isAirdropOnly: boolean + approveState: ApprovalState + approveCallback: () => void +} + +export type InvestmentClaimProps = EnhancedUserClaimData & { + investedAmount: string +} + +export type InvestOptionProps = Pick & { + claim: InvestmentClaimProps + updateInvestAmount: (idx: number, investAmount: string) => void +} + +export default function InvestmentFlow({ + hasClaims, + isAirdropOnly, + approveState, + approveCallback, +}: InvestmentFlowProps) { + const { account } = useActiveWeb3React() + const { activeClaimAccount, claimStatus, isInvestFlowActive, investFlowStep, selected } = useClaimState() + + const claimData = useUserEnhancedClaimData(activeClaimAccount) + + const [investData, setInvestData] = useState([]) + + useEffect(() => { + if (claimData) { + const data = claimData.reduce((acc, claim) => { + if (selected.includes(claim.index)) { + acc.push({ ...claim, investedAmount: '0' }) + } + + return acc + }, []) + + setInvestData(data) + } + }, [selected, claimData]) + + const updateInvestAmount = useCallback( + (idx: number, investedAmount: string) => { + const update = investData.map((claim) => (claim.index === idx ? { ...claim, investedAmount } : claim)) + setInvestData(update) + }, + [investData] + ) + + if ( + !activeClaimAccount || // no connected account + !hasClaims || // no claims + !isInvestFlowActive || // not on correct step (account change in mid step) + claimStatus !== ClaimStatus.DEFAULT || // not in default claim state + isAirdropOnly // is only for airdrop + ) { + return null + } + + return ( + + +

    + {investFlowStep === 0 + ? 'Claiming vCOW is a two step process' + : investFlowStep === 1 + ? 'Set allowance to Buy vCOW' + : 'Confirm transaction to claim all vCOW'} +

    + +
  • Allowances: Approve all tokens to be used for investment.
  • +
  • Submit and confirm the transaction to claim vCOW
  • +
    +
    + + {/* Invest flow: Step 1 > Set allowances and investment amounts */} + {investFlowStep === 1 ? ( + +

    + Your account can participate in the investment of vCOW. Each investment opportunity will allow you to invest + up to a predefined maximum amount of tokens{' '} +

    + + {investData.map((claim) => ( + + ))} + + {/* TODO: Update this with real data */} + + {activeClaimAccount} will receive: 4,054,671.28 vCOW based on investment(s) + + + Approve all investment tokens before continuing +
    + ) : null} + + {/* Invest flow: Step 2 > Review summary */} + {investFlowStep === 2 ? ( + + 1. Claim airdrop: {activeClaimAccount} receives 13,120.50 vCOW (Note: please make sure you intend to claim and + send vCOW to the mentioned account) +
    +
    + 2. Claim and invest: Investing with account: {account} (connected account). Investing: 1343 GNO (50% of + available investing opportunity) and 32 ETH (30% of available investing opportunity) +
    +
    + 3. Receive vCOW claims on account {activeClaimAccount}: 23,947.6 vCOW - available NOW! and 120,567.12 vCOW - + Vested linearly 4 years
    +
    +
    +

    Ready to claim your vCOW?

    +

    + What will happen? By sending this Ethereum transaction, you will be investing tokens from the + connected account and exchanging them for vCOW tokens that will be received by the claiming account + specified above. +

    +

    + Can I modify the invested amounts or invest partial amounts later? No. Once you send the transaction, + you cannot increase or reduce the investment. Investment oportunities can only be exercised once. +

    +
    + ) : null} +
    + ) +} diff --git a/src/custom/pages/Claim/index.tsx b/src/custom/pages/Claim/index.tsx index 2fdbcacaf..917b1d187 100644 --- a/src/custom/pages/Claim/index.tsx +++ b/src/custom/pages/Claim/index.tsx @@ -1,5 +1,6 @@ import { useEffect, useMemo } from 'react' import { Trans } from '@lingui/macro' +import { CurrencyAmount, MaxUint256 } from '@uniswap/sdk-core' import { useActiveWeb3React } from 'hooks/web3' import { useUserEnhancedClaimData, @@ -33,7 +34,6 @@ import { OperationType } from 'components/TransactionConfirmationModal' import useTransactionConfirmationModal from 'hooks/useTransactionConfirmationModal' import { GNO } from 'constants/tokens' -import { CurrencyAmount, MaxUint256 } from '@uniswap/sdk-core' const GNO_CLAIM_APPROVE_MESSAGE = 'Approving GNO for investing in vCOW'