Compare commits

...
Author SHA1 Message Date
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
00dcc346bf Version Packages (#8063)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-18 10:58:25 +12:00
Manan TankandGitHub e0738d68d2 SDK, Dashboard: Fix SwapWidget setting same token for buy and sell in chain page (#8067) 2025-09-17 15:47:19 -07:00
Joaquim VergesandGitHub e160deb8b7 [SDK] Fix wallet reconnection for previously connected wallets (#8066) 2025-09-17 15:18:13 -07:00
Manan TankandGitHub a6f846fc1e [BLD-286] SDK: add stable classNames to elements in Connect UI (#8065) 2025-09-17 13:34:27 -07:00
Manan TankandGitHub 0e3f3460ad Add SwapWidget in SDK, add in dashboard, BuyWidget UI improvements (#8044) 2025-09-17 12:57:58 -07:00
MananTank 552f7025c1 Dashboard: Fix Import Engine form submit not working (#8064)
<!--

## 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 dialog for importing engine instances in the `ImportEngineButton` component, enhancing user interaction by managing the dialog state and updating UI elements.

### Detailed summary
- Added `useState` to manage the dialog's open state.
- Changed navigation from `router.push` to `router.refresh` after import.
- Wrapped the dialog in a conditional open state.
- Updated the layout and styling of the dialog content and links.
- Modified form field styles for better UI consistency.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* New Features
  * Import dialog now closes automatically after a successful import and refreshes the current page.
  * Added an in-dialog warning beneath the URL field for clearer guidance.
  * Updated “Get started” link destination.

* Style
  * Redesigned import dialog with improved layout, spacing, rounded content, and a bottom action bar; inputs use card styling.
  * Refined labels, placeholders, and header padding for clearer readability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-17 15:58:39 +00:00
MananTank 15f083912c SDK: Fix onSuccess prop not called properly on CheckoutWidget and BuyWidget (#8054)
<!--

## 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 the `onSuccess` callback not being triggered properly in the `CheckoutWidget` and `BuyWidget` components, improving the handling of buy completion and post-transaction actions.

### Detailed summary
- Renamed `handleBuyComplete` to `handleDoneOrContinueClick`.
- Adjusted the invocation of `onComplete` to only occur when `uiOptions.mode` is not "transaction".
- Updated the dependency array for `handleExecutionComplete` to include `onComplete` and `uiOptions.mode`.
- Changed the `onDone` prop in `SuccessScreen` to use `handleDoneOrContinueClick`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Bug Fixes**
  * Fixed an issue where the success callback in checkout and buy widgets did not fire reliably.
  * Improved non-transaction flows so completion triggers after execution finishes (instead of on Done), ensuring proper state reset and more consistent behavior.

* **Chores**
  * Added a patch-level changeset entry documenting the fix.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-17 10:47:49 +00:00
GiselleNessi 04862de9e4 feat: add feedback collection UI to navigation (#7895)
<!--

## 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 feature for collecting user feedback within the application. It implements feedback submission forms in both desktop and mobile components, capturing user feedback and sending it to PostHog for analysis.

### Detailed summary
- Added `reportProductFeedback` function in `report.ts` to handle feedback submissions.
- Implemented feedback submission in the `SecondaryNavLinks` component.
- Added feedback dropdown with a form in `SecondaryNavLinks`.
- Integrated feedback functionality in the `MobileBurgerMenuButton` component.
- Included success notifications using `toast` for feedback submissions in both components.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Replaced external Feedback link with an in-app feedback panel in the header and mobile menu.
  * Panel includes a textarea (max 1000 chars), Cancel and Submit actions; Submit is disabled when empty, shows a success message, clears input and closes the panel.
  * Adds an in-panel “Contact support” link for technical issues and preserves existing header controls.

* **Style**
  * Expandable/collapsible feedback UI with visual indicators, improved positioning and mobile menu scrolling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-16 16:37:22 +00:00
ea46572947 [Dashboard] Display eip7702 support status (#8060)
Co-authored-by: Cursor Agent <[email protected]>
2025-09-15 17:39:14 -07:00
Joaquim VergesandGitHub f276dcfd76 [SDK] Filter out AGW from searchable wallets (#8062) 2025-09-15 17:38:51 -07:00
Joaquim VergesandGitHub 6495b94910 [SDK] Simplify wallet connection code and add Abstract wallet support (#8061) 2025-09-15 16:29:01 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
210f631cba Version Packages (#8053)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-15 11:01:51 -07:00
gregandGitHub 557f0e29e8 Fix: Add brief pause after onramp (#8052) 2025-09-15 10:06:47 -07:00
Joaquim VergesandGitHub f9bbf8101c [Nebula] Add deprecation banner with migration information (#8051) 2025-09-14 17:10:33 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
59d226512c Version Packages (#8049)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-14 04:21:13 -07:00
Joaquim VergesandGitHub 778bf2cc4b [SDK] Add wallet validation before executing transactions (#8050) 2025-09-14 04:06:05 -07:00
Joaquim VergesandGitHub 97e5080fdf [SDK] Fix fiat payments with no wallet connected (#8048) 2025-09-14 03:54:57 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
9f9fadf242 Version Packages (#8047)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-13 03:08:48 -07:00
Joaquim VergesandGitHub 224c9d034f [SDK] Upgrade WalletConnect and improve chain switching reliability (#8046) 2025-09-13 02:54:32 -07:00
Jonas DanielsandGitHub 8b8dfd9bf9 Handle ENS resolution errors gracefully (#8045) 2025-09-12 16:52:09 -07:00
Jonas DanielsandGitHub 70e52188a1 Add tier6 for SMS country pricing (#8043) 2025-09-12 16:51:43 -07:00
MananTank c82b606cd2 [MNY-166] Dashboard: Add Dex Screener charts in public ERC20 token page (#8035)
<!--

## 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 the `token-price-data` API and the `PriceChart` component, while introducing a new `DexScreener` feature that displays token price information based on the chain ID and contract address.

### Detailed summary
- Deleted `token-price-data.ts` and `PriceChart.tsx`.
- Added `mapChainIdToDexScreenerChainSlug` to map chain IDs to DexScreener chain slugs.
- Introduced `DexScreener` and `DexScreenerIframe` components for displaying token price data.
- Updated `erc20.tsx` to integrate `DexScreener` and remove token price fetching logic.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Added embedded DexScreener chart on ERC20 public pages for supported chains with automatic light/dark theming and responsive SSR fallback.
  * Introduced chain-to-DexScreener slug mapping for type-safe embeds.

* **Refactor**
  * Removed token price data fetching and the TokenStats/price chart UI from ERC20 public pages.

* **Style**
  * Widened layout to max-w-7xl, simplified Buy section spacing, and moved DexScreener before analytics; analytics and recent transfers now sit in a unified bottom container.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-12 19:25:59 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1c6b12449b Version Packages (#8040)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-12 04:43:43 -07:00
Joaquim VergesandGitHub ea8061048a [SDK] Fix signAuthorization for 1193 providers (#8042) 2025-09-12 04:35:10 -07:00
Joaquim VergesandGitHub 3ad9f7deba [SDK] Allow passing overrides to common extension functions (#8041) 2025-09-11 18:58:34 -07:00
Joaquim VergesandGitHub 0b454050b1 [SDK] Accept pre-signed authorizations for 7702 account transactions (#8038) 2025-09-11 17:07:44 -07:00
Joaquim VergesandGitHub dc1f7ab2ee [SDK] Support full MetaMask disconnection on wallet disconnect (#8039) 2025-09-11 16:47:40 -07:00
GiselleNessi bd17c08891 Fix/feedback api endpoints (#8024)
<!--

## 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 input validation and error handling in the `submitSupportFeedback` and `checkFeedbackStatus` functions, as well as improving the `SupportCaseDetails` component for better user experience during feedback submission.

### Detailed summary
- Improved input validation in `submitSupportFeedback` for `ticketId` and `rating`.
- Enhanced error handling with more specific error messages.
- Added comprehensive validation for API response structure.
- Updated `SupportCaseDetails` to handle status check errors non-blockingly.
- Adjusted feedback submission logic to improve user experience and error reporting.
- Refactored button and feedback display logic for clarity and usability.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Interactive 5‑star rating, accessible controls, auto‑resizing feedback textarea, and integrated Send Feedback button with spinner.
  * “Checking feedback status…” loader and clear “Thank you” state after successful submission.

* **Improvements**
  * Non‑blocking status check with a user‑facing warning if verification fails.
  * Faster, clearer error messages and timeouts for network/server issues; form validation prevents invalid submissions.
  * Feedback resets and success toasts on submission.

* **Bug Fixes**
  * Feedback UI no longer fails when status verification errors occur; malformed API responses handled gracefully.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-11 09:03:02 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
79e3694e37 Version Packages (#8037)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-10 13:41:10 -07:00
Joaquim VergesandGitHub ef15ba7c43 [SDK] Pass through authorizationList for injected providers (#8036) 2025-09-10 13:30:46 -07:00
MananTank a724bc39a2 [BLD-247] Add propagation time in domain restrictions in project settings page (#8025)
<!--

## 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 `bottomText` prop of the `SettingsCard` component in the `ProjectGeneralSettingsPage.tsx` file to provide more detailed information regarding domain restrictions for web applications.

### Detailed summary
- Changed `bottomText` from a string to a fragment containing:
  - A message about applicability for web applications.
  - An additional note that changes to domain restrictions may take up to 5 minutes to take effect.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **Documentation**
  * Clarified Allowed Domains setting copy to state it's only applicable for web applications.
  * Added guidance that domain restriction changes may take up to 5 minutes to take effect.
  * Purely a UI copy update; no functional or behavioral changes.
  * Improves in-app guidance and reduces configuration confusion.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-09 23:23:31 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2eaff98d8a Version Packages (#8030)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-09 16:22:34 -07:00
Joaquim VergesandGitHub 1759112d72 [SDK] Add signAuthorization support to 1193 provider (#8029) 2025-09-09 16:14:27 -07:00
MananTank 85d6074e92 [BLD-277] Fix Server Wallets balance fetching for custom chains (#8026)
<!--

## 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 code to utilize a new hook, `useGetV5DashboardChain`, which replaces the previous `defineChain` function in multiple files. This change enhances the code's readability and maintainability by standardizing the method of retrieving chain information.

### Detailed summary
- Replaced `defineChain` with `useGetV5DashboardChain` in `useAbiProcessing.ts`.
- Updated the `chain` variable initialization in `ServerWalletTableRow` to use `useV5DashboardChain`.
- Modified the `chain` parameter in `WalletBalanceCell` to utilize `useV5DashboardChain` instead of `defineChain`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Refactor**
  * Unified chain handling across wallet tables and webhook ABI processing using a new dashboard adapter, improving consistency and reliability.
  * Streamlined balance and smart account address resolution to reduce redundant computations and potential mismatches.
  * Internal imports and hooks updated without changing public interfaces or user workflows.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-09 19:13:00 +00:00
Joaquim VergesandGitHub 2f6f3a0947 [Dashboard] Update analytics API integration and improve number formatting (#8023) 2025-09-09 04:16:47 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
4284b9d77b Version Packages (#8008)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-09 00:39:51 -07:00
b149019318 [SDK] Feature: added glyph wallet in extra wallets (#7973)
Co-authored-by: Joaquim Verges <[email protected]>
2025-09-08 23:46:49 -07:00
Joaquim VergesandGitHub e2ebb602dc thirdweb AI inside dashboard (#8003) 2025-09-08 17:10:07 -07:00
MananTank 2d76cf9040 [BLD-259] Playground, Portal: Add revalidation for API Reference page (#8022)
<!--

## 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 metadata and content for the API reference pages in the `portal` and `playground-web` applications, enhancing the descriptions and titles to better reflect the purpose of the APIs. It also introduces a revalidation period for both pages.

### Detailed summary
- Updated `metadata` in `apps/portal/src/app/reference/page.tsx`:
  - Changed `title` from "API Reference" to "thirdweb API Reference".
  - Changed `description` from "Complete REST API reference for all thirdweb services" to "A Unified interface for Web3 development".
- Added `revalidate` property with value `3600` in `apps/portal/src/app/reference/page.tsx`.
- In `apps/playground-web/src/app/reference/page.tsx`:
  - Imported `ScalarApiReference` and `createMetadata`.
  - Added `metadata` with `title` "thirdwebAPI Reference" and `description` "A Unified interface for Web3 development".
  - Added `revalidate` property with value `3600`.

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

<!-- end pr-codex -->
2025-09-08 23:03:32 +00:00
MananTank e7d0899e76 [MNY-163] Portal: Fix broken links in payments sidebar (#8021)
<!--

## 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 `sidebar` configuration in two files: `sidebar.tsx` for the bridge and payments sections. It removes an entry and modifies the `href` properties for navigation links to ensure correct routing.

### Detailed summary
- Removed the `Playground` entry from the `sidebar` in `apps/portal/src/app/bridge/sidebar.tsx`.
- Updated the `href` for the `Playground` entry in `apps/portal/src/app/payments/sidebar.tsx` to point to `/payments`.
- Changed `href` values for `Sell Tokens`, `Swap Tokens`, `Get Token Prices`, and `Get Routes` to use absolute paths.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Bug Fixes**
  * Corrected Payments sidebar destinations: Playground now points to the dedicated payments playground, and Bridge-related items use absolute paths for reliable, consistent navigation across the app.

* **Chores**
  * Removed the obsolete Playground link from the Bridge sidebar to streamline navigation.
  * Cleaned up an unused icon import.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-08 23:03:32 +00:00
MananTank 88001cfe49 [BLD-271] Portal: Change Header nav link order (#8013)
<!--

## 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 modifying the `links` array in the `Header.tsx` file by removing and then re-adding the `Payments` and `Bridge` items, thereby changing their order within the array.

### Detailed summary
- Removed `Payments` link from the `links` array.
- Removed `Bridge` link from the `links` array.
- Re-added `Payments` link to the `links` array.
- Re-added `Bridge` link to the `links` array.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Style**
  * Updated top navigation order: Payments and Bridge now appear after Contracts instead of immediately after Wallets.
  * Applies to both desktop navigation and the mobile menu, improving consistency across views without changing the available links or destinations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-08 22:28:57 +00:00
MananTank a9c388a613 Dashboard: Add Bridge in framer paths (#8006)
<!--

## 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 `apps/dashboard/framer-rewrites.js` file by adding a new route for monetization.

### Detailed summary
- Added the route `"/monetize/bridge"` to the existing array of routes in `apps/dashboard/framer-rewrites.js`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* New Features
  * Introduced a new public route: /monetize/bridge. Users can now directly access the Monetize Bridge experience via this URL, enabling easier navigation and deep-linking from emails, bookmarks, or external references. Existing routes remain unchanged, so current links and workflows continue to work as before. This addition improves discoverability and provides a clearer, direct path to the relevant monetization flow.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-08 19:59:33 +00:00
nischitpra 4ff1cf7979 fix polygon amoy gasstation url (#8007)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on a patch for the `thirdweb` library that addresses a fix for the Polygon amoy gas station URL.

### Detailed summary
- Introduced a patch for the `thirdweb` library.
- Fixed the URL for the Polygon amoy gas station.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **Bug Fixes**
  * Corrected the Polygon Amoy gas station endpoint, improving fee estimation and transaction reliability on that network.

* **Chores**
  * Prepared a patch release entry reflecting this fix.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-08 18:41:17 +00:00
nischitpra e78a984b4e polygon amoy gasstation update (#8004)
<!-- start pr-codex -->

## PR-Codex overview
This PR updates the gas fee data source for the Polygon network in the `fee-data.ts` file, specifically changing the URL for the testnet case.

### Detailed summary
- In the `fee-data.ts` file, the return URL for the case `80002` has been changed from `"https://gasstation-testnet.polygon.technology/v2"` to `"https://gasstation.polygon.technology/amoy"`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Bug Fixes**
  * Updated the gas fee data source for Polygon Amoy (chain 80002) to the current gas station endpoint, improving accuracy and reliability of priority fee estimates.
  * Enhances transaction success rates and fee predictions for users interacting with the Amoy network.
  * No changes for Polygon mainnet (137).

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-08 12:45:14 +00:00
YashandGitHub e37bd8e994 feat: move insight page to APIs dropdown (#7991) 2025-09-07 16:12:39 -07:00
Joaquim VergesandGitHub a94cf80dbd [Dashboard] Update analytics API integration and add error logging (#8001) 2025-09-07 04:36:12 -07:00
gregandGitHub f789e7663a [Portal] Feature: Bridge page (#7998) 2025-09-06 12:54:42 -07:00
a51f4a0031 Add chat context controls to playground (#7999)
Co-authored-by: Cursor Agent <[email protected]>
2025-09-05 18:15:57 -07:00
Joaquim VergesandGitHub d06a15ecbb [Docs] Add OpenAI SDK documentation page (#7997) 2025-09-05 17:12:33 -07:00
Joaquim VergesandGitHub 61fddefcb1 [SDK] Add documentation for create7702MinimalAccount function (#7996) 2025-09-05 17:11:55 -07:00
FirekeeperandGitHub 14fe9ee2b9 Remove ability to disable Transactions (#7995) 2025-09-06 04:17:40 +07:00
MananTank 4ca407dbf0 [MNY-139] Dashboard, Portal, Playground: Add Bridge Product (#7984)
<!--

## 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 payment and bridge functionalities in the dashboard and playground applications. It includes UI improvements, new components, and refactoring for better organization and usability.

### Detailed summary
- Deleted `RouteDiscoveryCard.tsx`.
- Replaced `BridgeIcon` export.
- Added route rewrites in `next.config.mjs`.
- Updated navigation links in `navLinks.ts`.
- Modified `useTokensData.ts` to include price parameters.
- Refactored `CreatePaymentWebhookButton` to a function.
- Enhanced layout and styling in various components, including `ProjectSidebarLayout`, `EmptyState`, and `QuickstartSection`.
- Introduced `ProjectSettingsBreadcrumb` for better navigation.
- Added support for locked widgets in payment components.
- Improved `PaymentLinksTable` and `PaymentHistory` components with new table structures.
- Enhanced `FeatureCard` and `PayAnalytics` with updated styling and structure.
- Created `CheckoutPlayground` and `TransactionPlayground` components for better payment handling.
- Reorganized the bridge section with improved UI and functionality.

> The following files were skipped due to too many changes: `apps/playground-web/src/app/payments/transactions/page.tsx`

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Bridge page with analytics, route discovery, and Quickstart; CSV export for payment history; new Buy, Checkout, and Transaction playgrounds.

* **Navigation**
  * Added "Bridge" to project sidebar; Webhooks consolidated with tabs; Payments page streamlined; redirect from /payments/ui-components → /payments and removed UI Components page.

* **UI/UX Improvements**
  * Redesigned analytics and payment history table; updated FeatureCard and EmptyState visuals; removed "Swap Tokens" card; settings pages use breadcrumbs; assorted copy and spacing tweaks.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-05 20:17:41 +00:00
0xFirekeeper f714e5d810 Update API reference links to use local /reference paths (#7993)
Replaced external 'https://api.thirdweb.com/reference' links with local '/reference' paths across documentation, sidebar, and metadata files for consistency and improved navigation within the portal.

Closes BLD-221

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the reference URLs from absolute paths to relative paths across various components in the application. This change aims to streamline navigation within the app and improve consistency in referencing API documentation.

### Detailed summary
- Changed `href` values from absolute URLs to relative paths in multiple files:
  - `Header.tsx`
  - `ai/sidebar.tsx`
  - `wallets/sidebar.tsx`
  - `contracts/sidebar.tsx`
  - `ai/llm-txt/page.mdx`
  - `payments/page.mdx`
  - `nebula/api-reference` files
  - `transactions/sponsor/page.mdx`
  - `page.tsx`
  - `Document/APIEndpointMeta/OpenApiEndpoint.tsx`
  - `wallets/server/page.mdx`
  - `transactions/page.mdx`

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- Documentation
  - Updated API Reference links across the site (AI, Contracts, Transactions, Payments, Wallets, Nebula endpoints, Home, and Header) to use internal paths (/reference and anchored routes).
- Bug Fixes
  - API Reference links now navigate within the docs site instead of opening an external domain, improving navigation continuity.
- Refactor
  - Standardized reference URL handling to consistently use relative routes for a unified in-app experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-05 19:49:28 +00:00
Yash094 a328250be1 feat: Add api to project view section (#7992)
<!--

## 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 `IntegrateAPIKeyCodeTabs` component by adding new tab keys for `api` and `curl`, along with their respective code examples for API integration. It also updates the default selected tab to `api`.

### Detailed summary
- Added new `TabKey` values: `api` and `curl`.
- Updated `tabNames` to include labels for `api` and `curl`.
- Changed default tab state from `ts` to `api`.
- Introduced `apiCodeExample` function for API integration code.
- Introduced `curlCodeExample` function for cURL integration code.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added API and cURL tabs to the API integration UI with ready-to-copy code examples (JavaScript fetch and Bash curl) for server-side wallet API calls.
  * Set the API tab as the default selection and updated tab order to include API and cURL.
  * Examples auto-fill the masked secret key when available and include a sample identifier, improving first-time setup guidance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-05 15:49:11 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
928704ad5c Version Packages (#7990)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-05 00:43:09 -07:00
Joaquim VergesandGitHub acbbf2039f [Playground] Add Engine user transactions example to playground (#7470) 2025-09-05 00:37:54 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
4375e7331f Version Packages (#7989)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-05 13:35:47 +12:00
FirekeeperandGitHub 22d736cb24 Refactor wallet user API integration to use tw api (#7987) 2025-09-05 08:09:24 +07:00
0xFirekeeper bb89561a1b Fetch minimal account address from bundler (#7919)
```
<!--

## 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

Replaces the hardcoded `MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS` with a dynamic fetch from the bundler using the `tw_getDelegationContract` RPC method.

This change:
- Introduces an async function `getDelegationContractAddress` to fetch the address.
- Implements caching for the fetched address to prevent redundant RPC calls.
- Updates `_sendTxWithAuthorization` and `is7702MinimalAccount` to handle the async nature and use the dynamically fetched address.
- Ensures type safety for address fields.

## How to test

Unit tests cover the functionality. A temporary test was created and verified during development.
```
pnpm test packages/thirdweb/src/wallets/in-app/core/eip7702/minimal-account.ts
```

-->
```

---
[Slack Thread](https://thirdwebdev.slack.com/archives/C085FEPFLN9/p1756220164604699?thread_ts=1756220164.604699&cid=C085FEPFLN9)

<a href="https://cursor.com/background-agent?bcId=bc-874d9753-17ac-4429-8dfb-c3d6776f6711">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/open-in-cursor-dark.svg">
    <source media="(prefers-color-scheme: light)" srcset="https://cursor.com/open-in-cursor-light.svg">
    <img alt="Open in Cursor" src="https://cursor.com/open-in-cursor.svg">
  </picture>
</a>
<a href="https://cursor.com/agents?id=bc-874d9753-17ac-4429-8dfb-c3d6776f6711">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/open-in-web-dark.svg">
    <source media="(prefers-color-scheme: light)" srcset="https://cursor.com/open-in-web-light.svg">
    <img alt="Open in Web" src="https://cursor.com/open-in-web.svg">
  </picture>
</a>

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the `erc7702` extension, refining event filters, and modifying function signatures to enhance clarity and correctness. It also introduces delegation contract handling in the `minimal-account` implementation.

### Detailed summary
- Updated function signatures and event filters in multiple files to replace `signer` with `user` and `newSigner`.
- Improved data structures in the generated files for `MinimalAccount`.
- Added delegation contract address fetching in `minimal-account.ts`.
- Adjusted transaction handling logic to use the delegation contract address.
- Enhanced error handling for RPC responses.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- New Features
  - Minimal account updated: token-receiver callbacks, interface support check, payable receive, new executeWithSig entrypoint, and a ValueReceived event; SessionCreated/Executed events now include user/newSigner.

- Bug Fixes
  - Delegation contract resolved dynamically via bundler with caching and improved error handling.
  - Address normalization for transactions and nonce lookups to reduce mismatch errors.
  - Removed a redundant error from the ABI.

- Tests
  - Tests updated to assert renamed event fields (user/newSigner).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-04 22:22:30 +00:00
Joaquim VergesandGitHub c92081b056 [AI SDK Provider] Update sign swap action type with token fields (#7988) 2025-09-04 15:22:00 -07:00
Olexandr88andGitHub dc57b850d3 chore: add links to Twitter and Telegram (#7983)
Signed-off-by: Olexandr88 <[email protected]>
2025-09-05 04:35:54 +07:00
Manan TankandGitHub 6429a238c1 [BLD-231] Playground: Add API reference, Remove Insight and Payments API playgrounds (#7986) 2025-09-05 04:35:43 +07:00
MananTank 769019dd0b [BLD-242] Dashboard: Fix Notification overflow, UI Improvements (#7985)
<!--

## 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 of the notification components in the dashboard. It includes updates to layout, spacing, and styles, improving the overall user experience and visual consistency.

### Detailed summary
- Updated `PopoverContent` with new dimensions and layout.
- Enhanced `NotificationList` with improved tab spacing and structure.
- Adjusted styles for empty state messages in `InboxTab` and `ArchiveTab`.
- Modified `NotificationEntry` layout and added icons for better aesthetics.
- Changed button styles and interactions for improved usability.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **Style**
  * Refreshed Notifications panel: larger, rounded popover with improved spacing, overflow handling, and updated alignment.
  * Redesigned notification items: bell capsule indicator, clearer card-like layout, hover-revealed “Mark as read,” and an outline CTA with arrow icon.
  * Improved list layout: flexible, smoother scrolling, simplified tab styling, and refined spacing.
  * Enhanced empty states for Inbox and Archive with centered, card-style icon visuals.

* **Refactor**
  * Reorganized component layouts for consistency and responsiveness without changing behavior or public APIs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-04 18:09:34 +00:00
MananTank 488a3af154 [MNY-106] Playground: Add overview pages for all sections in sidebar (#7977)
<!--

## 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 components for various sections in a web application, particularly by introducing `OverviewPage` components for different features and updating the layout for better organization and presentation.

### Detailed summary
- Updated `className` for `PromptInput` in `chat-container.tsx`.
- Added `OverviewPage` components for `Tokens`, `AI`, `Contracts`, `Payments`, `Wallets`, `Transactions`, and `Account Abstraction`.
- Introduced `FeatureCard` metadata for various features in `pages-metadata.ts`.
- Refactored the main `Page` component to use `FeatureSection` for organized display.
- Removed hardcoded feature cards from the main `Page` component.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- New Features
  - Added overview pages for AI, Wallets, Headless Components, Transactions, Contracts, Payments, Tokens, and Account Abstraction.
  - Introduced reusable OverviewPage and FeatureCard components.
  - Centralized feature-card metadata for all categories.
  - Updated navigation to add “Overview” entries and renamed “AI SDK” to “AI SDK Integration”.
- Refactor
  - Home page rewritten to a data-driven layout using shared components and metadata with consistent grid rendering.
- Style
  - Tweaked AI chat input borders and top-edge rounding.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-04 17:50:50 +00:00
MananTank 8a29e36934 Portal: Remove unused pages, empty pages, Fix missing H1s (#7979)
<!--

## 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 the deletion of several `.mdx` files related to the `pay` and `account` sections, while adding new content to the `nebula` API reference and making updates to the `engine` wallet configurations.

### Detailed summary
- Deleted multiple `.mdx` files in the `pay` and `account` sections.
- Added new API endpoints in `nebula`:
  - `Execute Action`
  - `Get Session`
  - `List Sessions`
  - `Clear Session`
  - `Create Session`
  - `Delete Session`
  - `Update Session`
- Updated headings for clarity in existing `.mdx` files related to wallet configurations.

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

<!-- end pr-codex -->
2025-09-04 17:44:16 +00:00
MananTank ea98087d41 Portal: UI tweaks/fixes (#7978)
<!--

## 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 UI components and code structure in the `apps/portal` directory, improving styles, and enhancing the functionality of various elements such as tabs, lists, and layouts.

### Detailed summary
- Adjusted class names for styling consistency.
- Reduced icon sizes in several components.
- Removed unnecessary background styles from `InlineParameterItem`.
- Simplified `UnorderedList` and `OrderedList` components.
- Enhanced `FeatureCard` layout and styles.
- Updated tab triggers by removing redundant margin classes.
- Streamlined payment-related documentation and examples.
- Improved overall readability and structure in multiple markdown files.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- Style
  - Simplified tab headers by removing platform icons across multiple pages.
  - Updated layout and spacing: Insight grid tweaks, refined DocLayout text color, toned-down lists and parameter blocks, redesigned FeatureCard, and streamlined Table of Contents styling.

- Documentation
  - Replaced several tabbed sections with single-flow content; some pages now link directly to live demos.
  - Updated examples: Send now illustrates a multi-step execution flow; corrected/cleaned samples for Sell and other payments pages.
  - Transactions and Contracts pages use direct endpoint rendering in places for clarity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-04 17:44:16 +00:00
Yash094 030349bb92 remove looking for engine on project view page (#7981)
<!--

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

https://linear.app/thirdweb/issue/BLD-238/remove-looking-for-engine-hint-from-project-view

## 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 rendering logic in the `page.tsx` file for a specific team page, particularly around the display of a billing plan alert and the conditions under which it is shown.

### Detailed summary
- Replaced a conditional rendering of the free billing plan alert from a ternary operator to a logical AND (`&&`) operator.
- Removed the `DismissibleAlert` component that provided information about engines and projects.
- Kept the `Changelog` component intact.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Bug Fixes**
  * Resolved an issue where teams on paid plans saw an unnecessary alert on the team page. Paid teams will no longer see this message.
  * Free plan teams continue to see the upgrade banner as before.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-04 15:29:59 +00:00
MananTank 17d40afda3 Dashboard: fix payments tx history table crashing on onramp type purchase (#7982)
<!--

## 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 payment-related types and logic in the application to improve type safety and clarity. It introduces a new `BridgePayment` type, updates the `TableRow` component, and modifies how payments are filtered and processed.

### Detailed summary
- Changed `Payment` type to `BridgePayment` in `TableRow` component.
- Updated `originAmount` and `destinationAmount` to use `BigInt`.
- Introduced `BridgePaymentType` and `OnrampPaymentType`.
- Modified `getCSVData` to accept only `BridgePayment` types.
- Added `isBridgePayment` function to filter payments.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- New Features
  - Payments views and CSV export now explicitly show only bridge payments; onramp entries are excluded.

- Bug Fixes
  - Improved accuracy for large origin/destination amounts by converting amounts to integer form before token conversion.
  - Consistent token conversions across table, links, and CSV exports.

- Refactor
  - Payment data reorganized into distinct bridge vs onramp variants to improve filtering and export consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-04 14:45:15 +00:00
YashandGitHub bc6309ddc7 small fix to set API section as default for explorer code snippets (#7980) 2025-09-04 16:45:08 +07:00
f47eda6842 add api snippets to explorer section (#7971)
Co-authored-by: 0xFirekeeper <[email protected]>
2025-09-04 05:47:50 +07:00
2328985699 Add API Reference section to portal app (#7968)
Co-authored-by: Manan Tank <[email protected]>
2025-09-04 04:39:35 +07:00
MananTank 18b7f9104f Dashboard: Fix Import Contract Modal submit button loading state (#7970)
<!--

## 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 conditional rendering logic in the `modal.tsx` file to use the `form.formState.isSubmitting` state instead of `addContractToProject.isPending`. This change ensures that the button reflects the correct submission state during the import process.

### Detailed summary
- Changed the conditional check for displaying the loading spinner from `addContractToProject.isPending` to `form.formState.isSubmitting`.
- Updated the button text from "Importing" to reflect the new state condition.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Bug Fixes**
  * Import Contract modal: The submit button now accurately reflects the submission state. It shows a spinner and the label changes to “Importing” while the form is submitting, and reverts to the standard icon with “Import” when idle. This improves user feedback, reduces confusion, and helps prevent duplicate submissions by clearly indicating when an import is in progress.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-03 20:15:25 +00:00
MananTank 82be4fa53a [MNY-142] Dashboard: Do not render BuyWidget if token is not supported by Bridge (#7975)
<!--

## 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 `ERC20PublicPage` component to utilize `tokenInfoFromUB` instead of `tokenInfo` for setting the contract image. It also modifies the logic to conditionally render the `BuyEmbed` component based on whether the token is supported.

### Detailed summary
- Replaced `tokenInfo` with `tokenInfoFromUB` for contract image assignment.
- Removed the `buyEmbed` variable; integrated its logic directly into the render method.
- Added a new `showBuyEmbed` variable to determine if `BuyEmbed` should be displayed.
- Updated the rendering of `BuyEmbed` to use `claimConditionMeta`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- New Features
  - Buy section now appears only when supported by the token or when a claim condition exists.
  - Unified buy experience: automatically shows either a direct buy flow or a claim flow based on available conditions.
- Bug Fixes
  - More reliable token icon fallback to reduce missing/blank images.
- Style
  - Preserved decorative layout while conditionally rendering the buy UI for a cleaner, more consistent appearance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-03 20:04:08 +00:00
MananTank dbb2614306 [BLD-228] SDK/React: Do not exclude specified wallets from all wallets list in Connect UI (#7974)
<!--

## 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 removing the `specifiedWallets` prop from various components related to wallet selection in the `thirdweb` package, streamlining the wallet filtering logic.

### Detailed summary
- Removed `specifiedWallets` prop from the `WalletAuth` component.
- Updated `ConnectModalContent` to remove `specifiedWallets` and utilize `recommendedWallets`.
- Modified `AllWalletsUI` to eliminate `specifiedWallets` and simplify wallet filtering logic.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- Refactor
  - Simplified the wallet selection list to a standardized set across the Connect modal and in-app auth.
  - Removed custom list filtering, ensuring a consistent experience regardless of entry point.
  - The list now excludes in-app, embedded, and smart wallets from the “All Wallets” view.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-03 17:43:59 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
b8f496603c Version Packages (#7967)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-02 16:46:46 -07:00
Joey BrightandGitHub 4c782ad64d docs: Fix Typos in docs (#7963) 2025-09-02 16:46:17 -07:00
gregandGitHub af6a306955 [SDK] Feature: country Code on Payment Widgets (#7966) 2025-09-02 16:44:56 -07:00
MananTank 4b319c353d [BLD-227] Dashboard: Add Button to download snapshot as CSV file in Claim Phase UI (#7965)
<!--

## 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 `ClaimConditionsForm` and enhancing the `ClaimerSelection` component by adjusting imports, improving layout, and adding functionality to download snapshot data as a CSV file.

### Detailed summary
- Removed `ArrowDownToLineIcon` from `ClaimConditionsForm`.
- Updated layout in `ClaimerSelection` to use a flexible column layout.
- Added a button to download snapshots as CSV in `ClaimerSelection`.
- Introduced `downloadSnapshotAsCSV` function to handle CSV generation and downloading.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- New Features
  - Added CSV export for Claimer snapshots with a Download button visible when a snapshot exists.

- Style
  - Updated snapshot actions to a responsive layout (stack on small screens, inline on larger screens).
  - Added a distinct Download button with an icon and adjusted Edit button styling.
  - Removed the icon from the Save Phases button, showing text only.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-02 20:57:23 +00:00
saminacodes aadaf209b5 deprecated and redirected docs (#7960)
<!--

## 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 several files related to the `infrastructure` section of the application, including markdown files, images, and TypeScript components, while also updating the `redirects.mjs` file to include new external portal redirects.

### Detailed summary
- Deleted the following files:
  - `apps/portal/src/app/infrastructure/page.mdx`
  - `apps/portal/src/app/infrastructure/layout.tsx`
  - `apps/portal/src/app/infrastructure/sidebar.tsx`
  - `apps/portal/src/app/infrastructure/assets/test.jpg`
  - `apps/portal/src/app/infrastructure/storage/overview/page.mdx`
  - `apps/portal/src/app/infrastructure/rpc-edge/overview/page.mdx`
  - `apps/portal/src/app/infrastructure/rpc-edge/get-started/page.mdx`
  - `apps/portal/src/app/infrastructure/storage/how-storage-works/page.mdx`
  - `apps/portal/src/app/infrastructure/rpc-edge/get-started/assets/api-key.png`
  - `apps/portal/src/app/infrastructure/rpc-edge/overview/assets/rpc-diagram.svg`
  - `apps/portal/src/app/infrastructure/rpc-edge/get-started/assets/public-rpcs.png`
  - `apps/portal/src/app/infrastructure/rpc-edge/get-started/assets/usage-dashboard.png`
  - `apps/portal/src/app/infrastructure/storage/how-to-use-storage/host-web-app/page.mdx`
  - `apps/portal/src/app/infrastructure/storage/how-storage-works/assets/storage-diagram.svg`
  - `apps/portal/src/app/infrastructure/storage/how-to-use-storage/upload-files-to-ipfs/page.mdx`
  - `apps/portal/src/app/infrastructure/storage/how-to-use-storage/upload-files-to-ipfs/assets/storage-urls.png`
  - `apps/portal/src/app/infrastructure/storage/how-to-use-storage/upload-files-to-ipfs/assets/storage-upload.png`
  - `apps/portal/src/app/infrastructure/storage/how-to-use-storage/upload-files-to-ipfs/assets/storage-dashboard.png`
- Updated `apps/portal/redirects.mjs` to:
  - Redirect `"/storage/upload-to-ipfs"` to `"/infrastructure/storage/how-to-use-storage/upload-files-to-ipfs"`
  - Add infrastructure redirects to external portal links for `"/infrastructure/storage/:path*"` and `"/infrastructure/rpc-edge/:path*"`

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- Documentation
  - Removed the Infrastructure section from the portal, including Overview, Storage (Overview, How Storage Works, Upload Files to IPFS, Host Web App), and RPC Edge (Overview, Get Started) pages.
  - Updated navigation accordingly to reflect the removal of these pages.

- New Features
  - Added automatic redirects for Infrastructure content:
    - /infrastructure/storage/:path* now points to the external Storage documentation.
    - /infrastructure/rpc-edge/:path* now points to the external RPC documentation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-02 16:56:21 +00:00
Joaquim VergesandGitHub df6f8e2646 [Playground] Refactor AI types and move definitions to types.ts (#7964) 2025-09-02 02:05:40 -07:00
5b871ac281 [Portal] docs: fix broken link (#7962)
Signed-off-by: VolodymyrBg <[email protected]>
Signed-off-by: Joaquim Verges <[email protected]>
Co-authored-by: Joaquim Verges <[email protected]>
2025-09-02 20:54:54 +12:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
7eed8e61db Version Packages (#7957)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-01 00:06:04 -07:00
Joaquim VergesandGitHub 6ef2f3b37a [AI SDK Provider] Auto handle session ids (#7956) 2025-08-31 22:10:49 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
596aaa5495 Version Packages (#7949)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-31 16:18:40 -07:00
MananTank 89eeac1f8a Dashboard: Fix footer not sticked to bottom on short contracts page (#7951)
<!--

## 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 layout of the `contract-page-layout.tsx` component by adding a CSS class to the main `div` element for improved styling and structure.

### Detailed summary
- Changed the main `div` element from `<div>` to `<div className="flex flex-col grow">` to apply flexbox layout and allow the element to grow.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Style**
  * Updated contract page layout to use a flexible, column-based structure for more consistent spacing.
  * Improves use of available screen space so content expands naturally within the dashboard.
  * Enhances responsiveness across viewports, reducing awkward white space and unnecessary scrolling.
  * Provides more stable alignment between sidebar and main content on contract detail pages.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-30 04:03:07 +00:00
MananTank c331d189bb Dashboard: Remove OP Sponsored badge in Chain selector (#7948)
<!--

## 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 removes the handling of sponsored chains from the `CustomChainRenderer` component, including the related constant and UI elements that indicate whether a chain is sponsored.

### Detailed summary
- Deleted the import of `OPSponsoredChains` from `@/constants/chains`.
- Removed the `isSponsored` variable that checks if a chain is sponsored.
- Eliminated the UI block that displayed "Sponsored" for sponsored chains.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- Refactor
  - Removed the Sponsored badge from the chain list, simplifying the chain renderer.
  - Deprecated chains still display a Deprecated badge and remain non-interactive.
  - Clicking non-deprecated chains continues to switch networks; the Configure Network button is unchanged.

- Chores
  - Removed obsolete sponsor-related configuration and references.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-29 18:13:06 +00:00
MananTank f867799301 [MNY-129] Dashboard: Use useSendAndConfirmTx everywhere, Replace createToken with prepareCreateToken (#7947)
<!--

## 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 replacing the usage of `useSendAndConfirmTransaction` with `useSendAndConfirmTx` across various components in the dashboard application, streamlining transaction handling.

### Detailed summary
- Replaced `useSendAndConfirmTransaction` with `useSendAndConfirmTx` in multiple components.
- Updated imports to reflect the new hook in relevant files.
- Modified transaction handling logic to utilize the new hook consistently across the codebase.

> The following files were skipped due to too many changes: `apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Mintable.tsx`, `apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Claimable.tsx`, `apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx`, `apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Royalty.tsx`, `apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx`, `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/nft/create-nft-page.tsx`, `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page-impl.tsx`

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

<!-- end pr-codex -->
2025-08-29 16:25:21 +00:00
MananTank 597431ec1d [MNY-131] Add Pay Modal integration in useSendAndConfirmTransaction hook (#7946)
<!--

## 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 `Pay Modal` integration in the `useSendAndConfirmTransaction` hook, allowing users to handle insufficient funds by prompting them to purchase more. It also renames the hook and updates related components to utilize the new configuration.

### Detailed summary
- Renamed `useSendAndConfirmTransaction` to `useSendAndConfirmTransactionCore`.
- Updated exports to reflect the renaming.
- Added `payModal` configuration to `useSendAndConfirmTransaction`.
- Updated `CreateDirectListingButton`, `ClaimButton`, and `BuyDirectListingButton` to utilize the new `payModal` configuration.
- Enhanced documentation for `useSendAndConfirmTransaction` with usage examples for the `Pay Modal`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Pay Modal integrated into send-and-confirm transaction flows, matching send-only behavior.
  * Prebuilt buttons (Buy Direct Listing, Claim, Create Direct Listing) pass Pay Modal config and auto-fill metadata with sensible fallbacks for smoother top-ups.
  * Pay Modal options are configurable and compatible with gasless workflows.

* **Documentation**
  * Added examples and guidance for configuring the Pay Modal (theme, disabling, and usage).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-29 16:25:20 +00:00
GiselleNessi d5ff9f2f5d feat: add support siwa feedback system for closed support tickets (#7916)
<!--

## 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 feedback system for support tickets, allowing users to submit ratings and comments. It includes API calls for submitting feedback and checking its status, along with UI updates to handle user interactions and display feedback status.

### Detailed summary
- Added `submitSupportFeedback` function to submit feedback.
- Added `checkFeedbackStatus` function to verify if feedback has been submitted.
- Integrated feedback submission UI in `SupportCaseDetails` component.
- Implemented rating selection using stars.
- Added input field for optional feedback comments.
- Displayed loading and error states for feedback status checks.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * 5‑star rating UI and optional comment for closed support cases, with Send Feedback button, “Checking feedback status…” and sending spinners, disabled send until a rating is chosen, and a thank‑you message after submission.

* **Improvements**
  * Server-backed feedback status check and submission with input validation, 1–5 rating, 1000‑char comment limit, 10s request timeout, and contextual success/error toasts.
  * Accessible star controls with ARIA labels and filled‑star visuals.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-29 15:27:15 +00:00
saminacodes 76694f0cec Update Contracts FAQ (#7930)
<!--

## 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 adds new information to the `page.mdx` file regarding deployment costs and auditing of thirdweb contracts.

### Detailed summary
- Added a new `Details` section on deployment costs for thirdweb contracts, outlining:
  - Mint fees and primary sales fees for specific contract types.
  - A 2% convenience fee and a 0.5% protocol fee on primary sales.
  - Clarified that fees apply only to prebuilt contracts via the dashboard.
- Included information about thirdweb contracts being audited by third-party firms.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **Documentation**
  * Added an FAQ explaining costs for deploying prebuilt contracts via the dashboard.
  * Clarifies a 2% dashboard deployment fee plus a 0.5% protocol fee (total 2.5%) taken from primary sales.
  * Includes an example payout and lists affected contract categories.
  * Notes that custom or externally deployed contracts are exempt.
  * Specifies fees are deducted in the sale’s currency and placed after the existing FAQ on using thirdweb with existing contracts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-29 01:16:31 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
65a8c39ce0 Version Packages (#7945)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-28 18:15:35 -07:00
Joaquim VergesandGitHub ac3a95ebbb [AI SDK Provider] Fix type inference for tools (#7944) 2025-08-28 18:10:21 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
00ffe3e46e Version Packages (#7943)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-28 15:46:28 -07:00
gregandGitHub 50c6371e1e [SDK] Feature: add ability to exclude token prices from Bridge.tokens (#7942) 2025-08-28 15:22:12 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
93532ecbdc Version Packages (#7932)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-28 15:14:50 -07:00
Joaquim VergesandGitHub f3ad6efcf8 [Package] @thirdweb-dev/ai-sdk-provider (#7922) 2025-08-28 15:08:23 -07:00
MananTank 67616c22b4 [MNY-127] Dashboard: Remove unnecessary server action for fetching token list (#7940)
<!--

## 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 token management in the universal bridge API, including the introduction of a new `TokenMetadata` type and the relocation of the `getUniversalBridgeTokens` function to a new file. It also updates import paths for consistency.

### Detailed summary
- Added `TokenMetadata` type in `apps/dashboard/src/@/api/universal-bridge/types.ts`.
- Updated import path for `getUniversalBridgeTokens` in `apps/dashboard/src/@/hooks/tokens.ts` and `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/CreatePaymentLinkButton.client.tsx`.
- Created `getUniversalBridgeTokens` function in `apps/dashboard/src/@/api/universal-bridge/token-list.ts`.
- Removed old `getUniversalBridgeTokens` function and `TokenMetadata` type from `apps/dashboard/src/@/api/universal-bridge/tokens.ts`.

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

<!-- end pr-codex -->
2025-08-28 21:37:31 +00:00
MananTank 51f7418d1f [MNY-128] Dashboard: UI tweaks in token creation flow (#7939)
<!--

## 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 user interface and error handling in the token and NFT launch processes, as well as refining the layout and component interactions in the distribution section.

### Detailed summary
- Removed `isPending` state checks from token and NFT launch components.
- Updated button layouts to improve user experience in `launch-token.tsx` and `launch-nft.tsx`.
- Changed error message text in `token-distribution.tsx`.
- Added `errorMessage` prop to `StepCard` for better error display.
- Adjusted layout and styling for improved responsiveness and alignment.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- New Features
  - Step headers can now display error messages.
  - Distribution chart moved into the main form flow for clearer visibility.
  - Post-launch actions updated: show “View NFT Collection” or “View Coin” when available; show “Cancel” before completion.

- Refactor
  - Simplified bottom action areas and removed pending-based button disabling for a more predictable flow.

- Style
  - Improved spacing, alignment, and responsiveness in forms.
  - Updated error text to “Token allocation exceeds total supply” for clearer guidance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-28 21:26:54 +00:00
MananTank dc3df4bc35 [MNY-11] Dashboard: Add ERC20 token selector for starting price in token creation flow (#7933)
<!--

## 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 token creation process by modifying the `onLaunchSuccess` function to accept `CreateAssetFormValues`, updating the way initial tick values are calculated, and improving the token selection interface.

### Detailed summary
- Updated `onLaunchSuccess` in `launch-token.tsx` and `create-token-page.client.tsx` to accept `formValues` and `contractAddress`.
- Modified `getInitialTickValue` to include `tokenAddress`, `chain`, and `client` parameters.
- Added token selection functionality in the `PoolConfig` component using `TokenSelector`.
- Adjusted form validation and pricing logic for token creation.
- Enhanced UI layout in `PoolConfig` for better responsiveness and usability.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- New Features
  - Token selector added to choose paired currency (native or ERC‑20) and a field to specify/select token address for ERC‑20 pools.
  - Bridging on launch is now performed only for ERC‑20 asset pool launches.

- Bug Fixes
  - Starting-price handling improved to account for token decimals and precompute/validate launch pricing.

- Style
  - Responsive layout and spacing refinements for pricing and allocation inputs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-28 22:40:11 +05:30
MananTank 0853872d8d Dashboard: Fix WalletAddress ens image size (#7935)
<!--

## 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 adjusting the size of certain UI components in the `RecentTransfers` and `WalletAddress` sections, ensuring a more consistent design across the dashboard.

### Detailed summary
- In `RecentTransfers.tsx`, removed the `iconClassName` prop from a component.
- In `wallet-address.tsx`, changed the size of a `div` from `size-6` to `size-5`.
- Updated the `CircleSlashIcon` size from `size-6` to `size-5`.
- Added a `fallback` prop to the `Img` component in `WalletAvatar`.
- Changed the `size` of the `Blobbie` component in `WalletAvatar` from `24` to `20`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- New Features
  - Added automatic address-based avatar fallback when a wallet/avatar image isn’t available.

- Style
  - Reduced sizes for the invalid-address badge and zero-address indicator for better visual balance.
  - Adjusted fallback avatar size for improved consistency.
  - Standardized icon sizing in Recent Transfers by removing a custom icon size override.
  - Overall, wallet/address visuals are cleaner and more consistent across the dashboard.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-28 12:14:10 +00:00
gregandGitHub 44e6e115d9 [SDK] Feature: Allow proceeding past first checkout widget page with no wallet (#7937) 2025-08-27 21:16:01 -07:00
gregandGitHub 76cfecb305 [Dashboard] Feature: Update payment success copy on payment page (#7936) 2025-08-27 21:03:12 -07:00
MananTank f33d8f16e2 packages/ui: Add markdown-renderer (#7928)
<!--

## 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 and consolidating the `MarkdownRenderer` component across different applications, enhancing code reusability and organization by utilizing shared components from the `@workspace/ui` package. It also updates dependencies and modifies some configurations.

### Detailed summary
- Deleted `plaintext-code.tsx` from `apps/portal/src/components/code/`.
- Updated `knip.json` files to include new dependencies: `react-children-utilities`, `react-markdown`, and `remark-gfm`.
- Simplified export of `CodeClient` in `code.client.tsx`.
- Added `InlineCode` and `UnderlineLink` components in `packages/ui`.
- Refactored `MarkdownRenderer` in `apps/dashboard` to use shared components from `@workspace/ui`.
- Removed redundant imports in `MarkdownRenderer` files across different applications.
- Updated `pnpm-lock.yaml` with new package versions.
- Adjusted class names in `MarkdownRenderer` for consistent styling.

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

<!-- end pr-codex -->
2025-08-27 23:18:35 +00:00
MananTank 5c08c7c17d packages/ui: Add code components (#7927)
<!--

## 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 code related to the `CodeClient` component, improving its structure and imports, while also updating several components to use a new centralized location for code utilities. It removes deprecated files and enhances code organization across the project.

### Detailed summary
- Deleted unused files related to clipboard and code rendering.
- Refactored `CodeClient` to use a centralized import from `@workspace/ui/components/code/code.client`.
- Updated various components to import `CodeClient` and other utilities from the new centralized location.
- Added new `CodeServer` component for server-side rendering of code.
- Improved loading states and error handling in code components.
- Updated component imports across the project to maintain consistency.
- Enhanced `PlainTextCodeBlock` functionality with new props for better customization.
- Removed redundant code and improved overall project structure.

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

<!-- end pr-codex -->
2025-08-27 23:18:35 +00:00
MananTank e20b1dd883 packages/ui: Add breadcrumb, tooltip, table (#7926)
<!--

## 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 adding the `@radix-ui/react-tooltip` package and refactoring the tooltip and breadcrumb components across multiple applications to enhance UI functionality and maintainability.

### Detailed summary
- Added `@radix-ui/react-tooltip` dependency in `package.json`.
- Updated `knip.json` files to include `@radix-ui/react-tooltip` in ignored dependencies.
- Refactored tooltip components in `packages/ui/src/components/tooltip.tsx` to utilize `@radix-ui/react-tooltip`.
- Created new `Breadcrumb` components in `packages/ui/src/components/breadcrumb.tsx`.
- Updated table components in `packages/ui/src/components/table.tsx` to streamline exports and structure.
- Removed redundant tooltip component definitions from multiple applications and replaced them with imports from `@workspace/ui/components/tooltip`.

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

<!-- end pr-codex -->
2025-08-27 23:18:35 +00:00
MananTank 55a4728766 packages/ui: Add Spinner, Img (#7925)
<!--

## 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 refactoring the `Spinner` component across multiple applications by centralizing its import path and removing redundant files. It also introduces a new spinner style in the Tailwind configuration.

### Detailed summary
- Deleted multiple `Spinner.tsx` and `Spinner.module.css` files from various directories.
- Updated imports of `Spinner` to a centralized path: `@workspace/ui/components/spinner`.
- Added new spinner animation styles in `tailwind.config.ts`.
- Updated the `Spinner` component to use Tailwind classes for styling instead of CSS modules.
- Adjusted stories for `Spinner` to reflect the new structure and styles.

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

<!-- end pr-codex -->
2025-08-27 23:18:34 +00:00
MananTank c5ef55e4f6 packages/ui: Add scroll-shadow (#7924)
<!--

## 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 `ScrollShadow` component by consolidating its usage across various applications and removing redundant files. It also enhances the component's styling and functionality.

### Detailed summary
- Deleted multiple `ScrollShadow` component files from various apps.
- Created a new `ScrollShadow` component in `packages/ui/src/components/scroll-shadow.tsx`.
- Updated import paths for `ScrollShadow` in multiple files across the applications.
- Simplified styling by removing CSS module imports and integrating styles directly within the component.
- Added new CSS properties for shadows as inline styles.

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

<!-- end pr-codex -->
2025-08-27 23:18:34 +00:00
MananTank afd1150984 packages/ui: Add storybook, Add Badge, Input (#7923)
<!--

## 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 several updates focused on enhancing the UI components, improving Storybook integration, and updating dependencies. It also refactors some components for better modularity and performance.

### Detailed summary
- Added `class-variance-authority` and updated `tailwindcss-animate` in `knip.json`.
- Updated `tailwind.config.ts` to include `.storybook` directory in content.
- Refactored imports for `Button` and `Input` components.
- Created `Badge` component and its variants in `badge.tsx`.
- Updated Storybook configuration in `.storybook/main.ts` and `preview.tsx`.
- Refactored `Input` components in multiple locations to use a shared implementation.
- Added `Toaster` setup in Storybook preview for better notifications.
- Updated dependencies in `package.json` and `pnpm-lock.yaml`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Shared Badge and Input components added to the UI package and made available to apps.
  - Themed Storybook for the UI library with mobile viewports, notifications, data-fetching context, and an in-app theme toggle.

- **Refactor**
  - Apps now re-export Badge/Input from the shared UI package; story utilities and preview/layout centralized.

- **Chores**
  - Storybook script and dependencies added, Tailwind content updated, and import paths standardized.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-27 23:18:33 +00:00
gregandGitHub f79672ebbf [Dashboard]: Fix: Remove in-app wallet connection option on payment page (#7934) 2025-08-27 16:00:13 -07:00
MananTank 3a289e80d1 Dashboard: Fix token not added to UB on launch success (#7931)
<!--

## 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 handling of the `contractAddress` state in the `LaunchTokenStatus` component by using a `ref` to avoid stale closures and ensure the latest address is used during execution.

### Detailed summary
- Added `useCallback` and `useRef` imports.
- Replaced the state setter for `contractAddress` with a callback to update a `ref`.
- Updated the condition to check `contractAddressRef.current` instead of `contractAddress` when launching.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- Bug Fixes
  - Improved reliability of the token launch flow so the final confirmation consistently uses the latest deployed contract address.
  - Prevents rare cases where an outdated address could be shown or used after deployment, reducing intermittent post-launch errors.
  - Fix applies to ERC20 and Drop ERC20 deployments; no visible UI changes, just more consistent launches and confirmations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-27 18:54:33 +00:00
Jonas DanielsandGitHub e9efebaec4 Always show Engine in sidebar regardless of instances (#7929) 2025-08-27 11:53:25 -07:00
Jonas DanielsandGitHub d4a44f5b5f Add reason code type to team capabilities response (#7921) 2025-08-27 11:46:02 -07:00
MananTank e651d0ae91 Dashboard: Update Project header style (#7920)
<!--

## 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 updating UI components across various files in the application. It includes adjustments to icon sizes, component styles, and the introduction of new icons, enhancing the overall user interface and experience.

### Detailed summary
- Changed `PlusIcon` size from `size-4` to `size-3.5` in multiple buttons.
- Replaced `SettingsIcon` with `Settings2Icon`.
- Added `DatabaseIcon`, `WebhookIcon`, `TokenIcon`, `ArrowDownToLineIcon`, and others to various headers.
- Updated button styles to `bg-card` and changed variants to `outline`.
- Modified header layouts to include new icons and adjusted spacing.
- Removed actions from several `ProjectPage` components and replaced them with links.
- Adjusted descriptions and added new links in the `ContractsPage`, `PaymentsPage`, and other components.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- New Features
  - Header icons added across many pages; Settings now appears as a dedicated header control where provided.
  - Single link renders as a direct action button; multiple links grouped under a labeled "Resources" menu with chevron.

- Refactor
  - Project header redesigned to an icon-driven layout; mobile-specific branching removed and actions reorganized.

- Style
  - Button variants, spacing, iconography, and dropdown alignment adjusted (smaller icons, new arrow/chevron visuals, reduced item padding).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-26 22:35:44 +00:00
Joaquim VergesandGitHub 34239ea2cb [Docs] Rename Chat API to Blockchain LLM and add transaction execution docs (#7904) 2025-08-26 03:49:59 -07:00
MananTank 8828df0a58 Dashboard: Fix project links footer not sticked to the bottom on short pages (#7909)
<!--

## 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 layout of the `ProjectPage` component by modifying the class names of certain `div` elements to improve styling and spacing.

### Detailed summary
- Changed the class name of the main `div` from `pb-20` to `grow`.
- Added a new `div` with class `h-20` to provide spacing.
- Updated the class name of the footer `div` from `mt-20` to `mt-auto` for better positioning.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **Style**
  - Updated project page layout for more consistent spacing and alignment.
  - Standardized vertical spacing and removed conditional bottom padding to ensure predictable layout across views.
  - Footer now aligns to the bottom with automatic spacing, improving visual balance on longer pages.
  - Added a vertical spacer to separate main content from the footer for clearer hierarchy and readability.
  - No changes to public APIs or functionality; visual/spacing adjustments only.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-26 00:59:42 +00:00
MananTank c6150be299 Dashboard: Create Payment link dialog fixes/ui tweaks (#7908)
<!--

## 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 `CreatePaymentLinkButton` component to enhance its dialog interface and improve the overall user experience. It replaces the `LinkIcon` with a `PlusIcon`, refines form elements, and cleans up unused code.

### Detailed summary
- Replaced `LinkIcon` with `PlusIcon`.
- Updated dialog structure and content.
- Changed `RequiredFormLabel` to `FormLabel` for consistency.
- Improved styling by adding `bg-card` class to input fields.
- Removed commented-out code related to image upload functionality.
- Enhanced form layout and submission handling.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **Refactor**
  - Streamlined the payment link creation flow with a clearer dialog and improved form handling for a smoother experience.

- **Style**
  - Updated to a plus icon, added a header with title and description, standardized labels, themed inputs, and introduced a bottom action bar.

- **Bug Fixes**
  - Chain and token changes now properly reset related fields.
  - Improved validation for recipient address and token selection.
  - Added progress indicator during creation.
  - Success toast and automatic refresh ensure newly created payment links appear immediately.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-25 20:37:52 +00:00
MananTank 27f50a863c [BLD-143] Dashboard: Fix distribute funds in split contract (#7907)
<!--

## 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 `useSplitDistributeFunds` hook and the `DistributeButton` component by refining the distribution logic and enhancing error handling.

### Detailed summary
- Replaced direct comparison of `currency.name` with a check using `getAddress(currency.tokenAddress)` against `getAddress(NATIVE_TOKEN_ADDRESS)`.
- Added `await` for the promise in the `useSplitDistributeFunds` function.
- Renamed the mutation variable in `DistributeButton` from `mutation` to `distributeFundsMutation`.
- Updated the `distributeFunds` function to use `distributeFundsMutation.mutateAsync()`.
- Adjusted the `isPending` prop in `TransactionButton` to reference `distributeFundsMutation.isPending`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- Bug Fixes
  - Correctly detects and handles the native token during fund distribution, ensuring accurate routing and transaction counts.
  - Ensures each distribution transaction completes before the next begins, reducing partial or overlapping operations.

- Refactor
  - Streamlined distribution flow and loading state handling for a more consistent experience.
  - Removed toast-based error pop-ups during distribution; status is reflected via the ongoing operation state.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-25 20:09:45 +00:00
MananTank 157f4b9a19 Dashboard: Chain Infra pages UI improvements (#7906)
<!--

## 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 layout and user experience in the `Deploy Infrastructure` section of the application. It refines component styles, enhances breadcrumb navigation, and updates form elements for better accessibility and consistency.

### Detailed summary
- Adjusted spacing in the `InfraServiceCard` component.
- Enhanced typography and layout in various sections.
- Added breadcrumb navigation for better user context.
- Updated the `DeployInfrastructureForm` styles for consistency.
- Improved chain selection interface with better guidance.
- Refined the subscription summary layout.
- Enhanced accessibility features across components.

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

<!-- end pr-codex -->
2025-08-25 17:21:38 +00:00
MananTank fbe1aa2e53 Dashboard: Various UI tweaks in project pages (#7905)
<!--

## 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 UI components and improving the layout across various pages in the dashboard application. It includes adjustments to button styles, layout properties, and content formatting for better user experience.

### Detailed summary
- Updated button styles for consistency (size and variant).
- Adjusted layout properties for various components (e.g., `max-w-7xl`, padding).
- Enhanced descriptions with line breaks for clarity.
- Modified component structures for better alignment and spacing.
- Removed unnecessary links from `webhooks` and `contracts` sections.
- Improved the `ProjectPageHeader` and `ProjectPageFooter` components.
- Revised `LinkGroup` to utilize dynamic icon rendering.
- Enhanced `EmptyStateCard` and `TotalSponsoredCard` layouts.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- New Features
  - Headers now accept rich content (React nodes) enabling responsive line breaks.

- Style
  - Standardized small "secondary" bordered pill buttons across many actions and dialogs.
  - Updated header typography, spacing, avatar sizing, and max-width constraints for a more prominent layout.
  - Icons adjusted for size/color; token cards now stack vertically; dialog widths constrained; footer container width tightened.

- Bug Fixes / Refactor
  - Replaced visual separators with CSS borders and simplified DOM structure.
  - Removed certain header doc links from Webhooks and Payments pages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-25 17:09:36 +00:00
bdcbe0e25b Add ai usage section to dashboard (#7902)
Co-authored-by: Cursor Agent <[email protected]>
2025-08-24 19:47:56 -07:00
Joaquim VergesandGitHub 30e217870d [Docs] Update API domains (#7903) 2025-08-24 19:18:24 -07:00
Jonas DanielsandGitHub 44fdfeef1a Rename "Payment Links" to "Payments" in UI text (#7900) 2025-08-22 21:18:07 -07:00
0xFirekeeper 4cda30a87a Clarify authentication flows in wallet API docs (#7899)
Updated the documentation to distinguish between two-step authentication for email, phone, passkey, and SIWE, and single-step flows for guest, custom, and social authentication. Added details and endpoint references for each flow to improve clarity for API users.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the documentation for the authentication process in the `wallets` page, clarifying the steps for different authentication methods and introducing a new section on social authentication.

### Detailed summary
- Clarified the two-step authentication process for email, phone, passkey, or SIWE.
- Added information on skipping the first step for guest or custom authentication.
- Updated the description for initiating authentication to specify SIWE.
- Introduced a new section on social authentication, detailing a single-step flow.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Documentation**
  * Clarified API Authentication flows: two-step for email, phone, passkey, or SIWE; skip-first-step option for guest or custom auth.
  * Updated headings and examples to reflect the revised two-step flow (initiate to get a challenge, then complete).
  * Added a Social Authentication section describing a single-step OAuth flow via a dedicated endpoint, with links to provider details and code samples.
  * Reorganized content so Get Wallet Information follows the updated authentication flows for better readability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-23 00:37:20 +00:00
MananTank 98fab93e8b [BLD-166] Dashboard: Show Manage Contract button if user has used dashboard (#7898)
<!--

## 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 constant `HAS_USED_DASHBOARD` to track user engagement with the dashboard. It updates several components to include the `isDashboardUser` prop, allowing for conditional rendering based on whether the user has previously accessed the dashboard.

### Detailed summary
- Added `HAS_USED_DASHBOARD` constant in `cookie.ts`.
- Updated `NFTPublicPageLayout` to accept `isDashboardUser` prop.
- Modified `SaveLastVisitedTeamPage` to set `HAS_USED_DASHBOARD` cookie.
- Integrated `isDashboardUser` in `NFTPublicPage` and `ERC20PublicPage`.
- Updated `ContractHeaderUI` to conditionally render elements based on `isDashboardUser`.
- Enhanced storybook stories to include `isDashboardUser` prop for testing.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Dashboard users are now detected via a persistent flag so public ERC20 and NFT pages can tailor the header UI and surface “Manage Contract” controls when appropriate.
  * Stories updated to cover dashboard vs non-dashboard header states.

* **Bug Fixes**
  * More reliable dashboard-user detection ensures the “Manage Contract” controls display consistently on public contract pages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-22 21:27:48 +00:00
MananTank 6caba380b2 [MNY-115] Dashboard: Fix token creation flow not working for custom chains (#7896)
<!--

## 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 custom hook `useGetV5DashboardChain` and modifies existing components to utilize this hook for chain management, improving code clarity and reducing redundancy.

### Detailed summary
- Added `useGetV5DashboardChain` hook in `v5-adapter.ts`.
- Updated `LaunchNFT` component to use `useV5DashboardChain`.
- Refactored `CreateTokenAssetPageUI` to use `useV5DashboardChain`.
- Refactored `CreateNFTPage` to use `getChain` from `useGetV5DashboardChain`.
- Refactored `CreateTokenAssetPage` to use `getChain` from `useGetV5DashboardChain`.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- Refactor
  - Unified chain selection across NFT creation, NFT launch, and token creation flows using a single, consistent resolver.
  - Modernized internals for better compatibility with the latest chain data.

- Bug Fixes
  - Improved reliability of chain selection during deployments and validations, reducing chances of selecting the wrong network.
  - More consistent behavior when switching chains in forms and launch steps.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-21 19:26:44 +00:00
EimanandGitHub 68a07ed106 Portal Feature: changes to ai/mcp page to add buttons for downloading (#7890) 2025-08-21 11:49:17 -07:00
Jonas DanielsandGitHub b075ff55a1 Remove "dedicated" from engine URLs (#7897) 2025-08-21 09:28:25 -07:00
Jonas DanielsandGitHub 2d12249719 Remove "dedicated" from Engine URLs (#7894) 2025-08-20 21:35:47 -07:00
Jonas DanielsandGitHub 301b835b60 Remove RecentPaymentsSection and reduce payment history page size (#7893) 2025-08-20 20:33:51 -07:00
Jonas DanielsandGitHub a02b55744b unified project layout (#7888) 2025-08-20 18:38:12 -07:00
gregandGitHub 8b6deadc35 [Dashboard] Feature: Adds paired address for expedited token route finding (#7892) 2025-08-20 15:56:08 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
6d635dc266 Version Packages (#7891)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-20 10:24:39 -07:00
gregandGitHub 1083316ef6 [SDK] Feature: prepareCreateToken returns prepared transaction (#7872) 2025-08-20 10:06:41 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2c1ec47a04 Version Packages (#7889)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-20 03:50:57 -07:00
e01af9db71 fix: #7886 (#7887)
Signed-off-by: leo.shi <[email protected]>
Signed-off-by: Jonas Daniels <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2025-08-20 03:41:16 -07:00
Joaquim VergesandGitHub fd7e10d3de [Dashboard] Fix date handling in analytics components (#7885) 2025-08-19 17:59:55 -07:00
Joaquim VergesandGitHub 3126d87740 [Dashboard] improve wallet stats (#7880) 2025-08-19 15:51:01 -07:00
nischitpra b9e63ff0a7 add webhook id to test webhook (#7884)
<!-- start pr-codex -->

## PR-Codex overview
This PR introduces a new optional property `webhook_id` to the `TestWebhookPayload` interface and updates the call to `testWebhook` to include this new property.

### Detailed summary
- Added an optional property `webhook_id` to the `TestWebhookPayload` interface in `webhooks.ts`.
- Updated the `testWebhook` function call in `useTestWebhook.ts` to include `webhook_id` when invoking the function.

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

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Bug Fixes**
  * Test Webhook requests now include the specific webhook ID when available, ensuring the test targets the correct webhook and improving reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-19 22:35:05 +00:00
Jonas DanielsandGitHub 77053049a5 Always show Chain Infrastructure in sidebar regardless of subscriptions (#7883) 2025-08-19 09:51:09 -07:00
1017 changed files with 34576 additions and 25174 deletions
+2
View File
@@ -172,6 +172,8 @@ See our [open source page](https://thirdweb.com/open-source) for more informatio
- [Documentation](https://portal.thirdweb.com/)
- [Templates](https://thirdweb.com/templates)
- [YouTube](https://www.youtube.com/c/thirdweb)
- [X/Twitter](https://x.com/thirdweb)
- [Telegram](https://t.me/officialthirdweb)
## Support
+24
View File
@@ -95,6 +95,30 @@ module.exports = {
'Import "posthog-js" directly only within the analytics helpers ("src/@/analytics/*"). Use the exported helpers from "@/analytics/track" elsewhere.',
name: "posthog-js",
},
{
importNames: ["useSendTransaction"],
message:
'Use `import { useSendAndConfirmTx } from "@/hooks/useSendTx";` instead',
name: "thirdweb/react",
},
{
importNames: ["useSendAndConfirmTransaction"],
message:
'Use `import { useSendAndConfirmTx } from "@/hooks/useSendTx";` instead',
name: "thirdweb/react",
},
{
importNames: ["sendTransaction"],
message:
'Use `import { useSendAndConfirmTx } from "@/hooks/useSendTx";` instead if used in react component',
name: "thirdweb",
},
{
importNames: ["sendAndConfirmTransaction"],
message:
'Use `import { useSendAndConfirmTx } from "@/hooks/useSendTx";` instead if used in react component',
name: "thirdweb",
},
],
patterns: [
{
+6 -9
View File
@@ -1,14 +1,13 @@
import type { Preview } from "@storybook/nextjs";
import "../src/global.css";
import "@workspace/ui/global.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Button } from "@workspace/ui/components/button";
import { MoonIcon, SunIcon } from "lucide-react";
import { ThemeProvider, useTheme } from "next-themes";
import { Inter as interFont } from "next/font/google";
// biome-ignore lint/style/useImportType: <explanation>
import React from "react";
import { useEffect } from "react";
import { ThemeProvider, useTheme } from "next-themes";
// biome-ignore lint/style/useImportType: ok
import React, { useEffect } from "react";
import { Toaster } from "sonner";
import { Button } from "../src/@/components/ui/button";
const queryClient = new QueryClient();
@@ -69,9 +68,7 @@ const preview: Preview = {
export default preview;
function StoryLayout(props: {
children: React.ReactNode;
}) {
function StoryLayout(props: { children: React.ReactNode }) {
const { setTheme, theme } = useTheme();
useEffect(() => {
+1
View File
@@ -64,4 +64,5 @@ module.exports = [
"/enterprise",
"/token",
"/vault",
"/monetize/bridge",
];
+7 -1
View File
@@ -8,13 +8,19 @@
],
"ignoreBinaries": ["only-allow"],
"ignoreDependencies": [
"@thirdweb-dev/api",
"@thirdweb-dev/service-utils",
"@thirdweb-dev/vault-sdk",
"thirdweb",
"@types/color",
"fast-xml-parser",
"@workspace/ui",
"tailwindcss-animate"
"tailwindcss-animate",
"@radix-ui/react-tooltip",
"shiki",
"react-children-utilities",
"react-markdown",
"remark-gfm"
],
"next": true,
"project": ["src/**"]
+3 -3
View File
@@ -19,11 +19,11 @@
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.5",
"@radix-ui/react-tooltip": "1.2.7",
"@scalar/api-reference-react": "0.7.25",
"@sentry/nextjs": "9.34.0",
"@shazow/whatsabi": "0.22.2",
"@tanstack/react-query": "5.81.5",
"@tanstack/react-table": "^8.21.3",
"@thirdweb-dev/api": "workspace:*",
"@thirdweb-dev/service-utils": "workspace:*",
"@thirdweb-dev/vault-sdk": "workspace:*",
"@vercel/functions": "2.2.2",
@@ -35,6 +35,7 @@
"compare-versions": "^6.1.0",
"date-fns": "4.1.0",
"fast-xml-parser": "^5.2.5",
"fetch-event-stream": "0.1.5",
"fuse.js": "7.1.0",
"input-otp": "^1.4.1",
"ioredis": "^5.6.1",
@@ -47,7 +48,6 @@
"papaparse": "^5.5.3",
"pluralize": "^8.0.0",
"posthog-js": "1.256.1",
"posthog-node": "^5.4.0",
"prettier": "3.6.2",
"react": "19.1.0",
"react-children-utilities": "^2.10.0",
@@ -62,7 +62,7 @@
"remark-gfm": "4.0.1",
"responsive-rsc": "0.0.7",
"server-only": "^0.0.1",
"shiki": "1.27.0",
"shiki": "3.12.0",
"sonner": "2.0.6",
"spdx-correct": "^3.2.0",
"stripe": "17.7.0",
+1
View File
@@ -14,6 +14,7 @@ type ProxyActionParams = {
body?: string;
headers?: Record<string, string>;
parseAsText?: boolean;
signal?: AbortSignal;
};
type ProxyActionResult<T> =
@@ -1,57 +0,0 @@
import "server-only";
import { unstable_cache } from "next/cache";
import { PostHog } from "posthog-node";
let _posthogClient: PostHog | null = null;
function getPostHogServer(): PostHog | null {
if (!_posthogClient && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
_posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
host: "https://us.i.posthog.com",
});
}
return _posthogClient;
}
/**
* Check if a feature flag is enabled for a specific user
* @param flagKey - The feature flag key
* @param userEmail - The user's email address for filtering
*/
export const isFeatureFlagEnabled = unstable_cache(
async (params: {
flagKey: string;
accountId: string;
email: string | undefined;
}): Promise<boolean> => {
const posthogClient = getPostHogServer();
if (!posthogClient) {
console.warn("Posthog client not set");
return true;
}
const { flagKey, accountId, email } = params;
try {
if (posthogClient && accountId) {
const isEnabled = await posthogClient.isFeatureEnabled(
flagKey,
accountId,
{
personProperties: email ? { email } : undefined,
},
);
if (isEnabled !== undefined) {
return isEnabled;
}
}
} catch (error) {
console.error(`Error checking feature flag ${flagKey}:`, error);
}
return false;
},
["is-feature-flag-enabled"],
{
revalidate: 3600, // 1 hour
},
);
+92
View File
@@ -250,6 +250,54 @@ export function reportAssetBuySuccessful(properties: {
});
}
type TokenSwapParams = {
buyTokenChainId: number;
buyTokenAddress: string;
sellTokenChainId: number;
sellTokenAddress: string;
pageType: "asset" | "bridge" | "chain";
};
/**
* ### Why do we need to report this event?
* - To track number of successful token swaps from the token page
* - To track which tokens are being swapped the most
*
* ### Who is responsible for this event?
* @MananTank
*/
export function reportTokenSwapSuccessful(properties: TokenSwapParams) {
posthog.capture("token swap successful", properties);
}
/**
* ### Why do we need to report this event?
* - To track number of failed token swaps from the token page
* - To track which tokens are being swapped the most
*
* ### Who is responsible for this event?
* @MananTank
*/
export function reportTokenSwapFailed(
properties: TokenSwapParams & {
errorMessage: string;
},
) {
posthog.capture("token swap failed", properties);
}
/**
* ### Why do we need to report this event?
* - To track number of cancelled token swaps from the token page
* - To track which tokens are being swapped the most
*
* ### Who is responsible for this event?
* @MananTank
*/
export function reportTokenSwapCancelled(properties: TokenSwapParams) {
posthog.capture("token swap cancelled", properties);
}
/**
* ### Why do we need to report this event?
* - To track number of failed asset purchases from the token page
@@ -272,6 +320,26 @@ export function reportAssetBuyFailed(properties: {
});
}
/**
* ### Why do we need to report this event?
* - To track number of cancelled asset purchases from the token page
* - To track the errors that users encounter when trying to purchase an asset
*
* ### Who is responsible for this event?
* @MananTank
*/
export function reportAssetBuyCancelled(properties: {
chainId: number;
contractType: AssetContractType;
assetType: "nft" | "coin";
}) {
posthog.capture("asset buy cancelled", {
assetType: properties.assetType,
chainId: properties.chainId,
contractType: properties.contractType,
});
}
// Assets Landing Page ----------------------------
/**
@@ -547,3 +615,27 @@ export function reportChainInfraRpcOmissionAgreed(properties: {
}) {
posthog.capture("chain infra checkout rpc omission agreed", properties);
}
// ----------------------------
// FEEDBACK
// ----------------------------
/**
* ### Why do we need to report this event?
* - To track user feedback and sentiment about the product
* - To identify common issues or feature requests
* - To measure user satisfaction and engagement
* - To prioritize product improvements based on user input
*
* ### Who is responsible for this event?
* @gisellechacon
*/
export function reportProductFeedback(properties: {
feedback: string;
source: "desktop" | "mobile";
}) {
posthog.capture("product feedback submitted", {
feedback: properties.feedback,
source: properties.source,
});
}
+45 -94
View File
@@ -4,6 +4,7 @@ import { unstable_cache } from "next/cache";
import { ANALYTICS_SERVICE_URL } from "@/constants/server-envs";
import { normalizeTime } from "@/lib/time";
import type {
AIUsageStats,
AnalyticsQueryParams,
EcosystemWalletStats,
EngineCloudStats,
@@ -13,9 +14,7 @@ import type {
UniversalBridgeWalletStats,
UserOpStats,
WalletStats,
WalletUserStats,
WebhookLatencyStats,
WebhookRequestStats,
WebhookSummaryStats,
} from "@/types/analytics";
import { getChains } from "./chain";
@@ -147,7 +146,13 @@ const cached_getWalletConnections = unstable_cache(
}
const json = await res.json();
return json.data as WalletStats[];
return (json.data as WalletStats[]).filter(
(w) =>
w.walletType !== "smart" &&
w.walletType !== "smartWallet" &&
w.walletType !== "inApp" &&
w.walletType !== "inAppWallet",
);
},
["getWalletConnections"],
{
@@ -200,6 +205,41 @@ export function getInAppWalletUsage(
return cached_getInAppWalletUsage(normalizedParams(params), authToken);
}
const cached_getAiUsage = unstable_cache(
async (
params: AnalyticsQueryParams,
authToken: string,
): Promise<AIUsageStats[]> => {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics({
authToken,
url: `v2/nebula/usage?${searchParams.toString()}`,
init: {
method: "GET",
},
});
if (res?.status !== 200) {
const reason = await res?.text();
console.error(
`Failed to fetch AI usage, ${res?.status} - ${res.statusText} - ${reason}`,
);
return [];
}
const json = await res.json();
return json.data as AIUsageStats[];
},
["getAiUsage"],
{
revalidate: 60 * 60, // 1 hour
},
);
export function getAiUsage(params: AnalyticsQueryParams, authToken: string) {
return cached_getAiUsage(normalizedParams(params), authToken);
}
const cached_getUserOpUsage = unstable_cache(
async (
params: AnalyticsQueryParams,
@@ -398,44 +438,6 @@ export function getRpcUsageByType(
return cached_getRpcUsageByType(normalizedParams(params), authToken);
}
const cached_getWalletUsers = unstable_cache(
async (
params: AnalyticsQueryParams,
authToken: string,
): Promise<WalletUserStats[]> => {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics({
authToken,
url: `v2/sdk/wallet-connects/users?${searchParams.toString()}`,
init: {
method: "GET",
},
});
if (res?.status !== 200) {
const reason = await res?.text();
console.error(
`Failed to fetch wallet user stats: ${res?.status} - ${res.statusText} - ${reason}`,
);
return [];
}
const json = await res.json();
return json.data as WalletUserStats[];
},
["getWalletUsers"],
{
revalidate: 60 * 60, // 1 hour
},
);
export function getWalletUsers(
params: AnalyticsQueryParams,
authToken: string,
) {
return cached_getWalletUsers(normalizedParams(params), authToken);
}
type ActiveStatus = {
bundler: boolean;
storage: boolean;
@@ -693,7 +695,7 @@ export function getEngineCloudMethodUsage(
return cached_getEngineCloudMethodUsage(normalizedParams(params), authToken);
}
const cached_getWebhookSummary = unstable_cache(
const _cached_getWebhookSummary = unstable_cache(
async (
params: AnalyticsQueryParams & { webhookId: string },
authToken: string,
@@ -721,51 +723,7 @@ const cached_getWebhookSummary = unstable_cache(
},
);
export function getWebhookSummary(
params: AnalyticsQueryParams & { webhookId: string },
authToken: string,
) {
return cached_getWebhookSummary(normalizedParams(params), authToken);
}
const cached_getWebhookRequests = unstable_cache(
async (
params: AnalyticsQueryParams & { webhookId?: string },
authToken: string,
): Promise<{ data: WebhookRequestStats[] } | { error: string }> => {
const searchParams = buildSearchParams(params);
if (params.webhookId) {
searchParams.append("webhookId", params.webhookId);
}
const res = await fetchAnalytics({
authToken,
url: `v2/webhook/requests?${searchParams.toString()}`,
init: {
method: "GET",
},
});
if (!res.ok) {
const reason = await res.text();
return { error: reason };
}
return (await res.json()) as { data: WebhookRequestStats[] };
},
["getWebhookRequests"],
{
revalidate: 60 * 60, // 1 hour
},
);
export function getWebhookRequests(
params: AnalyticsQueryParams & { webhookId?: string },
authToken: string,
) {
return cached_getWebhookRequests(normalizedParams(params), authToken);
}
const cached_getWebhookLatency = unstable_cache(
const _cached_getWebhookLatency = unstable_cache(
async (
params: AnalyticsQueryParams & { webhookId?: string },
authToken: string,
@@ -795,13 +753,6 @@ const cached_getWebhookLatency = unstable_cache(
},
);
export function getWebhookLatency(
params: AnalyticsQueryParams & { webhookId?: string },
authToken: string,
) {
return cached_getWebhookLatency(normalizedParams(params), authToken);
}
const cached_getInsightChainUsage = unstable_cache(
async (
params: AnalyticsQueryParams,
@@ -58,6 +58,7 @@ interface WebhookSingleResponse {
}
interface TestWebhookPayload {
webhook_id?: string;
webhook_url: string;
type?: "event" | "transaction";
}
@@ -1,362 +0,0 @@
"use server";
import { getAuthToken } from "@/api/auth-token";
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "@/constants/public-envs";
export interface WebhookConfig {
id: string;
teamId: string;
projectId: string;
destinationUrl: string;
description: string;
pausedAt: string | null;
webhookSecret: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
topics: {
id: string;
serviceName: string;
description: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
filters: object | null;
}[];
}
type WebhookConfigsResponse =
| {
data: WebhookConfig[];
status: "success";
}
| {
body: string;
reason: string;
status: "error";
};
interface CreateWebhookConfigRequest {
topicIdsWithFilters: { id: string; filters: object | null }[];
destinationUrl: string;
description: string;
isPaused?: boolean;
}
type CreateWebhookConfigResponse =
| {
data: WebhookConfig;
status: "success";
}
| {
body: string;
reason: string;
status: "error";
};
export interface Topic {
id: string;
serviceName: string;
description: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
}
type TopicsResponse =
| {
data: Topic[];
status: "success";
}
| {
body: string;
reason: string;
status: "error";
};
interface UpdateWebhookConfigRequest {
destinationUrl?: string;
topicIdsWithFilters?: { id: string; filters: object | null }[];
description?: string;
isPaused?: boolean;
}
type UpdateWebhookConfigResponse =
| {
data: WebhookConfig;
status: "success";
}
| {
body: string;
reason: string;
status: "error";
};
type DeleteWebhookConfigResponse =
| {
data: WebhookConfig;
status: "success";
}
| {
body: string;
reason: string;
status: "error";
};
export async function getWebhookConfigs(props: {
teamIdOrSlug: string;
projectIdOrSlug: string;
}): Promise<WebhookConfigsResponse> {
const authToken = await getAuthToken();
if (!authToken) {
return {
body: "Authentication required",
reason: "no_auth_token",
status: "error",
};
}
const response = await fetch(
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${props.teamIdOrSlug}/projects/${props.projectIdOrSlug}/webhook-configs`,
{
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
method: "GET",
},
);
if (!response.ok) {
const body = await response.text();
return {
body,
reason: "unknown",
status: "error",
};
}
const result = await response.json();
return {
data: result.data,
status: "success",
};
}
export async function createWebhookConfig(props: {
teamIdOrSlug: string;
projectIdOrSlug: string;
config: CreateWebhookConfigRequest;
}): Promise<CreateWebhookConfigResponse> {
const authToken = await getAuthToken();
if (!authToken) {
return {
body: "Authentication required",
reason: "no_auth_token",
status: "error",
};
}
const response = await fetch(
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${props.teamIdOrSlug}/projects/${props.projectIdOrSlug}/webhook-configs`,
{
body: JSON.stringify(props.config),
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
method: "POST",
},
);
if (!response.ok) {
const body = await response.text();
return {
body,
reason: "unknown",
status: "error",
};
}
const result = await response.json();
return {
data: result.data,
status: "success",
};
}
export async function getAvailableTopics(): Promise<TopicsResponse> {
const authToken = await getAuthToken();
if (!authToken) {
return {
body: "Authentication required",
reason: "no_auth_token",
status: "error",
};
}
const response = await fetch(
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/webhook-topics`,
{
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
method: "GET",
},
);
if (!response.ok) {
const body = await response.text();
return {
body,
reason: "unknown",
status: "error",
};
}
const result = await response.json();
return {
data: result.data,
status: "success",
};
}
export async function updateWebhookConfig(props: {
teamIdOrSlug: string;
projectIdOrSlug: string;
webhookConfigId: string;
config: UpdateWebhookConfigRequest;
}): Promise<UpdateWebhookConfigResponse> {
const authToken = await getAuthToken();
if (!authToken) {
return {
body: "Authentication required",
reason: "no_auth_token",
status: "error",
};
}
const response = await fetch(
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${props.teamIdOrSlug}/projects/${props.projectIdOrSlug}/webhook-configs/${props.webhookConfigId}`,
{
body: JSON.stringify(props.config),
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
method: "PATCH",
},
);
if (!response.ok) {
const body = await response.text();
return {
body,
reason: "unknown",
status: "error",
};
}
const result = await response.json();
return {
data: result.data,
status: "success",
};
}
export async function deleteWebhookConfig(props: {
teamIdOrSlug: string;
projectIdOrSlug: string;
webhookConfigId: string;
}): Promise<DeleteWebhookConfigResponse> {
const authToken = await getAuthToken();
if (!authToken) {
return {
body: "Authentication required",
reason: "no_auth_token",
status: "error",
};
}
const response = await fetch(
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${props.teamIdOrSlug}/projects/${props.projectIdOrSlug}/webhook-configs/${props.webhookConfigId}`,
{
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
method: "DELETE",
},
);
if (!response.ok) {
const body = await response.text();
return {
body,
reason: "unknown",
status: "error",
};
}
const result = await response.json();
return {
data: result.data,
status: "success",
};
}
type TestDestinationUrlResponse =
| {
result: {
httpStatusCode: number;
httpResponseBody: string;
};
status: "success";
}
| {
body: string;
reason: string;
status: "error";
};
export async function testDestinationUrl(props: {
teamIdOrSlug: string;
projectIdOrSlug: string;
destinationUrl: string;
}): Promise<TestDestinationUrlResponse> {
const authToken = await getAuthToken();
if (!authToken) {
return {
body: "Authentication required",
reason: "no_auth_token",
status: "error",
};
}
const resp = await fetch(
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${props.teamIdOrSlug}/projects/${props.projectIdOrSlug}/webhook-configs/test-destination-url`,
{
body: JSON.stringify({ destinationUrl: props.destinationUrl }),
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
method: "POST",
},
);
if (!resp.ok) {
return {
body: await resp.text(),
reason: "unknown",
status: "error",
};
}
return await resp.json();
}
@@ -297,38 +297,53 @@ export type PaymentsResponse = {
totalCount: number;
};
};
type BridgePaymentType = "buy" | "sell" | "transfer";
type OnrampPaymentType = "onramp";
export type Payment = {
// common
id: string;
blockNumber?: bigint;
transactionId: string;
createdAt: string;
clientId: string;
sender: string;
receiver: string;
developerFeeRecipient: string;
developerFeeBps: number;
transactions: Array<{
chainId: number;
transactionHash: string;
}>;
status: "PENDING" | "COMPLETED" | "FAILED" | "NOT_FOUND";
type: "buy" | "sell" | "transfer";
originAmount: bigint;
destinationAmount: bigint;
purchaseData: unknown;
originToken: {
address: string;
symbol: string;
decimals: number;
chainId: number;
};
destinationAmount: string;
destinationToken: {
address: string;
symbol: string;
decimals: number;
chainId: number;
};
createdAt: string;
};
purchaseData: unknown;
} & (
| {
type: BridgePaymentType;
transactionId: string;
blockNumber?: string;
sender: string;
developerFeeRecipient: string;
developerFeeBps: number;
originAmount: string;
originToken: {
address: string;
symbol: string;
decimals: number;
chainId: number;
};
}
| {
onrampId: string;
type: OnrampPaymentType;
}
);
export type BridgePayment = Extract<Payment, { type: BridgePaymentType }>;
export async function getPayments(props: {
clientId: string;
@@ -0,0 +1,35 @@
import { NEXT_PUBLIC_DASHBOARD_CLIENT_ID } from "@/constants/public-envs";
import { UB_BASE_URL } from "./constants";
import type { TokenMetadata } from "./types";
export async function getUniversalBridgeTokens(props: {
chainId?: number;
address?: string;
}) {
const url = new URL(`${UB_BASE_URL}/v1/tokens`);
if (props.chainId) {
url.searchParams.append("chainId", String(props.chainId));
}
if (props.address) {
url.searchParams.append("tokenAddress", props.address);
}
url.searchParams.append("limit", "1000");
url.searchParams.append("includePrices", "false");
const res = await fetch(url.toString(), {
headers: {
"Content-Type": "application/json",
"x-client-id": NEXT_PUBLIC_DASHBOARD_CLIENT_ID,
} as Record<string, string>,
method: "GET",
});
if (!res.ok) {
const text = await res.text();
throw new Error(text);
}
const json = await res.json();
return json.data as Array<TokenMetadata>;
}
@@ -1,48 +1,8 @@
"use server";
import type { ProjectResponse } from "@thirdweb-dev/service-utils";
import { getAuthToken } from "@/api/auth-token";
import { DASHBOARD_THIRDWEB_SECRET_KEY } from "@/constants/server-envs";
import { UB_BASE_URL } from "./constants";
export type TokenMetadata = {
name: string;
symbol: string;
address: string;
decimals: number;
chainId: number;
iconUri?: string;
};
export async function getUniversalBridgeTokens(props: {
chainId?: number;
address?: string;
}) {
const url = new URL(`${UB_BASE_URL}/v1/tokens`);
if (props.chainId) {
url.searchParams.append("chainId", String(props.chainId));
}
if (props.address) {
url.searchParams.append("tokenAddress", props.address);
}
url.searchParams.append("limit", "1000");
const res = await fetch(url.toString(), {
headers: {
"Content-Type": "application/json",
"x-secret-key": DASHBOARD_THIRDWEB_SECRET_KEY,
} as Record<string, string>,
method: "GET",
});
if (!res.ok) {
const text = await res.text();
throw new Error(text);
}
const json = await res.json();
return json.data as Array<TokenMetadata>;
}
import type { TokenMetadata } from "./types";
export async function addUniversalBridgeTokenRoute(props: {
chainId: number;
@@ -0,0 +1,8 @@
export type TokenMetadata = {
name: string;
symbol: string;
address: string;
decimals: number;
chainId: number;
iconUri?: string;
};
@@ -86,7 +86,9 @@ export function getLastNDaysRange(id: DurationId) {
throw new Error(`Invalid duration id: ${id}`);
}
const todayDate = new Date(Date.now());
const todayDate = new Date();
todayDate.setDate(todayDate.getDate() + 1); // Move to next day
todayDate.setHours(0, 0, 0, 0); // Set to start of next day (00:00)
const value: Range = {
from: subDays(todayDate, durationInfo.days),
@@ -22,7 +22,7 @@ export function ResponsiveTimeFilters(props: { defaultRange: DurationId }) {
});
return (
<div className="flex justify-end gap-3 flex-col lg:flex-row">
<div className="flex gap-3 flex-row">
<DateRangeSelector
className="rounded-full"
range={range}
@@ -7,7 +7,7 @@ import { toast } from "sonner";
import { reSubscribePlan } from "@/actions/billing";
import type { Team } from "@/api/team/get-team";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { pollWithTimeout } from "@/utils/pollWithTimeout";
import { tryCatch } from "@/utils/try-catch";
@@ -0,0 +1,159 @@
"use client";
import { useTheme } from "next-themes";
import { useState } from "react";
import type { Chain, ThirdwebClient } from "thirdweb";
import { BuyWidget, SwapWidget } from "thirdweb/react";
import {
reportAssetBuyCancelled,
reportAssetBuyFailed,
reportAssetBuySuccessful,
reportTokenSwapCancelled,
reportTokenSwapFailed,
reportTokenSwapSuccessful,
} from "@/analytics/report";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { parseError } from "@/utils/errorParser";
import { getSDKTheme } from "@/utils/sdk-component-theme";
export function BuyAndSwapEmbed(props: {
client: ThirdwebClient;
chain: Chain;
tokenAddress: string | undefined;
buyAmount: string | undefined;
pageType: "asset" | "bridge" | "chain";
}) {
const { theme } = useTheme();
const [tab, setTab] = useState<"buy" | "swap">("swap");
const themeObj = getSDKTheme(theme === "light" ? "light" : "dark");
return (
<div className="bg-card rounded-2xl border overflow-hidden flex flex-col">
<div className="flex gap-2.5 p-4 border-b border-dashed">
<TabButton
label="Swap"
onClick={() => setTab("swap")}
isActive={tab === "swap"}
/>
<TabButton
label="Buy"
onClick={() => setTab("buy")}
isActive={tab === "buy"}
/>
</div>
{tab === "buy" && (
<BuyWidget
amount={props.buyAmount || "1"}
chain={props.chain}
className="!rounded-2xl !w-full !border-none"
title=""
client={props.client}
connectOptions={{
autoConnect: false,
}}
onError={(e) => {
const errorMessage = parseError(e);
if (props.pageType === "asset") {
reportAssetBuyFailed({
assetType: "coin",
chainId: props.chain.id,
contractType: "DropERC20",
error: errorMessage,
});
}
}}
onCancel={() => {
if (props.pageType === "asset") {
reportAssetBuyCancelled({
assetType: "coin",
chainId: props.chain.id,
contractType: "DropERC20",
});
}
}}
onSuccess={() => {
if (props.pageType === "asset") {
reportAssetBuySuccessful({
assetType: "coin",
chainId: props.chain.id,
contractType: "DropERC20",
});
}
}}
theme={themeObj}
tokenAddress={props.tokenAddress as `0x${string}`}
paymentMethods={["card"]}
/>
)}
{tab === "swap" && (
<SwapWidget
client={props.client}
theme={themeObj}
className="!rounded-2xl !border-none !w-full"
prefill={{
sellToken: {
chainId: props.chain.id,
tokenAddress: props.tokenAddress,
},
// only set `buyToken` as "Native token" if `sellToken` is not a "native token" already
buyToken: props.tokenAddress
? {
chainId: props.chain.id,
}
: undefined,
}}
onError={(error, quote) => {
const errorMessage = parseError(error);
reportTokenSwapFailed({
errorMessage: errorMessage,
buyTokenChainId: quote.intent.destinationChainId,
buyTokenAddress: quote.intent.destinationTokenAddress,
sellTokenChainId: quote.intent.originChainId,
sellTokenAddress: quote.intent.originTokenAddress,
pageType: props.pageType,
});
}}
onSuccess={(quote) => {
reportTokenSwapSuccessful({
buyTokenChainId: quote.intent.destinationChainId,
buyTokenAddress: quote.intent.destinationTokenAddress,
sellTokenChainId: quote.intent.originChainId,
sellTokenAddress: quote.intent.originTokenAddress,
pageType: props.pageType,
});
}}
onCancel={(quote) => {
reportTokenSwapCancelled({
buyTokenChainId: quote.intent.destinationChainId,
buyTokenAddress: quote.intent.destinationTokenAddress,
sellTokenChainId: quote.intent.originChainId,
sellTokenAddress: quote.intent.originTokenAddress,
pageType: props.pageType,
});
}}
/>
)}
</div>
);
}
function TabButton(props: {
label: string;
onClick: () => void;
isActive: boolean;
}) {
return (
<Button
onClick={props.onClick}
className={cn(
"rounded-full text-muted-foreground px-5 text-base bg-accent",
props.isActive && "text-foreground border-foreground",
)}
variant="outline"
>
{props.label}
</Button>
);
}
@@ -10,7 +10,7 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { cn } from "../../lib/utils";
import { DynamicHeight } from "../ui/DynamicHeight";
@@ -3,7 +3,7 @@ import { DownloadIcon } from "lucide-react";
import Papa from "papaparse";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { cn } from "../../lib/utils";
export function ExportToCSVButton(props: {
+1 -85
View File
@@ -1,87 +1,3 @@
/* eslint-disable @next/next/no-img-element */
"use client";
import { useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { useIsomorphicLayoutEffect } from "../../lib/useIsomorphicLayoutEffect";
type imgElementProps = React.DetailedHTMLProps<
React.ImgHTMLAttributes<HTMLImageElement>,
HTMLImageElement
> & {
skeleton?: React.ReactNode;
fallback?: React.ReactNode;
src: string | undefined;
containerClassName?: string;
};
export function Img(props: imgElementProps) {
const [_status, setStatus] = useState<"pending" | "fallback" | "loaded">(
"pending",
);
const status =
props.src === undefined
? "pending"
: props.src === ""
? "fallback"
: _status;
const { className, fallback, skeleton, containerClassName, ...restProps } =
props;
const defaultSkeleton = <div className="animate-pulse bg-accent" />;
const defaultFallback = <div className="bg-accent" />;
const imgRef = useRef<HTMLImageElement>(null);
useIsomorphicLayoutEffect(() => {
const imgEl = imgRef.current;
if (!imgEl) {
return;
}
if (imgEl.complete) {
setStatus("loaded");
} else {
function handleLoad() {
setStatus("loaded");
}
imgEl.addEventListener("load", handleLoad);
return () => {
imgEl.removeEventListener("load", handleLoad);
};
}
}, []);
return (
<div className={cn("relative shrink-0", containerClassName)}>
<img
{...restProps}
// avoid setting empty src string to prevent request to the entire page
alt={restProps.alt || ""}
className={cn(
"fade-in-0 object-cover transition-opacity duration-300",
className,
)}
decoding="async"
onError={() => {
setStatus("fallback");
}}
ref={imgRef}
src={restProps.src || undefined}
style={{
opacity: status === "loaded" ? 1 : 0,
...restProps.style,
}}
/>
{status !== "loaded" && (
<div
className={cn(
"fade-in-0 absolute inset-0 overflow-hidden transition-opacity duration-300 [&>*]:h-full [&>*]:w-full",
className,
)}
style={restProps.style}
>
{status === "pending" && (skeleton || defaultSkeleton)}
{status === "fallback" && (fallback || defaultFallback)}
</div>
)}
</div>
);
}
export { Img } from "@workspace/ui/components/img";
@@ -1,6 +1,6 @@
import type React from "react";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { cn } from "@/lib/utils";
export function SettingsCard(
@@ -19,7 +19,7 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { Separator } from "@/components/ui/separator";
import {
Table,
@@ -5,16 +5,16 @@ import {
type ThirdwebClient,
} from "thirdweb";
import { shortenAddress } from "thirdweb/utils";
import type { TokenMetadata } from "@/api/universal-bridge/tokens";
import type { TokenMetadata } from "@/api/universal-bridge/types";
import { Img } from "@/components/blocks/Img";
import { SelectWithSearch } from "@/components/blocks/select-with-search";
import { Badge } from "@/components/ui/badge";
import { Spinner } from "@/components/ui/Spinner";
import { fallbackChainIcon } from "@/constants/chain";
import { useAllChainsData } from "@/hooks/chains/allChains";
import { useTokensData } from "@/hooks/tokens";
import { cn } from "@/lib/utils";
import { resolveSchemeWithErrorHandler } from "@/utils/resolveSchemeWithErrorHandler";
import { Spinner } from "../ui/Spinner/Spinner";
type Option = { label: string; value: string };
@@ -1,4 +1,5 @@
"use client";
import type React from "react";
import { type Dispatch, type SetStateAction, useMemo } from "react";
import { CodeClient } from "@/components/ui/code/code.client";
@@ -6,11 +7,14 @@ import { TabButtons } from "@/components/ui/tabs";
import { cn } from "@/lib/utils";
export type CodeEnvironment =
| "api"
| "javascript"
| "typescript"
| "react"
| "react-native"
| "unity";
| "dotnet"
| "unity"
| "curl";
type SupportedEnvironment = {
environment: CodeEnvironment;
@@ -20,6 +24,10 @@ type SupportedEnvironment = {
type CodeSnippet = Partial<Record<CodeEnvironment, string>>;
const Environments: SupportedEnvironment[] = [
{
environment: "api",
title: "API",
},
{
environment: "javascript",
title: "JavaScript",
@@ -36,10 +44,18 @@ const Environments: SupportedEnvironment[] = [
environment: "react-native",
title: "React Native",
},
{
environment: "dotnet",
title: ".NET",
},
{
environment: "unity",
title: "Unity",
},
{
environment: "curl",
title: "cURL",
},
];
interface CodeSegmentProps {
@@ -122,9 +138,14 @@ export const CodeSegment: React.FC<CodeSegmentProps> = ({
: activeEnvironment === "react" ||
activeEnvironment === "react-native"
? "tsx"
: activeEnvironment === "unity"
: activeEnvironment === "unity" ||
activeEnvironment === "dotnet"
? "cpp"
: activeEnvironment
: activeEnvironment === "api"
? "javascript"
: activeEnvironment === "curl"
? "bash"
: activeEnvironment
}
/>
)}
@@ -1,6 +1,6 @@
import { useMemo } from "react";
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
import { ScrollShadow } from "@/components/ui/ScrollShadow";
export function UnexpectedValueErrorMessage(props: {
value: unknown;
@@ -3,7 +3,6 @@
import { ChevronDownIcon, ChevronRightIcon } from "lucide-react";
import { usePathname } from "next/navigation";
import { useMemo } from "react";
import { AppFooter } from "@/components/footers/app-footer";
import {
Collapsible,
CollapsibleContent,
@@ -91,7 +90,6 @@ export function FullWidthSidebarLayout(props: {
<main className="flex min-w-0 grow flex-col max-sm:w-full">
{children}
</main>
<AppFooter containerClassName="max-w-7xl" />
</div>
</div>
);
@@ -0,0 +1,23 @@
import { GridPattern } from "@/components/ui/background-patterns";
export function GridPatternEmbedContainer(props: {
children: React.ReactNode;
}) {
return (
<div className=" sm:flex sm:justify-center w-full sm:border sm:border-dashed sm:bg-accent/20 sm:py-12 rounded-lg overflow-hidden relative">
<GridPattern
width={30}
height={30}
x={-1}
y={-1}
strokeDasharray={"4 2"}
className="text-border dark:text-border/70 hidden lg:block"
style={{
maskImage:
"linear-gradient(to bottom right,white,transparent,transparent)",
}}
/>
<div className="sm:w-[420px] z-10">{props.children}</div>
</div>
);
}
@@ -1,225 +1 @@
import { onlyText } from "react-children-utilities";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { CodeClient } from "@/components/ui/code/code.client";
import { PlainTextCodeBlock } from "@/components/ui/code/plaintext-code";
import { InlineCode } from "@/components/ui/inline-code";
import {
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { UnderlineLink } from "@/components/ui/UnderlineLink";
import { cn } from "@/lib/utils";
export const MarkdownRenderer: React.FC<{
markdownText: string;
className?: string;
code?: {
disableCodeHighlight?: boolean;
ignoreFormattingErrors?: boolean;
className?: string;
};
inlineCode?: {
className?: string;
};
p?: {
className?: string;
};
li?: {
className?: string;
};
skipHtml?: boolean;
}> = (markdownProps) => {
const { markdownText, className, code } = markdownProps;
const commonHeadingClassName = "mb-2 leading-5 font-semibold tracking-tight";
return (
<div className={className}>
<ReactMarkdown
components={{
a: (props) => (
<UnderlineLink
href={props.href ?? "#"}
rel="noopener noreferrer"
target="_blank"
{...cleanedProps(props)}
className="mt-4"
/>
),
code: ({ ...props }) => {
const codeStr = onlyText(props.children);
if (props?.className || codeStr.length > 100) {
if (code?.disableCodeHighlight || !props.className) {
return (
<div className="my-4">
{/* @ts-expect-error - TODO: fix this */}
<PlainTextCodeBlock
{...cleanedProps(props)}
className={markdownProps.code?.className}
code={onlyText(props.children).trim()}
/>
</div>
);
}
const language = props.className.replace("language-", "");
return (
<div className="my-4">
<CodeClient
// @ts-expect-error - TODO: fix this
lang={language}
{...cleanedProps(props)}
className={markdownProps.code?.className}
code={onlyText(props.children).trim()}
ignoreFormattingErrors={code?.ignoreFormattingErrors}
/>
</div>
);
}
return (
<InlineCode
className={markdownProps.inlineCode?.className}
code={onlyText(props.children).trim()}
/>
);
},
h1: (props) => (
<h2
className={cn(
commonHeadingClassName,
"mb-3 border-dashed border-b pb-2 text-xl md:text-2xl",
)}
{...cleanedProps(props)}
/>
),
h2: (props) => (
<h3
{...cleanedProps(props)}
className={cn(
commonHeadingClassName,
"mt-8 mb-3 border-dashed border-b pb-2 text-lg md:text-xl",
)}
/>
),
h3: (props) => (
<h4
{...cleanedProps(props)}
className={cn(
commonHeadingClassName,
"mt-4 text-base md:text-lg",
)}
/>
),
h4: (props) => (
<h5
{...cleanedProps(props)}
className={cn(commonHeadingClassName, "mt-4 text-lg")}
/>
),
h5: (props) => (
<h6
{...cleanedProps(props)}
className={cn(commonHeadingClassName, "mt-4 text-lg")}
/>
),
h6: (props) => (
<p
{...cleanedProps(props)}
className={cn(commonHeadingClassName, "mt-4 text-lg")}
/>
),
hr: (props) => (
<hr {...cleanedProps(props)} className="my-5 bg-border" />
),
li: ({ children: c, ...props }) => (
<li
className={cn(
"text-muted-foreground leading-relaxed [&>p]:m-0",
markdownProps.li?.className,
)}
{...cleanedProps(props)}
>
{c}
</li>
),
ol: (props) => (
<ol
className="mb-4 list-outside list-decimal pl-4 space-y-2 [&>li]:first:mt-2"
{...cleanedProps(props)}
/>
),
p: (props) => (
<p
className={cn(
"mb-3 text-muted-foreground leading-7",
markdownProps.p?.className,
)}
{...cleanedProps(props)}
/>
),
strong(props) {
return <strong className="font-medium" {...cleanedProps(props)} />;
},
table: (props) => (
<div className="mb-6">
<TableContainer>
<Table {...cleanedProps(props)} />
</TableContainer>
</div>
),
tbody: (props) => <TableBody {...cleanedProps(props)} />,
td: (props) => (
<TableCell {...cleanedProps(props)} className="text-left" />
),
th: ({ children: c, ...props }) => (
<TableHead
{...cleanedProps(props)}
className="text-left text-muted-foreground"
>
{c}
</TableHead>
),
thead: (props) => <TableHeader {...cleanedProps(props)} />,
tr: (props) => <TableRow {...cleanedProps(props)} />,
ul: (props) => {
return (
<ul
className="mb-4 list-outside list-disc pl-4 space-y-2 [&>li]:first:mt-2"
{...cleanedProps(props)}
/>
);
},
}}
remarkPlugins={[remarkGfm]}
skipHtml={markdownProps.skipHtml}
>
{markdownText}
</ReactMarkdown>
</div>
);
};
function cleanedProps<T extends object & { node?: unknown }>(
props: T,
): Omit<T, "node"> {
// biome-ignore lint/correctness/noUnusedVariables: EXPECTED
const { node, ...rest } = props;
return rest;
}
export { MarkdownRenderer } from "@workspace/ui/components/markdown-renderer";
@@ -10,7 +10,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
import { ScrollShadow } from "@/components/ui/ScrollShadow";
import { Separator } from "@/components/ui/separator";
import { useShowMore } from "@/hooks/useShowMore";
import { cn } from "@/lib/utils";
@@ -7,8 +7,8 @@ import {
RefreshCwIcon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { DynamicHeight } from "../../ui/DynamicHeight";
import { Spinner } from "../../ui/Spinner/Spinner";
import { DynamicHeight } from "@/components/ui/DynamicHeight";
import { Spinner } from "@/components/ui/Spinner";
export type MultiStepState<T extends string> = {
id: T;
@@ -0,0 +1,119 @@
"use client";
import { Button } from "@workspace/ui/components/button";
import {
BookTextIcon,
BoxIcon,
ChevronDownIcon,
CodeIcon,
WebhookIcon,
} from "lucide-react";
import Link from "next/link";
import { useMemo } from "react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
type LinkType = "api" | "docs" | "playground" | "webhooks";
const linkTypeToLabel: Record<LinkType, string> = {
api: "API Reference",
docs: "Documentation",
playground: "Playground",
webhooks: "Webhooks",
};
const linkTypeToOrder: Record<LinkType, number> = {
docs: 0,
playground: 1,
api: 3,
webhooks: 4,
};
const linkTypeToIcon: Record<LinkType, React.FC<{ className?: string }>> = {
api: CodeIcon,
docs: BookTextIcon,
playground: BoxIcon,
webhooks: WebhookIcon,
};
function orderLinks(links: ActionLink[]) {
return links.slice().sort((a, b) => {
const aIndex = linkTypeToOrder[a.type];
const bIndex = linkTypeToOrder[b.type];
return aIndex - bIndex;
});
}
export type ActionLink = {
type: LinkType;
href: string;
};
export function LinkGroup(props: { links: ActionLink[] }) {
const orderedLinks = useMemo(() => orderLinks(props.links), [props.links]);
if (orderedLinks.length === 1 && orderedLinks[0]) {
const link = orderedLinks[0];
const Icon = linkTypeToIcon[link.type];
return (
<Link
href={link.href}
target={link.href.startsWith("http") ? "_blank" : undefined}
>
<Button
variant="outline"
size="sm"
className="rounded-full border gap-2 bg-card"
>
<Icon className="size-3.5 text-muted-foreground" />
{linkTypeToLabel[link.type]}
</Button>
</Link>
);
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="rounded-full border gap-2 bg-card [&[data-state=open]>svg]:rotate-180"
>
Resources
<ChevronDownIcon className="size-4 transition-transform duration-200 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="gap-1 flex flex-col w-48 rounded-xl"
sideOffset={10}
>
{orderedLinks.map((link) => {
const isExternal = link.href.startsWith("http");
const Icon = linkTypeToIcon[link.type];
return (
<DropdownMenuItem
key={link.type}
asChild
className="flex flex-row items-center gap-2 cursor-pointer py-1.5"
>
<Link
href={link.href}
target={isExternal ? "_blank" : undefined}
rel={isExternal ? "noopener noreferrer" : undefined}
>
<Icon className="size-4 text-muted-foreground" />
{linkTypeToLabel[link.type]}
</Link>
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}
@@ -0,0 +1,14 @@
import {
type FooterCardProps,
FooterLinksSection,
} from "../../../../app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/footer/FooterLinksSection";
export type ProjectPageFooterProps = FooterCardProps;
export function ProjectPageFooter(props: ProjectPageFooterProps) {
return (
<footer className="container max-w-7xl">
<FooterLinksSection {...props} />
</footer>
);
}
@@ -0,0 +1,152 @@
import { Button } from "@workspace/ui/components/button";
import { ArrowUpRightIcon, Settings2Icon } from "lucide-react";
import Link from "next/link";
import type { ThirdwebClient } from "thirdweb";
import { cn } from "@/lib/utils";
import { type ActionLink, LinkGroup } from "./header/link-group";
type Action =
| ({
label: string;
href: string;
} & (
| {
external: true;
icon?: never;
}
| {
icon?: React.ReactNode;
external?: false;
}
))
| {
component: React.ReactNode;
};
function Action(props: { action: Action; variant?: "default" | "outline" }) {
const action = props.action;
return "component" in action ? (
action.component
) : (
<Button
asChild
className={cn("rounded-full")}
size="sm"
variant={props.variant}
>
<Link
href={action.href}
target={action.external ? "_blank" : undefined}
rel={action.external ? "noopener noreferrer" : undefined}
className="flex flex-row items-center gap-2"
>
{action.icon}
{action.label}
{action.external && <ArrowUpRightIcon className="size-4" />}
</Link>
</Button>
);
}
export type ProjectPageHeaderProps = {
client: ThirdwebClient;
title: string;
description?: React.ReactNode;
imageUrl?: string | null;
icon: React.FC<{ className?: string }>;
isProjectIcon?: boolean;
actions: {
primary?: Action;
secondary?: Action;
} | null;
links?: ActionLink[];
settings?: {
href: string;
};
// TODO: add task card component
task?: never;
};
export function ProjectPageHeader(props: ProjectPageHeaderProps) {
return (
<header className="container max-w-7xl py-6 relative">
{/* top row */}
<div className="flex justify-between items-start mb-4">
{/* left - icon */}
<div className="flex">
{props.isProjectIcon ? (
<props.icon />
) : (
<div className="border rounded-full p-2.5 bg-card">
<props.icon className="size-5 text-muted-foreground" />
</div>
)}
</div>
{/* right - buttons */}
<div className="flex items-center gap-3">
<div className="flex items-center gap-3">
{props.links && props.links.length > 0 && (
<LinkGroup links={props.links} />
)}
{props.settings && (
<Link href={props.settings.href}>
<Button
variant="outline"
size="sm"
className="rounded-full gap-2 bg-card"
>
<Settings2Icon className="size-4 text-muted-foreground" />
Settings
</Button>
</Link>
)}
</div>
{/* hide on mobile */}
{props.actions && (
<div className="hidden lg:flex items-center gap-3">
{props.actions.secondary && (
<Action action={props.actions.secondary} variant="outline" />
)}
{props.actions.primary && (
<Action action={props.actions.primary} />
)}
</div>
)}
</div>
</div>
<div className="space-y-4">
{/* mid row */}
<div className="space-y-1 max-w-3xl">
<h2 className="text-3xl font-semibold tracking-tight">
{props.title}
</h2>
{/* description */}
<p className="text-sm text-muted-foreground text-pretty">
{props.description}
</p>
</div>
{/* bottom row - hidden on desktop */}
{props.actions && (
<div className="flex items-center gap-3 lg:hidden">
{props.actions?.primary && (
<Action action={props.actions.primary} />
)}
{props.actions?.secondary && (
<Action action={props.actions.secondary} variant="outline" />
)}
</div>
)}
</div>
</header>
);
}
@@ -0,0 +1,44 @@
import { cn } from "@/lib/utils";
import { type TabPathLink, TabPathLinks } from "../../ui/tabs";
import {
ProjectPageFooter,
type ProjectPageFooterProps,
} from "./project-page-footer";
import {
ProjectPageHeader,
type ProjectPageHeaderProps,
} from "./project-page-header";
type ProjectPageProps = {
header: ProjectPageHeaderProps;
footer?: ProjectPageFooterProps;
/**
* @deprecated only for legacy pages where we still need internal tabs for the moment, currently:
* - /webhooks
*/
tabs?: TabPathLink[];
};
export function ProjectPage(props: React.PropsWithChildren<ProjectPageProps>) {
return (
<div className="flex flex-col grow">
<div className={cn(!props.tabs && "border-b")}>
<ProjectPageHeader {...props.header} />
{props.tabs && (
<TabPathLinks
scrollableClassName="container max-w-7xl"
links={props.tabs}
/>
)}
</div>
<main className="container max-w-7xl pt-6">{props.children}</main>
<div className="h-20" />
{props.footer && (
<div className="border-t mt-auto">
<ProjectPageFooter {...props.footer} />
</div>
)}
</div>
);
}
@@ -10,7 +10,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
import { ScrollShadow } from "@/components/ui/ScrollShadow";
import { useShowMore } from "@/hooks/useShowMore";
import { cn } from "@/lib/utils";
@@ -1,6 +1,6 @@
"use client";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { cn } from "@/lib/utils";
export function GenericLoadingPage({ className }: { className?: string }) {
@@ -48,7 +48,7 @@ export function WalletAddress(props: {
return (
<ToolTipLabel hoverable label={address}>
<span className="flex items-center gap-2 underline-offset-4 hover:underline">
<div className="flex size-6 items-center justify-center rounded-full border bg-background">
<div className="flex size-5 items-center justify-center rounded-full border bg-background">
<XIcon className="size-4 text-muted-foreground" />
</div>
Invalid Address
@@ -62,7 +62,7 @@ export function WalletAddress(props: {
return (
<div className="flex items-center gap-2 py-2">
<CircleSlashIcon
className={cn("size-6 text-muted-foreground/70", props.iconClassName)}
className={cn("size-5 text-muted-foreground/70", props.iconClassName)}
/>
<span
className={cn("cursor-pointer font-mono text-sm", props.className)}
@@ -208,6 +208,13 @@ function WalletAvatar(props: {
<Img
className={cn("size-5 object-cover", props.iconClassName)}
src={resolvedAvatarSrc}
fallback={
<Blobbie
address={props.address}
className={props.iconClassName}
size={20}
/>
}
/>
) : props.fallbackIcon ? (
props.fallbackIcon
@@ -215,7 +222,7 @@ function WalletAvatar(props: {
<Blobbie
address={props.address}
className={props.iconClassName}
size={24}
size={20}
/>
)}
</div>
@@ -87,7 +87,7 @@ export function ChatBar(props: {
<PopoverContent className="w-72">
<div>
<p className="mb-3 text-muted-foreground text-sm">
Get access to image uploads by signing in to Nebula
Get access to image uploads by signing in to thirdweb
</p>
<Button
className="w-full"
@@ -28,7 +28,10 @@ export function CustomChatButton(props: {
const closeModal = useCallback(() => setIsOpen(false), []);
const ref = useRef<HTMLDivElement>(null);
if (layoutSegments[0] === "~" && layoutSegments[1] === "support") {
if (
(layoutSegments[0] === "~" && layoutSegments[1] === "support") ||
layoutSegments.includes("ai")
) {
return null;
}
@@ -15,7 +15,7 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
import { ScrollShadow } from "@/components/ui/ScrollShadow";
import { cn } from "@/lib/utils";
import { ThirdwebMiniLogo } from "../../../app/(app)/components/ThirdwebMiniLogo";
import { SupportTicketForm } from "../../../app/(app)/team/[team_slug]/(team)/~/support/_components/SupportTicketForm";
@@ -413,15 +413,15 @@ function StyledMarkdownRenderer(props: {
}) {
return (
<MarkdownRenderer
className="[&>*:first-child]:mt-0 [&>*:first-child]:border-none [&>*:first-child]:pb-0 [&>*:last-child]:mb-0"
className="text-sm text-foreground [&>*:first-child]:mt-0 [&>*:first-child]:border-none [&>*:first-child]:pb-0 [&>*:last-child]:mb-0 leading-relaxed"
code={{
className: "bg-card",
ignoreFormattingErrors: true,
}}
inlineCode={{ className: "border-none" }}
li={{ className: "text-foreground leading-relaxed" }}
markdownText={props.text}
p={{
className: props.type === "assistant" ? "" : "leading-normal",
className: "text-foreground leading-relaxed",
}}
skipHtml
/>
@@ -22,8 +22,8 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { ScrollShadow } from "@/components/ui/ScrollShadow";
import { Spinner } from "@/components/ui/Spinner";
import { ChainIconClient } from "@/icons/ChainIcon";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { fetchTopContracts, type TrendingContract } from "@/lib/search";
@@ -17,7 +17,7 @@ import {
DropdownMenuContent,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { SkeletonContainer } from "@/components/ui/skeleton";
import {
Table,
@@ -65,6 +65,23 @@ export default function Component() {
events: [preparedEvent]
});
}`,
unity: `using Thirdweb;
// Get your contract
var contract = await ThirdwebManager.Instance.GetContract(
address: "{{contract_address}}",
chainId: {{chainId}},
);
// Listen to contract events
var events = await contract.Events("{{function}}");`,
dotnet: `using Thirdweb;
// Get your contract
var contract = await ThirdwebContract.Create(client, "{{contract_address}}", {{chainId}});
// Listen to contract events
var events = await contract.Events("{{function}}");`,
},
install: {
javascript: "npm i thirdweb",
@@ -73,6 +90,9 @@ export default function Component() {
unity: `// Download the .unitypackage from the latest release:
// https://github.com/thirdweb-dev/unity-sdk/releases
// and drag it into your project`,
dotnet: `// Install the thirdweb SDK via NuGet:
// dotnet add package Thirdweb
// Or search for "Thirdweb" in your NuGet package manager`,
},
read: {
javascript: `import { readContract } from "thirdweb";
@@ -100,6 +120,33 @@ export default function Component() {
params: [{{args}}]
});
}`,
unity: `using Thirdweb;
// Get your contract
var contract = await ThirdwebManager.Instance.GetContract(
address: "{{contract_address}}",
chainId: {{chainId}}
);
// Read from the contract
var result = await contract.Read<T>(
"{{function}}",
{{args}}
);`,
dotnet: `using Thirdweb;
// Get your contract
var contract = await ThirdwebContract.Create(
client,
"{{contract_address}}",
{{chainId}}
);
// Read from the contract
var result = await contract.Read<T>(
"{{function}}",
{{args}}
);`,
},
setup: {
javascript: `import { createThirdwebClient, getContract } from "thirdweb";
@@ -165,11 +212,19 @@ function App() {
`,
unity: `using Thirdweb;
// Reference the SDK
var sdk = ThirdwebManager.Instance.SDK;
// Get your contract
var contract = await ThirdwebManager.Instance.GetContract(
address: "{{contract_address}}",
chainId: {{chainId}}
);`,
dotnet: `using Thirdweb;
// Get your contract
var contract = sdk.GetContract("{{contract_address}}");`,
var contract = await ThirdwebContract.Create(
client,
"{{contract_address}}",
{{chainId}}
);`,
},
write: {
javascript: `import { prepareContractCall, sendTransaction } from "thirdweb";
@@ -213,6 +268,123 @@ export default function Component() {
sendTransaction(transaction);
}
}`,
unity: `using Thirdweb;
// Get your contract
var contract = await ThirdwebManager.Instance.GetContract(
address: "{{contract_address}}",
chainId: {{chainId}}
);
// Write to the contract
var transactionReceipt = await contract.Write(
wallet,
contract,
"{{function}}",
weiValue,
{{args}}
);`,
dotnet: `using Thirdweb;
// Get your contract
var contract = await ThirdwebContract.Create(
client,
"{{contract_address}}",
{{chainId}}
);
// Write to the contract
var transactionReceipt = await contract.Write(
wallet,
contract,
"{{function}}",
weiValue,
{{args}}
);`,
},
api: {
read: `const response = await fetch('https://api.thirdweb.com/v1/contracts/read', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-secret-key': '<YOUR_SECRET_KEY>'
},
body: JSON.stringify({
calls: [
{
contractAddress: "{{contract_address}}",
method: "{{function}}",
params: [{{args}}]
}
],
chainId: {{chainId}}
})
});
const data = await response.json();`,
write: `const response = await fetch('https://api.thirdweb.com/v1/contracts/write', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-secret-key': '<YOUR_SECRET_KEY>'
},
body: JSON.stringify({
calls: [
{
contractAddress: "{{contract_address}}",
method: "{{function}}",
params: [{{args}}]
}
],
chainId: {{chainId}},
from: "<YOUR_WALLET_ADDRESS>"
})
});
const data = await response.json();`,
events: `const response = await fetch('https://api.thirdweb.com/v1/contracts/{{chainId}}/{{contract_address}}/events?eventSignature={{function}}', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'x-secret-key': '<YOUR_SECRET_KEY>'
}
});
const data = await response.json();`,
},
curl: {
read: `curl https://api.thirdweb.com/v1/contracts/read \\
--request POST \\
--header 'Content-Type: application/json' \\
--header 'x-secret-key: <YOUR_SECRET_KEY>' \\
--data '{
"calls": [
{
"contractAddress": "{{contract_address}}",
"method": "{{function}}",
"params": [{{args}}]
}
],
"chainId": {{chainId}}
}'`,
write: `curl -X POST https://api.thirdweb.com/v1/contracts/write \\
-H "Content-Type: application/json" \\
-H "x-secret-key: <YOUR_SECRET_KEY>" \\
-d '{
"calls": [
{
"contractAddress": "{{contract_address}}",
"method": "{{function}}",
"params": [{{args}}]
}
],
"chainId": {{chainId}},
"from": "<YOUR_WALLET_ADDRESS>"
}'`,
events: `curl https://api.thirdweb.com/v1/contracts/{{chainId}}/{{contract_address}}/events?eventSignature={{function}} \\
--request GET \\
--header 'Content-Type: application/json' \\
--header 'x-secret-key: <YOUR_SECRET_KEY>'`,
},
};
@@ -269,20 +441,17 @@ return (
public async void ConnectWallet()
{
// Reference to your Thirdweb SDK
var sdk = ThirdwebManager.Instance.SDK;
// Configure the connection
var connection = new WalletConnection(
provider: WalletProvider.SmartWallet, // The wallet provider you want to connect to (Required)
chainId: 1, // The chain you want to connect to (Required)
password: "myEpicPassword", // If using a local wallet as personal wallet (Optional)
email: "[email protected]", // If using an email wallet as personal wallet (Optional)
personalWallet: WalletProvider.LocalWallet // The personal wallet you want to use with your Account (Optional)
var wallet = await ConnectWallet(
new WalletOptions(
provider: WalletProvider.InAppWallet,
chainId: {{chainId}},
inAppWalletOptions: new InAppWalletOptions(
authprovider: AuthProvider.Google,
executionMode: ExecutionMode.EIP7702Sponsored
)
)
);
// Connect the wallet
string address = await sdk.wallet.Connect(connection);
string address = await wallet.GetAddress();
}`,
},
},
@@ -490,6 +659,9 @@ export function formatSnippet(
: "event",
});
break;
case "api":
// For API, we don't need special extension handling, just use the standard templates
break;
}
}
// end hacks on hacks on hacks -- now just hacks on hacks from here on out
@@ -533,7 +705,7 @@ export function CodeOverview(props: {
| undefined;
const [environment, setEnvironment] = useState<CodeEnvironment>(
defaultEnvironment || "javascript",
defaultEnvironment || "api",
);
const [tab, setTab] = useState("write");
@@ -677,7 +849,13 @@ export function CodeOverview(props: {
</div>
)}
<div className="flex flex-col gap-2">
{environment === "react-native" || environment === "unity" ? (
{environment === "api" || environment === "curl" ? (
<p className="text-sm text-muted-foreground">
No SDK install required for API/cURL. Call the HTTP endpoints
directly. Keep your secret key server-side; use client IDs in
frontends.
</p>
) : environment === "react-native" || environment === "unity" ? (
<p className="text-sm text-muted-foreground">
Install the latest version of the SDK.{" "}
<UnderlineLink
@@ -800,32 +978,76 @@ export function CodeOverview(props: {
<CodeSegment
environment={environment}
setEnvironment={setEnvironment}
snippet={formatSnippet(
// biome-ignore lint/suspicious/noExplicitAny: FIXME
COMMANDS[tab as keyof typeof COMMANDS] as any,
{
snippet={(() => {
const selectedFn =
tab === "read" ? read : tab === "write" ? write : event;
const commandsKey = tab as "read" | "write" | "events";
const baseSnippet = formatSnippet(COMMANDS[commandsKey], {
args:
abi
?.filter(
(f) => f.type === "function" || f.type === "event",
)
?.find(
(f) =>
f.name ===
(tab === "read"
? read?.name
: tab === "write"
? write?.name
: event?.name),
)
?.find((f) => f.name === selectedFn?.name)
?.inputs.map((i) => i.name || "") || [],
chainId,
contractAddress,
extensionNamespace,
fn: tab === "read" ? read : tab === "write" ? write : event,
},
)}
fn: selectedFn,
});
// Add API snippet if it exists
const apiSnippet = COMMANDS.api[commandsKey]
? formatSnippet(
{ api: COMMANDS.api[commandsKey] },
{
args:
abi
?.filter(
(f) =>
f.type === "function" || f.type === "event",
)
?.find((f) => f.name === selectedFn?.name)
?.inputs.map((i) => i.name || "") || [],
chainId,
contractAddress,
extensionNamespace,
fn: selectedFn,
},
)
: {};
// Add cURL snippet if it exists
const curlSnippet = COMMANDS.curl[commandsKey]
? formatSnippet(
{ curl: COMMANDS.curl[commandsKey] },
{
args:
abi
?.filter(
(f) =>
f.type === "function" || f.type === "event",
)
?.find((f) => f.name === selectedFn?.name)
?.inputs.map((i) => i.name || "") || [],
chainId,
contractAddress,
extensionNamespace,
fn: selectedFn,
},
)
: {};
return {
...baseSnippet,
...apiSnippet,
...curlSnippet,
};
})()}
/>
</div>
</div>
@@ -68,7 +68,7 @@ const ContractFunction: React.FC<{
function ContractFunctionInner(props: ContractFunctionProps) {
const { contract, fn } = props;
const [environment, setEnvironment] = useState<CodeEnvironment>("javascript");
const [environment, setEnvironment] = useState<CodeEnvironment>("api");
const functionSelectorQuery = useContractFunctionSelectors(contract);
const functionSelectors = functionSelectorQuery.data || [];
const isERC721Query = useReadContract(ERC721Ext.isERC721, { contract });
@@ -112,7 +112,7 @@ function ContractFunctionInner(props: ContractFunctionProps) {
: "write"
: ("events" as const);
const codeSnippet = formatSnippet(COMMANDS[commandsKey], {
const baseSnippet = formatSnippet(COMMANDS[commandsKey], {
args: fn.inputs?.map((i) => i.name || ""),
chainId: contract.chain.id,
contractAddress: contract.address,
@@ -120,6 +120,40 @@ function ContractFunctionInner(props: ContractFunctionProps) {
fn,
});
// Safety check: Only include API snippet if the command exists in COMMANDS.api
const apiSnippet = COMMANDS.api[commandsKey]
? formatSnippet(
{ api: COMMANDS.api[commandsKey] },
{
args: fn.inputs?.map((i) => i.name || ""),
chainId: contract.chain.id,
contractAddress: contract.address,
extensionNamespace,
fn,
},
)
: {};
// Safety check: Only include cURL snippet if the command exists in COMMANDS.curl
const curlSnippet = COMMANDS.curl[commandsKey]
? formatSnippet(
{ curl: COMMANDS.curl[commandsKey] },
{
args: fn.inputs?.map((i) => i.name || ""),
chainId: contract.chain.id,
contractAddress: contract.address,
extensionNamespace,
fn,
},
)
: {};
const codeSnippet = {
...baseSnippet,
...apiSnippet,
...curlSnippet,
};
return (
<div className="flex flex-col gap-1.5">
<div className="flex items-center flex-wrap gap-2 border-b pb-3 mb-3">
@@ -20,7 +20,7 @@ import {
toSerializableTransaction,
toWei,
} from "thirdweb";
import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react";
import { useActiveAccount } from "thirdweb/react";
import { parseAbiParams, stringify, toFunctionSelector } from "thirdweb/utils";
import { FormFieldSetup } from "@/components/blocks/FormFieldSetup";
import { SolidityInput } from "@/components/solidity-inputs";
@@ -31,9 +31,10 @@ import { Button } from "@/components/ui/button";
import { CodeClient } from "@/components/ui/code/code.client";
import { PlainTextCodeBlock } from "@/components/ui/code/plaintext-code";
import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { Skeleton } from "@/components/ui/skeleton";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { useSendAndConfirmTx } from "@/hooks/useSendTx";
function formatResponseData(data: unknown): {
type: "json" | "text";
@@ -264,7 +265,7 @@ export const InteractiveAbiFunction: React.FC<InteractiveAbiFunctionProps> = (
data: mutationData,
error: mutationError,
isPending: mutationLoading,
} = useSendAndConfirmTransaction();
} = useSendAndConfirmTx();
const {
mutate: readFn,
@@ -27,7 +27,7 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { useChainSlug } from "@/hooks/chains/chainSlug";
import { useAddContractToProject } from "@/hooks/project-contracts";
import { useDashboardRouter } from "@/lib/DashboardRouter";
@@ -231,13 +231,13 @@ function ImportForm(props: {
</Button>
) : (
<Button className="gap-2" type="submit">
{addContractToProject.isPending ? (
{form.formState.isSubmitting ? (
<Spinner className="size-4" />
) : (
<ArrowDownToLineIcon className="size-4" />
)}
{addContractToProject.isPending ? "Importing" : "Import"}
{form.formState.isSubmitting ? "Importing" : "Import"}
</Button>
)}
</div>
@@ -4,7 +4,7 @@ import { SearchIcon, XIcon } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import {
Select,
SelectContent,
@@ -10,7 +10,7 @@ import type { WalletUser } from "thirdweb/wallets";
import { TWTable } from "@/components/blocks/TWTable";
import { WalletAddress } from "@/components/blocks/wallet-address";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import {
ToolTipLabel,
Tooltip,
@@ -1,7 +1,79 @@
import type {
ListUserWalletsData,
ListUserWalletsResponses,
} from "@thirdweb-dev/api";
import { configure, listUserWallets } from "@thirdweb-dev/api";
import type { WalletUser } from "thirdweb/wallets";
import { THIRDWEB_EWS_API_HOST } from "@/constants/urls";
import { THIRDWEB_API_HOST } from "@/constants/urls";
import type { SearchType } from "./types";
// Configure the API client to use the correct base URL
configure({
override: {
baseUrl: THIRDWEB_API_HOST,
},
});
// Extract types from the generated API
type APIWallet = ListUserWalletsResponses[200]["result"]["wallets"][0];
type APIProfile = APIWallet["profiles"][0];
// Transform API response to match existing WalletUser format
function transformToWalletUser(apiWallet: APIWallet): WalletUser {
return {
id: getProfileId(apiWallet.profiles[0]) || "",
linkedAccounts: apiWallet.profiles.map((profile) => {
// Create details object based on the profile data
let details:
| { email: string; [key: string]: string }
| { phone: string; [key: string]: string }
| { address: string; [key: string]: string }
| { id: string; [key: string]: string };
const profileId = getProfileId(profile);
if ("email" in profile && profile.email) {
details = { email: profile.email, id: profileId };
} else if ("phone" in profile && profile.phone) {
details = { phone: profile.phone, id: profileId };
} else if ("walletAddress" in profile && profile.walletAddress) {
details = { address: profile.walletAddress, id: profileId };
} else {
details = { id: profileId };
}
return {
type: profile.type,
details,
};
}),
wallets: apiWallet.address
? [
{
address: apiWallet.address,
createdAt: apiWallet.createdAt || new Date().toISOString(),
type: "enclave" as const,
},
]
: [],
};
}
// Helper function to safely get ID from any profile type
function getProfileId(profile: APIProfile | undefined): string {
if (!profile) return "";
if ("id" in profile) {
return profile.id;
} else if ("credentialId" in profile) {
return profile.credentialId;
} else if ("identifier" in profile) {
return profile.identifier;
}
return "";
}
export async function searchUsers(
authToken: string,
clientId: string | undefined,
@@ -10,50 +82,57 @@ export async function searchUsers(
searchType: SearchType,
query: string,
): Promise<WalletUser[]> {
const url = new URL(`${THIRDWEB_EWS_API_HOST}/api/2024-05-05/account/list`);
try {
// Prepare query parameters
const queryParams: ListUserWalletsData["query"] = {
limit: 50,
};
// Add clientId or ecosystemSlug parameter
if (ecosystemSlug) {
url.searchParams.append("ecosystemSlug", `ecosystem.${ecosystemSlug}`);
} else if (clientId) {
url.searchParams.append("clientId", clientId);
// Add search parameter based on search type
switch (searchType) {
case "email":
queryParams.email = query;
break;
case "phone":
queryParams.phone = query;
break;
case "id":
queryParams.id = query;
break;
case "address":
queryParams.address = query;
break;
case "externalWallet":
queryParams.externalWalletAddress = query;
break;
}
// Use the generated API function with Bearer authentication
const response = await listUserWallets({
query: queryParams,
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
"x-thirdweb-team-id": teamId,
...(clientId && { "x-client-id": clientId }),
...(ecosystemSlug && {
"x-ecosystem-id": `ecosystem.${ecosystemSlug}`,
}),
},
});
// Handle response
if (response.error || !response.data) {
console.error(
"Error searching users:",
response.error || "No data returned",
);
return [];
}
return response.data.result.wallets.map(transformToWalletUser);
} catch (error) {
console.error("Error searching users:", error);
return [];
}
// Add search parameter based on search type
switch (searchType) {
case "email":
url.searchParams.append("email", query);
break;
case "phone":
url.searchParams.append("phone", query);
break;
case "id":
url.searchParams.append("id", query);
break;
case "address":
url.searchParams.append("address", query);
break;
case "externalWallet":
url.searchParams.append("externalWalletAddress", query);
break;
}
const response = await fetch(url.toString(), {
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
"x-thirdweb-team-id": teamId,
...(clientId && { "x-client-id": clientId }),
},
method: "GET",
});
if (!response.ok) {
throw new Error(
`Failed to search users: ${response.status} ${response.statusText}`,
);
}
const data = await response.json();
return data.users as WalletUser[];
}
@@ -2,8 +2,7 @@ import { SettingsIcon } from "lucide-react";
import type { ThirdwebClient } from "thirdweb";
import type { UseNetworkSwitcherModalOptions } from "thirdweb/react";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { OPSponsoredChains } from "@/constants/chains";
import { Spinner } from "@/components/ui/Spinner";
import { useAllChainsData } from "@/hooks/chains/allChains";
import { ChainIconClient } from "@/icons/ChainIcon";
import { cn } from "@/lib/utils";
@@ -32,7 +31,6 @@ export const CustomChainRenderer = ({
const { idToChain } = useAllChainsData();
const storedChain = idToChain.get(chain.id);
const isDeprecated = storedChain?.status === "deprecated";
const isSponsored = OPSponsoredChains.includes(chain.id);
return (
<div className="flex min-h-[48px] w-full cursor-pointer justify-start rounded-lg px-2 py-1 hover:bg-accent">
@@ -70,16 +68,6 @@ export const CustomChainRenderer = ({
Deprecated
</div>
)}
{isSponsored && (
<div
className="flex shrink-0 cursor-not-allowed items-center overflow-hidden rounded-xl px-2 py-1 font-medium text-xs"
style={{
background: "linear-gradient(to right, #701953, #5454B2)",
}}
>
Sponsored
</div>
)}
</div>
{switching && (
<div className="flex items-center gap-1 font-medium text-link-foreground text-xs">
@@ -7,7 +7,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
import { ScrollShadow } from "@/components/ui/ScrollShadow";
import { useAllChainsData } from "@/hooks/chains/allChains";
import { ChainIconClient } from "@/icons/ChainIcon";
import { useStore } from "@/lib/reactive";
@@ -5,7 +5,7 @@ import { toast } from "sonner";
import type { ThirdwebClient } from "thirdweb";
import { FileInput } from "@/components/blocks/FileInput";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { useDashboardStorageUpload } from "@/hooks/useDashboardStorageUpload";
export const IconUpload: React.FC<{
@@ -56,7 +56,8 @@ export function NotificationsButton(props: { accountId: string }) {
<PopoverTrigger asChild>{trigger}</PopoverTrigger>
<PopoverContent
align="end"
className="max-h-[90vh] min-h-[500px] w-[400px] max-w-md p-0"
sideOffset={8}
className="max-h-[90vh] min-h-[550px] w-[450px] max-w-md p-0 flex flex-col rounded-xl overflow-hidden"
>
<NotificationList {...manager} />
</PopoverContent>
@@ -7,7 +7,7 @@ import {
parseISO,
subDays,
} from "date-fns";
import { ArchiveIcon } from "lucide-react";
import { ArchiveIcon, ArrowUpRightIcon, BellIcon } from "lucide-react";
import { useMemo } from "react";
import type { Notification } from "@/api/notifications";
import { Button } from "@/components/ui/button";
@@ -42,39 +42,48 @@ export function NotificationEntry({
}, [notification.createdAt]);
return (
<div className="flex flex-row py-1.5">
{onMarkAsRead && (
<div className="min-h-full w-1 shrink-0 rounded-r-lg bg-primary" />
)}
<div className="flex w-full flex-row justify-between gap-2 border-b px-4 py-2 transition-colors last:border-b-0">
<div className="flex items-start gap-3 p-4 border-b group hover:bg-card last:border-b-0">
<div className="p-2.5 rounded-full border bg-card">
<BellIcon className="size-4 text-muted-foreground" />
</div>
<div className="flex w-full flex-row justify-between gap-2 transition-colors">
<div className="flex items-start gap-3">
<div className="flex-1 space-y-1">
<div className="flex-1 space-y-1.5">
<p className="text-sm">{notification.description}</p>
{timeAgo && (
<p className="text-muted-foreground text-xs">{timeAgo}</p>
)}
<div className="flex flex-row justify-between gap-2 pt-1">
<Button asChild className="px-0" size="sm" variant="link">
<div className="flex flex-row justify-between gap-2 pt-2">
<Button
asChild
size="sm"
variant="outline"
className="rounded-full bg-card gap-2"
>
<a
href={notification.ctaUrl}
rel="noopener noreferrer"
target="_blank"
>
{notification.ctaText}
<ArrowUpRightIcon className="size-4 text-muted-foreground" />
</a>
</Button>
</div>
</div>
</div>
{onMarkAsRead && (
<Button
className="text-muted-foreground hover:text-foreground"
onClick={() => onMarkAsRead(notification.id)}
size="icon"
variant="ghost"
>
<ArchiveIcon className="h-4 w-4" />
</Button>
<div className="flex items-center">
<Button
className="p-2 h-auto opacity-0 group-hover:opacity-100 rounded-full text-muted-foreground hover:text-foreground"
onClick={() => onMarkAsRead(notification.id)}
size="sm"
variant="ghost"
aria-label="Archive notification"
>
<ArchiveIcon className="size-4" />
</Button>
</div>
)}
</div>
</div>
@@ -21,15 +21,15 @@ export function NotificationList(props: ReturnType<typeof useNotifications>) {
const scrollContainerRef = useRef<HTMLDivElement>(null);
return (
<div className="flex w-full flex-1 flex-col">
<div className="flex flex-col overflow-hidden grow">
<TabButtons
tabClassName="!text-sm hover:!bg-transparent !px-0 !py-1"
tabContainerClassName="space-x-6 px-4 pt-2"
tabClassName="hover:bg-transparent"
tabContainerClassName="pt-1.5 px-4"
tabs={[
{
isActive: activeTab === "inbox",
name: (
<div className="flex items-center gap-1">
<div className="flex items-center gap-2">
Inbox
{props.unreadNotificationsCount > 0 && (
<Badge variant="secondary">
@@ -49,7 +49,7 @@ export function NotificationList(props: ReturnType<typeof useNotifications>) {
/>
<div
className="h-full w-full flex-1 overflow-y-auto"
className="flex-1 overflow-y-auto overscroll-contain flex flex-col grow"
ref={scrollContainerRef}
>
{activeTab === "inbox" ? (
@@ -93,9 +93,9 @@ function InboxTab(
},
) {
return props.unreadNotifications.length === 0 ? (
<div className="flex h-full flex-col items-center justify-center gap-4 p-8 text-center">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-muted">
<BellIcon className="h-4 w-4 text-muted-foreground" />
<div className="flex grow flex-col items-center justify-center gap-4 p-4 text-center">
<div className="p-2 bg-card rounded-full border">
<BellIcon className="size-5 text-muted-foreground" />
</div>
<p className="text-muted-foreground text-sm">No new notifications</p>
</div>
@@ -131,9 +131,9 @@ function ArchiveTab(
},
) {
return props.archivedNotifications.length === 0 ? (
<div className="flex flex-col items-center justify-center gap-4 p-8 text-center">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-muted">
<ArchiveIcon className="h-4 w-4 text-muted-foreground" />
<div className="flex grow flex-col items-center justify-center gap-4 p-4 text-center">
<div className="p-2 bg-card rounded-full border">
<ArchiveIcon className="size-5 text-muted-foreground" />
</div>
<p className="text-muted-foreground text-sm">No archived notifications</p>
</div>
@@ -32,7 +32,7 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { Textarea } from "@/components/ui/textarea";
import { createProjectClient } from "@/hooks/useApi";
import { useDashboardRouter } from "@/lib/DashboardRouter";
@@ -5,7 +5,7 @@ import { useEffect, useMemo, useState } from "react";
import { useActiveAccount } from "thirdweb/react";
import { isAddress, isValidENSName } from "thirdweb/utils";
import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { useEns } from "@/hooks/contract-hooks";
import type { SolidityInputProps } from ".";
import { validateAddress } from "./helpers";
@@ -12,6 +12,7 @@ import { forwardRef, useCallback, useRef, useState } from "react";
import { toast } from "sonner";
import {
prepareTransaction,
// eslint-disable-next-line no-restricted-imports
sendTransaction,
type ThirdwebClient,
toWei,
@@ -41,7 +42,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { LOCAL_NODE_PKEY } from "@/constants/misc";
import { useAllChainsData } from "@/hooks/chains/allChains";
import { useV5DashboardChain } from "@/hooks/chains/v5-adapter";
@@ -0,0 +1,3 @@
"use client";
export { ScrollShadow } from "@workspace/ui/components/scroll-shadow";
@@ -1,36 +0,0 @@
.scrollShadowY {
position: absolute;
left: 0;
width: 100%;
height: 32px;
pointer-events: none;
}
.scrollShadowX {
position: absolute;
top: 0;
width: 32px;
height: 100%;
pointer-events: none;
}
.scrollShadowTop {
top: 0;
background: linear-gradient(to bottom, var(--shadow), transparent);
opacity: 0;
}
.scrollShadowBottom {
bottom: 0;
background: linear-gradient(to top, var(--shadow), transparent);
}
.scrollShadowLeft {
left: 0;
background: linear-gradient(to right, var(--shadow), transparent);
}
.scrollShadowRight {
right: 0;
background: linear-gradient(to left, var(--shadow), transparent);
}
@@ -1,151 +0,0 @@
"use client";
import { useRef } from "react";
import { useIsomorphicLayoutEffect } from "@/lib/useIsomorphicLayoutEffect";
import { cn } from "@/lib/utils";
import styles from "./ScrollShadow.module.css";
export function ScrollShadow(props: {
children: React.ReactNode;
className?: string;
scrollableClassName?: string;
disableTopShadow?: boolean;
shadowColor?: string;
shadowClassName?: string;
}) {
const scrollableEl = useRef<HTMLDivElement>(null);
const shadowTopEl = useRef<HTMLDivElement>(null);
const shadowBottomEl = useRef<HTMLDivElement>(null);
const shadowLeftEl = useRef<HTMLDivElement>(null);
const shadowRightEl = useRef<HTMLDivElement>(null);
const wrapperEl = useRef<HTMLDivElement>(null);
useIsomorphicLayoutEffect(() => {
const content = scrollableEl.current;
const shadowTop = shadowTopEl.current;
const shadowBottom = shadowBottomEl.current;
const wrapper = wrapperEl.current;
const shadowLeft = shadowLeftEl.current;
const shadowRight = shadowRightEl.current;
if (!content || !shadowTop || !shadowBottom || !wrapper) {
return;
}
function handleScroll() {
if (
!content ||
!shadowTop ||
!shadowBottom ||
!shadowLeft ||
!shadowRight ||
!wrapper
) {
return;
}
const contentScrollHeight = content.scrollHeight - wrapper.offsetHeight;
const contentScrollWidth = content.scrollWidth - wrapper.offsetWidth;
if (contentScrollHeight > 10) {
const currentScroll = content.scrollTop / contentScrollHeight;
shadowTop.style.opacity = `${currentScroll}`;
shadowBottom.style.opacity = `${1 - currentScroll}`;
} else {
shadowTop.style.opacity = "0";
shadowBottom.style.opacity = "0";
}
if (contentScrollWidth > 10) {
const currentScrollX = content.scrollLeft / contentScrollWidth;
shadowLeft.style.opacity = `${currentScrollX}`;
shadowRight.style.opacity = `${1 - currentScrollX}`;
} else {
shadowLeft.style.opacity = "0";
shadowRight.style.opacity = "0";
}
}
requestAnimationFrame(() => {
requestAnimationFrame(() => {
handleScroll();
});
});
content.addEventListener("scroll", handleScroll);
const resizeObserver = new ResizeObserver(() => {
handleScroll();
});
resizeObserver.observe(content);
return () => {
content.removeEventListener("scroll", handleScroll);
resizeObserver.disconnect();
};
}, []);
return (
<div
className={cn(props.className, "relative overflow-hidden")}
ref={wrapperEl}
style={
{
"--shadow": props.shadowColor || "hsl(var(--muted))",
} as React.CSSProperties
}
>
<div
className={cn(
styles.scrollShadowTop,
styles.scrollShadowY,
props.shadowClassName,
)}
ref={shadowTopEl}
style={{
display: props.disableTopShadow ? "none" : "block",
opacity: "0",
}}
/>
<div
className={cn(
styles.scrollShadowBottom,
styles.scrollShadowY,
props.shadowClassName,
)}
ref={shadowBottomEl}
style={{
opacity: "0",
}}
/>
<div
className={cn(
styles.scrollShadowLeft,
styles.scrollShadowX,
props.shadowClassName,
)}
ref={shadowLeftEl}
style={{
opacity: "0",
}}
/>
<div
className={cn(
styles.scrollShadowRight,
styles.scrollShadowX,
props.shadowClassName,
)}
ref={shadowRightEl}
style={{
opacity: "0",
}}
/>
<div
className={cn("no-scrollbar overflow-auto", props.scrollableClassName)}
data-scrollable
ref={scrollableEl}
>
{props.children}
</div>
</div>
);
}
@@ -0,0 +1 @@
export { Spinner } from "@workspace/ui/components/spinner";
@@ -1,37 +0,0 @@
.loader {
border-radius: 50%;
position: relative;
animation: rotate 2s linear infinite;
}
.loader circle {
content: "";
box-sizing: border-box;
position: absolute;
inset: 0px;
border-radius: 50%;
border: 4px solid #fff;
stroke-linecap: round;
animation: dash 1.5s ease-in-out infinite;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes dash {
0% {
stroke-dasharray: 1, 150;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -124;
}
}
@@ -1,16 +1 @@
import Link from "next/link";
import { cn } from "../../lib/utils";
type LinkProps = React.ComponentProps<typeof Link>;
export function UnderlineLink(props: LinkProps) {
return (
<Link
{...props}
className={cn(
"underline decoration-muted-foreground/50 decoration-dotted underline-offset-[5px] hover:text-foreground hover:decoration-foreground hover:decoration-solid",
props.className,
)}
/>
);
}
export { UnderlineLink } from "@workspace/ui/components/UnderlineLink";
+2 -39
View File
@@ -1,39 +1,2 @@
import { cva, type VariantProps } from "class-variance-authority";
import type * as React from "react";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
"inline-flex items-center rounded-full border border-border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 leading-4",
{
defaultVariants: {
variant: "default",
},
variants: {
variant: {
default: "border-transparent bg-primary/20 text-primary",
destructive:
"border-transparent dark:bg-red-950 dark:text-red-400 bg-red-500/20 text-red-800",
outline: "text-foreground",
secondary:
"border-transparent bg-accent text-accent-foreground hover:bg-accent/80",
success:
"border-transparent dark:bg-green-950/50 dark:text-green-400 bg-green-200 text-green-950",
warning:
"border-transparent dark:bg-yellow-600/20 dark:text-yellow-500 bg-yellow-500/20 text-yellow-900",
},
},
},
);
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
);
}
export { Badge, badgeVariants };
export type { BadgeProps } from "@workspace/ui/components/badge";
export { Badge, badgeVariants } from "@workspace/ui/components/badge";
@@ -1,119 +1,9 @@
import { Slot } from "@radix-ui/react-slot";
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/lib/utils";
const Breadcrumb = React.forwardRef<
HTMLElement,
React.ComponentPropsWithoutRef<"nav"> & {
separator?: React.ReactNode;
}
>(({ ...props }, ref) => <nav aria-label="breadcrumb" ref={ref} {...props} />);
Breadcrumb.displayName = "Breadcrumb";
const BreadcrumbList = React.forwardRef<
HTMLOListElement,
React.ComponentPropsWithoutRef<"ol">
>(({ className, ...props }, ref) => (
<ol
className={cn(
"flex flex-wrap items-center gap-1.5 break-words text-muted-foreground text-sm sm:gap-2.5",
className,
)}
ref={ref}
{...props}
/>
));
BreadcrumbList.displayName = "BreadcrumbList";
const BreadcrumbItem = React.forwardRef<
HTMLLIElement,
React.ComponentPropsWithoutRef<"li">
>(({ className, ...props }, ref) => (
<li
className={cn("inline-flex items-center gap-1.5", className)}
ref={ref}
{...props}
/>
));
BreadcrumbItem.displayName = "BreadcrumbItem";
const BreadcrumbLink = React.forwardRef<
HTMLAnchorElement,
React.ComponentPropsWithoutRef<"a"> & {
asChild?: boolean;
}
>(({ asChild, className, ...props }, ref) => {
const Comp = asChild ? Slot : "a";
return (
<Comp
className={cn("transition-colors hover:text-foreground", className)}
ref={ref}
{...props}
/>
);
});
BreadcrumbLink.displayName = "BreadcrumbLink";
const BreadcrumbPage = React.forwardRef<
HTMLSpanElement,
React.ComponentPropsWithoutRef<"span">
>(({ className, ...props }, ref) => (
// biome-ignore lint/a11y/useFocusableInteractive: required
<span
aria-current="page"
aria-disabled="true"
className={cn("font-normal text-foreground", className)}
ref={ref}
role="link"
{...props}
/>
));
BreadcrumbPage.displayName = "BreadcrumbPage";
const BreadcrumbSeparator = ({
children,
className,
...props
}: React.ComponentProps<"li">) => (
<li
aria-hidden="true"
className={cn("[&>svg]:size-3.5", className)}
role="presentation"
{...props}
>
{children ?? <ChevronRightIcon />}
</li>
);
BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
const BreadcrumbEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
aria-hidden="true"
className={cn(
"relative flex h-9 w-9 items-center justify-center",
className,
)}
role="presentation"
{...props}
>
<MoreHorizontalIcon className="h-4 w-4" />
<span className="sr-only">More</span>
</span>
);
BreadcrumbEllipsis.displayName = "BreadcrumbEllipsis";
export {
Breadcrumb,
BreadcrumbList,
BreadcrumbEllipsis,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
BreadcrumbEllipsis,
};
} from "@workspace/ui/components/breadcrumb";
@@ -1,71 +1,3 @@
"use client";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import type { BundledLanguage } from "shiki";
import { getCodeHtml } from "./getCodeHtml";
import { PlainTextCodeBlock } from "./plaintext-code";
import { RenderCode } from "./RenderCode";
export type CodeProps = {
code: string;
lang: BundledLanguage;
className?: string;
scrollableClassName?: string;
keepPreviousDataOnCodeChange?: boolean;
copyButtonClassName?: string;
scrollableContainerClassName?: string;
shadowColor?: string;
ignoreFormattingErrors?: boolean;
onCopy?: (code: string) => void;
};
export const CodeClient: React.FC<CodeProps> = ({
code,
lang,
className,
scrollableClassName,
keepPreviousDataOnCodeChange = false,
copyButtonClassName,
ignoreFormattingErrors,
scrollableContainerClassName,
shadowColor,
onCopy,
}) => {
const codeQuery = useQuery({
placeholderData: keepPreviousDataOnCodeChange
? keepPreviousData
: undefined,
queryFn: () =>
getCodeHtml(code, lang, {
ignoreFormattingErrors: ignoreFormattingErrors,
}),
queryKey: ["html", code],
retry: false,
});
if (!codeQuery.data) {
return (
<PlainTextCodeBlock
className={className}
code={code}
copyButtonClassName={copyButtonClassName}
onCopy={onCopy}
scrollableClassName={scrollableClassName}
scrollableContainerClassName={scrollableContainerClassName}
shadowColor={shadowColor}
/>
);
}
return (
<RenderCode
className={className}
code={codeQuery.data.formattedCode}
copyButtonClassName={copyButtonClassName}
html={codeQuery.data.html}
onCopy={onCopy}
scrollableClassName={scrollableClassName}
scrollableContainerClassName={scrollableContainerClassName}
shadowColor={shadowColor}
/>
);
};
export { CodeClient } from "@workspace/ui/components/code/code.client";
@@ -1,22 +1,4 @@
import type { BundledLanguage } from "shiki";
import { getCodeHtml } from "./getCodeHtml";
import { RenderCode } from "./RenderCode";
export type CodeProps = {
code: string;
lang: BundledLanguage;
className?: string;
ignoreFormattingErrors?: boolean;
};
export const CodeServer: React.FC<CodeProps> = async ({
code,
lang,
className,
ignoreFormattingErrors,
}) => {
const { html, formattedCode } = await getCodeHtml(code, lang, {
ignoreFormattingErrors,
});
return <RenderCode className={className} code={formattedCode} html={html} />;
};
export {
type CodeProps,
CodeServer,
} from "@workspace/ui/components/code/code.server";
@@ -1,29 +1 @@
import { cn } from "@/lib/utils";
import { CodeBlockContainer } from "./CodeBlockContainer";
export function PlainTextCodeBlock(props: {
code: string;
copyButtonClassName?: string;
className?: string;
scrollableClassName?: string;
codeClassName?: string;
scrollableContainerClassName?: string;
shadowColor?: string;
onCopy?: (code: string) => void;
}) {
return (
<CodeBlockContainer
className={props.className}
codeToCopy={props.code}
copyButtonClassName={props.copyButtonClassName}
onCopy={props.onCopy}
scrollableClassName={props.scrollableClassName}
scrollableContainerClassName={props.scrollableContainerClassName}
shadowColor={props.shadowColor}
>
<code className={cn("block whitespace-pre", props.codeClassName)}>
{props.code}
</code>
</CodeBlockContainer>
);
}
export { PlainTextCodeBlock } from "@workspace/ui/components/code/plaintext-code";
@@ -0,0 +1,45 @@
"use client";
import type React from "react";
import { useRef } from "react";
import { Button } from "@/components/ui/button";
interface ImageUploadProps {
value: File | undefined;
onChange?: (files: File[]) => void;
children?: React.ReactNode;
variant?: React.ComponentProps<typeof Button>["variant"];
className?: string;
multiple?: boolean;
accept: string;
}
export function ImageUploadButton(props: ImageUploadProps) {
const fileInputRef = useRef<HTMLInputElement>(null);
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files || []);
props.onChange?.(files);
};
return (
<div>
<Button
className={props.className}
onClick={() => fileInputRef.current?.click()}
variant={props.variant}
>
{props.children}
</Button>
<input
accept={props.accept}
aria-label="Upload image"
className="hidden"
multiple={props.multiple}
onChange={handleFileChange}
ref={fileInputRef}
type="file"
/>
</div>
);
}
+1 -25
View File
@@ -1,25 +1 @@
import * as React from "react";
import { cn } from "@/lib/utils";
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background selection:bg-inverted selection:text-inverted-foreground file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-muted-foreground placeholder:text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className,
)}
ref={ref}
type={type}
{...props}
/>
);
},
);
Input.displayName = "Input";
export { Input };
export { Input } from "@workspace/ui/components/input";
+5 -150
View File
@@ -1,156 +1,11 @@
import * as React from "react";
import { cn } from "@/lib/utils";
import { ScrollShadow } from "./ScrollShadow/ScrollShadow";
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<table
className={cn(
"w-full caption-bottom border-collapse align-top text-sm lining-nums tabular-nums",
className,
)}
ref={ref}
{...props}
/>
));
Table.displayName = "Table";
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead
className={cn("relative border-b bg-background", className)}
ref={ref}
{...props}
/>
));
TableHeader.displayName = "TableHeader";
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
className={cn("[&_tr:last-child]:border-0", className)}
ref={ref}
{...props}
/>
));
TableBody.displayName = "TableBody";
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
className={cn(
"border-border border-t bg-card font-medium [&>tr]:last:border-b-0",
className,
)}
ref={ref}
{...props}
/>
));
TableFooter.displayName = "TableFooter";
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement> & {
/**
* Contain the absolutely position elements inside the row with position:relative + transform:translate(0)
* transform:translate(0) is required because position:relative on tr element does not work on webkit
*/
linkBox?: boolean;
}
>(({ className, linkBox, ...props }, ref) => (
<tr
className={cn(
"border-border border-b last:border-0 data-[state=selected]:bg-muted",
linkBox && "relative translate-x-0 translate-y-0",
className,
)}
ref={ref}
{...props}
/>
));
TableRow.displayName = "TableRow";
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
className={cn(
"px-6 py-4 text-left align-middle font-medium text-muted-foreground text-xs uppercase tracking-wider [&:has([role=checkbox])]:pr-0",
className,
)}
ref={ref}
{...props}
/>
));
TableHead.displayName = "TableHead";
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
className={cn(
"px-6 py-4 text-start align-middle text-sm [&:has([role=checkbox])]:pr-0",
className,
)}
ref={ref}
{...props}
/>
));
TableCell.displayName = "TableCell";
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
className={cn("py-4 text-muted-foreground text-sm", className)}
ref={ref}
{...props}
/>
));
TableCaption.displayName = "TableCaption";
function TableContainer(props: {
children: React.ReactNode;
className?: string;
scrollableContainerClassName?: string;
}) {
return (
<ScrollShadow
className={cn(
"relative whitespace-nowrap rounded-lg border border-border bg-card",
props.className,
)}
disableTopShadow
scrollableClassName={props.scrollableContainerClassName}
shadowClassName="z-30"
shadowColor="hsl(var(--muted))"
>
{props.children}
</ScrollShadow>
);
}
export {
Table,
TableHeader,
TableBody,
TableCaption,
TableCell,
TableContainer,
TableFooter,
TableHead,
TableHeader,
TableRow,
TableCell,
TableCaption,
TableContainer,
};
} from "@workspace/ui/components/table";
+10 -8
View File
@@ -6,7 +6,7 @@ import { useCallback, useRef, useState } from "react";
import { useIsomorphicLayoutEffect } from "@/lib/useIsomorphicLayoutEffect";
import { cn } from "@/lib/utils";
import { Button } from "./button";
import { ScrollShadow } from "./ScrollShadow/ScrollShadow";
import { ScrollShadow } from "./ScrollShadow";
import { ToolTipLabel } from "./tooltip";
export type TabLink = {
@@ -216,14 +216,16 @@ function useUnderline<El extends HTMLElement>() {
return { activeTabRef, containerRef, lineRef };
}
export type TabPathLink = {
name: React.ReactNode;
path: string;
exactMatch?: boolean;
isDisabled?: boolean;
isActive?: (pathname: string) => boolean;
};
export function TabPathLinks(props: {
links: {
name: React.ReactNode;
path: string;
exactMatch?: boolean;
isDisabled?: boolean;
isActive?: (pathname: string) => boolean;
}[];
links: TabPathLink[];
className?: string;
tabContainerClassName?: string;
shadowColor?: string;
+7 -68
View File
@@ -1,70 +1,9 @@
"use client";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import * as React from "react";
import { cn } from "@/lib/utils";
const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = TooltipPrimitive.Trigger;
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
className={cn(
"fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 animate-in overflow-hidden rounded-md border border-border bg-popover px-3 py-1.5 text-popover-foreground text-sm shadow-md data-[state=closed]:animate-out",
className,
)}
ref={ref}
sideOffset={sideOffset}
{...props}
/>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
export function ToolTipLabel(props: {
children: React.ReactNode;
label: React.ReactNode;
rightIcon?: React.ReactNode;
leftIcon?: React.ReactNode;
hoverable?: boolean;
contentClassName?: string;
side?: "top" | "right" | "bottom" | "left";
align?: "center" | "start" | "end";
}) {
if (!props.label) {
return props.children;
}
return (
<TooltipProvider>
<Tooltip delayDuration={100} disableHoverableContent={!props.hoverable}>
<TooltipTrigger asChild className="!pointer-events-auto" tabIndex={-1}>
{props.children}
</TooltipTrigger>
<TooltipContent
align={props.align}
className={cn(
"max-w-[400px] whitespace-normal p-0 leading-relaxed",
props.contentClassName,
)}
side={props.side}
sideOffset={10}
>
<div className="flex items-center gap-1.5 p-4 text-sm">
{props.leftIcon}
{props.label}
{props.rightIcon}
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
export {
ToolTipLabel,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@workspace/ui/components/tooltip";
-23
View File
@@ -1,23 +0,0 @@
// TODO: move to API
export const OPSponsoredChains = [
// Optimism
10,
// Base
8453,
// Zora
7777777,
// Mode
34443,
// Frax
252,
// Cyber
7560,
// Redstone
690,
// Ancient8
888888888,
//Donatuz
42026,
//Mantle
5000,
];
+1
View File
@@ -3,3 +3,4 @@ export const COOKIE_PREFIX_TOKEN = "tw_token_";
export const LAST_USED_PROJECT_ID = "last-used-project-id";
export const LAST_USED_TEAM_ID = "last-used-team-id";
export const HAS_USED_DASHBOARD = "has-used-dashboard";
@@ -29,3 +29,6 @@ export const NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET =
export const NEXT_PUBLIC_DEMO_ENGINE_URL =
process.env.NEXT_PUBLIC_DEMO_ENGINE_URL || "";
export const NEXT_PUBLIC_THIRDWEB_AI_HOST =
process.env.NEXT_PUBLIC_THIRDWEB_AI_HOST || "https://nebula-api.thirdweb.com";
+2 -3
View File
@@ -1,6 +1,5 @@
export const THIRDWEB_EWS_API_HOST =
process.env.NEXT_PUBLIC_THIRDWEB_EWS_API_HOST ||
"https://in-app-wallet.thirdweb.com";
export const THIRDWEB_API_HOST =
process.env.NEXT_PUBLIC_THIRDWEB_API_HOST || "https://api.thirdweb-dev.com";
export const THIRDWEB_PAY_DOMAIN =
process.env.NEXT_PUBLIC_PAY_URL || "pay.thirdweb-dev.com";
@@ -1,6 +1,6 @@
"use client";
import { useMemo } from "react";
import { useCallback, useMemo } from "react";
import type { Chain, ChainMetadata } from "thirdweb/chains";
import { useActiveWalletChain } from "thirdweb/react";
import { useAllChainsData } from "@/hooks/chains/allChains";
@@ -27,6 +27,17 @@ export function useV5DashboardChain(
}, [chainId, idToChain]);
}
export function useGetV5DashboardChain() {
const { idToChain } = useAllChainsData();
return useCallback(
(chainId: number) => {
// eslint-disable-next-line no-restricted-syntax
return defineDashboardChain(chainId, idToChain.get(chainId));
},
[idToChain],
);
}
/**
* same behavior as v4 `useChain()` but for v5
*/
+1 -1
View File
@@ -1,5 +1,5 @@
import { useQuery } from "@tanstack/react-query";
import { getUniversalBridgeTokens } from "@/api/universal-bridge/tokens";
import { getUniversalBridgeTokens } from "@/api/universal-bridge/token-list";
export function useTokensData({
chainId,
+114 -29
View File
@@ -1,9 +1,25 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type {
ListUserWalletsData,
ListUserWalletsResponses,
} from "@thirdweb-dev/api";
import { configure, listUserWallets } from "@thirdweb-dev/api";
import { useActiveAccount } from "thirdweb/react";
import type { WalletUser } from "thirdweb/wallets";
import { THIRDWEB_EWS_API_HOST } from "@/constants/urls";
import { THIRDWEB_API_HOST } from "@/constants/urls";
import { embeddedWalletsKeys } from "../query-keys/cache-keys";
// Configure the API client to use the correct base URL
configure({
override: {
baseUrl: THIRDWEB_API_HOST,
},
});
// Extract types from the generated API
type APIWallet = ListUserWalletsResponses[200]["result"]["wallets"][0];
type APIProfile = APIWallet["profiles"][0];
const fetchAccountList = ({
jwt,
clientId,
@@ -18,37 +34,104 @@ const fetchAccountList = ({
pageNumber: number;
}) => {
return async () => {
const url = new URL(`${THIRDWEB_EWS_API_HOST}/api/2024-05-05/account/list`);
try {
// Prepare query parameters for the new API
const queryParams: ListUserWalletsData["query"] = {
page: pageNumber,
limit: 50, // Keep the same page size
};
// Add clientId or ecosystemSlug parameter
if (ecosystemSlug) {
url.searchParams.append("ecosystemSlug", `ecosystem.${ecosystemSlug}`);
} else if (clientId) {
url.searchParams.append("clientId", clientId);
// Use the generated API function with Bearer authentication
const response = await listUserWallets({
query: queryParams,
headers: {
Authorization: `Bearer ${jwt}`,
"Content-Type": "application/json",
"x-thirdweb-team-id": teamId,
...(clientId && { "x-client-id": clientId }),
...(ecosystemSlug && {
"x-ecosystem-id": `ecosystem.${ecosystemSlug}`,
}),
},
});
// Handle response
if (response.error || !response.data) {
const errorMessage =
typeof response.error === "string"
? response.error
: "No data returned";
throw new Error(errorMessage);
}
// Transform the response to match the expected format
return {
users: response.data.result.wallets.map(transformToWalletUser),
hasMore: response.data.result.pagination.hasMore ?? false,
};
} catch (error) {
console.error("Failed to fetch wallets:", error);
throw error;
}
url.searchParams.append("page", pageNumber.toString());
const res = await fetch(url.href, {
headers: {
Authorization: `Bearer ${jwt}`,
"Content-Type": "application/json",
"x-thirdweb-team-id": teamId,
...(clientId && { "x-client-id": clientId }),
},
method: "GET",
});
if (!res.ok) {
throw new Error(`Failed to fetch wallets: ${await res.text()}`);
}
const json = await res.json();
return json as {
users: WalletUser[];
};
};
};
// Transform API response to match existing WalletUser format
function transformToWalletUser(apiWallet: APIWallet): WalletUser {
return {
id: getProfileId(apiWallet.profiles[0]) || "",
linkedAccounts: apiWallet.profiles.map((profile) => {
// Create details object based on the profile data
let details:
| { email: string; [key: string]: string }
| { phone: string; [key: string]: string }
| { address: string; [key: string]: string }
| { id: string; [key: string]: string };
const profileId = getProfileId(profile);
if ("email" in profile && profile.email) {
details = { email: profile.email, id: profileId };
} else if ("phone" in profile && profile.phone) {
details = { phone: profile.phone, id: profileId };
} else if ("walletAddress" in profile && profile.walletAddress) {
details = { address: profile.walletAddress, id: profileId };
} else {
details = { id: profileId };
}
return {
type: profile.type,
details,
};
}),
wallets: apiWallet.address
? [
{
address: apiWallet.address,
createdAt: apiWallet.createdAt || new Date().toISOString(),
type: "enclave" as const,
},
]
: [],
};
}
// Helper function to safely get ID from any profile type
function getProfileId(profile: APIProfile | undefined): string {
if (!profile) return "";
if ("id" in profile) {
return profile.id;
} else if ("credentialId" in profile) {
return profile.credentialId;
} else if ("identifier" in profile) {
return profile.identifier;
}
return "";
}
export function useEmbeddedWallets(params: {
clientId?: string;
ecosystemSlug?: string;
@@ -98,6 +181,7 @@ export function useAllEmbeddedWallets(params: { authToken: string }) {
while (true) {
const res = await queryClient.fetchQuery<{
users: WalletUser[];
hasMore: boolean;
}>({
queryFn: fetchAccountList({
clientId,
@@ -113,12 +197,13 @@ export function useAllEmbeddedWallets(params: { authToken: string }) {
),
});
if (res.users.length === 0) {
responses.push(...res.users);
if (!res.hasMore) {
break;
}
page++;
responses.push(...res.users);
}
return responses;
+3 -3
View File
@@ -11,8 +11,8 @@ import { useActiveAccount } from "thirdweb/react";
import invariant from "tiny-invariant";
import { apiServerProxy } from "@/actions/proxies";
import type { EngineBackendWalletType } from "@/constants/engine";
import type { ResultItem } from "../../app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/metrics/components/StatusCodes";
import type { EngineStatus } from "../../app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(instance)/[engineId]/overview/components/transactions-table";
import type { ResultItem } from "../../app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(instance)/[engineId]/metrics/components/StatusCodes";
import type { EngineStatus } from "../../app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(instance)/[engineId]/overview/components/transactions-table";
import { engineKeys } from "../query-keys/cache-keys";
// Engine instances
@@ -1662,7 +1662,7 @@ export function useEngineSystemMetrics(
queryFn: async () => {
const res = await apiServerProxy({
method: "GET",
pathname: `/v1/teams/${teamIdOrSlug}/${projectSlug}/engine/dedicated/${engineId}/metrics`,
pathname: `/v1/teams/${teamIdOrSlug}/${projectSlug}/engine/${engineId}/metrics`,
});
if (!res.ok) {
+19
View File
@@ -0,0 +1,19 @@
import { useTheme } from "next-themes";
import {
type SendTransactionConfig,
// eslint-disable-next-line no-restricted-imports
useSendAndConfirmTransaction,
} from "thirdweb/react";
import { getSDKTheme } from "@/utils/sdk-component-theme";
export function useSendAndConfirmTx(config?: SendTransactionConfig) {
const { theme } = useTheme();
const sendAndConfirmTransaction = useSendAndConfirmTransaction({
payModal: {
theme: getSDKTheme(theme === "light" ? "light" : "dark"),
},
...config,
});
return sendAndConfirmTransaction;
}
+9 -6
View File
@@ -7,8 +7,8 @@ import {
import { toast } from "sonner";
import {
type Chain,
getAddress,
NATIVE_TOKEN_ADDRESS,
sendAndConfirmTransaction,
type ThirdwebClient,
type ThirdwebContract,
} from "thirdweb";
@@ -20,6 +20,7 @@ import { getWalletBalance } from "thirdweb/wallets";
import invariant from "tiny-invariant";
import { parseError } from "../utils/errorParser";
import { tryCatch } from "../utils/try-catch";
import { useSendAndConfirmTx } from "./useSendTx";
function getTokenBalancesQuery(params: {
ownerAddress: string;
@@ -78,6 +79,7 @@ export function useOwnedTokenBalances(params: {
export function useSplitDistributeFunds(contract: ThirdwebContract) {
const account = useActiveAccount();
const queryClient = useQueryClient();
const sendAndConfirmTx = useSendAndConfirmTx();
const params = {
ownerAddress: contract.address, // because we want to fetch the balance of split contract
@@ -98,16 +100,15 @@ export function useSplitDistributeFunds(contract: ThirdwebContract) {
.filter((token) => token.value !== 0n)
.map(async (currency) => {
const transaction =
currency.name === "Native Token"
getAddress(currency.tokenAddress) ===
getAddress(NATIVE_TOKEN_ADDRESS)
? distribute({ contract })
: distributeByToken({
contract,
tokenAddress: currency.tokenAddress,
});
const promise = sendAndConfirmTransaction({
account,
transaction,
});
const promise = sendAndConfirmTx.mutateAsync(transaction);
toast.promise(promise, {
error: (err) => ({
message: `Error distributing ${currency.name}`,
@@ -116,6 +117,8 @@ export function useSplitDistributeFunds(contract: ThirdwebContract) {
loading: `Distributing ${currency.name}`,
success: `Successfully distributed ${currency.name}`,
});
await promise;
});
return await Promise.all(distributions);
+3 -3
View File
@@ -8,9 +8,9 @@ import {
} from "thirdweb";
import * as ERC20Ext from "thirdweb/extensions/erc20";
import * as VoteExt from "thirdweb/extensions/vote";
import { useSendAndConfirmTransaction } from "thirdweb/react";
import type { Account } from "thirdweb/wallets";
import invariant from "tiny-invariant";
import { useSendAndConfirmTx } from "@/hooks/useSendTx";
export async function tokensDelegated(
options: BaseTransactionOptions<{ account: Account | undefined }>,
@@ -81,7 +81,7 @@ export async function votingTokenDecimals(options: BaseTransactionOptions) {
}
export function useDelegateMutation() {
const { mutateAsync } = useSendAndConfirmTransaction();
const sendAndConfirmTx = useSendAndConfirmTx();
return useMutation({
mutationFn: async (contract: ThirdwebContract) => {
@@ -97,7 +97,7 @@ export function useDelegateMutation() {
contract: tokenContract,
delegatee: contract.address,
});
return await mutateAsync(transaction);
return await sendAndConfirmTx.mutateAsync(transaction);
},
});
}
@@ -0,0 +1 @@
export { BringToFrontIcon as BridgeIcon } from "lucide-react";
+6 -3
View File
@@ -17,18 +17,21 @@ export async function resolveEns(
ensName: await resolveName({
address: ensNameOrAddress,
client,
}),
}).catch(() => null),
};
}
if (!isValidENSName(ensNameOrAddress)) {
throw new Error("Invalid ENS name");
return {
address: null,
ensName: null,
};
}
const address = await resolveAddress({
client,
name: ensNameOrAddress,
});
}).catch(() => null);
if (!address) {
return {
+1 -1
View File
@@ -184,7 +184,7 @@ async function fetchContractName(chainId: number, contractAddress: string) {
return name;
}
const formatNumber = (num: number) => {
export const formatNumber = (num: number) => {
if (num >= 1000000) {
return `${(num / 1000000).toLocaleString(undefined, {
maximumFractionDigits: 1,
+1 -1
View File
@@ -147,4 +147,4 @@ export type ApiKeyPayConfigValidationSchema = z.infer<
>;
// FIXME: Remove
export const HIDDEN_SERVICES = ["relayer", "chainsaw"];
export const HIDDEN_SERVICES = ["relayer", "chainsaw", "engineCloud"];
+35
View File
@@ -74,6 +74,12 @@ export function teamStub(id: string, billingPlan: Team["billingPlan"]): Team {
rpc: {
enabled: true,
rateLimit: 1000,
websockets: {
enabled: false,
reasonCode: "enterprise_plan_required",
maxConnections: 0,
maxSubscriptions: 0,
},
},
storage: {
download: {
@@ -273,3 +279,32 @@ export function newAccountStub(overrides?: Partial<Account>): Account {
...overrides,
};
}
export function randomLorem(length: number) {
const loremWords = [
"lorem",
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipiscing",
"elit",
"sed",
"do",
"eiusmod",
"tempor",
"incididunt",
"ut",
"labore",
"et",
"dolore",
"magna",
"aliqua",
];
return Array.from({ length }, () => {
const randomIndex = Math.floor(Math.random() * loremWords.length);
return loremWords[randomIndex];
}).join(" ");
}
+9 -14
View File
@@ -7,17 +7,11 @@ export interface WalletStats {
walletType: string;
}
export interface WalletUserStats {
date: string;
newUsers: number;
returningUsers: number;
totalUsers: number;
}
export interface InAppWalletStats {
date: string;
authenticationMethod: string;
uniqueWalletsConnected: number;
newUsers: number;
}
export interface EcosystemWalletStats extends InAppWalletStats {
@@ -66,13 +60,6 @@ export interface UniversalBridgeWalletStats {
developerFeeUsdCents: number;
}
export interface WebhookRequestStats {
date: string;
webhookId: string;
httpStatusCode: number;
totalRequests: number;
}
export interface WebhookLatencyStats {
date: string;
webhookId: string;
@@ -91,6 +78,14 @@ export interface WebhookSummaryStats {
errorBreakdown: Record<string, number>;
}
export interface AIUsageStats {
date: string;
totalPromptTokens: number;
totalCompletionTokens: number;
totalSessions: number;
totalRequests: number;
}
export interface AnalyticsQueryParams {
teamId: string;
projectId?: string;
@@ -1,24 +1,24 @@
"use client";
import { useTheme } from "next-themes";
import { defineChain, type ThirdwebClient } from "thirdweb";
import type { ThirdwebClient } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { BuyWidget } from "thirdweb/react";
import { getSDKTheme } from "@/utils/sdk-component-theme";
import { BuyAndSwapEmbed } from "@/components/blocks/BuyAndSwapEmbed";
import { GridPatternEmbedContainer } from "@/components/blocks/grid-pattern-embed-container";
import { defineDashboardChain } from "@/lib/defineDashboardChain";
export function BuyFundsSection(props: {
chain: ChainMetadata;
client: ThirdwebClient;
}) {
const { theme } = useTheme();
return (
<section className="flex flex-col gap-4 items-center justify-center">
<BuyWidget
amount="0"
// eslint-disable-next-line no-restricted-syntax
chain={defineChain(props.chain.chainId)}
<GridPatternEmbedContainer>
<BuyAndSwapEmbed
client={props.client}
theme={getSDKTheme(theme === "dark" ? "dark" : "light")}
// eslint-disable-next-line no-restricted-syntax
chain={defineDashboardChain(props.chain.chainId, props.chain)}
buyAmount={undefined}
tokenAddress={undefined}
pageType="chain"
/>
</section>
</GridPatternEmbedContainer>
);
}
@@ -17,7 +17,6 @@ import type { Chain, ChainMetadata } from "thirdweb/chains";
import {
useActiveAccount,
useActiveWalletChain,
useSendTransaction,
useSwitchActiveWalletChain,
useWalletBalance,
} from "thirdweb/react";
@@ -43,11 +42,12 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import {
NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET,
NEXT_PUBLIC_TURNSTILE_SITE_KEY,
} from "@/constants/public-envs";
import { useSendAndConfirmTx } from "@/hooks/useSendTx";
import { parseError } from "@/utils/errorParser";
import { mapV4ChainToV5Chain } from "@/utils/map-chains";
@@ -310,7 +310,7 @@ function SendFundsToFaucetModalContent(props: {
const account = useActiveAccount();
const activeChain = useActiveWalletChain();
const switchActiveWalletChain = useSwitchActiveWalletChain();
const sendTxMutation = useSendTransaction({
const sendAndConfirmTx = useSendAndConfirmTx({
payModal: false,
});
const switchChainMutation = useMutation({
@@ -334,7 +334,7 @@ function SendFundsToFaucetModalContent(props: {
value: toWei(values.amount.toString()),
});
const promise = sendTxMutation.mutateAsync(sendNativeTokenTx);
const promise = sendAndConfirmTx.mutateAsync(sendNativeTokenTx);
toast.promise(promise, {
error: `Failed to send ${values.amount} ${props.chainMeta.nativeCurrency.symbol} to faucet`,
@@ -404,11 +404,11 @@ function SendFundsToFaucetModalContent(props: {
{activeChain.id === props.chain.id ? (
<Button
className="mt-4 w-full gap-2"
disabled={sendTxMutation.isPending}
disabled={sendAndConfirmTx.isPending}
key="submit"
type="submit"
>
{sendTxMutation.isPending && <Spinner className="size-4" />}
{sendAndConfirmTx.isPending && <Spinner className="size-4" />}
Send funds to faucet
</Button>
) : (
@@ -11,7 +11,7 @@ import {
import { useDebounce } from "use-debounce";
import { useCustomConnectModal } from "@/components/connect-wallet";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Spinner } from "@/components/ui/Spinner";
import { ToolTipLabel } from "@/components/ui/tooltip";
type AddChainToWalletProps = {
@@ -43,9 +43,7 @@ function useChainStatswithRPC(_rpcUrl: string) {
const latency = (performance.now() - startTimeStamp).toFixed(0);
const blockNumber = Number.parseInt(json.result.number, 16);
const blockGasLimit = Number.parseInt(json.result.gasLimit, 16);
return {
blockGasLimit,
blockNumber,
latency,
};
@@ -61,8 +59,70 @@ function useChainStatswithRPC(_rpcUrl: string) {
});
}
function useEIP7702Support(_rpcUrl: string) {
let rpcUrl = _rpcUrl.replace(
// eslint-disable-next-line no-template-curly-in-string
// biome-ignore lint/suspicious/noTemplateCurlyInString: this is expected in this case
"${THIRDWEB_API_KEY}",
NEXT_PUBLIC_DASHBOARD_CLIENT_ID,
);
// based on the environment hit dev or production
if (hostnameEndsWith(rpcUrl, "rpc.thirdweb.com")) {
if (!isProd) {
rpcUrl = rpcUrl.replace("rpc.thirdweb.com", "rpc.thirdweb-dev.com");
}
}
return useQuery({
enabled: !!rpcUrl,
queryFn: async () => {
try {
const res = await fetch(rpcUrl, {
body: JSON.stringify({
id: 1,
jsonrpc: "2.0",
method: "eth_estimateGas",
params: [
{
from: "0xdeadbeef00000000000000000000000000000000",
to: "0xdeadbeef00000000000000000000000000000000",
data: "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
value: "0x0",
},
"latest",
{
"0xdeadbeef00000000000000000000000000000000": {
code: "0xef01000000000000000000000000000000000000000001",
},
},
],
}),
method: "POST",
});
const json = await res.json();
// If the response has a valid result object, EIP-7702 is enabled
return {
isSupported: !!json.result,
};
} catch {
// If the request fails or errors, EIP-7702 is not supported
return {
isSupported: false,
};
}
},
queryKey: ["eip-7702-support", { rpcUrl }],
refetchInterval: false, // Don't refetch this as it's unlikely to change
refetchOnWindowFocus: false,
});
}
export function ChainLiveStats(props: { rpc: string }) {
const stats = useChainStatswithRPC(props.rpc);
const eip7702Support = useEIP7702Support(props.rpc);
return (
<>
@@ -107,8 +167,8 @@ export function ChainLiveStats(props: { rpc: string }) {
)}
</PrimaryInfoItem>
{/* Block Height */}
<PrimaryInfoItem title="Block Height" titleIcon={<PulseDot />}>
{/* Latest Block */}
<PrimaryInfoItem title="Latest Block" titleIcon={<PulseDot />}>
{stats.isError ? (
<p className="fade-in-0 animate-in text-destructive-text">N/A</p>
) : stats.data ? (
@@ -120,16 +180,37 @@ export function ChainLiveStats(props: { rpc: string }) {
)}
</PrimaryInfoItem>
{/* Block Gas Limit */}
<PrimaryInfoItem title="Block Gas Limit" titleIcon={<PulseDot />}>
{stats.isError ? (
<p className="fade-in-0 animate-in text-destructive-text">N/A</p>
) : stats.data ? (
<p className="fade-in-0 animate-in">
{stats.data.blockGasLimit ?? "N/A"}
</p>
{/* EIP-7702 */}
<PrimaryInfoItem
title="EIP-7702"
titleIcon={
eip7702Support.data ? (
eip7702Support.data.isSupported ? (
<ToolTipLabel label="Enabled">
<CircleCheckIcon className="size-4 text-success-text" />
</ToolTipLabel>
) : (
<ToolTipLabel label="Disabled">
<XIcon className="size-4 text-destructive-text" />
</ToolTipLabel>
)
) : eip7702Support.isError ? (
<ToolTipLabel label="Disabled">
<XIcon className="size-4 text-destructive-text" />
</ToolTipLabel>
) : null
}
>
{eip7702Support.data ? (
eip7702Support.data.isSupported ? (
"Available"
) : (
"Not Available"
)
) : eip7702Support.isError ? (
"Not Available"
) : (
<div className="flex h-[28px] w-[140px] py-1">
<div className="flex h-[28px] w-[80px] py-1">
<Skeleton className="h-full w-full" />
</div>
)}
@@ -2,8 +2,8 @@
import { toast } from "sonner";
import type { ThirdwebContract } from "thirdweb";
import { cancelAuction, cancelListing } from "thirdweb/extensions/marketplace";
import { useSendAndConfirmTransaction } from "thirdweb/react";
import { TransactionButton } from "@/components/tx-button";
import { useSendAndConfirmTx } from "@/hooks/useSendTx";
interface CancelTabProps {
id: string;
@@ -21,15 +21,15 @@ export const CancelTab: React.FC<CancelTabProps> = ({
const transaction = isAuction
? cancelAuction({ auctionId: BigInt(id), contract })
: cancelListing({ contract, listingId: BigInt(id) });
const cancelQuery = useSendAndConfirmTransaction();
const sendAndConfirmTx = useSendAndConfirmTx();
return (
<TransactionButton
className="self-end"
client={contract.client}
isLoggedIn={isLoggedIn}
isPending={cancelQuery.isPending}
isPending={sendAndConfirmTx.isPending}
onClick={() => {
const promise = cancelQuery.mutateAsync(transaction, {
const promise = sendAndConfirmTx.mutateAsync(transaction, {
onError: (error) => {
console.error(error);
},
@@ -28,7 +28,7 @@ import type {
CreateListingParams,
} from "thirdweb/extensions/marketplace";
import { createAuction, createListing } from "thirdweb/extensions/marketplace";
import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react";
import { useActiveAccount } from "thirdweb/react";
import { shortenAddress } from "thirdweb/utils";
import { CurrencySelector } from "@/components/blocks/CurrencySelector";
import { NFTMediaWithEmptyState } from "@/components/blocks/nft-media";
@@ -54,6 +54,7 @@ import {
import { Skeleton } from "@/components/ui/skeleton";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { useDashboardOwnedNFTs } from "@/hooks/useDashboardOwnedNFTs";
import { useSendAndConfirmTx } from "@/hooks/useSendTx";
import { useTxNotifications } from "@/hooks/useTxNotifications";
import { useOwnedNFTsInsight } from "@/hooks/useWalletNFTs";
import { cn } from "@/lib/utils";
@@ -122,7 +123,7 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
chain: contract.chain,
});
const sendAndConfirmTx = useSendAndConfirmTransaction();
const sendAndConfirmTx = useSendAndConfirmTx();
const listingNotifications = useTxNotifications(
"NFT listed Successfully",
"Failed to list NFT",

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