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

[ERC20 Launchpad] - Endpoint integration for Launch token step #9227

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
buyToken,
getPrice,
launchToken,
sellToken,
transferLiquidity,
} from '../../../../../../libs/shared/src/commonProtocol';
Expand All @@ -16,6 +17,23 @@ class LaunchpadBondingCurve extends ContractBase {
this.tokenAddress = tokenAddress;
}

async launchToken(name: string, symbol: string, walletAddress: string) {
if (!this.initialized || !this.walletEnabled) {
await this.initialize(true);
}

const txReceipt = await launchToken(
mzparacha marked this conversation as resolved.
Show resolved Hide resolved
this.contract,
name,
symbol,
[], // TODO 9207: where do shares come from?
[], // TODO 9207: where do holders come from?
0, // TODO 9207: where does totalSupply come from?
walletAddress,
);
return txReceipt;
}

async buyToken(amountEth: number, walletAddress: string) {
if (!this.initialized || !this.walletEnabled) {
await this.initialize(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import useLaunchTokenMutation from './launchToken';

export { useLaunchTokenMutation };
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { commonProtocol } from '@hicommonwealth/shared';
import { useMutation } from '@tanstack/react-query';
import LaunchpadBondingCurve from 'helpers/ContractHelpers/Launchpad';

interface LaunchTokenProps {
chainRpc: string;
ethChainId: number;
name: string;
symbol: string;
walletAddress: string;
}

const launchToken = async ({
ethChainId,
chainRpc,
name,
symbol,
walletAddress,
}: LaunchTokenProps) => {
const launchPad = new LaunchpadBondingCurve(
'',
commonProtocol.factoryContracts[ethChainId].factory,
chainRpc,
);

return await launchPad.launchToken(name, symbol, walletAddress);
};

const useLaunchTokenMutation = () => {
return useMutation({
mutationFn: launchToken,
});
};

export default useLaunchTokenMutation;
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ interface NewCommunityAdminModalProps {
handleClickConnectNewWallet: () => void;
handleClickContinue: (selectedAddress: string) => void;
selectedCommunity: SelectedCommunity;
isTokenizedCommunity?: boolean;
}

const NewCommunityAdminModal = ({
onModalClose,
handleClickConnectNewWallet,
handleClickContinue,
selectedCommunity,
isTokenizedCommunity,
}: NewCommunityAdminModalProps) => {
const user = useUserStore();
const availableAddressesOnSelectedChain = user.addresses?.filter(
Expand Down Expand Up @@ -68,14 +70,23 @@ const NewCommunityAdminModal = ({

return (
<div className="NewCommunityAdminModal">
<CWModalHeader label="New community admin" onModalClose={onModalClose} />
<CWModalHeader
label={`New ${isTokenizedCommunity ? 'token and' : ''} community admin`}
onModalClose={onModalClose}
/>
<CWModalBody>
<CWText type="b1" className="description">
{walletsAvailable
? 'Community admins are associated with a wallet. ' +
'Which wallet would you like to serve as the admin of the new community?'
: 'In order to launch a community within an ecosystem you must have a compatible wallet connected. ' +
'How would you like to create your community?'}
? `${isTokenizedCommunity ? 'Token creators and' : ''} Community admins are associated with a wallet. ` +
`Which wallet would you like to serve as the ${
isTokenizedCommunity ? 'author/' : ''
}admin of the new community?`
: `In order to launch a ${
isTokenizedCommunity ? 'token and' : ''
} community within ${
isTokenizedCommunity ? 'the BASE' : 'an'
} ecosystem you must have a compatible wallet connected. ` +
`How would you like to create your ${isTokenizedCommunity ? 'token and' : ''} community?`}
</CWText>
{walletsAvailable && (
<CWSelectList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ import { CreateTokenCommunityStep, getFormSteps } from './utils';

const LaunchToken = () => {
const navigate = useCommonNavigate();
const { createTokenCommunityStep, onChangeStep } = useCreateCommunity();
const {
createTokenCommunityStep,
onChangeStep,
selectedAddress,
setSelectedAddress,
createdTokenInfo,
setCreatedTokenInfo,
} = useCreateCommunity();

const { isAddedToHomeScreen } = useAppStatus();

Expand All @@ -33,14 +40,26 @@ const LaunchToken = () => {
return (
<TokenInformationStep
handleGoBack={() => navigate('/')} // redirect to home
handleContinue={() => onChangeStep(true)}
handleContinue={(tokenInfo) => {
setCreatedTokenInfo({
name: tokenInfo.tokenName,
symbol: tokenInfo.tokenTicker,
description: tokenInfo.tokenDescription,
imageURL: tokenInfo.tokenImageURL,
});

onChangeStep(true);
}}
onAddressSelected={(address) => setSelectedAddress(address)}
selectedAddress={selectedAddress}
/>
);
case CreateTokenCommunityStep.CommunityInformation:
return (
<CommunityInformationStep
handleGoBack={() => onChangeStep(false)}
handleContinue={() => onChangeStep(true)}
tokenInfo={createdTokenInfo}
/>
);
case CreateTokenCommunityStep.SignatureLaunch:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import CommunityInformationForm from 'views/components/CommunityInformationForm/
import { CWText } from 'views/components/component_kit/cw_text';
import CWBanner from 'views/components/component_kit/new_designs/CWBanner';
import { openConfirmation } from 'views/modals/confirmation_modal';
import { TokenInfo } from '../../types';
import './CommunityInformationStep.scss';

interface CommunityInformationStepProps {
handleGoBack: () => void;
handleContinue: () => void;
tokenInfo?: TokenInfo;
}

const CommunityInformationStep = ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
import { ChainBase, commonProtocol } from '@hicommonwealth/shared';
import { notifyError } from 'controllers/app/notifications';
import useAppStatus from 'hooks/useAppStatus';
import { useBrowserAnalyticsTrack } from 'hooks/useBrowserAnalyticsTrack';
import React, { useState } from 'react';
import useRunOnceOnCondition from 'hooks/useRunOnceOnCondition';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import {
BaseMixpanelPayload,
MixpanelCommunityCreationEvent,
MixpanelLoginPayload,
} from 'shared/analytics/types';
import { useLaunchTokenMutation } from 'state/api/launchPad';
import { fetchCachedNodes } from 'state/api/nodes';
import useUserStore from 'state/ui/user';
import {
CWCoverImageUploader,
ImageBehavior,
} from 'views/components/component_kit/cw_cover_image_uploader';
import { CWLabel } from 'views/components/component_kit/cw_label';
import { CWTextArea } from 'views/components/component_kit/cw_text_area';
import { CWButton } from 'views/components/component_kit/new_designs/CWButton';
import CWCommunitySelector from 'views/components/component_kit/new_designs/CWCommunitySelector';
import { CWForm } from 'views/components/component_kit/new_designs/CWForm';
import CWCommunitySelector, {
CommunityType,
} from 'views/components/component_kit/new_designs/CWCommunitySelector';
import {
CWForm,
CWFormRef,
} from 'views/components/component_kit/new_designs/CWForm';
import { CWModal } from 'views/components/component_kit/new_designs/CWModal';
import { CWTextInput } from 'views/components/component_kit/new_designs/CWTextInput';
import { AuthModal } from 'views/modals/AuthModal';
import NewCommunityAdminModal from 'views/modals/NewCommunityAdminModal';
import { openConfirmation } from 'views/modals/confirmation_modal';
import { communityTypeOptions } from 'views/pages/CreateCommunity/steps/CommunityTypeStep/helpers';
import './TokenInformationForm.scss';
Expand All @@ -25,25 +39,91 @@ import { tokenInformationFormValidationSchema } from './validation';
const TokenInformationForm = ({
onSubmit,
onCancel,
onAddressSelected,
selectedAddress,
}: TokenInformationFormProps) => {
const user = useUserStore();
const [baseOption] = communityTypeOptions;

const shouldSubmitOnAddressSelection = useRef(false);
const formMethodsRef = useRef<CWFormRef>();
const [
isNewTokenCommunityAdminModalOpen,
setIsNewTokenCommunityAdminModalOpen,
] = useState(false);
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
const [isProcessingProfileImage, setIsProcessingProfileImage] =
useState(false);

const { isAddedToHomeScreen } = useAppStatus();

const [baseOption] = communityTypeOptions;

const { trackAnalytics } = useBrowserAnalyticsTrack<
MixpanelLoginPayload | BaseMixpanelPayload
>({
onAction: true,
});

const handleSubmit = (values: FormSubmitValues) => {
// TODO 8705: call token launch endpoint
console.log('values => ', values);
onSubmit();
};
const { mutateAsync: launchToken } = useLaunchTokenMutation();

const openAddressSelectionModal = useCallback(() => {
if (selectedAddress) {
setIsAuthModalOpen(false);
setIsNewTokenCommunityAdminModalOpen(false);
} else {
setIsAuthModalOpen(!user.isLoggedIn);
setIsNewTokenCommunityAdminModalOpen(user.isLoggedIn);
}
}, [selectedAddress, user.isLoggedIn]);

useRunOnceOnCondition({
callback: openAddressSelectionModal,
shouldRun: true,
});

const handleSubmit = useCallback(
async (values: FormSubmitValues) => {
// get address from user
if (!selectedAddress) {
openAddressSelectionModal();
shouldSubmitOnAddressSelection.current = true;
return;
}

// get base chain node info
const nodes = fetchCachedNodes();
const baseNode = nodes?.find(
(n) => n.ethChainId === commonProtocol.ValidChains.Base,
);
if (!baseNode || !baseNode.ethChainId) {
notifyError('Could not find base chain node');
return;
}

// call endpoint
mzparacha marked this conversation as resolved.
Show resolved Hide resolved
const payload = {
chainRpc: baseNode.url,
ethChainId: baseNode.ethChainId,
name: values.tokenName.trim(),
symbol: values.tokenTicker.trim(),
walletAddress: selectedAddress.address,
// TODO 9207: where to store values.tokenDescription and values.tokenImageURL
};
await launchToken(payload).catch(console.error);
mzparacha marked this conversation as resolved.
Show resolved Hide resolved

onSubmit(values);
},
[openAddressSelectionModal, selectedAddress, onSubmit, launchToken],
);

useEffect(() => {
if (shouldSubmitOnAddressSelection.current) {
mzparacha marked this conversation as resolved.
Show resolved Hide resolved
formMethodsRef.current &&
formMethodsRef.current
.handleSubmit(handleSubmit)()
.catch(console.error);
shouldSubmitOnAddressSelection.current = false;
}
}, [selectedAddress, handleSubmit]);

const handleCancel = () => {
openConfirmation({
Expand Down Expand Up @@ -73,8 +153,24 @@ const TokenInformationForm = ({
});
};

const handleClickConnectNewWallet = () => {
setIsAuthModalOpen(true);
setIsNewTokenCommunityAdminModalOpen(false);
};

const handleSelectedAddress = (address: string) => {
const pickedAddressInfo = user.addresses.find(
({ addressId }) => String(addressId) === address,
);
pickedAddressInfo && onAddressSelected(pickedAddressInfo);

setIsNewTokenCommunityAdminModalOpen(false);
};

return (
<CWForm
// @ts-expect-error <StrictNullChecks/>
ref={formMethodsRef}
validationSchema={tokenInformationFormValidationSchema}
onSubmit={handleSubmit}
className="TokenInformationForm"
Expand Down Expand Up @@ -147,6 +243,32 @@ const TokenInformationForm = ({
disabled={isProcessingProfileImage}
/>
</section>

<CWModal
size="small"
visibleOverflow
content={
<NewCommunityAdminModal
onModalClose={() => setIsNewTokenCommunityAdminModalOpen(false)}
selectedCommunity={{
// token launch is only support for `Base`
chainBase: ChainBase.Ethereum,
type: CommunityType.Base,
}}
handleClickConnectNewWallet={handleClickConnectNewWallet}
handleClickContinue={handleSelectedAddress}
isTokenizedCommunity
/>
}
onClose={() => setIsNewTokenCommunityAdminModalOpen(false)}
open={isNewTokenCommunityAdminModalOpen}
/>
<AuthModal
isOpen={isAuthModalOpen}
onClose={() => setIsAuthModalOpen(false)}
onSuccess={() => setIsNewTokenCommunityAdminModalOpen(true)}
showWalletsFor={ChainBase.Ethereum}
/>
</CWForm>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import AddressInfo from 'models/AddressInfo';

export type FormSubmitValues = {
tokenChain: string;
tokenName: string;
Expand All @@ -7,6 +9,8 @@ export type FormSubmitValues = {
};

export type TokenInformationFormProps = {
onSubmit: () => void;
onSubmit: (values: FormSubmitValues) => void;
onCancel: () => void;
selectedAddress?: AddressInfo;
onAddressSelected: (address: AddressInfo) => void;
};
Loading
Loading