diff --git a/common/util/format.ts b/common/util/format.ts index fd3e7551..ce7849aa 100644 --- a/common/util/format.ts +++ b/common/util/format.ts @@ -60,6 +60,16 @@ export function formatLargeNumber(num: number, sigfigs = 2): string { return `${numStr}${suffix[i] ?? ''}` } +export function shortFormatNumber(num: number): string { + if (num < 1000) return showPrecision(num, 3) + + const suffix = ['', 'K', 'M', 'B', 'T', 'Q'] + const i = Math.floor(Math.log10(num) / 3) + + const numStr = showPrecision(num / Math.pow(10, 3 * i), 2) + return `${numStr}${suffix[i] ?? ''}` +} + export function toCamelCase(words: string) { const camelCase = words .split(' ') diff --git a/web/components/contract/contract-info-dialog.tsx b/web/components/contract/contract-info-dialog.tsx index 1f33e4e1..385b4570 100644 --- a/web/components/contract/contract-info-dialog.tsx +++ b/web/components/contract/contract-info-dialog.tsx @@ -7,7 +7,6 @@ import { capitalize } from 'lodash' import { Contract } from 'common/contract' import { formatMoney, formatPercent } from 'common/util/format' import { contractPool, updateContract } from 'web/lib/firebase/contracts' -import { LiquidityBountyPanel } from 'web/components/contract/liquidity-bounty-panel' import { Col } from '../layout/col' import { Modal } from '../layout/modal' import { Title } from '../title' @@ -228,7 +227,6 @@ export function ContractInfoDialog(props: { - {!contract.resolution && } diff --git a/web/components/contract/extra-contract-actions-row.tsx b/web/components/contract/extra-contract-actions-row.tsx index 809c6172..d81132b9 100644 --- a/web/components/contract/extra-contract-actions-row.tsx +++ b/web/components/contract/extra-contract-actions-row.tsx @@ -9,6 +9,7 @@ import { FollowMarketButton } from 'web/components/follow-market-button' import { LikeMarketButton } from 'web/components/contract/like-market-button' import { ContractInfoDialog } from 'web/components/contract/contract-info-dialog' import { Tooltip } from '../tooltip' +import { LiquidityButton } from './liquidity-button' export function ExtraContractActionsRow(props: { contract: Contract }) { const { contract } = props @@ -18,6 +19,9 @@ export function ExtraContractActionsRow(props: { contract: Contract }) { return ( + {contract.mechanism === 'cpmm-1' && ( + + )} - - - {isSuccess && amount && ( -
Success! Added {formatMoney(amount)} in liquidity.
- )} - - {isLoading &&
Processing...
} - - ) -} - -function ViewLiquidityPanel(props: { contract: CPMMContract }) { - const { contract } = props - const { pool } = contract - const { YES: yesShares, NO: noShares } = pool - - return ( - -
- The liquidity pool for this market currently contains: -
- - {yesShares.toFixed(2)} shares - - - - {noShares.toFixed(2)} shares - - - ) -} - -function WithdrawLiquidityPanel(props: { - contract: CPMMContract - lpShares: { YES: number; NO: number } -}) { - const { contract, lpShares } = props - const { YES: yesShares, NO: noShares } = lpShares - - const [_error, setError] = useState(undefined) - const [isSuccess, setIsSuccess] = useState(false) - const [isLoading, setIsLoading] = useState(false) - - const submit = () => { - setIsLoading(true) - setIsSuccess(false) - - withdrawLiquidity({ contractId: contract.id }) - .then((_) => { - setIsSuccess(true) - setError(undefined) - setIsLoading(false) - }) - .catch((_) => setError('Server error')) - - track('withdraw liquidity') - } - - if (isSuccess) - return ( -
- Success! Your liquidity was withdrawn. -
- ) - - if (!yesShares && !noShares) - return ( -
- You do not have any liquidity positions to withdraw. -
- ) - - return ( - -
- Your liquidity position is currently: -
- - - {yesShares.toFixed(2)} shares - - - - {noShares.toFixed(2)} shares - - - - - - - {isLoading &&
Processing...
} - - ) -} diff --git a/web/components/contract/liquidity-button.tsx b/web/components/contract/liquidity-button.tsx new file mode 100644 index 00000000..f99f175a --- /dev/null +++ b/web/components/contract/liquidity-button.tsx @@ -0,0 +1,88 @@ +import { useState } from 'react' +import clsx from 'clsx' + +import { Button } from 'web/components/button' +import { formatMoney, shortFormatNumber } from 'common/util/format' +import { Col } from 'web/components/layout/col' +import { Tooltip } from '../tooltip' +import { CPMMContract } from 'common/contract' +import { User } from 'common/user' +import { useLiquidity } from 'web/hooks/use-liquidity' +import { LiquidityModal } from './liquidity-modal' + +export function LiquidityButton(props: { + contract: CPMMContract + user: User | undefined | null +}) { + const { contract, user } = props + const { totalLiquidity: total } = contract + + const lp = useLiquidity(contract.id) + const userActive = lp?.find((l) => l.userId === user?.id) !== undefined + + const [open, setOpen] = useState(false) + + return ( + + setOpen(true)} + /> + + + ) +} + +function LiquidityIconButton(props: { + total: number + onClick: () => void + userActive: boolean + isCompact?: boolean + disabled?: boolean +}) { + const { total, userActive, isCompact, onClick, disabled } = props + + return ( + + ) +} diff --git a/web/components/contract/liquidity-modal.tsx b/web/components/contract/liquidity-modal.tsx new file mode 100644 index 00000000..9780c0a8 --- /dev/null +++ b/web/components/contract/liquidity-modal.tsx @@ -0,0 +1,107 @@ +import { CPMMContract } from 'common/contract' +import { formatMoney } from 'common/util/format' +import { useState } from 'react' +import { useUser } from 'web/hooks/use-user' +import { addSubsidy } from 'web/lib/firebase/api' +import { track } from 'web/lib/service/analytics' +import { AmountInput } from '../amount-input' +import { Button } from '../button' +import { InfoTooltip } from '../info-tooltip' +import { Col } from '../layout/col' +import { Modal } from '../layout/modal' +import { Row } from '../layout/row' +import { Title } from '../title' + +export function LiquidityModal(props: { + contract: CPMMContract + isOpen: boolean + setOpen: (open: boolean) => void +}) { + const { contract, isOpen, setOpen } = props + const { totalLiquidity } = contract + + return ( + + + + + <div>Total liquidity subsidies: {formatMoney(totalLiquidity)}</div> + <AddLiquidityPanel contract={contract as CPMMContract} /> + </Col> + </Modal> + ) +} + +function AddLiquidityPanel(props: { contract: CPMMContract }) { + const { contract } = props + const { id: contractId, slug } = contract + + const user = useUser() + + const [amount, setAmount] = useState<number | undefined>(undefined) + const [error, setError] = useState<string | undefined>(undefined) + const [isSuccess, setIsSuccess] = useState(false) + const [isLoading, setIsLoading] = useState(false) + + const onAmountChange = (amount: number | undefined) => { + setIsSuccess(false) + setAmount(amount) + + // Check for errors. + if (amount !== undefined) { + if (user && user.balance < amount) { + setError('Insufficient balance') + } else if (amount < 1) { + setError('Minimum amount: ' + formatMoney(1)) + } else { + setError(undefined) + } + } + } + + const submit = () => { + if (!amount) return + + setIsLoading(true) + setIsSuccess(false) + + addSubsidy({ amount, contractId }) + .then((_) => { + setIsSuccess(true) + setError(undefined) + setIsLoading(false) + }) + .catch((_) => setError('Server error')) + + track('add liquidity', { amount, contractId, slug }) + } + + return ( + <> + <div className="mb-4 text-gray-500"> + Contribute your M$ to make this market more accurate by subsidizing + trader participation. <InfoTooltip text="Liquidity is how much money traders can make if they're right. The more traders can earn, the greater the incentive to find out what the correct probability is." /> + </div> + + <Row> + <AmountInput + amount={amount} + onChange={onAmountChange} + label="M$" + error={error} + disabled={isLoading} + inputClassName="w-16 mr-4" + /> + <Button size="md" color="blue" onClick={submit} disabled={isLoading}> + Add + </Button> + </Row> + + {isSuccess && amount && ( + <div>Success! Added {formatMoney(amount)} in liquidity.</div> + )} + + {isLoading && <div>Processing...</div>} + </> + ) +}