Skip to content

Commit

Permalink
Deal token transfer modal was introduced
Browse files Browse the repository at this point in the history
  • Loading branch information
0xDmitry committed Jun 21, 2023
1 parent f12355e commit 52dd65d
Show file tree
Hide file tree
Showing 10 changed files with 718 additions and 147 deletions.
16 changes: 10 additions & 6 deletions src/components/form/TokenInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ interface Props {
disabled?: boolean
error?: string
maxDisabled?: boolean
maxValue: string
maxValue?: string
withBalance?: boolean
maxValueFormatted: string
withMaxButton?: boolean
maxValueFormatted?: string
setValue: (value: string) => void
value: string
symbol?: string
Expand All @@ -79,12 +80,13 @@ export const TokenInput = ({
error,
maxAllocationFormatted,
maxDisabled,
maxValue,
maxValue = '',
maxValueFormatted,
setValue,
symbol,
value,
withBalance = true,
withMaxButton = true,
...restProps
}: Props) => {
const setMax = () => setValue(maxValue)
Expand All @@ -106,9 +108,11 @@ export const TokenInput = ({
)}
value={value}
/>
<MaxButton disabled={maxDisabled} onClick={setMax}>
Max
</MaxButton>
{withMaxButton && (
<MaxButton disabled={maxDisabled} onClick={setMax}>
Max
</MaxButton>
)}
</InputWrapper>
{withBalance && (
<Balance>
Expand Down
45 changes: 41 additions & 4 deletions src/components/pools/PoolMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import styled from 'styled-components'

import NoActions from './actions/NoActions'
import ClaimUpfrontDealTokens from './actions/Vest/ClaimUpfrontDealTokens'
import TransferVestingShareModal from './actions/Vest/TransferVestingShareModal'
import UpfrontDealTransferVestingShareModal from './actions/Vest/UpfrontDealTransferVestingShareModal'
import VestUpfrontDeal from './actions/Vest/VestUpfrontDeal'
import Vouch from './actions/Vouch/Vouch'
import InvestorsModal from './common/InvestorsModal'
Expand Down Expand Up @@ -108,6 +110,9 @@ export default function PoolMain({ chainId, poolAddress }: Props) {
const { currentThemeName } = useThemeContext()

const [showInvestorsModal, setShowInvestorsModal] = useState<boolean>(false)
const [showTransferVestingShareModal, setShowTransferVestingShareModal] = useState<boolean>(false)
const [showUpfrontDealTransferVestingShareModal, setUpfrontDealShowTransferVestingShareModal] =
useState<boolean>(false)

const { derivedStatus, funding, pool, tabs, timeline } = useAelinPoolStatus(
chainId,
Expand Down Expand Up @@ -192,6 +197,11 @@ export default function PoolMain({ chainId, poolAddress }: Props) {
activeTab={tabs.actionTabs.active}
derivedStatus={derivedStatus}
funding={funding}
handleTransfer={() =>
pool.upfrontDeal
? setUpfrontDealShowTransferVestingShareModal(true)
: setShowTransferVestingShareModal(true)
}
isUpfrontDeal={!!pool.upfrontDeal}
pool={pool}
/>
Expand All @@ -209,6 +219,18 @@ export default function PoolMain({ chainId, poolAddress }: Props) {
</MainGrid>
{showInvestorsModal && <InvestorsModal onClose={handleCloseInvestorsModal} pool={pool} />}
</RightTimelineLayout>
{showTransferVestingShareModal && (
<TransferVestingShareModal
onClose={() => setShowTransferVestingShareModal(false)}
poolAddress={pool.address}
/>
)}
{showUpfrontDealTransferVestingShareModal && (
<UpfrontDealTransferVestingShareModal
onClose={() => setUpfrontDealShowTransferVestingShareModal(false)}
poolAddress={pool.address}
/>
)}
</>
)
}
Expand All @@ -219,6 +241,7 @@ type DealActionTabsProps = {
activeTab: PoolAction | null
derivedStatus: DerivedStatus
funding: Funding
handleTransfer: () => void
}
function DealActionTabs({ ...props }: DealActionTabsProps) {
return props.isUpfrontDeal ? (
Expand All @@ -228,7 +251,13 @@ function DealActionTabs({ ...props }: DealActionTabsProps) {
)
}

function RegularPoolsActionTabs({ activeTab, derivedStatus, funding, pool }: DealActionTabsProps) {
function RegularPoolsActionTabs({
activeTab,
derivedStatus,
funding,
handleTransfer,
pool,
}: DealActionTabsProps) {
return (
<>
{!activeTab && <NoActions pool={pool} status={derivedStatus} />}
Expand All @@ -238,14 +267,20 @@ function RegularPoolsActionTabs({ activeTab, derivedStatus, funding, pool }: Dea
{activeTab === PoolAction.CreateDeal && <CreateDeal pool={pool} />}
{activeTab === PoolAction.AcceptDeal && <AcceptDeal pool={pool} />}
{activeTab === PoolAction.FundDeal && <FundDeal pool={pool} />}
{activeTab === PoolAction.Vest && <Vest pool={pool} />}
{activeTab === PoolAction.Vest && <Vest handleTransfer={handleTransfer} pool={pool} />}
{activeTab === PoolAction.WithdrawUnredeemed && <WithdrawUnredeemed pool={pool} />}
{activeTab === PoolAction.SponsorClaim && <SponsorClaim pool={pool} />}
</>
)
}

function UpfrontDealActionTabs({ activeTab, derivedStatus, funding, pool }: DealActionTabsProps) {
function UpfrontDealActionTabs({
activeTab,
derivedStatus,
funding,
handleTransfer,
pool,
}: DealActionTabsProps) {
return (
<>
{!activeTab && <NoActions pool={pool} status={derivedStatus} />}
Expand All @@ -256,7 +291,9 @@ function UpfrontDealActionTabs({ activeTab, derivedStatus, funding, pool }: Deal
<WaitingForDeal isUpfrontDeal={!!pool.upfrontDeal} />
)}
{activeTab === PoolAction.FundDeal && <FundDeal pool={pool} />}
{activeTab === PoolAction.Vest && <VestUpfrontDeal pool={pool} />}
{activeTab === PoolAction.Vest && (
<VestUpfrontDeal handleTransfer={handleTransfer} pool={pool} />
)}
{activeTab === PoolAction.Settle && (
<ClaimUpfrontDealTokens
pool={pool}
Expand Down
209 changes: 209 additions & 0 deletions src/components/pools/actions/Vest/CommonTransferVestingShareModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import { useMemo, useState } from 'react'
import styled from 'styled-components'

import { isAddress } from '@ethersproject/address'
import { BigNumber } from 'alchemy-sdk'

import {
Modal as BaseModal,
ModalButtonCSS,
ModalLine,
ModalText,
} from '@/src/components/common/Modal'
import { TokenInput as BaseTokenInput } from '@/src/components/form/TokenInput'
import {
ButtonGradient,
ButtonPrimaryLight,
} from '@/src/components/pureStyledComponents/buttons/Button'
import {
Textfield as BaseTextField,
TextfieldState,
} from '@/src/components/pureStyledComponents/form/Textfield'
import { Error } from '@/src/components/pureStyledComponents/text/Error'
import { BASE_DECIMALS, DISPLAY_DECIMALS } from '@/src/constants/misc'
import { formatToken } from '@/src/web3/bigNumber'

const Modal = styled(BaseModal)`
.modalCard {
padding-left: 60px;
padding-right: 60px;
}
`
const TransferButton = styled(ButtonGradient)`
${ModalButtonCSS}
`

const Row = styled.div`
display: flex;
flex-direction: row;
gap: 6px;
justify-content: space-between;
`

const Text = styled.span`
color: ${({ theme }) => theme.colors.textColorLight};
font-size: 0.9rem;
font-weight: 400;
line-height: 1.2;
`

const Value = styled.span`
color: ${({ theme }) => theme.colors.primary};
font-weight: 500;
`

const TokensHeld = styled(Row)`
margin: 0 auto 20px;
`

const InputBox = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 8px;
padding: 20px;
margin: 0 auto 40px;
`

const InputLabel = styled.span`
color: ${({ theme }) => theme.colors.textColor};
font-size: 0.9rem;
font-weight: 400;
line-height: 1.3;
`

const Textfield = styled(BaseTextField)`
width: 320px;
`

const TokenInput = styled(BaseTokenInput)`
width: 320px !important;
margin: 0 !important;
`

const PercentsButton = styled(ButtonPrimaryLight)`
font-size: 0.7rem;
font-weight: 400;
line-height: 1.5;
height: 24px;
padding: 5px 10px;
`

const TokensToTransfer = styled(Row)`
margin: 0 auto 6px;
`

type Props = {
symbol: string | undefined
totalAmount: BigNumber
underlyingDealTokenDecimals: number | undefined
onClose: () => void
onTransfer: (amount: string, toAddress: string) => void
isTransferButtonDisabled: boolean
}

enum Percents {
P25 = '25%',
P50 = '50%',
P75 = '75%',
P100 = '100%',
}

const CommonTransferVestingShareModal = ({
isTransferButtonDisabled,
onClose,
onTransfer,
symbol,
totalAmount,
underlyingDealTokenDecimals,
}: Props) => {
const [amount, setAmount] = useState('0')
const [toAddress, setToAddress] = useState('')

const amountError = useMemo(() => {
return BigNumber.from(amount === '' ? 0 : amount).gt(totalAmount)
? 'Not enough deal tokens held'
: ''
}, [amount, totalAmount])

const addressError = useMemo(() => {
return toAddress && !isAddress(toAddress) ? 'Invalid address' : ''
}, [toAddress])

const setTokensToTransfer = (percents: Percents) => {
switch (percents) {
case Percents.P25:
setAmount(totalAmount.div(4).toString())
break
case Percents.P50:
setAmount(totalAmount.div(4).mul(2).toString())
break
case Percents.P75:
setAmount(totalAmount.div(4).mul(3).toString())
break
case Percents.P100:
setAmount(totalAmount.toString())
break
}
}

return (
<Modal onClose={onClose} size="560px" title="Deal tokens transfer">
<ModalText>Wording TBD</ModalText>
<ModalLine />
<TokensHeld>
<Text>Tokens held in vesting schedule:</Text>
<Value>
{formatToken(totalAmount, underlyingDealTokenDecimals, DISPLAY_DECIMALS)} {symbol}
</Value>
</TokensHeld>
<InputBox>
<InputLabel>Receiver address</InputLabel>
<Textfield
maxLength={42}
onChange={(e) => setToAddress(e.target.value)}
placeholder="Enter receiver address..."
status={addressError ? TextfieldState.error : undefined}
type="text"
value={toAddress}
/>
{addressError && <Error margin="0">{addressError}</Error>}
<InputLabel>Deal tokens to transfer</InputLabel>
<TokenInput
decimals={underlyingDealTokenDecimals ?? BASE_DECIMALS}
error={amountError}
setValue={setAmount}
symbol={symbol}
value={amount}
withBalance={false}
withMaxButton={false}
/>
<Row>
<PercentsButton onClick={() => setTokensToTransfer(Percents.P25)}>25%</PercentsButton>
<PercentsButton onClick={() => setTokensToTransfer(Percents.P50)}>50%</PercentsButton>
<PercentsButton onClick={() => setTokensToTransfer(Percents.P75)}>75%</PercentsButton>
<PercentsButton onClick={() => setTokensToTransfer(Percents.P100)}>100%</PercentsButton>
</Row>
</InputBox>
<TokensToTransfer>
<Text>Deal tokens to transfer:</Text>
<Value>
{formatToken(amount === '' ? 0 : amount, underlyingDealTokenDecimals, DISPLAY_DECIMALS)}{' '}
{symbol}
</Value>
</TokensToTransfer>
<TransferButton
disabled={
isTransferButtonDisabled || !amount || !toAddress || !!amountError || !!addressError
}
onClick={() => onTransfer(amount, toAddress)}
>
Transfer
</TransferButton>
</Modal>
)
}

export default CommonTransferVestingShareModal
Loading

0 comments on commit 52dd65d

Please sign in to comment.