Compare commits

..
Author SHA1 Message Date
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
755e5d40ca Version Packages (#6787)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-23 00:09:29 +02:00
jnsdls a2f561f97b Update team plan pricing (#6818)
# Update Team Plan Pricing

This PR updates the pricing for all team plans:

- **Starter**: Increased from $5 to $9
- **Growth**: Increased from $75 to $79
- **Accelerate**: Decreased from $250 to $249
- **Scale**: Increased from $500 to $549
2025-04-22 22:01:34 +00:00
Joaquim VergesandGitHub 72a4ac5a98 [SDK] Remove minHeight constraint from TokenSelectorScreen (#6817) 2025-04-22 14:19:45 -07:00
Jonas DanielsandGitHub 69ec6e0553 Expose rateLimitSlidingWindow strategy for low-level usage (#6815) 2025-04-22 23:11:01 +02:00
MananTank 21cc01a0aa [NEB-180] Update Nebula context selection UI, add wallet switcher (#6814)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on enhancing the integration of the `ThirdwebClient` across various components in the application, enabling better management of blockchain interactions and improving the overall architecture for network handling.

### Detailed summary
- Removed `chainId` parameter from `auth-actions.ts`.
- Updated various components to include `client` prop for `ThirdwebClient`.
- Modified `ChainIconClient` to use `src` instead of `ipfsSrc`.
- Integrated `client` in `SingleNetworkSelector`, `MultiNetworkSelector`, and other related components.
- Enhanced `NebulaLoginPage` to handle multiple chains.
- Refactored `EmptyStateChatPageContent` to support wallet management.
- Updated story files to include `client` for testing.
- Removed obsolete code related to chain ID fetching.

> The following files were skipped due to too many changes: `apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx`, `apps/dashboard/src/@/components/blocks/multi-select.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/ChatBar.tsx`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-23 00:06:34 +05:30
MananTank c03734aea9 [TOOL-4230] Dashboard: Remove add coupon UI from team billing page (#6813)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on commenting out the coupon application UI components and related functionality in the `CouponsUI` component, indicating that the feature is disabled for now and will be revisited in the future.

### Detailed summary
- Commented out the entire `Dialog` component for applying coupons.
- Commented out the `applyCouponFormSchema` and its related form handling logic.
- Disabled the submission functionality for applying coupons.
- Added comments indicating that the coupon UI will be enabled later.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-22 15:51:19 +00:00
MananTank 8da0c1fdcd [TOOL-4225] Dashboard: Show feedback on cancel transaction button click tool-4225 (#6812)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on refactoring the `CancelTransactionButton` component to use `useMutation` from `@tanstack/react-query` for handling transaction cancellation, replacing the previous notification handling with `toast` for success and error messages.

### Detailed summary
- Added `useMutation` from `@tanstack/react-query`.
- Removed `useTxNotifications` and its related logic.
- Implemented `cancelTransactionMutation` for handling the cancellation request.
- Replaced the old `onClickContinue` function with `cancelTransactions` to manage async mutation.
- Updated the button to use `cancelTransactions` and reflect loading state.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-22 13:00:22 +00:00
MananTank 8dc27ffcc4 [TOOL-4228] Dashboard: Add file upload for in-app wallet branding form tool-4228 (#6811)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `BrandingFieldset` component by introducing an `AppImageFormControl` for managing application images, while also refining the handling of branding properties in the form.

### Detailed summary
- Added `Team` type import.
- Introduced `useThirdwebClient` and `resolveSchemeWithErrorHandler`.
- Modified `BrandingFieldset` to swap `applicationName` and `applicationImageUrl` fields.
- Created `AppImageFormControl` for image upload handling.
- Integrated image upload logic with error handling using `toast`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-22 12:46:48 +00:00
Jonas DanielsandGitHub f85163edee Add engineCloud plan capability to service-utils (#6793) 2025-04-22 11:43:13 +02:00
7965d93b7a [Dashboard] Add webhook version support to Universal Bridge (#6779)
Co-authored-by: gregfromstl <[email protected]>
2025-04-21 17:46:40 -07:00
Joaquim VergesandGitHub 4ccf06260b [SDK] Fix loading state in PayEmbed source tokens (#6778) 2025-04-21 16:36:02 -07:00
Jonas DanielsandGitHub d7dc83f9e6 jonas/core-825-update-dependencies (#6782) 2025-04-21 20:53:25 +02:00
gregandGitHub 9877176f65 [SDK] Fix: update bridge tests (#6785) 2025-04-21 11:09:02 -07:00
MananTank 67768ac375 [TOOL-4208] Dashboard: Transfer tab improvements/fixes (#6784)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `TokenIdPage` components by adding `accountAddress` as a prop and updating several related functionalities to improve user experience and state management.

### Detailed summary
- Added `accountAddress` prop to `TokenIdPageClient` and `TokenIdPage`.
- Updated `Page` component to fetch `accountAddress` using `getAuthTokenWalletAddress`.
- Modified `TransferTab` to use `sendTxAndConfirm` for transaction handling.
- Updated ownership check in `useNFTDrawerTabs` to compare against `accountAddress`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-21 16:52:56 +00:00
MananTank 8937d5511e Dashboard: Fix table row not clickable in safari because of sticky thead (#6783)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the `className` property of the `<thead>` component in two files to simplify its styling.

### Detailed summary
- Updated `className` in `<thead>` component in `apps/dashboard/src/@/components/ui/table.tsx`:
  - Changed from a complex string with multiple classes to a simpler string: `"relative border-b bg-background"`.
- Applied the same change in `apps/playground-web/src/components/ui/table.tsx`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-21 16:21:38 +00:00
MananTank bf81deeb35 Dashboard: Fix Nebula card link in project FTUX (#6776)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the `href` property in the `ProjectFTUX` component to include the `projectSlug`, enhancing the link structure for better navigation within the application.

### Detailed summary
- Updated the `href` in the `ProjectFTUX` component from `/team/${props.teamSlug}/~/nebula` to `/team/${props.teamSlug}/${props.projectSlug}/nebula`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-18 23:14:25 +00:00
MananTank 25a7ad7eb9 [TOOL-4176] Dashboard: Add tracking to nebula floating chat (#6777)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR enhances the `NebulaFloatingChatButton` and related components by introducing a `pageType` prop, allowing the chat functionality to differentiate between "chain" and "contract" contexts. It also integrates tracking events for user interactions.

### Detailed summary
- Added `pageType` prop to `NebulaFloatingChatButton`, `FloatingChatContent`, and `FloatingChatContentLoggedIn`.
- Updated the `label` for the chat button to reflect the context ("chain" or "contract").
- Integrated tracking events for button clicks and message sends, capturing the `pageType`.
- Adjusted rendering logic in `FloatingChatContent` to use the new `pageType` prop.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-18 23:05:25 +00:00
MananTank ac8b09f35e Dashboard: Add team id to thirdweb client (#6775)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the usage of the `getThirdwebClient` function throughout the codebase, transitioning to using an options object that includes `jwt` and `teamId`. This change enhances client initialization consistency and improves the handling of team-related functionality.

### Detailed summary
- Updated `getThirdwebClient` to accept an options object with `jwt` and `teamId`.
- Modified several components to pass the new client structure.
- Added constants for `LAST_USED_PROJECT_ID` and `LAST_USED_TEAM_ID`.
- Enhanced various components to include `client` as a prop, improving flexibility.
- Adjusted several pages and components to handle team IDs better.
- Improved cookie management for storing and retrieving the last used team ID.
- Updated storybook examples to utilize the new client structure.

> The following files were skipped due to too many changes: `apps/dashboard/src/app/team/[team_slug]/(team)/~/usage/overview/components/SponsoredTransactionsTableUI.stories.tsx`, `apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/published-contracts.tsx`, `apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/Transactions/index.tsx`, `apps/dashboard/src/components/configure-networks/ConfigureNetworkForm.tsx`, `apps/dashboard/src/components/contract-components/contract-table/index.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx`, `apps/dashboard/src/app/(dashboard)/(bridge)/routes/components/server/routelist-card.tsx`, `apps/dashboard/src/app/(dashboard)/(bridge)/routes/components/server/routes-table.tsx`, `apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/page.tsx`, `apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/resolveAddressAndEns.tsx`, `apps/dashboard/src/app/team/components/TeamHeader/TeamSelectorMobileMenuButton.tsx`, `apps/dashboard/src/app/team/components/TeamHeader/TeamHeaderUI.stories.tsx`, `apps/dashboard/src/app/(dashboard)/contracts/deploy/[compiler_uri]/page.tsx`, `apps/dashboard/src/app/(dashboard)/(bridge)/routes/components/server/routelist-row.tsx`, `apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx`, `apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/members/TeamMembersSettingsPage.stories.tsx`, `apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/page.tsx`, `apps/dashboard/src/components/contract-components/contract-publish-form/index.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/ExecuteTransactionCard.stories.tsx`, `apps/dashboard/src/lib/wallet/nfts/alchemy.ts`, `apps/dashboard/src/lib/wallet/nfts/moralis.ts`, `apps/dashboard/src/app/(dashboard)/contracts/publish/[publish_uri]/page.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/metadata-header.tsx`, `apps/dashboard/src/app/team/components/TeamHeader/team-header-logged-in.client.tsx`, `apps/dashboard/src/components/contract-components/fetchPublishedContracts.ts`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/getModuleInstalledParams.ts`, `apps/dashboard/src/app/nebula-app/(app)/components/Chats.stories.tsx`, `apps/dashboard/src/components/explore/contract-card/index.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx`, `apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx`, `apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/ProfileUI.tsx`, `apps/dashboard/src/components/contract-components/fetch-contracts-with-versions.ts`, `apps/dashboard/src/app/team/[team_slug]/(team)/layout.tsx`, `apps/dashboard/src/@/components/blocks/Avatars/ProjectAvatar.stories.tsx`, `apps/dashboard/src/app/(dashboard)/published-contract/components/publish-based-deploy.tsx`, `apps/dashboard/src/app/team/components/TeamHeader/team-header.tsx`, `apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/Transactions/TransactionCharts.tsx`, `apps/dashboard/src/app/team/[team_slug]/[project_slug]/layout.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx`, `apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/utils/getPublishedContractsWithPublisherMapping.ts`, `apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/page.tsx`, `apps/dashboard/src/@/components/blocks/Avatars/GradientAvatar.stories.tsx`, `apps/dashboard/src/app/team/[team_slug]/[project_slug]/page.tsx`, `apps/dashboard/src/components/contract-components/hooks.ts`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-18 22:30:56 +00:00
GiandGitHub f7f179c771 updated top cta in playground (#6774) 2025-04-18 14:28:40 -07:00
MananTank d0bf3f3f5e [NEB-179] Nebula: Update context UI when context event type is received (#6770)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `ChatSidebar`, `ChatPageContent`, and `FloatingChatContent` components by adding new properties and functionality related to context management and event handling within the chat application.

### Detailed summary
- Updated `<NebulaConnectWallet />` to accept `detailsButtonClassName` prop.
- Improved `code` component to handle class names and text length conditions.
- Added new event handling for "context" in the chat API.
- Extended `ChatStreamedResponse` and `ChatStreamedEvent` types to include "context" data.
- Integrated `setContextFilters` functionality in `ChatPageContent` and `FloatingChatContent` components.
- Refactored `contextFilters` state management in `FloatingChatContent`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-18 20:48:43 +00:00
MananTank 96689c6e2a [NEB-176] Nebula: Separate auth for nebula app, TransactionButton props cleanup (#6759)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on refactoring the codebase to replace the usage of `twAccount` with `isLoggedIn` across various components, improving the handling of user authentication.

### Detailed summary
- Removed `twAccount` prop and replaced it with `isLoggedIn` in multiple components.
- Introduced new utility functions for managing Nebula authentication tokens.
- Updated related components to use the new authentication methods.
- Modified some UI elements for better clarity and consistency.

> The following files were skipped due to too many changes: `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-tab.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/royalties.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/components/table.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/primary-sale.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx`, `apps/dashboard/src/app/nebula-app/(app)/page.tsx`, `apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginServer.tsx`, `apps/dashboard/src/app/nebula-app/(app)/chat/[session_id]/page.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/proposal-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/platform-fees.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-button.tsx`, `apps/dashboard/src/components/contract-components/published-contract/index.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/claim-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/module-card.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-form.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/ExecuteTransactionCard.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/ChatPageLayout.stories.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/mint-supply-tab.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/transfer-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/royalty.stories.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/mint-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/reset-claim-eligibility.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.tsx`, `apps/dashboard/src/components/buttons/TransactionButton.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.tsx`, `apps/dashboard/src/app/layout.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/mintable.stories.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/batch-lazy-mint-button.tsx`, `apps/dashboard/src/components/contract-functions/contract-functions.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/proposal.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/FloatingChat/FloatingChat.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/FloatingChat/FloatingChatContent.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx`, `apps/dashboard/src/app/nebula-app/(app)/layout.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/OpenEditionMetadata.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/listing-drawer.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.tsx`, `apps/dashboard/src/app/login/verifyTurnstileToken.ts`, `apps/dashboard/src/components/contract-functions/contract-function.tsx`, `apps/dashboard/src/middleware.ts`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/NebulaConnectButton.tsx`, `apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginClient.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/useNftDrawerTabs.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Mintable.tsx`, `apps/dashboard/src/app/nebula-app/login/NebulaLoginPage.tsx`, `apps/dashboard/src/app/login/auth-actions.ts`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx`, `apps/dashboard/src/components/buttons/MismatchButton.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Claimable.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Royalty.tsx`, `apps/dashboard/src/app/nebula-app/login/NebulaConnectEmbedLogin.tsx`, `apps/dashboard/src/app/nebula-app/login/auth-actions.ts`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-18 20:48:43 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0c5c918b21 chore(deps): bump codecov/codecov-action from 5.4.0 to 5.4.2 (#6726)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-18 20:41:00 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e1763e216a chore(deps): bump github/codeql-action from 3.28.11 to 3.28.15 (#6725)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-18 20:40:50 +02:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
309d8f81a9 Version Packages (#6772)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-18 20:36:57 +02:00
Jonas DanielsandGitHub 9d72ddd604 Allow passing team ID explicitly via request headers (#6771) 2025-04-18 20:33:38 +02:00
MananTank 17b83686e4 Dashboard: Improve Progress bar implmentation with useTransition hook (#6769)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR refines the `useDashboardRouter` functionality by implementing a progress bar during route transitions and removing the use of `LoadingRouteHref`. It introduces `RouteStartTransitionFn` for managing transition states and modifies the `DashboardRouterTopProgressBar` to reflect these changes.

### Detailed summary
- Removed `usePathname` and `useSearchParams` from `useRouterLoadingStatus`.
- Replaced `LoadingRouteHref` with `RouteStartTransitionFn` for managing transition states.
- Updated `replace` and `push` methods in `useDashboardRouter` to use `startTransition`.
- Refactored `DashboardRouterTopProgressBarInner` to use `isRouteLoading`.
- Adjusted progress bar visibility and state management based on `isRouteLoading`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-18 16:12:06 +00:00
Joaquim VergesandGitHub 8ee427d590 Version Packages (#6767) 2025-04-18 11:34:20 +02:00
MananTank 24319f4570 [TOOL-4175] Dashboard: Add buy listing button in listing drawer in marketplace (#6757)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces the `BuyDirectListingButton` component into the `ListingDrawer` component, enhancing the marketplace functionality by allowing users to buy listings directly. It also updates documentation and adds error handling for the transaction process.

### Detailed summary
- Updated documentation for the `BuyDirectListingButton` to specify `BuyDirectListingButtonProps`.
- Imported `BuyDirectListingButton` in `listing-drawer.tsx`.
- Implemented the `BuyDirectListingButton` in the `ListingDrawer` component.
- Added error and success toast notifications for transaction outcomes.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2025-04-18 09:15:29 +00:00
Joaquim VergesandGitHub f38b262cd0 [SDK] Refactor ERC721 getNFTs to handle token ID offsets correctly (#6768) 2025-04-18 02:14:19 -07:00
jnsdls 5b35a6227b Update RPC usage charts to handle string values and improve visualization (#6766)
# Update RPC Usage Dashboard with Enhanced Visualization

This PR updates the RPC usage dashboard to improve data visualization and fix type issues:

- Changed numeric types in API response to strings for better precision
- Added peak RPS data to the rate graph for more comprehensive monitoring
- Implemented a "maxLine" feature to replace the previous maxLimit approach for clearer rate limit visualization
- Added support for both stacked and individual chart variants
- Fixed date formatting by properly handling UTC timezone with 'Z' suffix
- Updated cache key to "rpc-usage-last-24-hours:v2" to ensure fresh data after changes
- Removed unnecessary CardFooter component and simplified chart rendering logic
- Enhanced tooltip display for better readability

These changes provide users with more accurate and detailed information about their RPC usage patterns.
2025-04-18 08:55:00 +00:00
Joaquim VergesandGitHub 03e238515b [SDK] Improve NFT data fallbacks for indexer cache misses (#6765) 2025-04-18 01:49:58 -07:00
Jonas DanielsandGitHub 72875f9de5 Add support for service-to-service API key authentication (#6764) 2025-04-18 10:43:52 +02:00
jnsdls 97ce74f307 Add universal bridge landing pages to framer-rewrites (#6763)
### TL;DR

Added URL rewrites for Universal Bridge landing pages and Enterprise page.

### What changed?

Added two new URL paths to the framer-rewrites.js file:
- `/universal-bridge-regions/:region_slug` for region-specific Universal Bridge landing pages
- `/enterprise` for the Enterprise page

### How to test?

1. Navigate to `/universal-bridge-regions/[any-region-slug]` and verify the page loads correctly
2. Navigate to `/enterprise` and verify the page loads correctly
3. Ensure both pages render with the expected content and styling

### Why make this change?

These URL paths need to be included in the framer-rewrites to ensure proper routing and rendering of the Universal Bridge landing pages by region and the Enterprise page.
2025-04-18 08:06:49 +00:00
Joaquim VergesandGitHub 3a43fb23a4 [Build] Add insight package to turbo run commands (#6762) 2025-04-17 18:59:33 -07:00
389 changed files with 7004 additions and 4736 deletions
+1 -1
View File
@@ -89,7 +89,7 @@ jobs:
- run: pnpm test
- name: Code Coverage
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
with:
directory: packages/
flags: packages
+3 -3
View File
@@ -46,7 +46,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -59,7 +59,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
uses: github/codeql-action/autobuild@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -72,4 +72,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
+3
View File
@@ -58,4 +58,7 @@ module.exports = [
"/faucets",
// -- brand kit --
"/brand-kit",
// -- universal bridge landing pages --
"/universal-bridge-regions/:region_slug",
"/enterprise",
];
+31 -31
View File
@@ -29,26 +29,26 @@
"@emotion/styled": "11.14.0",
"@hookform/resolvers": "^3.9.1",
"@marsidev/react-turnstile": "^1.1.0",
"@radix-ui/react-accordion": "^1.2.4",
"@radix-ui/react-alert-dialog": "^1.1.7",
"@radix-ui/react-avatar": "^1.1.4",
"@radix-ui/react-checkbox": "^1.1.5",
"@radix-ui/react-dialog": "1.1.7",
"@radix-ui/react-dropdown-menu": "^2.1.7",
"@radix-ui/react-hover-card": "^1.1.7",
"@radix-ui/react-label": "^2.1.3",
"@radix-ui/react-popover": "^1.1.7",
"@radix-ui/react-progress": "^1.1.3",
"@radix-ui/react-radio-group": "^1.2.4",
"@radix-ui/react-select": "^2.1.7",
"@radix-ui/react-separator": "^1.1.3",
"@radix-ui/react-accordion": "^1.2.7",
"@radix-ui/react-alert-dialog": "^1.1.10",
"@radix-ui/react-avatar": "^1.1.6",
"@radix-ui/react-checkbox": "^1.2.2",
"@radix-ui/react-dialog": "1.1.10",
"@radix-ui/react-dropdown-menu": "^2.1.11",
"@radix-ui/react-hover-card": "^1.1.10",
"@radix-ui/react-label": "^2.1.4",
"@radix-ui/react-popover": "^1.1.10",
"@radix-ui/react-progress": "^1.1.4",
"@radix-ui/react-radio-group": "^1.3.3",
"@radix-ui/react-select": "^2.2.2",
"@radix-ui/react-separator": "^1.1.4",
"@radix-ui/react-slot": "^1.2.0",
"@radix-ui/react-switch": "^1.1.4",
"@radix-ui/react-tooltip": "1.2.0",
"@sentry/nextjs": "9.12.0",
"@radix-ui/react-switch": "^1.2.2",
"@radix-ui/react-tooltip": "1.2.3",
"@sentry/nextjs": "9.13.0",
"@shazow/whatsabi": "0.21.0",
"@tanstack/react-query": "5.72.1",
"@tanstack/react-table": "^8.21.2",
"@tanstack/react-query": "5.74.4",
"@tanstack/react-table": "^8.21.3",
"@thirdweb-dev/service-utils": "workspace:*",
"@vercel/functions": "2.0.0",
"@vercel/og": "^0.6.8",
@@ -66,14 +66,14 @@
"fuse.js": "7.1.0",
"idb-keyval": "^6.2.1",
"input-otp": "^1.4.1",
"ioredis": "^5.6.0",
"ioredis": "^5.6.1",
"ipaddr.js": "^2.2.0",
"lucide-react": "0.487.0",
"next": "15.2.4",
"lucide-react": "0.503.0",
"next": "15.3.1",
"next-plausible": "^3.12.4",
"next-themes": "^0.4.6",
"nextjs-toploader": "^1.6.12",
"nuqs": "^2.4.1",
"nuqs": "^2.4.3",
"p-limit": "^6.2.0",
"papaparse": "^5.5.2",
"pluralize": "^8.0.0",
@@ -89,7 +89,7 @@
"react-hook-form": "7.55.0",
"react-markdown": "^9.0.1",
"react-table": "^7.8.0",
"recharts": "2.15.2",
"recharts": "2.15.3",
"remark-gfm": "4.0.1",
"responsive-rsc": "0.0.7",
"server-only": "^0.0.1",
@@ -97,20 +97,20 @@
"sonner": "2.0.3",
"spdx-correct": "^3.2.0",
"stripe": "17.7.0",
"swagger-ui-react": "^5.20.7",
"swagger-ui-react": "^5.21.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"thirdweb": "workspace:*",
"tiny-invariant": "^1.3.3",
"use-debounce": "^10.0.4",
"zod": "3.24.2"
"zod": "3.24.3"
},
"devDependencies": {
"@chakra-ui/cli": "^2.4.1",
"@chromatic-com/storybook": "3.2.6",
"@next/bundle-analyzer": "15.2.4",
"@next/eslint-plugin-next": "15.2.4",
"@playwright/test": "1.51.1",
"@next/bundle-analyzer": "15.3.1",
"@next/eslint-plugin-next": "15.3.1",
"@playwright/test": "1.52.0",
"@storybook/addon-essentials": "8.6.12",
"@storybook/addon-interactions": "8.6.12",
"@storybook/addon-links": "8.6.12",
@@ -121,11 +121,11 @@
"@storybook/react": "8.6.12",
"@storybook/test": "8.6.12",
"@types/color": "4.2.0",
"@types/node": "22.14.0",
"@types/node": "22.14.1",
"@types/papaparse": "^5.3.15",
"@types/pluralize": "^0.0.33",
"@types/qrcode": "^1.5.5",
"@types/react": "19.1.0",
"@types/react": "19.1.2",
"@types/react-dom": "19.1.2",
"@types/react-table": "^7.7.20",
"@types/spdx-correct": "^3.1.3",
@@ -138,7 +138,7 @@
"eslint-config-biome": "1.9.4",
"eslint-plugin-react-compiler": "19.0.0-beta-e993439-20250405",
"eslint-plugin-storybook": "0.12.0",
"knip": "5.47.0",
"knip": "5.50.5",
"next-sitemap": "^4.2.3",
"postcss": "8.5.3",
"storybook": "8.6.12",
@@ -1,9 +1,8 @@
"use server";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { defineDashboardChain } from "lib/defineDashboardChain";
import { ZERO_ADDRESS, isAddress, toTokens } from "thirdweb";
import { getWalletBalance } from "thirdweb/wallets";
import { getUserThirdwebClient } from "../../app/api/lib/getAuthToken";
type BalanceQueryResponse = Array<{
balance: string;
@@ -24,6 +23,7 @@ export async function getTokenBalancesFromMoralis(params: {
error: string;
}
> {
const client = await getUserThirdwebClient();
const { contractAddress, chainId } = params;
if (!isAddress(contractAddress)) {
@@ -39,7 +39,7 @@ export async function getTokenBalancesFromMoralis(params: {
const balance = await getWalletBalance({
address: contractAddress,
chain,
client: getThirdwebClient(),
client,
});
return [
{
@@ -2,16 +2,16 @@
import {
generateAlchemyUrl,
isAlchemySupported,
transformAlchemyResponseToNFT,
} from "lib/wallet/nfts/alchemy";
import {
generateMoralisUrl,
isMoralisSupported,
transformMoralisResponseToNFT,
} from "lib/wallet/nfts/moralis";
import type { WalletNFT } from "lib/wallet/nfts/types";
import { getVercelEnv } from "../../lib/vercel-utils";
import { isAlchemySupported } from "../../lib/wallet/nfts/isAlchemySupported";
import { isMoralisSupported } from "../../lib/wallet/nfts/isMoralisSupported";
import { DASHBOARD_THIRDWEB_CLIENT_ID } from "../constants/env";
type WalletNFTApiReturn =
@@ -1,48 +0,0 @@
"use server";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { cookies } from "next/headers";
import { getAddress } from "thirdweb";
import { COOKIE_PREFIX_TOKEN } from "../constants/cookie";
import { API_SERVER_URL } from "../constants/env";
/**
* Check that the connected wallet is valid for the current active account
*/
export async function isWalletValidForActiveAccount(params: {
address: string;
account: Account;
authToken: string;
}) {
const cookieStore = await cookies();
const authCookieNameForAddress =
COOKIE_PREFIX_TOKEN + getAddress(params.address);
// authToken for this wallet address should be present
const authTokenForAddress = cookieStore.get(authCookieNameForAddress)?.value;
if (!authTokenForAddress) {
return false;
}
// this authToken should be same as current active authToken
if (authTokenForAddress !== params.authToken) {
return false;
}
// authToken should be valid
const accountRes = await fetch(`${API_SERVER_URL}/v1/account/me`, {
method: "GET",
headers: {
Authorization: `Bearer ${authTokenForAddress}`,
},
});
if (accountRes.status !== 200) {
return false;
}
const account = (await accountRes.json()).data as Account;
// the current account should match the account fetched for the authToken
return account.id === params.account.id;
}
@@ -0,0 +1,148 @@
"use server";
import { getAuthToken } from "app/api/lib/getAuthToken";
const UB_BASE_URL = process.env.NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST;
type Webhook = {
url: string;
label: string;
active: boolean;
createdAt: string;
id: string;
secret: string;
version?: number; // TODO (UB) make this mandatory after migration
};
export async function getWebhooks(props: {
clientId: string;
}) {
const authToken = await getAuthToken();
const res = await fetch(`${UB_BASE_URL}/v1/developer/webhooks`, {
method: "GET",
headers: {
"Content-Type": "application/json",
"x-client-id-override": props.clientId,
Authorization: `Bearer ${authToken}`,
},
});
if (!res.ok) {
const text = await res.text();
throw new Error(text);
}
const json = await res.json();
return json.data as Array<Webhook>;
}
export async function createWebhook(props: {
clientId: string;
version?: number;
url: string;
label: string;
secret?: string;
}) {
const authToken = await getAuthToken();
const res = await fetch(`${UB_BASE_URL}/v1/developer/webhooks`, {
method: "POST",
body: JSON.stringify({
url: props.url,
label: props.label,
version: props.version,
secret: props.secret,
}),
headers: {
"Content-Type": "application/json",
"x-client-id-override": props.clientId,
Authorization: `Bearer ${authToken}`,
},
});
if (!res.ok) {
const text = await res.text();
throw new Error(text);
}
return;
}
export async function deleteWebhook(props: {
clientId: string;
webhookId: string;
}) {
const authToken = await getAuthToken();
const res = await fetch(
`${UB_BASE_URL}/v1/developer/webhooks/${props.webhookId}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
"x-client-id-override": props.clientId,
Authorization: `Bearer ${authToken}`,
},
},
);
if (!res.ok) {
const text = await res.text();
throw new Error(text);
}
return;
}
export type Fee = {
feeRecipient: string;
feeBps: number;
createdAt: string;
updatedAt: string;
};
export async function getFees(props: {
clientId: string;
}) {
const authToken = await getAuthToken();
const res = await fetch(`${UB_BASE_URL}/v1/developer/fees`, {
method: "GET",
headers: {
"Content-Type": "application/json",
"x-client-id-override": props.clientId,
Authorization: `Bearer ${authToken}`,
},
});
if (!res.ok) {
const text = await res.text();
throw new Error(text);
}
const json = await res.json();
return json.data as Fee;
}
export async function updateFee(props: {
clientId: string;
feeRecipient: string;
feeBps: number;
}) {
const authToken = await getAuthToken();
const res = await fetch(`${UB_BASE_URL}/v1/developer/fees`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
"x-client-id-override": props.clientId,
Authorization: `Bearer ${authToken}`,
},
body: JSON.stringify({
feeRecipient: props.feeRecipient,
feeBps: props.feeBps,
}),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text);
}
return;
}
+10 -9
View File
@@ -56,19 +56,20 @@ export const fetchRPCUsage = unstable_cache(
type Last24HoursRPCUsageApiResponse = {
peakRate: {
date: string;
peakRPS: number;
peakRPS: string;
};
averageRate: {
date: string;
averageRate: number;
includedCount: number;
rateLimitedCount: number;
overageCount: number;
averageRate: string;
peakRPS: string;
includedCount: string;
rateLimitedCount: string;
overageCount: string;
}[];
totalCounts: {
includedCount: number;
rateLimitedCount: number;
overageCount: number;
includedCount: string;
rateLimitedCount: string;
overageCount: string;
};
};
@@ -106,7 +107,7 @@ export const getLast24HoursRPCUsage = unstable_cache(
data: resData.data as Last24HoursRPCUsageApiResponse,
};
},
["rpc-usage-last-24-hours"],
["rpc-usage-last-24-hours:v2"],
{
revalidate: 60, // 1 minute
},
@@ -1,7 +1,9 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { BadgeContainer } from "../../../../stories/utils";
import { getThirdwebClient } from "../../../constants/thirdweb.server";
import {
BadgeContainer,
storybookThirdwebClient,
} from "../../../../stories/utils";
import { Button } from "../../ui/button";
import { GradientAvatar } from "./GradientAvatar";
@@ -17,8 +19,6 @@ export const Variants: Story = {
args: {},
};
const client = getThirdwebClient();
function Story() {
return (
<div className="container flex max-w-6xl flex-col gap-10 py-10">
@@ -29,7 +29,7 @@ function Story() {
id={undefined}
src={undefined}
className="size-20"
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
@@ -38,7 +38,7 @@ function Story() {
id={"foo"}
src={undefined}
className="size-20"
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
@@ -47,7 +47,7 @@ function Story() {
id={"foo"}
src={""}
className="size-20"
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
@@ -56,7 +56,7 @@ function Story() {
id={"bar"}
src={""}
className="size-20"
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
@@ -65,7 +65,7 @@ function Story() {
src="invalid-src"
id={undefined}
className="size-20"
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
@@ -74,7 +74,7 @@ function Story() {
src="https://picsum.photos/200/300"
id={undefined}
className="size-20"
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
@@ -83,7 +83,7 @@ function Story() {
src="ipfs://QmZbeJYEs7kCJHyQxjxU2SJUtjSAr4m87wzJFJUyWomKdj/Smily.svg"
id={undefined}
className="size-20"
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
@@ -123,7 +123,7 @@ function ToggleTest() {
src={data?.src}
id={data?.id}
className="size-20"
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
@@ -132,7 +132,7 @@ function ToggleTest() {
className="size-20"
src={data ? "invalid-src" : undefined}
id={undefined}
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
</div>
@@ -1,7 +1,9 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { BadgeContainer } from "../../../../stories/utils";
import { getThirdwebClient } from "../../../constants/thirdweb.server";
import {
BadgeContainer,
storybookThirdwebClient,
} from "../../../../stories/utils";
import { Button } from "../../ui/button";
import { ProjectAvatar } from "./ProjectAvatar";
@@ -18,19 +20,25 @@ export const Variants: Story = {
args: {},
};
const client = getThirdwebClient();
function Story() {
return (
<div className="container flex max-w-6xl flex-col gap-10 py-10">
<p> All images below are set with size-6 className </p>
<BadgeContainer label="No Src - Skeleton">
<ProjectAvatar src={undefined} className="size-6" client={client} />
<ProjectAvatar
src={undefined}
className="size-6"
client={storybookThirdwebClient}
/>
</BadgeContainer>
<BadgeContainer label="Invalid/Empty Src - BoxIcon Fallback">
<ProjectAvatar src={""} className="size-6" client={client} />
<ProjectAvatar
src={""}
className="size-6"
client={storybookThirdwebClient}
/>
</BadgeContainer>
<ToggleTest />
@@ -65,14 +73,18 @@ function ToggleTest() {
<p> Src+Name is: {data ? "set" : "not set"} </p>
<BadgeContainer label="Valid Src">
<ProjectAvatar src={data?.src} className="size-6" client={client} />
<ProjectAvatar
src={data?.src}
className="size-6"
client={storybookThirdwebClient}
/>
</BadgeContainer>
<BadgeContainer label="invalid Src">
<ProjectAvatar
src={data ? "invalid-src" : undefined}
className="size-6"
client={client}
client={storybookThirdwebClient}
/>
</BadgeContainer>
</div>
@@ -1,6 +1,9 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { BadgeContainer } from "../../../stories/utils";
import {
BadgeContainer,
storybookThirdwebClient,
} from "../../../stories/utils";
import { MultiNetworkSelector } from "./NetworkSelectors";
const meta = {
@@ -41,6 +44,7 @@ function Variant(props: {
<BadgeContainer label={props.label}>
<MultiNetworkSelector
selectedChainIds={chainIds}
client={storybookThirdwebClient}
onChange={setChainIds}
/>
</BadgeContainer>
@@ -4,6 +4,7 @@ import { MultiSelect } from "@/components/blocks/multi-select";
import { SelectWithSearch } from "@/components/blocks/select-with-search";
import { Badge } from "@/components/ui/badge";
import { useCallback, useMemo } from "react";
import type { ThirdwebClient } from "thirdweb";
import { ChainIconClient } from "../../../components/icons/ChainIcon";
import { useAllChainsData } from "../../../hooks/chains/allChains";
@@ -20,6 +21,12 @@ export function MultiNetworkSelector(props: {
className?: string;
priorityChains?: number[];
hideTestnets?: boolean;
popoverContentClassName?: string;
customTrigger?: React.ReactNode;
align?: "center" | "start" | "end";
side?: "left" | "right" | "top" | "bottom";
showSelectedValuesInModal?: boolean;
client: ThirdwebClient;
}) {
const { allChains, idToChain } = useAllChainsData();
@@ -84,8 +91,9 @@ export function MultiNetworkSelector(props: {
<span className="flex grow gap-2 truncate text-left">
<ChainIconClient
className="size-5"
ipfsSrc={chain.icon?.url}
loading="lazy"
src={chain.icon?.url}
client={props.client}
/>
{cleanChainName(chain.name)}
</span>
@@ -99,14 +107,19 @@ export function MultiNetworkSelector(props: {
</div>
);
},
[idToChain, props.disableChainId],
[idToChain, props.disableChainId, props.client],
);
return (
<MultiSelect
searchPlaceholder="Search by Name or Chain Id"
selectedValues={props.selectedChainIds.map(String)}
popoverContentClassName={props.popoverContentClassName}
customTrigger={props.customTrigger}
options={options}
align={props.align}
side={props.side}
showSelectedValuesInModal={props.showSelectedValuesInModal}
onSelectedValuesChange={(chainIds) => {
props.onChange(chainIds.map(Number));
}}
@@ -132,6 +145,7 @@ export function SingleNetworkSelector(props: {
disableChainId?: boolean;
align?: "center" | "start" | "end";
placeholder?: string;
client: ThirdwebClient;
}) {
const { allChains, idToChain } = useAllChainsData();
@@ -179,7 +193,8 @@ export function SingleNetworkSelector(props: {
<span className="flex grow gap-2 truncate text-left">
<ChainIconClient
className="size-5"
ipfsSrc={chain.icon?.url}
src={chain.icon?.url}
client={props.client}
loading="lazy"
/>
{cleanChainName(chain.name)}
@@ -194,7 +209,7 @@ export function SingleNetworkSelector(props: {
</div>
);
},
[idToChain, props.disableChainId],
[idToChain, props.disableChainId, props.client],
);
const isLoadingChains = allChains.length === 0;
@@ -1,6 +1,9 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { BadgeContainer } from "../../../stories/utils";
import {
BadgeContainer,
storybookThirdwebClient,
} from "../../../stories/utils";
import { SingleNetworkSelector } from "./NetworkSelectors";
const meta = {
@@ -43,6 +46,7 @@ function Variant(props: {
return (
<BadgeContainer label={props.label}>
<SingleNetworkSelector
client={storybookThirdwebClient}
chainId={chainId}
onChange={setChainId}
chainIds={props.chainIds}
@@ -4,7 +4,6 @@ import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
@@ -31,18 +30,19 @@ type ThirdwebAreaChartProps<TConfig extends ChartConfig> = {
description?: string;
titleClassName?: string;
};
footer?: React.ReactNode;
customHeader?: React.ReactNode;
// chart config
config: TConfig;
data: Array<Record<keyof TConfig, number> & { time: number | string | Date }>;
showLegend?: boolean;
maxLimit?: number;
yAxis?: boolean;
xAxis?: {
sameDay?: boolean;
};
variant?: "stacked" | "individual";
// chart className
chartClassName?: string;
isPending: boolean;
@@ -77,17 +77,7 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
) : props.data.length === 0 ? (
<EmptyChartState />
) : (
<AreaChart
accessibilityLayer
data={
props.maxLimit
? props.data.map((d) => ({
...d,
maxLimit: props.maxLimit,
}))
: props.data
}
>
<AreaChart accessibilityLayer data={props.data}>
<CartesianGrid vertical={false} />
{props.yAxis && <YAxis tickLine={false} axisLine={false} />}
<XAxis
@@ -136,26 +126,28 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
</linearGradient>
))}
</defs>
{configKeys.map((key) => (
<Area
key={key}
dataKey={key}
type="natural"
fill={`url(#fill_${key})`}
fillOpacity={0.4}
stroke={`var(--color-${key})`}
stackId="a"
/>
))}
{props.maxLimit && (
<Area
type="monotone"
dataKey="maxLimit"
stroke="#ef4444"
strokeWidth={2}
strokeDasharray="5 5"
fill="none"
/>
{configKeys.map((key) =>
key === "maxLine" ? (
<Area
key={key}
type="monotone"
dataKey="maxLine"
stroke="#ef4444"
strokeWidth={2}
strokeDasharray="5 5"
fill="none"
/>
) : (
<Area
key={key}
dataKey={key}
type="natural"
fill={`url(#fill_${key})`}
fillOpacity={0.4}
stroke={`var(--color-${key})`}
stackId={props.variant !== "stacked" ? undefined : "a"}
/>
),
)}
{props.showLegend && (
@@ -167,9 +159,6 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
)}
</ChartContainer>
</CardContent>
{props.footer && (
<CardFooter className="w-full">{props.footer}</CardFooter>
)}
</Card>
);
}
@@ -47,7 +47,12 @@ interface MultiSelectProps
searchTerm: string,
) => boolean;
popoverContentClassName?: string;
customTrigger?: React.ReactNode;
renderOption?: (option: { value: string; label: string }) => React.ReactNode;
align?: "center" | "start" | "end";
side?: "left" | "right" | "top" | "bottom";
showSelectedValuesInModal?: boolean;
}
export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
@@ -62,6 +67,8 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
overrideSearchFn,
renderOption,
searchPlaceholder,
popoverContentClassName,
showSelectedValuesInModal = false,
...props
},
ref,
@@ -155,111 +162,100 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
return (
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen} modal>
<PopoverTrigger asChild>
<Button
ref={ref}
{...props}
onClick={handleTogglePopover}
className={cn(
"flex h-auto min-h-10 w-full items-center justify-between rounded-md border border-border bg-inherit p-3 hover:bg-inherit",
className,
)}
>
{selectedValues.length > 0 ? (
<div className="flex w-full justify-between">
{/* badges */}
<div className="flex flex-wrap items-center gap-1.5">
{selectedValues.slice(0, maxCount).map((value) => {
const option = options.find((o) => o.value === value);
if (!option) {
return null;
}
return (
<ClosableBadge
key={value}
label={option.label}
onClose={() => toggleOption(value)}
/>
);
})}
{/* +X more */}
{selectedValues.length > maxCount && (
<ClosableBadge
label={`+ ${selectedValues.length - maxCount} more`}
onClose={clearExtraOptions}
/>
)}
</div>
<div className="flex items-center justify-between gap-2">
{/* Clear All */}
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
{/* biome-ignore lint/a11y/useFocusableInteractive: <explanation> */}
<div
role="button"
onClick={(event) => {
event.stopPropagation();
handleClear();
}}
className="rounded p-1 hover:bg-accent"
>
<XIcon className="h-4 cursor-pointer text-muted-foreground" />
</div>
<Separator
orientation="vertical"
className="flex h-full min-h-6"
{props.customTrigger || (
<Button
ref={ref}
{...props}
onClick={handleTogglePopover}
className={cn(
"flex h-auto min-h-10 w-full items-center justify-between rounded-md border border-border bg-inherit p-3 hover:bg-inherit",
className,
)}
>
{selectedValues.length > 0 ? (
<div className="flex w-full items-center justify-between">
<SelectedChainsBadges
selectedValues={selectedValues}
options={options}
maxCount={maxCount}
onClose={handleClear}
toggleOption={toggleOption}
clearExtraOptions={clearExtraOptions}
/>
<div className="flex items-center justify-between gap-2">
{/* Clear All */}
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
{/* biome-ignore lint/a11y/useFocusableInteractive: <explanation> */}
<div
role="button"
onClick={(event) => {
event.stopPropagation();
handleClear();
}}
className="rounded p-1 hover:bg-accent"
>
<XIcon className="h-4 cursor-pointer text-muted-foreground" />
</div>
<Separator
orientation="vertical"
className="flex h-full min-h-6"
/>
<ChevronDown className="h-4 cursor-pointer text-muted-foreground" />
</div>
</div>
) : (
<div className="flex w-full items-center justify-between">
<span className="text-muted-foreground text-sm">
{placeholder}
</span>
<ChevronDown className="h-4 cursor-pointer text-muted-foreground" />
</div>
</div>
) : (
<div className="flex w-full items-center justify-between">
<span className="text-muted-foreground text-sm">
{placeholder}
</span>
<ChevronDown className="h-4 cursor-pointer text-muted-foreground" />
</div>
)}
</Button>
)}
</Button>
)}
</PopoverTrigger>
<PopoverContent
className="p-0"
align="center"
className={cn(
"flex max-h-[60vh] flex-col p-0",
popoverContentClassName,
)}
align={props.align}
side={props.side}
sideOffset={10}
onEscapeKeyDown={() => setIsPopoverOpen(false)}
style={{
width: "var(--radix-popover-trigger-width)",
maxHeight: "var(--radix-popover-content-available-height)",
height:
"calc(var(--radix-popover-content-available-height) - 40px)",
}}
ref={popoverElRef}
>
<div>
{/* Search */}
<div className="relative">
<Input
placeholder={searchPlaceholder || "Search"}
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
className="!h-auto rounded-b-none border-0 border-border border-b py-4 pl-10 focus-visible:ring-0 focus-visible:ring-offset-0"
onKeyDown={handleInputKeyDown}
/>
<SearchIcon className="-translate-y-1/2 absolute top-1/2 left-4 size-4 text-muted-foreground" />
</div>
{/* Search */}
<div className="relative">
<Input
placeholder={searchPlaceholder || "Search"}
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
className="!h-auto rounded-b-none border-0 border-border border-b py-4 pl-10 focus-visible:ring-0 focus-visible:ring-offset-0"
onKeyDown={handleInputKeyDown}
/>
<SearchIcon className="-translate-y-1/2 absolute top-1/2 left-4 size-4 text-muted-foreground" />
</div>
{optionsToShow.length === 0 && (
<div className="flex flex-1 flex-col items-center justify-center py-10 text-sm">
No results found
</div>
)}
{optionsToShow.length > 0 && (
<ScrollShadow
scrollableClassName="max-h-[min(calc(var(--radix-popover-content-available-height)-60px),350px)] p-1"
className="rounded"
scrollableClassName="p-1 h-full"
className="flex-1 rounded"
>
{/* List */}
<div>
{optionsToShow.length === 0 && (
<div className="flex justify-center py-10">
No results found
</div>
)}
{optionsToShow.map((option, i) => {
const isSelected = selectedValues.includes(option.value);
return (
@@ -293,7 +289,20 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
})}
</div>
</ScrollShadow>
</div>
)}
{showSelectedValuesInModal && selectedValues.length > 0 && (
<div className="border-t px-3 py-3">
<SelectedChainsBadges
selectedValues={selectedValues}
options={options}
maxCount={maxCount}
onClose={handleClear}
toggleOption={toggleOption}
clearExtraOptions={clearExtraOptions}
/>
</div>
)}
</PopoverContent>
</Popover>
);
@@ -319,3 +328,44 @@ function ClosableBadge(props: {
}
MultiSelect.displayName = "MultiSelect";
function SelectedChainsBadges(props: {
selectedValues: string[];
options: {
label: string;
value: string;
}[];
maxCount: number;
onClose: () => void;
toggleOption: (value: string) => void;
clearExtraOptions: () => void;
}) {
const { selectedValues, options, maxCount, toggleOption, clearExtraOptions } =
props;
return (
<div className="flex flex-wrap items-center gap-1.5">
{selectedValues.slice(0, maxCount).map((value) => {
const option = options.find((o) => o.value === value);
if (!option) {
return null;
}
return (
<ClosableBadge
key={value}
label={option.label}
onClose={() => toggleOption(value)}
/>
);
})}
{/* +X more */}
{selectedValues.length > maxCount && (
<ClosableBadge
label={`+ ${selectedValues.length - maxCount} more`}
onClose={clearExtraOptions}
/>
)}
</div>
);
}
+1 -3
View File
@@ -243,9 +243,7 @@ const ChartTooltipContent = React.forwardRef<
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">
{item.name === "maxLimit"
? "Upper Limit"
: itemConfig?.label || item.name}
{itemConfig?.label || item.name}
</span>
</div>
{item.value !== undefined && (
+1 -5
View File
@@ -24,11 +24,7 @@ const TableHeader = React.forwardRef<
>(({ className, ...props }, ref) => (
<thead
ref={ref}
className={cn(
"border-border border-b bg-background",
"sticky top-0 z-20 border-none before:absolute before:inset-0 before:border-border before:border-b",
className,
)}
className={cn("relative border-b bg-background", className)}
{...props}
/>
));
@@ -2,12 +2,16 @@ import { useQuery } from "@tanstack/react-query";
import { useMemo } from "react";
import { useActiveAccount } from "thirdweb/react";
import type { GetAuthTokenResponse } from "../../app/api/auth/get-auth-token/route";
import { LAST_USED_TEAM_ID } from "../../constants/cookies";
import { getCookie } from "../../lib/cookie";
import { getThirdwebClient } from "./thirdweb.server";
// returns a thirdweb client with optional JWT passed i
export function useThirdwebClient(jwt?: string) {
const account = useActiveAccount();
const lastUsedTeamId = getCookie(LAST_USED_TEAM_ID);
const query = useQuery({
queryKey: ["jwt", account?.address],
// only enable the query if there is an account and no JWT is passed in directly
@@ -33,8 +37,12 @@ export function useThirdwebClient(jwt?: string) {
return useMemo(
// prefer jwt from props over the one from the token query if it exists
() => getThirdwebClient(jwt || query.data),
[jwt, query.data],
() =>
getThirdwebClient({
jwt: jwt || query.data,
teamId: lastUsedTeamId,
}),
[jwt, query.data, lastUsedTeamId],
);
}
@@ -6,6 +6,7 @@ import {
import {
THIRDWEB_BUNDLER_DOMAIN,
THIRDWEB_INAPP_WALLET_DOMAIN,
THIRDWEB_INSIGHT_API_DOMAIN,
THIRDWEB_PAY_DOMAIN,
THIRDWEB_RPC_DOMAIN,
THIRDWEB_SOCIAL_API_DOMAIN,
@@ -22,7 +23,14 @@ import { getZkPaymasterData } from "thirdweb/wallets/smart";
import { getVercelEnv } from "../../lib/vercel-utils";
// returns a thirdweb client with optional JWT passed in
export function getThirdwebClient(jwt?: string) {
export function getThirdwebClient(
options:
| {
jwt: string | null | undefined;
teamId: string | undefined;
}
| undefined,
) {
if (getVercelEnv() !== "production") {
// if not on production: run this when creating a client to set the domains
setThirdwebDomains({
@@ -32,6 +40,7 @@ export function getThirdwebClient(jwt?: string) {
storage: THIRDWEB_STORAGE_DOMAIN,
social: THIRDWEB_SOCIAL_API_DOMAIN,
bundler: THIRDWEB_BUNDLER_DOMAIN,
insight: THIRDWEB_INSIGHT_API_DOMAIN,
});
}
@@ -69,7 +78,8 @@ export function getThirdwebClient(jwt?: string) {
}
return createThirdwebClient({
secretKey: jwt ? jwt : DASHBOARD_THIRDWEB_SECRET_KEY,
teamId: options?.teamId,
secretKey: options?.jwt ? options.jwt : DASHBOARD_THIRDWEB_SECRET_KEY,
clientId: DASHBOARD_THIRDWEB_CLIENT_ID,
config: {
storage: {
+31 -51
View File
@@ -1,75 +1,64 @@
"use client";
// eslint-disable-next-line no-restricted-imports
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useRouter } from "next/navigation";
import {
Suspense,
type TransitionStartFunction,
useEffect,
useMemo,
useRef,
useState,
useSyncExternalStore,
useTransition,
} from "react";
import { createStore } from "./reactive";
// Using useDashboardRouter instead of useRouter gives us a nice progress bar on top of the page when navigating using router.push or router.replace
// using a store instead of context to avoid triggering re-renders on root component
const LoadingRouteHref = createStore<string | undefined>(undefined);
const RouteStartTransitionFn = createStore<TransitionStartFunction>((fn) =>
fn(),
);
export function useDashboardRouter() {
const startTransition = useSyncExternalStore(
RouteStartTransitionFn.subscribe,
RouteStartTransitionFn.getValue,
RouteStartTransitionFn.getValue,
);
const router = useRouter();
return useMemo(() => {
return {
...router,
replace(href: string, options?: { scroll?: boolean }) {
LoadingRouteHref.setValue(href);
router.replace(href, options);
startTransition(() => {
router.replace(href, options);
});
},
push(href: string, options?: { scroll?: boolean }) {
LoadingRouteHref.setValue(href);
router.push(href, options);
startTransition(() => {
router.push(href, options);
});
},
};
}, [router]);
}
function useRouterLoadingStatus() {
const loadingHref = useSyncExternalStore(
LoadingRouteHref.subscribe,
LoadingRouteHref.getValue,
LoadingRouteHref.getValue,
);
const pathname = usePathname();
const searchParams = useSearchParams();
const searchParamsStr = searchParams?.toString();
const routerHref = pathname + (searchParamsStr ? `?${searchParamsStr}` : "");
const isLoading = loadingHref && loadingHref !== routerHref;
// reset loading on route load
// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
if (!isLoading) {
LoadingRouteHref.setValue(undefined);
}
}, [isLoading]);
return isLoading;
}, [router, startTransition]);
}
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Render this on root
function DashboardRouterTopProgressBarInner() {
const isLoading = useRouterLoadingStatus();
const [progress, setProgress] = useState(0);
export function DashboardRouterTopProgressBar() {
const [isRouteLoading, startTransition] = useTransition();
// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
RouteStartTransitionFn.setValue(startTransition);
}, []);
const [progress, setProgress] = useState(0);
const progressStartedRef = useRef(false);
// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
if (!isLoading) {
if (!isRouteLoading) {
setProgress(0);
progressStartedRef.current = false;
return;
@@ -85,7 +74,7 @@ function DashboardRouterTopProgressBarInner() {
async function updateProgressBar(progress: number, delay: number) {
if (!isMounted) {
if (!isLoading) {
if (!isRouteLoading) {
setProgress(100);
}
return;
@@ -112,14 +101,14 @@ function DashboardRouterTopProgressBarInner() {
return () => {
isMounted = false;
};
}, [isLoading]);
}, [isRouteLoading]);
const width = isLoading ? progress : 100;
const width = isRouteLoading ? progress : 100;
return (
<span
className="fixed top-0 block h-[3px] bg-foreground"
style={{
opacity: isLoading ? "100" : "0",
opacity: isRouteLoading ? "100" : "0",
width: `${width}%`,
transition: width === 0 ? "none" : "width 0.2s ease, opacity 0.3s ease",
zIndex: "100000000",
@@ -128,12 +117,3 @@ function DashboardRouterTopProgressBarInner() {
/>
);
}
// need to wrap with suspense because of useSearchParams usage
export function DashboardRouterTopProgressBar() {
return (
<Suspense fallback={null}>
<DashboardRouterTopProgressBarInner />
</Suspense>
);
}
@@ -175,6 +175,7 @@ export const CustomConnectWallet = (props: {
renderChain(props) {
return (
<CustomChainRenderer
client={thirdwebClient}
{...props}
openEditChainModal={(c) => {
setIsNetworkConfigModalOpen(true);
@@ -1,17 +1,21 @@
"use client";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { getSDKTheme } from "app/components/sdk-component-theme";
import { useV5DashboardChain } from "lib/v5-adapter";
import { useTheme } from "next-themes";
import type { ThirdwebClient } from "thirdweb";
import { PayEmbed } from "thirdweb/react";
export function UniversalBridgeEmbed({ chainId }: { chainId?: number }) {
export function UniversalBridgeEmbed({
chainId,
client,
}: { chainId?: number; client: ThirdwebClient }) {
const { theme } = useTheme();
const chain = useV5DashboardChain(chainId || 1);
return (
<PayEmbed
client={getThirdwebClient()}
client={client}
payOptions={{
mode: "fund_wallet",
prefillBuy: {
@@ -1,3 +1,4 @@
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Metadata } from "next";
import { UniversalBridgeEmbed } from "./components/client/UniversalBridgeEmbed";
@@ -18,10 +19,14 @@ export default async function RoutesPage({
searchParams,
}: { searchParams: Record<string, string | string[]> }) {
const { chainId } = searchParams;
const client = getThirdwebClient(undefined);
return (
<div className="relative mx-auto flex h-screen w-full flex-col items-center justify-center overflow-hidden border py-10">
<main className="container z-10 flex justify-center">
<UniversalBridgeEmbed chainId={chainId ? Number(chainId) : undefined} />
<UniversalBridgeEmbed
chainId={chainId ? Number(chainId) : undefined}
client={client}
/>
</main>
{/* eslint-disable-next-line @next/next/no-img-element */}
@@ -1,6 +1,6 @@
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
import type { ThirdwebClient } from "thirdweb";
import { defineChain } from "thirdweb";
import { getChainMetadata } from "thirdweb/chains";
@@ -15,6 +15,7 @@ type RouteListCardProps = {
destinationTokenIconUri?: string | null;
destinationTokenSymbol: string;
destinationTokenName: string;
client: ThirdwebClient;
};
export async function RouteListCard({
@@ -26,6 +27,7 @@ export async function RouteListCard({
destinationTokenAddress,
destinationTokenIconUri,
destinationTokenName,
client,
}: RouteListCardProps) {
const [
originChain,
@@ -40,13 +42,13 @@ export async function RouteListCard({
originTokenIconUri
? resolveSchemeWithErrorHandler({
uri: originTokenIconUri,
client: getThirdwebClient(),
client,
})
: undefined,
destinationTokenIconUri
? resolveSchemeWithErrorHandler({
uri: destinationTokenIconUri,
client: getThirdwebClient(),
client,
})
: undefined,
]);
@@ -1,7 +1,7 @@
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { TableCell, TableRow } from "@/components/ui/table";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
import type { ThirdwebClient } from "thirdweb";
import { defineChain, getChainMetadata } from "thirdweb/chains";
type RouteListRowProps = {
@@ -15,6 +15,7 @@ type RouteListRowProps = {
destinationTokenIconUri?: string | null;
destinationTokenSymbol?: string;
destinationTokenName?: string;
client: ThirdwebClient;
};
export async function RouteListRow({
@@ -26,6 +27,7 @@ export async function RouteListRow({
destinationTokenAddress,
destinationTokenIconUri,
destinationTokenSymbol,
client,
}: RouteListRowProps) {
const [
originChain,
@@ -40,13 +42,13 @@ export async function RouteListRow({
originTokenIconUri
? resolveSchemeWithErrorHandler({
uri: originTokenIconUri,
client: getThirdwebClient(),
client,
})
: undefined,
destinationTokenIconUri
? resolveSchemeWithErrorHandler({
uri: destinationTokenIconUri,
client: getThirdwebClient(),
client,
})
: undefined,
]);
@@ -6,6 +6,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Address } from "thirdweb";
import { checksumAddress } from "thirdweb/utils";
import { getRoutes } from "../../../utils";
@@ -136,6 +137,7 @@ export async function RoutesData(props: {
const startIndex = (activePage - 1) * pageSize;
const endIndex = startIndex + pageSize;
const paginatedRoutes = routesToRender.slice(startIndex, endIndex);
const client = getThirdwebClient(undefined);
return (
<>
@@ -170,6 +172,7 @@ export async function RoutesData(props: {
destinationTokenIconUri={route.destinationToken.iconUri}
destinationTokenSymbol={route.destinationToken.symbol}
destinationTokenName={route.destinationToken.name}
client={client}
/>
))}
</TableBody>
@@ -193,6 +196,7 @@ export async function RoutesData(props: {
destinationTokenIconUri={route.destinationToken.iconUri}
destinationTokenSymbol={route.destinationToken.symbol}
destinationTokenName={route.destinationToken.name}
client={client}
/>
</li>
))}
@@ -25,7 +25,6 @@ import {
} from "@/constants/env";
import { useThirdwebClient } from "@/constants/thirdweb.client";
import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { zodResolver } from "@hookform/resolvers/zod";
import { Turnstile } from "@marsidev/react-turnstile";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
@@ -52,7 +51,6 @@ import {
useWalletBalance,
} from "thirdweb/react";
import { z } from "zod";
import { isAccountOnboardingComplete } from "../../../../../../login/onboarding/isOnboardingRequired";
function formatTime(seconds: number) {
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
@@ -78,11 +76,11 @@ const claimFaucetSchema = z.object({
export function FaucetButton({
chain,
amount,
twAccount,
isLoggedIn,
}: {
chain: ChainMetadata;
amount: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const pathname = usePathname();
const client = useThirdwebClient();
@@ -186,7 +184,7 @@ export function FaucetButton({
</div>
<SendFundsToFaucetModalButton
chain={definedChain}
isLoggedIn={!!twAccount}
isLoggedIn={isLoggedIn}
client={client}
chainMeta={chain}
onFaucetRefill={() => {
@@ -198,7 +196,7 @@ export function FaucetButton({
}
// Force users to log in to claim the faucet
if (!address || !twAccount) {
if (!address || !isLoggedIn) {
return (
<Button variant="primary" className="w-full" asChild>
<Link
@@ -210,20 +208,6 @@ export function FaucetButton({
);
}
if (!isAccountOnboardingComplete(twAccount)) {
return (
<Button asChild className="w-full">
<Link
href={
pathname ? `/login?next=${encodeURIComponent(pathname)}` : "/login"
}
>
Verify your Email
</Link>
</Button>
);
}
// Can not claim
if (canClaimFaucetQuery.data && canClaimFaucetQuery.data.canClaim === false) {
return (
@@ -1,12 +1,16 @@
import { ExternalLinkIcon } from "lucide-react";
import Link from "next/link";
import type { ThirdwebClient } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { ChainIcon } from "../../../../components/server/chain-icon";
import { PayModalButton } from "../client/PayModal";
import { CreditCardIcon } from "../icons/CreditCardIcon";
import { SectionTitle } from "./SectionTitle";
export function BuyFundsSection(props: { chain: ChainMetadata }) {
export function BuyFundsSection(props: {
chain: ChainMetadata;
client: ThirdwebClient;
}) {
const sanitizedChainName = props.chain.name.replace("Mainnet", "").trim();
return (
@@ -18,6 +22,7 @@ export function BuyFundsSection(props: { chain: ChainMetadata }) {
<ChainIcon
className="-mr-2 size-12 rounded-full border p-1"
iconUrl={props.chain.icon?.url}
client={props.client}
/>
<CreditCardIcon
bg="hsl(var(--background))"
@@ -1,4 +1,4 @@
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebClient } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { getFaucetClaimAmount } from "../../../../../../api/testnet-faucet/claim/claim-amount";
import { ChainIcon } from "../../../../components/server/chain-icon";
@@ -8,9 +8,10 @@ import { SectionTitle } from "./SectionTitle";
export async function FaucetSection(props: {
chain: ChainMetadata;
twAccount: Account | undefined;
isLoggedIn: boolean;
client: ThirdwebClient;
}) {
const { chain, twAccount } = props;
const { chain, isLoggedIn, client } = props;
// Check eligibility.
const sanitizedChainName = chain.name.replace("Mainnet", "").trim();
@@ -25,6 +26,7 @@ export async function FaucetSection(props: {
<ChainIcon
className="-mr-2 size-12 rounded-full border p-1"
iconUrl={props.chain.icon?.url}
client={client}
/>
<GiftIcon bg="hsl(var(--background))" className="-ml-2 size-12" />
</div>
@@ -47,7 +49,7 @@ export async function FaucetSection(props: {
<FaucetButton
chain={chain}
amount={amountToGive}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
</div>
</div>
@@ -1,6 +1,7 @@
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import Link from "next/link";
import type { ThirdwebClient } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { mapV4ChainToV5Chain } from "../../../../../../../contexts/map-chains";
import { ChainIcon } from "../../../../components/server/chain-icon";
@@ -10,6 +11,7 @@ type ChainHeaderProps = {
headerImageUrl?: string;
logoUrl?: string;
chain: ChainMetadata;
client: ThirdwebClient;
};
// TODO: improve the behavior when clicking "Get started with thirdweb", currently just redirects to the dashboard
@@ -39,6 +41,7 @@ export function ChainHeader(props: ChainHeaderProps) {
{/* chain logo */}
<ChainIcon
client={props.client}
iconUrl={props.logoUrl}
className={cn(
"-translate-y-[50%] absolute top-0 left-0 size-20 overflow-hidden rounded-full border border-border bg-muted p-2 lg:size-36 lg:p-4",
@@ -13,13 +13,12 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { ChevronDownIcon, TicketCheckIcon } from "lucide-react";
import type { Metadata } from "next";
import Link from "next/link";
import { redirect } from "next/navigation";
import { getThirdwebClient } from "../../../../../@/constants/thirdweb.server";
import { mapV4ChainToV5Chain } from "../../../../../contexts/map-chains";
import { getRawAccount } from "../../../../account/settings/getAccount";
import {
getAuthToken,
getAuthTokenWalletAddress,
@@ -61,10 +60,9 @@ export default async function ChainPageLayout(props: {
}) {
const params = await props.params;
const { children } = props;
const [chain, authToken, account, accountAddress] = await Promise.all([
const [chain, authToken, accountAddress] = await Promise.all([
getChain(params.chain_id),
getAuthToken(),
getRawAccount(),
getAuthTokenWalletAddress(),
]);
@@ -73,7 +71,10 @@ export default async function ChainPageLayout(props: {
}
const chainMetadata = await getChainMetadata(chain.chainId);
const client = getThirdwebClient(authToken ?? undefined);
const client = getThirdwebClient({
jwt: authToken,
teamId: undefined,
});
const chainPromptPrefix = `\
You are assisting users exploring the chain ${chain.name} (Chain ID: ${chain.chainId}). Provide concise insights into the types of applications and activities prevalent on this chain, such as DeFi protocols, NFT marketplaces, or gaming platforms. Highlight notable projects or trends without delving into technical details like consensus mechanisms or gas fees.
@@ -99,8 +100,8 @@ The following is the user's message:
return (
<>
<NebulaFloatingChatButton
pageType="chain"
authToken={authToken ?? undefined}
account={account}
label="Ask AI about this chain"
client={client}
nebulaParams={{
@@ -160,6 +161,7 @@ The following is the user's message:
headerImageUrl={chainMetadata?.headerImgUrl}
logoUrl={chain.icon?.url}
chain={chain}
client={client}
/>
<div className="h-4 md:h-8" />
@@ -77,9 +77,10 @@ export default async function Image({
),
// download the chain icon if there is one
chain.icon?.url && hasWorkingChainIcon
? download({ uri: chain.icon.url, client: getThirdwebClient() }).then(
(res) => res.arrayBuffer(),
)
? download({
uri: chain.icon.url,
client: getThirdwebClient(undefined),
}).then((res) => res.arrayBuffer())
: undefined,
// download the background image (based on chain)
fetch(
@@ -1,5 +1,6 @@
import { CircleAlertIcon } from "lucide-react";
import { getRawAccount } from "../../../../account/settings/getAccount";
import { getUserThirdwebClient } from "../../../../api/lib/getAuthToken";
import { getChain, getChainMetadata } from "../../utils";
import NextSteps from "./components/client/NextSteps";
import { BuyFundsSection } from "./components/server/BuyFundsSection";
@@ -18,6 +19,7 @@ export default async function Page(props: {
const chainMetadata = await getChainMetadata(chain.chainId);
const isDeprecated = chain.status === "deprecated";
const client = await getUserThirdwebClient();
const account = await getRawAccount();
return (
@@ -42,9 +44,9 @@ export default async function Page(props: {
{/* Faucet / Buy Funds */}
{chain.testnet ? (
<FaucetSection chain={chain} twAccount={account} />
<FaucetSection chain={chain} isLoggedIn={!!account} client={client} />
) : chain.services.find((c) => c.service === "pay" && c.enabled) ? (
<BuyFundsSection chain={chain} />
<BuyFundsSection chain={chain} client={client} />
) : null}
{/* Chain Overview */}
@@ -1,6 +1,4 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { TransactionButton } from "components/buttons/TransactionButton";
import { useTrack } from "hooks/analytics/useTrack";
import { useAllChainsData } from "hooks/chains/allChains";
@@ -13,14 +11,14 @@ interface CancelTabProps {
id: string;
contract: ThirdwebContract;
isAuction?: boolean;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const CancelTab: React.FC<CancelTabProps> = ({
id,
contract,
isAuction,
twAccount,
isLoggedIn,
}) => {
const trackEvent = useTrack();
const { idToChain } = useAllChainsData();
@@ -32,7 +30,7 @@ export const CancelTab: React.FC<CancelTabProps> = ({
return (
<div className="flex flex-col gap-3 pt-3">
<TransactionButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
txChainID={contract.chain.id}
transactionCount={1}
isPending={cancelQuery.isPending}
@@ -10,20 +10,19 @@ import {
} from "@/components/ui/sheet";
import { TabButtons } from "@/components/ui/tabs";
import { ListerOnly } from "@3rdweb-sdk/react/components/roles/lister-only";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { isAlchemySupported } from "lib/wallet/nfts/alchemy";
import { isMoralisSupported } from "lib/wallet/nfts/moralis";
import { PlusIcon } from "lucide-react";
import { useState } from "react";
import type { ThirdwebContract } from "thirdweb";
import { useActiveAccount } from "thirdweb/react";
import { isAlchemySupported } from "../../../../../../../lib/wallet/nfts/isAlchemySupported";
import { isMoralisSupported } from "../../../../../../../lib/wallet/nfts/isMoralisSupported";
import { CreateListingsForm } from "./list-form";
interface CreateListingButtonProps {
contract: ThirdwebContract;
createText?: string;
type?: "direct-listings" | "english-auctions";
twAccount: Account | undefined;
isLoggedIn: boolean;
isInsightSupported: boolean;
}
@@ -33,7 +32,7 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
createText = "Create",
type,
contract,
twAccount,
isLoggedIn,
isInsightSupported,
...restButtonProps
}) => {
@@ -77,7 +76,7 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
/>
<div className="mt-5">
<CreateListingsForm
twAccount={twAccount}
isLoggedIn={isLoggedIn}
contract={contract}
type={type}
actionText={createText}
@@ -90,7 +89,7 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
) : (
<div className="mt-5">
<CreateListingsForm
twAccount={twAccount}
isLoggedIn={isLoggedIn}
contract={contract}
type={type}
actionText={createText}
@@ -1,7 +1,6 @@
import { Alert, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { useDashboardOwnedNFTs } from "@3rdweb-sdk/react/hooks/useDashboardOwnedNFTs";
import { useWalletNFTs } from "@3rdweb-sdk/react/hooks/useWalletNFTs";
import {
@@ -19,8 +18,6 @@ import { SolidityInput } from "contract-ui/components/solidity-inputs";
import { useTrack } from "hooks/analytics/useTrack";
import { useAllChainsData } from "hooks/chains/allChains";
import { useTxNotifications } from "hooks/useTxNotifications";
import { isAlchemySupported } from "lib/wallet/nfts/alchemy";
import { isMoralisSupported } from "lib/wallet/nfts/moralis";
import type { WalletNFT } from "lib/wallet/nfts/types";
import { CircleAlertIcon, InfoIcon } from "lucide-react";
import Link from "next/link";
@@ -57,6 +54,8 @@ import { shortenAddress } from "thirdweb/utils";
import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components";
import { NFTMediaWithEmptyState } from "tw-components/nft-media";
import { shortenIfAddress } from "utils/usedapp-external";
import { isAlchemySupported } from "../../../../../../../lib/wallet/nfts/isAlchemySupported";
import { isMoralisSupported } from "../../../../../../../lib/wallet/nfts/isMoralisSupported";
const LIST_FORM_ID = "marketplace-list-form";
@@ -85,7 +84,7 @@ type CreateListingsFormProps = {
setOpen: Dispatch<SetStateAction<boolean>>;
mode: "automatic" | "manual";
type?: "direct-listings" | "english-auctions";
twAccount: Account | undefined;
isLoggedIn: boolean;
isInsightSupported: boolean;
};
@@ -104,7 +103,7 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
type,
actionText,
setOpen,
twAccount,
isLoggedIn,
mode,
isInsightSupported,
}) => {
@@ -659,7 +658,7 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
Cancel
</Button>
<TransactionButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
txChainID={contract.chain.id}
isPending={isFormLoading}
transactionCount={2}
@@ -3,17 +3,16 @@ import { CopyAddressButton } from "@/components/ui/CopyAddressButton";
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { Badge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card";
import { CodeClient } from "@/components/ui/code/code.client";
import { Sheet, SheetContent } from "@/components/ui/sheet";
import { Flex, GridItem, SimpleGrid, usePrevious } from "@chakra-ui/react";
import { toast } from "sonner";
import type { ThirdwebContract } from "thirdweb";
import type {
DirectListing,
EnglishAuction,
} from "thirdweb/extensions/marketplace";
import { useActiveAccount } from "thirdweb/react";
import { CodeClient } from "@/components/ui/code/code.client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { BuyDirectListingButton, useActiveAccount } from "thirdweb/react";
import { NFTMediaWithEmptyState } from "tw-components/nft-media";
import { CancelTab } from "./cancel-tab";
import { LISTING_STATUS } from "./types";
@@ -23,7 +22,7 @@ interface NFTDrawerProps {
isOpen: boolean;
onClose: () => void;
data: DirectListing | EnglishAuction | null;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const ListingDrawer: React.FC<NFTDrawerProps> = ({
@@ -31,7 +30,7 @@ export const ListingDrawer: React.FC<NFTDrawerProps> = ({
isOpen,
onClose,
data,
twAccount,
isLoggedIn,
}) => {
const address = useActiveAccount()?.address;
const prevData = usePrevious(data);
@@ -141,11 +140,6 @@ export const ListingDrawer: React.FC<NFTDrawerProps> = ({
</GridItem>
</>
)}
{/*
Todo: Add a Buy button somewhere in this section once the Dashboard is fully migrated to v5 (?)
Kien already shipped a prebuilt component for the Marketplace Buy Button in SDK v5
*/}
</SimpleGrid>
</Card>
{data?.asset.metadata.properties ? (
@@ -165,9 +159,30 @@ export const ListingDrawer: React.FC<NFTDrawerProps> = ({
contract={contract}
id={renderData.id.toString()}
isAuction={renderData.type === "english-auction"}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
)}
{!isOwner && renderData.status === "ACTIVE" && (
<BuyDirectListingButton
listingId={renderData.id}
contractAddress={renderData.assetContractAddress}
chain={contract.chain}
client={contract.client}
className="w-full"
quantity={1n}
onError={(error) => {
toast.error("Failed to buy listing", {
description: error.message,
});
}}
onTransactionConfirmed={() => {
toast.success("Listing bought successfully");
}}
>
Buy Listing
</BuyDirectListingButton>
)}
</SheetContent>
</Sheet>
);
@@ -1,6 +1,5 @@
import { WalletAddress } from "@/components/blocks/wallet-address";
import { Button } from "@/components/ui/button";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import {
IconButton,
Select,
@@ -99,7 +98,7 @@ interface MarketplaceTableProps {
start: number;
}>
>;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
const DEFAULT_QUERY_STATE = { count: 50, start: 0 };
@@ -111,7 +110,7 @@ export const MarketplaceTable: React.FC<MarketplaceTableProps> = ({
totalCountQuery,
queryParams,
setQueryParams,
twAccount,
isLoggedIn,
}) => {
const [listingsToShow, setListingsToShow_] = useState<"all" | "valid">("all");
@@ -216,7 +215,7 @@ export const MarketplaceTable: React.FC<MarketplaceTableProps> = ({
data={tokenRow}
isOpen={!!tokenRow}
onClose={() => setTokenRow(null)}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
<Table {...getTableProps()}>
<Thead>
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../../_components/page-skeletons";
import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client";
@@ -9,7 +8,7 @@ import { ContractDirectListingsPage } from "./ContractDirectListingsPage";
export function ContractDirectListingsPageClient(props: {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
@@ -28,7 +27,7 @@ export function ContractDirectListingsPageClient(props: {
return (
<ContractDirectListingsPage
contract={props.contract}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
isInsightSupported={metadataQuery.data.isInsightSupported}
/>
);
@@ -1,19 +1,18 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { CreateListingButton } from "../components/list-button";
import { DirectListingsTable } from "./components/table";
interface ContractDirectListingsPageProps {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
isInsightSupported: boolean;
}
export const ContractDirectListingsPage: React.FC<
ContractDirectListingsPageProps
> = ({ contract, twAccount, isInsightSupported }) => {
> = ({ contract, isLoggedIn, isInsightSupported }) => {
return (
<div className="flex flex-col gap-6">
<div className="flex flex-row items-center justify-between">
@@ -26,12 +25,12 @@ export const ContractDirectListingsPage: React.FC<
contract={contract}
type="direct-listings"
createText="Create Direct Listing"
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
</div>
</div>
<DirectListingsTable contract={contract} twAccount={twAccount} />
<DirectListingsTable contract={contract} isLoggedIn={isLoggedIn} />
</div>
);
};
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { useState } from "react";
import type { ThirdwebContract } from "thirdweb";
import {
@@ -13,14 +12,14 @@ import { MarketplaceTable } from "../../components/marketplace-table";
interface DirectListingsTableProps {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
const DEFAULT_QUERY_STATE = { count: 50, start: 0 };
export const DirectListingsTable: React.FC<DirectListingsTableProps> = ({
contract,
twAccount,
isLoggedIn,
}) => {
const [queryParams, setQueryParams] = useState(DEFAULT_QUERY_STATE);
const getAllQueryResult = useReadContract(getAllListings, {
@@ -43,7 +42,7 @@ export const DirectListingsTable: React.FC<DirectListingsTableProps> = ({
totalCountQuery={totalCountQuery}
queryParams={queryParams}
setQueryParams={setQueryParams}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
);
};
@@ -24,7 +24,7 @@ export default async function Page(props: {
return (
<ContractDirectListingsPageClient
contract={info.contract}
twAccount={account}
isLoggedIn={!!account}
/>
);
}
@@ -39,7 +39,7 @@ export default async function Page(props: {
return (
<ContractDirectListingsPage
contract={info.contract}
twAccount={account}
isLoggedIn={!!account}
isInsightSupported={isInsightSupported}
/>
);
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../../_components/page-skeletons";
import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client";
@@ -9,7 +8,7 @@ import { ContractEnglishAuctionsPage } from "./ContractEnglishAuctionsPage";
export function ContractEnglishAuctionsPageClient(props: {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
@@ -28,7 +27,7 @@ export function ContractEnglishAuctionsPageClient(props: {
return (
<ContractEnglishAuctionsPage
contract={props.contract}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
isInsightSupported={metadataQuery.data.isInsightSupported}
/>
);
@@ -1,19 +1,18 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { CreateListingButton } from "../components/list-button";
import { EnglishAuctionsTable } from "./components/table";
interface ContractEnglishAuctionsProps {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
isInsightSupported: boolean;
}
export const ContractEnglishAuctionsPage: React.FC<
ContractEnglishAuctionsProps
> = ({ contract, twAccount, isInsightSupported }) => {
> = ({ contract, isLoggedIn, isInsightSupported }) => {
return (
<div className="flex flex-col gap-6">
<div className="flex flex-row items-center justify-between">
@@ -25,13 +24,13 @@ export const ContractEnglishAuctionsPage: React.FC<
contract={contract}
type="english-auctions"
createText="Create English Auction"
twAccount={twAccount}
isLoggedIn={isLoggedIn}
isInsightSupported={isInsightSupported}
/>
</div>
</div>
<EnglishAuctionsTable contract={contract} twAccount={twAccount} />
<EnglishAuctionsTable contract={contract} isLoggedIn={isLoggedIn} />
</div>
);
};
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { useState } from "react";
import type { ThirdwebContract } from "thirdweb";
import {
@@ -13,14 +12,14 @@ import { MarketplaceTable } from "../../components/marketplace-table";
interface EnglishAuctionsTableProps {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
const DEFAULT_QUERY_STATE = { count: 50, start: 0 };
export const EnglishAuctionsTable: React.FC<EnglishAuctionsTableProps> = ({
contract,
twAccount,
isLoggedIn,
}) => {
const [queryParams, setQueryParams] = useState(DEFAULT_QUERY_STATE);
const getAllQueryResult = useReadContract(getAllAuctions, {
@@ -37,7 +36,7 @@ export const EnglishAuctionsTable: React.FC<EnglishAuctionsTableProps> = ({
return (
<MarketplaceTable
twAccount={twAccount}
isLoggedIn={isLoggedIn}
contract={contract}
getAllQueryResult={getAllQueryResult}
getValidQueryResult={getValidQueryResult}
@@ -25,7 +25,7 @@ export default async function Page(props: {
return (
<ContractEnglishAuctionsPageClient
contract={info.contract}
twAccount={twAccount}
isLoggedIn={!!twAccount}
/>
);
}
@@ -40,7 +40,7 @@ export default async function Page(props: {
return (
<ContractEnglishAuctionsPage
contract={info.contract}
twAccount={twAccount}
isLoggedIn={!!twAccount}
isInsightSupported={isInsightSupported}
/>
);
@@ -3,7 +3,6 @@
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { useIsAdmin } from "@3rdweb-sdk/react/hooks/useContractRoles";
import {
Alert,
@@ -187,7 +186,7 @@ interface ClaimConditionsFormProps {
isColumn?: true;
isErc20: boolean;
isMultiPhase: boolean;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
@@ -196,7 +195,7 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
isColumn,
isErc20,
isMultiPhase,
twAccount,
isLoggedIn,
}) => {
// assume 1155 if we have a tokenId
const isErc1155 = tokenId !== undefined;
@@ -636,7 +635,7 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
{controlledFields.some((field) => field.fromSdk) && (
<ResetClaimEligibility
twAccount={twAccount}
isLoggedIn={isLoggedIn}
isErc20={isErc20}
contract={contract}
tokenId={tokenId}
@@ -657,7 +656,7 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
hasRemovedPhases ||
!isMultiPhase ? (
<TransactionButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
txChainID={contract.chain.id}
transactionCount={1}
disabled={claimConditionsQuery.isPending}
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { ClaimConditionsForm } from "./claim-conditions-form/index";
@@ -9,7 +8,7 @@ interface ClaimConditionsProps {
tokenId?: string;
isColumn?: true;
isERC20: boolean;
twAccount: Account | undefined;
isLoggedIn: boolean;
isMultiphase: boolean;
}
export const ClaimConditions: React.FC<ClaimConditionsProps> = ({
@@ -17,7 +16,7 @@ export const ClaimConditions: React.FC<ClaimConditionsProps> = ({
tokenId,
isColumn,
isERC20,
twAccount,
isLoggedIn,
isMultiphase,
}) => {
return (
@@ -34,7 +33,7 @@ export const ClaimConditions: React.FC<ClaimConditionsProps> = ({
{/* Set Claim Conditions */}
<ClaimConditionsForm
twAccount={twAccount}
isLoggedIn={isLoggedIn}
isErc20={isERC20}
contract={contract}
tokenId={tokenId}
@@ -2,7 +2,6 @@
import { ToolTipLabel } from "@/components/ui/tooltip";
import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { TransactionButton } from "components/buttons/TransactionButton";
import { useTrack } from "hooks/analytics/useTrack";
import { useTxNotifications } from "hooks/useTxNotifications";
@@ -17,7 +16,7 @@ interface ResetClaimEligibilityProps {
isErc20: boolean;
contract: ThirdwebContract;
tokenId?: string;
twAccount: Account | undefined;
isLoggedIn: boolean;
isMultiphase: boolean;
}
@@ -25,7 +24,7 @@ export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
contract,
tokenId,
isErc20,
twAccount,
isLoggedIn,
isMultiphase,
}) => {
const trackEvent = useTrack();
@@ -95,7 +94,7 @@ export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
return (
<AdminOnly contract={contract} fallback={<div className="pb-5" />}>
<TransactionButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
transactionCount={1}
type="button"
isPending={sendTxMutation.isPending}
@@ -1,4 +1,3 @@
import { getThirdwebClient } from "@/constants/thirdweb.server";
import {
type DashboardContractMetadata,
fetchDashboardContractMetadata,
@@ -32,6 +31,7 @@ export function ContractMetadata({
chain={chain}
address={contract.address}
externalLinks={externalLinks}
client={contract.client}
/>
);
}
@@ -43,7 +43,7 @@ export async function getContractMetadataHeaderData(
fetchDashboardContractMetadata(contract),
fetchPublishedContractsFromDeploy({
contract,
client: getThirdwebClient(),
client: contract.client,
}),
]);
@@ -2,10 +2,10 @@
import { CopyAddressButton } from "@/components/ui/CopyAddressButton";
import { Button } from "@/components/ui/button";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { ChainIconClient } from "components/icons/ChainIcon";
import { ExternalLinkIcon } from "lucide-react";
import Link from "next/link";
import type { ThirdwebClient } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { MediaRenderer } from "thirdweb/react";
@@ -22,6 +22,7 @@ interface MetadataHeaderProps {
};
chain: ChainMetadata;
externalLinks?: ExternalLink[];
client: ThirdwebClient;
}
export const MetadataHeader: React.FC<MetadataHeaderProps> = ({
@@ -29,6 +30,7 @@ export const MetadataHeader: React.FC<MetadataHeaderProps> = ({
data,
chain,
externalLinks,
client,
}) => {
const cleanedChainName = chain?.name?.replace("Mainnet", "").trim();
const validBlockExplorers = chain?.explorers
@@ -56,7 +58,7 @@ export const MetadataHeader: React.FC<MetadataHeaderProps> = ({
<div className="size-16 shrink-0 overflow-hidden rounded-lg border border-border bg-muted md:size-20">
<MediaRenderer
src={data?.image}
client={getThirdwebClient()}
client={client}
alt={""}
className="h-full w-full"
style={{
@@ -85,7 +87,11 @@ export const MetadataHeader: React.FC<MetadataHeaderProps> = ({
href={`/${chain.slug}`}
className="flex w-fit shrink-0 items-center gap-2 rounded-3xl border border-border bg-card px-2.5 py-1.5 hover:bg-accent"
>
<ChainIconClient ipfsSrc={chain.icon?.url} className="size-4" />
<ChainIconClient
src={chain.icon?.url}
client={client}
className="size-4"
/>
{cleanedChainName && (
<span className="text-xs">{cleanedChainName}</span>
)}
@@ -5,6 +5,7 @@ import { TW_LOCAL_CHAIN_STORE } from "stores/storageKeys";
import { getAddress, getContract, isAddress } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { fetchChain } from "utils/fetchChain";
import { LAST_USED_TEAM_ID } from "../../../../../../constants/cookies";
import { getAuthToken } from "../../../../../api/lib/getAuthToken";
export async function getContractPageParamsInfo(params: {
@@ -43,13 +44,18 @@ export async function getContractPageParamsInfo(params: {
// attempt to get the auth token
const token = await getAuthToken();
const cookiesObj = await cookies();
const teamId = cookiesObj.get(LAST_USED_TEAM_ID)?.value;
const contract = getContract({
address: contractAddress,
// eslint-disable-next-line no-restricted-syntax
chain: mapV4ChainToV5Chain(chainMetadata),
// if we have the auth token pass it into the client
client: getThirdwebClient(token || undefined),
client: getThirdwebClient({
jwt: token || undefined,
teamId,
}),
});
return {
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
@@ -11,7 +10,7 @@ import { AccountPage } from "./AccountPage";
export function AccountPageClient(props: {
contract: ThirdwebContract;
chainMetadata: ChainMetadata;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
@@ -31,7 +30,7 @@ export function AccountPageClient(props: {
<AccountPage
contract={props.contract}
chainMetadata={props.chainMetadata}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
isInsightSupported={metadataQuery.data.isInsightSupported}
/>
);
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { Heading } from "tw-components";
@@ -11,14 +10,14 @@ import { NftsOwned } from "./components/nfts-owned";
interface AccountPageProps {
contract: ThirdwebContract;
chainMetadata: ChainMetadata;
twAccount: Account | undefined;
isLoggedIn: boolean;
isInsightSupported: boolean;
}
export const AccountPage: React.FC<AccountPageProps> = ({
contract,
chainMetadata,
twAccount,
isLoggedIn,
isInsightSupported,
}) => {
const symbol = chainMetadata.nativeCurrency.symbol || "Native Token";
@@ -35,7 +34,7 @@ export const AccountPage: React.FC<AccountPageProps> = ({
{chainMetadata && (
<DepositNative
twAccount={twAccount}
isLoggedIn={isLoggedIn}
address={contract.address}
symbol={symbol}
chain={chainMetadata}
@@ -1,7 +1,6 @@
"use client";
import { useThirdwebClient } from "@/constants/thirdweb.client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { Input } from "@chakra-ui/react";
import { TransactionButton } from "components/buttons/TransactionButton";
import { useV5DashboardChain } from "lib/v5-adapter";
@@ -15,14 +14,14 @@ interface DepositNativeProps {
address: string;
symbol: string;
chain: StoredChain;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const DepositNative: React.FC<DepositNativeProps> = ({
address,
symbol,
chain,
twAccount,
isLoggedIn,
}) => {
const client = useThirdwebClient();
const { mutate: transfer, isPending } = useSendAndConfirmTransaction();
@@ -49,7 +48,7 @@ export const DepositNative: React.FC<DepositNativeProps> = ({
value={amount}
/>
<TransactionButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
txChainID={v5Chain.id}
transactionCount={1}
isPending={isPending}
@@ -28,7 +28,7 @@ export default async function Page(props: {
<AccountPageClient
contract={contract}
chainMetadata={chainMetadata}
twAccount={account}
isLoggedIn={!!account}
/>
);
}
@@ -44,7 +44,7 @@ export default async function Page(props: {
<AccountPage
contract={contract}
chainMetadata={chainMetadata}
twAccount={account}
isLoggedIn={!!account}
isInsightSupported={isInsightSupported}
/>
);
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
@@ -9,7 +8,7 @@ import { AccountsPage } from "./AccountsPage";
export function AccountsPageClient(props: {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
@@ -25,5 +24,7 @@ export function AccountsPageClient(props: {
return <RedirectToContractOverview contract={props.contract} />;
}
return <AccountsPage contract={props.contract} twAccount={props.twAccount} />;
return (
<AccountsPage contract={props.contract} isLoggedIn={props.isLoggedIn} />
);
}
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { ButtonGroup, Flex } from "@chakra-ui/react";
import type { ThirdwebContract } from "thirdweb";
import { Heading, TrackedLinkButton } from "tw-components";
@@ -10,12 +9,12 @@ import { CreateAccountButton } from "./components/create-account-button";
interface AccountsPageProps {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const AccountsPage: React.FC<AccountsPageProps> = ({
contract,
twAccount,
isLoggedIn,
}) => {
return (
<Flex direction="column" gap={6}>
@@ -40,7 +39,7 @@ export const AccountsPage: React.FC<AccountsPageProps> = ({
>
View Documentation
</TrackedLinkButton>
<CreateAccountButton contract={contract} twAccount={twAccount} />
<CreateAccountButton contract={contract} isLoggedIn={isLoggedIn} />
</ButtonGroup>
</Flex>
<AccountsCount contract={contract} />
@@ -2,7 +2,6 @@
import { Button } from "@/components/ui/button";
import { ToolTipLabel } from "@/components/ui/tooltip";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { TransactionButton } from "components/buttons/TransactionButton";
import type { ThirdwebContract } from "thirdweb";
import * as ERC4337Ext from "thirdweb/extensions/erc4337";
@@ -14,12 +13,12 @@ import {
interface CreateAccountButtonProps {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const CreateAccountButton: React.FC<CreateAccountButtonProps> = ({
contract,
twAccount,
isLoggedIn,
...restButtonProps
}) => {
const sendTxMutation = useSendAndConfirmTransaction();
@@ -62,7 +61,7 @@ export const CreateAccountButton: React.FC<CreateAccountButtonProps> = ({
return (
<TransactionButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
txChainID={contract.chain.id}
onClick={() => {
const tx = ERC4337Ext.createAccount({
@@ -24,7 +24,7 @@ export default async function Page(props: {
const account = await getRawAccount();
if (chainMetadata.chainId === localhost.id) {
return <AccountsPageClient contract={contract} twAccount={account} />;
return <AccountsPageClient contract={contract} isLoggedIn={!!account} />;
}
const { isAccountFactory } = await getContractPageMetadata(contract);
@@ -33,5 +33,5 @@ export default async function Page(props: {
redirect(`/${params.chain_id}/${params.contractAddress}`);
}
return <AccountsPage contract={contract} twAccount={account} />;
return <AccountsPage contract={contract} isLoggedIn={!!account} />;
}
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { ClaimConditions } from "../_components/claim-conditions/claim-conditions";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
@@ -9,7 +8,7 @@ import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
export function ClaimConditionsClient(props: {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
@@ -35,7 +34,7 @@ export function ClaimConditionsClient(props: {
<ClaimConditions
contract={props.contract}
isERC20={supportedERCs.isERC20}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
isMultiphase={true}
/>
);
@@ -23,7 +23,7 @@ export default async function Page(props: {
const account = await getRawAccount();
if (chainMetadata.chainId === localhost.id) {
return <ClaimConditionsClient contract={contract} twAccount={account} />;
return <ClaimConditionsClient contract={contract} isLoggedIn={!!account} />;
}
const {
@@ -40,7 +40,7 @@ export default async function Page(props: {
<ClaimConditions
contract={contract}
isERC20={supportedERCs.isERC20}
twAccount={account}
isLoggedIn={!!account}
isMultiphase={true}
/>
);
@@ -19,7 +19,6 @@ import {
TableRow,
} from "@/components/ui/table";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import {
@@ -39,6 +38,7 @@ import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import { useForm } from "react-hook-form";
import {
type ThirdwebClient,
defineChain,
eth_getCode,
getContract,
@@ -113,6 +113,7 @@ export function DataTable({
(m) => m.name === "SuperChainInterop",
);
const client = coreContract.client;
const addRowMutation = useMutation({
mutationFn: async (chain: { chainId: number; name: string }) => {
if (coreContract.chain.id === chain.chainId) {
@@ -122,7 +123,7 @@ export function DataTable({
const c = defineChain(chain.chainId);
const code = await eth_getCode(
getRpcClient({
client: getThirdwebClient(),
client: client,
chain: c,
}),
{ address: coreContract.address },
@@ -261,7 +262,7 @@ export function DataTable({
return (
<Button
type="button"
onClick={() => deployContract(row.getValue("chainId"))}
onClick={() => deployContract(row.getValue("chainId"), client)}
>
Deploy
</Button>
@@ -324,7 +325,7 @@ export function DataTable({
getCoreRowModel: getCoreRowModel(),
});
const deployContract = async (chainId: number) => {
const deployContract = async (chainId: number, client: ThirdwebClient) => {
try {
if (!activeAccount) {
throw new Error("No active account");
@@ -332,7 +333,6 @@ export function DataTable({
// eslint-disable-next-line no-restricted-syntax
const chain = defineChain(chainId);
const client = getThirdwebClient();
const salt =
inputSalt || concatHex(["0x03", padHex("0x", { size: 31 })]).toString();
@@ -487,6 +487,7 @@ export function DataTable({
<div className="mt-4">
<SingleNetworkSelector
client={client}
onAddRow={addRowMutation.mutate}
isAddingRow={addRowMutation.isPending}
className="w-full"
@@ -3,6 +3,7 @@ import { Badge } from "@/components/ui/badge";
import { ChainIconClient } from "components/icons/ChainIcon";
import { useAllChainsData } from "hooks/chains/allChains";
import { useCallback, useMemo } from "react";
import type { ThirdwebClient } from "thirdweb";
type Option = { label: string; value: string };
@@ -10,6 +11,7 @@ export function SingleNetworkSelector(props: {
onAddRow: (chain: { chainId: number; name: string }) => void;
isAddingRow: boolean;
className?: string;
client: ThirdwebClient;
}) {
const { allChains, idToChain } = useAllChainsData();
@@ -47,7 +49,8 @@ export function SingleNetworkSelector(props: {
<span className="flex grow gap-2 truncate text-left">
<ChainIconClient
className="size-5"
ipfsSrc={chain.icon?.url}
src={chain.icon?.url}
client={props.client}
loading="lazy"
/>
{chain.name}
@@ -59,7 +62,7 @@ export function SingleNetworkSelector(props: {
</div>
);
},
[idToChain],
[idToChain, props.client],
);
const handleChange = async (chainId: string) => {
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { LoadingPage } from "../_components/page-skeletons";
@@ -10,13 +9,13 @@ import { ContractExplorerPage } from "./ContractExplorerPage";
interface ContractExplorePageProps {
contract: ThirdwebContract;
chainMetadata: ChainMetadata;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const ContractExplorerPageClient: React.FC<ContractExplorePageProps> = ({
contract,
chainMetadata,
twAccount,
isLoggedIn,
}) => {
const abiQuery = useResolveContractABI(contract);
@@ -29,7 +28,7 @@ export const ContractExplorerPageClient: React.FC<ContractExplorePageProps> = ({
abi={abiQuery.data}
contract={contract}
chainMetadata={chainMetadata}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
);
};
@@ -1,5 +1,4 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { Abi } from "abitype";
import { getContractFunctionsFromAbi } from "components/contract-components/getContractFunctionsFromAbi";
import { ContractFunctionsOverview } from "components/contract-functions/contract-functions";
@@ -11,14 +10,14 @@ interface ContractExplorePageProps {
contract: ThirdwebContract;
abi: Abi | undefined;
chainMetadata: ChainMetadata;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const ContractExplorerPage: React.FC<ContractExplorePageProps> = ({
contract,
abi,
chainMetadata,
twAccount,
isLoggedIn,
}) => {
if (!abi) {
return (
@@ -41,7 +40,7 @@ export const ContractExplorerPage: React.FC<ContractExplorePageProps> = ({
onlyFunctions
functions={functions}
contract={contract}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
) : (
<div className="flex flex-col items-center justify-center gap-2 bg-card text-center">
@@ -28,7 +28,7 @@ export default async function Page(props: {
<ContractExplorerPageClient
contract={contract}
chainMetadata={chainMetadata}
twAccount={account}
isLoggedIn={!!account}
/>
);
}
@@ -40,7 +40,7 @@ export default async function Page(props: {
contract={contract}
abi={abi}
chainMetadata={chainMetadata}
twAccount={account}
isLoggedIn={!!account}
/>
);
}
@@ -1,6 +1,5 @@
import { getProjects } from "@/api/projects";
import { getTeams } from "@/api/team";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { localhost } from "thirdweb/chains";
@@ -9,10 +8,10 @@ import { isAddress, isContractDeployed } from "thirdweb/utils";
import type { MinimalTeamsAndProjects } from "../../../../../components/contract-components/contract-deploy-form/add-to-project-card";
import { resolveFunctionSelectors } from "../../../../../lib/selectors";
import { shortenIfAddress } from "../../../../../utils/usedapp-external";
import { getRawAccount } from "../../../../account/settings/getAccount";
import {
getAuthToken,
getAuthTokenWalletAddress,
getUserThirdwebClient,
} from "../../../../api/lib/getAuthToken";
import { NebulaFloatingChatButton } from "../../../../nebula-app/(app)/components/FloatingChat/FloatingChat";
import { ConfigureCustomChain } from "./_layout/ConfigureCustomChain";
@@ -48,13 +47,12 @@ export default async function Layout(props: {
notFound();
}
const [authToken, account, accountAddress] = await Promise.all([
const [authToken, accountAddress] = await Promise.all([
getAuthToken(),
getRawAccount(),
getAuthTokenWalletAddress(),
]);
const client = getThirdwebClient();
const client = await getUserThirdwebClient();
const teamsAndProjects = await getTeamsAndProjectsIfLoggedIn();
if (contract.chain.id === localhost.id) {
@@ -117,8 +115,8 @@ The following is the user's message:`;
client={client}
>
<NebulaFloatingChatButton
pageType="contract"
authToken={authToken ?? undefined}
account={account}
label="Ask AI about this contract"
client={client}
nebulaParams={{
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
@@ -9,7 +8,7 @@ import { ContractEditModulesPage } from "./ContractEditModulesPage";
export function ContractEditModulesPageClient(props: {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
@@ -28,7 +27,7 @@ export function ContractEditModulesPageClient(props: {
return (
<ContractEditModulesPage
contract={props.contract}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
/>
);
}
@@ -2,7 +2,6 @@
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { UserXIcon } from "lucide-react";
import type { ThirdwebContract } from "thirdweb";
import { getInstalledModules, owner } from "thirdweb/modules";
@@ -12,12 +11,12 @@ import { InstallModuleForm } from "./components/ModuleForm";
interface ContractEditModulesPageProps {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const ContractEditModulesPage: React.FC<
ContractEditModulesPageProps
> = ({ contract, twAccount }) => {
> = ({ contract, isLoggedIn }) => {
const account = useActiveAccount();
const installedModulesQuery = useReadContract(getInstalledModules, {
@@ -68,7 +67,7 @@ export const ContractEditModulesPage: React.FC<
</div>
<div className="h-10" />
<InstallModuleForm
twAccount={twAccount}
isLoggedIn={isLoggedIn}
contract={contract}
refetchModules={() => installedModulesQuery.refetch()}
account={account}
@@ -100,7 +99,7 @@ export const ContractEditModulesPage: React.FC<
refetchModules={() => installedModulesQuery.refetch()}
contract={contract}
ownerAccount={isOwner ? account : undefined}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
</div>
);
@@ -16,7 +16,6 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import { TransactionButton } from "components/buttons/TransactionButton";
@@ -104,7 +103,7 @@ export function BatchMetadataModuleUI(
isOwnerAccount: boolean;
uploadMetadata: (values: UploadMetadataFormValues) => Promise<void>;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
},
) {
return (
@@ -119,7 +118,7 @@ export function BatchMetadataModuleUI(
<AccordionContent className="px-1">
{props.isOwnerAccount && (
<UploadMetadataNFTSection
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
uploadMetadata={props.uploadMetadata}
contractChainId={props.contractChainId}
/>
@@ -163,7 +162,7 @@ export function BatchMetadataModuleUI(
function UploadMetadataNFTSection(props: {
uploadMetadata: (values: UploadMetadataFormValues) => Promise<void>;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const form = useForm<UploadMetadataFormValues>({
resolver: zodResolver(uploadMetadataFormSchema),
@@ -262,7 +261,7 @@ function UploadMetadataNFTSection(props: {
<div className="flex justify-end">
<TransactionButton
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
size="sm"
className="min-w-24"
disabled={uploadMetadataMutation.isPending}
@@ -23,7 +23,6 @@ import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
import { ToolTipLabel } from "@/components/ui/tooltip";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import { useQuery } from "@tanstack/react-query";
@@ -342,7 +341,7 @@ export function ClaimableModuleUI(
| undefined;
isLoading: boolean;
};
twAccount: Account | undefined;
isLoggedIn: boolean;
},
) {
return (
@@ -361,7 +360,7 @@ export function ClaimableModuleUI(
mint={props.mintSection.mint}
name={props.name}
contractChainId={props.contractChainId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
/>
</AccordionContent>
</AccordionItem>
@@ -390,7 +389,7 @@ export function ClaimableModuleUI(
{props.name !== "ClaimableERC1155" || props.isValidTokenId ? (
props.claimConditionSection.data ? (
<ClaimConditionSection
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
isOwnerAccount={props.isOwnerAccount}
claimCondition={
props.claimConditionSection.data.claimCondition
@@ -424,7 +423,7 @@ export function ClaimableModuleUI(
<AccordionContent className="px-1">
{props.primarySaleRecipientSection.data ? (
<PrimarySaleRecipientSection
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
isOwnerAccount={props.isOwnerAccount}
primarySaleRecipient={
props.primarySaleRecipientSection.data.primarySaleRecipient
@@ -489,7 +488,7 @@ function ClaimConditionSection(props: {
tokenDecimals?: number | undefined;
tokenId: string;
noClaimConditionSet: boolean;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const { idToChain } = useAllChainsData();
const chain = idToChain.get(props.chainId);
@@ -741,7 +740,7 @@ function ClaimConditionSection(props: {
<div className="flex justify-end">
<TransactionButton
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
size="sm"
className="min-w-24"
disabled={updateMutation.isPending || !props.isOwnerAccount}
@@ -774,7 +773,7 @@ function PrimarySaleRecipientSection(props: {
update: (values: PrimarySaleRecipientFormValues) => Promise<void>;
isOwnerAccount: boolean;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const form = useForm<PrimarySaleRecipientFormValues>({
resolver: zodResolver(primarySaleRecipientFormSchema),
@@ -823,7 +822,7 @@ function PrimarySaleRecipientSection(props: {
<div className="flex justify-end">
<TransactionButton
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
size="sm"
className="min-w-24 gap-2"
disabled={
@@ -858,7 +857,7 @@ function MintNFTSection(props: {
mint: (values: MintFormValues) => Promise<void>;
name: string;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const form = useForm<MintFormValues>({
resolver: zodResolver(mintFormSchema),
@@ -947,7 +946,7 @@ function MintNFTSection(props: {
isPending={mintMutation.isPending}
txChainID={props.contractChainId}
transactionCount={1}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
>
Mint
</TransactionButton>
@@ -2,7 +2,6 @@
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
import { Alert, AlertTitle } from "@/components/ui/alert";
import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi";
import { CircleSlash } from "lucide-react";
import type { ThirdwebContract } from "thirdweb/contract";
import type { Account } from "thirdweb/wallets";
@@ -17,7 +16,7 @@ export const InstalledModulesTable = (props: {
};
refetchModules: () => void;
ownerAccount: Account | undefined;
twAccount: TWAccount | undefined;
isLoggedIn: boolean;
}) => {
const { installedModules, ownerAccount } = props;
@@ -59,7 +58,7 @@ export const InstalledModulesTable = (props: {
contract={props.contract}
onRemoveModule={props.refetchModules}
ownerAccount={ownerAccount}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
/>
))}
</div>
@@ -20,7 +20,6 @@ import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
import { Textarea } from "@/components/ui/textarea";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import { TransactionButton } from "components/buttons/TransactionButton";
@@ -192,7 +191,7 @@ export function MintableModuleUI(
name: string;
isBatchMetadataInstalled: boolean;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
},
) {
return (
@@ -211,7 +210,7 @@ export function MintableModuleUI(
<AccordionContent className="px-1">
{props.isOwnerAccount && (
<MintNFTSection
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
mint={props.mint}
name={props.name}
isBatchMetadataInstalled={props.isBatchMetadataInstalled}
@@ -242,7 +241,7 @@ export function MintableModuleUI(
primarySaleRecipient={props.primarySaleRecipient}
update={props.updatePrimaryRecipient}
contractChainId={props.contractChainId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
/>
</AccordionContent>
</AccordionItem>
@@ -262,7 +261,7 @@ function PrimarySalesSection(props: {
update: (values: UpdateFormValues) => Promise<void>;
isOwnerAccount: boolean;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const form = useForm<UpdateFormValues>({
resolver: zodResolver(primarySaleRecipientFormSchema),
@@ -312,7 +311,7 @@ function PrimarySalesSection(props: {
<div className="mt-4 flex justify-end">
<TransactionButton
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
size="sm"
className="min-w-24"
disabled={updateMutation.isPending || !props.isOwnerAccount}
@@ -340,7 +339,7 @@ function MintNFTSection(props: {
name: string;
isBatchMetadataInstalled: boolean;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const form = useForm<MintFormValues>({
resolver: zodResolver(mintFormSchema),
@@ -524,7 +523,7 @@ function MintNFTSection(props: {
<div className="flex justify-end">
<TransactionButton
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
size="sm"
className="min-w-24"
disabled={mintMutation.isPending}
@@ -2,7 +2,6 @@
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { useThirdwebClient } from "@/constants/thirdweb.client";
import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi";
import { FormControl, Input, Select, Skeleton, Spacer } from "@chakra-ui/react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { TransactionButton } from "components/buttons/TransactionButton";
@@ -55,7 +54,7 @@ type InstallModuleFormProps = {
data?: string[];
isPending: boolean;
};
twAccount: TWAccount | undefined;
isLoggedIn: boolean;
};
export type InstallModuleForm = UseFormReturn<FormData>;
@@ -388,7 +387,7 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => {
{/* Submit */}
<div className="flex justify-end">
<TransactionButton
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
txChainID={contract.chain.id}
transactionCount={1}
isPending={installMutation.isPending}
@@ -16,7 +16,6 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import { TransactionButton } from "components/buttons/TransactionButton";
@@ -83,7 +82,7 @@ export function OpenEditionMetadataModuleUI(
isOwnerAccount: boolean;
setSharedMetadata: (values: SetSharedMetadataFormValues) => Promise<void>;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
},
) {
return (
@@ -99,7 +98,7 @@ export function OpenEditionMetadataModuleUI(
<SetSharedMetadataSection
setSharedMetadata={props.setSharedMetadata}
contractChainId={props.contractChainId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
/>
)}
{!props.isOwnerAccount && (
@@ -121,7 +120,7 @@ export function OpenEditionMetadataModuleUI(
function SetSharedMetadataSection(props: {
setSharedMetadata: (values: SetSharedMetadataFormValues) => Promise<void>;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const form = useForm<SetSharedMetadataFormValues>({
resolver: zodResolver(setSharedMetadataFormSchema),
@@ -219,7 +218,7 @@ function SetSharedMetadataSection(props: {
isPending={setSharedMetadataMutation.isPending}
transactionCount={1}
txChainID={props.contractChainId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
>
Update
</TransactionButton>
@@ -16,7 +16,6 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Skeleton } from "@/components/ui/skeleton";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import { TransactionButton } from "components/buttons/TransactionButton";
@@ -167,7 +166,7 @@ export function RoyaltyModuleUI(
) => Promise<void>;
setRoyaltyInfoForToken: (values: RoyaltyInfoFormValues) => Promise<void>;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
},
) {
return (
@@ -186,7 +185,7 @@ export function RoyaltyModuleUI(
<RoyaltyInfoPerTokenSection
setRoyaltyInfoForToken={props.setRoyaltyInfoForToken}
contractChainId={props.contractChainId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
/>
) : (
<Alert variant="info">
@@ -209,7 +208,7 @@ export function RoyaltyModuleUI(
update={props.setDefaultRoyaltyInfo}
defaultRoyaltyInfo={props.defaultRoyaltyInfo}
contractChainId={props.contractChainId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
/>
) : (
<Alert variant="info">
@@ -232,7 +231,7 @@ export function RoyaltyModuleUI(
update={props.setTransferValidator}
transferValidator={props.transferValidator}
contractChainId={props.contractChainId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
/>
) : (
<Alert variant="info">
@@ -269,7 +268,7 @@ export type RoyaltyInfoFormValues = z.infer<typeof royaltyInfoFormSchema>;
function RoyaltyInfoPerTokenSection(props: {
setRoyaltyInfoForToken: (values: RoyaltyInfoFormValues) => Promise<void>;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const form = useForm<RoyaltyInfoFormValues>({
resolver: zodResolver(royaltyInfoFormSchema),
@@ -349,7 +348,7 @@ function RoyaltyInfoPerTokenSection(props: {
<div className="mt-4 flex justify-end">
<TransactionButton
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
size="sm"
className="min-w-24"
disabled={setRoyaltyInfoForTokenMutation.isPending}
@@ -383,7 +382,7 @@ function DefaultRoyaltyInfoSection(props: {
defaultRoyaltyInfo?: readonly [string, number];
update: (values: DefaultRoyaltyFormValues) => Promise<void>;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const [defaultRoyaltyRecipient, defaultRoyaltyPercentage] =
props.defaultRoyaltyInfo || [];
@@ -460,7 +459,7 @@ function DefaultRoyaltyInfoSection(props: {
transactionCount={1}
isPending={updateMutation.isPending}
txChainID={props.contractChainId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
>
Update
</TransactionButton>
@@ -483,7 +482,7 @@ function TransferValidatorSection(props: {
transferValidator: string | undefined;
update: (values: TransferValidatorFormValues) => Promise<void>;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const form = useForm<TransferValidatorFormValues>({
resolver: zodResolver(transferValidatorFormSchema),
@@ -531,7 +530,7 @@ function TransferValidatorSection(props: {
<div className="mt-4 flex justify-end">
<TransactionButton
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
size="sm"
className="min-w-24 gap-2"
disabled={updateMutation.isPending}
@@ -14,7 +14,6 @@ import { Input } from "@/components/ui/input";
import { Skeleton } from "@/components/ui/skeleton";
import { Switch } from "@/components/ui/switch";
import { ToolTipLabel } from "@/components/ui/tooltip";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import { TransactionButton } from "components/buttons/TransactionButton";
@@ -137,7 +136,7 @@ export function TransferableModuleUI(
isOwnerAccount: boolean;
update: (values: TransferableModuleFormValues) => Promise<void>;
contractChainId: number;
twAccount: Account | undefined;
isLoggedIn: boolean;
},
) {
const form = useForm<TransferableModuleFormValues>({
@@ -200,7 +199,7 @@ export function TransferableModuleUI(
(props.isRestricted ? 0 : 1) + allowListLength
}
txChainID={props.contractChainId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
>
Update
</TransactionButton>
@@ -1,14 +1,12 @@
import { Checkbox } from "@/components/ui/checkbox";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Meta, StoryObj } from "@storybook/react";
import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
import { toast } from "sonner";
import { BadgeContainer } from "stories/utils";
import { BadgeContainer, storybookThirdwebClient } from "stories/utils";
import { ZERO_ADDRESS } from "thirdweb";
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
import type { TransactionError } from "../../../../../../../contexts/error-handler";
import { accountStub } from "../../../../../../../stories/stubs";
import {
BatchMetadataModuleUI,
type UploadMetadataFormValues,
@@ -71,7 +69,7 @@ function Component() {
<ThirdwebProvider>
<div className="container flex max-w-6xl flex-col gap-10 py-10">
<div>
<ConnectButton client={getThirdwebClient()} />
<ConnectButton client={storybookThirdwebClient} />
</div>
<div className="flex items-center gap-5">
@@ -85,7 +83,7 @@ function Component() {
<BadgeContainer label="Default">
<BatchMetadataModuleUI
twAccount={accountStub()}
isLoggedIn={true}
contractInfo={contractInfo}
moduleAddress="0x0000000000000000000000000000000000000000"
uploadMetadata={uploadMetadataStub}
@@ -6,7 +6,6 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Meta, StoryObj } from "@storybook/react";
import { useMutation } from "@tanstack/react-query";
import { subDays } from "date-fns";
@@ -15,7 +14,7 @@ import { toast } from "sonner";
import { NATIVE_TOKEN_ADDRESS, ZERO_ADDRESS } from "thirdweb";
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
import { checksumAddress } from "thirdweb/utils";
import { accountStub } from "../../../../../../../stories/stubs";
import { storybookThirdwebClient } from "../../../../../../../stories/utils";
import {
type ClaimConditionFormValues,
type ClaimConditionValue,
@@ -103,7 +102,7 @@ function Component() {
<ThirdwebProvider>
<div className="container flex max-w-6xl flex-col gap-10 py-10">
<div>
<ConnectButton client={getThirdwebClient()} />
<ConnectButton client={storybookThirdwebClient} />
</div>
<div className="flex flex-wrap items-center gap-5">
@@ -148,7 +147,7 @@ function Component() {
</div>
<ClaimableModuleUI
twAccount={accountStub()}
isLoggedIn={true}
contractInfo={contractInfo}
moduleAddress="0x0000000000000000000000000000000000000000"
primarySaleRecipientSection={{
@@ -1,21 +1,23 @@
import { getThirdwebClient } from "@/constants/thirdweb.server";
import {
fetchLatestPublishedContractVersion,
fetchPublishedContractVersions,
} from "components/contract-components/fetch-contracts-with-versions";
import { isAddress } from "thirdweb";
import { type ThirdwebClient, isAddress } from "thirdweb";
import type { FetchDeployMetadataResult } from "thirdweb/contract";
import { resolveAddress } from "thirdweb/extensions/ens";
import invariant from "tiny-invariant";
import type { ModuleMeta } from "./install-module-params";
export async function getModuleInstalledParams(ext: ModuleMeta) {
export async function getModuleInstalledParams(
ext: ModuleMeta,
client: ThirdwebClient,
) {
// get all versions of the module
// if the publisher is an ens name, resolve it
const publisherAddress = isAddress(ext.publisherAddress)
? ext.publisherAddress
: await resolveAddress({
client: getThirdwebClient(),
client,
name: ext.publisherAddress,
});
@@ -25,11 +27,13 @@ export async function getModuleInstalledParams(ext: ModuleMeta) {
publishedModule = await fetchLatestPublishedContractVersion(
publisherAddress,
ext.moduleName,
client,
);
} else {
const allPublishedModules = await fetchPublishedContractVersions(
publisherAddress,
ext.moduleName,
client,
);
publishedModule = allPublishedModules.find(
@@ -1,3 +1,4 @@
import { useThirdwebClient } from "@/constants/thirdweb.client";
import { FormControl } from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
import { SolidityInput } from "contract-ui/components/solidity-inputs";
@@ -20,12 +21,13 @@ export function useModuleInstallParams(props: {
module?: ModuleMeta;
isQueryEnabled: boolean;
}) {
const client = useThirdwebClient();
const { module, isQueryEnabled } = props;
return useQuery({
queryKey: ["useModuleInstallParams", module],
queryFn: async () => {
invariant(module, "module must be defined");
return await getModuleInstalledParams(module);
return await getModuleInstalledParams(module, client);
},
enabled: !!(isQueryEnabled && module),
refetchOnWindowFocus: false,
@@ -7,14 +7,12 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Meta, StoryObj } from "@storybook/react";
import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
import { toast } from "sonner";
import { BadgeContainer } from "stories/utils";
import { BadgeContainer, storybookThirdwebClient } from "stories/utils";
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
import { accountStub } from "../../../../../../../stories/stubs";
import {
type MintFormValues,
MintableModuleUI,
@@ -72,13 +70,11 @@ function Component() {
version: "1.0.0",
};
const twAccount = accountStub();
return (
<ThirdwebProvider>
<div className="container flex max-w-6xl flex-col gap-10 py-10">
<div>
<ConnectButton client={getThirdwebClient()} />
<ConnectButton client={storybookThirdwebClient} />
</div>
<div className="flex flex-wrap items-center gap-5">
@@ -110,7 +106,7 @@ function Component() {
<BadgeContainer label="Empty Primary Sale Recipient">
<MintableModuleUI
twAccount={twAccount}
isLoggedIn={true}
contractInfo={contractInfo}
moduleAddress="0x0000000000000000000000000000000000000000"
isPending={false}
@@ -130,7 +126,7 @@ function Component() {
<BadgeContainer label="Filled Primary Sale Recipient">
<MintableModuleUI
twAccount={twAccount}
isLoggedIn={true}
contractInfo={contractInfo}
moduleAddress="0x0000000000000000000000000000000000000000"
isPending={false}
@@ -150,7 +146,7 @@ function Component() {
<BadgeContainer label="Pending">
<MintableModuleUI
twAccount={twAccount}
isLoggedIn={true}
contractInfo={contractInfo}
moduleAddress="0x0000000000000000000000000000000000000000"
isPending={true}
@@ -14,7 +14,6 @@ import {
DialogTrigger,
} from "@/components/ui/dialog";
import { Skeleton } from "@/components/ui/skeleton";
import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi";
import * as Sentry from "@sentry/nextjs";
import { useMutation } from "@tanstack/react-query";
import { TransactionButton } from "components/buttons/TransactionButton";
@@ -44,7 +43,7 @@ type ModuleCardProps = {
version?: string;
publisher?: string;
}[];
twAccount: TWAccount | undefined;
isLoggedIn: boolean;
};
export function ModuleCard(props: ModuleCardProps) {
@@ -108,7 +107,7 @@ export function ModuleCard(props: ModuleCardProps) {
)}
>
<ModuleInstance
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
contract={contract}
contractInfo={{
name: contractInfo.name,
@@ -161,7 +160,7 @@ export function ModuleCard(props: ModuleCardProps) {
</Button>
<TransactionButton
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
txChainID={contract.chain.id}
transactionCount={1}
isPending={uninstallMutation.isPending}
@@ -1,6 +1,5 @@
"use client";
import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi";
import { lazy } from "react";
import type { ThirdwebContract } from "thirdweb";
import type { Account } from "thirdweb/wallets";
@@ -25,7 +24,7 @@ export type ModuleInstanceProps = Omit<
version?: string;
publisher?: string;
}[];
twAccount: TWAccount | undefined;
isLoggedIn: boolean;
};
export function ModuleInstance(props: ModuleInstanceProps) {
@@ -1,12 +1,10 @@
import { Checkbox } from "@/components/ui/checkbox";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Meta, StoryObj } from "@storybook/react";
import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
import { toast } from "sonner";
import { BadgeContainer } from "stories/utils";
import { BadgeContainer, storybookThirdwebClient } from "stories/utils";
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
import { accountStub } from "../../../../../../../stories/stubs";
import {
OpenEditionMetadataModuleUI,
type SetSharedMetadataFormValues,
@@ -57,7 +55,7 @@ function Component() {
<ThirdwebProvider>
<div className="container flex max-w-[1150px] flex-col gap-10 py-10">
<div>
<ConnectButton client={getThirdwebClient()} />
<ConnectButton client={storybookThirdwebClient} />
</div>
<div className="flex gap-2">
@@ -87,7 +85,7 @@ function Component() {
}}
isOwnerAccount={isOwner}
contractChainId={1}
twAccount={accountStub()}
isLoggedIn={true}
/>
</BadgeContainer>
</div>
@@ -1,12 +1,10 @@
import { Checkbox } from "@/components/ui/checkbox";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Meta, StoryObj } from "@storybook/react";
import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
import { toast } from "sonner";
import { BadgeContainer } from "stories/utils";
import { BadgeContainer, storybookThirdwebClient } from "stories/utils";
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
import { accountStub } from "../../../../../../../stories/stubs";
import {
type DefaultRoyaltyFormValues,
type RoyaltyInfoFormValues,
@@ -67,13 +65,11 @@ function Component() {
version: "1.0.0",
};
const twAccount = accountStub();
return (
<ThirdwebProvider>
<div className="container flex max-w-6xl flex-col gap-10 py-10">
<div>
<ConnectButton client={getThirdwebClient()} />
<ConnectButton client={storybookThirdwebClient} />
</div>
<div className="flex items-center gap-5">
@@ -99,7 +95,7 @@ function Component() {
}}
isOwnerAccount={isOwner}
contractChainId={1}
twAccount={twAccount}
isLoggedIn={true}
/>
</BadgeContainer>
@@ -118,7 +114,7 @@ function Component() {
}}
isOwnerAccount={isOwner}
contractChainId={1}
twAccount={twAccount}
isLoggedIn={true}
/>
</BadgeContainer>
@@ -137,7 +133,7 @@ function Component() {
}}
isOwnerAccount={isOwner}
contractChainId={1}
twAccount={twAccount}
isLoggedIn={true}
/>
</BadgeContainer>
@@ -157,7 +153,7 @@ function Component() {
}}
isOwnerAccount={isOwner}
contractChainId={1}
twAccount={twAccount}
isLoggedIn={true}
/>
</BadgeContainer>
</div>
@@ -1,12 +1,10 @@
import { Checkbox } from "@/components/ui/checkbox";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Meta, StoryObj } from "@storybook/react";
import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
import { toast } from "sonner";
import { BadgeContainer } from "stories/utils";
import { BadgeContainer, storybookThirdwebClient } from "stories/utils";
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
import { accountStub } from "../../../../../../../stories/stubs";
import {
type TransferableModuleFormValues,
TransferableModuleUI,
@@ -75,7 +73,7 @@ function Component() {
</div>
<div>
<ConnectButton client={getThirdwebClient()} />
<ConnectButton client={storybookThirdwebClient} />
</div>
<BadgeContainer label="Empty AllowList, Not Restricted">
@@ -92,7 +90,7 @@ function Component() {
}}
isOwnerAccount={isOwner}
contractChainId={1}
twAccount={accountStub()}
isLoggedIn={true}
/>
</BadgeContainer>
@@ -110,7 +108,7 @@ function Component() {
}}
isOwnerAccount={isOwner}
contractChainId={1}
twAccount={accountStub()}
isLoggedIn={true}
/>
</BadgeContainer>
@@ -128,7 +126,7 @@ function Component() {
}}
isOwnerAccount={isOwner}
contractChainId={1}
twAccount={accountStub()}
isLoggedIn={true}
/>
</BadgeContainer>
</div>
@@ -25,7 +25,10 @@ export default async function Page(props: {
if (contract.chain.id === localhost.id) {
return (
<ContractEditModulesPageClient contract={contract} twAccount={account} />
<ContractEditModulesPageClient
contract={contract}
isLoggedIn={!!account}
/>
);
}
@@ -35,5 +38,5 @@ export default async function Page(props: {
redirect(`/${params.chain_id}/${params.contractAddress}`);
}
return <ContractEditModulesPage contract={contract} twAccount={account} />;
return <ContractEditModulesPage contract={contract} isLoggedIn={!!account} />;
}
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
@@ -9,7 +8,7 @@ import { ContractNFTPage } from "./ContractNFTPage";
export function ContractNFTPageClient(props: {
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
@@ -32,7 +31,7 @@ export function ContractNFTPageClient(props: {
contract={props.contract}
isErc721={supportedERCs.isERC721}
functionSelectors={functionSelectors}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
/>
);
}
@@ -1,4 +1,3 @@
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import * as ERC721Ext from "thirdweb/extensions/erc721";
import * as ERC1155Ext from "thirdweb/extensions/erc1155";
@@ -16,14 +15,14 @@ interface NftOverviewPageProps {
isErc721: boolean;
tokenId?: string;
functionSelectors: string[];
twAccount: Account | undefined;
isLoggedIn: boolean;
}
export const ContractNFTPage: React.FC<NftOverviewPageProps> = ({
contract,
isErc721,
functionSelectors,
twAccount,
isLoggedIn,
}) => {
const isERC721ClaimToSupported =
ERC721Ext.isClaimToSupported(functionSelectors);
@@ -63,38 +62,38 @@ export const ContractNFTPage: React.FC<NftOverviewPageProps> = ({
<h2 className="font-semibold text-2xl tracking-tight">NFTs</h2>
<div className="grid grid-cols-2 gap-2 md:flex">
{isRevealable && (
<NFTRevealButton contract={contract} twAccount={twAccount} />
<NFTRevealButton contract={contract} isLoggedIn={isLoggedIn} />
)}
{isERC721ClaimToSupported && (
/**
* This button is used for claiming NFT Drop contract (erc721) only!
* For Edition Drop we have a dedicated ClaimTabERC1155 inside each Edition's page
*/
<NFTClaimButton contract={contract} twAccount={twAccount} />
<NFTClaimButton contract={contract} isLoggedIn={isLoggedIn} />
)}
{isMintToSupported && (
<NFTMintButton
contract={contract}
isErc721={isErc721}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
)}
{isSetSharedMetadataSupported && (
<NFTSharedMetadataButton
contract={contract}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
)}
{isLazyMintable && (
<NFTLazyMintButton
contract={contract}
isErc721={isErc721}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
/>
)}
{isLazyMintable && (
<BatchLazyMintButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
canCreateDelayedRevealBatch={canCreateDelayedRevealBatch}
isErc721={isErc721}
contract={contract}
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../../_components/page-skeletons";
import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client";
@@ -10,7 +9,8 @@ import { TokenIdPage } from "./token-id";
export function TokenIdPageClient(props: {
contract: ThirdwebContract;
tokenId: string;
twAccount: Account | undefined;
isLoggedIn: boolean;
accountAddress: string | undefined;
}) {
const { contract } = props;
const metadataQuery = useContractPageMetadata(props.contract);
@@ -34,7 +34,8 @@ export function TokenIdPageClient(props: {
contract={contract}
isErc721={supportedERCs.isERC721}
tokenId={props.tokenId}
twAccount={props.twAccount}
isLoggedIn={props.isLoggedIn}
accountAddress={props.accountAddress}
/>
);
}
@@ -9,7 +9,6 @@ import {
SheetTrigger,
} from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { TransactionButton } from "components/buttons/TransactionButton";
import { useTrack } from "hooks/analytics/useTrack";
import { useTxNotifications } from "hooks/useTxNotifications";
@@ -29,7 +28,7 @@ import {
interface AirdropTabProps {
contract: ThirdwebContract;
tokenId: string;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
/**
@@ -38,7 +37,7 @@ interface AirdropTabProps {
const AirdropTab: React.FC<AirdropTabProps> = ({
contract,
tokenId,
twAccount,
isLoggedIn,
}) => {
const address = useActiveAccount()?.address;
const { handleSubmit, setValue, watch, reset } = useForm<{
@@ -163,7 +162,7 @@ const AirdropTab: React.FC<AirdropTabProps> = ({
more, please do it in multiple transactions.
</p>
<TransactionButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
txChainID={contract.chain.id}
transactionCount={1}
isPending={sendAndConfirmTx.isPending}
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { FormControl, Input } from "@chakra-ui/react";
import { TransactionButton } from "components/buttons/TransactionButton";
import { useTrack } from "hooks/analytics/useTrack";
@@ -25,10 +24,10 @@ import {
interface BurnTabProps {
tokenId: string;
contract: ThirdwebContract;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
const BurnTab: React.FC<BurnTabProps> = ({ contract, tokenId, twAccount }) => {
const BurnTab: React.FC<BurnTabProps> = ({ contract, tokenId, isLoggedIn }) => {
const account = useActiveAccount();
const trackEvent = useTrack();
const {
@@ -137,7 +136,7 @@ const BurnTab: React.FC<BurnTabProps> = ({ contract, tokenId, twAccount }) => {
type="submit"
className="self-end"
txChainID={contract.chain.id}
twAccount={twAccount}
isLoggedIn={isLoggedIn}
>
Burn
</TransactionButton>
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { Flex, FormControl, Input } from "@chakra-ui/react";
import { TransactionButton } from "components/buttons/TransactionButton";
import { useTrack } from "hooks/analytics/useTrack";
@@ -15,13 +14,13 @@ import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components";
interface ClaimTabProps {
contract: ThirdwebContract;
tokenId: string;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
const ClaimTabERC1155: React.FC<ClaimTabProps> = ({
contract,
tokenId,
twAccount,
isLoggedIn,
}) => {
const trackEvent = useTrack();
const address = useActiveAccount()?.address;
@@ -125,12 +124,12 @@ const ClaimTabERC1155: React.FC<ClaimTabProps> = ({
</Flex>
<TransactionButton
isLoggedIn={isLoggedIn}
txChainID={contract.chain.id}
transactionCount={1}
isPending={form.formState.isSubmitting}
type="submit"
className="self-end"
twAccount={twAccount}
>
Claim
</TransactionButton>
@@ -10,7 +10,6 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { zodResolver } from "@hookform/resolvers/zod";
import { TransactionButton } from "components/buttons/TransactionButton";
import { useTrack } from "hooks/analytics/useTrack";
@@ -24,7 +23,7 @@ import { z } from "zod";
interface MintSupplyTabProps {
contract: ThirdwebContract;
tokenId: string;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
const mintAdditionalSupplyFormSchema = z.object({
@@ -37,7 +36,7 @@ const mintAdditionalSupplyFormSchema = z.object({
const MintSupplyTab: React.FC<MintSupplyTabProps> = ({
contract,
tokenId,
twAccount,
isLoggedIn,
}) => {
const trackEvent = useTrack();
const address = useActiveAccount()?.address;
@@ -132,7 +131,7 @@ const MintSupplyTab: React.FC<MintSupplyTabProps> = ({
isPending={sendAndConfirmTx.isPending}
type="submit"
className="self-end"
twAccount={twAccount}
isLoggedIn={isLoggedIn}
>
Mint
</TransactionButton>
@@ -1,6 +1,6 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { GenericLoadingPage } from "@/components/blocks/skeletons/GenericLoadingPage";
import { FormControl, Input } from "@chakra-ui/react";
import { TransactionButton } from "components/buttons/TransactionButton";
import { SolidityInput } from "contract-ui/components/solidity-inputs";
@@ -20,13 +20,13 @@ import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components";
interface TransferTabProps {
contract: ThirdwebContract;
tokenId: string;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
const TransferTab: React.FC<TransferTabProps> = ({
contract,
tokenId,
twAccount,
isLoggedIn,
}) => {
const account = useActiveAccount();
@@ -46,7 +46,11 @@ const TransferTab: React.FC<TransferTabProps> = ({
{ contract },
);
const { mutate, isPending } = useSendAndConfirmTransaction();
const sendTxAndConfirm = useSendAndConfirmTransaction();
if (checking1155) {
return <GenericLoadingPage />;
}
return (
<div className="flex w-full flex-col gap-2">
@@ -72,7 +76,7 @@ const TransferTab: React.FC<TransferTabProps> = ({
tokenId: BigInt(tokenId),
from: account?.address ?? "",
});
mutate(transaction, {
sendTxAndConfirm.mutate(transaction, {
onSuccess: () => {
trackEvent({
category: "nft",
@@ -126,14 +130,17 @@ const TransferTab: React.FC<TransferTabProps> = ({
)}
</div>
<TransactionButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
txChainID={contract.chain.id}
transactionCount={1}
isPending={isPending || checking1155}
isPending={sendTxAndConfirm.isPending}
type="submit"
className="self-end"
disabled={
!form.formState.isDirty || checking1155 || isPending || !account
!form.formState.isDirty ||
checking1155 ||
sendTxAndConfirm.isPending ||
!account
}
>
Transfer
@@ -1,6 +1,5 @@
"use client";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import {
Accordion,
AccordionButton,
@@ -54,7 +53,7 @@ type UpdateNftMetadataForm = {
* else (NFT Collection, Edition) -> use `setTokenURI`
*/
useUpdateMetadata: boolean;
twAccount: Account | undefined;
isLoggedIn: boolean;
};
export const UpdateNftMetadata: React.FC<UpdateNftMetadataForm> = ({
@@ -62,7 +61,7 @@ export const UpdateNftMetadata: React.FC<UpdateNftMetadataForm> = ({
nft,
useUpdateMetadata,
setOpen,
twAccount,
isLoggedIn,
}) => {
const trackEvent = useTrack();
const address = useActiveAccount()?.address;
@@ -408,7 +407,7 @@ export const UpdateNftMetadata: React.FC<UpdateNftMetadataForm> = ({
Cancel
</Button>
<TransactionButton
twAccount={twAccount}
isLoggedIn={isLoggedIn}
txChainID={contract.chain.id}
transactionCount={1}
isPending={sendAndConfirmTx.isPending}
@@ -6,7 +6,6 @@ import {
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { useState } from "react";
import type { NFT, ThirdwebContract } from "thirdweb";
import { UpdateNftMetadata } from "./update-metadata-form";
@@ -19,14 +18,14 @@ interface UpdateMetadataTabProps {
* else (NFT Collection, Edition) -> use `setTokenURI`
*/
useUpdateMetadata: boolean;
twAccount: Account | undefined;
isLoggedIn: boolean;
}
const UpdateMetadataTab: React.FC<UpdateMetadataTabProps> = ({
contract,
nft,
useUpdateMetadata,
twAccount,
isLoggedIn,
}) => {
const [open, setOpen] = useState(false);
@@ -40,7 +39,7 @@ const UpdateMetadataTab: React.FC<UpdateMetadataTabProps> = ({
<SheetTitle className="text-left">Update NFT Metadata</SheetTitle>
</SheetHeader>
<UpdateNftMetadata
twAccount={twAccount}
isLoggedIn={isLoggedIn}
contract={contract}
nft={nft}
useUpdateMetadata={useUpdateMetadata}

Some files were not shown because too many files have changed in this diff Show More