From e435aa58aebb00cb478ae1c82d2ecedf538d3f25 Mon Sep 17 00:00:00 2001 From: Bran <52735957+brancoder@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:19:26 +0100 Subject: [PATCH 01/25] feat(wallet): improve displaying assets (#4038) * fix: featch all assets in wallet * fix: bring back asset pagination in wallet * fix: add assets loading on scroll * fix: remove useMemo * fix: add scroll to Other * fix: simplify assets mapping --- .../app/(protected)/assets/page.tsx | 73 +++++++------------ .../components/AssetsList.tsx | 34 ++++++++- .../src/ui/app/pages/home/nfts/index.tsx | 23 ++++-- 3 files changed, 74 insertions(+), 56 deletions(-) diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index 00fe1d57e24..4ff62fed3f8 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -3,16 +3,15 @@ 'use client'; -import { PageSizeSelector, PaginationOptions } from '@/components'; -import { Panel, Title, Chip, TitleSize, DropdownPosition } from '@iota/apps-ui-kit'; -import { hasDisplayData, useCursorPagination, useGetOwnedObjects } from '@iota/core'; +import { Panel, Title, Chip, TitleSize } from '@iota/apps-ui-kit'; +import { hasDisplayData, useGetOwnedObjects } from '@iota/core'; import { useCurrentAccount } from '@iota/dapp-kit'; import { IotaObjectData } from '@iota/iota-sdk/client'; import { useState } from 'react'; import { AssetCategory } from '@/lib/enums'; import { AssetList } from '@/components/AssetsList'; -const PAGINATION_RANGE = [20, 40, 60]; +const OBJECTS_PER_REQ = 50; const ASSET_CATEGORIES: { label: string; value: AssetCategory }[] = [ { @@ -27,40 +26,28 @@ const ASSET_CATEGORIES: { label: string; value: AssetCategory }[] = [ export default function AssetsDashboardPage(): React.JSX.Element { const [selectedCategory, setSelectedCategory] = useState(AssetCategory.Visual); - const [limit, setLimit] = useState(PAGINATION_RANGE[1]); - const account = useCurrentAccount(); - const ownedObjectsQuery = useGetOwnedObjects(account?.address, undefined, limit); - - const { data, pagination } = useCursorPagination(ownedObjectsQuery); - - const { data: ownedObjects } = data || {}; + const { data, isFetching, fetchNextPage, hasNextPage } = useGetOwnedObjects( + account?.address, + undefined, + OBJECTS_PER_REQ, + ); - const [visual, nonVisual] = (() => { - const visual: IotaObjectData[] = []; - const nonVisual: IotaObjectData[] = []; + const assets: IotaObjectData[] = []; - ownedObjects - ?.filter((asset) => asset.data && asset.data.objectId) - .forEach((asset) => { - if (asset.data) { + for (const page of data?.pages || []) { + for (const asset of page.data) { + if (asset.data && asset.data.objectId) { + if (selectedCategory == AssetCategory.Visual) { if (hasDisplayData(asset)) { - visual.push(asset.data); - } else { - nonVisual.push(asset.data); + assets.push(asset.data); } + } else if (selectedCategory == AssetCategory.Other) { + assets.push(asset.data); } - }); - - return [visual, nonVisual]; - })(); - - const categoryToAsset: Record = { - [AssetCategory.Visual]: visual, - [AssetCategory.Other]: nonVisual, - }; - - const assetList = categoryToAsset[selectedCategory]; + } + } + } return ( @@ -77,21 +64,13 @@ export default function AssetsDashboardPage(): React.JSX.Element { ))} - -
- setLimit(e)} - limit={limit.toString()} - /> - } - /> -
+
); diff --git a/apps/wallet-dashboard/components/AssetsList.tsx b/apps/wallet-dashboard/components/AssetsList.tsx index ca14660daff..0835f0a98e5 100644 --- a/apps/wallet-dashboard/components/AssetsList.tsx +++ b/apps/wallet-dashboard/components/AssetsList.tsx @@ -4,10 +4,17 @@ import { AssetCategory } from '@/lib/enums'; import { IotaObjectData } from '@iota/iota-sdk/client'; import { AssetTileLink } from '@/components'; +import { LoadingIndicator } from '@iota/apps-ui-kit'; +import { useEffect, useRef } from 'react'; +import { useOnScreen } from '@iota/core'; +import cl from 'clsx'; interface AssetListProps { assets: IotaObjectData[]; selectedCategory: AssetCategory; + hasNextPage: boolean; + isFetchingNextPage: boolean; + fetchNextPage: () => void; } const ASSET_LAYOUT: Record = { @@ -16,12 +23,35 @@ const ASSET_LAYOUT: Record = { [AssetCategory.Other]: 'flex flex-col overflow-auto py-sm', }; -export function AssetList({ assets, selectedCategory }: AssetListProps): React.JSX.Element { +export function AssetList({ + assets, + selectedCategory, + hasNextPage, + isFetchingNextPage, + fetchNextPage, +}: AssetListProps): React.JSX.Element { + const observerElem = useRef(null); + const { isIntersecting } = useOnScreen(observerElem); + const isSpinnerVisible = isFetchingNextPage && hasNextPage; + + useEffect(() => { + if (isIntersecting && hasNextPage && !isFetchingNextPage && fetchNextPage) { + fetchNextPage(); + } + }, [isIntersecting, fetchNextPage, hasNextPage, isFetchingNextPage]); + return ( -
+
{assets.map((asset) => ( ))} +
+ {isSpinnerVisible ? ( +
+ +
+ ) : null} +
); } diff --git a/apps/wallet/src/ui/app/pages/home/nfts/index.tsx b/apps/wallet/src/ui/app/pages/home/nfts/index.tsx index 5c4cba0e491..52c62bf96ae 100644 --- a/apps/wallet/src/ui/app/pages/home/nfts/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/nfts/index.tsx @@ -19,6 +19,7 @@ import HiddenAssets from './HiddenAssets'; import NonVisualAssets from './NonVisualAssets'; import VisualAssets from './VisualAssets'; import { Warning } from '@iota/ui-icons'; +import { useOnScreen } from '@iota/core'; enum AssetCategory { Visual = 'Visual', @@ -44,6 +45,7 @@ const ASSET_CATEGORIES = [ function NftsPage() { const [selectedAssetCategory, setSelectedAssetCategory] = useState(null); const observerElem = useRef(null); + const { isIntersecting } = useOnScreen(observerElem); const accountAddress = useActiveAddress(); const { @@ -51,6 +53,7 @@ function NftsPage() { hasNextPage, isLoading, isFetchingNextPage, + fetchNextPage, error, isPending, isError, @@ -95,6 +98,12 @@ function NftsPage() { ); }, [ownedAssets]); + useEffect(() => { + if (isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }, [isIntersecting, fetchNextPage, hasNextPage, isFetchingNextPage]); + useEffect(() => { let computeSelectedCategory = false; if ( @@ -172,17 +181,17 @@ function NftsPage() { ) : ( )} +
+ {isSpinnerVisible ? ( +
+ +
+ ) : null} +
)} -
- {isSpinnerVisible ? ( -
- -
- ) : null} -
); From 82e585bd5924414d4ed40f88326e69e3796dfd93 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Wed, 27 Nov 2024 10:37:08 +0100 Subject: [PATCH 02/25] feat(wallet): Make GH Releases for wallet production releases (#3954) * feat(wallet): Make GH Releases for wallet production releases * upload artifact again * fmt * fmt with dprint * chore: Always create drafted releases * chore: Temporary add `workflow_dipsatch` for testing purposes * chore: Remove `workflow_dipsatch` * chore: Pin `@elgohr/create-release` --- .github/workflows/apps_wallet_prod_build.yml | 42 ++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.github/workflows/apps_wallet_prod_build.yml b/.github/workflows/apps_wallet_prod_build.yml index 964ee7d1411..ade6a448804 100644 --- a/.github/workflows/apps_wallet_prod_build.yml +++ b/.github/workflows/apps_wallet_prod_build.yml @@ -39,6 +39,48 @@ jobs: echo "artifact_name=${artifact_name}" >> $GITHUB_ENV - name: Build Wallet run: pnpm wallet build + - name: Get Previous Tag + id: prev_tag + run: | + tags=$(git tag --list 'wallet-v*.*.*' --sort=-creatordate) + current_tag=$(echo "$tags" | sed -n 1p) + prev_tag=$(echo "$tags" | sed -n 2p) + if [ -z "$prev_tag" ]; then + echo "No previous tag found. Skipping changelog generation." + echo "PREV_TAG=none" >> $GITHUB_ENV + else + echo "PREV_TAG=$prev_tag" >> $GITHUB_ENV + fi + echo "CURRENT_TAG=$current_tag" >> $GITHUB_ENV + + - name: Generate Changelog + id: generate_changelog + run: | + if [ "${{ env.PREV_TAG }}" = "none" ]; then + echo "No previous tag found. Skipping changelog generation." + echo "changelog=No previous tag found. Changelog generation skipped." >> $GITHUB_ENV + else + git log ${{ env.PREV_TAG }}..${{ env.CURRENT_TAG }} --pretty=format:"- %s by @%an in #%h" -- ./apps/wallet > CHANGELOG.md + changelog=$(cat CHANGELOG.md) + echo "changelog=$changelog" >> $GITHUB_ENV + fi + + - name: Get version from tag + id: version + run: echo "::set-output name=version::${GITHUB_REF#refs/tags/wallet-v}" + + - name: Create GitHub Release + uses: elgohr/create-release@c5ea99036abb741a89f8bf1f2cd7fba845e3313a # pin@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.CURRENT_TAG }} + release_name: IOTA Wallet v${{ steps.version.outputs.version }} + body: | + ## Changelog + {{ env.changelog }} + draft: true + prerelease: false - name: Upload artifacts uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # pin@v4 with: From c0ed32d7711f43bc917d9f86920acb18f976c669 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Wed, 27 Nov 2024 10:55:02 +0100 Subject: [PATCH 03/25] fix(tooling-general): Fix Apps Wallet prod build workflow (#4265) * fix: Use correct name of gh action to make gh releases * chore: add workflow_dispatch --- .github/workflows/apps_wallet_prod_build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/apps_wallet_prod_build.yml b/.github/workflows/apps_wallet_prod_build.yml index ade6a448804..7fe6657ef78 100644 --- a/.github/workflows/apps_wallet_prod_build.yml +++ b/.github/workflows/apps_wallet_prod_build.yml @@ -1,6 +1,7 @@ name: Build Wallet App (Prod) on: + workflow_dispatch: push: tags: - "wallet-v[0-9]+.[0-9]+.[0-9]+" @@ -70,7 +71,7 @@ jobs: run: echo "::set-output name=version::${GITHUB_REF#refs/tags/wallet-v}" - name: Create GitHub Release - uses: elgohr/create-release@c5ea99036abb741a89f8bf1f2cd7fba845e3313a # pin@v5 + uses: elgohr/Github-Release-Action@c5ea99036abb741a89f8bf1f2cd7fba845e3313a # pin@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 207bfa04863b141f062e2c47b30597fb3248f6d4 Mon Sep 17 00:00:00 2001 From: Sergiu Popescu <44298302+sergiupopescu199@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:59:59 +0100 Subject: [PATCH 04/25] fix(iota-indexer): fix failing test (#4173) * fix(iota-indexer): fix failing test * fixup! fix(iota-indexer): fix failing test * fixup! fixup! fix(iota-indexer): fix failing test * fixup! fixup! fixup! fix(iota-indexer): fix failing test * fix(*): add rest api sync flag * fix(*): fallback to rest api * Update crates/iota-indexer/src/test_utils.rs Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * fixup! fix(*): fallback to rest api --------- Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> --- crates/iota-cluster-test/src/cluster.rs | 4 ++-- crates/iota-indexer/src/test_utils.rs | 9 ++++++--- crates/iota-indexer/tests/common/mod.rs | 6 +++--- crates/iota-indexer/tests/rpc-tests/read_api.rs | 1 + crates/iota/src/iota_commands.rs | 4 ++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/crates/iota-cluster-test/src/cluster.rs b/crates/iota-cluster-test/src/cluster.rs index 20eee208d0b..3c1d993f992 100644 --- a/crates/iota-cluster-test/src/cluster.rs +++ b/crates/iota-cluster-test/src/cluster.rs @@ -264,7 +264,7 @@ impl Cluster for LocalNewCluster { Some(pg_address.clone()), fullnode_url.clone(), ReaderWriterConfig::writer_mode(None), - data_ingestion_path.clone(), + Some(data_ingestion_path.clone()), None, ) .await; @@ -274,7 +274,7 @@ impl Cluster for LocalNewCluster { Some(pg_address), fullnode_url.clone(), ReaderWriterConfig::reader_mode(indexer_address.to_string()), - data_ingestion_path, + Some(data_ingestion_path), None, ) .await; diff --git a/crates/iota-indexer/src/test_utils.rs b/crates/iota-indexer/src/test_utils.rs index 4058acc5634..3d9bf3f80cc 100644 --- a/crates/iota-indexer/src/test_utils.rs +++ b/crates/iota-indexer/src/test_utils.rs @@ -44,7 +44,7 @@ pub async fn start_test_indexer( db_url: Option, rpc_url: String, reader_writer_config: ReaderWriterConfig, - data_ingestion_path: PathBuf, + data_ingestion_path: Option, new_database: Option<&str>, ) -> (PgIndexerStore, JoinHandle>) { start_test_indexer_impl( @@ -53,7 +53,7 @@ pub async fn start_test_indexer( reader_writer_config, // reset_database false, - Some(data_ingestion_path), + data_ingestion_path, CancellationToken::new(), new_database, ) @@ -86,11 +86,14 @@ pub async fn start_test_indexer_impl( let mut config = IndexerConfig { db_url: Some(db_url.clone().into()), + // As fallback sync mechanism enable Rest Api if `data_ingestion_path` was not provided + remote_store_url: data_ingestion_path + .is_none() + .then_some(format!("{rpc_url}/api/v1")), rpc_client_url: rpc_url, reset_db: true, fullnode_sync_worker: true, rpc_server_worker: false, - remote_store_url: None, data_ingestion_path, ..Default::default() }; diff --git a/crates/iota-indexer/tests/common/mod.rs b/crates/iota-indexer/tests/common/mod.rs index 55273b8a1d9..23ccf354899 100644 --- a/crates/iota-indexer/tests/common/mod.rs +++ b/crates/iota-indexer/tests/common/mod.rs @@ -114,7 +114,7 @@ pub async fn start_test_cluster_with_read_write_indexer( builder_modifier: Option TestClusterBuilder>>, ) -> (TestCluster, PgIndexerStore, HttpClient) { let temp = tempdir().unwrap().into_path(); - let mut builder = TestClusterBuilder::new().with_data_ingestion_dir(temp.clone()); + let mut builder = TestClusterBuilder::new(); if let Some(builder_modifier) = builder_modifier { builder = builder_modifier(builder); @@ -127,7 +127,7 @@ pub async fn start_test_cluster_with_read_write_indexer( Some(get_indexer_db_url(None)), cluster.rpc_url().to_string(), ReaderWriterConfig::writer_mode(None), - temp.clone(), + None, database_name, ) .await; @@ -305,7 +305,7 @@ pub async fn start_simulacrum_rest_api_with_write_indexer( Some(get_indexer_db_url(None)), format!("http://{}", server_url), ReaderWriterConfig::writer_mode(None), - data_ingestion_path, + Some(data_ingestion_path), database_name, ) .await; diff --git a/crates/iota-indexer/tests/rpc-tests/read_api.rs b/crates/iota-indexer/tests/rpc-tests/read_api.rs index 5d0d17f7e05..382c6ea4ad3 100644 --- a/crates/iota-indexer/tests/rpc-tests/read_api.rs +++ b/crates/iota-indexer/tests/rpc-tests/read_api.rs @@ -1158,6 +1158,7 @@ fn get_total_transaction_blocks() { runtime.block_on(async move { indexer_wait_for_checkpoint(store, checkpoint).await; + let total_transaction_blocks = client.get_total_transaction_blocks().await.unwrap(); let fullnode_checkpoint = cluster diff --git a/crates/iota/src/iota_commands.rs b/crates/iota/src/iota_commands.rs index 0e37b2f016b..b4eb64d0724 100644 --- a/crates/iota/src/iota_commands.rs +++ b/crates/iota/src/iota_commands.rs @@ -755,7 +755,7 @@ async fn start( Some(pg_address.clone()), fullnode_url.clone(), ReaderWriterConfig::writer_mode(None), - data_ingestion_path.clone(), + Some(data_ingestion_path.clone()), None, ) .await; @@ -766,7 +766,7 @@ async fn start( Some(pg_address.clone()), fullnode_url.clone(), ReaderWriterConfig::reader_mode(indexer_address.to_string()), - data_ingestion_path, + Some(data_ingestion_path), None, ) .await; From ea7499c5f48866fdbf0092a6f3f60beb5dd5a227 Mon Sep 17 00:00:00 2001 From: JCNoguera <88061365+VmMad@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:13:44 +0100 Subject: [PATCH 05/25] fix(wallet): toast overlay connecting dapp buttons (#4243) --- apps/wallet/src/ui/app/shared/toaster/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/wallet/src/ui/app/shared/toaster/index.tsx b/apps/wallet/src/ui/app/shared/toaster/index.tsx index d5ff889a72c..47c7d8dcb64 100644 --- a/apps/wallet/src/ui/app/shared/toaster/index.tsx +++ b/apps/wallet/src/ui/app/shared/toaster/index.tsx @@ -37,8 +37,10 @@ function getBottomSpace(pathname: string, isMenuVisible: boolean, isBottomNavSpa '/accounts/manage', ].includes(pathname); - if (overlayWithActionButton || isBottomNavSpace) { - return '!bottom-16'; + const matchDynamicPaths = ['/dapp/connect'].some((path) => pathname.startsWith(path)); + + if (overlayWithActionButton || isBottomNavSpace || matchDynamicPaths) { + return '!bottom-20'; } return ''; From c010a5ddd47557df2c5bc0db80de0f4d6bb3963b Mon Sep 17 00:00:00 2001 From: JCNoguera <88061365+VmMad@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:39:24 +0100 Subject: [PATCH 06/25] fix(wallet, dashboard): use validator image when possible (#4247) * fix(wallet, dashboard): use validator image when possible * fix: remove unnecessary change * feat: add missing image in dashboard --- .../Dialogs/Staking/views/DetailsView.tsx | 2 +- .../components/Dialogs/Staking/views/Validator.tsx | 14 ++++++++++---- .../ui/app/staking/validators/ValidatorLogo.tsx | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/DetailsView.tsx index 79106443924..7e81f984e79 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/DetailsView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/DetailsView.tsx @@ -108,7 +108,7 @@ export function DetailsView({ @@ -47,7 +48,12 @@ export function Validator({ return ( - + {showAction && ( diff --git a/apps/wallet/src/ui/app/staking/validators/ValidatorLogo.tsx b/apps/wallet/src/ui/app/staking/validators/ValidatorLogo.tsx index dcb8954a274..bf8d05a97b6 100644 --- a/apps/wallet/src/ui/app/staking/validators/ValidatorLogo.tsx +++ b/apps/wallet/src/ui/app/staking/validators/ValidatorLogo.tsx @@ -82,7 +82,7 @@ export function ValidatorLogo({ Date: Wed, 27 Nov 2024 13:36:07 +0100 Subject: [PATCH 07/25] feat: rebrand toasts (#4261) Co-authored-by: Bran <52735957+brancoder@users.noreply.github.com> --- apps/wallet-dashboard/components/Toaster.tsx | 40 +++++++++++++++++++ apps/wallet-dashboard/components/index.ts | 1 + .../providers/AppProviders.tsx | 9 +---- 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 apps/wallet-dashboard/components/Toaster.tsx diff --git a/apps/wallet-dashboard/components/Toaster.tsx b/apps/wallet-dashboard/components/Toaster.tsx new file mode 100644 index 00000000000..12b29435158 --- /dev/null +++ b/apps/wallet-dashboard/components/Toaster.tsx @@ -0,0 +1,40 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import toast, { Toaster as ToasterLib, type ToastType, resolveValue } from 'react-hot-toast'; +import { Snackbar, SnackbarType } from '@iota/apps-ui-kit'; + +export type ToasterProps = { + bottomNavEnabled?: boolean; +}; + +export function Toaster() { + function getSnackbarType(type: ToastType): SnackbarType { + switch (type) { + case 'success': + return SnackbarType.Default; + case 'error': + return SnackbarType.Error; + case 'loading': + return SnackbarType.Default; + default: + return SnackbarType.Default; + } + } + + return ( + + {(t) => ( +
+ toast.dismiss(t.id)} + text={resolveValue(t.message, t)} + type={getSnackbarType(t.type)} + showClose + duration={t.duration} + /> +
+ )} +
+ ); +} diff --git a/apps/wallet-dashboard/components/index.ts b/apps/wallet-dashboard/components/index.ts index c1320cc7447..d35471140d9 100644 --- a/apps/wallet-dashboard/components/index.ts +++ b/apps/wallet-dashboard/components/index.ts @@ -23,3 +23,4 @@ export * from './ExplorerLink'; export * from './Dialogs'; export * from './ValidatorStakingData'; export * from './tiles'; +export * from './Toaster'; diff --git a/apps/wallet-dashboard/providers/AppProviders.tsx b/apps/wallet-dashboard/providers/AppProviders.tsx index 342a6fa6c34..69b2cebb513 100644 --- a/apps/wallet-dashboard/providers/AppProviders.tsx +++ b/apps/wallet-dashboard/providers/AppProviders.tsx @@ -3,7 +3,7 @@ 'use client'; -import { PopupProvider } from '@/components'; +import { PopupProvider, Toaster } from '@/components'; import { GrowthBookProvider } from '@growthbook/growthbook-react'; import { IotaClientProvider, lightTheme, darkTheme, WalletProvider } from '@iota/dapp-kit'; import { getAllNetworks, getDefaultNetwork } from '@iota/iota-sdk/client'; @@ -11,7 +11,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useState } from 'react'; import { growthbook } from '@/lib/utils'; import { Popup } from '@/components/Popup'; -import { Toaster } from 'react-hot-toast'; import { ThemeProvider } from '@iota/core'; growthbook.init(); @@ -40,11 +39,7 @@ export function AppProviders({ children }: React.PropsWithChildren) { {children} - + From 5c63706b638378d6a2d6b3c1f1ae2164c57a5e24 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk Date: Wed, 27 Nov 2024 16:21:06 +0200 Subject: [PATCH 08/25] feat(dashboard): update activity page (#4251) * feat(dashboard): update activity page * fix(dashboard): update layout height --- .../app/(protected)/activity/page.tsx | 37 ++++++------------- .../app/(protected)/layout.tsx | 2 +- apps/wallet-dashboard/app/globals.css | 2 +- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/apps/wallet-dashboard/app/(protected)/activity/page.tsx b/apps/wallet-dashboard/app/(protected)/activity/page.tsx index 63a84224f75..14f4bc516da 100644 --- a/apps/wallet-dashboard/app/(protected)/activity/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/activity/page.tsx @@ -4,34 +4,21 @@ 'use client'; import React from 'react'; -import { useCurrentAccount } from '@iota/dapp-kit'; -import { VirtualList, TransactionTile } from '@/components'; -import { useQueryTransactionsByAddress } from '@iota/core'; -import { getExtendedTransaction } from '@/lib/utils/transaction'; -import { IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; +import { Panel, Title, TitleSize } from '@iota/apps-ui-kit'; +import { TransactionsList } from '@/components'; function ActivityPage(): JSX.Element { - const currentAccount = useCurrentAccount(); - const { data: transactions, error } = useQueryTransactionsByAddress(currentAccount?.address); - - if (error) { - return
{error?.message}
; - } - - const virtualItem = (rawTransaction: IotaTransactionBlockResponse): JSX.Element => { - const transaction = getExtendedTransaction(rawTransaction, currentAccount?.address || ''); - return ; - }; - return ( -
-

Your Activity

-
- 100} - render={virtualItem} - /> +
+
+ +
+ + </div> + <div className="px-sm pb-md pt-sm"> + <TransactionsList /> + </div> + </Panel> </div> </div> ); diff --git a/apps/wallet-dashboard/app/(protected)/layout.tsx b/apps/wallet-dashboard/app/(protected)/layout.tsx index 79f7a084546..2ba32059a9b 100644 --- a/apps/wallet-dashboard/app/(protected)/layout.tsx +++ b/apps/wallet-dashboard/app/(protected)/layout.tsx @@ -17,7 +17,7 @@ function DashboardLayout({ children }: PropsWithChildren): JSX.Element { }; return ( - <div className="h-full"> + <div className="min-h-full"> <div className="fixed left-0 top-0 z-50 h-full"> <Sidebar /> </div> diff --git a/apps/wallet-dashboard/app/globals.css b/apps/wallet-dashboard/app/globals.css index f4151e588f5..82c7527e111 100644 --- a/apps/wallet-dashboard/app/globals.css +++ b/apps/wallet-dashboard/app/globals.css @@ -6,7 +6,7 @@ html, body { - height: 100%; + min-height: 100%; @apply bg-gray-100 dark:bg-gray-900; @apply text-gray-900 dark:text-gray-100; } From de4df8e2274291d5365e545585a4fafe00515cc9 Mon Sep 17 00:00:00 2001 From: Marc Espin <mespinsanz@gmail.com> Date: Wed, 27 Nov 2024 17:20:59 +0100 Subject: [PATCH 09/25] docs(ts-sdk): Update outdated TS SDK Docs (#4255) * docs(ts-sdk): Update outdated TS SDK Docs * ' * use Network enum * fix usage of Network * fix reference of Network.Custom --- dapps/kiosk-cli/index.js | 4 ++-- .../kiosk/src/context/KioskClientContext.tsx | 5 +++-- .../developer/standards/wallet-standard.mdx | 6 +++--- .../ts-sdk/kiosk/advanced-examples.mdx | 4 ++-- .../kiosk/kiosk-client/introduction.mdx | 21 +++++++------------ .../kiosk-transaction/purchasing.mdx | 5 ++--- 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/dapps/kiosk-cli/index.js b/dapps/kiosk-cli/index.js index a54976a9a9a..aff1f965b12 100644 --- a/dapps/kiosk-cli/index.js +++ b/dapps/kiosk-cli/index.js @@ -34,7 +34,7 @@ import { } from '@iota/iota-sdk/utils'; import { bcs } from '@iota/iota-sdk/bcs'; import { program } from 'commander'; -import { KIOSK_LISTING, KioskClient, KioskTransaction, Network } from '@iota/kiosk'; +import { KIOSK_LISTING, KioskClient, KioskTransaction } from '@iota/kiosk'; import { IotaClient, getFullnodeUrl } from '@iota/iota-sdk/client'; import { Ed25519Keypair } from '@iota/iota-sdk/keypairs/ed25519'; import { Transaction } from '@iota/iota-sdk/transactions'; @@ -49,7 +49,7 @@ const client = new IotaClient({ url: getFullnodeUrl('testnet') }); const kioskClient = new KioskClient({ client, - network: Network.TESTNET, + network: 'testnet', }); /** diff --git a/dapps/kiosk/src/context/KioskClientContext.tsx b/dapps/kiosk/src/context/KioskClientContext.tsx index e193465a553..1aa6339edfd 100644 --- a/dapps/kiosk/src/context/KioskClientContext.tsx +++ b/dapps/kiosk/src/context/KioskClientContext.tsx @@ -3,7 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 import { useIotaClient, useIotaClientContext } from '@iota/dapp-kit'; -import { KioskClient, Network } from '@iota/kiosk'; +import { NetworkId } from '@iota/iota-sdk/client'; +import { KioskClient } from '@iota/kiosk'; import { createContext, ReactNode, useContext, useMemo } from 'react'; export const KioskClientContext = createContext<KioskClient | undefined>(undefined); @@ -15,7 +16,7 @@ export function KioskClientProvider({ children }: { children: ReactNode }) { () => new KioskClient({ client: iotaClient, - network: network as Network, + network: network as NetworkId, }), [iotaClient, network], ); diff --git a/docs/content/developer/standards/wallet-standard.mdx b/docs/content/developer/standards/wallet-standard.mdx index 8cc9142dde4..4a529531af1 100644 --- a/docs/content/developer/standards/wallet-standard.mdx +++ b/docs/content/developer/standards/wallet-standard.mdx @@ -20,7 +20,7 @@ Create a class that represents your wallet. Use the `Wallet` interface from `@iota/wallet-standard` to help ensure your class adheres to the standard. ```tsx -import { IOTA_DEVNET_CHAIN, Wallet } from '@iota/wallet-standard'; +import { SUPPORTED_CHAINS, Wallet } from '@iota/wallet-standard'; class YourWallet implements Wallet { get version() { @@ -35,7 +35,7 @@ class YourWallet implements Wallet { } // Return the IOTA chains that your wallet supports. get chains() { - return [IOTA_DEVNET_CHAIN]; + return SUPPORTED_CHAINS; } } ``` @@ -152,7 +152,7 @@ class YourWallet implements Wallet { address: walletAccount.iotaAddress, publicKey: walletAccount.pubkey, // The IOTA chains that your wallet supports. - chains: [IOTA_DEVNET_CHAIN], + chains: [SUPPORTED_CHAINS], // The features that this account supports. This can be a subset of the wallet's supported features. // These features must exist on the wallet as well. features: [ diff --git a/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx b/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx index 181ec1ed41d..1948d65c4f1 100644 --- a/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx +++ b/docs/content/references/ts-sdk/kiosk/advanced-examples.mdx @@ -12,9 +12,9 @@ const otherType = `${packageId}::other_module::OtherStruct<${packageId}::other_c // initialize a kioskClient. const kioskClient = new KioskClient({ client: new IotaClient({ - url: getFullnodeUrl('testnet'), + url: getFullnodeUrl(Network.Testnet), }), - network: Network.TESTNET, + network: Network.Testnet, }); ``` diff --git a/docs/content/references/ts-sdk/kiosk/kiosk-client/introduction.mdx b/docs/content/references/ts-sdk/kiosk/kiosk-client/introduction.mdx index 73bea1b8fce..e52f56bc105 100644 --- a/docs/content/references/ts-sdk/kiosk/kiosk-client/introduction.mdx +++ b/docs/content/references/ts-sdk/kiosk/kiosk-client/introduction.mdx @@ -7,38 +7,33 @@ Kiosk Client is the base for all Kiosk SDK functionality. ## Creating a kiosk client -You can follow the example to create a `KioskClient`. The client currently supports `MAINNET` and -`TESTNET`. View next section for usage in other networks. - -_IOTA Kiosk rules and extensions are not supported in Devnet due to network wipes (that would -require constantly changing the package IDs)._ - +You can follow the example to create a `KioskClient`. View next section for usage in custom networks. ```typescript -import { KioskClient, Network } from '@iota/kiosk'; -import { getFullnodeUrl, IotaClient } from '@iota/iota-sdk/client'; +import { KioskClient } from '@iota/kiosk'; +import { getFullnodeUrl, IotaClient, Network } from '@iota/iota-sdk/client'; // We need a IOTA Client. You can re-use the IotaClient of your project // (it's not recommended to create a new one). -const client = new IotaClient({ url: getFullnodeUrl('testnet') }); +const client = new IotaClient({ url: getFullnodeUrl(Network.Testnet) }); // Now we can use it to create a kiosk Client. const kioskClient = new KioskClient({ client, - network: Network.TESTNET, + network: Network.Testnet, }); ``` -### Using KioskClient on devnet or localnet +### Using KioskClient on custom networks -To use all the functionality of Kiosk SDK outside of `MAINNET` and `TESTNET`, use `Network.CUSTOM` +To use all the functionality of Kiosk SDK outside of official networks, use `Network.Custom` as the network, and pass the `packageIds` for the rules and extensions you want to use. ```typescript // constructing it for custom network use. const kioskClient = new KioskClient({ client, - network: Network.CUSTOM, + network: Network.Custom, packageIds: { kioskLockRulePackageId: '0x...', royaltyRulePackageId: '0x...', diff --git a/docs/content/references/ts-sdk/kiosk/kiosk-client/kiosk-transaction/purchasing.mdx b/docs/content/references/ts-sdk/kiosk/kiosk-client/kiosk-transaction/purchasing.mdx index 0e2492241ce..771150f2592 100644 --- a/docs/content/references/ts-sdk/kiosk/kiosk-client/kiosk-transaction/purchasing.mdx +++ b/docs/content/references/ts-sdk/kiosk/kiosk-client/kiosk-transaction/purchasing.mdx @@ -1,9 +1,8 @@ # Purchasing from a kiosk One of the base functionalities of the SDK is a seamless purchasing flow, allowing for ease of rules -resolving (hiding away the calls). The SDK supports all four rules by default, and works for -`TESTNET` and `MAINNET`. To support other networks, -[follow the instructions in the Introduction](../introduction#using-kioskclient-on-devnet-or-localnet). +resolving (hiding away the calls). To use custom networks, +[follow the instructions in the Introduction](../introduction#using-kioskclient-on-custom-networks). ## How to purchase From 7f082f2e820436f99e2af03c2e2902592686a2c1 Mon Sep 17 00:00:00 2001 From: evavirseda <evirseda@boxfish.studio> Date: Wed, 27 Nov 2024 19:15:25 +0100 Subject: [PATCH 10/25] feat(wallet): change delete account action to destructive color (#4253) --- apps/wallet/src/ui/app/pages/accounts/manage/RemoveDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet/src/ui/app/pages/accounts/manage/RemoveDialog.tsx b/apps/wallet/src/ui/app/pages/accounts/manage/RemoveDialog.tsx index e657642946d..807ef298573 100644 --- a/apps/wallet/src/ui/app/pages/accounts/manage/RemoveDialog.tsx +++ b/apps/wallet/src/ui/app/pages/accounts/manage/RemoveDialog.tsx @@ -59,7 +59,7 @@ export function RemoveDialog({ isOpen, setOpen, accountID }: RemoveDialogProps) /> <Button fullWidth - type={ButtonType.Primary} + type={ButtonType.Destructive} text="Remove" onClick={handleRemove} /> From 1dd90acf929d1663a78ab6a1ddc968d05bc5772b Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:30:22 +0100 Subject: [PATCH 11/25] feat(iota): don't require key generation for client.yaml if there is already one (#3978) * feat(iota): don't require key generation for client.yaml if there is already one * print if existing address will be used as active address * Update crates/iota/src/iota_commands.rs Co-authored-by: Thibault Martinez <thibault@iota.org> --------- Co-authored-by: Thibault Martinez <thibault@iota.org> --- crates/iota/src/iota_commands.rs | 39 +++++++++++++++++++------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/crates/iota/src/iota_commands.rs b/crates/iota/src/iota_commands.rs index b4eb64d0724..54cad050dae 100644 --- a/crates/iota/src/iota_commands.rs +++ b/crates/iota/src/iota_commands.rs @@ -1201,29 +1201,36 @@ async fn prompt_if_no_config( .unwrap_or(&iota_config_dir()?) .join(IOTA_KEYSTORE_FILENAME); let mut keystore = Keystore::from(FileBasedKeystore::new(&keystore_path)?); - let key_scheme = if accept_defaults { - SignatureScheme::ED25519 + // Get an existing address or generate a new one + let active_address = if let Some(existing_address) = keystore.addresses().first() { + println!("Using existing address {existing_address} as active address."); + *existing_address } else { + let key_scheme = if accept_defaults { + SignatureScheme::ED25519 + } else { + println!( + "Select key scheme to generate keypair (0 for ed25519, 1 for secp256k1, 2: for secp256r1):" + ); + match SignatureScheme::from_flag(read_line()?.trim()) { + Ok(s) => s, + Err(e) => return Err(anyhow!("{e}")), + } + }; + let (new_address, phrase, scheme) = + keystore.generate_and_add_new_key(key_scheme, None, None, None)?; + let alias = keystore.get_alias_by_address(&new_address)?; println!( - "Select key scheme to generate keypair (0 for ed25519, 1 for secp256k1, 2: for secp256r1):" + "Generated new keypair and alias for address with scheme {:?} [{alias}: {new_address}]", + scheme.to_string() ); - match SignatureScheme::from_flag(read_line()?.trim()) { - Ok(s) => s, - Err(e) => return Err(anyhow!("{e}")), - } + println!("Secret Recovery Phrase : [{phrase}]"); + new_address }; - let (new_address, phrase, scheme) = - keystore.generate_and_add_new_key(key_scheme, None, None, None)?; - let alias = keystore.get_alias_by_address(&new_address)?; - println!( - "Generated new keypair and alias for address with scheme {:?} [{alias}: {new_address}]", - scheme.to_string() - ); - println!("Secret Recovery Phrase : [{phrase}]"); let alias = env.alias().clone(); IotaClientConfig::new(keystore) .with_envs([env]) - .with_active_address(new_address) + .with_active_address(active_address) .with_active_env(alias) .persisted(wallet_conf_path) .save()?; From 64bca645258c3b13572557806791caa79d254c58 Mon Sep 17 00:00:00 2001 From: Samuel Rufinatscha <samuel.rufinatscha@iota.org> Date: Thu, 28 Nov 2024 12:01:51 +0100 Subject: [PATCH 12/25] Review iota-graphql test coverage (#4135) * documentation: review iota-graphql test coverage * documentation: reference the transactional test runner * fix: refer tests.rs module * fix: coverage for dynamic queries * fix: coverage for filter options WIP * fix: restructure coverage * docs: Add missing queries * fix: location paths * fix: format * fix: add missing filter options * fix: remove link * fix: graphql highlights --- crates/iota-graphql-e2e-tests/README.md | 8 + .../mutation/executeTransactionBlock.md | 118 +++++++++ .../coverage/query/address.md | 191 ++++++++++++++ .../coverage/query/availableRange.md | 43 ++++ .../coverage/query/chainIdentifier.md | 16 ++ .../coverage/query/checkpoint.md | 69 ++++++ .../coverage/query/checkpoints.md | 96 ++++++++ .../coverage/query/coinMetadata.md | 45 ++++ .../coverage/query/coins.md | 156 ++++++++++++ .../coverage/query/dryRunTransactionBlock.md | 90 +++++++ .../coverage/query/epoch.md | 107 ++++++++ .../coverage/query/events.md | 49 ++++ .../coverage/query/latestPackage.md | 232 ++++++++++++++++++ .../coverage/query/object.md | 165 +++++++++++++ .../coverage/query/objects.md | 187 ++++++++++++++ .../coverage/query/owner.md | 156 ++++++++++++ .../coverage/query/package.md | 232 ++++++++++++++++++ .../coverage/query/packages.md | 230 +++++++++++++++++ .../coverage/query/protocolConfig.md | 59 +++++ .../coverage/query/serviceConfig.md | 37 +++ .../coverage/query/transactionBlock.md | 74 ++++++ .../coverage/query/transactionBlocks.md | 175 +++++++++++++ .../coverage/query/type.md | 28 +++ crates/iota-graphql-rpc/README.md | 11 + 24 files changed, 2574 insertions(+) create mode 100644 crates/iota-graphql-e2e-tests/coverage/mutation/executeTransactionBlock.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/address.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/availableRange.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/chainIdentifier.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/checkpoint.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/checkpoints.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/coinMetadata.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/coins.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/dryRunTransactionBlock.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/epoch.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/events.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/latestPackage.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/object.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/objects.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/owner.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/package.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/packages.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/protocolConfig.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/serviceConfig.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/transactionBlock.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/transactionBlocks.md create mode 100644 crates/iota-graphql-e2e-tests/coverage/query/type.md diff --git a/crates/iota-graphql-e2e-tests/README.md b/crates/iota-graphql-e2e-tests/README.md index 4e0c24eee8a..a0001d1c739 100644 --- a/crates/iota-graphql-e2e-tests/README.md +++ b/crates/iota-graphql-e2e-tests/README.md @@ -1,6 +1,14 @@ End-to-end tests for GraphQL service, built on top of the transactional test runner. +# Testing through the transactional test runner + +This crate specifically tests GraphQL queries in an e2e manner against the Indexer database. +The tests are executed through the [transactional test runner](../iota-transactional-test-runner), mediated by the [test.rs](tests/tests.rs) module. + +Each test is defined in a `.move` file, which contains various statements that are executed in a transactional manner. +In addition to the `.move` file, there is a corresponding `.exp` file that contains the expected output of the transactional test. + # Local Set-up These tests require a running instance of the `postgres` service, with a diff --git a/crates/iota-graphql-e2e-tests/coverage/mutation/executeTransactionBlock.md b/crates/iota-graphql-e2e-tests/coverage/mutation/executeTransactionBlock.md new file mode 100644 index 00000000000..708215005ff --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/mutation/executeTransactionBlock.md @@ -0,0 +1,118 @@ +Query: `executeTransactionBlock` + +```graphql +mutation { + executeTransactionBlock(txBytes: $tx, signatures: $sigs) { + effects { + transactionBlock { + digest + } + status + lamportVersion + errors + dependencies { + edges { + node { + digest + bcs + } + } + } + gasEffects { + __typename + gasObject { + digest + storageRebate + bcs + } + gasSummary { + computationCost + computationCostBurned + storageCost + storageRebate + nonRefundableStorageFee + } + } + unchangedSharedObjects { + edges { + node { + __typename + } + } + } + objectChanges { + edges { + node { + idCreated + idDeleted + } + } + } + balanceChanges { + edges { + node { + amount + } + } + } + events { + edges { + node { + timestamp + } + } + } + timestamp + epoch { + referenceGasPrice + endTimestamp + totalCheckpoints + totalTransactions + totalGasFees + totalStakeRewards + fundSize + netInflow + fundInflow + fundOutflow + systemStateVersion + iotaTotalSupply + iotaTreasuryCapId + liveObjectSetDigest + } + checkpoint { + previousCheckpointDigest + networkTotalTransactions + } + bcs + } + errors + } +} +``` + +tested by [crates/iota-graphql-rpc/tests/e2e_tests.rs](../../../iota-graphql-rpc/tests/e2e_tests.rs): + +```graphql +{ + executeTransactionBlock(txBytes: $tx, signatures: $sigs) { + effects { + transactionBlock { + digest + } + } + errors + } +} +``` + +tested by [crates/iota-graphql-rpc/tests/e2e_tests.rs](../../../iota-graphql-rpc/tests/e2e_tests.rs): + +```graphql +mutation { + executeTransactionBlock(txBytes: "{}", signatures: "{}") { + effects { + status + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/address.md b/crates/iota-graphql-e2e-tests/coverage/query/address.md new file mode 100644 index 00000000000..7cc2ff3aa0e --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/address.md @@ -0,0 +1,191 @@ +Query: `address` + +```graphql +{ + address(address: "0x1") { + address + objects { + edges { + node { + digest + storageRebate + bcs + } + } + } + balance { + coinObjectCount + totalBalance + } + balances { + edges { + node { + coinObjectCount + totalBalance + } + } + } + coins { + edges { + node { + digest + storageRebate + bcs + coinBalance + } + } + } + stakedIotas { + edges { + node { + digest + storageRebate + bcs + poolId + principal + estimatedReward + } + } + } + transactionBlocks { + edges { + node { + digest + bcs + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/call/owned_objects.move](../../../iota-graphql-e2e-tests/tests/call/owned_objects.move): + +```graphql +//# run-graphql +{ + address(address: "0x42") { + objects { + edges { + node { + owner { + __typename + ... on AddressOwner { + owner { + address + } + } + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/transaction_block_effects/balance_changes.move](../../../iota-graphql-e2e-tests/tests/transaction_block_effects/balance_changes.move): + +```graphql +//# run-graphql +{ + address(address: "@{C}") { + transactionBlocks(last: 1) { + nodes { + effects { + balanceChanges { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + node { + amount + } + cursor + } + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/consistency/coins.move](../../../iota-graphql-e2e-tests/tests/consistency/coins.move): + +```graphql +//# run-graphql +{ + queryCoins: coins(type: "@{P0}::fake::FAKE") { + edges { + cursor + node { + owner { + ... on AddressOwner { + owner { + address + coins(type: "@{P0}::fake::FAKE") { + edges { + cursor + node { + contents { + json + } + } + } + } + } + } + } + contents { + json + } + } + } + } + addressCoinsA: address(address: "@{A}") { + coins(type: "@{P0}::fake::FAKE") { + edges { + cursor + node { + contents { + json + } + } + } + } + } + addressCoinsB: address(address: "@{B}") { + coins(type: "@{P0}::fake::FAKE") { + edges { + cursor + node { + contents { + json + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/consistency/staked_iota.move](../../../iota-graphql-e2e-tests/tests/consistency/staked_iota.move): + +```graphql +//# run-graphql +{ + address(address: "@{C}") { + stakedIotas { + edges { + cursor + node { + principal + } + } + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/availableRange.md b/crates/iota-graphql-e2e-tests/coverage/query/availableRange.md new file mode 100644 index 00000000000..5b76a865112 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/availableRange.md @@ -0,0 +1,43 @@ +Query: `availableRange` + +```graphql +{ + availableRange { + first { + digest + } + last { + digest + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/available_range/available_range.move](../../../iota-graphql-e2e-tests/tests/available_range/available_range.move): + +```graphql +//# run-graphql +{ + availableRange { + first { + digest + sequenceNumber + } + last { + digest + sequenceNumber + } + } + + first: checkpoint(id: { sequenceNumber: 0 } ) { + digest + sequenceNumber + } + + last: checkpoint { + digest + sequenceNumber + } +} +``` + diff --git a/crates/iota-graphql-e2e-tests/coverage/query/chainIdentifier.md b/crates/iota-graphql-e2e-tests/coverage/query/chainIdentifier.md new file mode 100644 index 00000000000..9dda9821f3e --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/chainIdentifier.md @@ -0,0 +1,16 @@ +Query: `chainIdentifier` + +```graphql +{ + chainIdentifier +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/epoch/chain_identifier.move](../../../iota-graphql-e2e-tests/tests/epoch/chain_identifier.move): + +```graphql +//# run-graphql +{ +chainIdentifier +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/checkpoint.md b/crates/iota-graphql-e2e-tests/coverage/query/checkpoint.md new file mode 100644 index 00000000000..278b5a5d8fc --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/checkpoint.md @@ -0,0 +1,69 @@ +Query: `checkpoint` + +```graphql +{ + checkpoint(id: null) { + digest + sequenceNumber + timestamp + validatorSignatures + previousCheckpointDigest + networkTotalTransactions + rollingGasSummary { + computationCost + computationCostBurned + storageCost + storageRebate + nonRefundableStorageFee + } + epoch { + referenceGasPrice + endTimestamp + totalCheckpoints + totalTransactions + totalGasFees + totalStakeRewards + fundSize + netInflow + fundInflow + fundOutflow + systemStateVersion + iotaTotalSupply + iotaTreasuryCapId + liveObjectSetDigest + } + transactionBlocks { + edges { + node { + digest + bcs + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/call/simple.move](../../../iota-graphql-e2e-tests/tests/call/simple.move): + +```graphql +//# run-graphql +{ + checkpoint { + sequenceNumber + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/transactions/at_checkpoint.move](../../../iota-graphql-e2e-tests/tests/transactions/at_checkpoint.move): + +```graphql +//# run-graphql +{ # Via a checkpoint query + c0: checkpoint(id: { sequenceNumber: 0 }) { transactionBlocks { nodes { ...Tx } } } + c1: checkpoint(id: { sequenceNumber: 1 }) { transactionBlocks { nodes { ...Tx } } } + c2: checkpoint(id: { sequenceNumber: 2 }) { transactionBlocks { nodes { ...Tx } } } + c3: checkpoint(id: { sequenceNumber: 3 }) { transactionBlocks { nodes { ...Tx } } } + c4: checkpoint(id: { sequenceNumber: 4 }) { transactionBlocks { nodes { ...Tx } } } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/checkpoints.md b/crates/iota-graphql-e2e-tests/coverage/query/checkpoints.md new file mode 100644 index 00000000000..35e1d963e78 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/checkpoints.md @@ -0,0 +1,96 @@ +Query: `checkpoints` + +```graphql +{ + checkpoints(after: null, first: null, last: null, before: null) { + edges { + node { + digest + sequenceNumber + timestamp + validatorSignatures + previousCheckpointDigest + networkTotalTransactions + rollingGasSummary { + computationCost + computationCostBurned + storageCost + storageRebate + nonRefundableStorageFee + } + epoch { + referenceGasPrice + endTimestamp + totalCheckpoints + totalTransactions + totalGasFees + totalStakeRewards + fundSize + netInflow + fundInflow + fundOutflow + systemStateVersion + iotaTotalSupply + iotaTreasuryCapId + liveObjectSetDigest + } + transactionBlocks { + edges { + node { + digest + bcs + } + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/limits/output_node_estimation.move](../../../iota-graphql-e2e-tests/tests/limits/output_node_estimation.move): + +```graphql +//# run-graphql --show-usage +# build on previous example with nested connection +{ + checkpoints { # 1 + nodes { # 1 + transactionBlocks { # 20 + edges { # 20 + txns: node { # 400 + digest # 400 + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/consistency/checkpoints/transaction_blocks.move](../../../iota-graphql-e2e-tests/tests/consistency/checkpoints/transaction_blocks.move): + +```graphql +{ + checkpoints { + nodes { + sequenceNumber + transactionBlocks(filter: { signAddress: "@{A}"}) { + edges { + cursor + node { + digest + sender { + objects(last: 1) { + edges { + cursor + } + } + } + } + } + } + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/coinMetadata.md b/crates/iota-graphql-e2e-tests/coverage/query/coinMetadata.md new file mode 100644 index 00000000000..40324539394 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/coinMetadata.md @@ -0,0 +1,45 @@ +Query: `coinMetadata` + +```graphql +{ + coinMetadata(coinType: "@{test}::fake::FAKE") { + digest + storageRebate + bcs + decimals + name + symbol + description + iconUrl + supply + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/call/coin_metadata.move](../../../iota-graphql-e2e-tests/tests/call/coin_metadata.move): + +```graphql +//# run-graphql +{ + coinMetadata(coinType: "@{test}::fake::FAKE") { + decimals + name + symbol + description + iconUrl + supply + } +} + +//# run-graphql +{ + coinMetadata(coinType: "@{test}::fake::FAKE") { + decimals + name + symbol + description + iconUrl + supply + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/coins.md b/crates/iota-graphql-e2e-tests/coverage/query/coins.md new file mode 100644 index 00000000000..c1034e2a691 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/coins.md @@ -0,0 +1,156 @@ +Query: `type` + +```graphql +{ + coins(first: null, last: null, after: null) { + edges { + node { + address + objects { + edges { + node { + digest + storageRebate + bcs + } + } + } + balance { + coinObjectCount + totalBalance + } + balances { + edges { + node { + coinObjectCount + totalBalance + } + } + } + coins { + edges { + node { + digest + storageRebate + bcs + coinBalance + } + } + } + stakedIotas { + edges { + node { + digest + storageRebate + bcs + poolId + principal + estimatedReward + } + } + } + version + status + digest + owner { + __typename + } + previousTransactionBlock { + digest + bcs + } + storageRebate + receivedTransactionBlocks { + edges { + node { + digest + bcs + } + } + } + bcs + contents { + __typename + data + } + display { + value + error + } + dynamicField( + name: {type: "0x0000000000000000000000000000000000000000000000000000000000000001::string::String", bcs: "A2RmNQ=="} + ) { + __typename + } + dynamicObjectField( + name: {type: "0x0000000000000000000000000000000000000000000000000000000000000001::string::String", bcs: "A2RmNQ=="} + ) { + __typename + } + dynamicFields { + edges { + node { + __typename + } + } + } + coinBalance + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/objects/coin.move](../../../iota-graphql-e2e-tests/tests/objects/coin.move): + +```graphql +//# run-graphql +fragment C on Coin { + coinBalance + contents { type { repr } } +} + +{ + iotaCoins: coins { + edges { + cursor + node { ...C } + } + } + + fakeCoins: coins(type: "@{P0}::fake::FAKE") { + edges { + cursor + node { ...C } + } + } + + address(address: "@{A}") { + coins { + edges { + cursor + node { ...C } + } + } + + allBalances: balances { + edges { + cursor + node { + coinType { repr } + coinObjectCount + totalBalance + } + } + } + + firstBalance: balances(first: 1) { + edges { cursor } + } + + lastBalance: balances(last: 1) { + edges { cursor } + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/dryRunTransactionBlock.md b/crates/iota-graphql-e2e-tests/coverage/query/dryRunTransactionBlock.md new file mode 100644 index 00000000000..c02bea1a742 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/dryRunTransactionBlock.md @@ -0,0 +1,90 @@ +Query: `dryRunTransactionBlock` + +```graphql +{ + dryRunTransactionBlock( + txBytes: "AAIAIC3Pg7fIBNN95RZluYu1Ll8icKxoO/oGYoEAfLP0szGAAAjoAwAAAAAAAAICAAEBAQABAQIAAAEAAA==" + txMeta: {} + ) { + transaction { + digest + sender { + address + } + gasInput { + gasSponsor { + address + } + gasPayment { + edges { + node { + digest + storageRebate + bcs + } + } + } + gasPrice + gasBudget + } + } + error + results { + mutatedReferences { + input { + __typename + ... on Input { + ix + } + ... on Result { + cmd + ix + } + } + type { + repr + } + bcs + } + returnValues { + type { + repr + signature + layout + abilities + } + bcs + } + } + } +} +``` + +tested by [crates/iota-graphql-rpc/tests/e2e_tests.rs](../../../iota-graphql-rpc/tests/e2e_tests.rs): + +```graphql +{ + dryRunTransactionBlock(txBytes: $tx, txMeta: {}) { + results { + mutatedReferences { + input { + __typename + } + } + } + transaction { + digest + sender { + address + } + gasInput { + gasSponsor { + address + } + gasPrice + } + } + error + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/epoch.md b/crates/iota-graphql-e2e-tests/coverage/query/epoch.md new file mode 100644 index 00000000000..cd5f8245498 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/epoch.md @@ -0,0 +1,107 @@ +Query: `epoch` + +```graphql +{ + epoch(id: 1) { + epochId + referenceGasPrice + validatorSet { + totalStake + pendingActiveValidatorsId + pendingActiveValidatorsSize + stakingPoolMappingsId + stakingPoolMappingsSize + inactivePoolsId + inactivePoolsSize + validatorCandidatesId + validatorCandidatesSize + } + startTimestamp + endTimestamp + totalCheckpoints + totalTransactions + totalGasFees + totalStakeRewards + fundSize + netInflow + fundInflow + fundOutflow + protocolConfigs { + __typename + } + systemStateVersion + iotaTotalSupply + iotaTreasuryCapId + liveObjectSetDigest + checkpoints { + edges { + node { + previousCheckpointDigest + networkTotalTransactions + } + } + } + transactionBlocks { + edges { + node { + digest + bcs + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/validator/validator.move](../../../iota-graphql-e2e-tests/tests/validator/validator.move): + +```graphql +{ + epoch(id: 1) { + validatorSet { + activeValidators { + nodes { + name + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/consistency/epochs/checkpoints.move](../../../iota-graphql-e2e-tests/tests/consistency/epochs/checkpoints.move): + +```graphql +//# run-graphql --cursors {"s":3,"c":4} {"s":7,"c":8} {"s":9,"c":10} +# View checkpoints before the last checkpoint in each epoch, from the perspective of the first +# checkpoint in the next epoch. +{ + checkpoint { + sequenceNumber + } + epoch_0: epoch(id: 0) { + epochId + checkpoints(before: "@{cursor_0}") { + nodes { + sequenceNumber + } + } + } + epoch_1: epoch(id: 1) { + epochId + checkpoints(before: "@{cursor_1}") { + nodes { + sequenceNumber + } + } + } + epoch_2: epoch(id: 2) { + epochId + checkpoints(before: "@{cursor_2}") { + nodes { + sequenceNumber + } + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/events.md b/crates/iota-graphql-e2e-tests/coverage/query/events.md new file mode 100644 index 00000000000..ea0100eb520 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/events.md @@ -0,0 +1,49 @@ +Query: `events` + +```graphql +{ + events(first: null, last: null, after: null, before: null, filter: null) { + edges { + node { + __typename + data + timestamp + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/event_connection/no_filter.move](../../../iota-graphql-e2e-tests/tests/event_connection/no_filter.move): + +```graphql +//# run-graphql +{ + events { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + nodes { + json + } + } +} + +//# run-graphql --cursors {"tx":2,"e":19,"c":1} +{ + events(after: "@{cursor_0}") { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + nodes { + json + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/latestPackage.md b/crates/iota-graphql-e2e-tests/coverage/query/latestPackage.md new file mode 100644 index 00000000000..f6ccf36ae32 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/latestPackage.md @@ -0,0 +1,232 @@ +Query: `latestPackage` + +```graphql +{ + latestPackage(address: "0x1") { + address + objects { + edges { + node { + digest + storageRebate + bcs + } + } + } + balance { + coinObjectCount + totalBalance + } + balances { + edges { + node { + coinObjectCount + totalBalance + } + } + } + coins { + edges { + node { + digest + storageRebate + bcs + coinBalance + } + } + } + stakedIotas { + edges { + node { + digest + storageRebate + bcs + poolId + principal + estimatedReward + } + } + } + version + status + digest + owner { + __typename + } + previousTransactionBlock { + digest + bcs + } + storageRebate + receivedTransactionBlocks { + edges { + node { + digest + bcs + } + } + } + bcs + packageVersions { + edges { + node { + digest + storageRebate + bcs + moduleBcs + } + } + } + latestPackage { + digest + storageRebate + bcs + moduleBcs + } + module(name: "address") { + bytes + disassembly + } + modules { + edges { + node { + name + package { + digest + storageRebate + bcs + moduleBcs + } + datatype(name: "Char") { + abilities + asMoveEnum { + abilities + name + variants { + name + __typename + } + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + asMoveStruct { + abilities + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + datatypes { + edges { + node { + abilities + asMoveEnum { + abilities + name + variants { + name + __typename + } + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + asMoveStruct { + abilities + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + } + } + __typename + fileFormatVersion + bytes + disassembly + } + } + } + linkage { + version + __typename + } + typeOrigins { + __typename + } + moduleBcs + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/packages/versioning.move](../../../iota-graphql-e2e-tests/tests/packages/versioning.move): + +```graphql +//# run-graphql +{ + latestPackage(address: "@{P0}") { + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } + } + + firstPackage: package(address: "@{P0}", version: 1) { + address + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } + } + + packages(first: 10) { + nodes { + address + version + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/object.md b/crates/iota-graphql-e2e-tests/coverage/query/object.md new file mode 100644 index 00000000000..62b2c246845 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/object.md @@ -0,0 +1,165 @@ +Query: `object` + +```graphql +{ + object(address: "0x1") { + address + objects { + edges { + node { + digest + } + } + } + balance { + coinType { + repr + signature + layout + abilities + } + coinObjectCount + totalBalance + } + balances { + edges { + node { + coinObjectCount + } + } + } + coins { + edges { + node { + digest + } + } + } + stakedIotas { + edges { + node { + digest + } + } + } + version + status + digest + owner { + __typename + } + previousTransactionBlock { + digest + bcs + } + storageRebate + receivedTransactionBlocks { + edges { + node { + digest + bcs + } + } + } + bcs + dynamicField( + name: {type: "0x0000000000000000000000000000000000000000000000000000000000000001::string::String", bcs: "A2RmMQ=="} + ) { + __typename + } + dynamicObjectField( + name: {type: "0x0000000000000000000000000000000000000000000000000000000000000001::string::String", bcs: "A2RmNQ=="} + ) { + name { + bcs + } + } + dynamicFields { + edges { + node { + name { + __typename + } + } + } + } + asMoveObject { + digest + } + asMovePackage { + digest + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/call/owned_objects.move](../../../iota-graphql-e2e-tests/tests/call/owned_objects.move): + +```graphql +//# run-graphql +{ + object(address: "0x42") { + objects { + edges { + node { + owner { + __typename + ... on AddressOwner { + owner { + address + } + } + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/transactions/random.move](../../../iota-graphql-e2e-tests/tests/transactions/random.move): + +```graphql +//# run-graphql +{ + object(address: "0x8") { + address + version + asMoveObject { + contents { + type { repr } + json + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/call/dynamic_fields.move](../../../iota-graphql-e2e-tests/tests/call/dynamic_fields.move): + +```graphql +//# run-graphql +{ + object(address: "@{obj_2_0}") { + dynamicFields { + nodes { + name { + type { + repr + } + data + bcs + } + value { + ... on MoveObject { + __typename + } + ... on MoveValue { + __typename + } + } + } + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/objects.md b/crates/iota-graphql-e2e-tests/coverage/query/objects.md new file mode 100644 index 00000000000..b48d531f62a --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/objects.md @@ -0,0 +1,187 @@ +Query: `objects` + +```graphql +{ + objects(first: null, last: null, after: null, filter: null) { + edges { + node { + address + objects { + edges { + node { + digest + } + } + } + balance { + coinType { + repr + signature + layout + abilities + } + coinObjectCount + totalBalance + } + balances { + edges { + node { + coinObjectCount + } + } + } + coins { + edges { + node { + digest + } + } + } + stakedIotas { + edges { + node { + digest + } + } + } + version + status + digest + owner { + __typename + } + previousTransactionBlock { + digest + bcs + } + storageRebate + receivedTransactionBlocks { + edges { + node { + digest + bcs + } + } + } + bcs + dynamicField( + name: {type: "0x0000000000000000000000000000000000000000000000000000000000000001::string::String", bcs: "A2RmMQ=="} + ) { + __typename + } + dynamicObjectField( + name: {type: "0x0000000000000000000000000000000000000000000000000000000000000001::string::String", bcs: "A2RmNQ=="} + ) { + name { + bcs + } + } + dynamicFields { + edges { + node { + name { + __typename + } + } + } + } + asMoveObject { + digest + } + asMovePackage { + digest + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/consistency/performance/many_objects.move](../../../iota-graphql-e2e-tests/tests/consistency/performance/many_objects.move): + +```graphql +//# run-graphql +{ + last_2: objects(last: 2, filter: {type: "@{Test}"}) { + nodes { + version + asMoveObject { + owner { + ... on AddressOwner { + owner { + address + } + } + } + contents { + json + type { + repr + } + } + } + } + } + last_4_objs_owned_by_A: address(address: "@{A}") { + objects(last: 4) { + nodes { + owner { + ... on AddressOwner { + owner { + address + } + } + } + contents { + json + type { + repr + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/consistency/objects_pagination.move](../../../iota-graphql-e2e-tests/tests/consistency/objects_pagination.move): + +```graphql +//# run-graphql --cursors @{obj_6_0,2} +{ + before_obj_6_0_at_checkpoint_2: objects(filter: {type: "@{Test}"}, before: "@{cursor_0}") { + nodes { + version + asMoveObject { + contents { + type { + repr + } + json + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/coverage/query/objects.md](../../../iota-graphql-e2e-tests/coverage/query/objects.md): + +```graphql +//# run-graphql +{ + objects(filter: {type: "0x2::coin"}) { + edges { + node { + asMoveObject { + contents { + type { + repr + } + } + } + } + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/owner.md b/crates/iota-graphql-e2e-tests/coverage/query/owner.md new file mode 100644 index 00000000000..4c7236775b3 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/owner.md @@ -0,0 +1,156 @@ +Query: `owner` + +```graphql +{ + owner(address: "0x1", rootVersion: null) { + address + objects { + edges { + node { + address + } + } + } + balance { + coinType { + repr + signature + layout + abilities + } + coinObjectCount + totalBalance + } + coins { + edges { + node { + balance { + coinObjectCount + totalBalance + } + } + } + } + stakedIotas { + edges { + node { + digest + } + } + } + asAddress { + address + } + asObject { + digest + } + dynamicField( + name: {type: "0x0000000000000000000000000000000000000000000000000000000000000001::string::String", bcs: "A2RmNQ=="} + ) { + __typename + } + dynamicObjectField( + name: {type: "0x0000000000000000000000000000000000000000000000000000000000000001::string::String", bcs: "A2RmNQ=="} + ) { + __typename + } + dynamicFields { + edges { + node { + __typename + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/call/owned_objects.move](../../../iota-graphql-e2e-tests/tests/call/owned_objects.move): + +```graphql +//# run-graphql +{ + owner(address: "0x42") { + objects { + edges { + node { + owner { + __typename + ... on AddressOwner { + owner { + address + } + } + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/call/dynamic_fields.move](../../../iota-graphql-e2e-tests/tests/call/dynamic_fields.move): + +```graphql +//# run-graphql +{ + owner(address: "@{obj_2_0}") { + dynamicFields { + nodes { + name { + type { + repr + } + data + bcs + } + value { + ... on MoveObject { + __typename + } + ... on MoveValue { + bcs + data + __typename + } + } + } + } + } +} + +//# run-graphql +{ + owner(address: "@{obj_2_0}") { + dynamicField(name: {type: "u64", bcs: "AAAAAAAAAAA="}) { + name { + type { + repr + } + data + bcs + } + value { + ... on MoveValue { + __typename + bcs + data + } + } + } + } +} + +//# run-graphql +{ + owner(address: "@{obj_2_0}") { + dynamicObjectField(name: {type: "u64", bcs: "AAAAAAAAAAA="}) { + value { + ... on MoveObject { + __typename + } + } + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/package.md b/crates/iota-graphql-e2e-tests/coverage/query/package.md new file mode 100644 index 00000000000..00d014d1da7 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/package.md @@ -0,0 +1,232 @@ +Query: `package` + +```graphql +{ + package(address: "0x1") { + address + objects { + edges { + node { + digest + storageRebate + bcs + } + } + } + balance { + coinObjectCount + totalBalance + } + balances { + edges { + node { + coinObjectCount + totalBalance + } + } + } + coins { + edges { + node { + digest + storageRebate + bcs + coinBalance + } + } + } + stakedIotas { + edges { + node { + digest + storageRebate + bcs + poolId + principal + estimatedReward + } + } + } + version + status + digest + owner { + __typename + } + previousTransactionBlock { + digest + bcs + } + storageRebate + receivedTransactionBlocks { + edges { + node { + digest + bcs + } + } + } + bcs + packageVersions { + edges { + node { + digest + storageRebate + bcs + moduleBcs + } + } + } + latestPackage { + digest + storageRebate + bcs + moduleBcs + } + module(name: "address") { + bytes + disassembly + } + modules { + edges { + node { + name + package { + digest + storageRebate + bcs + moduleBcs + } + datatype(name: "Char") { + abilities + asMoveEnum { + abilities + name + variants { + name + __typename + } + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + asMoveStruct { + abilities + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + datatypes { + edges { + node { + abilities + asMoveEnum { + abilities + name + variants { + name + __typename + } + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + asMoveStruct { + abilities + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + } + } + __typename + fileFormatVersion + bytes + disassembly + } + } + } + linkage { + version + __typename + } + typeOrigins { + __typename + } + moduleBcs + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/packages/versioning.move](../../../iota-graphql-e2e-tests/tests/packages/versioning.move): + +```graphql +//# run-graphql +{ + latestPackage(address: "@{P0}") { + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } + } + + firstPackage: package(address: "@{P0}", version: 1) { + address + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } + } + + packages(first: 10) { + nodes { + address + version + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/packages.md b/crates/iota-graphql-e2e-tests/coverage/query/packages.md new file mode 100644 index 00000000000..04d1dfbf6df --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/packages.md @@ -0,0 +1,230 @@ +Query: `package` + +```graphql +{ + packages(first: null, last: 5, after: null, filter: null) { + edges { + node { + address + objects { + edges { + node { + digest + storageRebate + bcs + } + } + } + balance { + coinObjectCount + totalBalance + } + balances { + edges { + node { + coinObjectCount + totalBalance + } + } + } + coins { + edges { + node { + digest + storageRebate + bcs + coinBalance + } + } + } + stakedIotas { + edges { + node { + digest + storageRebate + bcs + poolId + principal + estimatedReward + } + } + } + version + status + digest + owner { + __typename + } + previousTransactionBlock { + digest + bcs + } + storageRebate + receivedTransactionBlocks { + edges { + node { + digest + bcs + } + } + } + bcs + packageVersions { + edges { + node { + digest + storageRebate + bcs + moduleBcs + } + } + } + latestPackage { + digest + storageRebate + bcs + moduleBcs + } + module(name: "address") { + bytes + disassembly + } + modules { + edges { + node { + name + package { + digest + storageRebate + bcs + moduleBcs + } + datatype(name: "Char") { + abilities + asMoveEnum { + abilities + name + variants { + name + __typename + } + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + asMoveStruct { + abilities + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + datatypes { + edges { + node { + abilities + asMoveEnum { + abilities + name + variants { + name + __typename + } + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + asMoveStruct { + abilities + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + name + __typename + typeParameters { + isPhantom + __typename + constraints + } + } + } + } + __typename + fileFormatVersion + bytes + disassembly + } + } + } + linkage { + version + __typename + } + typeOrigins { + __typename + } + moduleBcs + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/packages/versioning.move](../../../iota-graphql-e2e-tests/tests/packages/versioning.move): + +```graphql +//# run-graphql +{ # Querying packages with checkpoint bounds + before: packages(first: 10, filter: { beforeCheckpoint: 1 }) { + nodes { + address + version + previousTransactionBlock { + effects { checkpoint { sequenceNumber } } + } + } + } + + after: packages(first: 10, filter: { afterCheckpoint: 1 }) { + nodes { + address + version + previousTransactionBlock { + effects { checkpoint { sequenceNumber } } + } + } + } + + between: packages(first: 10, filter: { afterCheckpoint: 1, beforeCheckpoint: 3 }) { + nodes { + address + version + previousTransactionBlock { + effects { checkpoint { sequenceNumber } } + } + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/protocolConfig.md b/crates/iota-graphql-e2e-tests/coverage/query/protocolConfig.md new file mode 100644 index 00000000000..876ae677796 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/protocolConfig.md @@ -0,0 +1,59 @@ +Query: `protocolConfig` + +```graphql +{ + protocolConfig(protocolVersion: 1) { + protocolVersion + featureFlags { + key + value + __typename + } + configs { + key + value + __typename + } + config(key: "address_from_bytes_cost_base") { + key + value + __typename + } + featureFlag(key: "bridge") { + key + value + __typename + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/epoch/protocol_configs.move](../../../iota-graphql-e2e-tests/tests/epoch/protocol_configs.move): + +```graphql +//# run-graphql +{ + protocolConfig { + protocolVersion + config(key: "max_move_identifier_len") { + value + } + featureFlag(key: "bridge") { + value + } + } +} + +//# run-graphql +{ + protocolConfig(protocolVersion: 1) { + protocolVersion + config(key: "max_move_identifier_len") { + value + } + featureFlag(key: "bridge") { + value + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/serviceConfig.md b/crates/iota-graphql-e2e-tests/coverage/query/serviceConfig.md new file mode 100644 index 00000000000..97960565217 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/serviceConfig.md @@ -0,0 +1,37 @@ +Query: `serviceConfig` + +```graphql +{ + serviceConfig { + isEnabled(feature:COINS) + availableVersions + enabledFeatures + maxQueryDepth + maxQueryNodes + maxOutputNodes + maxDbQueryCost + defaultPageSize + maxPageSize + mutationTimeoutMs + requestTimeoutMs + maxQueryPayloadSize + maxTypeArgumentDepth + maxTypeArgumentWidth + maxTypeNodes + maxMoveValueDepth + maxTransactionIds + maxScanLimit + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/call/simple.move](../../../iota-graphql-e2e-tests/tests/call/simple.move): + +```graphql +//# run-graphql +{ + serviceConfig { + availableVersions + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/transactionBlock.md b/crates/iota-graphql-e2e-tests/coverage/query/transactionBlock.md new file mode 100644 index 00000000000..1273c8687c8 --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/transactionBlock.md @@ -0,0 +1,74 @@ +Query: `transactionBlock` + +```graphql +{ + transactionBlock(digest: "63X49x2QuuYNduExZWoJjfXut3s3WDWZ7Tr7nXJu32ZT") { + digest + sender { + address + balance { + coinObjectCount + totalBalance + } + balances { + edges { + node { + coinObjectCount + totalBalance + } + } + } + stakedIotas { + edges { + node { + digest + storageRebate + bcs + poolId + principal + estimatedReward + } + } + } + transactionBlocks { + edges { + node { + digest + bcs + } + } + } + } + gasInput { + gasPrice + gasBudget + } + kind { + __typename + } + signatures + effects { + status + errors + timestamp + } + expiration { + referenceGasPrice + endTimestamp + totalCheckpoints + totalTransactions + totalGasFees + totalStakeRewards + fundSize + netInflow + fundInflow + fundOutflow + systemStateVersion + iotaTotalSupply + iotaTreasuryCapId + liveObjectSetDigest + } + bcs + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/transactionBlocks.md b/crates/iota-graphql-e2e-tests/coverage/query/transactionBlocks.md new file mode 100644 index 00000000000..3fff42b23aa --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/transactionBlocks.md @@ -0,0 +1,175 @@ +Query: `transactionBlocks` + +```graphql +{ + transactionBlocks(first: null, last: null, after: null, scanLimit: null) { + edges { + node { + digest + sender { + address + balance { + coinObjectCount + totalBalance + } + balances { + edges { + node { + coinObjectCount + totalBalance + } + } + } + stakedIotas { + edges { + node { + digest + storageRebate + bcs + poolId + principal + estimatedReward + } + } + } + transactionBlocks { + edges { + node { + digest + bcs + } + } + } + } + gasInput { + gasPrice + gasBudget + } + kind { + __typename + } + signatures + effects { + status + errors + timestamp + } + expiration { + referenceGasPrice + endTimestamp + totalCheckpoints + totalTransactions + totalGasFees + totalStakeRewards + fundSize + netInflow + fundInflow + fundOutflow + systemStateVersion + iotaTotalSupply + iotaTreasuryCapId + liveObjectSetDigest + } + bcs + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/consistency/balances.move](../../../iota-graphql-e2e-tests/tests/consistency/balances.move): + +```graphql +//# run-graphql --cursors {"c":2,"t":1,"i":false} +# Emulating viewing transaction blocks at checkpoint 2. Fake coin balance should be 700. +{ + transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + nodes { + sender { + fakeCoinBalance: balance(type: "@{P0}::fake::FAKE") { + totalBalance + } + allBalances: balances { + nodes { + coinType { + repr + } + coinObjectCount + totalBalance + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/transaction_block_effects/object_changes.move](../../../iota-graphql-e2e-tests/tests/transaction_block_effects/object_changes.move): + +```graphql +//# run-graphql +{ + transactionBlocks(first: 1) { + nodes { + effects { + objectChanges { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + } + } + } + } + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.move](../../../iota-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.move): + +```graphql +//# run-graphql --cursors {"t":5,"i":false,"c":6} +# Verify that with a cursor, we are locked into a view as if we were at the checkpoint stored in +# the cursor. Compare against `without_cursor`, which should show the latest state at the actual +# latest checkpoint. There should only be 1 transaction block in the `with_cursor` query, but +# multiple in the second +{ + checkpoint { + sequenceNumber + } + with_cursor: transactionBlocks(after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { + edges { + cursor + node { + digest + sender { + objects { + edges { + cursor + } + } + } + } + } + } + without_cursor: transactionBlocks(filter: {signAddress: "@{A}"}) { + edges { + cursor + node { + digest + sender { + objects { + edges { + cursor + } + } + } + } + } + } +} +``` diff --git a/crates/iota-graphql-e2e-tests/coverage/query/type.md b/crates/iota-graphql-e2e-tests/coverage/query/type.md new file mode 100644 index 00000000000..72155a597bf --- /dev/null +++ b/crates/iota-graphql-e2e-tests/coverage/query/type.md @@ -0,0 +1,28 @@ +Query: `type` + +```graphql +{ + type(type: "vector<u64>") { + repr + signature + layout + abilities + __typename + } +} +``` + +tested by [crates/iota-graphql-e2e-tests/coverage/query/type.md](../../../iota-graphql-e2e-tests/coverage/query/type.md): + +```graphql +//# run-graphql +# Happy path -- primitive type with generic parameter + +{ + type(type: "vector<u64>") { + repr + signature + layout + } +} +``` diff --git a/crates/iota-graphql-rpc/README.md b/crates/iota-graphql-rpc/README.md index 55ee84500fb..c7dadc63929 100644 --- a/crates/iota-graphql-rpc/README.md +++ b/crates/iota-graphql-rpc/README.md @@ -125,6 +125,9 @@ To run it with the `iota start` subcommand, switch to the root directory of the ## Running tests +The crate provides test coverage for server functionality, covering client validation, query validation, reading and writing data, query limits, and health checks. +For query tests, please see the `iota-graphql-e2e-tests` crate. + To run the tests, a running postgres database is required. To do so, follow the [Indexer database setup](../iota-indexer/README.md#database-setup) to set up a database. @@ -136,3 +139,11 @@ cargo nextest run -p iota-graphql-rpc --features pg_integration --no-fail-fast - To check for compatibility with json-rpc `pnpm --filter @iota/graphql-transport test:e2e` + +## To re-generate the GraphQL schema after code changes + +In order to re-generate the GraphQL schema ([schema.graphql](schema.graphql)), run the following command: + +```sh +cargo run --bin iota-graphql-rpc generate-schema +``` From f9aa341004b77253800f64e5a4debcb4fc2f8b83 Mon Sep 17 00:00:00 2001 From: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:10:27 +0100 Subject: [PATCH 13/25] feat(CI): add Move IDE Tests workflow (#4226) * feat(CI): add Move IDE Tests workflow * don't run move-ide-test if draft * Address review comments * Run on any change in external crates, fix test command * Rename diff output to isExternalCrates --- .github/actions/diffs/action.yml | 5 + .github/workflows/_move_ide.yml | 96 +++++++++++++++++++ .github/workflows/hierarchy.yml | 10 ++ .../editors/code/scripts/create.sh | 23 +++-- 4 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/_move_ide.yml diff --git a/.github/actions/diffs/action.yml b/.github/actions/diffs/action.yml index 4e6922ba60f..ac54e779fdd 100644 --- a/.github/actions/diffs/action.yml +++ b/.github/actions/diffs/action.yml @@ -10,6 +10,9 @@ outputs: isMove: description: True when changes happened to the Move code value: "${{ steps.diff.outputs.isMove }}" + isExternalCrates: + description: True when changes happened in external crates + value: "${{ steps.diff.outputs.isExternalCrates }}" isReleaseNotesEligible: description: True when changes happened in Release Notes eligible paths value: "${{ steps.diff.outputs.isReleaseNotesEligible }}" @@ -56,6 +59,8 @@ runs: - "examples/**" - "iota_programmability/**" - ".github/workflows/_move_tests.yml" + isExternalCrates: + - "external-crates/move/crates/**" isReleaseNotesEligible: - "consensus/**" - "crates/**" diff --git a/.github/workflows/_move_ide.yml b/.github/workflows/_move_ide.yml new file mode 100644 index 00000000000..5878489c9aa --- /dev/null +++ b/.github/workflows/_move_ide.yml @@ -0,0 +1,96 @@ +name: Move IDE + +on: workflow_call + +concurrency: + group: move-ide-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + move-auto-formatter-test: + name: Move Auto-formatter Test + runs-on: [self-hosted] + + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Pin v4.1.1 + with: + ref: ${{ github.event.inputs.iota_repo_ref || github.ref }} + + - name: Setup Node + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # pin@v4.0.2 + with: + node-version: "20" + + - name: Install dependencies + working-directory: ./external-crates/move/crates/move-analyzer/prettier-plugin + run: npm install && npm i web-tree-sitter + + - name: Run npm test + working-directory: ./external-crates/move/crates/move-analyzer/prettier-plugin + shell: bash + run: npm run test + + move-ide-test: + name: Move IDE Test + runs-on: [self-hosted] + + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Pin v4.1.1 + with: + ref: ${{ github.event.inputs.iota_repo_ref || github.ref }} + + - name: Setup Node + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # pin@v4.0.2 + with: + node-version: "20" + + - name: Install dependencies and start Xvfb (emulate a display so VS Code can be started) + working-directory: ./external-crates/move/crates/move-analyzer/editors/code + run: | + sudo apt install libgtk-3-0 -y + sudo apt-get install -y xvfb x11-apps x11-xkb-utils libx11-6 libx11-xcb1 + set -eux + # Start server + /usr/bin/Xvfb :99 -screen 0 1024x768x24 & + sleep 1 + ps aux | grep Xvfb --color=always | grep -v grep + sudo add-apt-repository ppa:kisak/kisak-mesa -y + sudo apt update + sudo apt upgrade -y + npm install && npm install --save-dev @types/node @types/semver + + - name: Build move-analyzer + run: | + cargo build --bin move-analyzer + mkdir -p ~/.iota/bin + cp ./target/debug/move-analyzer* ~/.iota/bin + + - name: Run npm test + working-directory: ./external-crates/move/crates/move-analyzer/editors/code + shell: bash + run: npm run pretest && DISPLAY=:99.0 npm run test + + move-vscode-extension-build: + name: Move VSCode extension build + runs-on: [self-hosted] + + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Pin v4.1.1 + with: + ref: ${{ github.event.inputs.iota_repo_ref || github.ref }} + + - name: Setup Node + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # pin@v4.0.2 + with: + node-version: "20" + + - name: Install dependencies + working-directory: ./external-crates/move/crates/move-analyzer/editors/code + run: npm install && npm install --save-dev @types/node @types/semver + + - name: Build VSCode extension + working-directory: ./external-crates/move/crates/move-analyzer/editors/code + run: npm run package diff --git a/.github/workflows/hierarchy.yml b/.github/workflows/hierarchy.yml index 88862232da1..6456b3e033a 100644 --- a/.github/workflows/hierarchy.yml +++ b/.github/workflows/hierarchy.yml @@ -22,6 +22,7 @@ jobs: isMove: ${{ steps.diff.outputs.isMove }} isDoc: ${{ steps.diff.outputs.isDoc }} isReleaseNotesEligible: ${{ steps.diff.outputs.isReleaseNotesEligible }} + isExternalCrates: ${{ steps.diff.outputs.isExternalCrates }} isWallet: ${{ (steps.turbo.outputs.packages && contains(fromJson(steps.turbo.outputs.packages), 'iota-wallet')) }} isExplorer: ${{ (steps.turbo.outputs.packages && contains(fromJson(steps.turbo.outputs.packages), 'iota-explorer')) }} isTypescriptSDK: ${{ (steps.turbo.outputs.packages && contains(fromJson(steps.turbo.outputs.packages), '@iota/iota-sdk')) }} @@ -148,3 +149,12 @@ jobs: - typos uses: ./.github/workflows/_ledgernano.yml secrets: inherit + + move-ide: + if: (!cancelled() && !failure()) && needs.diff.outputs.isExternalCrates == 'true' && github.event.pull_request.draft == false + needs: + - diff + - dprint-format + - license-check + - typos + uses: ./.github/workflows/_move_ide.yml diff --git a/external-crates/move/crates/move-analyzer/editors/code/scripts/create.sh b/external-crates/move/crates/move-analyzer/editors/code/scripts/create.sh index a63c8adc9fa..f94940ab8fa 100755 --- a/external-crates/move/crates/move-analyzer/editors/code/scripts/create.sh +++ b/external-crates/move/crates/move-analyzer/editors/code/scripts/create.sh @@ -43,9 +43,8 @@ do fi done -# these will have to change if we want a different network/version -NETWORK="alphanet" -VERSION="0.4.0" +# these will have to change if we want a different version +VERSION="0.7.2-rc" # a map from os version identifiers in Iota's binary distribution to os version identifiers # representing VSCode's target platforms used for creating platform-specific plugin distributions @@ -55,7 +54,7 @@ SUPPORTED_OS[macos-arm64]=darwin-arm64 SUPPORTED_OS[linux-x86_64]=linux-x64 #SUPPORTED_OS[windows-x86_64]=win32-x64 -TMP_DIR=$( mktemp -d -t vscode-create ) +TMP_DIR=$( mktemp -d -t vscode-createXXX ) trap "clean_tmp_dir $TMP_DIR" EXIT LANG_SERVER_DIR="language-server" @@ -64,21 +63,21 @@ rm -rf $LANG_SERVER_DIR mkdir $LANG_SERVER_DIR for DIST_OS VSCODE_OS in "${(@kv)SUPPORTED_OS}"; do - # Iota distribution identifier - IOTA_DISTRO=$NETWORK"-v"$VERSION - # name of the Iota distribution archive file, for example iota-testnet-v1.0.0-macos-arm64.tgz - IOTA_ARCHIVE="iota-"$IOTA_DISTRO"-"$DIST_OS".tgz" + # IOTA distribution identifier + IOTA_VERSION="v"$VERSION + # name of the Iota distribution archive file, for example iota-v1.0.0-macos-arm64.tgz + IOTA_ARCHIVE="iota-"$IOTA_VERSION"-"$DIST_OS".tgz" # a path to downloaded Iota archive IOTA_ARCHIVE_PATH=$TMP_DIR"/"$IOTA_ARCHIVE - # download Iota archive file to a given location and uncompress it - curl https://github.com/iotaledger/iota/releases/download/"$IOTA_DISTRO"/"$IOTA_ARCHIVE" -L -o $IOTA_ARCHIVE_PATH + # download IOTA archive file to a given location and uncompress it + curl https://github.com/iotaledger/iota/releases/download/"$IOTA_VERSION"/"$IOTA_ARCHIVE" -L -o $IOTA_ARCHIVE_PATH tar -xf $IOTA_ARCHIVE_PATH --directory $TMP_DIR # names of the move-analyzer binary, both the one becoming part of the extension ($SERVER_BIN) # and the one in the Iota archive ($ARCHIVE_SERVER_BIN) SERVER_BIN="move-analyzer" - ARCHIVE_SERVER_BIN=$SERVER_BIN"-"$DIST_OS + ARCHIVE_SERVER_BIN=$SERVER_BIN if [[ "$DIST_OS" == *"windows"* ]]; then SERVER_BIN="$SERVER_BIN".exe ARCHIVE_SERVER_BIN="$ARCHIVE_SERVER_BIN".exe @@ -86,7 +85,7 @@ for DIST_OS VSCODE_OS in "${(@kv)SUPPORTED_OS}"; do # copy move-analyzer binary to the appropriate location where it's picked up when bundling the # extension - SRC_SERVER_BIN_LOC=$TMP_DIR"/external-crates/move/target/release/"$ARCHIVE_SERVER_BIN + SRC_SERVER_BIN_LOC=$TMP_DIR"/"$ARCHIVE_SERVER_BIN DST_SERVER_BIN_LOC=$LANG_SERVER_DIR"/"$SERVER_BIN cp $SRC_SERVER_BIN_LOC $DST_SERVER_BIN_LOC From 3ff8bf8927182923f01b239d2ced84280f48bb3a Mon Sep 17 00:00:00 2001 From: Samuel Rufinatscha <samuel.rufinatscha@iota.org> Date: Thu, 28 Nov 2024 14:33:46 +0100 Subject: [PATCH 14/25] [iota-faucet] Enable the use of batched mode on faucet (#4110) * fix: Pass at least one coin to the faucet batch queue * fix: Clippy * fix: Rework split point * fix: Enable `batch_enabled` flag for existing batch tests * fix: Add test_faucet_batch_concurrent_requests test * fix: Fix comment * fix: Remove wrong comment --- .../iota-faucet/src/faucet/simple_faucet.rs | 36 ++- crates/iota/tests/cli_tests.rs | 270 +++++++++++++++++- 2 files changed, 293 insertions(+), 13 deletions(-) diff --git a/crates/iota-faucet/src/faucet/simple_faucet.rs b/crates/iota-faucet/src/faucet/simple_faucet.rs index 1a168c35c48..eafb50d4a4a 100644 --- a/crates/iota-faucet/src/faucet/simple_faucet.rs +++ b/crates/iota-faucet/src/faucet/simple_faucet.rs @@ -119,6 +119,11 @@ impl SimpleFaucet { .map(|q| GasCoin::try_from(&q.1).unwrap()) .filter(|coin| coin.0.balance.value() >= (config.amount * config.num_coins as u64)) .collect::<Vec<GasCoin>>(); + + if coins.is_empty() { + return Err(FaucetError::NoGasCoinAvailable); + } + let metrics = FaucetMetrics::new(prometheus_registry); let wal = WriteAheadLog::open(wal_path); @@ -131,16 +136,19 @@ impl SimpleFaucet { config.max_request_queue_length as usize, ); - // This is to handle the case where there is only 1 coin, we want it to go to - // the normal queue - let split_point = if coins.len() > 10 { - coins.len() / 2 + // Split the coins eventually into two pools: one for the gas pool and one for + // the batch pool. The batch pool will only be populated if the batch feature is + // enabled. + let split_point = if config.batch_enabled { + if coins.len() > 1 { + 1 // At least one coin goes to the gas pool the rest to the batch pool + } else { + 0 // Only one coin available, all coins go to the batch pool. This is safe as we have already checked above that `coins` is not empty. + } } else { - coins.len() + coins.len() // All coins go to the gas pool if batch is disabled }; - // Put half of the coins in the old faucet impl queue, and put half in the other - // queue for batch coins. In the test cases we create an account with 5 - // coins so we just let this run with a minimum of 5 coins + for (coins_processed, coin) in coins.iter().enumerate() { let coin_id = *coin.id(); if let Some(write_ahead_log::Entry { @@ -946,6 +954,7 @@ impl Faucet for SimpleFaucet { { return Err(FaucetError::BatchSendQueueFull); } + let mut task_map = self.task_id_cache.lock().await; task_map.insert( id, @@ -1035,6 +1044,7 @@ pub async fn batch_transfer_gases( "Batch transfer attempted of size: {:?}", total_requests ); let total_iota_needed: u64 = requests.iter().flat_map(|(_, _, amounts)| amounts).sum(); + // This loop is utilized to grab a coin that is large enough for the request loop { let gas_coin_response = faucet @@ -1292,7 +1302,10 @@ mod tests { #[tokio::test] async fn test_batch_transfer_interface() { let test_cluster = TestClusterBuilder::new().build().await; - let config: FaucetConfig = Default::default(); + let config: FaucetConfig = FaucetConfig { + batch_enabled: true, + ..Default::default() + }; let coin_amount = config.amount; let prom_registry = Registry::new(); let tmp = tempfile::tempdir().unwrap(); @@ -1892,7 +1905,10 @@ mod tests { #[tokio::test] async fn test_amounts_transferred_on_batch() { let test_cluster = TestClusterBuilder::new().build().await; - let config: FaucetConfig = Default::default(); + let config: FaucetConfig = FaucetConfig { + batch_enabled: true, + ..Default::default() + }; let address = test_cluster.get_address_0(); let mut context = test_cluster.wallet; let gas_coins = context diff --git a/crates/iota/tests/cli_tests.rs b/crates/iota/tests/cli_tests.rs index 3f75f606082..aaded6cecdb 100644 --- a/crates/iota/tests/cli_tests.rs +++ b/crates/iota/tests/cli_tests.rs @@ -55,8 +55,8 @@ use iota_test_transaction_builder::batch_make_transfer_transactions; use iota_types::{ base_types::{IotaAddress, ObjectID}, crypto::{ - Ed25519IotaSignature, IotaKeyPair, IotaSignatureInner, Secp256k1IotaSignature, - SignatureScheme, get_key_pair, + AccountKeyPair, Ed25519IotaSignature, IotaKeyPair, IotaSignatureInner, + Secp256k1IotaSignature, SignatureScheme, get_key_pair, }, error::IotaObjectResponseError, gas_coin::GasCoin, @@ -4168,8 +4168,10 @@ async fn test_faucet() -> Result<(), anyhow::Error> { let wallet_config = test_cluster.swarm.dir().join(IOTA_CLIENT_CONFIG); let mut context = WalletContext::new(&wallet_config, None, None)?; + let (address, _): (_, AccountKeyPair) = get_key_pair(); + let faucet_result = IotaClientCommands::Faucet { - address: None, + address: Some(KeyIdentity::Address(address)), url: Some("http://127.0.0.1:5003/gas".to_string()), } .execute(&mut context) @@ -4180,6 +4182,268 @@ async fn test_faucet() -> Result<(), anyhow::Error> { unreachable!("Invalid response"); }; + sleep(Duration::from_secs(5)).await; + + let gas_objects_after = context + .get_gas_objects_owned_by_address(address, None) + .await + .unwrap() + .len(); + assert_eq!(gas_objects_after, 1); + + Ok(()) +} + +#[sim_test] +async fn test_faucet_batch() -> Result<(), anyhow::Error> { + let test_cluster = TestClusterBuilder::new() + .with_fullnode_rpc_port(9000) + .build() + .await; + + let context = test_cluster.wallet; + + let tmp = tempfile::tempdir().unwrap(); + let prom_registry = prometheus::Registry::new(); + let config = iota_faucet::FaucetConfig { + batch_enabled: true, + ..Default::default() + }; + + let prometheus_registry = prometheus::Registry::new(); + let app_state = std::sync::Arc::new(iota_faucet::AppState { + faucet: iota_faucet::SimpleFaucet::new( + context, + &prometheus_registry, + &tmp.path().join("faucet.wal"), + config.clone(), + ) + .await + .unwrap(), + config, + }); + tokio::spawn(async move { iota_faucet::start_faucet(app_state, 10, &prom_registry).await }); + + // Wait for the faucet to be up + sleep(Duration::from_secs(1)).await; + let wallet_config = test_cluster.swarm.dir().join(IOTA_CLIENT_CONFIG); + let mut context = WalletContext::new(&wallet_config, None, None)?; + + let (address_1, _): (_, AccountKeyPair) = get_key_pair(); + let (address_2, _): (_, AccountKeyPair) = get_key_pair(); + let (address_3, _): (_, AccountKeyPair) = get_key_pair(); + + assert_ne!(address_1, address_2); + assert_ne!(address_1, address_3); + assert_ne!(address_2, address_3); + + for address in [address_1, address_2, address_3].iter() { + let gas_objects_after = context + .get_gas_objects_owned_by_address(*address, None) + .await + .unwrap() + .len(); + assert_eq!(gas_objects_after, 0); + } + + for address in [address_1, address_2, address_3].iter() { + let faucet_result = IotaClientCommands::Faucet { + address: Some(KeyIdentity::Address(*address)), + url: Some("http://127.0.0.1:5003/v1/gas".to_string()), + } + .execute(&mut context) + .await?; + + if let IotaClientCommandResult::NoOutput = faucet_result { + } else { + unreachable!("Invalid response"); + }; + } + + // we need to wait a minimum of 10 seconds for gathering the batch + some time + // for transaction to be sequenced + sleep(Duration::from_secs(15)).await; + + for address in [address_1, address_2, address_3].iter() { + let gas_objects_after = context + .get_gas_objects_owned_by_address(*address, None) + .await + .unwrap() + .len(); + assert_eq!(gas_objects_after, 1); + } + + // try with a new batch + let (address_4, _): (_, AccountKeyPair) = get_key_pair(); + let (address_5, _): (_, AccountKeyPair) = get_key_pair(); + let (address_6, _): (_, AccountKeyPair) = get_key_pair(); + + assert_ne!(address_4, address_5); + assert_ne!(address_4, address_6); + assert_ne!(address_5, address_6); + + for address in [address_4, address_5, address_6].iter() { + let gas_objects_after = context + .get_gas_objects_owned_by_address(*address, None) + .await + .unwrap() + .len(); + assert_eq!(gas_objects_after, 0); + } + + for address in [address_4, address_5, address_6].iter() { + let faucet_result = IotaClientCommands::Faucet { + address: Some(KeyIdentity::Address(*address)), + url: Some("http://127.0.0.1:5003/v1/gas".to_string()), + } + .execute(&mut context) + .await?; + + if let IotaClientCommandResult::NoOutput = faucet_result { + } else { + unreachable!("Invalid response"); + }; + } + + // we need to wait a minimum of 10 seconds for gathering the batch + some time + // for transaction to be sequenced + sleep(Duration::from_secs(15)).await; + + for address in [address_4, address_5, address_6].iter() { + let gas_objects_after = context + .get_gas_objects_owned_by_address(*address, None) + .await + .unwrap() + .len(); + assert_eq!(gas_objects_after, 1); + } + + Ok(()) +} + +#[sim_test] +async fn test_faucet_batch_concurrent_requests() -> Result<(), anyhow::Error> { + let test_cluster = TestClusterBuilder::new() + .with_fullnode_rpc_port(9000) + .build() + .await; + + let context = test_cluster.wallet; + + let tmp = tempfile::tempdir().unwrap(); + let prom_registry = prometheus::Registry::new(); + let config = iota_faucet::FaucetConfig { + batch_enabled: true, + ..Default::default() + }; + + let prometheus_registry = prometheus::Registry::new(); + let app_state = std::sync::Arc::new(iota_faucet::AppState { + faucet: iota_faucet::SimpleFaucet::new( + context, + &prometheus_registry, + &tmp.path().join("faucet.wal"), + config.clone(), + ) + .await + .unwrap(), + config, + }); + tokio::spawn(async move { iota_faucet::start_faucet(app_state, 10, &prom_registry).await }); + + // Wait for the faucet to be up + sleep(Duration::from_secs(1)).await; + + let wallet_config = test_cluster.swarm.dir().join(IOTA_CLIENT_CONFIG); + let context = WalletContext::new(&wallet_config, None, None)?; // Use immutable context + + // Generate multiple addresses + let addresses: Vec<_> = (0..6) + .map(|_| get_key_pair::<AccountKeyPair>().0) + .collect::<Vec<IotaAddress>>(); + + // Ensure all addresses have zero gas objects initially + for address in &addresses { + assert_eq!( + context + .get_gas_objects_owned_by_address(*address, None) + .await + .unwrap() + .len(), + 0 + ); + } + + // First batch: send faucet requests concurrently for all addresses + let first_batch_results: Vec<_> = futures::future::join_all(addresses.iter().map(|address| { + let wallet_config = wallet_config.clone(); + async move { + let mut context = WalletContext::new(&wallet_config, None, None)?; // Use mutable context (for faucet requests) + IotaClientCommands::Faucet { + address: Some(KeyIdentity::Address(*address)), + url: Some("http://127.0.0.1:5003/v1/gas".to_string()), + } + .execute(&mut context) + .await + } + })) + .await; + + // Ensure all results are `NoOutput` indicating requests were batched + for result in first_batch_results { + assert!(matches!(result, Ok(IotaClientCommandResult::NoOutput))); + } + + // Wait for the first batch to complete + sleep(Duration::from_secs(15)).await; + + // Validate gas objects after the first batch + for address in &addresses { + assert_eq!( + context + .get_gas_objects_owned_by_address(*address, None) + .await + .unwrap() + .len(), + 1 + ); + } + + // Second batch: send faucet requests again for all addresses + let second_batch_results: Vec<_> = futures::future::join_all(addresses.iter().map(|address| { + let wallet_config = wallet_config.clone(); + async move { + let mut context = WalletContext::new(&wallet_config, None, None)?; // Use mutable context + IotaClientCommands::Faucet { + address: Some(KeyIdentity::Address(*address)), + url: Some("http://127.0.0.1:5003/v1/gas".to_string()), + } + .execute(&mut context) + .await + } + })) + .await; + + // Ensure all results are `NoOutput` for the second batch + for result in second_batch_results { + assert!(matches!(result, Ok(IotaClientCommandResult::NoOutput))); + } + + // Wait for the second batch to complete + sleep(Duration::from_secs(15)).await; + + // Validate gas objects after the second batch + for address in &addresses { + assert_eq!( + context + .get_gas_objects_owned_by_address(*address, None) + .await + .unwrap() + .len(), + 2 + ); + } + Ok(()) } From b627c887c0a23ae6dacbcd42b4a00d2a2f2c724d Mon Sep 17 00:00:00 2001 From: Samuel Rufinatscha <samuel.rufinatscha@iota.org> Date: Thu, 28 Nov 2024 15:09:18 +0100 Subject: [PATCH 15/25] Fix `StructTag` conversion for `iotax_queryEvents` Indexer-RPC method (#4289) * fix: convert the struct tag into a complete canonical string in order for the indexer's `MoveEventType` filter to work * fix: clippy --- crates/iota-indexer/src/indexer_reader.rs | 3 +- .../tests/rpc-tests/indexer_api.rs | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/crates/iota-indexer/src/indexer_reader.rs b/crates/iota-indexer/src/indexer_reader.rs index 3915588184f..1fa26c1969c 100644 --- a/crates/iota-indexer/src/indexer_reader.rs +++ b/crates/iota-indexer/src/indexer_reader.rs @@ -1161,7 +1161,8 @@ impl<U: R2D2Connection> IndexerReader<U> { ) } EventFilter::MoveEventType(struct_tag) => { - format!("event_type = '{}'", struct_tag) + let formatted_struct_tag = struct_tag.to_canonical_string(true); + format!("event_type = '{formatted_struct_tag}'") } EventFilter::MoveEventModule { package, module } => { let package_module_prefix = format!("{}::{}", package.to_hex_literal(), module); diff --git a/crates/iota-indexer/tests/rpc-tests/indexer_api.rs b/crates/iota-indexer/tests/rpc-tests/indexer_api.rs index c830f7ae7a7..49f4e35ff80 100644 --- a/crates/iota-indexer/tests/rpc-tests/indexer_api.rs +++ b/crates/iota-indexer/tests/rpc-tests/indexer_api.rs @@ -185,6 +185,38 @@ fn query_events_supported_events() { }); } +#[test] +fn query_validator_epoch_info_event() { + let ApiTestSetup { + runtime, + store, + client, + cluster, + } = ApiTestSetup::get_or_init(); + + runtime.block_on(async move { + indexer_wait_for_checkpoint(store, 1).await; + + cluster.force_new_epoch().await; + + let result = client.query_events(EventFilter::MoveEventType("0x0000000000000000000000000000000000000000000000000000000000000003::validator_set::ValidatorEpochInfoEventV1".parse().unwrap()), None, None, None).await; + assert!(result.is_ok()); + assert!(!result.unwrap().data.is_empty()); + + let result = client.query_events(EventFilter::MoveEventType("0x3::validator_set::ValidatorEpochInfoEventV1".parse().unwrap()), None, None, None).await; + assert!(result.is_ok()); + assert!(!result.unwrap().data.is_empty()); + + let result = client.query_events(EventFilter::MoveEventType("0x0003::validator_set::ValidatorEpochInfoEventV1".parse().unwrap()), None, None, None).await; + assert!(result.is_ok()); + assert!(!result.unwrap().data.is_empty()); + + let result = client.query_events(EventFilter::MoveEventType("0x1::validator_set::ValidatorEpochInfoEventV1".parse().unwrap()), None, None, None).await; + assert!(result.is_ok()); + assert!(result.unwrap().data.is_empty()); + }); +} + #[test] fn test_get_owned_objects() -> Result<(), anyhow::Error> { let ApiTestSetup { From baed3f70db2b26d1617489f2987ec95b85d5cd1b Mon Sep 17 00:00:00 2001 From: Thibault Martinez <thibault@iota.org> Date: Thu, 28 Nov 2024 15:43:38 +0100 Subject: [PATCH 16/25] chore(*): bump rust toolchain to 1.82 (#4235) --- crates/iota-graphql-rpc-client/src/response.rs | 2 ++ crates/iota-graphql-rpc-client/src/simple_client.rs | 2 +- crates/iota-replay/src/data_fetcher.rs | 6 ++++-- crates/iota-replay/src/types.rs | 2 +- crates/iota-source-validation/src/error.rs | 4 ++-- crates/iota-source-validation/src/lib.rs | 6 +++--- rust-toolchain.toml | 2 +- scripts/generate_files/Dockerfile | 2 +- 8 files changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/iota-graphql-rpc-client/src/response.rs b/crates/iota-graphql-rpc-client/src/response.rs index 2100a1aac94..40c24e93cf8 100644 --- a/crates/iota-graphql-rpc-client/src/response.rs +++ b/crates/iota-graphql-rpc-client/src/response.rs @@ -40,6 +40,7 @@ impl GraphqlResponse { }) } + #[allow(clippy::result_large_err)] pub fn graphql_version(&self) -> Result<String, ClientError> { Ok(self .headers @@ -90,6 +91,7 @@ impl GraphqlResponse { self.full_response.errors.clone() } + #[allow(clippy::result_large_err)] pub fn usage(&self) -> Result<Option<BTreeMap<String, u64>>, ClientError> { Ok(match self.full_response.extensions.get("usage").cloned() { Some(Value::Object(obj)) => Some( diff --git a/crates/iota-graphql-rpc-client/src/simple_client.rs b/crates/iota-graphql-rpc-client/src/simple_client.rs index 852ece48b8c..f78f8ca3753 100644 --- a/crates/iota-graphql-rpc-client/src/simple_client.rs +++ b/crates/iota-graphql-rpc-client/src/simple_client.rs @@ -117,7 +117,7 @@ impl SimpleClient { } } -#[allow(clippy::type_complexity)] +#[allow(clippy::type_complexity, clippy::result_large_err)] pub fn resolve_variables( vars: &[GraphqlQueryVariable], ) -> Result<(BTreeMap<String, String>, BTreeMap<String, Value>), ClientError> { diff --git a/crates/iota-replay/src/data_fetcher.rs b/crates/iota-replay/src/data_fetcher.rs index 4bf7a73d398..70749a03dbe 100644 --- a/crates/iota-replay/src/data_fetcher.rs +++ b/crates/iota-replay/src/data_fetcher.rs @@ -522,7 +522,7 @@ impl DataFetcher for RemoteFetcher { u64::from_str(&w["reference_gas_price"].to_string().replace('\"', "")).unwrap() } else { return Err(ReplayEngineError::UnexpectedEventFormat { - event: event.clone(), + event: Box::new(event.clone()), }); }; @@ -639,7 +639,9 @@ pub fn extract_epoch_and_version(ev: IotaEvent) -> Result<(u64, u64), ReplayEngi return Ok((epoch, version)); } - Err(ReplayEngineError::UnexpectedEventFormat { event: ev }) + Err(ReplayEngineError::UnexpectedEventFormat { + event: Box::new(ev), + }) } #[derive(Clone)] diff --git a/crates/iota-replay/src/types.rs b/crates/iota-replay/src/types.rs index 993868020a6..05c90fffe71 100644 --- a/crates/iota-replay/src/types.rs +++ b/crates/iota-replay/src/types.rs @@ -166,7 +166,7 @@ pub enum ReplayEngineError { InvalidEpochChangeTx { epoch: u64 }, #[error("Unexpected event format {:#?}", event)] - UnexpectedEventFormat { event: IotaEvent }, + UnexpectedEventFormat { event: Box<IotaEvent> }, #[error("Unable to find event for epoch {epoch}")] EventNotFound { epoch: u64 }, diff --git a/crates/iota-source-validation/src/error.rs b/crates/iota-source-validation/src/error.rs index 36cb79955fb..c9060245b5e 100644 --- a/crates/iota-source-validation/src/error.rs +++ b/crates/iota-source-validation/src/error.rs @@ -49,8 +49,8 @@ pub enum Error { module: Symbol, }, - #[error("Dependency ID contains a Iota object, not a Move package: {0}")] - ObjectFoundWhenPackageExpected(ObjectID, IotaRawMoveObject), + #[error("Dependency ID contains a Iota object, not a Move package: {}", .0.0)] + ObjectFoundWhenPackageExpected(Box<(ObjectID, IotaRawMoveObject)>), #[error("Could not deserialize on-chain dependency {address}::{module}.")] OnChainDependencyDeserializationError { diff --git a/crates/iota-source-validation/src/lib.rs b/crates/iota-source-validation/src/lib.rs index 38009f1fe89..18a3942c7cc 100644 --- a/crates/iota-source-validation/src/lib.rs +++ b/crates/iota-source-validation/src/lib.rs @@ -414,9 +414,9 @@ impl<'a> BytecodeSourceVerifier<'a> { match obj { IotaRawData::Package(pkg) => Ok(pkg), - IotaRawData::MoveObject(move_obj) => { - Err(Error::ObjectFoundWhenPackageExpected(obj_id, move_obj)) - } + IotaRawData::MoveObject(move_obj) => Err(Error::ObjectFoundWhenPackageExpected( + Box::new((obj_id, move_obj)), + )), } } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 4cef0b738ff..76fcadb5b32 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.81" +channel = "1.82" diff --git a/scripts/generate_files/Dockerfile b/scripts/generate_files/Dockerfile index 7b832715976..334aa855498 100644 --- a/scripts/generate_files/Dockerfile +++ b/scripts/generate_files/Dockerfile @@ -1,7 +1,7 @@ FROM node:20-bookworm # Accept a build argument for the Rust version -ARG RUST_VERSION=1.81 +ARG RUST_VERSION=1.82 # Get USER_ID from build-args ARG USER_ID=1000 From b6a9a71a90b2c932e6090d86a0e8f253d0faabff Mon Sep 17 00:00:00 2001 From: Thibault Martinez <thibault@iota.org> Date: Thu, 28 Nov 2024 16:54:05 +0100 Subject: [PATCH 17/25] fix(*): rustls RUSTSEC-2024-0399 (#4248) --- Cargo.lock | 38 +++++++++++++++++----------------- Cargo.toml | 2 +- crates/iota-storage/Cargo.toml | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 029a587e761..a6985400c04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -191,7 +191,7 @@ dependencies = [ "rand 0.8.5", "rcgen", "ring 0.17.8", - "rustls 0.23.13", + "rustls 0.23.18", "rustls-webpki 0.102.8", "serde", "serde_json", @@ -1625,7 +1625,7 @@ dependencies = [ "hyper 1.4.1", "hyper-util", "pin-project-lite", - "rustls 0.23.13", + "rustls 0.23.18", "rustls-pemfile 2.1.3", "rustls-pki-types", "tokio", @@ -2708,7 +2708,7 @@ dependencies = [ "quinn-proto", "rand 0.8.5", "rstest", - "rustls 0.23.13", + "rustls 0.23.18", "serde", "shared-crypto", "strum_macros 0.26.4", @@ -5436,7 +5436,7 @@ dependencies = [ "hyper 1.4.1", "hyper-util", "log", - "rustls 0.23.13", + "rustls 0.23.18", "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", @@ -7655,7 +7655,7 @@ dependencies = [ "move-core-types", "rand 0.8.5", "reqwest 0.12.7", - "rustls 0.23.13", + "rustls 0.23.18", "serde", "serde_json", "serde_with", @@ -7903,7 +7903,7 @@ dependencies = [ "percent-encoding", "prometheus", "reqwest 0.12.7", - "rustls 0.23.13", + "rustls 0.23.18", "serde", "serde_json", "tap", @@ -8023,7 +8023,7 @@ dependencies = [ "rand 0.8.5", "rcgen", "reqwest 0.12.7", - "rustls 0.23.13", + "rustls 0.23.18", "rustls-webpki 0.102.8", "tokio", "tokio-rustls 0.26.0", @@ -8510,7 +8510,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee-core", "pin-project", - "rustls 0.23.13", + "rustls 0.23.18", "rustls-pki-types", "rustls-platform-verifier", "soketto", @@ -8563,7 +8563,7 @@ dependencies = [ "hyper-util", "jsonrpsee-core", "jsonrpsee-types", - "rustls 0.23.13", + "rustls 0.23.18", "rustls-platform-verifier", "serde", "serde_json", @@ -11949,7 +11949,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.13", + "rustls 0.23.18", "socket2", "thiserror", "tokio", @@ -11966,7 +11966,7 @@ dependencies = [ "rand 0.8.5", "ring 0.17.8", "rustc-hash 2.0.0", - "rustls 0.23.13", + "rustls 0.23.18", "slab", "thiserror", "tinyvec", @@ -12397,7 +12397,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.13", + "rustls 0.23.18", "rustls-native-certs 0.7.3", "rustls-pemfile 2.1.3", "rustls-pki-types", @@ -12865,9 +12865,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" dependencies = [ "aws-lc-rs", "log", @@ -12938,9 +12938,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-platform-verifier" @@ -12953,7 +12953,7 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.13", + "rustls 0.23.18", "rustls-native-certs 0.7.3", "rustls-platform-verifier-android", "rustls-webpki 0.102.8", @@ -14790,7 +14790,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.13", + "rustls 0.23.18", "rustls-pki-types", "tokio", ] @@ -15554,7 +15554,7 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.13", + "rustls 0.23.18", "rustls-pki-types", "url", "webpki-roots 0.26.6", diff --git a/Cargo.toml b/Cargo.toml index 55279866086..d9a5cb5b76b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -302,7 +302,7 @@ reqwest = { version = "0.12", default-features = false, features = ["http2", "js roaring = "0.10.6" rocksdb = { version = "0.21.0", default-features = false, features = ["snappy", "lz4", "zstd", "zlib", "multi-threaded-cf"] } rstest = "0.16.0" -rustls = { version = "0.23", default-features = false, features = ["std", "tls12", "ring"] } +rustls = { version = "0.23.18", default-features = false, features = ["std", "tls12", "ring"] } schemars = { version = "0.8.21", features = ["either"] } scopeguard = "1.1" serde = { version = "1.0.144", features = ["derive", "rc"] } diff --git a/crates/iota-storage/Cargo.toml b/crates/iota-storage/Cargo.toml index bc6d3a66e96..fda14adfb6e 100644 --- a/crates/iota-storage/Cargo.toml +++ b/crates/iota-storage/Cargo.toml @@ -70,7 +70,7 @@ iota-types = { workspace = true, features = ["test-utils"] } [target.'cfg(msim)'.dependencies] # external dependencies axum.workspace = true -rustls = "0.23" +rustls = "0.23.18" # internal dependencies iota-simulator.workspace = true From c4bcdf9f089520b160034ec0d4b299c2d4bddc92 Mon Sep 17 00:00:00 2001 From: Thibault Martinez <thibault@iota.org> Date: Thu, 28 Nov 2024 17:02:09 +0100 Subject: [PATCH 18/25] fix(ci): labeler.yml for explorer (#4295) --- .github/labeler.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 83af79e05b4..9c85e15b9f1 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,6 +1,6 @@ -Documentation: +documentation: - changed-files: - any-glob-to-any-file: docs/content/**/* -iota-explorer: +explorer: - changed-files: - - any-glob-to-any-file: explorer/**/* + - any-glob-to-any-file: apps/explorer/**/* From 5a9a8f352b44f58a9b405794cc9f089f970ac0e3 Mon Sep 17 00:00:00 2001 From: Alexander Sporn <github@alexsporn.de> Date: Thu, 28 Nov 2024 17:39:42 +0100 Subject: [PATCH 19/25] Adapt CODEOWNERS to new team names (#4302) --- .github/CODEOWNERS | 84 +++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 45652f18036..f77a9c2b71b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,41 +1,41 @@ # Default fallback, if no other path below this matches -* @iotaledger/l1-core @iotaledger/core-node @iotaledger/core-consensus +* @iotaledger/sc-platform @iotaledger/core-protocol # Everything within the SDK folder -/sdk/ @iotaledger/boxfish @iotaledger/l1-core @iotaledger/tooling +/sdk/ @iotaledger/tooling # Changes to the genesis builder should be approved by Konstantinos or Mirko at least /crates/iota-genesis-builder/ @kodemartin @miker83z # vm-language team -/iota-execution/ @miker83z @valeriyr -/external-crates/ @miker83z @valeriyr +/iota-execution/ @iotaledger/vm-language +/external-crates/ @iotaledger/vm-language -# l1-core-infra team -/docker/ @iotaledger/l1-core-infra @iotaledger/core-node @iotaledger/devops-admin -/crates/iota-json-rpc*/ @iotaledger/l1-core-infra -/crates/iota-graphql*/ @iotaledger/l1-core-infra -/crates/iota-indexer*/ @iotaledger/l1-core-infra -/crates/iota-data-ingestion*/ @iotaledger/l1-core-infra -/crates/iota-analytics-indexer/ @iotaledger/l1-core-infra +# infrastructure team +/docker/ @iotaledger/infrastructure @iotaledger/node @iotaledger/devops-admin +/crates/iota-json-rpc*/ @iotaledger/infrastructure +/crates/iota-graphql*/ @iotaledger/infrastructure +/crates/iota-indexer*/ @iotaledger/infrastructure +/crates/iota-data-ingestion*/ @iotaledger/infrastructure +/crates/iota-analytics-indexer/ @iotaledger/infrastructure -# core-node team -/crates/iota-archival/ @iotaledger/core-node -/crates/iota-authority-aggregation/ @iotaledger/core-node @iotaledger/core-consensus -/crates/iota-config/ @iotaledger/core-node -/crates/iota-core/ @iotaledger/core-node @iotaledger/core-consensus -/crates/iota-network/ @iotaledger/core-node -/crates/iota-network-stack/ @iotaledger/core-node -/crates/iota-node/ @iotaledger/core-node -/crates/iota-types/ @iotaledger/core-node @iotaledger/l1-core -/crates/iota-protocol-config/ @iotaledger/core-node @iotaledger/l1-core -/crates/iota-protocol-config-macros/ @iotaledger/core-node @iotaledger/l1-core -/crates/iota-rest-api/ @iotaledger/core-node @iotaledger/l1-core-infra -/crates/iota-snapshot/ @iotaledger/core-node -/crates/iota-storage/ @iotaledger/core-node +# node team +/crates/iota-archival/ @iotaledger/node +/crates/iota-authority-aggregation/ @iotaledger/node @iotaledger/consensus +/crates/iota-config/ @iotaledger/node +/crates/iota-core/ @iotaledger/node @iotaledger/consensus +/crates/iota-network/ @iotaledger/node +/crates/iota-network-stack/ @iotaledger/node +/crates/iota-node/ @iotaledger/node +/crates/iota-types/ @iotaledger/node @iotaledger/sc-platform +/crates/iota-protocol-config/ @iotaledger/node @iotaledger/sc-platform +/crates/iota-protocol-config-macros/ @iotaledger/node @iotaledger/sc-platform +/crates/iota-rest-api/ @iotaledger/node @iotaledger/infrastructure +/crates/iota-snapshot/ @iotaledger/node +/crates/iota-storage/ @iotaledger/node -# core-consensus team -/consensus/ @iotaledger/core-consensus +# consensus team +/consensus/ @iotaledger/consensus # dev-tools team /crates/iota/ @iotaledger/dev-tools @@ -45,21 +45,21 @@ /crates/iota-transaction-builder/ @iotaledger/dev-tools # Frontend apps to be looked after by Boxfish Studio or the tooling team -/apps/ @iotaledger/boxfish @iotaledger/tooling -/dapps/ @iotaledger/boxfish @iotaledger/tooling -/linting/ @iotaledger/boxfish @iotaledger/tooling -/.husky/ @iotaledger/boxfish @iotaledger/tooling -/.changeset/ @iotaledger/boxfish @iotaledger/tooling -.eslintrc.js @iotaledger/boxfish @iotaledger/tooling -.lintstagedrc.json @iotaledger/boxfish @iotaledger/tooling -.npmrc @iotaledger/boxfish @iotaledger/tooling -.prettierignore @iotaledger/boxfish @iotaledger/tooling -graphql.config.ts @iotaledger/boxfish @iotaledger/tooling -package.json @iotaledger/boxfish @iotaledger/tooling -pnpm-workspace.yaml @iotaledger/boxfish @iotaledger/tooling -prettier.config.js @iotaledger/boxfish @iotaledger/tooling -turbo.json @iotaledger/boxfish @iotaledger/tooling -vercel.json @iotaledger/boxfish @iotaledger/tooling +/apps/ @iotaledger/tooling +/dapps/ @iotaledger/tooling +/linting/ @iotaledger/tooling +/.husky/ @iotaledger/tooling +/.changeset/ @iotaledger/tooling +.eslintrc.js @iotaledger/tooling +.lintstagedrc.json @iotaledger/tooling +.npmrc @iotaledger/tooling +.prettierignore @iotaledger/tooling +graphql.config.ts @iotaledger/tooling +package.json @iotaledger/tooling +pnpm-workspace.yaml @iotaledger/tooling +prettier.config.js @iotaledger/tooling +turbo.json @iotaledger/tooling +vercel.json @iotaledger/tooling # Docs and examples are for DevEx to approve upon /docs/ @iotaledger/devx From 74c388e94f284f148538be031d02a43740d8c208 Mon Sep 17 00:00:00 2001 From: Luca Moser <me+github@lucamoser.ch> Date: Thu, 28 Nov 2024 21:01:36 +0100 Subject: [PATCH 20/25] fix(docs): Typo in node docker install (#4304) Signed-off-by: Luca Moser <me@lucamoser.ch> --- nre/docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nre/docker/README.md b/nre/docker/README.md index e015e6077f6..640e94dabd3 100644 --- a/nre/docker/README.md +++ b/nre/docker/README.md @@ -7,7 +7,7 @@ Tested using: ## Prerequisites and Setup -1. Confirm you have either [Docker Engine](https://docs.docker.com/engine/install/) or [Docker Desktop](https://docs.docker.com/desktop/install/linux-install/) instllled, as well as [Docker Compose](https://github.com/docker/compose#linux). +1. Confirm you have either [Docker Engine](https://docs.docker.com/engine/install/) or [Docker Desktop](https://docs.docker.com/desktop/install/linux-install/) installed, as well as [Docker Compose](https://github.com/docker/compose#linux). 2. Update [validator.yaml](../config/validator.yaml) and place it in the same directory as `docker-compose.yaml`. From 42fbfb284929d17876e1e20cc9aba5dc1b68cefa Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 29 Nov 2024 07:32:26 +0000 Subject: [PATCH 21/25] fix(docker):iota-private-network (#4293) * fixed config * addressed dprint error --------- Co-authored-by: Hans Moog <hmoog@users.noreply.github.com> --- docker/iota-private-network/configs/genesis-template.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/iota-private-network/configs/genesis-template.yaml b/docker/iota-private-network/configs/genesis-template.yaml index 0ce6832306c..1650e388275 100644 --- a/docker/iota-private-network/configs/genesis-template.yaml +++ b/docker/iota-private-network/configs/genesis-template.yaml @@ -45,3 +45,4 @@ validator_config_info: network_address: /dns/validator-4/tcp/8080/http p2p_address: /dns/validator-4/udp/8084 stake: 20000000000000000 +migration_sources: [] From d26dc3c1f3e436df38a2ce8e403a3bd727a0cb97 Mon Sep 17 00:00:00 2001 From: Valerii Reutov <valeriy.reutov@gmail.com> Date: Fri, 29 Nov 2024 11:01:15 +0200 Subject: [PATCH 22/25] fix(move-analyzer): change the publisher name (#4294) --- .../move/crates/move-analyzer/editors/code/package.json | 2 +- .../crates/move-analyzer/editors/code/src/extension.ts | 2 +- .../crates/move-analyzer/editors/code/tests/ext.test.ts | 2 +- .../crates/move-analyzer/editors/code/tests/lsp.test.ts | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/external-crates/move/crates/move-analyzer/editors/code/package.json b/external-crates/move/crates/move-analyzer/editors/code/package.json index 0f6737c2c29..7528263217d 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/package.json +++ b/external-crates/move/crates/move-analyzer/editors/code/package.json @@ -2,7 +2,7 @@ "name": "iota-move", "displayName": "IOTA Move", "description": "A Move language integrated development environment for IOTA.", - "publisher": "iota-foundation", + "publisher": "iotaledger", "icon": "images/move.png", "license": "Apache-2.0", "version": "1.0.0", diff --git a/external-crates/move/crates/move-analyzer/editors/code/src/extension.ts b/external-crates/move/crates/move-analyzer/editors/code/src/extension.ts index a414f1827a6..b9115931b95 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/src/extension.ts +++ b/external-crates/move/crates/move-analyzer/editors/code/src/extension.ts @@ -9,7 +9,7 @@ import * as vscode from 'vscode'; /** Information related to this extension itself, such as its identifier and version. */ export class Extension { /** The string used to uniquely identify this particular extension to VS Code. */ - readonly identifier = 'iota-foundation.iota-move'; + readonly identifier = 'iotaledger.iota-move'; private readonly extension: vscode.Extension<unknown>; diff --git a/external-crates/move/crates/move-analyzer/editors/code/tests/ext.test.ts b/external-crates/move/crates/move-analyzer/editors/code/tests/ext.test.ts index 093d0b24f62..266761ed8e8 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/tests/ext.test.ts +++ b/external-crates/move/crates/move-analyzer/editors/code/tests/ext.test.ts @@ -8,7 +8,7 @@ import * as vscode from 'vscode'; Mocha.suite('ext', () => { Mocha.test('ext_exists', () => { - const ext = vscode.extensions.getExtension('iota-foundation.iota-move'); + const ext = vscode.extensions.getExtension('iotaledger.iota-move'); assert.ok(ext); }); }); diff --git a/external-crates/move/crates/move-analyzer/editors/code/tests/lsp.test.ts b/external-crates/move/crates/move-analyzer/editors/code/tests/lsp.test.ts index 0dc0512d528..f824eb30d98 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/tests/lsp.test.ts +++ b/external-crates/move/crates/move-analyzer/editors/code/tests/lsp.test.ts @@ -24,7 +24,7 @@ const PRIMITIVE_TYPES = ['u8', 'u16', 'u32', 'u64', 'u128', 'u256', 'bool', 'vec Mocha.suite('LSP', () => { Mocha.test('textDocument/documentSymbol', async () => { - const ext = vscode.extensions.getExtension('iota-foundation.iota-move'); + const ext = vscode.extensions.getExtension('iotaledger.iota-move'); assert.ok(ext); await ext.activate(); // Synchronous waiting for activation to complete @@ -69,7 +69,7 @@ Mocha.suite('LSP', () => { }); Mocha.test('textDocument/hover for definition in the same module', async () => { - const ext = vscode.extensions.getExtension('iota-foundation.iota-move'); + const ext = vscode.extensions.getExtension('iotaledger.iota-move'); assert.ok(ext); await ext.activate(); // Synchronous waiting for activation to complete @@ -108,7 +108,7 @@ Mocha.suite('LSP', () => { }); Mocha.test('textDocument/hover for definition in an external module', async () => { - const ext = vscode.extensions.getExtension('iota-foundation.iota-move'); + const ext = vscode.extensions.getExtension('iotaledger.iota-move'); assert.ok(ext); await ext.activate(); // Synchronous waiting for activation to complete @@ -147,7 +147,7 @@ Mocha.suite('LSP', () => { }); Mocha.test('textDocument/completion', async () => { - const ext = vscode.extensions.getExtension('iota-foundation.iota-move'); + const ext = vscode.extensions.getExtension('iotaledger.iota-move'); assert.ok(ext); await ext.activate(); // Synchronous waiting for activation to complete From d899a6c404871ba0e3374209ef059ba744e83a60 Mon Sep 17 00:00:00 2001 From: Mario <mario.sarcevic@iota.org> Date: Fri, 29 Nov 2024 10:50:28 +0100 Subject: [PATCH 23/25] feat(core): Add hook with logic to check if an asset is 'Transferable'. (#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 --- apps/core/src/hooks/index.ts | 1 + apps/core/src/hooks/useIsAssetTransferable.ts | 50 +++++++++++++++++++ apps/core/src/utils/index.ts | 1 - apps/core/src/utils/isAssetTransferable.ts | 10 ---- .../(protected)/assets/[objectId]/page.tsx | 7 ++- .../ui/app/pages/home/nft-details/index.tsx | 10 ++-- .../ui/app/pages/home/nft-transfer/index.tsx | 8 +-- 7 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 apps/core/src/hooks/useIsAssetTransferable.ts delete mode 100644 apps/core/src/utils/isAssetTransferable.ts diff --git a/apps/core/src/hooks/index.ts b/apps/core/src/hooks/index.ts index 2ffa7f95688..026f01c6656 100644 --- a/apps/core/src/hooks/index.ts +++ b/apps/core/src/hooks/index.ts @@ -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'; diff --git a/apps/core/src/hooks/useIsAssetTransferable.ts b/apps/core/src/hooks/useIsAssetTransferable.ts new file mode 100644 index 00000000000..7f5e75890a8 --- /dev/null +++ b/apps/core/src/hooks/useIsAssetTransferable.ts @@ -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, + }); +} diff --git a/apps/core/src/utils/index.ts b/apps/core/src/utils/index.ts index 8cbb31afa73..4d57564fa92 100644 --- a/apps/core/src/utils/index.ts +++ b/apps/core/src/utils/index.ts @@ -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'; diff --git a/apps/core/src/utils/isAssetTransferable.ts b/apps/core/src/utils/isAssetTransferable.ts deleted file mode 100644 index 4dad0779706..00000000000 --- a/apps/core/src/utils/isAssetTransferable.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { IotaObjectData } from '@iota/iota-sdk/client'; - -export const isAssetTransferable = (obj: IotaObjectData | null | undefined): boolean => - // TODO: Either the type abilities will be added to 'IotaParsedData' and - // we need to check if the object has the 'store' ability or there will be a new endpoint - // that returns the "transferable" status of a MoveType. - !!obj && obj.content?.dataType === 'moveObject'; // && obj.content.hasPublicTransfer; diff --git a/apps/wallet-dashboard/app/(protected)/assets/[objectId]/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/[objectId]/page.tsx index 7f42d41f04b..563d7a9e9c0 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/[objectId]/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/[objectId]/page.tsx @@ -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'; @@ -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(); @@ -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" /> @@ -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> diff --git a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx index ab9508a19f0..3879f492cea 100644 --- a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx @@ -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'; @@ -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); @@ -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'); @@ -241,7 +243,7 @@ function NFTDetailsPage() { ) : ( <div className="flex flex-1 items-end"> <Button - disabled={!isTransferable} + disabled={!isAssetTransferable} onClick={handleSend} text="Send" fullWidth diff --git a/apps/wallet/src/ui/app/pages/home/nft-transfer/index.tsx b/apps/wallet/src/ui/app/pages/home/nft-transfer/index.tsx index d8dcaf5af50..bae076ae8a2 100644 --- a/apps/wallet/src/ui/app/pages/home/nft-transfer/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/nft-transfer/index.tsx @@ -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 /> From 978fa075d887311ee360f6c2dc083a8194784f03 Mon Sep 17 00:00:00 2001 From: Thibault Martinez <thibault@iota.org> Date: Fri, 29 Nov 2024 12:33:25 +0100 Subject: [PATCH 24/25] fix(iota-types): elided_named_lifetimes 1.83 compiler warning (#4309) --- crates/iota-types/src/crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/iota-types/src/crypto.rs b/crates/iota-types/src/crypto.rs index e9887a20102..a78c50b3ca7 100644 --- a/crates/iota-types/src/crypto.rs +++ b/crates/iota-types/src/crypto.rs @@ -1390,7 +1390,7 @@ impl<const STRONG_THRESHOLD: bool> AuthorityQuorumSignInfo<STRONG_THRESHOLD> { pub fn authorities<'a>( &'a self, committee: &'a Committee, - ) -> impl Iterator<Item = IotaResult<&AuthorityName>> { + ) -> impl Iterator<Item = IotaResult<&'a AuthorityName>> { self.signers_map.iter().map(|i| { committee .authority_by_index(i) From 28aeefca2cb3bf4daddd9270dfd50d11547e70af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20=C3=81lvarez=20de=20la=20Cruz?= <balvarez@boxfish.studio> Date: Fri, 29 Nov 2024 13:48:21 +0100 Subject: [PATCH 25/25] fix(explorer): validator info event query (#4307) --- apps/core/src/hooks/useGetValidatorsEvents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/core/src/hooks/useGetValidatorsEvents.ts b/apps/core/src/hooks/useGetValidatorsEvents.ts index c0511d73aae..7be52ae4227 100644 --- a/apps/core/src/hooks/useGetValidatorsEvents.ts +++ b/apps/core/src/hooks/useGetValidatorsEvents.ts @@ -12,7 +12,7 @@ type GetValidatorsEvent = { // NOTE: This copies the query limit from our Rust JSON RPC backend, this needs to be kept in sync! const QUERY_MAX_RESULT_LIMIT = 50; -const VALIDATORS_EVENTS_QUERY = '0x3::validator_set::ValidatorEpochInfoEventV2'; +const VALIDATORS_EVENTS_QUERY = '0x3::validator_set::ValidatorEpochInfoEventV1'; //TODO: get validatorEvents by validator address export function useGetValidatorsEvents({ limit, order }: GetValidatorsEvent) {