Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added uniswap trade widget #10148

Merged
merged 13 commits into from
Dec 12, 2024
Merged
2 changes: 1 addition & 1 deletion packages/commonwealth/client/scripts/helpers/launchpad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const calculateTokenPricing = (
);
const marketCapCurrent = currentPrice * token.initial_supply;
const marketCapGoal = token.eth_market_cap_target * ethToUsdRate;
const isMarketCapGoalReached = false; // TODO: https://github.com/hicommonwealth/commonwealth/issues/9887
const isMarketCapGoalReached = marketCapCurrent >= marketCapGoal;

return {
currentPrice: parseFloat(`${currentPrice.toFixed(8)}`),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import './MarketCapProgress.scss';

interface MarketCapProgressProps {
currency?: SupportedCurrencies;
marketCap: { current: number; goal: number };
marketCap: { current: number; goal: number; isCapped: boolean };
onBodyClick?: (e: React.MouseEvent) => void;
}

Expand All @@ -18,15 +18,14 @@ const MarketCapProgress = ({
onBodyClick,
}: MarketCapProgressProps) => {
const currencySymbol = currencyNameToSymbolMap[currency];
const isCapped = marketCap.current === marketCap.goal;
const progressPercentage = Math.floor(
(marketCap.current / marketCap.goal) * 100,
);

return (
<div className="MarketCapProgress" onClick={onBodyClick}>
<progress
className={clsx('goal-progress', { isCapped })}
className={clsx('goal-progress', { isCapped: marketCap.isCapped })}
value={progressPercentage}
max={100}
/>
Expand All @@ -36,7 +35,7 @@ const MarketCapProgress = ({
{numeral(marketCap.current).format('0.0a')} | Goal {currencySymbol}
{numeral(marketCap.goal).format('0.0a')}
</CWText>
{isCapped && (
{marketCap.isCapped && (
<CWIcon iconName="rocketLaunch" className="token-capped-icon" />
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import clsx from 'clsx';
import { currencyNameToSymbolMap, SupportedCurrencies } from 'helpers/currency';
import React, { ReactNode } from 'react';
import { TradingMode } from '../../modals/TradeTokenModel';
import { CWText } from '../component_kit/cw_text';
import { CWButton } from '../component_kit/new_designs/CWButton';
import { CWTooltip } from '../component_kit/new_designs/CWTooltip';
Expand All @@ -14,12 +15,12 @@ interface TokenCardProps {
symbol: string;
iconURL: string;
currency?: SupportedCurrencies;
marketCap: { current: number; goal: number };
marketCap: { current: number; goal: number; isCapped: boolean };
price: number;
pricePercentage24HourChange: number;
mode: 'buy' | 'swap';
mode: TradingMode.Buy | TradingMode.Swap;
className?: string;
onCTAClick?: () => void;
onCTAClick?: (mode: TradingMode) => void;
onCardBodyClick?: () => void;
}

Expand Down Expand Up @@ -129,7 +130,7 @@ const TokenCard = ({
buttonWidth="full"
buttonType="secondary"
buttonAlt="green"
onClick={onCTAClick}
onClick={() => onCTAClick?.(mode)}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@

.action-btns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
width: 100%;
padding: 8px;

&.cols-1 {
grid-template-columns: 1fr;
}

&.cols-2 {
grid-template-columns: 1fr 1fr;
}

button {
text-transform: capitalize;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { TokenView } from '@hicommonwealth/schemas';
import { ChainBase } from '@hicommonwealth/shared';
import clsx from 'clsx';
import { currencyNameToSymbolMap, SupportedCurrencies } from 'helpers/currency';
import { calculateTokenPricing } from 'helpers/launchpad';
import useDeferredConditionTriggerCallback from 'hooks/useDeferredConditionTriggerCallback';
import React, { useState } from 'react';
import app from 'state';
import { useFetchTokenUsdRateQuery } from 'state/api/communityStake';
import TradeTokenModal from 'views/modals/TradeTokenModel';
import {
import useUserStore from 'state/ui/user';
import { AuthModal } from 'views/modals/AuthModal';
import TradeTokenModal, {
TokenWithCommunity,
TradingMode,
} from 'views/modals/TradeTokenModel/TradeTokenForm';
} from 'views/modals/TradeTokenModel';
import { z } from 'zod';
import { CWDivider } from '../../../component_kit/cw_divider';
import { CWIconButton } from '../../../component_kit/cw_icon_button';
Expand All @@ -32,6 +35,7 @@ export const TokenTradeWidget = ({
token,
currency = SupportedCurrencies.USD,
}: TokenTradeWidgetProps) => {
const user = useUserStore();
const currencySymbol = currencyNameToSymbolMap[currency];

const [isWidgetExpanded, setIsWidgetExpanded] = useState(true);
Expand All @@ -44,6 +48,11 @@ export const TokenTradeWidget = ({
};
}>({ isOpen: false, tradeConfig: undefined });

const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
const { register, trigger } = useDeferredConditionTriggerCallback({
shouldRunTrigger: user.isLoggedIn,
});

const { data: ethToCurrencyRateData, isLoading: isLoadingETHToCurrencyRate } =
useFetchTokenUsdRateQuery({
tokenSymbol: 'ETH',
Expand All @@ -53,7 +62,19 @@ export const TokenTradeWidget = ({
);
const tokenPricing = calculateTokenPricing(token, ethToUsdRate);

const openAuthModalOrTriggerCallback = () => {
if (user.isLoggedIn) {
trigger();
} else {
setIsAuthModalOpen(!user.isLoggedIn);
}
};

const handleCTAClick = (mode: TradingMode) => {
if (!user.isLoggedIn) {
setIsAuthModalOpen(true);
}

setTokenLaunchModalConfig({
isOpen: true,
tradeConfig: {
Expand Down Expand Up @@ -108,23 +129,57 @@ export const TokenTradeWidget = ({
marketCap={{
current: tokenPricing.marketCapCurrent,
goal: tokenPricing.marketCapGoal,
isCapped: tokenPricing.isMarketCapGoalReached,
}}
/>
<div className="action-btns">
{[TradingMode.Buy, TradingMode.Sell].map((mode) => (
<div
className={clsx('action-btns', {
[`cols-${tokenPricing.isMarketCapGoalReached ? 1 : 2}`]: true,
})}
>
{!tokenPricing.isMarketCapGoalReached ? (
[TradingMode.Buy, TradingMode.Sell].map((mode) => (
<CWButton
key={mode}
label={mode}
buttonAlt={mode === TradingMode.Buy ? 'green' : 'rorange'}
buttonWidth="full"
buttonType="secondary"
buttonHeight="sm"
onClick={() => {
register({
cb: () => {
handleCTAClick(mode);
},
});
openAuthModalOrTriggerCallback();
}}
/>
))
) : (
<CWButton
key={mode}
label={mode}
buttonAlt={mode === TradingMode.Buy ? 'green' : 'rorange'}
label={TradingMode.Swap}
buttonAlt="green"
buttonWidth="full"
buttonType="secondary"
buttonHeight="sm"
onClick={() => handleCTAClick(mode)}
onClick={() => {
register({
cb: () => {
handleCTAClick(TradingMode.Swap);
},
});
openAuthModalOrTriggerCallback();
}}
/>
))}
)}
</div>
</>
)}
<AuthModal
isOpen={isAuthModalOpen}
onClose={() => setIsAuthModalOpen(false)}
/>
{tokenLaunchModalConfig.tradeConfig && (
<TradeTokenModal
isOpen={tokenLaunchModalConfig.isOpen}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@import '../../../styles/shared.scss';
@import '../../../../styles/shared.scss';

.TradeTokenModal {
.CommonTradeModal {
overflow-y: scroll;
padding-left: 8px;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { SupportedCurrencies } from 'helpers/currency';
import useBeforeUnload from 'hooks/useBeforeUnload';
import React from 'react';
import { CWText } from '../../../components/component_kit/cw_text';
import {
CWModal,
CWModalBody,
CWModalFooter,
CWModalHeader,
} from '../../../components/component_kit/new_designs/CWModal';
import TokenIcon from '../TokenIcon';
import { TradeTokenModalProps } from '../types';
import './CommonTradeModal.scss';
import TradeTokenForm, {
useCommonTradeTokenForm,
} from './CommonTradeTokenForm';

const TRADING_CURRENCY = SupportedCurrencies.USD; // make configurable when needed

const CommonTradeModal = ({
isOpen,
onModalClose,
tradeConfig,
}: TradeTokenModalProps) => {
const { trading, addresses, isActionPending, onCTAClick } =
useCommonTradeTokenForm({
tradeConfig: {
...tradeConfig,
currency: TRADING_CURRENCY,
buyTokenPresetAmounts: [100, 300, 1000],
sellTokenPresetAmounts: ['Max'],
},
addressType: tradeConfig.addressType,
onTradeComplete: () => onModalClose?.(),
});

useBeforeUnload(isActionPending);

return (
<CWModal
open={isOpen}
onClose={() => !isActionPending && onModalClose?.()}
size="medium"
className="CommonTradeModal"
content={
<>
<CWModalHeader
label={
<CWText type="h4" className="token-info">
Trade Token - {tradeConfig.token.symbol}{' '}
{trading.token.icon_url && (
<TokenIcon size="large" url={trading.token.icon_url} />
)}
</CWText>
}
onModalClose={() => !isActionPending && onModalClose?.()}
/>
<CWModalBody>
<TradeTokenForm
trading={trading}
addresses={addresses}
onCTAClick={onCTAClick}
isActionPending={isActionPending}
/>
</CWModalBody>
<CWModalFooter>
<></>
</CWModalFooter>
</>
}
/>
);
};

export default CommonTradeModal;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '../../../../../styles/shared.scss';
@import '../../../../../../styles/shared.scss';

.AddressBalance {
display: flex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React from 'react';
import { Skeleton } from 'views/components/Skeleton';
import { CWIcon } from 'views/components/component_kit/cw_icons/cw_icon';
import { CWText } from 'views/components/component_kit/cw_text';
import TokenIcon from '../../TokenIcon';
import { AddressBalanceProps, TradingMode } from '../types';
import TokenIcon from '../../../TokenIcon';
import { TradingMode } from '../../../types';
import { AddressBalanceProps } from '../types';
import './AddressBalance.scss';

const AddressBalance = ({ trading, addresses }: AddressBalanceProps) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '../../../../../styles/shared.scss';
@import '../../../../../../styles/shared.scss';

.AmountSelections {
display: flex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { CWIcon } from 'views/components/component_kit/cw_icons/cw_icon';
import { CWText } from 'views/components/component_kit/cw_text';
import { CWTag } from 'views/components/component_kit/new_designs/CWTag';
import { CWTextInput } from 'views/components/component_kit/new_designs/CWTextInput';
import TokenIcon from '../../TokenIcon';
import TokenIcon from '../../../TokenIcon';
import { BuyAmountSelectionProps } from '../types';
import './AmountSelections.scss';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
@import '../../../../styles/shared.scss';
@import '../../../../../styles/shared.scss';

.TradeTokenForm {
.CommonTradeTokenForm {
display: flex;
flex-direction: column;
gap: 12px;

.Tab {
.Text {
text-transform: capitalize;
}
}

.balance-row {
display: flex;
justify-content: space-between;
Expand Down
Loading
Loading