diff --git a/CHANGELOG.md b/CHANGELOG.md index ce54dd59c..2cd827c8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#727](https://github.com/alleslabs/celatone-frontend/pull/727) Add amp nft pages - [#720](https://github.com/alleslabs/celatone-frontend/pull/720) Add delegations for contract detail - [#684](https://github.com/alleslabs/celatone-frontend/pull/684) Add nft page - [#717](https://github.com/alleslabs/celatone-frontend/pull/717) Add total value for contract detail @@ -48,6 +49,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Improvements - [#725](https://github.com/alleslabs/celatone-frontend/pull/725) Adjust unsupported asset display in account and contract detail +- [#728](https://github.com/alleslabs/celatone-frontend/pull/728) Support tx events for cosmos sdk 0.50 +- [#729](https://github.com/alleslabs/celatone-frontend/pull/729) Add missing title for block transactions - [#726](https://github.com/alleslabs/celatone-frontend/pull/726) Add missing title for instantiate permission and adjust contract menu in nav bar - [#713](https://github.com/alleslabs/celatone-frontend/pull/713) Adjust search state consistency - [#712](https://github.com/alleslabs/celatone-frontend/pull/712) api v1 - proposal list diff --git a/src/config/chain/neutron.ts b/src/config/chain/neutron.ts index 8d4a52897..75d8196b0 100644 --- a/src/config/chain/neutron.ts +++ b/src/config/chain/neutron.ts @@ -80,7 +80,7 @@ export const NEUTRON_CHAIN_CONFIGS: ChainConfigs = { enabled: false, }, nft: { - enabled: true, + enabled: false, }, }, gas: { diff --git a/src/lib/amplitude/types.ts b/src/lib/amplitude/types.ts index 4bb0477c3..85fb609b8 100644 --- a/src/lib/amplitude/types.ts +++ b/src/lib/amplitude/types.ts @@ -29,7 +29,7 @@ export enum AmpEvent { // NAVIGATE TO_OVERVIEW = "To Overview", TO_BLOCKS = "To Blocks", - TO_BLOCK_DETAIL = "To Block Detail", + TO_BLOCK_DETAILS = "To Block Detail", TO_TXS = "To Txs", TO_MODULES = "To Modules", TO_PAST_TXS = "To Past Txs", @@ -51,25 +51,25 @@ export enum AmpEvent { TO_LIST_OTHERS = "To List Others", TO_ALL_LISTS = "To All Lists", TO_ALL_PROJECTS = "To All Public Projects", - TO_ACCOUNT_DETAIL = "To Account Detail", - TO_CONTRACT_DETAIL = "To Contract Detail", - TO_CODE_DETAIL = "To Code Detail", - TO_PROJECT_DETAIL = "To Public Project Detail", - TO_TRANSACTION_DETAIL = "To Transaction Detail", + TO_ACCOUNT_DETAILS = "To Account Detail", + TO_CONTRACT_DETAILS = "To Contract Detail", + TO_CODE_DETAILS = "To Code Detail", + TO_PROJECT_DETAILS = "To Public Project Detail", + TO_TRANSACTION_DETAILS = "To Transaction Detail", TO_NOT_FOUND = "To 404 Not Found", TO_FAUCET = "To Faucet", TO_POOL_LIST = "To Pool List", - TO_POOL_DETAIL = "To Pool Detail", + TO_POOL_DETAILS = "To Pool Detail", TO_PROPOSAL_TO_STORE_CODE = "To Proposal To Store Code", TO_PROPOSAL_TO_WHITELIST = "To Proposal To Whitelist", - TO_MODULE_DETAIL = "To Module Detail", + TO_MODULE_DETAILS = "To Module Detail", TO_MODULE_INTERACTION = "To Module Interaction", TO_PUBLISH_MODULE = "To Publish Module", TO_DEPLOY_SCRIPT = "To Deploy Script", TO_MY_PUBLISHED_MODULES = "To My Published Modules", - TO_NFT_COLLECTIONS = "To NFT Collections", - TO_NFT_COLLECTION_DETAIL = "To NFT Collection Detail", - TO_NFT_DETAIL = "To NFT Detail", + TO_NFT_COLLECTIONS_LIST = "To NFT Collections List", + TO_NFT_COLLECTION_DETAILS = "To NFT Collection Detail", + TO_NFT_DETAILS = "To NFT Detail", // ACTIONS ACTION_UPLOAD = "Action Upload", ACTION_INSTANTIATE = "Action Instantiate", @@ -152,7 +152,7 @@ export enum AmpEvent { USE_CONTRACT_STATES_DOWNLOAD = "Use Contract States Download", USE_NAMESPACE_TAB = "Use Namespace Tab", USE_NAVIGATING_BUTTON = "Use Navigating Button", - USE_MODULE_DETAIL_MAIN_CTA = "Use Module Detail Main CTA", + USE_MODULE_DETAILS_MAIN_CTA = "Use Module Detail Main CTA", USE_MODULE_FUNCTION_CTA = "Use Module Function CTA", USE_BREADCRUMB = "Use Breadcrumb", USE_MODULE_SELECTION_DRAWER = "Use Module Selection Drawer", @@ -175,6 +175,10 @@ export enum AmpEvent { USE_MY_PUBLISHED_MODULES_CTA = "Use My Publised Modules CTA", USE_SELECT_RESOURCE_GROUP = "Use Select Resource Group", USE_MODULE_CARD = "Use Module Card", + USE_NFT_COLLECTION_INFO_CARD = "Use NFT Collection Info Card", + USE_NFT_VIEW_RESOURCE_CTA = "Use NFT View Resource CTA", + USE_NFT_CARD = "Use NFT Card", + USE_SELECT_NFT_COLLECTION_GROUP = "Use Select NFT Selection Group", // TX TX_SUCCEED = "Tx Succeed", diff --git a/src/lib/components/copy/Copier.tsx b/src/lib/components/copy/Copier.tsx index 11f5c940b..5f62d6023 100644 --- a/src/lib/components/copy/Copier.tsx +++ b/src/lib/components/copy/Copier.tsx @@ -25,7 +25,6 @@ export const Copier = ({ { const { onCopy, hasCopied, setValue } = useClipboard(value); useEffect(() => setValue(value), [value, setValue]); @@ -29,6 +27,7 @@ export const CopyTemplate = ({ return ( { onCopy(); @@ -36,7 +35,6 @@ export const CopyTemplate = ({ }} w={w} ml={ml} - display={display} > {triggerElement} diff --git a/src/lib/components/nft/NftCard.tsx b/src/lib/components/nft/NftCard.tsx index 6d34f9a77..3d4421da9 100644 --- a/src/lib/components/nft/NftCard.tsx +++ b/src/lib/components/nft/NftCard.tsx @@ -1,6 +1,7 @@ import { Box, Image, Flex, Text } from "@chakra-ui/react"; import { AppLink } from "../AppLink"; +import { AmpEvent, track } from "lib/amplitude"; import { NFT_IMAGE_PLACEHOLDER } from "lib/data"; import { useMetadata } from "lib/services/nft"; import type { HexAddr32, Option } from "lib/types"; @@ -26,7 +27,10 @@ export const NftCard = ({ return ( - + track(AmpEvent.USE_NFT_CARD, { showCollection })} + > { + track(AmpEvent.USE_SELECT_NFT_COLLECTION_GROUP, { + collectionName, + nftsCount: count, + }); + onClick(); + }} align="center" justify="space-between" > diff --git a/src/lib/pages/account-details/components/nfts/NftsByCollection.tsx b/src/lib/pages/account-details/components/nfts/NftsByCollection.tsx index 0fc9b7248..c304946be 100644 --- a/src/lib/pages/account-details/components/nfts/NftsByCollection.tsx +++ b/src/lib/pages/account-details/components/nfts/NftsByCollection.tsx @@ -59,6 +59,7 @@ export const NftsByCollection = ({ autoFocus onChange={(e) => setSearchKeyword(e.target.value)} size={{ base: "md", md: "lg" }} + amptrackSection="nft-account-detail-tokenid-search" /> { useEffect(() => { if (router.isReady && validated.success) - track(AmpEvent.TO_ACCOUNT_DETAIL, { tab: validated.data.tab }); + track(AmpEvent.TO_ACCOUNT_DETAILS, { tab: validated.data.tab }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [router.isReady]); diff --git a/src/lib/pages/block-details/components/BlockDetailsTop.tsx b/src/lib/pages/block-details/components/BlockDetailsTop.tsx index 3aac1eee1..c554c6c60 100644 --- a/src/lib/pages/block-details/components/BlockDetailsTop.tsx +++ b/src/lib/pages/block-details/components/BlockDetailsTop.tsx @@ -39,8 +39,8 @@ export const BlockDetailsTop = ({ blockData }: BlockDetailsTopProps) => { return ( diff --git a/src/lib/pages/block-details/components/BlockTxsTable.tsx b/src/lib/pages/block-details/components/BlockTxsTable.tsx index a72f5de90..b110b5f9f 100644 --- a/src/lib/pages/block-details/components/BlockTxsTable.tsx +++ b/src/lib/pages/block-details/components/BlockTxsTable.tsx @@ -1,7 +1,7 @@ import { Pagination } from "lib/components/pagination"; import { usePaginator } from "lib/components/pagination/usePaginator"; import { EmptyState } from "lib/components/state"; -import { TransactionsTable } from "lib/components/table"; +import { TableTitle, TransactionsTable } from "lib/components/table"; import { useTxsByBlockHeight } from "lib/services/txService"; const scrollComponentId = "block_tx_table_header"; @@ -32,6 +32,7 @@ export const BlockTxsTable = ({ height }: BlockTxsTableProps) => { return ( <> + { const validated = zBlockDetailQueryParams.safeParse(router.query); useEffect(() => { - if (router.isReady) track(AmpEvent.TO_BLOCK_DETAIL); + if (router.isReady) track(AmpEvent.TO_BLOCK_DETAILS); }, [router.isReady]); return ( diff --git a/src/lib/pages/code-details/index.tsx b/src/lib/pages/code-details/index.tsx index cf1318629..6a975d77e 100644 --- a/src/lib/pages/code-details/index.tsx +++ b/src/lib/pages/code-details/index.tsx @@ -50,7 +50,7 @@ const CodeDetailsBody = observer( const tab = getFirstQueryParam(router.query.tab) as TabIndex; useEffect(() => { - if (router.isReady) track(AmpEvent.TO_CODE_DETAIL, { tab }); + if (router.isReady) track(AmpEvent.TO_CODE_DETAILS, { tab }); // Note: we don't want to track when tab changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [router.isReady, track]); diff --git a/src/lib/pages/contract-details/index.tsx b/src/lib/pages/contract-details/index.tsx index e415bae4a..cf5a7e1ab 100644 --- a/src/lib/pages/contract-details/index.tsx +++ b/src/lib/pages/contract-details/index.tsx @@ -253,7 +253,7 @@ const ContractDetails = observer(() => { useEffect(() => { if (router.isReady && validated.success) - track(AmpEvent.TO_CONTRACT_DETAIL, { tab: validated.data.tab }); + track(AmpEvent.TO_CONTRACT_DETAILS, { tab: validated.data.tab }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [router.isReady]); diff --git a/src/lib/pages/module-details/components/ModuleTop.tsx b/src/lib/pages/module-details/components/ModuleTop.tsx index 945d4ee69..305fafb24 100644 --- a/src/lib/pages/module-details/components/ModuleTop.tsx +++ b/src/lib/pages/module-details/components/ModuleTop.tsx @@ -202,7 +202,9 @@ export const ModuleTop = ({ moduleData, isVerified }: ModuleTopProps) => { leftIcon={} size={{ base: "sm", md: "md" }} onClick={() => { - track(AmpEvent.USE_MODULE_DETAIL_MAIN_CTA, { label: "view" }); + track(AmpEvent.USE_MODULE_DETAILS_MAIN_CTA, { + label: "view", + }); navigate({ pathname: "/interact", query: { @@ -221,7 +223,7 @@ export const ModuleTop = ({ moduleData, isVerified }: ModuleTopProps) => { leftIcon={} size={{ base: "sm", md: "md" }} onClick={() => { - track(AmpEvent.USE_MODULE_DETAIL_MAIN_CTA, { + track(AmpEvent.USE_MODULE_DETAILS_MAIN_CTA, { label: "execute", }); navigate({ @@ -248,7 +250,7 @@ export const ModuleTop = ({ moduleData, isVerified }: ModuleTopProps) => { leftIcon={} size={{ base: "sm", md: "md" }} onClick={() => { - track(AmpEvent.USE_MODULE_DETAIL_MAIN_CTA, { + track(AmpEvent.USE_MODULE_DETAILS_MAIN_CTA, { label: "republish", }); navigate({ diff --git a/src/lib/pages/module-details/index.tsx b/src/lib/pages/module-details/index.tsx index 97aebbd8c..6907f8bbe 100644 --- a/src/lib/pages/module-details/index.tsx +++ b/src/lib/pages/module-details/index.tsx @@ -127,7 +127,7 @@ export const ModuleDetailsBody = ({ moduleData }: ModuleDetailsBodyProps) => { useEffect(() => { if (router.isReady && tab && !verificationLoading) - track(AmpEvent.TO_MODULE_DETAIL, { + track(AmpEvent.TO_MODULE_DETAILS, { tab, isVerified: Boolean(verificationData), }); diff --git a/src/lib/pages/nft-collection-details/components/CollectionInfoSection.tsx b/src/lib/pages/nft-collection-details/components/CollectionInfoSection.tsx index d49229a62..184775b3a 100644 --- a/src/lib/pages/nft-collection-details/components/CollectionInfoSection.tsx +++ b/src/lib/pages/nft-collection-details/components/CollectionInfoSection.tsx @@ -1,7 +1,6 @@ import { Flex, Heading, Link, Text } from "@chakra-ui/react"; -import { useInternalNavigate, useMobile } from "lib/app-provider"; -import { CopyLink } from "lib/components/CopyLink"; +import { useMobile } from "lib/app-provider"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { LabelText } from "lib/components/LabelText"; import { Loading } from "lib/components/Loading"; @@ -19,6 +18,8 @@ interface CollectionInfoSectionProps { activities: number; mutateEventes: number; royalty: number; + onClickActivities: () => void; + onClickMutateEvents: () => void; } export const CollectionInfoSection = ({ @@ -29,10 +30,10 @@ export const CollectionInfoSection = ({ activities, mutateEventes, royalty, + onClickActivities, + onClickMutateEvents, }: CollectionInfoSectionProps) => { const isMobile = useMobile(); - const navigate = useInternalNavigate(); - const { data: collectionCreator, isLoading } = useCollectionCreator(collectionAddress); @@ -65,6 +66,7 @@ export const CollectionInfoSection = ({ type="block_height" showCopyOnHover fixedHeight + ampCopierSection="collection-creation-information" /> @@ -73,10 +75,16 @@ export const CollectionInfoSection = ({ type="user_address" showCopyOnHover fixedHeight + ampCopierSection="collection-creation-information" /> - + Collection - @@ -144,24 +155,14 @@ export const CollectionInfoSection = ({ title="Activities" icon="list" content={activities} - onClick={() => - navigate({ - pathname: `/nft-collections/[collectionAddress]/activities`, - query: { collectionAddress }, - }) - } + onClick={onClickActivities} isDisabled={!activities} /> - navigate({ - pathname: `/nft-collections/[collectionAddress]/mutate_events`, - query: { collectionAddress }, - }) - } + onClick={onClickMutateEvents} isDisabled={!mutateEventes} /> diff --git a/src/lib/pages/nft-collection-details/components/CollectionSupplies.tsx b/src/lib/pages/nft-collection-details/components/CollectionSupplies.tsx index d9776a500..6a11268de 100644 --- a/src/lib/pages/nft-collection-details/components/CollectionSupplies.tsx +++ b/src/lib/pages/nft-collection-details/components/CollectionSupplies.tsx @@ -49,6 +49,7 @@ export const CollectionSupplies = ({ autoFocus onChange={(e) => setSearchKeyword(e.target.value)} size={{ base: "md", md: "lg" }} + amptrackSection="collection-supplies-tokenId-search" /> ; isLoading: boolean; + onViewMore: () => void; } export const CollectionSuppliesOverview = ({ - collectionAddress, totalCount, nfts, isLoading, + onViewMore, }: CollectionSuppliesOverviewProps) => { - const navigate = useInternalNavigate(); - const displayedNftCount = useBreakpointValue({ - "2xl": 6, - xl: 5, - sm: 4, - }); + const displayedNftCount = + useBreakpointValue({ + "2xl": 6, + xl: 5, + sm: 4, + }) ?? 4; const nftsInfo = nfts?.slice(0, displayedNftCount); @@ -59,16 +57,7 @@ export const CollectionSuppliesOverview = ({ ))} - {(displayedNftCount ?? 0) <= totalCount && ( - - navigate({ - pathname: "/nft-collections/[collectionAddress]/[tab]", - query: { collectionAddress, tab: TabIndex.Supplies }, - }) - } - /> - )} + {totalCount > displayedNftCount && } ); }; diff --git a/src/lib/pages/nft-collection-details/components/InfoCard.tsx b/src/lib/pages/nft-collection-details/components/InfoCard.tsx index f8073969e..694b777b2 100644 --- a/src/lib/pages/nft-collection-details/components/InfoCard.tsx +++ b/src/lib/pages/nft-collection-details/components/InfoCard.tsx @@ -1,5 +1,6 @@ import { Flex, Heading, Text } from "@chakra-ui/react"; +import { AmpEvent, track } from "lib/amplitude"; import type { IconKeys } from "lib/components/icon"; import { CustomIcon } from "lib/components/icon"; @@ -34,7 +35,10 @@ export const InfoCard = ({ bg: "gray.800", _hover: { bg: "gray.700" }, cursor: "pointer", - onClick, + onClick: () => { + track(AmpEvent.USE_NFT_COLLECTION_INFO_CARD, { label: title }); + onClick(); + }, })} > diff --git a/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTable.tsx b/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTable.tsx index 184df3f1a..2ff3a5758 100644 --- a/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTable.tsx +++ b/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTable.tsx @@ -26,7 +26,7 @@ export const ActivitiesTable = ({ if (isLoading) return ; if (!activities || !activities.length) return emptyState; - const templateColumns = `190px 200px minmax(360px, 1fr) 280px`; + const templateColumns = `190px minmax(360px, 1fr) minmax(360px, 1fr) 280px`; return isMobile ? ( diff --git a/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTableMobileCard.tsx b/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTableMobileCard.tsx index 5e0895166..833108e0b 100644 --- a/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTableMobileCard.tsx +++ b/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTableMobileCard.tsx @@ -38,9 +38,12 @@ export const ActivitiesTableMobileCard = ({ }) } topContent={ - - - + } middleContent={ diff --git a/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTableRow.tsx b/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTableRow.tsx index 9dc90f507..73eb440f8 100644 --- a/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTableRow.tsx +++ b/src/lib/pages/nft-collection-details/components/tables/activities/ActivitiesTableRow.tsx @@ -55,6 +55,7 @@ export const ActivitiesTableRow = ({ value={txhash.toUpperCase()} type="tx_hash" showCopyOnHover + ampCopierSection="nft-collection-activities-table" /> @@ -63,8 +64,11 @@ export const ActivitiesTableRow = ({ href={`/nft-collections/${collectionAddress}/nft/${nftAddress}`} > {tokenId} diff --git a/src/lib/pages/nft-collection-details/components/tables/activities/index.tsx b/src/lib/pages/nft-collection-details/components/tables/activities/index.tsx index abf48bdca..c2c81cf3f 100644 --- a/src/lib/pages/nft-collection-details/components/tables/activities/index.tsx +++ b/src/lib/pages/nft-collection-details/components/tables/activities/index.tsx @@ -1,4 +1,4 @@ -import { Stack, Text } from "@chakra-ui/react"; +import { Heading, Stack } from "@chakra-ui/react"; import { useState } from "react"; import InputWithIcon from "lib/components/InputWithIcon"; @@ -44,9 +44,9 @@ export const Activities = ({ return ( - + Activities in this collection - + resource.group === "collection") - .map((resource) => resource.items) - .flat(); - - const collectionSupplies = resources.filter( - (resource) => !resource.structTag?.includes("0x1::collection::Collection") - ); - - const royalty = groupedByName.find((group) => group.group === "royalty"); - - const collectionRoyalty = - royalty && JSON.parse(royalty.items[0].moveResource); - - const parsed = collectionSupplies - .map((resource) => { - try { - return JSON.parse(resource.moveResource); - } catch { - return {}; - } - }) - .map((data) => data?.data); + const collectionSupplyResource = resourcesData.groupedByName + .find((group) => group.group === "collection") + ?.items.find( + (resource) => + resource.structTag === "0x1::collection::FixedSupply" || + resource.structTag === "0x1::collection::UnlimitedSupply" + ); + const isUnlimited = + collectionSupplyResource?.structTag === "0x1::collection::UnlimitedSupply"; + const supplyData: Option = collectionSupplyResource + ? JSON.parse(collectionSupplyResource.moveResource).data + : undefined; - const [type] = collectionSupplies; - const isUnlimited = type?.structTag === "0x1::collection::UnlimitedSupply"; + const collectionRoyaltyResource = resourcesData.groupedByName + .find((group) => group.group === "royalty") + ?.items.find((resource) => resource.structTag === "0x1::royalty::Royalty"); + const royaltyData: Option = collectionRoyaltyResource + ? JSON.parse(collectionRoyaltyResource.moveResource).data + : undefined; - const [supplyData] = parsed; - const supplies = snakeToCamel(supplyData); return { collectionInfos: { isUnlimited, - supplies, - royalty: collectionRoyalty?.data?.royalty ?? 0, + supplies: { + currentSupply: Number(supplyData?.current_supply ?? 0), + totalMinted: Number(supplyData?.total_minted ?? 0), + maxSupply: supplyData?.max_supply + ? Number(supplyData.max_supply) + : undefined, + }, + royalty: Number(royaltyData?.royalty ?? 0), }, isLoading: isFetching, }; diff --git a/src/lib/pages/nft-collection-details/index.tsx b/src/lib/pages/nft-collection-details/index.tsx index 6c474db9f..9e278eab4 100644 --- a/src/lib/pages/nft-collection-details/index.tsx +++ b/src/lib/pages/nft-collection-details/index.tsx @@ -1,6 +1,5 @@ import { Badge, - Box, Button, Flex, Heading, @@ -107,7 +106,7 @@ const CollectionDetailsBody = ({ name.length > 20 ? `${name.slice(0, 20)}...` : name; return ( - + <> @@ -166,6 +166,11 @@ const CollectionDetailsBody = ({ w={{ base: "full", md: "auto" }} size={{ base: "sm", md: "md" }} mb={{ base: 4, md: 0 }} + onClick={() => + track(AmpEvent.USE_NFT_VIEW_RESOURCE_CTA, { + amptrackSection: "nft-collection-details", + }) + } > View Resource @@ -219,8 +224,8 @@ const CollectionDetailsBody = ({ @@ -253,7 +260,7 @@ const CollectionDetailsBody = ({ - + ); }; @@ -263,7 +270,7 @@ const CollectionDetails = () => { useEffect(() => { if (router.isReady && validated.success) - track(AmpEvent.TO_NFT_COLLECTION_DETAIL, { tab: validated.data.tab }); + track(AmpEvent.TO_NFT_COLLECTION_DETAILS, { tab: validated.data.tab }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [router.isReady]); diff --git a/src/lib/pages/nft-collections/components/Collections.tsx b/src/lib/pages/nft-collections/components/Collections.tsx index a5e7a629f..0485743b6 100644 --- a/src/lib/pages/nft-collections/components/Collections.tsx +++ b/src/lib/pages/nft-collections/components/Collections.tsx @@ -1,6 +1,8 @@ -import { Box, Stack } from "@chakra-ui/react"; -import { useState } from "react"; +import { Stack } from "@chakra-ui/react"; +import router from "next/router"; +import { useEffect, useState } from "react"; +import { AmpEvent, track } from "lib/amplitude"; import InputWithIcon from "lib/components/InputWithIcon"; import { Pagination } from "lib/components/pagination"; import { usePaginator } from "lib/components/pagination/usePaginator"; @@ -35,8 +37,13 @@ export const Collections = () => { } ); + useEffect(() => { + if (router.isReady) track(AmpEvent.TO_NFT_COLLECTIONS_LIST); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [router.isReady]); + return ( - + <> { autoFocus onChange={(e) => setSearchKeyword(e.target.value)} size={{ base: "md", md: "lg" }} + amptrackSection="nft-collections-list-search" /> { }} /> )} - + ); }; diff --git a/src/lib/pages/nft-details/components/MintInfo.tsx b/src/lib/pages/nft-details/components/MintInfo.tsx index 588f61b11..7dd359002 100644 --- a/src/lib/pages/nft-details/components/MintInfo.tsx +++ b/src/lib/pages/nft-details/components/MintInfo.tsx @@ -43,6 +43,7 @@ export const MintInfo = ({ holderAddress, nftAddress }: MintInfoProps) => { value={String(mintInfo.height)} type="block_height" showCopyOnHover + ampCopierSection="nft-detail-mint-information" /> @@ -50,13 +51,15 @@ export const MintInfo = ({ holderAddress, nftAddress }: MintInfoProps) => { value={holderAddress} type="user_address" showCopyOnHover + ampCopierSection="nft-detail-mint-information" /> diff --git a/src/lib/pages/nft-details/components/ViewResourceButton.tsx b/src/lib/pages/nft-details/components/ViewResourceButton.tsx index c96c2147b..ecbab95c7 100644 --- a/src/lib/pages/nft-details/components/ViewResourceButton.tsx +++ b/src/lib/pages/nft-details/components/ViewResourceButton.tsx @@ -1,5 +1,6 @@ import { Button } from "@chakra-ui/react"; +import { AmpEvent, track } from "lib/amplitude"; import { useInternalNavigate } from "lib/app-provider"; import type { HexAddr32 } from "lib/types"; @@ -16,15 +17,18 @@ export const ViewResourceButton = ({ nftAddress }: ViewResourceButtonProps) => { w={{ base: "full", md: "auto" }} size={{ base: "sm", md: "md" }} mb={{ base: 4, md: 0 }} - onClick={() => + onClick={() => { + track(AmpEvent.USE_NFT_VIEW_RESOURCE_CTA, { + amptrackSection: "nft-details", + }); navigate({ pathname: "/accounts/[accountAddress]/[tab]", query: { accountAddress: nftAddress, tab: "resources", }, - }) - } + }); + }} > View Resource diff --git a/src/lib/pages/nft-details/components/attributes/index.tsx b/src/lib/pages/nft-details/components/attributes/index.tsx index f3592ac75..1dd989338 100644 --- a/src/lib/pages/nft-details/components/attributes/index.tsx +++ b/src/lib/pages/nft-details/components/attributes/index.tsx @@ -37,9 +37,7 @@ export const Attributes = ({ Attributes - - {attributes.length} - + {attributes.length} @@ -166,6 +167,7 @@ const NftDetailsBody = ({ type="user_address" textFormat="normal" maxWidth="full" + ampCopierSection="holder-address-nft-detail-top" /> @@ -202,7 +204,15 @@ const NftDetailsBody = ({ - + + track(AmpEvent.USE_SUBTAB, { + currentTab: !tab ? "transactions" : "mutate-events", + }) + } + > { const validated = zNftDetailQueryParams.safeParse(router.query); useEffect(() => { - if (router.isReady && validated.success) track(AmpEvent.TO_NFT_DETAIL); + if (router.isReady && validated.success) track(AmpEvent.TO_NFT_DETAILS); // eslint-disable-next-line react-hooks/exhaustive-deps }, [router.isReady]); diff --git a/src/lib/pages/pools/poolId.tsx b/src/lib/pages/pools/poolId.tsx index 10a014446..15c18df65 100644 --- a/src/lib/pages/pools/poolId.tsx +++ b/src/lib/pages/pools/poolId.tsx @@ -21,7 +21,7 @@ export const PoolId = () => { const { pool, isLoading } = usePool(poolId); useEffect(() => { - if (router.isReady) track(AmpEvent.TO_POOL_DETAIL); + if (router.isReady) track(AmpEvent.TO_POOL_DETAILS); }, [router.isReady]); if (isLoading) return ; diff --git a/src/lib/pages/public-project/slug.tsx b/src/lib/pages/public-project/slug.tsx index a651bdab7..d1482c591 100644 --- a/src/lib/pages/public-project/slug.tsx +++ b/src/lib/pages/public-project/slug.tsx @@ -66,7 +66,7 @@ const ProjectDetail = () => { useEffect(() => { if (router.isReady) { - if (tab) track(AmpEvent.TO_PROJECT_DETAIL, { tab }); + if (tab) track(AmpEvent.TO_PROJECT_DETAILS, { tab }); if (!tab || !Object.values(TabIndex).includes(tab)) { navigate({ diff --git a/src/lib/pages/tx-details/components/MessageSection.tsx b/src/lib/pages/tx-details/components/MessageSection.tsx index 375f07822..442ff3709 100644 --- a/src/lib/pages/tx-details/components/MessageSection.tsx +++ b/src/lib/pages/tx-details/components/MessageSection.tsx @@ -8,6 +8,7 @@ import { import { CustomIcon } from "lib/components/icon"; import type { TxData } from "lib/services/txService"; +import { extractTxLogs } from "lib/utils"; import { TxMessage } from "./tx-message"; @@ -17,6 +18,7 @@ interface MessageSectionProps { export const MessageSection = ({ txData }: MessageSectionProps) => { const msgs = txData.tx.body.messages; + const msgLogs = extractTxLogs(txData); return ( {txData.isTxFailed && ( @@ -39,17 +41,14 @@ export const MessageSection = ({ txData }: MessageSectionProps) => { {msgs.length} - {msgs.map((msg, idx) => { - const msgLog = txData.logs.find((log) => log.msg_index === idx); - return ( - - ); - })} + {msgs.map((msg, idx) => ( + + ))} ); }; diff --git a/src/lib/pages/tx-details/components/tx-message/TxMsgExpand.tsx b/src/lib/pages/tx-details/components/tx-message/TxMsgExpand.tsx index 394a29701..4f9566138 100644 --- a/src/lib/pages/tx-details/components/tx-message/TxMsgExpand.tsx +++ b/src/lib/pages/tx-details/components/tx-message/TxMsgExpand.tsx @@ -41,12 +41,12 @@ export const TxMsgExpand = ({ const { "@type": type, ...body } = msgBody; const isIBC = - Boolean(log?.events?.find((event) => event.type === "send_packet")) || + Boolean(log.events.find((event) => event.type === "send_packet")) || type.startsWith("/ibc"); - let msgIcon: IconKeys = "info-circle"; - let content: ReactNode; const isOpinit = Boolean(type.startsWith("/opinit")); + let msgIcon: IconKeys = "info-circle"; + let content: ReactNode; switch (type) { case "/cosmwasm.wasm.v1.MsgStoreCode": msgIcon = "upload"; diff --git a/src/lib/pages/tx-details/components/tx-message/index.tsx b/src/lib/pages/tx-details/components/tx-message/index.tsx index 448c2cab5..7403265d2 100644 --- a/src/lib/pages/tx-details/components/tx-message/index.tsx +++ b/src/lib/pages/tx-details/components/tx-message/index.tsx @@ -3,14 +3,13 @@ import type { logs } from "@cosmjs/stargate"; import { useState } from "react"; import type { MsgBody } from "lib/services/tx"; -import type { Option } from "lib/types"; import { TxMsgDetails } from "./TxMsgDetails"; import { TxMsgExpand } from "./TxMsgExpand"; export interface TxMsgData { msgBody: MsgBody; - log: Option; + log: logs.Log; isSingleMsg?: boolean; } diff --git a/src/lib/pages/tx-details/index.tsx b/src/lib/pages/tx-details/index.tsx index cd49baa3e..4a9f0b72f 100644 --- a/src/lib/pages/tx-details/index.tsx +++ b/src/lib/pages/tx-details/index.tsx @@ -31,7 +31,7 @@ const TxDetails = () => { false: "success", undefined: "not_found", }; - track(AmpEvent.TO_TRANSACTION_DETAIL, { + track(AmpEvent.TO_TRANSACTION_DETAILS, { tx_status: mapTxFailed[String(txData?.isTxFailed) as keyof typeof mapTxFailed], }); diff --git a/src/lib/utils/tx/__test__/extractTxLogs.example.ts b/src/lib/utils/tx/__test__/extractTxLogs.example.ts new file mode 100644 index 000000000..931c8ff9f --- /dev/null +++ b/src/lib/utils/tx/__test__/extractTxLogs.example.ts @@ -0,0 +1,702 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +import type { logs } from "@cosmjs/stargate"; + +import type { TypeUrl } from "lib/data"; +import type { TxData } from "lib/services/txService"; +import { parseDate } from "lib/utils/date"; + +type TestCase = { txData: TxData; result: logs.Log[] }; + +export const fromLogs: TestCase = { + txData: { + chainId: "osmo-test-5", + isTxFailed: false, + code: 0, + codespace: "", + data: "0A260A242F636F736D7761736D2E7761736D2E76312E4D73674D696772617465436F6E7472616374", + events: [ + { + attributes: [ + { + key: "c3BlbmRlcg==", + value: + "b3NtbzE4cmYydmtldHVoZnZydzBuOTg2bWdobXMzM2FobTg4NHdzcmZzag==", + }, + { + key: "YW1vdW50", + value: "NDg4MHVvc21v", + }, + ], + type: "coin_spent", + }, + { + attributes: [ + { + key: "cmVjZWl2ZXI=", + value: + "b3NtbzE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bGN6c3NhMA==", + }, + { + key: "YW1vdW50", + value: "NDg4MHVvc21v", + }, + ], + type: "coin_received", + }, + { + attributes: [ + { + key: "cmVjaXBpZW50", + value: + "b3NtbzE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bGN6c3NhMA==", + }, + { + key: "c2VuZGVy", + value: + "b3NtbzE4cmYydmtldHVoZnZydzBuOTg2bWdobXMzM2FobTg4NHdzcmZzag==", + }, + { + key: "YW1vdW50", + value: "NDg4MHVvc21v", + }, + ], + type: "transfer", + }, + { + attributes: [ + { + key: "c2VuZGVy", + value: + "b3NtbzE4cmYydmtldHVoZnZydzBuOTg2bWdobXMzM2FobTg4NHdzcmZzag==", + }, + ], + type: "message", + }, + { + attributes: [ + { + key: "ZmVl", + value: "NDg4MHVvc21v", + }, + ], + type: "tx", + }, + { + attributes: [ + { + key: "YWNjX3NlcQ==", + value: + "b3NtbzE4cmYydmtldHVoZnZydzBuOTg2bWdobXMzM2FobTg4NHdzcmZzai8w", + }, + ], + type: "tx", + }, + { + attributes: [ + { + key: "c2lnbmF0dXJl", + value: + "RE0rSU42a3hsUlhDNjg4RTM3cFNrcFc4NkxNMHQ4TGxXRGJmK2o2c09QNTJTM2ZOdHBVTEp3Vjd3MUcrQ0tpZXQwcE9jWnAyR1ZJbVVmdnBFc3pDdHc9PQ==", + }, + ], + type: "tx", + }, + { + attributes: [ + { + key: "YWN0aW9u", + value: "L2Nvc213YXNtLndhc20udjEuTXNnTWlncmF0ZUNvbnRyYWN0", + }, + ], + type: "message", + }, + { + attributes: [ + { + key: "bW9kdWxl", + value: "d2FzbQ==", + }, + { + key: "c2VuZGVy", + value: + "b3NtbzE4cmYydmtldHVoZnZydzBuOTg2bWdobXMzM2FobTg4NHdzcmZzag==", + }, + ], + type: "message", + }, + { + attributes: [ + { + key: "Y29kZV9pZA==", + value: "MTc=", + }, + { + key: "X2NvbnRyYWN0X2FkZHJlc3M=", + value: + "b3NtbzFjdnR6d3NqOGxhbTlhdDh2Zmd4Zm52ZXl6am5lOGVlY3FqbnNoNmszanZ0M2w3dmU2em1zM3d0cGQw", + }, + ], + type: "migrate", + }, + { + attributes: [ + { + key: "c3BlbmRlcg==", + value: + "b3NtbzE4cmYydmtldHVoZnZydzBuOTg2bWdobXMzM2FobTg4NHdzcmZzag==", + }, + { + key: "YW1vdW50", + value: "NDg4MHVvc21v", + }, + ], + type: "coin_spent", + }, + { + attributes: [ + { + key: "cmVjZWl2ZXI=", + value: + "b3NtbzE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bGN6c3NhMA==", + }, + { + key: "YW1vdW50", + value: "NDg4MHVvc21v", + }, + ], + type: "coin_received", + }, + { + attributes: [ + { + key: "cmVjaXBpZW50", + value: + "b3NtbzE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bGN6c3NhMA==", + }, + { + key: "c2VuZGVy", + value: + "b3NtbzE4cmYydmtldHVoZnZydzBuOTg2bWdobXMzM2FobTg4NHdzcmZzag==", + }, + { + key: "YW1vdW50", + value: "NDg4MHVvc21v", + }, + ], + type: "transfer", + }, + { + attributes: [ + { + key: "c2VuZGVy", + value: + "b3NtbzE4cmYydmtldHVoZnZydzBuOTg2bWdobXMzM2FobTg4NHdzcmZzag==", + }, + ], + type: "message", + }, + { + attributes: [ + { + key: "ZmVl", + value: "NDg4MHVvc21v", + }, + ], + type: "tx", + }, + { + attributes: [ + { + key: "YWNjX3NlcQ==", + value: + "b3NtbzE4cmYydmtldHVoZnZydzBuOTg2bWdobXMzM2FobTg4NHdzcmZzai8w", + }, + ], + type: "tx", + }, + { + attributes: [ + { + key: "c2lnbmF0dXJl", + value: + "RE0rSU42a3hsUlhDNjg4RTM3cFNrcFc4NkxNMHQ4TGxXRGJmK2o2c09QNTJTM2ZOdHBVTEp3Vjd3MUcrQ0tpZXQwcE9jWnAyR1ZJbVVmdnBFc3pDdHc9PQ==", + }, + ], + type: "tx", + }, + ], + gas_used: "150668", + gas_wanted: "195164", + height: "1417710", + info: "", + logs: [ + { + events: [ + { + attributes: [ + { + key: "action", + value: "/cosmwasm.wasm.v1.MsgMigrateContract", + }, + { + key: "module", + value: "wasm", + }, + { + key: "sender", + value: "osmo18rf2vketuhfvrw0n986mghms33ahm884wsrfsj", + }, + ], + type: "message", + }, + { + attributes: [ + { + key: "code_id", + value: "17", + }, + { + key: "_contract_address", + value: + "osmo1cvtzwsj8lam9at8vfgxfnveyzjne8eecqjnsh6k3jvt3l7ve6zms3wtpd0", + }, + ], + type: "migrate", + }, + ], + log: "", + msg_index: 0, + }, + ], + raw_log: + '[{"events":[{"type":"message","attributes":[{"key":"action","value":"/cosmwasm.wasm.v1.MsgMigrateContract"},{"key":"module","value":"wasm"},{"key":"sender","value":"osmo18rf2vketuhfvrw0n986mghms33ahm884wsrfsj"}]},{"type":"migrate","attributes":[{"key":"code_id","value":"17"},{"key":"_contract_address","value":"osmo1cvtzwsj8lam9at8vfgxfnveyzjne8eecqjnsh6k3jvt3l7ve6zms3wtpd0"}]}]}]', + timestamp: parseDate("2023-06-29T06:09:47Z"), + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [ + { + amount: "4880", + denom: "uosmo", + }, + ], + gas_limit: "195164", + granter: "", + payer: "", + }, + signer_infos: [ + { + mode_info: { + single: { + mode: "SIGN_MODE_DIRECT", + }, + }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "AjpLfngY/rc6Nk4GWQx6HcxHah8KM77D9q01vk83wnF2", + }, + sequence: "0", + }, + ], + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/cosmwasm.wasm.v1.MsgMigrateContract", + code_id: "17", + contract: + "osmo1cvtzwsj8lam9at8vfgxfnveyzjne8eecqjnsh6k3jvt3l7ve6zms3wtpd0", + msg: {}, + sender: "osmo18rf2vketuhfvrw0n986mghms33ahm884wsrfsj", + }, + ], + non_critical_extension_options: [], + timeout_height: "0", + }, + signatures: [ + "DM+IN6kxlRXC688E37pSkpW86LM0t8LlWDbf+j6sOP52S3fNtpULJwV7w1G+CKiet0pOcZp2GVImUfvpEszCtw==", + ], + }, + txhash: "1B1269C4B5704E9872B26ACE447B043099C98E9B7EA20373517CF59038321A43", + }, + result: [ + { + events: [ + { + attributes: [ + { + key: "action", + value: "/cosmwasm.wasm.v1.MsgMigrateContract", + }, + { + key: "module", + value: "wasm", + }, + { + key: "sender", + value: "osmo18rf2vketuhfvrw0n986mghms33ahm884wsrfsj", + }, + ], + type: "message", + }, + { + attributes: [ + { + key: "code_id", + value: "17", + }, + { + key: "_contract_address", + value: + "osmo1cvtzwsj8lam9at8vfgxfnveyzjne8eecqjnsh6k3jvt3l7ve6zms3wtpd0", + }, + ], + type: "migrate", + }, + ], + log: "", + msg_index: 0, + }, + ], +}; + +export const fromEvents: TestCase = { + txData: { + chainId: "stone-13", + isTxFailed: false, + code: 0, + codespace: "", + data: "12240A222F696E697469612E6D6F76652E76312E4D73675075626C697368526573706F6E7365", + events: [ + { + attributes: [ + { + key: "sender", + value: "0x38d2a65b2be5d2c1b9f329f5b45f708c7b7d9cf5", + }, + { + key: "module_addr", + value: "0x1", + }, + { + key: "module_name", + value: "coin", + }, + { + key: "function_name", + value: "transfer", + }, + ], + type: "execute", + }, + { + attributes: [ + { + key: "type_tag", + value: "0x1::fungible_asset::WithdrawEvent", + }, + { + key: "data", + value: + '{"store_addr":"0x48b7bc1a3e58ffa9f964ebc3f4217556dcdaa6749d1df92abd9146bb5405dece","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"114877"}', + }, + ], + type: "move", + }, + { + attributes: [ + { + key: "type_tag", + value: "0x1::fungible_asset::DepositEvent", + }, + { + key: "data", + value: + '{"store_addr":"0x66a8cb0bfb991610dcffb8a6543ac0887c7c5405b8f985ebed6d628fe50c4686","metadata_addr":"0x8e4733bdabcf7d4afc3d14f0dd46c9bf52fb0fce9e4b996c939e195b8bc891d9","amount":"114877"}', + }, + ], + type: "move", + }, + { + attributes: [ + { + key: "spender", + value: "init18rf2vketuhfvrw0n986mghms33ahm884gas2dz", + }, + { + key: "amount", + value: "114877uinit", + }, + ], + type: "coin_spent", + }, + { + attributes: [ + { + key: "receiver", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql", + }, + { + key: "amount", + value: "114877uinit", + }, + ], + type: "coin_received", + }, + { + attributes: [ + { + key: "recipient", + value: "init17xpfvakm2amg962yls6f84z3kell8c5l70rnql", + }, + { + key: "sender", + value: "init18rf2vketuhfvrw0n986mghms33ahm884gas2dz", + }, + { + key: "amount", + value: "114877uinit", + }, + ], + type: "transfer", + }, + { + attributes: [ + { + key: "sender", + value: "init18rf2vketuhfvrw0n986mghms33ahm884gas2dz", + }, + ], + type: "message", + }, + { + attributes: [ + { + key: "fee", + value: "114877uinit", + }, + { + key: "fee_payer", + value: "init18rf2vketuhfvrw0n986mghms33ahm884gas2dz", + }, + ], + type: "tx", + }, + { + attributes: [ + { + key: "acc_seq", + value: "init18rf2vketuhfvrw0n986mghms33ahm884gas2dz/0", + }, + ], + type: "tx", + }, + { + attributes: [ + { + key: "signature", + value: + "THA48YPvYrty2OrQakrTpQWanP/BG9rCjDGgdAJ0TpB4xBezWiX5VTFl7EiCx6pF7T0nAf/bznL4l3LcKLGa5g==", + }, + ], + type: "tx", + }, + { + attributes: [ + { + key: "action", + value: "/initia.move.v1.MsgPublish", + }, + { + key: "sender", + value: "init18rf2vketuhfvrw0n986mghms33ahm884gas2dz", + }, + { + key: "module", + value: "move", + }, + { + key: "msg_index", + value: "0", + }, + ], + type: "message", + }, + { + attributes: [ + { + key: "sender", + value: "0x38d2a65b2be5d2c1b9f329f5b45f708c7b7d9cf5", + }, + { + key: "module_addr", + value: "0x1", + }, + { + key: "module_name", + value: "code", + }, + { + key: "function_name", + value: "publish", + }, + { + key: "msg_index", + value: "0", + }, + ], + type: "execute", + }, + { + attributes: [ + { + key: "type_tag", + value: "0x1::code::ModulePublishedEvent", + }, + { + key: "data", + value: + '{"module_id":"0x38d2a65b2be5d2c1b9f329f5b45f708c7b7d9cf5::pool_infos","upgrade_policy":0}', + }, + { + key: "msg_index", + value: "0", + }, + ], + type: "move", + }, + ], + gas_used: "374198", + gas_wanted: "760770", + height: "8030", + info: "", + logs: [], + raw_log: "", + timestamp: parseDate("2024-01-16T16:51:20Z"), + tx: { + "@type": "/cosmos.tx.v1beta1.Tx", + auth_info: { + fee: { + amount: [ + { + amount: "114877", + denom: "uinit", + }, + ], + gas_limit: "760770", + granter: "", + payer: "", + }, + signer_infos: [ + { + mode_info: { + single: { + mode: "SIGN_MODE_DIRECT", + }, + }, + public_key: { + "@type": "/cosmos.crypto.secp256k1.PubKey", + key: "AjpLfngY/rc6Nk4GWQx6HcxHah8KM77D9q01vk83wnF2", + }, + sequence: "0", + }, + ], + }, + body: { + extension_options: [], + memo: "", + messages: [ + { + "@type": "/initia.move.v1.MsgPublish" as TypeUrl, + code_bytes: [ + "oRzrCwYAAAALAQASAhI0A0Z3BL0BCgXHAbQBB/sC2AUI0whABpMJVBDnCS4KlQoiDLcKkQMAAAEBAQIBAwEEAQUBBgEHAQgACQAAAAoAAAgLBwAGDQcBAAEFDggAAxoHAAQmAgAEJwgABCgHAAQpAgAHKgcBAAAELQcAAAwAAQAADwIBAAAQAwQAABEFBgAAEgcHAAEfCQoBAAggCgEACCELAwACIgIBAAYjDgUBCAgkDxAABiURBQAHKxQVAQAELBYXAAQuGRoABi8FDgEIBDAcHQAEMR4fAAQyHCAABDMhIgAENCEiAAQ1ISMAAhUCBwAFBQkNDAUPGw8NAQYFAQgCAQsDAQgEAAEKCAEBBQEIAAECCAIKAgMCAwgCAgoCAQYJAAEKAgIHCAIIAgIFCAIBCAQBCwMBCQABBggCAQYKAgIFCgINBQgFBQgFCAYDAwsDAQgHBQoICAgJCggBBgoICAEIAQEJAAELCgEJAAQLCgEFCwoBBQsKAQUCAQoICAEICAEGCAgFBQUFCAsIBQEIBwELAwEIBwEIBgEGCAYCCAUIBQEICQEGCAkBAwEECnBvb2xfaW5mb3MDYmNzBGNvaW4KZGVjaW1hbDEyOANkZXgOZnVuZ2libGVfYXNzZXQGb2JqZWN0Bm9wdGlvbgZzdHJpbmcJQXNzZXRJbmZvCFBvb2xJbmZvBlN0cmluZxFhZGRyZXNzX3RvX3N0cmluZwZPYmplY3QITWV0YWRhdGEWY29pbl9tZXRhZGF0YV90b19kZW5vbRJnZXRfYWxsX3BhaXJfaW5mb3MOZ2V0X2Fzc2V0X2luZm8PaGV4X251bV90b191dGY4CG1ldGFkYXRhBWRlbm9tCGRlY2ltYWxzBmNvaW5fYQZjb2luX2IPbGlxdWlkaXR5X3Rva2VuDWNvaW5fYV93ZWlnaHQKRGVjaW1hbDEyOA1jb2luX2Jfd2VpZ2h0DWNvaW5fYV9hbW91bnQNY29pbl9iX2Ftb3VudAt0b3RhbF9zaGFyZQh0b19ieXRlcwR1dGY4BmFwcGVuZAZzeW1ib2wOb2JqZWN0X2FkZHJlc3MFYnl0ZXMVY3JlYXRlX29iamVjdF9hZGRyZXNzFUN1cnJlbnRXZWlnaHRSZXNwb25zZQZDb25maWcMUGFpclJlc3BvbnNlEFBvb2xJbmZvUmVzcG9uc2UGT3B0aW9uBHNvbWUNZ2V0X2FsbF9wYWlycwdXZWlnaHRzFHVucGFja19wYWlyX3Jlc3BvbnNlEWFkZHJlc3NfdG9fb2JqZWN0EmdldF9jdXJyZW50X3dlaWdodB51bnBhY2tfY3VycmVudF93ZWlnaHRfcmVzcG9uc2UNZ2V0X3Bvb2xfaW5mbylnZXRfY29pbl9hX2Ftb3VudF9mcm9tX3Bvb2xfaW5mb19yZXNwb25zZSlnZXRfY29pbl9iX2Ftb3VudF9mcm9tX3Bvb2xfaW5mb19yZXNwb25zZSdnZXRfdG90YWxfc2hhcmVfZnJvbV9wb29sX2luZm9fcmVzcG9uc2UAAAAAAAAAAAAAAAA40qZbK+XSwbnzKfW0X3CMe32c9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgEeCgIBAAoCBgVtb3ZlLwUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE2luaXRpYTo6bWV0YWRhdGFfdjAZAAABEmdldF9hbGxfcGFpcl9pbmZvcwEBAAACAxMFFAgCFQIBAggWCAAXCAAYCAAZCAUbCAUcAx0DHgQAAAAACDILADgADAIHAQwIDgJBBwwFBgAAAAAAAAAADAMKAwoFIwQpBQ8OAgoDQgcUDAcKBzEQGgwBCwcxEBkMBA0ICwERBEQHDQgLBBEERAcLAwYBAAAAAAAAABYMAwUKBwIRBgwGDQYLCBEGEQcLBgIBAAAADBMKABEIDAILADgBDAEHAw4CEQoUEQsKASEEEAsCAg4BEQACAgAAABJVQBMAAAAAAAAAAAwLBwQMAAcEDAIHBAwICgA4AgoCOAIKCDgCBwARDQwJDgkMDAYAAAAAAAAAAAwFCgxBGAwGCgUKBiMESQUdCgwKBUIYEQ4BAQwIDAIMAAoIOAMMBwoHERAMBA4EEREMAwwBCwcREgwKDQsKABEDCgIRAwoIEQMLAQsDDgoREw4KERQOChEVEgFEEwsFBgEAAAAAAAAAFgwFBRgLDAEOCUEYBwA0IwRSBVMFCAsLAgMAAAACCgoAOAQMAQsACgERAQsBERYSAAIEAAAABw8KADEKIwQJMTALABYMAQUNMVcLABYMAQsBAgA=", + ], + sender: "init18rf2vketuhfvrw0n986mghms33ahm884gas2dz", + upgrade_policy: "ARBITRARY", + }, + ], + non_critical_extension_options: [], + timeout_height: "0", + }, + signatures: [ + "THA48YPvYrty2OrQakrTpQWanP/BG9rCjDGgdAJ0TpB4xBezWiX5VTFl7EiCx6pF7T0nAf/bznL4l3LcKLGa5g==", + ], + }, + txhash: "CA11A83C242A5BF7139CFB1CCF529EC46A87CB6188B71EAAC63A8B7123894132", + }, + result: [ + { + msg_index: 0, + log: "", + events: [ + { + attributes: [ + { + key: "action", + value: "/initia.move.v1.MsgPublish", + }, + { + key: "sender", + value: "init18rf2vketuhfvrw0n986mghms33ahm884gas2dz", + }, + { + key: "module", + value: "move", + }, + { + key: "msg_index", + value: "0", + }, + ], + type: "message", + }, + { + attributes: [ + { + key: "sender", + value: "0x38d2a65b2be5d2c1b9f329f5b45f708c7b7d9cf5", + }, + { + key: "module_addr", + value: "0x1", + }, + { + key: "module_name", + value: "code", + }, + { + key: "function_name", + value: "publish", + }, + { + key: "msg_index", + value: "0", + }, + ], + type: "execute", + }, + { + attributes: [ + { + key: "type_tag", + value: "0x1::code::ModulePublishedEvent", + }, + { + key: "data", + value: + '{"module_id":"0x38d2a65b2be5d2c1b9f329f5b45f708c7b7d9cf5::pool_infos","upgrade_policy":0}', + }, + { + key: "msg_index", + value: "0", + }, + ], + type: "move", + }, + ], + }, + ], +}; diff --git a/src/lib/utils/tx/__test__/extractTxLogs.test.ts b/src/lib/utils/tx/__test__/extractTxLogs.test.ts new file mode 100644 index 000000000..dde869309 --- /dev/null +++ b/src/lib/utils/tx/__test__/extractTxLogs.test.ts @@ -0,0 +1,12 @@ +import { extractTxLogs } from "../extractTxLogs"; + +import { fromLogs, fromEvents } from "./extractTxLogs.example"; + +describe("extractTxLogs", () => { + test("from logs", () => { + expect(extractTxLogs(fromLogs.txData)).toEqual(fromLogs.result); + }); + test("from events", () => { + expect(extractTxLogs(fromEvents.txData)).toEqual(fromEvents.result); + }); +}); diff --git a/src/lib/utils/tx/extractTxLogs.ts b/src/lib/utils/tx/extractTxLogs.ts new file mode 100644 index 000000000..2678813dd --- /dev/null +++ b/src/lib/utils/tx/extractTxLogs.ts @@ -0,0 +1,28 @@ +import type { Event, logs } from "@cosmjs/stargate"; + +import type { TxData } from "lib/services/txService"; + +export const extractTxLogs = (txData: TxData): logs.Log[] => { + const msgLogs = txData.tx.body.messages.map((_, idx) => ({ + msg_index: idx, + log: "", + events: [] as Event[], + })); + + if (txData.logs.length > 0) + txData.logs.forEach((log) => + msgLogs[log.msg_index].events.push(...log.events) + ); + else + txData.events.forEach((event) => { + const attribute = event.attributes.find( + (attr) => attr.key === "msg_index" + ); + if (attribute) { + const index = Number(attribute.value); + msgLogs[index].events.push(event); + } + }); + + return msgLogs; +}; diff --git a/src/lib/utils/tx/index.ts b/src/lib/utils/tx/index.ts index e0b26945f..4fc6d9e62 100644 --- a/src/lib/utils/tx/index.ts +++ b/src/lib/utils/tx/index.ts @@ -1,4 +1,5 @@ export * from "./composeMsg"; -export * from "./findAttr"; export * from "./extractTxDetails"; +export * from "./extractTxLogs"; +export * from "./findAttr"; export * from "./mapping";