Skip to content

Commit

Permalink
feat(core): Add hook with logic to check if an asset is 'Transferable…
Browse files Browse the repository at this point in the history
…'. (#4159)

* feat(tooling-core): Add hook with logic to check if an asset is 'Transferabe'. Remove old util

* feat(apps-core): Use useQuery in place of useEffect in useIsAssetTransferable

* feat(apps-core): Improve early return condition in useIsAssetTransferable

* feat(apps-core): Improve per PR comments
  • Loading branch information
msarcev authored Nov 29, 2024
1 parent d26dc3c commit d899a6c
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 22 deletions.
1 change: 1 addition & 0 deletions apps/core/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export * from './useGetKioskContents';
export * from './useZodForm';
export * from './useElementDimensions';
export * from './useIotaCoinData';
export * from './useIsAssetTransferable';
export * from './useLocalStorage';
export * from './useTokenPrice';
export * from './useKioskClient';
Expand Down
50 changes: 50 additions & 0 deletions apps/core/src/hooks/useIsAssetTransferable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { useIotaClient } from '@iota/dapp-kit';
import { IotaMoveNormalizedStruct, IotaObjectData } from '@iota/iota-sdk/client';
import { useQuery } from '@tanstack/react-query';

function getObjectTypeParams(obj: IotaObjectData | null | undefined) {
const objectType =
obj?.type ??
(obj?.content?.dataType === 'package' ? 'package' : obj?.content?.type) ??
null;

return objectType?.split('<')[0]?.split('::') || [];
}

export function useIsAssetTransferable(obj: IotaObjectData | null | undefined) {
const client = useIotaClient();
const [packageId, moduleName, functionName] = getObjectTypeParams(obj);

return useQuery({
// eslint-disable-next-line @tanstack/query/exhaustive-deps
queryKey: ['is-asset-transferable', packageId, moduleName, functionName],
queryFn: async () => {
if (!packageId || !moduleName || !functionName) {
return undefined;
}

return await client.getNormalizedMoveStruct({
package: packageId,
module: moduleName,
struct: functionName,
});
},
select: (moveNormalizedStruct: IotaMoveNormalizedStruct | undefined): boolean => {
if (!moveNormalizedStruct) {
return false;
}

const structAbilities = moveNormalizedStruct?.abilities?.abilities ?? null;

if (!structAbilities) {
return false;
}

return structAbilities.includes('Store');
},
enabled: !!packageId && !!moduleName && !!functionName,
});
}
1 change: 0 additions & 1 deletion apps/core/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './isAssetTransferable';
export * from './calculateStakeShare';
export * from './chunkArray';
export * from './formatAmount';
Expand Down
10 changes: 0 additions & 10 deletions apps/core/src/utils/isAssetTransferable.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import React, { useCallback } from 'react';
import { useParams } from 'next/navigation';
import { Button, RouteLink, SendAssetPopup, VisualAssetDetailsCard } from '@/components';
import { isAssetTransferable, useGetObject } from '@iota/core';
import { useIsAssetTransferable, useGetObject } from '@iota/core';
import { usePopups } from '@/hooks';
import { useCurrentAccount } from '@iota/dapp-kit';
import { ASSETS_ROUTE } from '@/lib/constants/routes.constants';
Expand All @@ -15,6 +15,7 @@ const VisualAssetDetailPage = () => {
const params = useParams();
const objectId = params.objectId as string;
const { data: asset } = useGetObject(objectId);
const { data: isAssetTransferable } = useIsAssetTransferable(asset?.data);
const activeAccount = useCurrentAccount();

const { openPopup, closePopup } = usePopups();
Expand All @@ -25,8 +26,6 @@ const VisualAssetDetailPage = () => {
}
}, [asset, openPopup, closePopup]);

const assetIsTransferable = asset?.data ? isAssetTransferable(asset?.data) : false;

return (
<div className="flex h-full w-full flex-col space-y-4 px-40">
<RouteLink path={ASSETS_ROUTE.path} title="Back" />
Expand All @@ -35,7 +34,7 @@ const VisualAssetDetailPage = () => {
) : (
<div className="flex justify-center p-20">Asset not found</div>
)}
{assetIsTransferable && activeAccount ? (
{isAssetTransferable && activeAccount ? (
<Button onClick={showSendAssetPopup}>Send Asset</Button>
) : null}
</div>
Expand Down
10 changes: 6 additions & 4 deletions apps/wallet/src/ui/app/pages/home/nft-details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Collapsible } from '_app/shared/collapse';
import { ExplorerLink, ExplorerLinkType, Loading, NFTDisplayCard, PageTemplate } from '_components';
import { useNFTBasicData, useOwnedNFT } from '_hooks';
import { useUnlockedGuard } from '_src/ui/app/hooks/useUnlockedGuard';
import { isAssetTransferable, useGetKioskContents, useGetNFTMeta } from '@iota/core';
import { useIsAssetTransferable, useGetKioskContents, useGetNFTMeta } from '@iota/core';
import { formatAddress } from '@iota/iota-sdk/utils';
import cl from 'clsx';
import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom';
Expand All @@ -24,7 +24,8 @@ function NFTDetailsPage() {
const nftId = searchParams.get('objectId');
const accountAddress = useActiveAddress();
const { data: objectData, isPending: isNftLoading } = useOwnedNFT(nftId || '', accountAddress);
const isTransferable = isAssetTransferable(objectData);
const { data: isAssetTransferable, isLoading: isCheckingAssetTransferability } =
useIsAssetTransferable(objectData);
const { nftFields, fileExtensionType, filePath } = useNFTBasicData(objectData);
const address = useActiveAddress();
const { data } = useGetKioskContents(address);
Expand Down Expand Up @@ -55,7 +56,8 @@ function NFTDetailsPage() {
objectData.owner.AddressOwner) ||
'';
const isGuardLoading = useUnlockedGuard();
const isPending = isNftLoading || isPendingDisplay || isGuardLoading;
const isPending =
isNftLoading || isPendingDisplay || isGuardLoading || isCheckingAssetTransferability;

function handleMoreAboutKiosk() {
window.open('https://docs.iota.org/references/ts-sdk/kiosk/', '_blank');
Expand Down Expand Up @@ -241,7 +243,7 @@ function NFTDetailsPage() {
) : (
<div className="flex flex-1 items-end">
<Button
disabled={!isTransferable}
disabled={!isAssetTransferable}
onClick={handleSend}
text="Send"
fullWidth
Expand Down
8 changes: 5 additions & 3 deletions apps/wallet/src/ui/app/pages/home/nft-transfer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,23 @@ import { useOwnedNFT } from '_hooks';
import { useUnlockedGuard } from '_src/ui/app/hooks/useUnlockedGuard';
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import { TransferNFTForm } from './TransferNFTForm';
import { isAssetTransferable } from '@iota/core';
import { useIsAssetTransferable } from '@iota/core';

function NftTransferPage() {
const { nftId } = useParams();
const address = useActiveAddress();
// verify that the nft is owned by the user and is transferable
const { data: ownedNFT, isPending: isNftLoading } = useOwnedNFT(nftId || '', address);
const { data: isAssetTransferable, isLoading: isCheckingAssetTransferability } =
useIsAssetTransferable(ownedNFT);
const navigate = useNavigate();
const isGuardLoading = useUnlockedGuard();
const isPending = isNftLoading || isGuardLoading;
const isPending = isNftLoading || isGuardLoading || isCheckingAssetTransferability;
return (
<Overlay showModal title="Send NFT" closeOverlay={() => navigate('/nfts')} showBackButton>
<Loading loading={isPending}>
<div className="flex h-full w-full flex-col gap-md">
{nftId && !!ownedNFT && isAssetTransferable(ownedNFT) ? (
{nftId && !!ownedNFT && isAssetTransferable ? (
<>
<div className="w-[172px] self-center">
<NFTDisplayCard objectId={nftId} wideView />
Expand Down

0 comments on commit d899a6c

Please sign in to comment.