diff --git a/gno/r/launchpad_grc20/token_factory_grc20.gno b/gno/r/launchpad_grc20/token_factory_grc20.gno index 5f6e279cf..0fcf3d414 100644 --- a/gno/r/launchpad_grc20/token_factory_grc20.gno +++ b/gno/r/launchpad_grc20/token_factory_grc20.gno @@ -190,6 +190,19 @@ func GetLastTokensJSON() string { return json.ArrayNode("", nodes).String() } +func GetUserTokensJSON(user string) string { + userAddr := std.Address(user) + var nodes []*json.Node + tokens.Iterate("", "", func(key string, value interface{}) bool { + token, _ := value.(*Token) + if token.admin.Owner() == userAddr { + nodes = append(nodes, token.ToJSON()) + } + return false + }) + return json.ArrayNode("", nodes).String() +} + func mustGetToken(name string) *Token { t, exists := tokens.Get(name) if !exists { diff --git a/packages/components/navigation/getNormalModeScreens.tsx b/packages/components/navigation/getNormalModeScreens.tsx index daefa37c4..6cdb4c1cd 100644 --- a/packages/components/navigation/getNormalModeScreens.tsx +++ b/packages/components/navigation/getNormalModeScreens.tsx @@ -28,6 +28,7 @@ import { LaunchpadERC20CreateSaleScreen } from "@/screens/LaunchpadERC20/Launchp import { LaunchpadERC20SalesScreen } from "@/screens/LaunchpadERC20/LaunchpadERC20Sales/LaunchpadERC20SalesScreen"; import { LaunchpadERC20Screen } from "@/screens/LaunchpadERC20/LaunchpadERC20Screen"; import { LaunchpadERC20CreateTokenScreen } from "@/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20CreateTokenScreen"; +import { LaunchpadERC20ManageTokenScreen } from "@/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20ManageToken"; import { LaunchpadERC20TokensScreen } from "@/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20TokensScreen"; import { LaunchpadERC20AirdropsScreen } from "@/screens/LaunchpadERC20/LaunchpadERCAirdrops/LaunchpadERC20AirdropsScreen"; import { LaunchpadERC20CreateAirdropScreen } from "@/screens/LaunchpadERC20/LaunchpadERCAirdrops/LaunchpadERC20CreateAirdropScreen"; @@ -315,6 +316,15 @@ export const getNormalModeScreens = ({ appMode }: { appMode: AppMode }) => { }} /> + null, + title: screenTitle("Launchpad ERC20 Manage Token"), + }} + /> + { ); await queryClient.invalidateQueries(["lastTokens"]); + await queryClient.invalidateQueries(["userTokens"]); setIsShowConfirmModal(false); setIsShowModal(true); diff --git a/packages/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20ManageToken.tsx b/packages/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20ManageToken.tsx new file mode 100644 index 000000000..04540eb54 --- /dev/null +++ b/packages/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20ManageToken.tsx @@ -0,0 +1,40 @@ +import { LaunchpadERC20DetailTokenBox } from "../component/LaunchpadERC20DetailTokenBox"; + +import { BrandText } from "@/components/BrandText"; +import { ScreenContainer } from "@/components/ScreenContainer"; +import { SpacerColumn } from "@/components/spacer"; +import { useForceNetworkSelection } from "@/hooks/useForceNetworkSelection"; +import { NetworkFeature, NetworkKind } from "@/networks"; +import { ScreenFC, useAppNavigation } from "@/utils/navigation"; +import { useState } from "react"; +import { LaunchpadERC20TokenAmountButton } from "../component/LaunchpadERC20TokenAmountButton"; + +export const LaunchpadERC20ManageTokenScreen: ScreenFC< + "LaunchpadERC20ManageToken" +> = ({ route: { params } }) => { + const network = params?.network; + const token = params.token; + useForceNetworkSelection(network); + const navigation = useAppNavigation(); + const [mintAmount, setMintAmount] = useState(0); + const [burnAmount, setBurnAmount] = useState(0); + + return ( + Launchpad ERC 20} + forceNetworkFeatures={[NetworkFeature.LaunchpadERC20]} + forceNetworkKind={NetworkKind.Gno} + isLarge + responsive + onBackPress={() => navigation.navigate("LaunchpadERC20Tokens")} + > + + + Tokens Details + + + + + + ); +}; diff --git a/packages/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20TokensScreen.tsx b/packages/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20TokensScreen.tsx index 1c6b5dc0a..4d7a77e4f 100644 --- a/packages/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20TokensScreen.tsx +++ b/packages/screens/LaunchpadERC20/LaunchpadERC20Tokens/LaunchpadERC20TokensScreen.tsx @@ -1,10 +1,12 @@ -import React from "react"; +import { useState } from "react"; import { useWindowDimensions, View } from "react-native"; import exploreSVG from "../../../../assets/icons/explore-neutral77.svg"; import penSVG from "../../../../assets/icons/pen-neutral77.svg"; import registerSVG from "../../../../assets/icons/register-neutral77.svg"; +import { SelectUserTokenModal } from "../component/LaunchpadERC20SelectUserTokenModal"; import { TokensTable } from "../component/LaunchpadERC20TokensTable"; +import { useUserTokens } from "../hooks/useUserTokens"; import { breakpoints } from "../utils/breakpoints"; import { BrandText } from "@/components/BrandText"; @@ -13,6 +15,7 @@ import { FlowCard } from "@/components/cards/FlowCard"; import { SpacerColumn } from "@/components/spacer"; import { useForceNetworkSelection } from "@/hooks/useForceNetworkSelection"; import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import useSelectedWallet from "@/hooks/useSelectedWallet"; import { NetworkFeature, NetworkKind } from "@/networks"; import { ScreenFC, useAppNavigation } from "@/utils/navigation"; @@ -24,6 +27,14 @@ export const LaunchpadERC20TokensScreen: ScreenFC<"LaunchpadERC20Tokens"> = ({ const networkId = useSelectedNetworkId(); const { width } = useWindowDimensions(); const navigation = useAppNavigation(); + const [isModalVisible, setIsModalVisible] = useState(false); + const selectedWallet = useSelectedWallet(); + const caller = selectedWallet?.address; + const { data: tokens } = useUserTokens(networkId, caller || ""); + + const toggleModal = () => { + setIsModalVisible(!isModalVisible); + }; return ( = ({ label="Manage" description="Mint, burn, or transfer the tokens you own" iconSVG={penSVG} - onPress={() => {}} + onPress={() => { + setIsModalVisible(true); + }} style={{ marginHorizontal: width >= breakpoints.MD_BREAKPOINT ? 12 : 0, marginVertical: width >= breakpoints.LG_BREAKPOINT ? 0 : 12, }} - disabled /> = ({ disabled /> + diff --git a/packages/screens/LaunchpadERC20/component/LaunchpadERC20AirdropsTable.tsx b/packages/screens/LaunchpadERC20/component/LaunchpadERC20AirdropsTable.tsx index f8c1a3c15..6da10cd32 100644 --- a/packages/screens/LaunchpadERC20/component/LaunchpadERC20AirdropsTable.tsx +++ b/packages/screens/LaunchpadERC20/component/LaunchpadERC20AirdropsTable.tsx @@ -15,8 +15,8 @@ import { TableRow } from "@/components/table/TableRow"; import { TableTextCell } from "@/components/table/TableTextCell"; import { TableWrapper } from "@/components/table/TableWrapper"; import { TableColumns } from "@/components/table/utils"; -import { Airdrop } from "@/utils/launchpadERC20/types"; import { screenContentMaxWidthLarge } from "@/utils/style/layout"; +import { Airdrop } from "@/utils/types/types"; const columns: TableColumns = { id: { diff --git a/packages/screens/LaunchpadERC20/component/LaunchpadERC20DetailTokenBox.tsx b/packages/screens/LaunchpadERC20/component/LaunchpadERC20DetailTokenBox.tsx new file mode 100644 index 000000000..f6dee4a2a --- /dev/null +++ b/packages/screens/LaunchpadERC20/component/LaunchpadERC20DetailTokenBox.tsx @@ -0,0 +1,36 @@ +import { View } from "react-native"; + +import { BrandText } from "@/components/BrandText"; +import { neutralA3 } from "@/utils/style/colors"; +import { layout } from "@/utils/style/layout"; +import { Token } from "@/utils/types/types"; + +interface SelectTokenModalProps { + item: Token; +} + +export const LaunchpadERC20DetailTokenBox: React.FC = ({ + item, +}) => { + return ( + + {"Name: " + item.name} + {"Symbol: " + item.symbol} + {"Decimals: " + item.decimals} + + {"Total Supply : " + item.totalSupply + " " + item.symbol} + + {item.allowMint ? "Allow Mint" : "Not Allow Mint"} + {item.allowBurn ? "Allow Burn" : "Not Allow Burn"} + + ); +}; diff --git a/packages/screens/LaunchpadERC20/component/LaunchpadERC20SalesTable.tsx b/packages/screens/LaunchpadERC20/component/LaunchpadERC20SalesTable.tsx index 69627f08b..dfc6ff893 100644 --- a/packages/screens/LaunchpadERC20/component/LaunchpadERC20SalesTable.tsx +++ b/packages/screens/LaunchpadERC20/component/LaunchpadERC20SalesTable.tsx @@ -11,8 +11,8 @@ import { TableRow } from "@/components/table/TableRow"; import { TableTextCell } from "@/components/table/TableTextCell"; import { TableWrapper } from "@/components/table/TableWrapper"; import { TableColumns } from "@/components/table/utils"; -import { Sale } from "@/utils/launchpadERC20/types"; import { screenContentMaxWidthLarge } from "@/utils/style/layout"; +import { Sale } from "@/utils/types/types"; const columns: TableColumns = { id: { diff --git a/packages/screens/LaunchpadERC20/component/LaunchpadERC20SelectUserTokenModal.tsx b/packages/screens/LaunchpadERC20/component/LaunchpadERC20SelectUserTokenModal.tsx new file mode 100644 index 000000000..643c4832d --- /dev/null +++ b/packages/screens/LaunchpadERC20/component/LaunchpadERC20SelectUserTokenModal.tsx @@ -0,0 +1,77 @@ +import { useState } from "react"; +import { View } from "react-native"; + +import { LaunchpadERC20TokensDropdown } from "./LaunchpadERC20TokensDropdown"; + +import { BrandText } from "@/components/BrandText"; +import FlexRow from "@/components/FlexRow"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import ModalBase from "@/components/modals/ModalBase"; +import { SpacerColumn } from "@/components/spacer"; +import { useAppNavigation } from "@/utils/navigation"; +import { errorColor, neutral77 } from "@/utils/style/colors"; +import { fontSemibold14 } from "@/utils/style/fonts"; +import { emptyToken, Token } from "@/utils/types/types"; + +interface SelectTokenModalProps { + isVisible: boolean; + onClose: () => void; + items: Token[] | undefined | null; +} + +export const SelectUserTokenModal: React.FC = ({ + isVisible, + onClose, + items, +}) => { + const [selectedItem, setSelectedItem] = useState(emptyToken); + const props = { isVisible, onClose }; + const navigation = useAppNavigation(); + + return ( + + + + Select your ERC20 Token + + + {items && items.length !== 0 ? ( + + ) : ( + + You don't have any ERC20 tokens + + )} + + + { + navigation.navigate("LaunchpadERC20ManageToken", { + token: selectedItem, + }); + onClose(); + }} + text="Open" + size="SM" + disabled={!selectedItem} + /> + + + + + ); +}; diff --git a/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokenAmountButton.tsx b/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokenAmountButton.tsx new file mode 100644 index 000000000..1564c832d --- /dev/null +++ b/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokenAmountButton.tsx @@ -0,0 +1,67 @@ +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { Label } from "@/components/inputs/TextInputCustom"; +import { neutral17, neutralFF } from "@/utils/style/colors"; +import { fontSemibold16 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; +import { StyleProp, TextInput, View, ViewStyle } from "react-native"; + +interface LaunchpadERC20TokenAmountButtonProps { + amount: number; + setAmount: (amount: number) => void; + placeholder?: string; + buttonLabel: string; + onPress?: () => void; + disabled?: boolean; + viewStyle: StyleProp; +} + +export const LaunchpadERC20TokenAmountButton: React.FC = ({ + amount, + setAmount, + placeholder = "Amount", + buttonLabel, + onPress, + disabled, + viewStyle +}) => { + return ( + + + + + setAmount(Number(text))} + keyboardType="numeric" + /> + + + + ); +}; \ No newline at end of file diff --git a/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokensDropdown.tsx b/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokensDropdown.tsx new file mode 100644 index 000000000..8b0172049 --- /dev/null +++ b/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokensDropdown.tsx @@ -0,0 +1,124 @@ +import React, { useState } from "react"; +import { TouchableOpacity, View } from "react-native"; + +import chevronDownSVG from "../../../../assets/icons/chevron-down.svg"; +import chevronUpSVG from "../../../../assets/icons/chevron-up.svg"; + +import { BrandText } from "@/components/BrandText"; +import { SVG } from "@/components/SVG"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { Label } from "@/components/inputs/TextInputCustom"; +import { neutral17, neutralFF, secondaryColor } from "@/utils/style/colors"; +import { fontSemibold16 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; +import { Token } from "@/utils/types/types"; + +interface LaunchpadERC20TokensDropdownProps { + items: Token[]; + setSelectedItem: (token: Token) => void; + selectedItem?: Token | null; + placeholder?: string; +} + +export const LaunchpadERC20TokensDropdown: React.FC< + LaunchpadERC20TokensDropdownProps +> = ({ + items, + setSelectedItem, + selectedItem, + placeholder = "Select an item", +}) => { + const [isDropdownOpen, setDropdownState] = useState(false); + + const selectItem = (item: Token) => { + setSelectedItem(item); + setDropdownState(false); + }; + + return ( + + + + + setDropdownState(!isDropdownOpen)} + > + + {selectedItem?.name || placeholder} + + + + + {isDropdownOpen && ( + + + {items && + items.map((item, index) => ( + selectItem(item)} + > + + + {item.name} + + + + ))} + + + )} + + ); +}; diff --git a/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokensTable.tsx b/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokensTable.tsx index 3dfe0b486..cea6a9f57 100644 --- a/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokensTable.tsx +++ b/packages/screens/LaunchpadERC20/component/LaunchpadERC20TokensTable.tsx @@ -15,8 +15,8 @@ import { TableRow } from "@/components/table/TableRow"; import { TableTextCell } from "@/components/table/TableTextCell"; import { TableWrapper } from "@/components/table/TableWrapper"; import { TableColumns } from "@/components/table/utils"; -import { Token } from "@/utils/launchpadERC20/types"; import { screenContentMaxWidthLarge } from "@/utils/style/layout"; +import { Token } from "@/utils/types/types"; const columns: TableColumns = { symbol: { diff --git a/packages/screens/LaunchpadERC20/hooks/useLastAirdrops.ts b/packages/screens/LaunchpadERC20/hooks/useLastAirdrops.ts index 212f7d8f6..e90a5369c 100644 --- a/packages/screens/LaunchpadERC20/hooks/useLastAirdrops.ts +++ b/packages/screens/LaunchpadERC20/hooks/useLastAirdrops.ts @@ -9,10 +9,10 @@ import { } from "../../../networks"; import { extractGnoJSONString } from "@/utils/gno"; -import { zodAidrop } from "@/utils/launchpadERC20/types"; +import { zodAidrop } from "@/utils/types/types"; export const useLastAirdrops = (networkId: string) => { - return useQuery(["lastAirdrops"], async () => { + return useQuery(["lastAirdrops", networkId], async () => { const gnoNetwork = getGnoNetwork(networkId); if (!gnoNetwork) { return null; diff --git a/packages/screens/LaunchpadERC20/hooks/useLastSales.ts b/packages/screens/LaunchpadERC20/hooks/useLastSales.ts index c350e1e34..5ae3f7479 100644 --- a/packages/screens/LaunchpadERC20/hooks/useLastSales.ts +++ b/packages/screens/LaunchpadERC20/hooks/useLastSales.ts @@ -9,10 +9,10 @@ import { } from "../../../networks"; import { extractGnoJSONString } from "@/utils/gno"; -import { zodSale } from "@/utils/launchpadERC20/types"; +import { zodSale } from "@/utils/types/types"; export const useLastSales = (networkId: string) => { - return useQuery(["lastSales"], async () => { + return useQuery(["lastSales", networkId], async () => { const gnoNetwork = getGnoNetwork(networkId); if (!gnoNetwork) { return null; diff --git a/packages/screens/LaunchpadERC20/hooks/useLastTokens.ts b/packages/screens/LaunchpadERC20/hooks/useLastTokens.ts index a14b66225..4ac1d3498 100644 --- a/packages/screens/LaunchpadERC20/hooks/useLastTokens.ts +++ b/packages/screens/LaunchpadERC20/hooks/useLastTokens.ts @@ -9,10 +9,10 @@ import { } from "../../../networks"; import { extractGnoJSONString } from "@/utils/gno"; -import { zodToken } from "@/utils/launchpadERC20/types"; +import { zodToken } from "@/utils/types/types"; export const useLastTokens = (networkId: string) => { - return useQuery(["lastTokens"], async () => { + return useQuery(["lastTokens", networkId], async () => { const gnoNetwork = getGnoNetwork(networkId); if (!gnoNetwork) { return null; diff --git a/packages/screens/LaunchpadERC20/hooks/useUserTokens.ts b/packages/screens/LaunchpadERC20/hooks/useUserTokens.ts new file mode 100644 index 000000000..7a22248cc --- /dev/null +++ b/packages/screens/LaunchpadERC20/hooks/useUserTokens.ts @@ -0,0 +1,35 @@ +import { GnoJSONRPCProvider } from "@gnolang/gno-js-client"; +import { useQuery } from "@tanstack/react-query"; +import { z } from "zod"; + +import { getGnoNetwork, getNetworkFeature, NetworkFeature } from "@/networks"; +import { extractGnoJSONString } from "@/utils/gno"; +import { zodToken } from "@/utils/types/types"; + +export const useUserTokens = (networkId: string, addr: string) => { + return useQuery(["userTokens", addr, networkId], async () => { + const gnoNetwork = getGnoNetwork(networkId); + if (!gnoNetwork) { + return null; + } + + const pmFeature = getNetworkFeature( + networkId, + NetworkFeature.LaunchpadERC20, + ); + + if (!pmFeature) { + return null; + } + + const client = new GnoJSONRPCProvider(gnoNetwork.endpoint); + const pkgPath = pmFeature.launchpadERC20PkgPath; + const query = `GetUserTokensJSON("${addr}")`; + const contractData = await client.evaluateExpression(pkgPath, query); + + const res = extractGnoJSONString(contractData); + const tokens = z.array(zodToken).parse(res); + + return tokens; + }); +}; diff --git a/packages/screens/Projects/components/MilestoneBoard.tsx b/packages/screens/Projects/components/MilestoneBoard.tsx index ca69fa4af..d47cd5029 100644 --- a/packages/screens/Projects/components/MilestoneBoard.tsx +++ b/packages/screens/Projects/components/MilestoneBoard.tsx @@ -18,9 +18,9 @@ import { SVG } from "../../../components/SVG"; import { SimpleButton } from "../../../components/buttons/SimpleButton"; import { SpacerColumn, SpacerRow } from "../../../components/spacer"; import { - ProjectMilestone, - MilestoneStatus, MilestoneFormValues, + MilestoneStatus, + ProjectMilestone, } from "../../../utils/projects/types"; import { neutral00, diff --git a/packages/screens/Projects/components/ProjectInfo/RelatedUsers.tsx b/packages/screens/Projects/components/ProjectInfo/RelatedUsers.tsx index 56453ce10..a0a9be7ec 100644 --- a/packages/screens/Projects/components/ProjectInfo/RelatedUsers.tsx +++ b/packages/screens/Projects/components/ProjectInfo/RelatedUsers.tsx @@ -1,6 +1,6 @@ import { Link } from "@react-navigation/native"; import React from "react"; -import { Table, Row, Cell, TableWrapper } from "react-native-reanimated-table"; +import { Cell, Row, Table, TableWrapper } from "react-native-reanimated-table"; import { SubmitCandidacyButton } from "./LeftBlock"; diff --git a/packages/screens/Projects/components/ProjectMilestones.tsx b/packages/screens/Projects/components/ProjectMilestones.tsx index f976e967d..611a758d8 100644 --- a/packages/screens/Projects/components/ProjectMilestones.tsx +++ b/packages/screens/Projects/components/ProjectMilestones.tsx @@ -9,10 +9,10 @@ import { Separator } from "../../../components/separators/Separator"; import { SpacerRow } from "../../../components/spacer"; import { ProjectMilestone } from "../../../utils/projects/types"; import { - neutral77, - neutral33, neutral00, neutral17, + neutral33, + neutral77, } from "../../../utils/style/colors"; import { fontSemibold20 } from "../../../utils/style/fonts"; import { layout } from "../../../utils/style/layout"; diff --git a/packages/utils/navigation.ts b/packages/utils/navigation.ts index ecb6498e2..a16b30cef 100644 --- a/packages/utils/navigation.ts +++ b/packages/utils/navigation.ts @@ -6,6 +6,7 @@ import { feedsTabItems } from "./social-feed"; import { AppMode } from "./types/app-mode"; import { NewPostFormValues } from "./types/feed"; import { MessageFriendsTabItem } from "./types/message"; +import { Token } from "./types/types"; import { uppTabItems } from "./upp"; export type RouteName = keyof RootStackParamList; @@ -43,6 +44,7 @@ export type RootStackParamList = { LaunchpadERC20: undefined; LaunchpadERC20Tokens?: { network?: string }; LaunchpadERC20CreateToken: { step?: number }; + LaunchpadERC20ManageToken: { network?: string; token: Token }; LaunchpadERC20Airdrops?: { network?: string }; LaunchpadERC20CreateAirdrop: { step?: number }; LaunchpadERC20Sales?: { network?: string }; @@ -229,6 +231,7 @@ const navConfig: { LaunchpadERC20: "launchpad-erc20", LaunchpadERC20Tokens: "launchpad-erc20/tokens", LaunchpadERC20CreateToken: "launchpad-erc20/create-token", + LaunchpadERC20ManageToken: "launchpad-erc20/manage-token", LaunchpadERC20Airdrops: "launchpad-erc20/airdrops", LaunchpadERC20CreateAirdrop: "launchpad-erc20/create-airdrop", LaunchpadERC20Sales: "launchpad-erc20/sales", diff --git a/packages/utils/launchpadERC20/types.ts b/packages/utils/types/types.ts similarity index 82% rename from packages/utils/launchpadERC20/types.ts rename to packages/utils/types/types.ts index 9da691e6b..fce712b6a 100644 --- a/packages/utils/launchpadERC20/types.ts +++ b/packages/utils/types/types.ts @@ -14,6 +14,18 @@ export const zodToken = z.object({ export type Token = z.infer; +export const emptyToken = { + symbol: "", + name: "", + decimals: "", + admin: "", + image: "", + totalSupply: "", + totalSupplyCap: "", + allowMint: false, + allowBurn: false, +}; + export const zodAidrop = z.object({ id: z.string(), tokenName: z.string(),