Compare commits

..
Author SHA1 Message Date
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
001e5102c9 Version Packages (#6851)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-26 04:19:03 -07:00
adcbec607f [SDK] Fix: Add custom title & icon to wagmi connectors (#6858)
Co-authored-by: Joaquim Verges <[email protected]>
2025-04-26 03:07:16 -07:00
joaquim-verges d43ad17e8b [Portal] Fix HTTP method in wallet users API example from POST to GET (#6868)
<!--

## 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 documentation for fetching user details from the in-app wallet API, providing clearer instructions, examples, and a new method for retrieving user data.

### Detailed summary
- Changed the title from `Get Users` to `Fetch a Single User`.
- Added detailed instructions for fetching user details via a `GET` request.
- Specified query parameters and their descriptions.
- Included authentication requirements.
- Provided example `curl` commands for fetching user details by email and wallet address.
- Described the response format for the API.
- Introduced convenience methods using the `getUser` method from the thirdweb SDK.
- Updated the command for fetching all users from `POST` to `GET`.

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

<!-- end pr-codex -->
2025-04-26 09:15:12 +00:00
MananTank dbb782430a [TOOL-4291] Dashboard: Show recommended team members in invite section (#6867)
<!--

## 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 team onboarding and member management features by introducing a `client` prop for API interactions, implementing recommended members functionality, and updating various components to utilize this new feature effectively.

### Detailed summary
- Added `client` prop to `InviteTeamMembers` and related components.
- Implemented `getRecommendedMembers` function to fetch recommended team members.
- Updated `TeamMembersSettingsPage` to display recommended members.
- Enhanced `InviteSection` to handle recommended members.
- Modified story files to include new client interactions and recommended members.

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

<!-- end pr-codex -->
2025-04-26 08:23:25 +00:00
MananTank 692c25ed83 Dashboard: Fix team invite UI not showing feedback on submit (#6866)
<!--

## 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 improving the `InviteSection` component by modifying the form handling logic and enhancing the user interface with additional functionality.

### Detailed summary
- Removed the `useEffect` hook that reset the mutation result on form updates.
- Changed the form's `onChange` to reset the mutation result if `sendInvites.data` exists.
- Updated the button layout to include a `Reset` button for clearing the form and resetting invites.
- Adjusted class names for better spacing and alignment.

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

<!-- end pr-codex -->
2025-04-25 21:55:27 +00:00
MananTank d568f8ff35 [NEB-192] Nebula: Add favicon (#6865)
<!--

## 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 adding a favicon to the `nebula-app` by linking the favicon file in the HTML head section.

### Detailed summary
- Added a `<link>` element to the `<head>` of the HTML to include the favicon located at `/assets/nebula/favicon.ico`.

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

<!-- end pr-codex -->
2025-04-25 20:18:13 +00:00
MananTank 7b0a213591 Nebula: Save auth token for 14d (#6864)
<!--

## 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 session cookie expiration time from 3 days to 14 days by introducing a constant `FOURTEEN_DAYS_IN_SECONDS`. This change is reflected in multiple functions related to user login and session management.

### Detailed summary
- Added constant `FOURTEEN_DAYS_IN_SECONDS` set to `14 * 24 * 60 * 60`.
- Updated `maxAge` for cookies in `doNebulaLogin` to `FOURTEEN_DAYS_IN_SECONDS`.
- Updated `maxAge` for cookies in `isNebulaLoggedIn` to `FOURTEEN_DAYS_IN_SECONDS`.

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

<!-- end pr-codex -->
2025-04-25 19:43:03 +00:00
MananTank fee560a265 [NEB-196] Tx button improvements: hide switch chain and approve tx popovers when possible (#6863)
<!--

## 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 `TransactionButton` and `MismatchButton` components by incorporating wallet connection status and improving transaction handling. It introduces logic for determining if a transaction requires external confirmation based on the connected wallets.

### Detailed summary
- Added `useConnectedWallets` to `TransactionButton`.
- Introduced `canSendTransactionWithoutConfirmation` function to check if a wallet can send transactions without confirmation.
- Removed `isGasless` prop from `TransactionButton`.
- Updated `MismatchButton` to include `isPending` prop.
- Added connection status handling in `MismatchButton`.
- Enhanced transaction handling based on network mismatch and wallet conditions.
- Implemented `canSwitchNetworkWithoutConfirmation` function to determine switching requirements.

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

<!-- end pr-codex -->
2025-04-25 19:43:03 +00:00
MananTank 2d27c4fce0 [NEB-215] Update transaction confirmation prompt (#6861)
<!--

## 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 transaction message in the `Chats` component to use a new function for generating the prompt, enhancing clarity and consistency in user communication.

### Detailed summary
- Replaced the inline message in `onTxSettled` with a call to `getTransactionSettledPrompt`.
- Added the `getTransactionSettledPrompt` function, which formats a detailed success message for a settled transaction.

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

<!-- end pr-codex -->
2025-04-25 19:43:02 +00:00
MananTank 72628ef63c [NEB-191] Nebula, Dashboard: Reduce vertical spacing in markdown renderer (#6860)
<!--

## 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 refining the styling of various components in the dashboard application, enhancing visual consistency and layout adjustments.

### Detailed summary
- In `Chats.tsx`, removed a conditional class for the `assistant` type.
- In `ExecuteTransactionCard.tsx`, adjusted padding from `py-6` to `py-5`.
- In `markdown-renderer.tsx`, modified heading margins and added an `hr` element.
- Updated list margins in `MarkdownRenderer`.

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

<!-- end pr-codex -->
2025-04-25 19:43:02 +00:00
GiselleNessi 7087b3fcbc Dashboard: add nebula chat on support page, update CTAs (#6862)
<!--

## 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 `FloatingChat` component by renaming it to `NebulaChatButton` and adding support for a new `support` page type. It also updates various components and their usage across the application to reflect this change.

### Detailed summary
- Renamed `NebulaFloatingChatButton` to `NebulaChatButton`.
- Added `support` as a new value for the `pageType` property.
- Updated imports and component usage to reflect the new name.
- Added `isFloating` prop to `NebulaChatButton`.
- Modified `SupportPage` to utilize `NebulaChatButton` for support inquiries.

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

<!-- end pr-codex -->
2025-04-25 19:34:39 +00:00
MananTank bb554e568d [NEB-211] Nebula: Hide tx button after transaction is confirmed (#6859)
<!--

## 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 UI and functionality of the `ExecuteTransactionCard` component in the dashboard app. It includes visual improvements and adjustments to transaction handling.

### Detailed summary
- Updated the border styling for child elements in the main `div`.
- Changed text styles for labels (`From`, `To`, `Value`, `Status`, and `Transaction Hash`) to use `font-medium`.
- Added a conditional rendering for the footer based on transaction status.
- Improved transaction tracking event labels.
- Refactored the transaction submission logic for clarity.

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

<!-- end pr-codex -->
2025-04-25 16:03:10 +00:00
0xFirekeeper 90823ef3b8 [Portal] Update Unity Onchain Interactions link (#6857)
Closes TOOL-4290

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the sidebar in the `sidebar.tsx` file, changing a menu item to better reflect its content.

### Detailed summary
- Changed the menu item name from `Full Reference` to `Contract Extensions`.
- Updated the `href` from `https://thirdweb-dev.github.io/dotnet/index.html` to `/dotnet/contracts/extensions`.

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

<!-- end pr-codex -->
2025-04-25 15:57:48 +00:00
MananTank 94949bc4b3 Nebula: Fix wallet address not set in context when clicking example prompt on login page (#6856)
<!--

## 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 `ChatPageContent` component by adding the `accountAddress` prop and updates various pages to retrieve and pass this new data. It ensures that both `authToken` and `accountAddress` are validated before proceeding.

### Detailed summary
- Added `accountAddress` prop to `ChatPageContent`.
- Updated `walletAddress` assignment to prioritize `props.accountAddress`.
- Modified `Page` components to fetch `accountAddress` using `getNebulaAuthTokenWalletAddress`.
- Updated authentication checks to validate both `authToken` and `accountAddress`.

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

<!-- end pr-codex -->
2025-04-25 14:57:29 +00:00
Joaquim VergesandGitHub 3825563d88 [Portal] Separate LLM content extraction from search data extraction (#6854) 2025-04-25 05:33:29 -07:00
Jonas DanielsandGitHub f0dce0ab22 Update plan tier hierarchy for sponsorship policies (#6853) 2025-04-25 10:48:14 +02:00
Manan TankandGitHub e28eb73894 SDK: Signature screen loading state improvements (#6849) 2025-04-25 01:42:02 +02:00
Toomas OosaluandGitHub 185a3cbe21 fix typo in insight docs (#6848) 2025-04-25 01:41:31 +02:00
Jonas DanielsandGitHub 473c9cc5b0 Add domain verification functionality for teams (#6850) 2025-04-25 01:41:09 +02:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
9eab56fff2 Version Packages (#6847)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-24 15:06:24 -07:00
Manan TankandGitHub b6fdd0cc36 SDK: Remove @description tags from JSDoc (#6844) 2025-04-24 14:52:14 -07:00
Joaquim VergesandGitHub 31ce837b54 [SDK] Only update isLoggedIn state on successful logins (#6846) 2025-04-24 14:50:35 -07:00
GiselleNessi 68aa693b18 removed wallet connect coming soon title in portal docs (#6845)
<!--

## 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 `page.mdx` file in the `faq` section of the `ecosystem` by modifying the mention of `WalletConnect` to reflect its new branding as `Reown` and clarifying the billing details without altering the content.

### Detailed summary
- Changed the heading from `WalletConnect (Coming Soon)` to `WalletConnect [(now Reown)](https://reown.com/)`.
- No content changes in the billing section, but ensured proper formatting by adding a newline at the end of the file.

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

<!-- end pr-codex -->
2025-04-24 21:03:35 +00:00
MananTank 270e53ccaf [TOOL-4280] Nebula: Send automatic msg after sent transaction is settled (#6843)
<!--

## 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 chat and transaction components by adding a `sendMessage` prop and handling transaction settlement events. It improves the interaction between components and provides better logging for transaction settlements.

### Detailed summary
- Added `sendMessage` prop to `FloatingChatContent` and `ChatPageContent`.
- Introduced `onTxSettled` callback in `ExecuteTransactionCard` and `ExecuteTransactionCardLayout`.
- Enhanced `Chats` component to utilize `sendMessage` for transaction confirmations.
- Implemented logging for transaction settlements in `ExecuteTransactionCard`.

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

<!-- end pr-codex -->
2025-04-24 19:41:15 +00:00
Toomas OosaluandGitHub 7809c6204e update insight playground nav (#6840) 2025-04-24 10:58:49 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
31e87b07a8 Version Packages (#6835)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-24 19:26:56 +02:00
Jonas DanielsandGitHub f2d0f119c1 Add webhooks capability to insight team capabilities (#6842) 2025-04-24 19:23:52 +02:00
saminacodes dc07fdd952 [Portal] removed glossary, added new articles, deprecated old pages (#6841)
CORE-0000

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on the removal of various glossary-related files and the addition of new features and content in the sidebar navigation for different sections of the application.

### Detailed summary
- Deleted multiple `.mdx` files related to the glossary in `apps/portal/src/app/glossary/`.
- Updated sidebar navigation in `apps/portal/src/app/account/sidebar.tsx` to rename "API Keys" to "Projects" and added "Transfer Projects".
- Added new sidebar entry "Playground" in `apps/portal/src/app/engine/sidebar.tsx`.
- Enhanced `apps/portal/src/app/account/api-keys/transfer-projects/page.mdx` with steps for transferring projects, including images.
- Improved `apps/portal/src/app/connect/pay/fees/page.mdx` with detailed explanations of fees.
- Introduced `glossaryRedirects` in `apps/portal/redirects.mjs` for various glossary pages.

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

<!-- end pr-codex -->
2025-04-24 17:14:35 +00:00
MananTank 1d598ce907 Nebula: Use Nebula project keys (#6839)
<!--

## 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 authentication and client configuration for the `Nebula` application by replacing the usage of the `DASHBOARD_THIRDWEB_SECRET_KEY` with `NEBULA_APP_SECRET_KEY`, and modifying the thirdweb client setup.

### Detailed summary
- Added `NEXT_PUBLIC_NEBULA_APP_CLIENT_ID` and `NEBULA_APP_SECRET_KEY` to `env.ts`.
- Replaced `nebulaThirdwebClient` with `nebulaAppThirdwebClient` in `EmptyStateChatPageContent.tsx`.
- Updated headers in `auth-actions.ts` to use `NEBULA_APP_SECRET_KEY`.
- Refactored `nebulaThirdwebClient.ts` to create a new client using `NEBULA_APP_SECRET_KEY` and `NEXT_PUBLIC_NEBULA_APP_CLIENT_ID`.
- Updated `NebulaConnectWallet` component to use `nebulaAppThirdwebClient`.

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

<!-- end pr-codex -->
2025-04-24 16:08:07 +00:00
MananTank f157e3484a [TOOL-4277] Dashboard: Update gated features (#6838)
<!--

## 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 handling of team billing plans across various components in the application. It introduces new properties for better plan management and refines the UI elements related to billing features.

### Detailed summary
- Added `teamSlug` prop to several components for better context.
- Changed `validTeamPlan` to `teamPlan` in multiple files for consistency.
- Introduced `planToTierRecordForGating` to manage billing plans.
- Updated UI components to reflect new billing logic and props.
- Refactored `GatedSwitch` to utilize `currentPlan` and `requiredPlan`.
- Enhanced form handling in `InAppWalletSettingsUI` and other components to reflect billing plan changes.
- Renamed story exports for clarity in `GatedSwitch` stories.

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

<!-- end pr-codex -->
2025-04-24 14:50:39 +00:00
arcoraven d95b3d38c7 chore: Add unthreadCustomerId to TeamResponse (#6837)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on adding a new property, `unthreadCustomerId`, to the `TeamResponse` structure across various files, enhancing the data model for teams in the application.

### Detailed summary
- Added `unthreadCustomerId: null` to the `TeamResponse` in `apps/dashboard/src/stories/stubs.ts`.
- Updated `validTeamAndProjectResponse` to include `unthreadCustomerId` in `packages/service-utils/src/mocks.ts`.
- Defined `unthreadCustomerId: string | null` in the `TeamResponse` interface in `packages/service-utils/src/core/api.ts`.

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

<!-- end pr-codex -->
2025-04-24 11:19:05 +00:00
Manan TankandGitHub b50b320144 [NEB-189] Nebula: Minor UI tweaks (#6831) 2025-04-24 01:20:18 -07:00
Joaquim VergesandGitHub 4c339363c0 [SDK] Always checksum Account.address (#6834) 2025-04-24 01:19:54 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
125cc972de Version Packages (#6833)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-23 20:07:15 -07:00
Joaquim VergesandGitHub 436750ed15 [SDK] Lazy import native dependencies in React Native (#6832) 2025-04-23 19:35:46 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
9cf5ba83e2 Version Packages (#6820)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-23 17:00:56 -07:00
Joaquim VergesandGitHub 33666aa8c5 [SDK] Optimize multi-event querying with indexer (#6822) 2025-04-23 16:36:10 -07:00
gregfromstl e526f75f22 [SDK] Feature: Update sdk bridge functions with new schema (#6830)
<!--

## 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 SDK Bridge functionality by standardizing parameter naming, introducing a `purchaseData` field, and improving quote structures with a `steps` array. It also updates API interactions to use JSON bodies for complex data.

### Detailed summary
- Standardized parameter naming: `buyAmountWei` and `sellAmountWei` changed to `amount`.
- Added optional `purchaseData` parameter to Buy and Sell functions.
- Introduced `steps` array in quotes for detailed transaction information.
- Enhanced status responses to include `purchaseData`.
- Updated API interactions to use JSON body instead of query parameters for prepare functions.

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

<!-- end pr-codex -->
2025-04-23 20:43:50 +00:00
Jonas DanielsandGitHub f912e42342 Add plan cancellation and re-subscription functionality (#6826) 2025-04-23 21:10:47 +02:00
MananTank fe46e81cd5 [TOOL-4248] Dashboard: Fix explore category page not showing contract cards (#6829)
<!--

## 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 `ExploreCategoryPage` component in the `page.tsx` file to utilize a new client obtained from `getThirdwebClient` instead of passing it as a prop. This change simplifies the component's props and enhances its internal logic.

### Detailed summary
- Removed the `client` prop from `ExploreCategoryPageProps`.
- Introduced `const client = getThirdwebClient(undefined);` to obtain the client internally.
- Updated the `client` prop of the `ContractCard` component to use the newly defined `client` variable instead of `props.client`.

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

<!-- end pr-codex -->
2025-04-23 18:01:55 +00:00
jnsdls 525e60393f Add support for additional languages in Framer rewrites (#6828)
closes: TOOL-4244

# Add support for additional languages in Framer rewrites

This PR adds support for additional languages in the Framer rewrite paths. It introduces a new constant `FRAMER_ADDITIONAL_LANGUAGES` with Spanish ("es") as the initial additional language.

The rewrites configuration has been updated to:
- Keep the existing English paths
- Generate additional paths for each language in the format `/{lang}{path}`
- Direct these paths to the corresponding landing page URLs

This change enables proper routing for localized landing pages.
2025-04-23 16:17:06 +00:00
MananTank 4baefa5dae Dashboard: transfer tab disabled for owner of nft (#6827)
<!--

## 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 `useNFTDrawerTabs` function by improving the owner address comparison for NFTs. It now uses the `checksumAddress` utility to ensure that both the NFT owner and the account address are valid checksummed addresses before comparing them.

### Detailed summary
- Added import for `checksumAddress` from `thirdweb/utils`.
- Updated the owner address comparison in the `useNFTDrawerTabs` function to use `checksumAddress`.
- Ensured that both `accountAddress` and `nft.owner` are checked for validity before comparison.

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

<!-- end pr-codex -->
2025-04-23 13:20:53 +00:00
YashandGitHub b2e56f89fe Custom factory addr for Chiliz mainnet (#6824) 2025-04-23 14:36:53 +02:00
Manan TankandGitHub ec194a623e Dashboard: Fix login infinite loop on certain wallets (#6825) 2025-04-23 13:29:35 +02:00
YashandGitHub dc2e514e56 Deprecate pack (#6823) 2025-04-23 02:30:18 -07:00
Jonas DanielsandGitHub ec5362b84a Update dependencies and add atomicRequired option to sendCalls (#6786) 2025-04-23 01:25:24 -07:00
YashandGitHub 683e3b0838 Remove Pack from explore (#6781) 2025-04-23 01:18:11 -07:00
Joaquim VergesandGitHub 8b4b873317 [SDK] Add service key functionality for API requests (#6819) 2025-04-23 16:03:29 +12:00
Manan TankandGitHub 67a7d3e3a5 [NEB-6814] Nebula: move outside of app layout, connect to smart wallet on login (#6816) 2025-04-23 01:33:15 +02:00
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
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
c7cb6ae7a4 Version Packages (#6728)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-17 18:01:02 -07:00
Joaquim VergesandGitHub 7ecfcb95af [SDK] Add thirdweb branding to PayEmbed (#6760) 2025-04-17 17:55:11 -07:00
Joaquim VergesGitHubCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
4cf15a2475 [SDK] Use insight for 1155 getNFTs, getOwnedNFTs and getNFT (#6753)
Signed-off-by: Joaquim Verges <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-04-17 17:01:20 -07:00
Joaquim VergesandGitHub 62ce05ec35 [SDK] Improve error message for getUser (#6758) 2025-04-17 16:12:42 -07:00
MananTank 563c4c7df1 Dashboard: disable prefetch for footer links (#6756)
<!--

## 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 refactors the `AppFooter` component in `app-footer.tsx` by introducing a new `footerLinks` array to manage footer link data and replaces individual `Link` components with a new `FooterLink` function to enhance maintainability and readability.

### Detailed summary
- Added `footerLinks` array containing link objects with `href` and `label`.
- Replaced individual `Link` components with a mapped `FooterLink` component.
- Introduced `FooterLink` function to streamline link rendering and improve code clarity.

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

<!-- end pr-codex -->
2025-04-17 19:41:27 +00:00
Joe-ThirdwebandGitHub 32c91abc02 [Dashboard] Adding telegram field to support ticket creation (#6754) 2025-04-17 21:41:09 +02:00
Manan TankandGitHub 7a2dc25129 [NEB-167] Add Nebula floating chat button in chain and contract pages (#6750) 2025-04-17 18:00:20 +02:00
jnsdls 32568fd421 Add "Invite Team Members" button to team overview page (#6755)
# Add "Invite Team Members" Button to Team Overview Page

This PR adds a new "Invite Team Members" button to the Team Overview page, replacing the previous implementation. The changes include:

- Created a new reusable `InviteTeamMembersButton` component
- Added the button to the Team Overview page header
- Removed the old "Share Button" component from the TeamProjectsPage
- Improved the layout of the Team Overview page header to accommodate the new button
- Maintained analytics tracking for the invite action

The button links to the team members settings page and includes a user plus icon for better visual indication of its purpose.
2025-04-17 08:06:51 +00:00
GiselleNessi 9091e813ab added invite team button in dash homepage (#6751)
<!--

## 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 a new `ShareButton` component to the `TeamProjectsPage`, allowing users to invite team members. It also tracks the button click event for analytics and includes a link to the team members settings.

### Detailed summary
- Added `ShareButton` component for inviting team members.
- Integrated `useTrack` hook to track button click events.
- Added a link to the team members settings using `Link`.
- Imported `UserPlus` icon from `lucide-react`.

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

<!-- end pr-codex -->
2025-04-17 05:21:12 +00:00
Joaquim VergesandGitHub 23d375778b [SDK] Simplify RPC request handling (#6752) 2025-04-16 18:07:46 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e7b0110803 chore(deps-dev): bump msw from 2.7.3 to 2.7.4 (#6723)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 16:49:50 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1825ac857e chore(deps-dev): bump vite from 6.2.5 to 6.2.6 in the npm_and_yarn group (#6711)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 16:09:15 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
3ce2d0602e chore(deps-dev): bump @types/react-dom from 19.1.1 to 19.1.2 (#6721)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 16:08:43 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4bde2eb157 chore(deps): bump @abstract-foundation/agw-react from 1.5.4 to 1.6.4 (#6722)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 16:06:23 -07:00
Joaquim VergesandGitHub 8d4d991e57 [SDK] Use insight for erc721/getNFTs and erc721/getOwnedNFTs (#6741) 2025-04-16 15:53:14 -07:00
MananTank 22090cc400 [NEB-170] Nebula: Various UI changes (#6747)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on enhancing the user interface and experience of the `Nebula` app by updating text content, adjusting styles, and introducing a new `StyledMarkdownRenderer` component for better message rendering.

### Detailed summary
- Updated text in `EmptyStateChatPageContent.tsx` and `ContextFilters.tsx` to reflect new terminology.
- Changed section headings from `Context` to `Settings`.
- Improved styling for various components, including `ChatSidebarLink` and buttons.
- Introduced `StyledMarkdownRenderer` for consistent message formatting.
- Adjusted scrollbar styles in `global.css`.
- Modified button states and classes for better responsiveness and appearance.

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

<!-- end pr-codex -->
2025-04-16 21:16:02 +00:00
MananTank 95a6c38703 [NEB-173]: Hide testnets in nebula chain selector (#6746)
<!--

## 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 ability to hide test networks in the `MultiNetworkSelector` component and updates its props accordingly.

### Detailed summary
- Added `hideTestnets` prop to `MultiNetworkSelector` in `NetworkSelectors.tsx`.
- Implemented filtering of `sortedChains` to exclude test networks if `hideTestnets` is true.
- Updated dependency array in `useEffect` to include `props.hideTestnets`.

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

<!-- end pr-codex -->
2025-04-16 21:16:01 +00:00
MananTank b1060de7da [NEB-172]: Remove Nebula waitlist (#6745)
<!--

## 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 removing components and related functionality for managing the Nebula waitlist in the application. It simplifies the codebase by eliminating unused imports and references to the waitlist, streamlining the `ProjectSidebarLayout`, and adjusting the layout logic.

### Detailed summary
- Deleted files related to the Nebula waitlist functionality.
- Removed `getTeamNebulaWaitList` function and its associated type.
- Updated `ProjectSidebarLayout` to eliminate the `showNebula` prop.
- Simplified `ProjectLayout` by removing waitlist checks and related logic.
- Adjusted `Page` component to remove references to `NebulaWaitListPage`.
- Cleaned up the `Layout` component by removing waitlist handling and related imports.

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

<!-- end pr-codex -->
2025-04-16 21:16:01 +00:00
jnsdls 0aca56317a Fix RPC usage page to handle missing peak date (#6748)
This PR adds a null check for the peak date in the RPC Usage page to handle cases where there are no requests in the last 24 hours. It displays "No Requests in last 24 hours" instead of attempting to format a non-existent date. Additionally, it destructures the `averageRate` directly from the API data to improve code readability when passing it to the RateGraph and CountGraph components.
2025-04-16 16:42:07 +00:00
MananTank 5463baa7da Nebula: Fix chat session randomly showing empty state after first prompt (#6744)
<!--

## 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 addresses an issue with updating the page URL in the `ChatPageContent.tsx` file due to changes in Next.js behavior. The existing method for updating the URL is commented out, and a temporary solution is proposed.

### Detailed summary
- Commented out the line: `window.history.replaceState({}, "", `/chat/${sessionId}`);`
- Added comments explaining that the URL update does not work anymore due to Next.js changes.
- Stated intention to not update the URL until a better solution is found.

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

<!-- end pr-codex -->
2025-04-16 11:34:58 +00:00
MananTank 64ffe199af Dashboard: Remove reo.dev from CSP script-src in next.config (#6740)
<!--

## 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 Content Security Policy (CSP) in the `next.config.ts` file by modifying the `script-src` directive, removing `static.reo.dev` from the list of allowed sources.

### Detailed summary
- Removed `static.reo.dev` from the `script-src` directive in the CSP.
- No other changes to the CSP directives were made.

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

<!-- end pr-codex -->
2025-04-16 10:53:29 +00:00
MananTank 76b522e7bc Portal: Use next-themes for theme management instead of custom code (#6739)
<!--

## 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 theme management in the application by integrating `next-themes` for better theme switching functionality and adding a new `Skeleton` component for loading states.

### Detailed summary
- Removed `theme.tsx` and its usage.
- Added `next-themes` dependency in `package.json`.
- Introduced `Skeleton` component in `skeleton.tsx`.
- Updated `ThemeSwitcher` to use `next-themes` for theme management.
- Implemented `useIsClientMounted` for client-side rendering checks.
- Wrapped the application in `ThemeProvider` in `layout.tsx`.
- Modified CSS for dark and light themes.

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

<!-- end pr-codex -->
2025-04-16 10:43:16 +00:00
Joe-Thirdweb 49786404b3 [Dashboard] Feature : Adding Next Steps guide into chain pages (#6743)
Add actionable next step guide for users who visit a chain page.

Steps to Test:
1. Visit thirdweb.com/{chainID}
2. Scroll to the bottom and you should see
![image](https://github.com/user-attachments/assets/df68ad4b-a0b7-4384-9e2d-1bb207400087)
3. Click on the article
4. Get redirected to a blog post.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new component called `NextSteps` to the `page.tsx` file in the dashboard application. This component provides users with actionable links and information related to integrating in-app wallets, enhancing user experience and adoption.

### Detailed summary
- Added `NextSteps` component to `page.tsx`.
- `NextSteps` displays a title and a link for creating a login for the specified `chain`.
- Integrated tracking for link clicks using `useTrack` to monitor user engagement.
- Included a brief description under the link in `NextSteps`.

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

<!-- end pr-codex -->
2025-04-16 05:24:27 +00:00
MananTank 64bd603301 Nebula: Do not override chains set from search params (#6738)
<!--

## 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 logic for updating `updatedContextFilters.chainIds` by ensuring that it only attempts to retrieve last used chains from storage when `chainIds` is empty. It also maintains error handling for local storage access.

### Detailed summary
- Moved the check for `updatedContextFilters.chainIds` length to before retrieving last used chains.
- Retained the logic to fetch last used chain IDs from storage if `chainIds` is empty.
- Preserved error handling for local storage access.

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

<!-- end pr-codex -->
2025-04-15 22:16:26 +00:00
MananTank 60a5b7c3e7 [NEB-123] Nebula: Allow setting context with search params (#6736)
<!--

## 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 chat functionality by introducing `prefillMessage` support across various components and updating the way parameters are handled in the chat interface.

### Detailed summary
- Replaced `initialPrompt` with `initialParams` in chat components.
- Added `prefillMessage` prop to `ChatBar`, `EmptyStateChatPageContent`, and related story files.
- Updated `NebulaLogin` to handle search parameters.
- Modified `ChatPageContent` to utilize `initialParams` for message handling.
- Introduced a new function `getChainIds` for fetching chain IDs based on user input.

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

<!-- end pr-codex -->
2025-04-15 21:44:25 +00:00
jnsdls baf95b9077 Fix RPC usage graphs and improve rate limit visualization (#6737)
# Improve RPC Usage Graphs

This PR enhances the RPC usage graphs in the dashboard:

- Only show rate limited requests in the legend when there are actually rate limited requests
- Fix data processing by removing the `.slice(1, -1)` which was incorrectly trimming data points
- Ensure numeric values by explicitly converting to Number
- Move data slicing to the parent component for better control
- Add a type assertion comment to handle the TypeScript warning
- Add a check to conditionally display relevant chart configuration

These changes improve the accuracy and presentation of the RPC usage data visualization.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `CountGraph` component by improving the handling of rate-limited data and formatting the `currentRateLimit` value for better readability.

### Detailed summary
- Updated the `currentRateLimit` display to use `toLocaleString()`.
- Added a check for `hasAnyRateLimited` to conditionally configure the chart.
- Modified the `config` object to include `rateLimitedCount` only if there are any rate-limited requests.
- Converted `includedCount` and `rateLimitedCount` to `Number` type during data mapping.

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

<!-- end pr-codex -->
2025-04-15 21:16:37 +00:00
jnsdls e29f749342 Add support for starter, accelerate, and scale tiers (#6735)
fixes: CORE-826

# Add support for new pricing tiers in ticket creation

This PR adds support for the new pricing tiers (starter, accelerate, scale) in the ticket creation system. It updates the `planToCustomerId` mapping to include these new tiers and improves the fallback logic to use the free tier when a customer's plan doesn't have a corresponding Unthread customer ID.
2025-04-15 22:15:28 +02:00
Joaquim VergesandGitHub 2a7df337f7 [SDK] Improve error messages for failed HTTP requests (#6734) 2025-04-15 12:43:54 -07:00
jnsdls 348e3ab4ad Add RPC usage dashboard with rate limit monitoring (#6733)
currently blocked by analytics API needing to go to prod

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new RPC usage dashboard for monitoring requests over the last 24 hours. It adds new components for visualizing request data and enhances the existing layout to accommodate this functionality.

### Detailed summary
- Added a new `RPC` tab in the usage layout.
- Introduced `CountGraph` and `RateGraph` components for visualizing RPC data.
- Implemented API call to fetch last 24 hours RPC usage data.
- Enhanced `ThirdwebAreaChart` to support max limit display.
- Updated error handling and user feedback for API data fetching.

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

<!-- end pr-codex -->
2025-04-15 19:25:24 +00:00
MananTank e4590a7ada [NEB-168] Nebula: Improve Execute Transaction Card UI (#6731)
![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/yNOf1svJ8o3zjO7zQouZ/5807d025-2166-4b28-bedb-2baccd52054c.png)
2025-04-16 00:42:26 +05:30
gregandGitHub a45c558e06 [SDK] Fix: Improve PayEmbed error message parsing (#6732) 2025-04-15 11:45:48 -07:00
a3e7300723 Get events from insight (#6683)
Co-authored-by: Joaquim Verges <[email protected]>
2025-04-15 04:02:59 -07:00
YashandGitHub 4c1f384a63 Fix dynamic contract publish (#6729) 2025-04-15 02:55:20 -07:00
arcoraven ec099a781a chore: Update rate limit to sliding window (10s) (#6730)
Updates the rate limit to use a sliding window

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the rate limiting functionality in the `rateLimit` function to utilize a sliding window approach instead of a fixed window, enhancing how requests are counted over time.

### Detailed summary
- Renamed `RATE_LIMIT_WINDOW_SECONDS` to `SLIDING_WINDOW_SECONDS`.
- Modified Redis interaction to use `mget` for batch fetching request counts.
- Updated the logic to calculate total requests over the last 10 seconds.
- Removed `sampleRate` handling and its related logic.
- Changed error messages for exceeding rate limits.
- Adjusted tests to align with the new sliding window logic.

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

<!-- end pr-codex -->
2025-04-15 09:30:46 +00:00
Joaquim VergesandGitHub 185d2f309c [SDK] Add Insight API client (#6706) 2025-04-14 20:33:40 -07:00
gregandGitHub e8fb41752e [Dashboard] Feature: bridge UI (#6727) 2025-04-14 16:53:46 -07:00
jnsdls 3f42f522b7 Add RPC usage chart to dashboard (#6719)
closes: TOOL-4079

### TL;DR

Added RPC usage chart to the team usage overview page.

### What changed?

- Created a new API endpoint `fetchRPCUsage` to retrieve RPC usage data from the analytics service
- Added a stacked bar chart to visualize RPC usage in the team usage overview page
- The chart displays both regular RPC requests and rate-limited requests
- Replaced the static "Unlimited Requests" display with an interactive visualization

### How to test?

1. Navigate to a team's usage overview page
2. Verify that the RPC Requests section now shows a stacked bar chart
3. Confirm that the chart displays data for the past 7 days
4. Check that the chart properly differentiates between regular RPC requests and rate-limited requests

### Why make this change?

This change provides teams with better visibility into their RPC usage patterns over time, allowing them to monitor both successful requests and rate-limited requests. This visualization helps teams understand their API consumption patterns and make informed decisions about their plan requirements.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new feature to fetch and display RPC usage data in the dashboard. It enhances the usage overview by integrating RPC metrics, providing insights into included and rate-limited requests.

### Detailed summary
- Added `RPCUsageDataItem` type and `fetchRPCUsage` function in `rpc.ts`.
- Updated `page.tsx` to fetch RPC usage data alongside account usage and subscriptions.
- Passed `rpcUsage` data to the `Usage` component.
- Modified `Usage` component to process and display RPC usage data using `ThirdwebBarChart`.

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

<!-- end pr-codex -->
2025-04-14 20:27:11 +00:00
MananTank bb29e1f705 [TOOL-4033] Playground: UI revamp and cleanup, Improved engine playgrounds, updated theme (#6720)
<!--

## 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 removing unused assets and files, updating configurations, and enhancing UI components across the `playground-web` application. It also adds new constants and improves code structure for better maintainability.

### Detailed summary
- Deleted numerous unused image files and TypeScript files.
- Updated `postcss.config.js` and added Tailwind CSS configurations.
- Enhanced UI components with consistent styling changes.
- Introduced new constants for `mintExample` and `claimExample`.
- Improved API handling in the `engine` actions.
- Refactored several components to use `PageLayout` for better structure.
- Added code examples for minting and airdrop functionalities.

> The following files were skipped due to too many changes: `apps/playground-web/src/app/engine/airdrop/_components/airdrop-code.tsx`, `apps/playground-web/src/app/connect/pay/commerce/page.tsx`, `apps/playground-web/src/components/code/code-example.tsx`, `apps/playground-web/src/app/connect/in-app-wallet/ecosystem/page.tsx`, `apps/playground-web/src/components/blocks/APIHeader.tsx`, `apps/playground-web/src/app/connect/account-abstraction/connect/page.tsx`, `apps/playground-web/src/app/connect/pay/fund-wallet/page.tsx`, `apps/playground-web/src/app/connect/account-abstraction/sponsor/page.tsx`, `apps/playground-web/src/components/blocks/NetworkSelectors.tsx`, `apps/playground-web/src/app/connect/ui/page.tsx`, `apps/playground-web/src/app/connect/in-app-wallet/sponsor/page.tsx`, `apps/playground-web/src/components/in-app-wallet/custom-login-form.tsx`, `apps/playground-web/src/app/connect/pay/transactions/page.tsx`, `apps/playground-web/src/app/connect/in-app-wallet/page.tsx`, `apps/playground-web/src/components/in-app-wallet/profile-sections.tsx`, `apps/playground-web/src/app/connect/account-abstraction/native-aa/page.tsx`, `apps/playground-web/src/app/engine/airdrop/_components/airdrop-preview.tsx`, `apps/playground-web/src/components/headless-ui/token-examples.tsx`, `apps/playground-web/src/components/headless-ui/nft-examples.tsx`, `apps/playground-web/src/app/connect/sign-in/headless/page.tsx`, `apps/playground-web/src/app/connect/auth/page.tsx`, `apps/playground-web/src/app/globals.css`, `apps/playground-web/src/app/engine/webhooks/_components/webhooks-preview.tsx`, `apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx`, `apps/playground-web/src/app/engine/airdrop/_components/airdrop-card.tsx`, `apps/playground-web/src/app/connect/blockchain-api/page.tsx`, `apps/playground-web/src/app/engine/minting/_components/mint-preview.tsx`, `apps/playground-web/src/components/headless-ui/account-examples.tsx`, `pnpm-lock.yaml`

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

<!-- end pr-codex -->
2025-04-14 19:54:24 +00:00
jnsdls 76822982f3 Add billing warning and open invoice filtering (#6718)
# Add Warning for Teams with Invalid Payment Methods

This PR adds a warning message for teams with invalid payment methods, preventing them from upgrading plans until they resolve outstanding invoices.

## Changes:

- Added a warning component that displays when a team has an `invalidPayment` billing status
- Added a filter to view only open invoices in the invoices page
- Updated the `getTeamInvoices` function to accept an optional `status` parameter
- Disabled checkout buttons for teams with invalid payment methods
- Added a warning in the cancel plan modal for teams with unpaid invoices
- Added a link to direct users to pay their outstanding invoices

These changes help users understand why they can't upgrade their plan and guides them to resolve payment issues.
2025-04-14 14:19:42 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
52261a5740 Version Packages (#6715)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-14 00:44:22 -07:00
Joaquim VergesandGitHub cd30ba4f3a [Dashboard] Add partner-based analytics for ecosystem wallet usage (#6717) 2025-04-13 22:13:15 -07:00
gregandGitHub 0ea898712b [SDK] Fix: Error message cleanup on UB embed (#6716) 2025-04-13 19:17:21 -07:00
gregfromstl 1b2caecfc4 [Dashboard] Feature: Improve chainlist bridge copy (#6714)
Closes: tool-4076
![Screenshot 2025-04-13 at 17 24 15](https://github.com/user-attachments/assets/1e6a3f04-cf0d-4dfa-9924-b5b611caaa7a)

<!--

## 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 `BuyFundsSection` component to reflect a shift in terminology from "Buy Funds" to "Bridge Funds," enhancing clarity regarding the functionality offered.

### Detailed summary
- Changed the `SectionTitle` from "Buy Funds" to "Bridge".
- Updated the heading text from "Buy Funds on {sanitizedChainName} using Universal Bridge" to "Bridge Funds to {sanitizedChainName}".
- Modified the paragraph text to clarify the process of obtaining the native currency.
- Altered the `PayModalButton` label from "Buy Funds on {sanitizedChainName}" to "Bridge to {sanitizedChainName}".

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

<!-- end pr-codex -->
2025-04-14 01:28:24 +00:00
Joaquim VergesandGitHub e574114188 [SDK] Don't retry on user connection rejection in EIP1193 connector (#6712) 2025-04-13 17:28:28 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
aa733b7b3e Version Packages (#6709)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-13 15:50:19 -07:00
Joaquim VergesandGitHub fa0d1141da [SDK] Add retry logic to predictAccountAddress (#6707) 2025-04-13 15:31:08 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
f618e2bffb Version Packages (#6705)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-11 22:40:10 +02:00
Jonas DanielsandGitHub 3ab31c8210 Fix incrBy to incrby in rate limit implementation (#6704) 2025-04-11 22:38:34 +02:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
8eb5d8c01e Version Packages (#6703)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-11 22:30:14 +02:00
Jonas DanielsandGitHub d036b87ae1 Refactor rate limiting to use separate read/write Redis operations (#6702) 2025-04-11 22:26:11 +02:00
YashandGitHub c86cac4d61 Fix getClaimConditions (#6701) 2025-04-11 19:19:32 +02:00
MananTank af09576247 Dashboard: Fix contract overpage page overflow when there are too many events in single tx (#6700)
<!--

## 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 `ContractOverviewPage` and `LatestEvents` components in a React application by adding overflow handling and improving the display of event data.

### Detailed summary
- Added `overflow-hidden` class to the `div` in `ContractOverviewPage.tsx`.
- Refactored the mapping of `allEvents` in `LatestEvents.tsx` to use a block structure for better readability.
- Limited the displayed events to 3 and added a button to show additional events if more than 3 exist.

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

<!-- end pr-codex -->
2025-04-11 17:01:47 +00:00
Manan TankandGitHub 316cef1a58 [TOOL-4057] Dashboard: Update Storage pinning card in team usage page (#6697) 2025-04-11 18:08:19 +02:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1bfcd375e4 Version Packages (#6696)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-10 20:51:36 -07:00
Joaquim VergesandGitHub 86070fa309 [Pay] Add feePayer option for direct_payment mode in PayEmbed (#6695) 2025-04-10 20:43:43 -07:00
1336 changed files with 25041 additions and 14754 deletions
+17 -6
View File
@@ -16,6 +16,7 @@ env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
TW_SECRET_KEY: ${{ secrets.TW_SECRET_KEY }}
TW_CLIENT_ID: ${{ secrets.TW_CLIENT_ID }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
jobs:
@@ -88,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
@@ -102,7 +103,7 @@ jobs:
runs-on: ubuntu-latest-8
strategy:
matrix:
package_manager: [npm, yarn, pnpm, bun]
package_manager: [pnpm] # TODO, reenable [npm, yarn, pnpm, bun]
bundler: [vite, webpack, esbuild]
steps:
- name: Check out the code
@@ -123,7 +124,14 @@ jobs:
mkdir test-project
cd test-project
npm init -y
${{ matrix.package_manager }} add react react-dom ../packages/thirdweb
# Handle different package managers
if [ "${{ matrix.package_manager }}" = "pnpm" ]; then
# Create pnpm workspace
echo '{"name": "test-project", "private": true, "workspaces": ["."]}' > package.json
echo '{"packages": ["../packages/*"]}' > pnpm-workspace.yaml
pnpm add react react-dom ../packages/thirdweb -w
fi
- name: Create test file
run: |
cd test-project
@@ -133,7 +141,7 @@ jobs:
if: matrix.bundler == 'vite'
run: |
cd test-project
${{matrix.package_manager}} add vite
${{matrix.package_manager}} add vite -w
echo 'import { defineConfig } from "vite"; import {resolve} from "path"; export default defineConfig({ build: { lib: { entry: resolve(__dirname, "index.js"), name: "e2e_test" }, outDir: "dist" }});' > vite.config.js
npx vite build
@@ -141,7 +149,7 @@ jobs:
if: matrix.bundler == 'webpack'
run: |
cd test-project
${{matrix.package_manager}} add webpack webpack-cli
${{matrix.package_manager}} add webpack webpack-cli -w
echo 'const path = require("path"); module.exports = { mode: "production", entry: "./index.js", output: { path: path.resolve(__dirname, "dist"), filename: "bundle.js" }};' > webpack.config.js
npx webpack
@@ -149,7 +157,7 @@ jobs:
if: matrix.bundler == 'esbuild'
run: |
cd test-project
${{matrix.package_manager}} add esbuild
${{matrix.package_manager}} add esbuild -w
npx esbuild index.js --bundle --outdir=dist
- name: Verify bundle
@@ -177,6 +185,9 @@ jobs:
- name: Setup & Install
uses: ./.github/composite-actions/install
- name: Build Packages
run: pnpm build
- name: Report bundle size
uses: andresz1/size-limit-action@94bc357df29c36c8f8d50ea497c3e225c3c95d1d # v1.8.0
with:
+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
+2 -1
View File
@@ -1,2 +1,3 @@
public-hoist-pattern[]=*import-in-the-middle*
public-hoist-pattern[]=*require-in-the-middle*
public-hoist-pattern[]=*require-in-the-middle*
public-hoist-pattern[]=*pino-pretty*
+3
View File
@@ -58,4 +58,7 @@ module.exports = [
"/faucets",
// -- brand kit --
"/brand-kit",
// -- universal bridge landing pages --
"/universal-bridge-regions/:region_slug",
"/enterprise",
];
+16 -5
View File
@@ -12,7 +12,7 @@ const ContentSecurityPolicy = `
style-src 'self' 'unsafe-inline' vercel.live;
font-src 'self' vercel.live assets.vercel.com framerusercontent.com fonts.gstatic.com;
frame-src * data:;
script-src 'self' 'unsafe-eval' 'unsafe-inline' 'wasm-unsafe-eval' 'inline-speculation-rules' *.thirdweb.com *.thirdweb-dev.com vercel.live js.stripe.com framerusercontent.com events.framer.com challenges.cloudflare.com static.reo.dev;
script-src 'self' 'unsafe-eval' 'unsafe-inline' 'wasm-unsafe-eval' 'inline-speculation-rules' *.thirdweb.com *.thirdweb-dev.com vercel.live js.stripe.com framerusercontent.com events.framer.com challenges.cloudflare.com;
connect-src * data: blob:;
worker-src 'self' blob:;
block-all-mixed-content;
@@ -111,6 +111,9 @@ const SENTRY_OPTIONS: SentryBuildOptions = {
automaticVercelMonitors: false,
};
// add additional languages to the framer rewrite paths here (english is already included by default)
const FRAMER_ADDITIONAL_LANGUAGES = ["es"];
const baseNextConfig: NextConfig = {
eslint: {
ignoreDuringBuilds: true,
@@ -155,10 +158,18 @@ const baseNextConfig: NextConfig = {
source: "/home",
destination: "https://landing.thirdweb.com",
},
...FRAMER_PATHS.map((path) => ({
source: path,
destination: `https://landing.thirdweb.com${path}`,
})),
// flatmap the framer paths for the default language and the additional languages
...FRAMER_PATHS.flatMap((path) => [
{
source: path,
destination: `https://landing.thirdweb.com${path}`,
},
// this is for additional languages
...FRAMER_ADDITIONAL_LANGUAGES.map((lang) => ({
source: `/${lang}${path}`,
destination: `https://landing.thirdweb.com/${lang}${path}`,
})),
]),
];
},
images: {
+32 -32
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,12 +121,12 @@
"@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-dom": "19.1.1",
"@types/react": "19.1.2",
"@types/react-dom": "19.1.2",
"@types/react-table": "^7.7.20",
"@types/spdx-correct": "^3.1.3",
"@types/swagger-ui-react": "^4.19.0",
@@ -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",
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
"use server";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
export async function acceptInvite(options: {
+78 -1
View File
@@ -2,7 +2,7 @@
import "server-only";
import { API_SERVER_URL } from "@/constants/env";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import type { ProductSKU } from "../lib/billing";
export type GetBillingCheckoutUrlOptions = {
@@ -64,6 +64,83 @@ export async function getBillingCheckoutUrl(
export type GetBillingCheckoutUrlAction = typeof getBillingCheckoutUrl;
export async function getPlanCancelUrl(options: {
teamId: string;
redirectUrl: string;
}): Promise<{ status: number; url?: string }> {
const token = await getAuthToken();
if (!token) {
return {
status: 401,
};
}
const res = await fetch(
`${API_SERVER_URL}/v1/teams/${options.teamId}/checkout/cancel-plan-link`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
redirectTo: options.redirectUrl,
}),
},
);
if (!res.ok) {
return {
status: res.status,
};
}
const json = await res.json();
if (!json.result) {
return {
status: 500,
};
}
return {
status: 200,
url: json.result as string,
};
}
export async function reSubscribePlan(options: {
teamId: string;
}): Promise<{ status: number }> {
const token = await getAuthToken();
if (!token) {
return {
status: 401,
};
}
const res = await fetch(
`${API_SERVER_URL}/v1/teams/${options.teamId}/checkout/resubscribe-plan`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({}),
},
);
if (!res.ok) {
return {
status: res.status,
};
}
return {
status: 200,
};
}
export type GetBillingPortalUrlOptions = {
teamSlug: string | undefined;
redirectUrl: string;
+1 -1
View File
@@ -1,6 +1,6 @@
"use server";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
export async function confirmEmailWithOTP(otp: string) {
+1 -1
View File
@@ -1,6 +1,6 @@
"use server";
import { getRawAccount } from "../../app/account/settings/getAccount";
import { getRawAccount } from "../../app/(app)/account/settings/getAccount";
export async function getRawAccountAction() {
return getRawAccount();
@@ -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/(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 =
@@ -81,6 +81,7 @@ export async function getWalletNFTs(params: {
const result = await transformMoralisResponseToNFT(
await parsedResponse,
owner,
chainId,
);
return { result };
@@ -194,6 +195,8 @@ async function getWalletNFTsFromInsight(params: {
tokenURI: nft.metadata_url,
type: nft.token_type === "erc721" ? "ERC721" : "ERC1155",
supply: nft.balance,
tokenAddress: nft.contract.address,
chainId: nft.contract.chain_id,
};
return walletNFT;
@@ -1,40 +0,0 @@
"use server";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
export async function joinTeamWaitlist(options: {
teamSlug: string;
// currently only 'nebula' is supported
scope: "nebula";
}) {
const { teamSlug, scope } = options;
const token = await getAuthToken();
if (!token) {
return {
errorMessage: "You are not authorized to perform this action",
};
}
const res = await fetch(`${API_SERVER_URL}/v1/teams/${teamSlug}/waitlist`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
scope,
}),
});
if (!res.ok) {
return {
errorMessage: "Failed to join waitlist",
};
}
return {
success: true,
};
}
+1 -1
View File
@@ -1,6 +1,6 @@
"use server";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
type ProxyActionParams = {
@@ -1,6 +1,6 @@
"use server";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
export async function sendTeamInvites(options: {
@@ -23,7 +23,7 @@ function getStripe() {
export async function getTeamInvoices(
team: Team,
options?: { cursor?: string },
options?: { cursor?: string; status?: "open" },
) {
try {
const customerId = team.stripeCustomerId;
@@ -37,6 +37,8 @@ export async function getTeamInvoices(
customer: customerId,
limit: 10,
starting_after: options?.cursor,
// Only return open invoices if the status is open
status: options?.status,
});
return invoices;
@@ -1,5 +1,5 @@
"use server";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
export async function updateAccount(values: {
@@ -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;
}
+6 -3
View File
@@ -10,7 +10,7 @@ import type {
WalletStats,
WalletUserStats,
} from "types/analytics";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { getChains } from "./chain";
async function fetchAnalytics(
@@ -346,7 +346,7 @@ export async function getEcosystemWalletUsage(args: {
searchParams.append("period", period);
}
const res = await fetchAnalytics(
`v2/wallets/connects/${ecosystemSlug}?${searchParams.toString()}`,
`v2/wallet/connects?${searchParams.toString()}`,
{
method: "GET",
headers: {
@@ -356,7 +356,10 @@ export async function getEcosystemWalletUsage(args: {
);
if (res?.status !== 200) {
console.error("Failed to fetch ecosystem wallet stats");
const reason = await res?.text();
console.error(
`Failed to fetch ecosystem wallet stats: ${res?.status} - ${res.statusText} - ${reason}`,
);
return null;
}
+1 -1
View File
@@ -1,6 +1,6 @@
import "server-only";
import type { ChainMetadata } from "thirdweb/chains";
import type { ChainService } from "../../app/(dashboard)/(chain)/types/chain";
import type { ChainService } from "../../app/(app)/(dashboard)/(chain)/types/chain";
import { API_SERVER_URL, THIRDWEB_API_SECRET } from "../constants/env";
export async function getGasSponsoredChains() {
+1 -1
View File
@@ -1,4 +1,4 @@
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
export type LinkedWallet = {
+1 -1
View File
@@ -1,7 +1,7 @@
import "server-only";
import { API_SERVER_URL } from "@/constants/env";
import type { ProjectResponse } from "@thirdweb-dev/service-utils";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
export type Project = ProjectResponse;
+1 -1
View File
@@ -1,4 +1,4 @@
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
export type TeamInvite = {
+1 -1
View File
@@ -1,6 +1,6 @@
import "server-only";
import { API_SERVER_URL } from "@/constants/env";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
const TeamAccountRole = {
OWNER: "OWNER",
@@ -1,4 +1,4 @@
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
import type { ProductSKU } from "../lib/billing";
+1 -29
View File
@@ -1,7 +1,7 @@
import "server-only";
import { API_SERVER_URL, THIRDWEB_API_SECRET } from "@/constants/env";
import type { TeamResponse } from "@thirdweb-dev/service-utils";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
export type Team = TeamResponse & { stripeCustomerId: string | null };
@@ -74,31 +74,3 @@ export async function getDefaultTeam() {
}
return null;
}
type TeamNebulaWaitList = {
onWaitlist: boolean;
createdAt: null | string;
};
export async function getTeamNebulaWaitList(teamSlug: string) {
const token = await getAuthToken();
if (!token) {
return null;
}
const res = await fetch(
`${API_SERVER_URL}/v1/teams/${teamSlug}/waitlist?scope=nebula`,
{
headers: {
Authorization: `Bearer ${token}`,
},
},
);
if (res.ok) {
return (await res.json()).result as TeamNebulaWaitList;
}
return null;
}
@@ -0,0 +1,148 @@
"use server";
import { getAuthToken } from "app/(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;
}
+114
View File
@@ -0,0 +1,114 @@
import "server-only";
import { unstable_cache } from "next/cache";
export type RPCUsageDataItem = {
date: string;
usageType: "included" | "overage" | "rate-limit";
count: string;
};
export const fetchRPCUsage = unstable_cache(
async (params: {
teamId: string;
projectId?: string;
authToken: string;
from: string;
to: string;
period: "day" | "week" | "month" | "year" | "all";
}) => {
const analyticsEndpoint = process.env.ANALYTICS_SERVICE_URL as string;
const url = new URL(`${analyticsEndpoint}/v2/rpc/usage-types`);
url.searchParams.set("teamId", params.teamId);
if (params.projectId) {
url.searchParams.set("projectId", params.projectId);
}
url.searchParams.set("from", params.from);
url.searchParams.set("to", params.to);
url.searchParams.set("period", params.period);
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${params.authToken}`,
},
});
if (!res.ok) {
const error = await res.text();
return {
ok: false as const,
error: error,
};
}
const resData = await res.json();
return {
ok: true as const,
data: resData.data as RPCUsageDataItem[],
};
},
["rpc-usage"],
{
revalidate: 60 * 60, // 1 hour
},
);
type Last24HoursRPCUsageApiResponse = {
peakRate: {
date: string;
peakRPS: string;
};
averageRate: {
date: string;
averageRate: string;
peakRPS: string;
includedCount: string;
rateLimitedCount: string;
overageCount: string;
}[];
totalCounts: {
includedCount: string;
rateLimitedCount: string;
overageCount: string;
};
};
export const getLast24HoursRPCUsage = unstable_cache(
async (params: {
teamId: string;
projectId?: string;
authToken: string;
}) => {
const analyticsEndpoint = process.env.ANALYTICS_SERVICE_URL as string;
const url = new URL(`${analyticsEndpoint}/v2/rpc/24-hours`);
url.searchParams.set("teamId", params.teamId);
if (params.projectId) {
url.searchParams.set("projectId", params.projectId);
}
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${params.authToken}`,
},
});
if (!res.ok) {
const error = await res.text();
return {
ok: false as const,
error: error,
};
}
const resData = await res.json();
return {
ok: true as const,
data: resData.data as Last24HoursRPCUsageApiResponse,
};
},
["rpc-usage-last-24-hours:v2"],
{
revalidate: 60, // 1 minute
},
);
@@ -0,0 +1,94 @@
"use server";
import "server-only";
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
import { API_SERVER_URL } from "../constants/env";
export type VerifiedDomainResponse =
| {
status: "pending";
domain: string;
dnsSublabel: string;
dnsValue: string;
}
| {
status: "verified";
domain: string;
verifiedAt: Date;
};
export async function checkDomainVerification(
teamIdOrSlug: string,
): Promise<VerifiedDomainResponse | null> {
const token = await getAuthToken();
if (!token) {
return null;
}
const res = await fetch(
`${API_SERVER_URL}/v1/teams/${teamIdOrSlug}/verified-domain`,
{
headers: {
Authorization: `Bearer ${token}`,
},
},
);
if (res.ok) {
return (await res.json())?.result as VerifiedDomainResponse;
}
return null;
}
export async function createDomainVerification(
teamIdOrSlug: string,
domain: string,
): Promise<VerifiedDomainResponse | { error: string }> {
const token = await getAuthToken();
if (!token) {
return {
error: "Unauthorized",
};
}
const res = await fetch(
`${API_SERVER_URL}/v1/teams/${teamIdOrSlug}/verified-domain`,
{
method: "POST",
body: JSON.stringify({ domain }),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
},
);
if (res.ok) {
return (await res.json())?.result as VerifiedDomainResponse;
}
const resJson = (await res.json()) as {
error: {
code: string;
message: string;
statusCode: number;
};
};
switch (resJson?.error?.statusCode) {
case 400:
return {
error: "The domain you provided is not valid.",
};
case 409:
return {
error: "This domain is already verified by another team.",
};
default:
return {
error: resJson?.error?.message ?? "Failed to verify domain",
};
}
}
+61 -26
View File
@@ -1,6 +1,8 @@
"use client";
import { useMutation } from "@tanstack/react-query";
import { AlertTriangleIcon } from "lucide-react";
import Link from "next/link";
import { toast } from "sonner";
import type {
GetBillingCheckoutUrlAction,
@@ -8,6 +10,7 @@ import type {
GetBillingPortalUrlAction,
GetBillingPortalUrlOptions,
} from "../actions/billing";
import type { Team } from "../api/team";
import { cn } from "../lib/utils";
import { Spinner } from "./ui/Spinner/Spinner";
import { Button, type ButtonProps } from "./ui/button";
@@ -16,6 +19,7 @@ type CheckoutButtonProps = Omit<GetBillingCheckoutUrlOptions, "redirectUrl"> & {
getBillingCheckoutUrl: GetBillingCheckoutUrlAction;
buttonProps?: Omit<ButtonProps, "children">;
children: React.ReactNode;
billingStatus: Team["billingStatus"];
};
export function CheckoutButton({
@@ -25,6 +29,7 @@ export function CheckoutButton({
getBillingCheckoutUrl,
children,
buttonProps,
billingStatus,
}: CheckoutButtonProps) {
const getUrlMutation = useMutation({
mutationFn: async () => {
@@ -40,35 +45,65 @@ export function CheckoutButton({
const errorMessage = "Failed to open checkout page";
return (
<Button
{...buttonProps}
className={cn(buttonProps?.className, "gap-2")}
disabled={getUrlMutation.isPending || buttonProps?.disabled}
onClick={async (e) => {
buttonProps?.onClick?.(e);
getUrlMutation.mutate(undefined, {
onSuccess: (res) => {
if (!res.url) {
toast.error(errorMessage);
return;
}
<div className="flex w-full flex-col items-center gap-2">
{/* show warning if the team has an invalid payment method */}
{billingStatus === "invalidPayment" && (
<BillingWarning teamSlug={teamSlug} />
)}
<Button
{...buttonProps}
className={cn(buttonProps?.className, "w-full gap-2")}
disabled={
// disable button if the team has an invalid payment method
// api will return 402 error if the team has an invalid payment method
billingStatus === "invalidPayment" ||
getUrlMutation.isPending ||
buttonProps?.disabled
}
onClick={async (e) => {
buttonProps?.onClick?.(e);
getUrlMutation.mutate(undefined, {
onSuccess: (res) => {
if (!res.url) {
toast.error(errorMessage);
return;
}
const tab = window.open(res.url, "_blank");
const tab = window.open(res.url, "_blank");
if (!tab) {
if (!tab) {
toast.error(errorMessage);
return;
}
},
onError: () => {
toast.error(errorMessage);
return;
}
},
onError: () => {
toast.error(errorMessage);
},
});
}}
>
{getUrlMutation.isPending && <Spinner className="size-4" />}
{children}
</Button>
},
});
}}
>
{getUrlMutation.isPending && <Spinner className="size-4" />}
{children}
</Button>
</div>
);
}
function BillingWarning({ teamSlug }: { teamSlug: string }) {
return (
<div className="flex items-center gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-amber-800 dark:border-amber-700/50 dark:bg-amber-900/30 dark:text-amber-400">
<AlertTriangleIcon className="h-5 w-5 flex-shrink-0" />
<p className="text-sm">
You have outstanding invoices. Please{" "}
<Link
href={`/team/${teamSlug}/~/settings/invoices?status=open`}
className="font-medium text-amber-700 underline transition-colors hover:text-amber-900 dark:text-amber-400 dark:hover:text-amber-300"
>
pay them
</Link>{" "}
to continue.
</p>
</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 { 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,7 +4,8 @@ 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 { ChainIcon } from "../../../components/icons/ChainIcon";
import type { ThirdwebClient } from "thirdweb";
import { ChainIconClient } from "../../../components/icons/ChainIcon";
import { useAllChainsData } from "../../../hooks/chains/allChains";
function cleanChainName(chainName: string) {
@@ -19,6 +20,13 @@ export function MultiNetworkSelector(props: {
disableChainId?: boolean;
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();
@@ -44,13 +52,17 @@ export function MultiNetworkSelector(props: {
sortedChains = [...priorityChains, ...otherChains];
}
if (props.hideTestnets) {
sortedChains = sortedChains.filter((chain) => !chain.testnet);
}
return sortedChains.map((chain) => {
return {
label: cleanChainName(chain.name),
value: String(chain.chainId),
};
});
}, [allChains, props.priorityChains, idToChain]);
}, [allChains, props.priorityChains, idToChain, props.hideTestnets]);
const searchFn = useCallback(
(option: Option, searchValue: string) => {
@@ -77,10 +89,11 @@ export function MultiNetworkSelector(props: {
return (
<div className="flex justify-between gap-4">
<span className="flex grow gap-2 truncate text-left">
<ChainIcon
<ChainIconClient
className="size-5"
ipfsSrc={chain.icon?.url}
loading="lazy"
src={chain.icon?.url}
client={props.client}
/>
{cleanChainName(chain.name)}
</span>
@@ -94,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));
}}
@@ -127,6 +145,7 @@ export function SingleNetworkSelector(props: {
disableChainId?: boolean;
align?: "center" | "start" | "end";
placeholder?: string;
client: ThirdwebClient;
}) {
const { allChains, idToChain } = useAllChainsData();
@@ -172,9 +191,10 @@ export function SingleNetworkSelector(props: {
return (
<div className="flex justify-between gap-4">
<span className="flex grow gap-2 truncate text-left">
<ChainIcon
<ChainIconClient
className="size-5"
ipfsSrc={chain.icon?.url}
src={chain.icon?.url}
client={props.client}
loading="lazy"
/>
{cleanChainName(chain.name)}
@@ -189,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}
@@ -1,6 +1,6 @@
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { ThirdwebMiniLogo } from "app/components/ThirdwebMiniLogo";
import { ThirdwebMiniLogo } from "app/(app)/components/ThirdwebMiniLogo";
import { GithubIcon } from "components/icons/brand-icons/GithubIcon";
import { InstagramIcon } from "components/icons/brand-icons/InstagramIcon";
import { LinkedInIcon } from "components/icons/brand-icons/LinkedinIcon";
@@ -15,6 +15,37 @@ type AppFooterProps = {
containerClassName?: string;
};
const footerLinks = [
{
href: "/home",
label: "Home",
},
{
href: "https://blog.thirdweb.com",
label: "Blog",
},
{
href: "https://portal.thirdweb.com/changelog",
label: "Changelog",
},
{
href: "https://feedback.thirdweb.com/",
label: "Feedback",
},
{
href: "https://thirdweb.com/privacy-policy",
label: "Privacy Policy",
},
{
href: "https://thirdweb.com/terms",
label: "Terms of Service",
},
{
href: "https://thirdweb.com/chainlist",
label: "Chainlist",
},
];
export function AppFooter(props: AppFooterProps) {
return (
<footer
@@ -80,57 +111,28 @@ export function AppFooter(props: AppFooterProps) {
</div>
{/* bottom row */}
<div className="grid grid-flow-col grid-cols-2 grid-rows-5 gap-2 md:flex md:flex-row md:justify-between">
<Link
className="px-[10px] py-[6px] text-muted-foreground text-sm hover:underline"
href="/home"
>
Home
</Link>
<Link
className="px-[10px] py-[6px] text-muted-foreground text-sm hover:underline"
href="https://blog.thirdweb.com"
target="_blank"
>
Blog
</Link>
<Link
className="px-[10px] py-[6px] text-muted-foreground text-sm hover:underline"
href="https://portal.thirdweb.com/changelog"
target="_blank"
>
Changelog
</Link>
<Link
className="px-[10px] py-[6px] text-muted-foreground text-sm hover:underline"
href="https://feedback.thirdweb.com/"
target="_blank"
>
Feedback
</Link>
<Link
className="px-[10px] py-[6px] text-muted-foreground text-sm hover:underline"
href="https://thirdweb.com/privacy-policy"
target="_blank"
>
Privacy Policy
</Link>
<Link
className="px-[10px] py-[6px] text-muted-foreground text-sm hover:underline"
href="https://thirdweb.com/terms"
target="_blank"
>
Terms of Service
</Link>
<Link
className="px-[10px] py-[6px] text-muted-foreground text-sm hover:underline"
href="https://thirdweb.com/chainlist"
target="_blank"
>
Chain List
</Link>
{footerLinks.map((link) => (
<FooterLink key={link.href} {...link} />
))}
</div>
</div>
</footer>
);
}
function FooterLink(props: {
href: string;
label: string;
prefetch?: boolean;
}) {
return (
<Link
href={props.href}
prefetch={false}
className="px-0 py-[6px] text-muted-foreground text-sm hover:underline"
target="_blank"
>
{props.label}
</Link>
);
}
@@ -17,7 +17,7 @@ import {
} from "@/components/ui/chart";
import { formatDate } from "date-fns";
import { useMemo } from "react";
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
import {
EmptyChartState,
LoadingChartState,
@@ -36,6 +36,13 @@ type ThirdwebAreaChartProps<TConfig extends ChartConfig> = {
data: Array<Record<keyof TConfig, number> & { time: number | string | Date }>;
showLegend?: boolean;
yAxis?: boolean;
xAxis?: {
sameDay?: boolean;
};
variant?: "stacked" | "individual";
// chart className
chartClassName?: string;
isPending: boolean;
@@ -72,15 +79,21 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
) : (
<AreaChart accessibilityLayer data={props.data}>
<CartesianGrid vertical={false} />
{props.yAxis && <YAxis tickLine={false} axisLine={false} />}
<XAxis
dataKey="time"
tickLine={false}
axisLine={false}
tickMargin={20}
tickFormatter={(value) => formatDate(new Date(value), "MMM dd")}
tickFormatter={(value) =>
formatDate(
new Date(value),
props.xAxis?.sameDay ? "MMM dd, HH:mm" : "MMM dd",
)
}
/>
<ChartTooltip
cursor={false}
cursor={true}
content={
<ChartTooltipContent
hideLabel={
@@ -113,17 +126,29 @@ 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"
/>
))}
{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 && (
<ChartLegend
@@ -8,7 +8,7 @@ import {
} from "@/components/ui/select";
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { BadgeContainer } from "../../../stories/utils";
import { BadgeContainer } from "stories/utils";
import { type CodeEnvironment, CodeSegment } from "./code-segment.client";
const meta = {
@@ -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>
);
}
@@ -8,6 +8,7 @@ import { CheckIcon, CircleDollarSignIcon } from "lucide-react";
import Link from "next/link";
import type React from "react";
import { TEAM_PLANS } from "utils/pricing";
import { RenewSubscriptionButton } from "../../../components/settings/Account/Billing/renew-subscription/renew-subscription-button";
import { useTrack } from "../../../hooks/analytics/useTrack";
import { remainingDays } from "../../../utils/date-utils";
import type { GetBillingCheckoutUrlAction } from "../../actions/billing";
@@ -16,20 +17,28 @@ import { CheckoutButton } from "../billing";
type PricingCardCta = {
hint?: string;
title: string;
onClick?: () => void;
} & (
| {
type: "link";
href: string;
label: string;
}
| {
type: "checkout";
label: string;
}
| {
type: "renew";
}
);
type PricingCardProps = {
getTeam: () => Promise<Team>;
teamSlug: string;
teamId: string;
billingStatus: Team["billingStatus"];
billingPlan: keyof typeof TEAM_PLANS;
cta?: PricingCardCta;
ctaHint?: string;
@@ -40,7 +49,10 @@ type PricingCardProps = {
};
export const PricingCard: React.FC<PricingCardProps> = ({
getTeam,
teamSlug,
teamId,
billingStatus,
billingPlan,
cta,
highlighted = false,
@@ -129,8 +141,12 @@ export const PricingCard: React.FC<PricingCardProps> = ({
{cta && (
<div className="flex flex-col gap-3">
{cta.type === "renew" && (
<RenewSubscriptionButton teamId={teamId} getTeam={getTeam} />
)}
{billingPlanToSkuMap[billingPlan] && cta.type === "checkout" && (
<CheckoutButton
billingStatus={billingStatus}
buttonProps={{
variant: highlighted ? "default" : "outline",
className: highlighted ? undefined : "bg-background",
@@ -140,7 +156,7 @@ export const PricingCard: React.FC<PricingCardProps> = ({
sku={billingPlanToSkuMap[billingPlan]}
getBillingCheckoutUrl={getBillingCheckoutUrl}
>
{cta.title}
{cta.label}
</CheckoutButton>
)}
@@ -151,7 +167,7 @@ export const PricingCard: React.FC<PricingCardProps> = ({
asChild
>
<Link href={cta.href} target="_blank" onClick={handleCTAClick}>
{cta.title}
{cta.label}
</Link>
</Button>
)}
@@ -23,6 +23,7 @@ export function WalletAddress(props: {
address: string | undefined;
shortenAddress?: boolean;
className?: string;
iconClassName?: string;
}) {
const thirdwebClient = useThirdwebClient();
// default back to zero address if no address provided
@@ -78,6 +79,7 @@ export function WalletAddress(props: {
address={address}
profiles={profiles.data || []}
thirdwebClient={thirdwebClient}
iconClassName={props.iconClassName}
/>
)}
<span className="cursor-pointer font-mono">
@@ -168,6 +170,7 @@ function WalletAvatar(props: {
address: string;
profiles: SocialProfile[];
thirdwebClient: ThirdwebClient;
iconClassName?: string;
}) {
const avatar = useMemo(() => {
return props.profiles.find(
@@ -186,11 +189,20 @@ function WalletAvatar(props: {
: undefined;
return (
<div className="size-6 overflow-hidden rounded-full">
<div
className={cn("size-6 overflow-hidden rounded-full", props.iconClassName)}
>
{resolvedAvatarSrc ? (
<Img src={resolvedAvatarSrc} className="size-6 object-cover" />
<Img
src={resolvedAvatarSrc}
className={cn("size-6 object-cover", props.iconClassName)}
/>
) : (
<Blobbie address={props.address} size={24} />
<Blobbie
address={props.address}
size={24}
className={props.iconClassName}
/>
)}
</div>
);
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { BadgeContainer } from "../../stories/utils";
import { BadgeContainer } from "stories/utils";
import { PaginationButtons } from "./pagination-buttons";
const meta = {
+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}
/>
));
+5
View File
@@ -4,6 +4,11 @@ export const DASHBOARD_THIRDWEB_CLIENT_ID =
export const DASHBOARD_THIRDWEB_SECRET_KEY =
process.env.DASHBOARD_SECRET_KEY || "";
export const NEXT_PUBLIC_NEBULA_APP_CLIENT_ID =
process.env.NEXT_PUBLIC_NEBULA_APP_CLIENT_ID || "";
export const NEBULA_APP_SECRET_KEY = process.env.NEBULA_APP_SECRET_KEY || "";
export const THIRDWEB_API_SECRET = process.env.API_SERVER_SECRET || "";
export const IPFS_GATEWAY_URL =
@@ -1,13 +1,17 @@
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 type { GetAuthTokenResponse } from "../../app/(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>
);
}
@@ -3,7 +3,7 @@
import { Button } from "@/components/ui/button";
import { useThirdwebClient } from "@/constants/thirdweb.client";
import { useStore } from "@/lib/reactive";
import { getSDKTheme } from "app/components/sdk-component-theme";
import { getSDKTheme } from "app/(app)/components/sdk-component-theme";
import { CustomChainRenderer } from "components/selects/CustomChainRenderer";
import { mapV4ChainToV5Chain } from "contexts/map-chains";
import { useTrack } from "hooks/analytics/useTrack";
@@ -19,8 +19,8 @@ import {
useActiveAccount,
useConnectModal,
} from "thirdweb/react";
import { useFavoriteChainIds } from "../../../../app/(dashboard)/(chain)/components/client/star-button";
import { doLogout } from "../../../../app/login/auth-actions";
import { useFavoriteChainIds } from "../../../../app/(app)/(dashboard)/(chain)/components/client/star-button";
import { doLogout } from "../../../../app/(app)/login/auth-actions";
import { LazyConfigureNetworkModal } from "../../../../components/configure-networks/LazyConfigureNetworkModal";
import { useAllChainsData } from "../../../../hooks/chains/allChains";
import {
@@ -175,6 +175,7 @@ export const CustomConnectWallet = (props: {
renderChain(props) {
return (
<CustomChainRenderer
client={thirdwebClient}
{...props}
openEditChainModal={(c) => {
setIsNetworkConfigModalOpen(true);
@@ -1,12 +1,6 @@
import { type AbiEvent, formatAbiItem } from "abitype";
import { useMemo } from "react";
import {
type PreparedEvent,
type ThirdwebContract,
prepareEvent,
} from "thirdweb";
import type { ThirdwebContract } from "thirdweb";
import { useContractEvents } from "thirdweb/react";
import { useResolveContractAbi } from "./useResolveContractAbi";
export interface InternalTransaction {
transactionHash: string;
@@ -20,20 +14,8 @@ export interface InternalTransaction {
}
export function useActivity(contract: ThirdwebContract, autoUpdate?: boolean) {
const abiQuery = useResolveContractAbi(contract);
// Get all the Prepare Events from the contract abis
const events: PreparedEvent<AbiEvent>[] = useMemo(() => {
const eventsItems = (abiQuery.data || []).filter((o) => o.type === "event");
const eventSignatures = eventsItems.map((event) => formatAbiItem(event));
return eventSignatures.map((signature) =>
prepareEvent({ signature: signature as `event ${string}` }),
);
}, [abiQuery.data]);
const eventsQuery = useContractEvents({
contract,
events,
blockRange: 20000,
watch: autoUpdate,
});
@@ -42,26 +24,30 @@ export function useActivity(contract: ThirdwebContract, autoUpdate?: boolean) {
if (!eventsQuery.data) {
return [];
}
const obj = eventsQuery.data.slice(0, 100).reduce(
(acc, curr) => {
const internalTx = acc[curr.transactionHash];
if (internalTx) {
internalTx.events.push(curr);
internalTx.events.sort((a, b) => b.logIndex - a.logIndex);
if (internalTx.blockNumber > curr.blockNumber) {
internalTx.blockNumber = curr.blockNumber;
const obj = eventsQuery.data
.slice()
.reverse()
.slice(0, 100)
.reduce(
(acc, curr) => {
const internalTx = acc[curr.transactionHash];
if (internalTx) {
internalTx.events.push(curr);
internalTx.events.sort((a, b) => b.logIndex - a.logIndex);
if (internalTx.blockNumber > curr.blockNumber) {
internalTx.blockNumber = curr.blockNumber;
}
} else {
acc[curr.transactionHash] = {
transactionHash: curr.transactionHash,
blockNumber: curr.blockNumber,
events: [curr],
};
}
} else {
acc[curr.transactionHash] = {
transactionHash: curr.transactionHash,
blockNumber: curr.blockNumber,
events: [curr],
};
}
return acc;
},
{} as Record<string, InternalTransaction>,
);
return acc;
},
{} as Record<string, InternalTransaction>,
);
return Object.values(obj).sort((a, b) =>
a.blockNumber > b.blockNumber ? -1 : 1,
);
@@ -7,12 +7,12 @@ import {
useQuery,
useQueryClient,
} from "@tanstack/react-query";
import type { ResultItem } from "app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/metrics/components/StatusCodes";
import type { ResultItem } from "app/(app)/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/metrics/components/StatusCodes";
import type { EngineBackendWalletType } from "lib/engine";
import { useState } from "react";
import { useActiveAccount } from "thirdweb/react";
import invariant from "tiny-invariant";
import type { EngineStatus } from "../../../app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table";
import type { EngineStatus } from "../../../app/(app)/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table";
import { engineKeys } from "../cache-keys";
// Engine instances
@@ -0,0 +1,29 @@
"use client";
import { getSDKTheme } from "app/(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,
client,
}: { chainId?: number; client: ThirdwebClient }) {
const { theme } = useTheme();
const chain = useV5DashboardChain(chainId || 1);
return (
<PayEmbed
client={client}
payOptions={{
mode: "fund_wallet",
prefillBuy: {
chain,
amount: "0.01",
},
}}
theme={getSDKTheme(theme === "light" ? "light" : "dark")}
/>
);
}
@@ -0,0 +1,40 @@
import { getThirdwebClient } from "@/constants/thirdweb.server";
import type { Metadata } from "next";
import { UniversalBridgeEmbed } from "./components/client/UniversalBridgeEmbed";
const title = "Universal Bridge: Swap, Bridge, and On-Ramp";
const description =
"Swap, bridge, and on-ramp to any EVM chain with thirdweb's Universal Bridge.";
export const metadata: Metadata = {
title,
description,
openGraph: {
title,
description,
},
};
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}
client={client}
/>
</main>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
alt=""
src="/assets/login/background.svg"
className="-bottom-12 -right-12 pointer-events-none absolute lg:right-0 lg:bottom-0"
/>
</div>
);
}
@@ -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,12 +6,13 @@ 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";
import { ChainlistPagination } from "../client/pagination";
import { RouteListCard } from "../server/routelist-card";
import { RouteListRow } from "../server/routelist-row";
import { RouteListCard } from "./routelist-card";
import { RouteListRow } from "./routelist-row";
export type SearchParams = Partial<{
includeDeprecated: boolean;
@@ -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>
))}
@@ -1,7 +1,7 @@
import "server-only";
import { BRIDGE_URL, DASHBOARD_THIRDWEB_SECRET_KEY } from "@/constants/env";
import { getAuthToken } from "app/api/lib/getAuthToken";
import { getAuthToken } from "app/(app)/api/lib/getAuthToken";
import type { Route } from "./types/route";
export async function getRoutes({
@@ -25,11 +25,10 @@ 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";
import type { CanClaimResponseType } from "app/api/testnet-faucet/can-claim/CanClaimResponseType";
import type { CanClaimResponseType } from "app/(app)/api/testnet-faucet/can-claim/CanClaimResponseType";
import { mapV4ChainToV5Chain } from "contexts/map-chains";
import { useTrack } from "hooks/analytics/useTrack";
import Link from "next/link";
@@ -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 (
@@ -0,0 +1,46 @@
"use client";
import { useTrack } from "hooks/analytics/useTrack";
import { FileText } from "lucide-react";
import Link from "next/link";
import type { ChainMetadata } from "thirdweb/chains";
import { SectionTitle } from "../server/SectionTitle";
export default function NextSteps(props: { chain: ChainMetadata }) {
const { chain } = props;
const trackEvent = useTrack();
return (
<section>
<SectionTitle title="Next Steps" />
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3">
<div className="relative flex gap-3 rounded-lg border bg-card p-4 pr-8 transition-colors hover:border-active-border">
<FileText className="mt-0.5 size-5 shrink-0" />
<div>
<h3 className="mb-1.5 font-medium">
<Link
href={
"https://blog.thirdweb.com/supercharge-user-adoption-integrate-embedded-wallets-in-minutes/"
}
className="before:absolute before:inset-0"
target="_blank"
onClick={() =>
trackEvent({
category: "nextSteps",
action: "click-inapp",
label: "success",
chain_id: chain.chainId,
})
}
>
Create a login for {chain.name}
</Link>
</h3>
<p className="text-muted-foreground text-sm">
Supercharge User Adoption—Integrate In-App Wallets in Minutes
</p>
</div>
</div>
</div>
</section>
);
}
@@ -1,23 +1,28 @@
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 (
<section>
<SectionTitle title="Buy Funds" />
<SectionTitle title="Bridge" />
<div className="flex justify-center rounded-lg border border-border bg-card px-4 py-10">
<div className="flex max-w-[520px] flex-col items-center ">
<div className="flex items-center">
<ChainIcon
className="-mr-2 size-12 rounded-full border p-1"
iconUrl={props.chain.icon?.url}
client={props.client}
/>
<CreditCardIcon
bg="hsl(var(--background))"
@@ -28,21 +33,21 @@ export function BuyFundsSection(props: { chain: ChainMetadata }) {
<div className="h-6" />
<h2 className="px-4 text-center font-semibold text-lg tracking-tight">
Buy Funds on {sanitizedChainName} using Universal Bridge
Bridge Funds to {sanitizedChainName}
</h2>
<div className="h-2" />
<p className="max-w-[520px] px-4 text-center text-muted-foreground text-sm">
Instantly onramp, bridge & swap into {sanitizedChainName} with any
token.
Get {props.chain.nativeCurrency.symbol} on {sanitizedChainName} with
fiat or any token on another chain.
</p>
<div className="h-8" />
<PayModalButton
chainId={props.chain.chainId}
label={`Buy Funds on ${sanitizedChainName}`}
label={`Bridge to ${sanitizedChainName}`}
/>
<div className="h-4" />
@@ -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,8 +1,9 @@
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 { mapV4ChainToV5Chain } from "../../../../../../../../contexts/map-chains";
import { ChainIcon } from "../../../../components/server/chain-icon";
import { AddChainToWallet } from "../client/add-chain-to-wallet";
@@ -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",
@@ -17,8 +17,13 @@ import { ChevronDownIcon, TicketCheckIcon } from "lucide-react";
import type { Metadata } from "next";
import Link from "next/link";
import { redirect } from "next/navigation";
import { mapV4ChainToV5Chain } from "../../../../../contexts/map-chains";
import { getAuthToken } from "../../../../api/lib/getAuthToken";
import { getThirdwebClient } from "../../../../../../@/constants/thirdweb.server";
import { mapV4ChainToV5Chain } from "../../../../../../contexts/map-chains";
import { NebulaChatButton } from "../../../../../nebula-app/(app)/components/FloatingChat/FloatingChat";
import {
getAuthToken,
getAuthTokenWalletAddress,
} from "../../../../api/lib/getAuthToken";
import { StarButton } from "../../components/client/star-button";
import { getChain, getChainMetadata } from "../../utils";
import { AddChainToWallet } from "./components/client/add-chain-to-wallet";
@@ -55,17 +60,61 @@ export default async function ChainPageLayout(props: {
}) {
const params = await props.params;
const { children } = props;
const chain = await getChain(params.chain_id);
const authToken = await getAuthToken();
const [chain, authToken, accountAddress] = await Promise.all([
getChain(params.chain_id),
getAuthToken(),
getAuthTokenWalletAddress(),
]);
if (params.chain_id !== chain.slug) {
redirect(chain.slug);
}
const chainMetadata = await getChainMetadata(chain.chainId);
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.
Users may seek comparisons between ${chain.name} and other chains. Provide objective, succinct comparisons focusing on performance, fees, and ecosystem support. Refrain from transaction-specific advice unless requested.
Provide users with an understanding of the unique use cases and functionalities that ${chain.name} supports. Discuss how developers leverage this chain for specific applications, such as scalable dApps, low-cost transactions, or specialized token standards, focusing on practical implementations.
Users may be interested in utilizing thirdweb tools on ${chain.name}. Offer clear guidance on how thirdweb's SDKs, smart contract templates, and deployment tools integrate with this chain. Emphasize the functionalities enabled by thirdweb without discussing transaction execution unless prompted.
Avoid transaction-related actions to be executed by the user unless inquired about.
The following is the user's message:
`;
const examplePrompts: string[] = [
"What are users doing on this chain?",
"What are the most active contracts?",
"Why would I use this chain over others?",
"Can I deploy thirdweb contracts to this chain?",
];
if (chain.chainId !== 1) {
examplePrompts.push("Can I bridge assets from Ethereum to this chain?");
}
return (
<>
<NebulaChatButton
isFloating={true}
pageType="chain"
authToken={authToken ?? undefined}
label="Ask AI about this chain"
client={client}
nebulaParams={{
messagePrefix: chainPromptPrefix,
chainIds: [chain.chainId],
wallet: accountAddress ?? undefined,
}}
examplePrompts={examplePrompts.map((prompt) => ({
title: prompt,
message: prompt,
}))}
/>
<div className="flex h-14 border-border border-b pl-7">
<Breadcrumb className="my-auto">
<BreadcrumbList>
@@ -113,6 +162,7 @@ export default async function ChainPageLayout(props: {
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,6 +1,8 @@
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";
import { ChainOverviewSection } from "./components/server/ChainOverviewSection";
import { ClaimChainSection } from "./components/server/ClaimChainSection";
@@ -17,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 (
@@ -41,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 */}
@@ -57,6 +60,8 @@ export default async function Page(props: {
{chain.services.filter((s) => s.enabled).length > 0 && (
<SupportedProductsSection services={chain.services} />
)}
{/*Next Steps */}
<NextSteps chain={chain} />
{/* Claim Chain */}
{!chainMetadata && <ClaimChainSection />}
@@ -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,9 +10,8 @@ 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 { isAlchemySupported } from "lib/wallet/nfts/isAlchemySupported";
import { isMoralisSupported } from "lib/wallet/nfts/isMoralisSupported";
import { PlusIcon } from "lucide-react";
import { useState } from "react";
import type { ThirdwebContract } from "thirdweb";
@@ -23,7 +22,7 @@ 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,8 @@ 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 { isAlchemySupported } from "lib/wallet/nfts/isAlchemySupported";
import { isMoralisSupported } from "lib/wallet/nfts/isMoralisSupported";
import type { WalletNFT } from "lib/wallet/nfts/types";
import { CircleAlertIcon, InfoIcon } from "lucide-react";
import Link from "next/link";
@@ -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,
}) => {
@@ -205,6 +204,8 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
owner: nft.owner,
type: "ERC721",
tokenURI: nft.tokenURI,
chainId: nft.chainId,
tokenAddress: nft.tokenAddress,
};
}
return {
@@ -216,6 +217,8 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
owner: nft.owner,
type: "ERC1155",
tokenURI: nft.tokenURI,
chainId: nft.chainId,
tokenAddress: nft.tokenAddress,
};
}) as WalletNFT[];
}, [ownedNFTs, form]);
@@ -655,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}
/>
);
@@ -21,6 +21,7 @@ const dummyMetadata: (idx: number) => NFTWithContract = (idx) => ({
owner: `0x_fake_${idx}`,
type: "ERC721",
supply: 1n,
tokenAddress: ZERO_ADDRESS,
});
interface NFTCardsProps {

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