Compare commits

..
Author SHA1 Message Date
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2606da6d87 Version Packages (#6606)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-01 13:02:59 -07:00
kumaryash90 9cf137660a Remove custom config for Ronin (#6612)
Create2 deployer restrictions are lifted by the chain. Enable deployments through default create2 factory.

<!--

## 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 custom configuration for the `Ronin` blockchain within the `thirdweb` package.

### Detailed summary
- Removed the `Ronin` configuration from the gas price settings in the `create-2-factory.ts` file.
- Updated the gas price for `Ronin` from `110n * 10n ** 9n` to a static value of `110000n`.

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

<!-- end pr-codex -->
2025-04-01 19:06:06 +00:00
MananTank 58ab8c2ee7 [TOOL-3766] Dashboard: Account Abstraction page updates (#6609)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on improving the UI and functionality of various components in the dashboard by modifying styles, enhancing layout elements, and integrating new features related to account abstraction and user operation statistics.

### Detailed summary
- Removed `lg:gap-6` from grid classes in multiple components.
- Increased header sizes from `text-lg` to `text-xl` in `SponsoredTransactionsTableUI.tsx`.
- Adjusted margins and padding for improved spacing in various components.
- Added new `GasCreditAlert` component for displaying gas credit information.
- Integrated `AccountAbstractionSummary` into `AccountAbstractionAnalytics`.
- Updated `userOpStats` handling in `Page` component to use promises for better performance.
- Replaced certain text with more concise phrasing in descriptions.
- Removed unused imports and optimized component structure for clarity.

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

<!-- end pr-codex -->
2025-04-01 18:12:49 +00:00
MananTank 447a519ddc [TOOL-3768] Dashboard: Move Nebula from team to project layout (#6608)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on enhancing the `Nebula` components in the dashboard application by improving layout styles, adding project ID handling in analytics, and adjusting waitlist checks.

### Detailed summary
- Updated class names for better styling in `nebula-analytics-filter.tsx` and `nebula-waitlist-page.tsx`.
- Added `projectId` parameter to `fetchNebulaAnalytics`.
- Modified `NebulaWaitListPageUI` layout.
- Integrated `showNebula` prop in `ProjectSidebarLayout`.
- Adjusted project layout and waitlist checks in `layout.tsx`.
- Enhanced `NebulaAnalyticsPage` to pass `projectId`.
- Updated page handling in `page.tsx` to include project fetching and redirects.

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

<!-- end pr-codex -->
2025-04-01 11:56:32 +00:00
Joaquim VergesandGitHub a9b7633d05 [SDK] Show past transactions in transactions modal screen (#6607) 2025-04-01 03:14:28 -07:00
Joaquim VergesandGitHub 38e089a5f3 [SDK] Only onramp for required transaction amount (#6605) 2025-04-01 02:01:11 -07:00
Joaquim VergesandGitHub ef2f86cff0 Version Packages (#6589) 2025-03-31 23:05:33 -07:00
gregfromstl 8fdef82147 [Playground] Fix: Filter UB Backend API for only GETs (#6604)
<!--

## 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 and functionality of the `Page` component in `apps/playground-web/src/app/connect/pay/backend/page.tsx`. It improves the presentation of the Universal Bridge REST API information and adds a link to view all endpoints.

### Detailed summary
- Wrapped the header and description in a `div` for better layout.
- Added a `Link` component with a button to "View all endpoints".
- Changed the title of the `BlueprintSection` from "Available endpoints" to "Available GET endpoints".
- Updated the filtering of `blueprints` to only include those with defined `get` properties.

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

<!-- end pr-codex -->
2025-04-01 01:02:26 +00:00
MananTank cbb15692a7 [TOOL-3880] Playground: Add utm_source on links (#6597)
<!-- start pr-codex -->

## PR-Codex overview
This PR primarily updates various `docsLink` URLs across multiple components in the `playground-web` application to include a query parameter (`?utm_source=playground`). This change likely aims to track traffic sources more effectively.

### Detailed summary
- Updated `docsLink` in `apps/playground-web/src/app/connect/auth/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/pay/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/blockchain-api/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/pay/commerce/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/pay/fund-wallet/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/pay/transactions/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/sign-in/embed/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/sign-in/button/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/ui/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/sign-in/headless/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/in-app-wallet/layout.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/insight/layout.tsx`
- Updated `docsLink` in `apps/playground-web/src/components/blocks/APIHeader.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/pay/backend/layout.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/account-abstraction/connect/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/account-abstraction/sponsor/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/ui/nft/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/otherLinks.ts`
- Updated `docsLink` in `apps/playground-web/src/app/connect/account-abstraction/native-aa/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/ui/chain/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/ui/token/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/social/page.tsx`
- Updated `docsLink` in `apps/playground-web/src/app/connect/ui/wallet/page.tsx`
- Updated `href` in `apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx`
- Updated `href` in `apps/playground-web/src/app/insight/page.tsx`
- Updated `href` in `apps/playground-web/src/components/auth/gated-content.tsx`
- Updated `href` in `apps/playground-web/src/components/engine/minting/erc1155-mint-to.tsx`
- Updated `href` in `apps/playground-web/src/app/connect/sign-in/components/InAppWalletFormGroup.tsx`
- Updated `href` in `apps/playground-web/src/app/connect/sign-in/button/LeftSection.tsx`

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

<!-- end pr-codex -->
2025-03-31 19:37:09 +00:00
MananTank 1d23e5cd9c Dashboard: Fix AA page alert styles (#6596)
<!--

## 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 `SmartWalletsBillingAlert` component in the `Alerts.tsx` file. It updates the alert's UI and removes the dismissible functionality, while also changing the import paths for components used in the alert.

### Detailed summary
- Removed unused imports related to `Alert`, `AlertDescription`, `AlertIcon`, `AlertTitle`, `Flex`, and `IconButton`.
- Updated imports for `Alert`, `AlertDescription`, `AlertTitle`, `TrackedUnderlineLink`, and `CircleAlertIcon`.
- Removed the `dismissible` prop and related logic.
- Simplified the alert structure with a new layout.
- Changed the alert icon and styling.

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

<!-- end pr-codex -->
2025-03-31 19:07:57 +00:00
MananTank a17d893811 [TOOL-3873] Fix pageview tracking in dashboard, Add posthog to playground (#6595) 2025-03-31 18:38:58 +00:00
kumaryash90 0dcde077f5 [SDK] Fix bytecode caching (#6593)
<!--

## 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 bytecode caching mechanism in the `thirdweb` package by including additional metadata in the cache key, which improves cache differentiation and management.

### Detailed summary
- Updated the `cacheKey` in `deploy-metadata.ts` to include `compilerMetadata.publisher` and `compilerMetadata.version` along with `compilerMetadata.name` and `chain.id`.
- Removed the `version` and `publisher` fields from the deployment test case in `process-ref-deployments.test.ts`.

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

<!-- end pr-codex -->
2025-03-31 18:30:28 +00:00
jnsdls 05a041fac3 Add glossary slug route to framer rewrites (#6594)
### TL;DR

Added a rewrite rule for glossary detail pages in the dashboard app.

### What changed?

Added a new path `/learn/glossary/:glossary_slug` to the framer-rewrites.js file to support individual glossary term pages.

### How to test?

1. Navigate to a glossary term detail page (e.g., `/learn/glossary/some-term`)
2. Verify that the page loads correctly and the routing works as expected

### Why make this change?

To enable proper routing for individual glossary term pages, allowing users to access specific glossary entries directly through their unique URLs. This complements the existing glossary index page that was already configured.
2025-03-31 18:00:30 +00:00
MananTank 3d140eed97 [TOOL-3874] Portal: Fix pl.js script CSP error (#6591)
<!--

## 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 Content Security Policy in the `next.config.mjs` file to enhance security by modifying the `script-src` directive.

### Detailed summary
- Updated the `script-src` directive to include `thirdweb.com` and `thirdweb-dev.com` without a wildcard prefix.
- Retained existing sources for `self`, `unsafe-eval`, `unsafe-inline`, `wasm-unsafe-eval`, `inline-speculation-rules`, `vercel.live`, and `js.stripe.com`.

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

<!-- end pr-codex -->
2025-03-31 17:43:57 +00:00
jnsdls a5f005fff0 Remove tutorial routes from framer rewrites (#6592)
closes: CORE-802

Redirect `/learn/tutorials` to `/learn/guides`

This PR removes `/learn/tutorials` and `/learn/tutorials/:tutorial_slug` from the framer-rewrites list and adds redirects from these paths to their corresponding `/learn/guides` equivalents.
2025-03-31 17:14:04 +00:00
saminacodes fb70cab6d4 [portal] added module warning (#6590)
CORE-0000

<!-- start pr-codex -->

---

## PR-Codex overview
This PR adds a warning callout to the `page.mdx` file in the `cross-chain/agglayer` directory, informing users that the Agglayer module is in beta and has deployment limitations.

### Detailed summary
- Added a warning callout indicating that the Agglayer module is in beta.
- Specified that deployments are limited to `Sepolia` and `Cardona` due to requirements for Ethereum as L1 and zkEVM as L2.

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

<!-- end pr-codex -->
2025-03-31 14:53:39 +00:00
Joaquim VergesandGitHub cd9ea69c13 [SDK] Add erc20Value for makeOffer extension function (#6588) 2025-03-31 00:24:42 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
dc892bd136 Version Packages (#6585)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-30 16:14:40 -07:00
Joaquim VergesandGitHub 3d7c009d72 [CLI] Fix secret key handling (#6584) 2025-03-30 15:49:45 -07:00
YashandGitHub 6dbed0d4fc [SDK] fix: Use default contract params with optional overrides (#6581) 2025-03-30 15:45:13 -07:00
MananTank adee1903d5 [TOOL-3814] Dashboard: Show all active coupons in team billing page (#6568)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on refactoring the coupon management system within the billing section of the dashboard. It introduces a new structure for handling coupons, including a client component for API interactions and UI components for displaying and applying coupons.

### Detailed summary
- Deleted obsolete files: `CouponCard.tsx`, `CouponDetails.stories.tsx`, `ApplyCouponCard.stories.tsx`.
- Updated global styles import in `preview.tsx`.
- Replaced `CouponSection` with `Coupons` in `index.tsx`.
- Introduced `CouponsClient` for managing coupon API interactions.
- Created `AsyncCoupons` to fetch coupon data asynchronously.
- Developed `Coupons` component to handle loading states and display.
- Added `CouponsUI` for rendering coupon information and actions.
- Implemented coupon application and deletion logic with error handling.
- Created a story for `CouponsUI` to demonstrate different states and interactions.

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

<!-- end pr-codex -->
2025-03-30 22:18:56 +00:00
gregandGitHub a1d785b8ed Fix: Increase commerce price to avoid minimum purchase errors (#6582) 2025-03-29 20:50:25 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
ba92cd2cbe Version Packages (#6575)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-29 03:41:26 -07:00
Joaquim VergesandGitHub c3d0ea0059 [PayEmbed] Respect supportedTokens prop (#6580) 2025-03-29 03:35:22 -07:00
MananTank 35c5f57179 [TOOL-3839] Fix double scrollbars in project settings page (#6579) 2025-03-29 07:51:23 +00:00
gregandGitHub 2a1f4be48b Fix: Not Found Status should show Pending (#6578) 2025-03-28 20:11:43 -07:00
gregandGitHub 0121ccd702 fix: reduce cost of playground commerce example (#6577) 2025-03-28 19:19:03 -07:00
Toomas OosaluandGitHub 12bcfc596a insight webhooks docs (#6574) 2025-03-29 11:11:25 +13:00
MananTank a4ff738268 [TOOL-3833] Dashboard: Fix overflow scroll created by sr-only element (#6576) 2025-03-28 19:17:40 +00:00
YashandGitHub b49c191355 Support stylus contracts publish and deploy (#6495) 2025-03-29 07:34:37 +13:00
saminaandGitHub 06f0765665 Update page.mdx (#6572)
Signed-off-by: samina <[email protected]>
2025-03-27 19:54:50 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
12adbd8314 Version Packages (#6570)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-27 17:25:33 -07:00
saminacodes 48e014d426 added FAQ (#6567)
<!--

## 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 new details to the `page.mdx` file, providing guidance on how to enhance the performance of the `Nebula` character and improve its responses.

### Detailed summary
- Added a section on how to make `Nebula` play a certain character better, including:
  - Defining the role clearly.
  - Avoiding personality traits in responses.
  - Setting output expectations.
  - Preloading context for specific domains.
  - Utilizing session handling features.

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

<!-- end pr-codex -->
2025-03-27 23:41:37 +00:00
saminacodes 826748bb5d [Portal] fee-changes (#6573)
CORE-0000

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the fee structure and related information for the `Buy With Crypto` and swap transactions in the `fee-sharing` documentation.

### Detailed summary
- Removed references to a 1% fee and 70% sharing for `Buy With Crypto`.
- Updated the protocol fee to 0.3% for swap transactions.
- Allowed developers to set their own additional fees.
- Clarified fee structure for buying with crypto and swap transactions.

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

<!-- end pr-codex -->
2025-03-27 22:54:25 +00:00
gregfromstl d4f5a9db12 [Portal] Add cross-chain swapping guide and add bridge submodules (#6571)
<!--

## 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 documentation and functionality surrounding the `thirdweb` Universal Bridge, particularly for cross-chain swapping and related features. It updates guides, introduces new functionalities, and improves the clarity of the code.

### Detailed summary
- Updated `yarn` command in `page.mdx` for installing `thirdweb`.
- Renamed the guide from "Build a Custom Experience" to "Build a Custom Onramp Experience" in `sidebar.tsx`.
- Added a new guide for "Cross-Chain Swapping" in `page.mdx`.
- Enhanced tag filtering in `slugs.ts` to include `@bridge`.
- Updated comments in `Buy.ts` and `Sell.ts` to specify `@bridge Buy` and `@bridge Sell`.
- Improved sidebar link generation in `getSidebarLinkGroups.ts` to include `@bridge` links.
- Added comprehensive steps for cross-chain swapping in the new guide, including installation, obtaining quotes, preparing transactions, and checking swap status.

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

<!-- end pr-codex -->
2025-03-27 19:07:18 +00:00
MananTank 3b827ea32c [NEB-148] Do not add active chain to nebula context by default (#6564) 2025-03-27 18:58:20 +00:00
MananTank 2231db7980 [NEB-125] Nebula: Show popular chains at the top of the list (#6565) 2025-03-27 18:49:02 +00:00
MananTank f9872f442c [TOOL-3761] Dashboard: Use new sidebar layout in project pages (#6551)
<!--

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

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

## Notes for the reviewer

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

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on enhancing the layout and styling of the application, particularly around sidebar and project-related components. It introduces new sidebar features, refines the UI, and improves mobile responsiveness.

### Detailed summary
- Removed unused files: `tabs.tsx`, `layout.tsx` in `connect`, and `settings/layout.tsx`.
- Simplified layout in `DeployedContractsPage.tsx` by removing unnecessary divs.
- Added `AnnouncementBanner` to multiple layouts for notifications.
- Introduced `useActiveSidebarLink` for better sidebar link management.
- Enhanced `Sidebar` components with new features and improved styling.
- Added `Header` component in `ProjectOverviewPage` for better title display.
- Updated Tailwind CSS configuration to include sidebar styles.
- Improved mobile responsiveness with `useIsMobile` hook.
- Cleaned up `AnnouncementBanner` logic and structure.

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

<!-- end pr-codex -->
2025-03-27 18:38:45 +00:00
Joe-Thirdweb 473a25237e [Dashboard] reactive(1597) chainlist page update (#6566)
Added custom chain page info for Reactive Network

* Banner image
* CTA image
* About section

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces new assets and functionality for a "Reactive Mainnet" section in the dashboard. It adds images and a call-to-action that promotes Web3 automation features.

### Detailed summary
- Added `reactiveBanner.jpg` and `reactiveCTA.jpg` assets.
- Introduced a new section for "Reactive Mainnet" in `chainMetaRecord`:
  - Set `headerImgUrl` to `reactiveBanner.src`.
  - Added an `about` description for Web3 automation.
  - Configured `cta` with `backgroundImageUrl`, `title`, `buttonLink`, and `buttonText`.

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

<!-- end pr-codex -->
2025-03-27 18:23:19 +00:00
Manan TankandGitHub 2d4c5ff07c Fix "Cannot read properties of undefined (reading 'forwardRef')" error on certain bundler setups (#6569) 2025-03-27 10:41:53 -07:00
saminacodes ab738eb7ba new narrative (#6563)
<!--

## 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 `AnnouncementBanner` and `Banner` components to promote the new `Universal Bridge` feature, enhancing the messaging around payment integration, and refining the content in the documentation to reflect these changes.

### Detailed summary
- Updated `AnnouncementBanner` with new `href`, `label`, and `trackingLabel`.
- Changed `Banner` ID, `text`, and `href` to reflect the `Universal Bridge` launch.
- Revised documentation for webhook events to include `Universal Bridge`.
- Enhanced content in the overview page for `Universal Bridge`.
- Updated descriptions and titles for multiple `FeatureCard` components.
- Removed fee-sharing section and added new integration options for `Universal Bridge`.

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

<!-- end pr-codex -->
2025-03-27 14:55:00 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
8e6651e591 Version Packages (#6546)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-26 11:37:15 -07:00
gregandGitHub e5026a10ab [SDK] Fix: Account for bigint values in engine typed data (#6562) 2025-03-26 11:10:51 -07:00
MananTank 9c1c5f2fc8 [TOOL-3789] Dashboard: Replace simplehash with insight (#6554) 2025-03-26 14:49:27 +00:00
Phillip HoandGitHub 45cdb86911 chore: hide Engine projects from list (#6560) 2025-03-26 22:05:00 +08:00
Joaquim VergesandGitHub fe3351ce79 [TypeDoc] Remove Gist update from workflow (#6559) 2025-03-26 02:45:20 -07:00
Joaquim VergesandGitHub fc657e0b89 [Portal] Redesign documentation homepage with SDK cards and product icons (#6558) 2025-03-26 02:37:35 -07:00
Phillip HoandGitHub cff2481c06 chore: changset for make req optional when calling usage from cloudflare (#6557) 2025-03-26 12:14:05 +08:00
Phillip HoandGitHub e0e31826a8 chore: make req optional when calling usage from cloudflare (#6556) 2025-03-26 11:57:03 +08:00
Joaquim VergesandGitHub fe8b3ce5a5 [Playground] Rename "Fund Wallet" to "Buy Crypto" across pay components (#6555) 2025-03-25 17:48:21 -07:00
kumaryash90 3c120a7584 [SDK] Functions to manage extensions on a dynamic contract (#6550)
<!--

## 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 `thirdweb` library by adding functionality to manage extensions on dynamic contracts, including the ability to install and uninstall extensions.

### Detailed summary
- Introduced `uninstallExtension` function to remove extensions from dynamic contracts.
- Added `installPublishedExtension` function to install extensions with various options.
- Updated `generateExtensionFunctionsFromAbi` to use a specific string format for `functionSelector`.
- Enhanced tests for `uninstallExtension` and `installPublishedExtension` to ensure proper functionality.
- Updated generated files for `addExtension` and `removeExtension` methods with new parameter types and encoding functions.

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

<!-- end pr-codex -->
2025-03-26 00:24:19 +00:00
MananTank b51157c0ff [TOOL-3735] Fix minor typos (#6531)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on fixing typos and improving text clarity in various components of the application.

### Detailed summary
- In `PayConfig.tsx`, corrected "will are" to "are" in the `bottomText` prop.
- In `page.tsx` (commerce), corrected "paymets" to "payments".
- In `page.tsx` (headless), corrected "Seemlessly" to "Seamlessly" in the `description`.
- In `page.tsx` (embed), corrected "Seemlessly" to "Seamlessly" in the `description`.

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

<!-- end pr-codex -->
2025-03-25 22:48:05 +00:00
MananTank f1df19e612 [TOOL-3689] Dashboard: Keep switch in disabled state if field value is empty in Sponsorship rules form (#6535)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on improving the `transformedQueryData` computation by filtering out empty values from the `policy` properties. It enhances data handling for `allowedContractAddresses`, `allowedWallets`, `blockedWallets`, and `allowedChainIds`, ensuring cleaner and more accurate data is returned.

### Detailed summary
- Refactored `transformedQueryData` to filter out empty values from `allowedContractAddresses`, `allowedWallets`, `blockedWallets`, and `allowedChainIds`.
- Simplified return conditions for these properties.
- Changed handling of `allowedChainIds` to return an empty array instead of null to prevent API issues.

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

<!-- end pr-codex -->
2025-03-25 22:39:38 +00:00
MananTank 98781531bb [TOOL-3786] Portal: Improve llms.txt, llms-full.txt (#6532) 2025-03-26 03:59:07 +05:30
saminacodes d3a5ea7833 fixed broken links (#6553)
<!--

## 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 documentation and modifying the sidebar structure in the `Nebula` application, including changes to links and adding a new FAQ section.

### Detailed summary
- Added a new FAQ section in `faqs/page.mdx` about using Nebula in a stateless manner.
- Changed the sidebar entry from `MCP Server` to `MCP Clients` in `sidebar.tsx`.
- Updated the link for `Examples` in `sidebar.tsx` to point to a GitHub repository.

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

<!-- end pr-codex -->
2025-03-25 20:43:24 +00:00
MananTank 1a9448e152 Portal: Fix lint error (#6552)
<!--

## 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 restructuring the `sidebar` object in the `apps/portal/src/app/nebula/sidebar.tsx` file, ensuring proper formatting of the sidebar items and improving the overall readability of the code.

### Detailed summary
- Adjusted the formatting of the `sidebar` entries for `MCP Server` and `Tools`.
- Converted closing brackets from separate lines to inline, enhancing code clarity.
- Ensured consistent object structure for items in the `sidebar`.

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

<!-- end pr-codex -->
2025-03-25 19:36:16 +00:00
saminaandGitHub 2fe16119f5 [Portal] AI updated docs (#6536) 2025-03-25 02:33:38 -07:00
Joaquim VergesandGitHub c549d5934e [Bridge] Skip tests without TW_SECRET_KEY environment variable (#6545) 2025-03-25 02:32:53 -07:00
Joaquim VergesandGitHub dd65506dfb [Playground] Refactor Universal Bridge sidebar links and update order (#6543) 2025-03-24 19:30:22 -07:00
MananTank 26cfd1b30e [TOOL-3682] Dashboard: Change deploy contract status for in-app wallet (#6533) 2025-03-25 01:57:05 +00:00
MananTank 495d8edfa2 [TOOL-3757] Dashboard: Prefill last used project in contract deployment page (#6534) 2025-03-25 01:49:04 +00:00
kumaryash90 88c213b6d5 Handle default extensions (#6497)
<!--

## 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 deployment and transaction management of dynamic contracts within the `thirdweb` framework, adding support for extensions and improving the handling of contract metadata.

### Detailed summary
- Improved return formatting in `bootstrap.ts`.
- Updated `platformFeeBps` handling in `custom-contract.tsx`.
- Added tests for dynamic contract transactions in `get-required-transactions.test.ts`.
- Enhanced logic for deploying extensions in `deploy-published.ts`.
- Introduced new functions for managing dynamic contract extensions in `get-required-transactions.ts`.
- Updated `deploy-marketplace.ts` to include dynamic extensions in marketplace contracts.

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

<!-- end pr-codex -->
2025-03-24 17:21:13 +00:00
Joaquim VergesandGitHub d9091bd2e7 [Dashboard] Disable engineCloud service by default (#6530) 2025-03-24 02:31:52 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
ab7bf1ad1d Version Packages (#6528)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-24 00:28:06 -07:00
Joaquim VergesandGitHub 21389bbef6 [Service-Utils] feat: Add engine-cloud service definition (#6526) 2025-03-24 00:16:18 -07:00
Joaquim VergesandGitHub 4f51201249 [SDK] Update @mobile-wallet-protocol/client to 1.0.0 (#6527) 2025-03-24 00:15:56 -07:00
pink hoodie.andGitHub a0cee488e9 quick get started docs fixes per Jem's revisions (#6525) 2025-03-23 14:46:23 -07:00
Phillip HoandGitHub 0b8907cb7c chore: Add jittered retry on auth call (#6524) 2025-03-23 22:51:59 +08:00
Reza RahemtolaandGitHub 85a78d4460 [Docs] Fix: typo (#6517)
Signed-off-by: Reza Rahemtola <[email protected]>
2025-03-23 17:42:29 +13:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Jake Loo
2a6778eb42 chore(deps): bump the npm_and_yarn group across 5 directories with 1 update (#6522)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jake Loo <[email protected]>
2025-03-23 15:59:58 +13:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
882fd3dcbd Version Packages (#6519)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-22 04:59:50 -07:00
Joaquim VergesandGitHub 744491f853 [SDK] fix: Handle wallet chain mismatch on post onramp flow (#6518) 2025-03-22 04:52:59 -07:00
d3f788471b [Portal] Neb Changes (#6451)
Signed-off-by: samina <[email protected]>
Co-authored-by: Manan Tank <[email protected]>
2025-03-22 00:48:04 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
6db81ef68d Version Packages (#6516)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-22 00:39:59 -07:00
Alberto GarcíaandGitHub f8b2bdc64b [Dashboard] Fix: wrong URL (#6510) 2025-03-22 00:18:06 -07:00
Joaquim VergesandGitHub c407bd10c7 [SDK] fix: Fetch fresh quotes before post onramp step in PayEmbed (#6515) 2025-03-22 00:14:45 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
e540dc11f5 Version Packages (#6508)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-22 15:36:55 +13:00
Joaquim VergesandGitHub 7476b7927d [SDK] fix: Improve swap transaction details UI (#6509) 2025-03-21 02:45:34 -07:00
Joaquim VergesandGitHub ccb641698e [SDK] fix: Force redirect to phantom app on mobile (#6507) 2025-03-20 18:22:05 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
aa9d858f1c Version Packages (#6504)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-20 17:18:57 -07:00
Joaquim VergesandGitHub d8540216f1 [SDK] fix: Pass chainId to internal 1193 provider when connecting (#6506) 2025-03-20 14:52:26 -07:00
gregfromstl 654f8795b4 [SDK] Feature: Adds Universal Bridge (#6464)
<!--

## 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 `Bridge` module to the `thirdweb` SDK, enabling users to perform token buy and sell operations across chains, along with route discovery and transaction status checks.

### Detailed summary
- Added `Bridge` module for Universal Bridge operations.
- Implemented `Bridge.Buy` and `Bridge.Sell` for token transactions.
- Created `quote` and `prepare` functions for both buy and sell operations.
- Introduced `routes` function to discover available bridge routes.
- Added `status` function to check transaction statuses.
- Defined TypeScript types: `Route`, `Status`, `Quote`, `PreparedQuote`.

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

<!-- end pr-codex -->
2025-03-20 21:27:26 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
279fb918f5 chore(deps): bump lucide-react from 0.479.0 to 0.482.0 (#6487)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-20 02:00:22 -07:00
pink hoodie.andGitHub c5cff26faa Docs Revisions: In-app & ecosystem wallet FAQs page, Pay -> Univ Bridge, Wallets Get Started (#6494)
Signed-off-by: pink hoodie. <[email protected]>
2025-03-20 01:59:57 -07:00
Joaquim VergesandGitHub 78901457cf [SDK] fix: Enforce requireApproval option for wallet connection (#6505) 2025-03-20 01:59:33 -07:00
Joaquim VergesandGitHub 47f8cd6d8d [SDk] fix: Improve multistep swap UI in PayEmbed (#6503) 2025-03-20 00:08:12 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
d14a78c06c Version Packages (#6499)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-19 17:38:22 -07:00
Jonas DanielsandGitHub f81fd46e9c add SMS country selection for in-app wallets (#6502) 2025-03-19 17:30:41 -07:00
Joaquim VergesandGitHub d6ce3aedd8 [SDK] fix: Remove fixed gas limit for Saga Mainnet (#6501) 2025-03-19 14:07:22 -07:00
Joaquim VergesandGitHub 6672549edd [SDK] feat: Add fixed gas limit for Saga Mainnet (#6500) 2025-03-19 13:17:56 -07:00
Joaquim VergesandGitHub 639b542d27 [SDK] docs: Add example for shouldUpdateSessionKey function (#6491) 2025-03-19 12:53:43 -07:00
Joaquim VergesandGitHub b5fb5dd0cc [SDK] feat: Support account overrides in engineAccount() (#6496) 2025-03-19 12:53:27 -07:00
Joaquim VergesandGitHub 8b036dcfa7 [SDK] feat: Add Saga Mainnet to gas-free chains (#6498) 2025-03-19 12:52:35 -07:00
Joaquim VergesandGitHub 56bc4ae335 [Playground] feat: Add PayEmbed component playground (#6493) 2025-03-18 18:43:05 -07:00
0xFirekeeper 71f83a884b .NET Docs - Synchronous EngineWallet.Create (#6469)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on modifying the instantiation of the `engineWallet` variable by removing the `await` keyword from the `EngineWallet.Create` method call, indicating a change in how the method is executed.

### Detailed summary
- Removed `await` from the `EngineWallet.Create` method call for `engineWallet`.
- The method now executes synchronously instead of asynchronously.

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

<!-- end pr-codex -->
2025-03-18 19:29:08 +00:00
saminacodes 2d5ff74335 [Portal] added agglayer and erc-20 guide (#6492)
CORE-0000

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the documentation for deploying ERC-20 and Agglayer module contracts, providing step-by-step guides and visual aids for users.

### Detailed summary
- Added a new section for "Tutorials" in the `sidebar` with a link to "Deploy ERC-20 Core."
- Introduced a tutorial on deploying an ERC-20 Core contract, including steps and images.
- Created a guide for adding the Agglayer module to modular contracts, detailing prerequisites and steps.
- Included images for each step in both tutorials for better understanding.

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

<!-- end pr-codex -->
2025-03-18 05:17:21 +00:00
Joaquim VergesandGitHub cbc3bf1d36 [Dashboard] feat: Add allowed operations to partner access control (#6478) 2025-03-17 18:23:52 -07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
20ead112e2 Version Packages (#6460)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-17 16:59:32 -07:00
saminacodes 06d25bb7a9 [Portal] redrafted insight use case page (#6489)
CORE-0000

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the documentation for various blockchain use cases, enhancing clarity and structure. It introduces a new section summarizing insights and specific blueprints for tracking on-chain activities.

### Detailed summary
- Removed detailed explanations for various use cases in sections for `Gaming`, `DeFi`, `NFTs`, and `Wallets`.
- Added a new section titled `Insight Use Cases`.
- Provided a concise table summarizing use cases and corresponding blueprints for tracking blockchain data.

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

<!-- end pr-codex -->
2025-03-17 23:34:29 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
91f8b2dbf2 Version Packages (#6460)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-17 16:34:12 -07:00
Joaquim VergesandGitHub 9901351fb2 [SDK] fix: Handle serialized bigints for packing userops (#6490) 2025-03-17 16:24:41 -07:00
cb86fa2929 [Portal] Update UB Docs (#6482)
Signed-off-by: samina <[email protected]>
Co-authored-by: gregfromstl <[email protected]>
2025-03-17 12:05:46 -07:00
EimanGitHubJoaquim VergesJonas Danielsgraphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
53718208cd updated description of using session keys with Engine (#6474)
Signed-off-by: Eiman <[email protected]>
Signed-off-by: Joaquim Verges <[email protected]>
Co-authored-by: Joaquim Verges <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-03-15 15:25:14 -07:00
Jonas DanielsandGitHub ef2613aca5 Pin GitHub Actions to specific commit hashes (#6479) 2025-03-15 15:06:34 -07:00
saminacodes f2e61b83cb [Portal] updated billing docs (#6477)
CORE-0000

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the billing information section in the documentation, specifically enhancing the instructions for viewing past invoices and modifying the related content.

### Detailed summary
- Updated the navigation steps for viewing past invoices from **Settings > Billing > Manage Billing** to **Settings > Invoices**.
- Changed the instructions for viewing invoices to emphasize breakdown by plan and usage.
- Removed an unused `invoices-settings.png` import.

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

<!-- end pr-codex -->
2025-03-15 00:21:42 +00:00
Jonas DanielsandGitHub ab0d33016d add Stripe integration for team invoices (#6475) 2025-03-14 16:09:35 -07:00
Joe-ThirdwebandGitHub f54a0cb39b [Dashboard] Feature: Adding custom chain info to Hemi Sepolia (#6154)
Signed-off-by: Joe-Thirdweb <[email protected]>
2025-03-14 13:27:16 -07:00
Joaquim VergesandGitHub 90010ee723 [Dashboard] feat: Add access control and server verifier to partner configuration (#6473) 2025-03-13 23:20:00 -07:00
MananTank 269fbe5ecf [TOOL-3691] Dashbaord: Keep project settings submit button always enabled (#6467)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on simplifying the logic for determining if form fields are "dirty" before enabling the save button in the `ProjectGeneralSettingsPage`. It removes checks for dirty states, allowing the save button to be enabled regardless of whether the fields have been modified.

### Detailed summary
- Removed `isNameDirty` check for the `name` field in `ProjectNameSetting`.
- Removed `isDomainsDirty` check for the `domains` field in `AllowedDomainsSetting`.
- Removed `isBundleIdsDirty` check for the `bundleIds` field in `AllowedBundleIDsSetting`.
- Updated save button to always have `disabled: false` for all settings.
- Kept `isPending` functionality unchanged based on `props.isUpdatingProject`.

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

<!-- end pr-codex -->
2025-03-13 22:48:40 +00:00
Jonas DanielsandGitHub 7ed8fc2c43 update dependencies (#6471) 2025-03-13 15:35:06 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9f2f2e794a chore(deps-dev): bump eslint-plugin-storybook from 0.11.3 to 0.11.4 (#6454)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-13 09:29:21 -07:00
Reza RahemtolaGitHubgraphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
eefd2649fd [Docs] Fix: Typo in react/getting-started guide (#6470)
Signed-off-by: Reza Rahemtola <[email protected]>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-03-13 09:24:38 -07:00
MananTank 62de6dd04c [TOOL-3684] Dashboard: Add FTUX in project overview page (#6466)
<!--

## 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 enhancements and fixes across various components in the dashboard application, including new features, property adjustments, and structural changes for improved functionality and usability.

### Detailed summary
- Added `showRangeSelector` property to `AnalyticsHeader`.
- Updated `RotateSecretKeyButton` to be exported.
- Removed `isEnabled` property and replaced it with `isDisabled` across multiple components.
- Introduced `IntegrateAPIKeyCodeTabs` and related sections in `ProjectFTUX`.
- Enhanced project integration sections with new UI components and improved code examples.
- Refactored `NotificationButtonInner` and other components to remove `isEnabled` checks.

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

<!-- end pr-codex -->
2025-03-13 16:10:03 +00:00
MananTank 6fc6c6e681 Portal: Fix knip lint error - Delete AAChainList (#6468) 2025-03-12 19:26:29 +00:00
Joe-ThirdwebandGitHub 58cd79426d [Portal] Fix: Updating AA supported chains (#6459)
Signed-off-by: Joe-Thirdweb <[email protected]>
2025-03-12 00:09:02 -07:00
kumaryash90 185e8f48df Fix cross chain tab event queries (#6465)
<!--

## 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 `blockRange` parameter in the `page.tsx` file to adjust the maximum block number limit for a specific condition.

### Detailed summary
- Updated `blockRange` from `latestBlockNumber < 2000000n` to `latestBlockNumber < 100000n`, changing the maximum limit from `2000000n` to `100000n`.

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

<!-- end pr-codex -->
2025-03-12 05:02:08 +00:00
jnsdls d8865b74ef add copy invite link feature to team invites (#6463) 2025-03-12 01:58:07 +00:00
MananTank 1e8d1e4b52 [NEB-113] Save context chains in local storage (#6452)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on enhancing the `ChatPageContent` component by implementing functionality to save and retrieve the last used chain IDs from local storage, ensuring that the user's selection persists across sessions.

### Detailed summary
- Added `saveLastUsedChainIds` function to store chain IDs in local storage.
- Implemented `getLastUsedChainIds` function to retrieve chain IDs from local storage.
- Updated `updatedContextFilters` to use last used chain IDs if available.
- Ensured wallet address is set only from a connected wallet.

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

<!-- end pr-codex -->
2025-03-11 17:36:20 +00:00
MananTank 91194ce63a [TOOL-3681] Portal: Fix code identation in reference pages (#6461)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on improving code readability and fixing a typo in the console log message, as well as adjusting the CSS class for better text formatting in a component.

### Detailed summary
- In `packages/thirdweb/src/react/core/utils/wallet.ts`, the console log message was changed from `"Walelt name"` to `"wallet name"` for clarity.
- In `apps/portal/src/components/Document/Code.tsx`, the CSS class was modified from `rounded-lg border bg-card font-mono text-sm leading-relaxed` to `whitespace-pre rounded-lg border bg-card font-mono text-sm leading-relaxed` to enhance text formatting.

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

<!-- end pr-codex -->
2025-03-11 17:27:01 +00:00
MananTank a1141f316f [TOOL-3673] Dashboard: change Turnstile widget appearance on missing turnstile token (#6453) 2025-03-11 12:53:44 +00:00
kumaryash90 1273cc7b83 Compute addresses with ref contracts (#5699)
TOOL-2796 (XPROT-934)

## Problem solved

This was an issue when installing modules published with ref contracts. Module installation goes through infra deployment functions which use computed addresses.

SDK should process ref contracts when computing published contract addresses.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the handling of published contract references in the `thirdweb` library, particularly in the deployment process. It introduces a new function to compute addresses for these references and updates existing functions and tests accordingly.

### Detailed summary
- Added `processRefDeployments` function to process contract references in constructor params.
- Introduced `computeRefDeployments` function for computing addresses of published contract references.
- Updated the handling of `salt` values to default to an empty string instead of `"thirdweb"`.
- Modified `computeDeploymentInfoFromMetadata` to use processed constructor parameters.
- Adjusted tests to reflect changes in contract deployment parameters and added new tests for address computation of deployed contracts with references.

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

<!-- end pr-codex -->
2025-03-11 00:51:50 +00:00
Joaquim VergesGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
691110286d Version Packages (#6448)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-10 00:27:12 -07:00
Joaquim VergesandGitHub 4d197a822d [SDK] test: Add factory address to smart wallet test (#6450) 2025-03-10 00:20:16 -07:00
Joaquim VergesandGitHub f655657fce [SDK] fix: EIP1193 provider value filtering in transactions (#6449) 2025-03-10 00:01:00 -07:00
Joaquim VergesandGitHub 109f250068 [SDK] fix: Handle authorizationList tx prop in ethers5 adapter (#6447) 2025-03-09 23:06:21 -07:00
saminaandGitHub f7cf09b3cb [Portal] Account Docs (#6442) 2025-03-09 22:39:20 -07:00
gregfromstl dd06be8def [Dashboard] Feature: update dashboard routes page to handle more routes (#6446)
<!--

## 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 renaming and updating the `RoutesPage` functionality, enhancing token properties, and adjusting UI elements in the routing components for better clarity and usability.

### Detailed summary
- Renamed `ChainListPage` to `RoutesPage`.
- Updated token properties to include `name` and `symbol` for both origin and destination tokens.
- Increased `filters.limit` from `10,000` to `50,000`.
- Modified UI classes for improved styling and consistency.

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

<!-- end pr-codex -->
2025-03-10 04:57:15 +00:00
Joaquim VergesGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
bf8da3588f Version Packages (#6445)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-09 21:45:12 -07:00
gregandGitHub f6cbdb7e30 BREAKING: Removes 3D model support on MediaRenderer (#6443) 2025-03-09 20:46:12 -07:00
Joaquim VergesGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
d350f8cb13 Version Packages (#6440)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-09 18:44:17 -07:00
MananTank 4b200503b8 [TOOL-3639] Dashboard: Pay > Universal Bridge renaming (#6431)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on renaming and rebranding the `Pay` feature to `Universal Bridge`, updating related paths, descriptions, and components accordingly.

### Detailed summary
- Renamed `Pay` to `Universal Bridge` in various components and files.
- Updated links and routes to reflect the new branding.
- Changed descriptions to align with the new feature purpose.
- Replaced `TrackedLinkTW` with `TrackedUnderlineLink` in multiple components.

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

<!-- end pr-codex -->
2025-03-08 15:16:08 +00:00
MananTank f8eb86da47 [TOOL-3640] Dashboard: Add sponsored transactions table in team usage page (#6434)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on enhancing the functionality and user experience of the dashboard by adding new features, improving existing components, and refining data handling, particularly around sponsored transactions and usage analytics.

### Detailed summary
- Added `placeholder` prop to `SingleNetworkSelector`.
- Integrated `SponsoredTransactionsTable` in `AccountAbstractionAnalytics`.
- Enhanced `Usage` component with `client` and `projects` props.
- Improved `ExportToCSVButton` to handle different data types.
- Introduced `SponsoredTransactionsTable` for displaying sponsored transaction data.
- Updated `SponsoredTransactionsTableUI` with pagination and filter options.
- Added error handling and loading states in various components.
- Refactored proxy actions to support optional search parameters and text parsing.
- Introduced `getAuthToken` for authentication in several pages.

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

<!-- end pr-codex -->
2025-03-08 12:57:58 +00:00
gregandGitHub 2ae778ed7f Feature: Adds OTP cooldown timer (#6439) 2025-03-07 15:55:52 -08:00
0xFirekeeper 1c135b0f65 Improve .NET/Unity Documentation (#6438)
Also adds Insight and Engine
Closes TOOL-3570

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing documentation and sidebar navigation for the Thirdweb .NET SDK, introducing new wallet integrations, and updating metadata and descriptions for clarity.

### Detailed summary
- Updated `PrivateKeyWallet` description to markdown format.
- Changed `title` in `nebula/quickstart` metadata.
- Added utility functions description in `utils/page.mdx`.
- Removed `Constants.DECIMALS_18`.
- Added `EngineWallet` documentation with usage example.
- Updated sidebar links for better organization and added new sections.

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

<!-- end pr-codex -->
2025-03-07 22:26:33 +00:00
Joaquim VergesGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
286114ab81 Version Packages (#6435)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-07 14:08:27 -08:00
gregandGitHub cbc4e80330 fix: onramp improvements (#6437) 2025-03-07 13:49:15 -08:00
joaquim-verges efceadd3c1 [SDK] test: Update smart wallet tests to use Arbitrum Sepolia (#6436)
<!--

## 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 test file `smart-wallet-integration-v07.test.ts` to switch the blockchain network from `sepolia` to `arbitrumSepolia`, modifies the contract address, and adds a new expectation for contract deployment verification.

### Detailed summary
- Added import for `arbitrumSepolia`.
- Changed the blockchain network from `sepolia` to `arbitrumSepolia`.
- Updated the contract address from `0xe2cb0eb5147b42095c2FfA6F7ec953bb0bE347D8` to `0x6A7a26c9a595E6893C255C9dF0b593e77518e0c3`.
- Added expectation to verify if the contract is deployed.

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

<!-- end pr-codex -->
2025-03-07 20:37:57 +00:00
Manan TankandGitHub c653d7033d [TOOL-3446] Dashboard: Update account onboarding UI, Add Team onboarding, Open stripe links in new tab (#6354) 2025-03-08 08:40:09 +13:00
YashandGitHub 77029a2e01 Interop module support (#6325) 2025-03-06 17:27:41 -08:00
Joaquim VergesGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
b0b3b7f8f6 Version Packages (#6433)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-06 16:58:32 -08:00
Joaquim VergesandGitHub 617fe68c32 [SDK] feat: Allow custom storage for inApp and ecosystem wallets (#6432) 2025-03-06 16:16:32 -08:00
saminacodes 01d0666208 [Portal] Renames (#6430)
CORE-0000

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the terminology and references in the codebase from `thirdweb Pay` to `thirdweb Universal Bridge`, reflecting a rebranding and restructuring of the payment system.

### Detailed summary
- Updated sidebar links, changing "Testing Pay" to "Developer Mode" and renamed "Guides" to "Tutorials".
- Replaced instances of `thirdweb Pay` with `thirdweb Universal Bridge` in multiple files.
- Updated metadata titles and descriptions across various `.mdx` files.
- Modified FAQs to reflect new terminology for the Universal Bridge service.
- Adjusted documentation on customization and integration guides to use the new branding.

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

<!-- end pr-codex -->
2025-03-06 20:53:34 +00:00
MananTank 621357a5c6 [TOOL-3635] Dashboard: Fix AA factories page crash (#6428) 2025-03-06 16:11:27 +00:00
MananTank 0527ee26b7 [TOOL-3630] Dashboard: Add cancel subscriptions option in delete account flow (#6429) 2025-03-06 15:59:30 +00:00
Waren GonzagaandGitHub 834ed8337b Update page.mdx (#6425)
Signed-off-by: Waren Gonzaga <[email protected]>
2025-03-06 02:44:50 -08:00
Phillip HoandGitHub 11bb4f5fee chore: update mainnet AA message (#6427) 2025-03-06 02:44:28 -08:00
Joaquim VergesGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c547164320 Version Packages (#6417)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-05 17:37:39 -08:00
Joaquim VergesandGitHub 38ae2d3c6b [SDK] expose getUserOpHash utility function (#6424) 2025-03-05 17:08:15 -08:00
jnsdls d75320191d update ecosystem API routes to include team slug (#6422)
Updates ecosystem API endpoints to be scoped under team routes

The ecosystem wallet API endpoints have been updated to be scoped under team routes, ensuring proper data isolation between teams. All ecosystem-related API calls now include the team ID or slug in the path.

Key changes:
- Updated API endpoints from `/v1/ecosystem-wallet/*` to `/v1/teams/{teamIdOrSlug}/ecosystem-wallet/*`
- Added team slug parameter to ecosystem fetch and list operations
- Modified React hooks to include team context in query keys
- Updated component props to pass team information through the component tree

This change ensures ecosystem wallets are properly scoped to their respective teams and maintains data isolation between different team contexts.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates various functions and components within the ecosystem module to include `team_slug` as a parameter. This change enhances the ability to fetch ecosystems and their details specific to a team, improving data handling across the application.

### Detailed summary
- Updated `fetchEcosystem` to accept `team_slug`.
- Modified `fetchEcosystemList` to include `team_slug`.
- Adjusted the `useEcosystemList` hook to utilize `teamIdOrSlug`.
- Enhanced `useEcosystem` to accept `teamIdOrSlug`.
- Updated components to pass `team_slug` where necessary.
- Changed redirect logic to incorporate team-specific ecosystems.

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

<!-- end pr-codex -->
2025-03-05 19:00:54 +00:00
saminacodes 6046d4510d [Portal] Universal Bridge Docs (#6421)
CORE-0000

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on renaming and updating the documentation related to `thirdweb Pay`, transitioning to the `Universal Bridge` terminology. It enhances clarity and alignment with the new features and guides for web3 payments.

### Detailed summary
- Updated titles and descriptions in various `.mdx` files to reflect `Universal Bridge`.
- Changed sidebar navigation names and links to align with `Universal Bridge`.
- Revised content in `page.mdx` files to include new functionalities and guides.
- Added sections on fee sharing and supported chains in the `Overview` documentation.
- Modified FAQ entries to clarify `Universal Bridge` specifics.

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

<!-- end pr-codex -->
2025-03-05 18:28:34 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f1d32261f0 chore(deps-dev): bump eslint-plugin-react-compiler from 19.0.0-beta-e1e972c-20250221 to 19.0.0-beta-40c6c23-20250301 (#6395)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 09:19:51 -08:00
Manan TankandGitHub 04baa74ad7 [TOOL-3610] Dashboard: Fix z-index issue with network selector on chakra modal (#6418) 2025-03-05 07:51:33 -08:00
Phillip HoandGitHub 44bb0034a6 [service-utils] Add pay as a usage source (#6416) 2025-03-05 18:51:39 +08:00
Joaquim VergesGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
cfeba37e40 Version Packages (#6410)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-05 02:15:40 -08:00
Joaquim VergesandGitHub fb446bd099 [SDK] feat: Add transfer success callback (#6415) 2025-03-05 02:09:02 -08:00
jnsdls 24063af948 update engine API routes to include team slug (#6407)
blocked by: https://app.graphite.dev/github/pr/thirdweb-dev/api-server/1437/add-team-engine-management-endpoints

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the handling of `teamIdOrSlug` across various components and utility functions related to engine instances and alerts, ensuring that the correct team context is maintained throughout the application.

### Detailed summary
- Added `teamIdOrSlug` parameter to multiple functions and components.
- Updated API endpoints to include `teamIdOrSlug` in fetch calls.
- Modified components like `EngineAlertsPage`, `EngineSystem`, and `EngineInstancesTable` to pass `teamIdOrSlug`.
- Adjusted data structure handling in API responses to use `result` instead of `data`.

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

<!-- end pr-codex -->
2025-03-05 01:39:38 +00:00
MananTank e2c9d4de34 [TOOL-3606] playground: Remove Ethereum as default selected chain in insight playground (#6413)
<!-- start pr-codex -->

## PR-Codex overview
This PR refines the logic for setting default values in the `values` object within the `blueprint-playground.client.tsx` file. It consolidates the conditions for assigning a default value to parameters.

### Detailed summary
- Removed the specific check for `param.name === "chain"` that assigned a hardcoded value of `"1"`.
- Simplified the condition to check if `param.schema` has a `type` and a `default` property.
- Updated the assignment of `values[param.name]` to use `param.schema.default` when applicable, otherwise setting it to an empty string.

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

<!-- end pr-codex -->
2025-03-05 01:14:05 +00:00
jnsdls 0f6aa6b85d add /faucets to framer rewrites (#6414)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on adding a new route to the `framer-rewrites.js` file for redirecting to the `/faucets` page.

### Detailed summary
- Added the route `"/faucets"` to the array of paths in `apps/dashboard/framer-rewrites.js`.

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

<!-- end pr-codex -->
2025-03-05 01:06:38 +00:00
Joaquim VergesandGitHub 9f1a77f4c4 [SDK] fix: Allow resending OTP on failures (#6412) 2025-03-04 16:58:30 -08:00
Manan TankandGitHub 158a08aafc [TOOL-3598] Dashboard: Add error message in create ecosystem form for image size (#6411) 2025-03-04 12:16:25 -08:00
Phillip HoandGitHub 67354dc848 [service-utils] remove unneeded cf worker imports (#6409) 2025-03-04 21:57:02 +08:00
Joaquim VergesandGitHub 575b7b3a58 [SDK] feat: update zksync chain checks (#6408) 2025-03-04 01:50:43 -08:00
Jonas DanielsandGitHub 35ccf8f2e8 updated dependencies (#6404) 2025-03-03 20:19:54 -08:00
MananTank b5e02a4f10 [TOOl-3590] Dashboard: Remove unused components and stories (#6406)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on removing unused components and simplifying the `CouponCard` functionality by streamlining the handling of coupon applications and eliminating the modal for sharing free wallets.

### Detailed summary
- Deleted multiple files related to `share-free-wallets-modal` and analytics components.
- Removed the dynamic import of `LazyShareFreeWalletsModal` in `CouponCard.tsx`.
- Simplified the `onCouponApplied` function signature by removing the `isFreeWalletsCoupon` parameter.
- Eliminated state management for `showShareModal` in `CouponSection`.

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

<!-- end pr-codex -->
2025-03-04 01:47:57 +00:00
kumaryash90 cb71dd597e Update implementations (#6405)
<!--

## 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 `MarketplaceV3` addresses in the `ZKSYNC_IMPLEMENTATIONS` object for various entries to new contract addresses.

### Detailed summary
- Updated `MarketplaceV3` address from `0xBc02441a36Bb4029Cd191b20243c2e41B862F118` to `0x88e7F2bEf0a23073b7Cc2c79D307Dbd9138Fa79c` at index `324`.
- Updated `MarketplaceV3` address from `0x4027561E163a420c4e5Db46E07EBd581CAa8Bb62` to `0x4b14569c7B79DBe686Ac3Ba5996131E7EDaB7a93` at index `2741`.

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

<!-- end pr-codex -->
2025-03-04 00:03:26 +00:00
Alec AnanianandGitHub 83ec6957f9 playground: fix pay example imports (#6391) 2025-03-03 15:56:36 -08:00
kumaryash90 c0bbc21a84 Update deploy configs (#6299)
TOOL-3478

<!--

## 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 platform fee configuration in the contract deployment process, introducing a new fee constant, and modifying the logic to handle different fee scenarios based on the publisher address.

### Detailed summary
- Added `THIRDWEB_PUBLISHER_ADDRESS` constant in `addresses.ts`.
- Introduced `DEFAULT_FEE_BPS_NEW` constant in `addresses.ts`.
- Updated `PlatformFeeFieldset` to destructure `isMarketplace`.
- Modified `CustomContractForm` to conditionally use `DEFAULT_FEE_BPS_NEW` based on the publisher address.
- Adjusted imports in `custom-contract.tsx` to include new constants.

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

<!-- end pr-codex -->
2025-03-03 22:09:24 +00:00
Jonas DanielsandGitHub 9fcf1b80de skip smart wallet tests without TW_SECRET_KEY (#6403) 2025-03-03 13:43:07 -08:00
Jonas DanielsandGitHub 33ddf56a0a remove secretHash from rotate key response (#6401) 2025-03-03 13:32:38 -08:00
MananTank f9fa5df6b1 [TOOl-3587] Dashboard: Add in-app-wallet login mode (#6400)
<!-- start pr-codex -->

## PR-Codex overview
This PR introduces enhancements to the `LoginAndOnboardingPage` by adding support for an in-app wallet login option and refactoring the login handling logic.

### Detailed summary
- Added `loginWithInAppWallet` prop to `LoginAndOnboardingPage` and `LoginAndOnboardingPageContent`.
- Updated `Page` component to handle `in-app-wallet` search parameter.
- Introduced `inAppWalletLoginOptions` for in-app wallet authentication.
- Modified wallet selection logic based on `loginWithInAppWallet` prop.

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

<!-- end pr-codex -->
2025-03-03 21:17:31 +00:00
MananTank 0d82fc0dd1 [TOOL-3584] Dashboad: Move turnstile widget above connect embed in login page (#6394) 2025-03-03 20:21:59 +00:00
MananTank ef6029b939 [TOOL-3575] Dashboard: Fix /home link 404 after upgrading to nextjs 15.2 (#6393)
<!--

## 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 routing and links in the dashboard application to improve navigation.

### Detailed summary
- In `apps/dashboard/next.config.ts`, the `destination` was changed from `"/"` to `"https://landing.thirdweb.com"`.
- In `apps/dashboard/src/app/components/MobileBurgerMenuButton.tsx`, the `href` for the `Link` component was updated from `"/"` to `"/home"`.

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

<!-- end pr-codex -->
2025-03-03 19:56:41 +00:00
MananTank 004601812a [TOOL-3581] Dashboard: Fix project overview page crashing because of ContractDistributionCard (#6392) 2025-03-03 18:39:45 +00:00
Joaquim VergesGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
cbc0efff31 Version Packages (#6383)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-03 00:45:13 -08:00
gregfromstl 9799b0aae1 [SDK] Fix: add gpt protocol to legacy tx chains (#6390)
<!--

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

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

## Notes for the reviewer

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

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on adding support for the GPT Protocol to chains that only support legacy transactions.

### Detailed summary
- Updated the `.changeset/every-bees-exist.md` file to document the addition of the GPT Protocol.
- Added `1511670449` as the fee data for `GPT Mainnet` in the `packages/thirdweb/src/gas/fee-data.ts` file.

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

<!-- end pr-codex -->
2025-03-03 07:59:11 +00:00
Joaquim VergesandGitHub c3b90a6f1b [SDK] fix: Handle comma decimal input in PayEmbed (#6386) 2025-03-02 16:20:59 -08:00
MananTank 323e9d7a37 [TOOL-3572] Dashboard:Fix theming issue in engine pages (#6389) 2025-03-01 17:58:51 +00:00
Phillip HoandGitHub 80ae08bd5a [service-utils] fix: build error on kafka disconnect() (#6388) 2025-03-01 20:13:20 +08:00
Phillip HoandGitHub 0c0d837f23 [service-utils] fix: crash if Kafka connects more than once (#6387) 2025-03-01 20:08:42 +08:00
Phillip HoandGitHub 879148f37e fix: relative path import for usageV2 (#6385) 2025-03-01 13:47:53 +08:00
Phillip HoandGitHub 25b149d3a9 chore: forward origin header to usage tracker (#6384) 2025-03-01 13:21:30 +08:00
Phillip HoandGitHub 47c9c43a87 [service-utils] trim project ID prefix (#6382) 2025-03-01 10:53:56 +08:00
saminacodes 31dce55025 Portal Changes (#6381)
CORE-0000

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating descriptions and FAQ content related to `Nebula` and `EVM networks` across various markdown files, enhancing clarity and accuracy.

### Detailed summary
- Updated `FeatureCard` description for `Nebula`.
- Revised contract deployment description to specify "any EVM network".
- Changed RPC endpoints description to "every EVM chain".
- Added new FAQ entries and details about `Nebula`'s model and capabilities.
- Reformatted existing FAQs with `<Details>` components for better presentation.

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

<!-- end pr-codex -->
2025-03-01 02:48:06 +00:00
MananTank b535cc816f [TOOL-3571] Dashboard: Remove custom high z-indices (#6380) 2025-03-01 00:11:46 +00:00
catalyst17 c3ded8a37c docs: multichain queries (#6375)
[Portal] Feature: Add multichain queries documentation and examples

## Notes for the reviewer
This PR introduces comprehensive documentation for multichain query functionality in Insight, including detailed examples, response formats, and best practices.

## How to test
- Review the new multichain queries documentation page
- Verify the code examples work with the described response formats
- Check that the sidebar navigation correctly links to the new documentation
- Confirm the authentication methods and chain ID formats are accurately described

---

## PR-Codex overview
This PR enhances the `Insight` docs by documenting multichain queries. It updates various documentation files to reflect new features, examples, and response formats related to multichain functionality.

### Detailed summary
- Added `Multichain Queries` section to the sidebar in `sidebar.tsx`.
- Introduced examples for querying multiple chains in `page.mdx` files.
- Updated chain ID handling examples in `agents-and-llms/page.mdx` and `llmstxt/page.mdx`.
- Detailed multichain response structure added to documentation.
- Explained authentication methods for multichain queries.
- Provided best practices and use cases for multichain queries in `multichain-queries/page.mdx`.
- Updated response format details for multichain queries, including metadata and data array structure.
- Included examples for cross-chain portfolio tracking and activity monitoring.

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

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces support for multichain queries in the `thirdweb Insight` API, enhancing the ability to retrieve data across multiple blockchain networks in a single API call.

### Detailed summary
- Added a new entry for "Multichain Queries" in the `sidebar`.
- Updated examples to illustrate querying multiple chains using query parameters.
- Enhanced documentation for multichain queries with examples and best practices.
- Modified response formats to include `chain_ids` and `limit_per_chain` for multichain queries.
- Updated existing examples to reflect multichain capabilities.

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

<!-- end pr-codex -->
2025-02-28 23:32:19 +00:00
MananTank 257d0bddbf [NEB-85] Portal: Update Nebula sidebar grouping (#6378) 2025-02-28 23:28:11 +00:00
MananTank 5a9a76b7a2 [TOOL-3392] Dashboard: Add project Image field in project settings page (#6379) 2025-02-28 23:19:43 +00:00
Joaquim VergesGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
e772581044 Version Packages (#6367)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-28 13:51:45 -08:00
Joaquim VergesandGitHub 5625ff1f80 [SDK] expose estimateUserOpGasCost (#6370) 2025-02-28 13:46:39 -08:00
MananTank 062504f463 [TOOL-3569] Dashboard: Add acount.id to posthog identify calls (#6377) 2025-02-28 21:13:58 +00:00
MananTank 853ed473c6 [TOOL-3525] Dashboard: Add delete account in account settings page (#6339)
<!--

## 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 management of teams and accounts in the application. It introduces a new method to fetch the default team and updates various components to utilize this information, improving user experience and functionality.

### Detailed summary
- Added `getDefaultTeam` function to fetch the default team using an API call.
- Updated `TeamRootPage` to use `getDefaultTeam` instead of fetching all teams.
- Modified middleware to redirect using the default team.
- Refactored account settings page to include `defaultTeamSlug` and `defaultTeamName`.
- Enhanced `DangerSettingCard` to manage confirmation dialog state.
- Updated `AccountSettingsPageUI` to handle account deletion with relevant alerts for errors.
- Added UI components for handling account deletion status responses in the storybook.

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

<!-- end pr-codex -->
2025-02-28 21:05:11 +00:00
MananTank f5765819d7 [TOOL-3566] Dashboard: Add static.reo.dev in Next.js CSP settings (#6374)
<!-- start pr-codex -->

## PR-Codex overview
This PR updates the Content Security Policy (CSP) in the `next.config.ts` file to include an additional source for `script-src`.

### Detailed summary
- Added `static.reo.dev` to the `script-src` directive in the CSP.

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

<!-- end pr-codex -->
2025-02-28 20:57:16 +00:00
jnsdls f4ca73c4bf add /storage to framer rewrites (#6373)
closes: CORE-758

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the `framer-rewrites.js` file to include a new route related to storage within the application's routing configuration.

### Detailed summary
- Added a new route `"/storage"` to the existing routes in `framer-rewrites.js`.

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

<!-- end pr-codex -->
2025-02-28 20:23:53 +00:00
MananTank 1f517fa10d [NEB-110] Dashboard: Add Playground and API references links (#6376) 2025-02-28 20:16:25 +00:00
MananTank 8887e46a00 [TOOL-3562] Portal: Add llms.txt and llms-full.txt generation script (#6372)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on enhancing the layout components and search data extraction in the application. It introduces new files for LLM content and modifies existing components to include additional properties for better indexing and rendering.

### Detailed summary
- Added `public/llms.txt` and `public/llms-full.txt` files.
- Removed `data-noindex` from `<div>` in multiple layout files.
- Added `noLLM` property to `DocLayout` in various components.
- Updated `extractSearchData` to return LLM content.
- Modified `extractContent` to handle new LLM data.
- Adjusted `ArticleCard` and `Code` components to include `data-noindex`.
- 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 -->
2025-02-28 19:13:59 +00:00
joaquim-verges 56301bc50e [thirdweb] deps: Upgrade viem to 2.23.5 (#6336)
<!--

## 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 several dependencies, particularly the `viem` package, and makes adjustments to the `package.json` and lock files to reflect these changes. It also increases the size limit in the `.size-limit.json` file.

### Detailed summary
- Updated `viem` version from `2.22.17` to `2.23.5` in `package.json` and `pnpm-lock.yaml`.
- Increased size limit in `packages/thirdweb/.size-limit.json` from `130 kB` to `131 kB`.
- Adjusted various dependencies in `pnpm-lock.yaml` to include `@swc/[email protected]`.

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

<!-- end pr-codex -->
2025-02-28 10:31:01 +00:00
MananTank 36b4ea5260 [TOOL-3556] Dashboard: Add Notifications, Revamp Team Overview page (#6368)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on enhancing the notification system in the dashboard by integrating changelog and inbox notifications, improving UI components, and updating dependencies.

### Detailed summary
- Added `getChangelogNotifications`, `getInboxNotifications`, and `markNotificationAsRead` functions.
- Integrated notifications in `AccountHeaderUI`, `TeamHeader`, and `SecondaryNav`.
- Updated styles for various components.
- Replaced `Changelog` component with a new layout in `TeamProjectsPage`.
- Enhanced notification handling using `idb-keyval` for local storage.

> The following files were skipped due to too many changes: `pnpm-lock.yaml`

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

<!-- end pr-codex -->
2025-02-28 01:19:00 +00:00
joaquim-verges e1f73c8286 [SDK] chore: Re-enable flaky smart wallet tests (#6369)
<!--

## 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 addressing flaky tests in the `thirdweb` codebase by enabling previously skipped tests related to `SmartWallet` functionality, ensuring they are executed as part of the test suite.

### Detailed summary
- Removed `.github/workflows/issue.yml`.
- Enabled `describe` and `test` blocks previously marked as skipped in:
  - `smart-wallet-modular.test.ts`
  - `verify-typed-data.test.ts`
  - `smart-wallet-integration-v07.test.ts`
- Adjusted test configurations by removing `FIMXE` comments and retry limits.

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

<!-- end pr-codex -->
2025-02-28 01:05:01 +00:00
Joaquim VergesandGitHub c4e7f3b344 [SDK] chore: fix all await expect tests (#6359) 2025-02-27 16:45:52 -08:00
Joaquim VergesandGitHub 6235fe76d3 [SDK] fix: Initialize wallet connection status as unknown (#6360) 2025-02-27 16:19:23 -08:00
Jonas DanielsandGitHub d8e9245ecf upgrade Next.js to 15.2.0 (#6365) 2025-02-27 15:56:46 -08:00
806 changed files with 33282 additions and 17839 deletions
+4 -4
View File
@@ -6,15 +6,15 @@ runs:
steps:
# we use bun for some test suites
- name: Setup bun
uses: oven-sh/setup-bun@v1
uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2.0.1
with:
bun-version: 1.0.35
# pnpm for our dependencies
- uses: pnpm/action-setup@v3
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
with:
version: 9
version: 9.11.0
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
with:
node-version: 20
check-latest: true
+10 -10
View File
@@ -26,7 +26,7 @@ jobs:
steps:
- name: Optimize CI
id: check_skip
uses: withgraphite/graphite-ci-action@main
uses: withgraphite/graphite-ci-action@9cb601a55e114099561b6d755505de377d45db40 # v0.0.9 ("main")
with:
graphite_token: ${{ secrets.GRAPHITE_OMTIMIZE_TOKEN }}
@@ -38,7 +38,7 @@ jobs:
name: Build Packages
steps:
- name: Check out the code
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup & Install
uses: ./.github/composite-actions/install
@@ -54,13 +54,13 @@ jobs:
runs-on: ubuntu-latest-8
steps:
- name: Check out the code
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup & Install
uses: ./.github/composite-actions/install
- name: Setup Biome
uses: biomejs/setup-biome@v2
uses: biomejs/setup-biome@c016c38f26f2c4a6eb3662679143614a254263fd # v2.3.0
with:
version: latest
@@ -74,13 +74,13 @@ jobs:
runs-on: ubuntu-latest-8
steps:
- name: Check out the code
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup & Install
uses: ./.github/composite-actions/install
- name: Set up foundry
uses: foundry-rs/foundry-toolchain@v1
uses: foundry-rs/foundry-toolchain@de808b1eea699e761c404bda44ba8f21aba30b2c # v1.3.1
with:
cache: false
version: nightly-c4a984fbf2c48b793c8cd53af84f56009dd1070c
@@ -88,7 +88,7 @@ jobs:
- run: pnpm test
- name: Code Coverage
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0
with:
directory: packages/
flags: packages
@@ -106,7 +106,7 @@ jobs:
bundler: [vite, webpack, esbuild]
steps:
- name: Check out the code
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup & Install
uses: ./.github/composite-actions/install
@@ -172,13 +172,13 @@ jobs:
runs-on: ubuntu-latest-8
steps:
- name: Check out the code
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup & Install
uses: ./.github/composite-actions/install
- name: Report bundle size
uses: andresz1/size-limit-action@master
uses: andresz1/size-limit-action@94bc357df29c36c8f8d50ea497c3e225c3c95d1d # v1.8.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
package_manager: pnpm
+1 -1
View File
@@ -16,4 +16,4 @@ jobs:
github.event.pull_request.author_association == 'COLLABORATOR' ||
github.event.pull_request.author_association == 'CONTRIBUTOR'
steps:
- uses: toshimaru/[email protected]
- uses: toshimaru/auto-author-assign@16f0022cf3d7970c106d8d1105f75a1165edb516 # v2.1.1
+4 -4
View File
@@ -42,11 +42,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -59,7 +59,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -72,4 +72,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
-49
View File
@@ -1,49 +0,0 @@
name: Linked Issue
on:
pull_request:
types: [opened, edited, ready_for_review]
env:
VALID_ISSUE_PREFIXES: "CORE|TOOL|NEB|INFRA"
jobs:
linear:
name: Linear
runs-on: ubuntu-latest
steps:
- name: Check for linked issue
uses: actions/github-script@v7
with:
script: |
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
// Check if contributor is external
const isInternalContributor = ['MEMBER', 'OWNER', 'COLLABORATOR'].includes(
context.payload.pull_request.author_association
);
// Automatically pass for external contributors
if (!isInternalContributor) {
console.log('External contributor detected - automatically passing check');
return;
}
const body = pr.data.body || '';
const branchName = pr.data.head.ref;
const issueRegex = new RegExp(`(${process.env.VALID_ISSUE_PREFIXES})-\\d+`, 'i');
const branchIssueRegex = new RegExp(`(${process.env.VALID_ISSUE_PREFIXES.toLowerCase()})-\\d+`, 'i');
if (!issueRegex.test(body) && !branchIssueRegex.test(branchName)) {
core.setFailed(
`No valid issue reference found. PR body or branch name must contain an issue ID with one of these prefixes: ${process.env.VALID_ISSUE_PREFIXES}`
);
return;
}
const matches = body.match(issueRegex) || branchName.match(branchIssueRegex);
console.log(`Found issue reference: ${matches[0]}`);
+1 -1
View File
@@ -17,6 +17,6 @@ jobs:
pull-requests: write
steps:
- uses: actions/labeler@v5
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout branch
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
# Do not use the GITHUB_TOKEN by default
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout branch
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
# Do not use the GITHUB_TOKEN by default
@@ -36,7 +36,7 @@ jobs:
- name: Create release Pull Request or publish to NPM
id: changesets
uses: changesets/action@v1
uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc #v1.4.0
with:
publish: pnpm release
version: pnpm version-packages
+1 -1
View File
@@ -7,7 +7,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v.9.1.0
with:
stale-issue-message: 'This issue has been inactive for 7 days. It is now marked as stale and will be closed in 2 days if no further activity occurs.'
stale-pr-message: 'This PR has been inactive for 7 days. It is now marked as stale and will be closed in 2 days if no further activity occurs.'
+1 -21
View File
@@ -21,30 +21,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install
uses: ./.github/composite-actions/install
- name: Run TypeDoc
run: pnpm typedoc
- name: Update Gist
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GIST_TOKEN }}
script: |
const fs = require('fs');
const content = fs.readFileSync('./packages/thirdweb/typedoc/parsed.json', 'utf8');
const gistId = '678fe1f331a01270bb002fee660f285d';
await github.rest.gists.update({
gist_id: gistId,
files: {
'data.json': {
content: content
}
}
});
console.log(`Permalink: https://gist.githubusercontent.com/raw/${gistId}/data.json`);
+3
View File
@@ -102,3 +102,6 @@ ANALYTICS_SERVICE_URL=""
# Required for Nebula Chat
NEXT_PUBLIC_NEBULA_URL=""
# required for billing parts of the dashboard (team -> settings -> billing / invoices)
STRIPE_SECRET_KEY=""
+3
View File
@@ -27,5 +27,8 @@ const config: StorybookConfig = {
},
},
staticDirs: ["../public"],
features: {
experimentalRSC: true,
},
};
export default config;
+33 -4
View File
@@ -1,5 +1,5 @@
import type { Preview } from "@storybook/react";
import "@/styles/globals.css";
import "../src/global.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { MoonIcon, SunIcon } from "lucide-react";
import { ThemeProvider, useTheme } from "next-themes";
@@ -7,6 +7,7 @@ import { Inter as interFont } from "next/font/google";
// biome-ignore lint/style/useImportType: <explanation>
import React from "react";
import { useEffect } from "react";
import { Toaster } from "sonner";
import { Button } from "../src/@/components/ui/button";
const queryClient = new QueryClient();
@@ -16,8 +17,30 @@ const fontSans = interFont({
variable: "--font-sans",
});
const customViewports = {
xs: {
// Regular sized phones (iphone 15 / 15 pro)
name: "iPhone",
styles: {
width: "390px",
height: "844px",
},
},
sm: {
// Larger phones (iphone 15 plus / 15 pro max)
name: "iPhone Plus",
styles: {
width: "430px",
height: "932px",
},
},
};
const preview: Preview = {
parameters: {
viewport: {
viewports: customViewports,
},
controls: {
matchers: {
color: /(background|color)$/i,
@@ -57,13 +80,13 @@ function StoryLayout(props: {
return (
<QueryClientProvider client={queryClient}>
<div className="flex min-h-screen min-w-0 flex-col bg-background text-foreground">
<div className="flex min-h-dvh min-w-0 flex-col bg-background text-foreground">
<div className="flex justify-end gap-2 border-b p-4">
<Button
onClick={() => setTheme("dark")}
size="sm"
variant={theme === "dark" ? "default" : "outline"}
className="h-auto w-auto rounded-full p-2"
className="h-auto w-auto shrink-0 rounded-full p-2"
>
<MoonIcon className="size-4" />
</Button>
@@ -72,14 +95,20 @@ function StoryLayout(props: {
onClick={() => setTheme("light")}
size="sm"
variant={theme === "light" ? "default" : "outline"}
className="h-auto w-auto rounded-full p-2"
className="h-auto w-auto shrink-0 rounded-full p-2"
>
<SunIcon className="size-4" />
</Button>
</div>
<div className="flex min-w-0 grow flex-col">{props.children}</div>
<ToasterSetup />
</div>
</QueryClientProvider>
);
}
function ToasterSetup() {
const { theme } = useTheme();
return <Toaster richColors theme={theme === "light" ? "light" : "dark"} />;
}
+5 -2
View File
@@ -13,6 +13,8 @@ module.exports = [
"/connect/universal-bridge",
"/connect/auth",
"/connect/in-app-wallets",
// -- storage
"/storage",
// -- nebula
"/nebula",
// --insight
@@ -47,10 +49,11 @@ module.exports = [
"/templates/:template_slug",
// -- learn --
"/learn",
"/learn/tutorials",
"/learn/tutorials/:tutorial_slug",
"/learn/guides",
"/learn/guides/:guide_slug",
"/learn/courses",
"/learn/glossary",
"/learn/glossary/:glossary_slug",
// -- faucets --
"/faucets",
];
+2 -2
View File
@@ -12,7 +12,7 @@ const ContentSecurityPolicy = `
style-src 'self' 'unsafe-inline' vercel.live;
font-src 'self' vercel.live assets.vercel.com framerusercontent.com fonts.gstatic.com;
frame-src * data:;
script-src 'self' 'unsafe-eval' 'unsafe-inline' 'wasm-unsafe-eval' 'inline-speculation-rules' *.thirdweb.com *.thirdweb-dev.com vercel.live js.stripe.com framerusercontent.com events.framer.com challenges.cloudflare.com;
script-src 'self' 'unsafe-eval' 'unsafe-inline' 'wasm-unsafe-eval' 'inline-speculation-rules' *.thirdweb.com *.thirdweb-dev.com vercel.live js.stripe.com framerusercontent.com events.framer.com challenges.cloudflare.com static.reo.dev;
connect-src * data: blob:;
worker-src 'self' blob:;
block-all-mixed-content;
@@ -153,7 +153,7 @@ const baseNextConfig: NextConfig = {
// re-write /home to / (this is so that logged in users will be able to go to /home and NOT be redirected to the logged in app)
{
source: "/home",
destination: "/",
destination: "https://landing.thirdweb.com",
},
...FRAMER_PATHS.map((path) => ({
source: path,
+34 -32
View File
@@ -4,7 +4,7 @@
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "next dev --turbo",
"dev": "next dev --turbopack",
"build": "NODE_OPTIONS=--max-old-space-size=6144 next build",
"start": "next start",
"format": "biome format ./src --write",
@@ -45,9 +45,9 @@
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tooltip": "1.1.8",
"@sentry/nextjs": "9.2.0",
"@shazow/whatsabi": "^0.19.0",
"@tanstack/react-query": "5.66.9",
"@sentry/nextjs": "9.5.0",
"@shazow/whatsabi": "0.20.0",
"@tanstack/react-query": "5.67.3",
"@tanstack/react-table": "^8.21.2",
"@thirdweb-dev/service-utils": "workspace:*",
"@vercel/functions": "2.0.0",
@@ -61,17 +61,18 @@
"date-fns": "4.1.0",
"fetch-event-stream": "0.1.5",
"flat": "^6.0.1",
"framer-motion": "12.4.7",
"framer-motion": "12.5.0",
"fuse.js": "7.1.0",
"idb-keyval": "^6.2.1",
"input-otp": "^1.4.1",
"ioredis": "^5.5.0",
"ioredis": "^5.6.0",
"ipaddr.js": "^2.2.0",
"lucide-react": "0.476.0",
"next": "15.1.7",
"lucide-react": "0.483.0",
"next": "15.2.3",
"next-plausible": "^3.12.4",
"next-themes": "^0.4.4",
"next-themes": "^0.4.6",
"nextjs-toploader": "^1.6.12",
"nuqs": "^2.4.0",
"nuqs": "^2.4.1",
"p-limit": "^6.2.0",
"papaparse": "^5.5.2",
"pluralize": "^8.0.0",
@@ -93,7 +94,8 @@
"shiki": "1.27.0",
"sonner": "2.0.1",
"spdx-correct": "^3.2.0",
"swagger-ui-react": "^5.19.0",
"stripe": "17.7.0",
"swagger-ui-react": "^5.20.1",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"thirdweb": "workspace:*",
@@ -103,21 +105,21 @@
},
"devDependencies": {
"@chakra-ui/cli": "^2.4.1",
"@chromatic-com/storybook": "3.2.4",
"@next/bundle-analyzer": "15.1.7",
"@next/eslint-plugin-next": "15.1.7",
"@playwright/test": "1.50.1",
"@storybook/addon-essentials": "8.6.0",
"@storybook/addon-interactions": "8.6.0",
"@storybook/addon-links": "8.6.0",
"@storybook/addon-onboarding": "8.6.0",
"@storybook/addon-viewport": "8.6.0",
"@storybook/blocks": "8.6.0",
"@storybook/nextjs": "8.6.0",
"@storybook/react": "8.6.0",
"@storybook/test": "8.6.0",
"@chromatic-com/storybook": "3.2.6",
"@next/bundle-analyzer": "15.2.3",
"@next/eslint-plugin-next": "15.2.3",
"@playwright/test": "1.51.1",
"@storybook/addon-essentials": "8.6.4",
"@storybook/addon-interactions": "8.6.4",
"@storybook/addon-links": "8.6.4",
"@storybook/addon-onboarding": "8.6.4",
"@storybook/addon-viewport": "8.6.4",
"@storybook/blocks": "8.6.4",
"@storybook/nextjs": "8.6.4",
"@storybook/react": "8.6.4",
"@storybook/test": "8.6.4",
"@types/color": "4.2.0",
"@types/node": "22.13.5",
"@types/node": "22.13.10",
"@types/papaparse": "^5.3.15",
"@types/pluralize": "^0.0.33",
"@types/qrcode": "^1.5.5",
@@ -128,17 +130,17 @@
"@types/swagger-ui-react": "^4.19.0",
"@typescript-eslint/eslint-plugin": "7.14.1",
"@typescript-eslint/parser": "7.14.1",
"autoprefixer": "^10.4.19",
"checkly": "^4.19.1",
"autoprefixer": "^10.4.21",
"checkly": "5.0.1",
"eslint": "8.57.0",
"eslint-config-biome": "1.9.4",
"eslint-plugin-react-compiler": "19.0.0-beta-e1e972c-20250221",
"eslint-plugin-storybook": "0.11.3",
"knip": "5.45.0",
"eslint-plugin-react-compiler": "19.0.0-beta-40c6c23-20250301",
"eslint-plugin-storybook": "0.11.6",
"knip": "5.46.0",
"next-sitemap": "^4.2.3",
"postcss": "8.5.3",
"storybook": "8.6.0",
"storybook": "8.6.4",
"tailwindcss": "3.4.17",
"typescript": "5.7.3"
"typescript": "5.8.2"
}
}
+20
View File
@@ -354,6 +354,26 @@ async function redirects() {
destination: "/",
permanent: false,
},
// pay > universal-bridge redirect
{
source: "/team/:team_slug/:project_slug/connect/pay/:path*",
destination:
"/team/:team_slug/:project_slug/connect/universal-bridge/:path*",
permanent: false,
},
// all /learn/tutorials (and sub-routes) -> /learn/guides
{
source: "/learn/tutorials/:path*",
destination: "/learn/guides/:path*",
permanent: false,
},
{
source: "/learn/tutorials",
destination: "/learn/guides",
permanent: false,
},
...legacyDashboardToTeamRedirects,
];
}
+20 -16
View File
@@ -1,21 +1,20 @@
"use server";
import "server-only";
import { API_SERVER_URL } from "@/constants/env";
import { redirect } from "next/navigation";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import type { ProductSKU } from "../lib/billing";
export type RedirectCheckoutOptions = {
export type GetBillingCheckoutUrlOptions = {
teamSlug: string;
sku: ProductSKU;
redirectUrl: string;
metadata?: Record<string, string>;
};
export async function redirectToCheckout(
options: RedirectCheckoutOptions,
): Promise<{ status: number }> {
export async function getBillingCheckoutUrl(
options: GetBillingCheckoutUrlOptions,
): Promise<{ status: number; url?: string }> {
if (!options.teamSlug) {
return {
status: 400,
@@ -49,6 +48,7 @@ export async function redirectToCheckout(
status: res.status,
};
}
const json = await res.json();
if (!json.result) {
return {
@@ -56,20 +56,22 @@ export async function redirectToCheckout(
};
}
// redirect to the stripe checkout session
redirect(json.result);
return {
status: 200,
url: json.result as string,
};
}
export type RedirectBillingCheckoutAction = typeof redirectToCheckout;
export type GetBillingCheckoutUrlAction = typeof getBillingCheckoutUrl;
export type BillingPortalOptions = {
export type GetBillingPortalUrlOptions = {
teamSlug: string | undefined;
redirectUrl: string;
};
export async function redirectToBillingPortal(
options: BillingPortalOptions,
): Promise<{ status: number }> {
export async function getBillingPortalUrl(
options: GetBillingPortalUrlOptions,
): Promise<{ status: number; url?: string }> {
if (!options.teamSlug) {
return {
status: 400,
@@ -110,8 +112,10 @@ export async function redirectToBillingPortal(
};
}
// redirect to the stripe billing portal
redirect(json.result);
return {
status: 200,
url: json.result as string,
};
}
export type BillingBillingPortalAction = typeof redirectToBillingPortal;
export type GetBillingPortalUrlAction = typeof getBillingPortalUrl;
+120 -31
View File
@@ -10,12 +10,9 @@ import {
isMoralisSupported,
transformMoralisResponseToNFT,
} from "lib/wallet/nfts/moralis";
import {
generateSimpleHashUrl,
isSimpleHashSupported,
transformSimpleHashResponseToNFT,
} from "lib/wallet/nfts/simpleHash";
import type { WalletNFT } from "lib/wallet/nfts/types";
import { getVercelEnv } from "../../lib/vercel-utils";
import { DASHBOARD_THIRDWEB_CLIENT_ID } from "../constants/env";
type WalletNFTApiReturn =
| { result: WalletNFT[]; error?: undefined }
@@ -24,40 +21,20 @@ type WalletNFTApiReturn =
export async function getWalletNFTs(params: {
chainId: number;
owner: string;
isInsightSupported: boolean;
}): Promise<WalletNFTApiReturn> {
const { chainId, owner } = params;
const supportedChainSlug = await isSimpleHashSupported(chainId);
if (supportedChainSlug && process.env.SIMPLEHASH_API_KEY) {
const url = generateSimpleHashUrl({ chainSlug: supportedChainSlug, owner });
if (params.isInsightSupported) {
const response = await getWalletNFTsFromInsight({ chainId, owner });
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-KEY": process.env.SIMPLEHASH_API_KEY,
},
next: {
revalidate: 10, // cache for 10 seconds
},
});
if (response.status >= 400) {
if (!response.ok) {
return {
error: response.statusText,
error: response.error,
};
}
try {
const parsedResponse = await response.json();
const result = await transformSimpleHashResponseToNFT(
parsedResponse,
owner,
);
return { result };
} catch {
return { error: "error parsing response" };
}
return { result: response.data };
}
if (isAlchemySupported(chainId)) {
@@ -115,3 +92,115 @@ export async function getWalletNFTs(params: {
return { error: "unsupported chain" };
}
type OwnedNFTInsightResponse = {
name: string;
description: string;
image_url: string;
background_color: string;
external_url: string;
metadata_url: string;
extra_metadata: {
customImage?: string;
customAnimationUrl?: string;
animation_original_url?: string;
image_original_url?: string;
};
collection: {
name: string;
description: string;
extra_metadata: Record<string, string>;
};
contract: {
chain_id: number;
address: string;
type: "erc1155" | "erc721";
name: string;
};
owner_addresses: string[];
token_id: string;
balance: string;
token_type: "erc1155" | "erc721";
};
async function getWalletNFTsFromInsight(params: {
chainId: number;
owner: string;
}): Promise<
| {
data: WalletNFT[];
ok: true;
}
| {
ok: false;
error: string;
}
> {
const { chainId, owner } = params;
const thirdwebDomain =
getVercelEnv() === "production" ? "thirdweb" : "thirdweb-dev";
const url = new URL(`https://insight.${thirdwebDomain}.com/v1/nfts`);
url.searchParams.append("chain", chainId.toString());
url.searchParams.append("limit", "10");
url.searchParams.append("owner_address", owner);
const response = await fetch(url, {
headers: {
"x-client-id": DASHBOARD_THIRDWEB_CLIENT_ID,
},
});
if (!response.ok) {
const errorMessage = await response.text();
return {
ok: false,
error: errorMessage,
};
}
const nftsResponse = (await response.json()) as {
data: OwnedNFTInsightResponse[];
};
const isDev = getVercelEnv() !== "production";
// NOTE: ipfscdn.io/ to thirdwebstorage-dev.com/ replacement is temporary
// This should be fixed in the insight dev endpoint
const walletNFTs = nftsResponse.data.map((nft) => {
const walletNFT: WalletNFT = {
id: nft.token_id,
contractAddress: nft.contract.address,
metadata: {
uri: isDev
? nft.metadata_url.replace("ipfscdn.io/", "thirdwebstorage-dev.com/")
: nft.metadata_url,
name: nft.name,
description: nft.description,
image: isDev
? nft.image_url.replace("ipfscdn.io/", "thirdwebstorage-dev.com/")
: nft.image_url,
animation_url: isDev
? nft.extra_metadata.animation_original_url?.replace(
"ipfscdn.io/",
"thirdwebstorage-dev.com/",
)
: nft.extra_metadata.animation_original_url,
external_url: nft.external_url,
background_color: nft.background_color,
},
owner: params.owner,
tokenURI: nft.metadata_url,
type: nft.token_type === "erc721" ? "ERC721" : "ERC1155",
supply: nft.balance,
};
return walletNFT;
});
return {
data: walletNFTs,
ok: true,
};
}
+15 -11
View File
@@ -5,13 +5,14 @@ import { API_SERVER_URL } from "../constants/env";
type ProxyActionParams = {
pathname: string;
searchParams?: Record<string, string>;
searchParams?: Record<string, string | undefined>;
method: "GET" | "POST" | "PUT" | "DELETE";
body?: string;
headers?: Record<string, string>;
parseAsText?: boolean;
};
type ProxyActionResult<T extends object> =
type ProxyActionResult<T> =
| {
status: number;
ok: true;
@@ -23,7 +24,7 @@ type ProxyActionResult<T extends object> =
error: string;
};
async function proxy<T extends object>(
async function proxy<T>(
baseUrl: string,
params: ProxyActionParams,
): Promise<ProxyActionResult<T>> {
@@ -34,7 +35,10 @@ async function proxy<T extends object>(
url.pathname = params.pathname;
if (params.searchParams) {
for (const key in params.searchParams) {
url.searchParams.append(key, params.searchParams[key] as string);
const value = params.searchParams[key];
if (value) {
url.searchParams.append(key, value);
}
}
}
@@ -67,19 +71,15 @@ async function proxy<T extends object>(
return {
status: res.status,
ok: true,
data: await res.json(),
data: params.parseAsText ? await res.text() : await res.json(),
};
}
export async function apiServerProxy<T extends object = object>(
params: ProxyActionParams,
) {
export async function apiServerProxy<T>(params: ProxyActionParams) {
return proxy<T>(API_SERVER_URL, params);
}
export async function payServerProxy<T extends object = object>(
params: ProxyActionParams,
) {
export async function payServerProxy<T>(params: ProxyActionParams) {
return proxy<T>(
process.env.NEXT_PUBLIC_PAY_URL
? `https://${process.env.NEXT_PUBLIC_PAY_URL}`
@@ -87,3 +87,7 @@ export async function payServerProxy<T extends object = object>(
params,
);
}
export async function analyticsServerProxy<T>(params: ProxyActionParams) {
return proxy<T>(process.env.ANALYTICS_SERVICE_URL || "", params);
}
@@ -0,0 +1,60 @@
import "server-only";
import Stripe from "stripe";
import type { Team } from "../api/team";
let existingStripe: Stripe | undefined;
function getStripe() {
if (!existingStripe) {
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY;
if (!STRIPE_SECRET_KEY) {
throw new Error("STRIPE_SECRET_KEY is not set");
}
existingStripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: "2025-02-24.acacia",
});
}
return existingStripe;
}
export async function getTeamInvoices(
team: Team,
options?: { cursor?: string },
) {
try {
const customerId = team.stripeCustomerId;
if (!customerId) {
throw new Error("No customer ID found");
}
// Get the list of invoices for the customer
const invoices = await getStripe().invoices.list({
customer: customerId,
limit: 10,
starting_after: options?.cursor,
});
return invoices;
} catch (error) {
console.error("Error fetching billing history:", error);
// If the error is that the customer doesn't exist, return an empty array
// instead of throwing an error
if (
error instanceof Stripe.errors.StripeError &&
error.message.includes("No such customer")
) {
return {
data: [],
has_more: false,
};
}
throw new Error("Failed to fetch billing history");
}
}
+53
View File
@@ -0,0 +1,53 @@
import { API_SERVER_URL, THIRDWEB_API_SECRET } from "../constants/env";
export type SMSCountryTiers = {
tier1: string[];
tier2: string[];
tier3: string[];
tier4: string[];
tier5: string[];
};
export async function getSMSCountryTiers() {
if (!THIRDWEB_API_SECRET) {
throw new Error("API_SERVER_SECRET is not set");
}
const res = await fetch(`${API_SERVER_URL}/v1/sms/list-country-tiers`, {
headers: {
"Content-Type": "application/json",
"x-service-api-key": THIRDWEB_API_SECRET,
},
next: {
revalidate: 15 * 60, //15 minutes
},
});
if (!res.ok) {
console.error(
"Failed to fetch sms country tiers",
res.status,
res.statusText,
);
res.body?.cancel();
return {
tier1: [],
tier2: [],
tier3: [],
tier4: [],
tier5: [],
};
}
try {
return (await res.json()).data as SMSCountryTiers;
} catch (e) {
console.error("Failed to parse sms country tiers", e);
return {
tier1: [],
tier2: [],
tier3: [],
tier4: [],
tier5: [],
};
}
}
+19 -1
View File
@@ -3,7 +3,8 @@ import { API_SERVER_URL, THIRDWEB_API_SECRET } from "@/constants/env";
import type { TeamResponse } from "@thirdweb-dev/service-utils";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
export type Team = TeamResponse;
export type Team = TeamResponse & { stripeCustomerId: string | null };
export async function getTeamBySlug(slug: string) {
const token = await getAuthToken();
@@ -57,6 +58,23 @@ export async function getTeams() {
return null;
}
export async function getDefaultTeam() {
const token = await getAuthToken();
if (!token) {
return null;
}
const res = await fetch(`${API_SERVER_URL}/v1/teams/~`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (res.ok) {
return (await res.json())?.result as Team;
}
return null;
}
type TeamNebulaWaitList = {
onWaitlist: boolean;
createdAt: null | string;
@@ -1,19 +0,0 @@
import { cn } from "@/lib/utils";
export function TextDivider(props: {
text: string;
className?: string;
}) {
return (
<div
className={cn(
"flex items-center text-muted-foreground text-sm",
props.className,
)}
>
<span className="h-[1px] flex-1 bg-border" />
<span className="mx-4">{props.text}</span>
<span className="h-[1px] flex-1 bg-border" />
</div>
);
}
+93 -35
View File
@@ -1,78 +1,136 @@
"use client";
import { useMutation } from "@tanstack/react-query";
import { toast } from "sonner";
import type {
BillingBillingPortalAction,
BillingPortalOptions,
RedirectBillingCheckoutAction,
RedirectCheckoutOptions,
GetBillingCheckoutUrlAction,
GetBillingCheckoutUrlOptions,
GetBillingPortalUrlAction,
GetBillingPortalUrlOptions,
} from "../actions/billing";
import { cn } from "../lib/utils";
import { Spinner } from "./ui/Spinner/Spinner";
import { Button, type ButtonProps } from "./ui/button";
type CheckoutButtonProps = Omit<RedirectCheckoutOptions, "redirectUrl"> &
ButtonProps & {
redirectPath: string;
redirectToCheckout: RedirectBillingCheckoutAction;
};
type CheckoutButtonProps = Omit<GetBillingCheckoutUrlOptions, "redirectUrl"> & {
getBillingCheckoutUrl: GetBillingCheckoutUrlAction;
buttonProps?: Omit<ButtonProps, "children">;
children: React.ReactNode;
};
export function CheckoutButton({
onClick,
teamSlug,
sku,
metadata,
redirectPath,
getBillingCheckoutUrl,
children,
redirectToCheckout,
...restProps
buttonProps,
}: CheckoutButtonProps) {
const getUrlMutation = useMutation({
mutationFn: async () => {
return getBillingCheckoutUrl({
teamSlug,
sku,
metadata,
redirectUrl: getAbsoluteUrl("/stripe-redirect"),
});
},
});
const errorMessage = "Failed to open checkout page";
return (
<Button
{...restProps}
{...buttonProps}
className={cn(buttonProps?.className, "gap-2")}
disabled={getUrlMutation.isPending || buttonProps?.disabled}
onClick={async (e) => {
onClick?.(e);
await redirectToCheckout({
teamSlug,
sku,
metadata,
redirectUrl: getRedirectUrl(redirectPath),
buttonProps?.onClick?.(e);
getUrlMutation.mutate(undefined, {
onSuccess: (res) => {
if (!res.url) {
toast.error(errorMessage);
return;
}
const tab = window.open(res.url, "_blank");
if (!tab) {
toast.error(errorMessage);
return;
}
},
onError: () => {
toast.error(errorMessage);
},
});
}}
>
{getUrlMutation.isPending && <Spinner className="size-4" />}
{children}
</Button>
);
}
type BillingPortalButtonProps = Omit<BillingPortalOptions, "redirectUrl"> &
ButtonProps & {
redirectPath: string;
redirectToBillingPortal: BillingBillingPortalAction;
};
type BillingPortalButtonProps = Omit<
GetBillingPortalUrlOptions,
"redirectUrl"
> & {
getBillingPortalUrl: GetBillingPortalUrlAction;
buttonProps?: Omit<ButtonProps, "children">;
children: React.ReactNode;
};
export function BillingPortalButton({
onClick,
teamSlug,
redirectPath,
children,
redirectToBillingPortal,
...restProps
getBillingPortalUrl,
buttonProps,
}: BillingPortalButtonProps) {
const getUrlMutation = useMutation({
mutationFn: async () => {
return getBillingPortalUrl({
teamSlug,
redirectUrl: getAbsoluteUrl("/stripe-redirect"),
});
},
});
const errorMessage = "Failed to open billing portal";
return (
<Button
{...restProps}
{...buttonProps}
className={cn(buttonProps?.className, "gap-2")}
disabled={getUrlMutation.isPending || buttonProps?.disabled}
onClick={async (e) => {
onClick?.(e);
await redirectToBillingPortal({
teamSlug,
redirectUrl: getRedirectUrl(redirectPath),
buttonProps?.onClick?.(e);
getUrlMutation.mutate(undefined, {
onSuccess(res) {
if (!res.url) {
toast.error(errorMessage);
return;
}
const tab = window.open(res.url, "_blank");
if (!tab) {
toast.error(errorMessage);
return;
}
},
onError: () => {
toast.error(errorMessage);
},
});
}}
>
{getUrlMutation.isPending && <Spinner className="size-4" />}
{children}
</Button>
);
}
function getRedirectUrl(path: string) {
function getAbsoluteUrl(path: string) {
const url = new URL(window.location.origin);
url.pathname = path;
return url.toString();
@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { BadgeContainer } from "../../../../stories/utils";
import { getThirdwebClient } from "../../../constants/thirdweb.server";
import { Button } from "../../ui/button";
import { ProjectAvatar } from "./ProjectAvatar";
@@ -17,17 +18,19 @@ export const Desktop: Story = {
args: {},
};
const client = getThirdwebClient();
function Story() {
return (
<div className="flex flex-col gap-10 p-10">
<p> All images below are set with size-6 className </p>
<BadgeContainer label="No Src - Skeleton">
<ProjectAvatar src={undefined} className="size-6" />
<ProjectAvatar src={undefined} className="size-6" client={client} />
</BadgeContainer>
<BadgeContainer label="Invalid/Empty Src - BoxIcon Fallback">
<ProjectAvatar src={""} className="size-6" />
<ProjectAvatar src={""} className="size-6" client={client} />
</BadgeContainer>
<ToggleTest />
@@ -62,13 +65,14 @@ function ToggleTest() {
<p> Src+Name is: {data ? "set" : "not set"} </p>
<BadgeContainer label="Valid Src">
<ProjectAvatar src={data?.src} className="size-6" />
<ProjectAvatar src={data?.src} className="size-6" client={client} />
</BadgeContainer>
<BadgeContainer label="invalid Src">
<ProjectAvatar
src={data ? "invalid-src" : undefined}
className="size-6"
client={client}
/>
</BadgeContainer>
</div>
@@ -1,15 +1,23 @@
import { Img } from "@/components/blocks/Img";
import { BoxIcon } from "lucide-react";
import type { ThirdwebClient } from "thirdweb";
import { resolveSchemeWithErrorHandler } from "../../../lib/resolveSchemeWithErrorHandler";
import { cn } from "../../../lib/utils";
export function ProjectAvatar(props: {
src: string | undefined;
className: string | undefined;
client: ThirdwebClient;
}) {
return (
<Img
src={props.src}
className={cn("rounded-lg border border-border", props.className)}
src={
resolveSchemeWithErrorHandler({
uri: props.src,
client: props.client,
}) || ""
}
className={cn("rounded-full border border-border", props.className)}
alt={""}
fallback={
<div className="flex items-center justify-center bg-card">
@@ -10,7 +10,9 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { useState } from "react";
import { cn } from "../../lib/utils";
import { DynamicHeight } from "../ui/DynamicHeight";
export function DangerSettingCard(props: {
title: string;
@@ -24,9 +26,14 @@ export function DangerSettingCard(props: {
confirmationDialog: {
title: string;
description: React.ReactNode;
children?: React.ReactNode;
onClose?: () => void;
};
children?: React.ReactNode;
}) {
const [isConfirmationDialogOpen, setIsConfirmationDialogOpen] =
useState(false);
return (
<div
className={cn(
@@ -50,7 +57,15 @@ export function DangerSettingCard(props: {
props.footerClassName,
)}
>
<Dialog>
<Dialog
open={isConfirmationDialogOpen}
onOpenChange={(v) => {
setIsConfirmationDialogOpen(v);
if (!v) {
props.confirmationDialog.onClose?.();
}
}}
>
<DialogTrigger asChild>
<Button
variant="destructive"
@@ -62,21 +77,21 @@ export function DangerSettingCard(props: {
</Button>
</DialogTrigger>
<DialogContent
className="z-[10001] overflow-hidden p-0"
dialogOverlayClassName="z-[10000]"
>
<div className="p-6">
<DialogHeader className="pr-10">
<DialogTitle className="leading-snug">
{props.confirmationDialog.title}
</DialogTitle>
<DialogContent className="overflow-hidden p-0">
<DynamicHeight>
<div className="p-6">
<DialogHeader className="pr-10">
<DialogTitle className="leading-snug">
{props.confirmationDialog.title}
</DialogTitle>
<DialogDescription>
{props.confirmationDialog.description}
</DialogDescription>
</DialogHeader>
</div>
<DialogDescription>
{props.confirmationDialog.description}
</DialogDescription>
</DialogHeader>
{props.confirmationDialog.children}
</div>
</DynamicHeight>
<div className="flex justify-end gap-4 border-t bg-card p-6 lg:gap-2">
<DialogClose asChild>
@@ -2,11 +2,12 @@ import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Button } from "@/components/ui/button";
import { useMutation } from "@tanstack/react-query";
import { DownloadIcon } from "lucide-react";
import Papa from "papaparse";
import { toast } from "sonner";
import { cn } from "../../lib/utils";
export function ExportToCSVButton(props: {
getData: () => Promise<{ header: string[]; rows: string[][] }>;
getData: () => Promise<{ header: string[]; rows: string[][] } | string>;
fileName: string;
disabled?: boolean;
className?: string;
@@ -14,7 +15,12 @@ export function ExportToCSVButton(props: {
const exportMutation = useMutation({
mutationFn: async () => {
const data = await props.getData();
exportToCSV(props.fileName, data);
if (typeof data === "string") {
exportToCSV(props.fileName, data);
} else {
const fileContent = convertToCSVFormat(data);
exportToCSV(props.fileName, fileContent);
}
},
onError: () => {
toast.error("Failed to download CSV");
@@ -29,7 +35,7 @@ export function ExportToCSVButton(props: {
return (
<Button
variant="outline"
disabled={props.disabled}
disabled={props.disabled || exportMutation.isPending}
className={cn("flex items-center gap-2 border text-xs", props.className)}
onClick={async () => {
exportMutation.mutate();
@@ -50,15 +56,15 @@ export function ExportToCSVButton(props: {
);
}
function exportToCSV(
fileName: string,
data: { header: string[]; rows: string[][] },
) {
const { header, rows } = data;
const csvContent = `data:text/csv;charset=utf-8,${header.join(",")}\n${rows
.map((e) => e.join(","))
.join("\n")}`;
function convertToCSVFormat(data: { header: string[]; rows: string[][] }) {
return Papa.unparse({
fields: data.header,
data: data.rows,
});
}
function exportToCSV(fileName: string, fileContent: string) {
const csvContent = `data:text/csv;charset=utf-8,${fileContent}`;
const encodedUri = encodeURI(csvContent);
const link = document.createElement("a");
link.setAttribute("href", encodedUri);
@@ -47,7 +47,7 @@ export function Img(props: imgElementProps) {
}, []);
return (
<div className="relative">
<div className="relative shrink-0">
<img
{...restProps}
// avoid setting empty src string to prevent request to the entire page
@@ -19,30 +19,7 @@ export function MobileSidebar(props: {
triggerClassName?: string;
}) {
const [isOpen, setIsOpen] = useState(false);
const pathname = usePathname();
const activeLink = useMemo(() => {
function isActive(link: SidebarBaseLink) {
if (link.exactMatch) {
return link.href === pathname;
}
return pathname?.startsWith(link.href);
}
for (const link of props.links) {
if ("group" in link) {
for (const subLink of link.links) {
if (isActive(subLink)) {
return subLink;
}
}
} else {
if (isActive(link)) {
return link;
}
}
}
}, [props.links, pathname]);
const activeLink = useActiveSidebarLink(props.links);
const defaultTrigger = (
<Button
@@ -75,3 +52,32 @@ export function MobileSidebar(props: {
</Dialog>
);
}
export function useActiveSidebarLink(links: SidebarLink[]) {
const pathname = usePathname();
const activeLink = useMemo(() => {
function isActive(link: SidebarBaseLink) {
if (link.exactMatch) {
return link.href === pathname;
}
return pathname?.startsWith(link.href);
}
for (const link of links) {
if ("group" in link) {
for (const subLink of link.links) {
if (isActive(subLink)) {
return subLink;
}
}
} else if ("href" in link) {
if (isActive(link)) {
return link;
}
}
}
}, [links, pathname]);
return activeLink;
}
@@ -18,17 +18,39 @@ export function MultiNetworkSelector(props: {
onChange: (chainIds: number[]) => void;
disableChainId?: boolean;
className?: string;
priorityChains?: number[];
}) {
const { allChains, idToChain } = useAllChainsData();
const options = useMemo(() => {
return allChains.map((chain) => {
let sortedChains = allChains;
if (props.priorityChains) {
const priorityChainsSet = new Set();
for (const chainId of props.priorityChains || []) {
priorityChainsSet.add(chainId);
}
const priorityChains = (props.priorityChains || [])
.map((chainId) => {
return idToChain.get(chainId);
})
.filter((v) => !!v);
const otherChains = allChains.filter(
(chain) => !priorityChainsSet.has(chain.chainId),
);
sortedChains = [...priorityChains, ...otherChains];
}
return sortedChains.map((chain) => {
return {
label: cleanChainName(chain.name),
value: String(chain.chainId),
};
});
}, [allChains]);
}, [allChains, props.priorityChains, idToChain]);
const searchFn = useCallback(
(option: Option, searchValue: string) => {
@@ -104,6 +126,7 @@ export function SingleNetworkSelector(props: {
side?: "left" | "right" | "top" | "bottom";
disableChainId?: boolean;
align?: "center" | "start" | "end";
placeholder?: string;
}) {
const { allChains, idToChain } = useAllChainsData();
@@ -180,7 +203,11 @@ export function SingleNetworkSelector(props: {
props.onChange(Number(chainId));
}}
closeOnSelect={true}
placeholder={isLoadingChains ? "Loading Chains..." : "Select Chain"}
placeholder={
isLoadingChains
? "Loading Chains..."
: props.placeholder || "Select Chain"
}
overrideSearchFn={searchFn}
renderOption={renderOption}
className={props.className}
@@ -2,11 +2,13 @@ import { ExternalLinkIcon } from "lucide-react";
import type React from "react";
import { cn } from "../../lib/utils";
import { NavLink } from "../ui/NavLink";
import { Separator } from "../ui/separator";
export type SidebarBaseLink = {
href: string;
label: React.ReactNode;
exactMatch?: boolean;
icon?: React.FC<{ className?: string }>;
tracking?: {
category: string;
action: string;
@@ -19,6 +21,9 @@ export type SidebarLink =
| {
group: string;
links: SidebarBaseLink[];
}
| {
separator: true;
};
type SidebarContentProps = {
@@ -27,21 +32,18 @@ type SidebarContentProps = {
className?: string;
};
export function Sidebar(props: SidebarContentProps) {
export function CustomSidebar(props: SidebarContentProps) {
return (
<aside
className={cn(
"sticky top-0 hidden w-[230px] flex-shrink-0 self-start lg:block",
props.className,
)}
>
<div className="py-7">
{props.header}
<div className="flex flex-col gap-1">
<RenderSidebarLinks links={props.links} />
<div className={cn("hidden w-[230px] shrink-0 lg:block", props.className)}>
<aside className="sticky top-0 self-start">
<div className="py-7">
{props.header}
<div className="flex flex-col gap-1">
<RenderSidebarLinks links={props.links} />
</div>
</div>
</div>
</aside>
</aside>
</div>
);
}
@@ -61,6 +63,10 @@ export function RenderSidebarLinks(props: { links: SidebarLink[] }) {
);
}
if ("separator" in link) {
return <Separator className="my-2" />;
}
const isExternal = link.href.startsWith("http");
return (
<NavLink
@@ -68,9 +74,10 @@ export function RenderSidebarLinks(props: { links: SidebarLink[] }) {
key={i}
href={link.href}
className="flex items-center gap-2 rounded-md px-3 py-2 text-muted-foreground text-sm hover:bg-accent"
activeClassName="text-foreground"
activeClassName="text-foreground bg-accent"
exactMatch={link.exactMatch}
>
{link.icon && <link.icon className="size-4" />}
{link.label}
{isExternal && <ExternalLinkIcon className="size-3" />}
</NavLink>
@@ -1,6 +1,22 @@
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
SidebarSeparator,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar";
import { cn } from "../../lib/utils";
import { MobileSidebar } from "./MobileSidebar";
import { Sidebar, type SidebarLink } from "./Sidebar";
import { NavLink } from "../ui/NavLink";
import { Separator } from "../ui/separator";
import { MobileSidebar, useActiveSidebarLink } from "./MobileSidebar";
import { CustomSidebar, type SidebarLink } from "./Sidebar";
export function SidebarLayout(props: {
sidebarLinks: SidebarLink[];
@@ -17,7 +33,10 @@ export function SidebarLayout(props: {
props.className,
)}
>
<Sidebar links={sidebarLinks} className={props.desktopSidebarClassName} />
<CustomSidebar
links={sidebarLinks}
className={props.desktopSidebarClassName}
/>
<MobileSidebar
links={sidebarLinks}
triggerClassName={props.mobileSidebarClassName}
@@ -29,3 +48,118 @@ export function SidebarLayout(props: {
</div>
);
}
export function FullWidthSidebarLayout(props: {
contentSidebarLinks: SidebarLink[];
footerSidebarLinks?: SidebarLink[];
children: React.ReactNode;
className?: string;
footer?: React.ReactNode;
}) {
const { contentSidebarLinks, children, footerSidebarLinks } = props;
return (
<div
className={cn(
"relative flex w-full flex-1 overflow-y-hidden",
props.className,
)}
>
{/* left - sidebar */}
<Sidebar collapsible="icon" side="left">
<SidebarContent>
<SidebarGroup>
<SidebarGroupContent>
<RenderSidebarGroup
sidebarLinks={contentSidebarLinks}
groupName={undefined}
/>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
{footerSidebarLinks && (
<SidebarFooter className="pb-3">
<RenderSidebarGroup
sidebarLinks={footerSidebarLinks}
groupName={undefined}
/>
</SidebarFooter>
)}
<SidebarRail />
</Sidebar>
{/* right - content */}
<div className="flex h-full flex-grow flex-col overflow-y-auto">
<MobileSidebarTrigger
links={[...contentSidebarLinks, ...(footerSidebarLinks || [])]}
/>
<main className="container z-0 flex min-w-0 max-w-[1280px] grow flex-col pb-20 max-sm:w-full lg:pt-6">
{children}
</main>
{props.footer}
</div>
</div>
);
}
function RenderSidebarGroup(props: {
sidebarLinks: SidebarLink[];
groupName: string | undefined;
}) {
const { sidebarLinks } = props;
const sidebar = useSidebar();
return (
<SidebarMenu className="gap-1.5">
{sidebarLinks.map((link) => {
if ("href" in link) {
return (
<SidebarMenuItem key={link.href}>
<SidebarMenuButton asChild>
<NavLink
href={link.href}
className="flex items-center gap-2 text-muted-foreground text-sm hover:bg-accent"
activeClassName="text-foreground bg-accent"
exactMatch={link.exactMatch}
tracking={link.tracking}
onClick={() => {
sidebar.setOpenMobile(false);
}}
>
{link.icon && <link.icon className="size-4" />}
<span>{link.label}</span>
</NavLink>
</SidebarMenuButton>
</SidebarMenuItem>
);
}
if ("separator" in link) {
return <SidebarSeparator className="my-1" />;
}
return (
<RenderSidebarGroup
sidebarLinks={link.links}
groupName={link.group}
key={link.group}
/>
);
})}
</SidebarMenu>
);
}
function MobileSidebarTrigger(props: { links: SidebarLink[] }) {
const activeLink = useActiveSidebarLink(props.links);
return (
<div className="mb-4 flex items-center gap-3 border-b px-4 py-4 lg:hidden">
<SidebarTrigger className="size-4" />
<Separator orientation="vertical" className="h-4" />
{activeLink && <span className="text-sm">{activeLink.label}</span>}
</div>
);
}
@@ -96,7 +96,6 @@ export const CodeSegment: React.FC<CodeSegmentProps> = ({
onClick: () => setEnvironment(env.environment),
isActive: activeEnvironment === env.environment,
name: env.title,
isEnabled: true,
}))}
tabClassName="text-sm gap-2 !text-sm"
tabIconClassName="size-4"
@@ -225,7 +225,7 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
</Button>
</PopoverTrigger>
<PopoverContent
className="z-[10001] p-0"
className="p-0"
align="center"
sideOffset={10}
onEscapeKeyDown={() => setIsPopoverOpen(false)}
@@ -1,3 +1,4 @@
"use client";
import type { Team } from "@/api/team";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@@ -8,7 +9,7 @@ import { CheckIcon, CircleAlertIcon, CircleDollarSignIcon } from "lucide-react";
import type React from "react";
import { TEAM_PLANS } from "utils/pricing";
import { remainingDays } from "../../../utils/date-utils";
import type { RedirectBillingCheckoutAction } from "../../actions/billing";
import type { GetBillingCheckoutUrlAction } from "../../actions/billing";
import { CheckoutButton } from "../billing";
type ButtonProps = React.ComponentProps<typeof Button>;
@@ -27,14 +28,13 @@ type PricingCardProps = {
label?: string;
};
variant?: ButtonProps["variant"];
onClick?: () => void;
};
ctaHint?: string;
highlighted?: boolean;
current?: boolean;
canTrialGrowth?: boolean;
activeTrialEndsAt?: string;
redirectPath: string;
redirectToCheckout: RedirectBillingCheckoutAction;
getBillingCheckoutUrl: GetBillingCheckoutUrlAction;
};
export const PricingCard: React.FC<PricingCardProps> = ({
@@ -43,10 +43,8 @@ export const PricingCard: React.FC<PricingCardProps> = ({
cta,
highlighted = false,
current = false,
canTrialGrowth = false,
activeTrialEndsAt,
redirectPath,
redirectToCheckout,
getBillingCheckoutUrl,
}) => {
const plan = TEAM_PLANS[billingPlan];
const isCustomPrice = typeof plan.price === "string";
@@ -88,18 +86,7 @@ export const PricingCard: React.FC<PricingCardProps> = ({
<div className="flex flex-col gap-0.5">
<div className="flex items-center gap-2">
<span className="font-semibold text-3xl text-foreground tracking-tight">
{isCustomPrice ? (
plan.price
) : canTrialGrowth ? (
<>
<span className="text-muted-foreground line-through">
${plan.price}
</span>{" "}
$0
</>
) : (
`$${plan.price}`
)}
${plan.price}
</span>
{!isCustomPrice && (
@@ -135,7 +122,7 @@ export const PricingCard: React.FC<PricingCardProps> = ({
</div>
</div>
<div className="flex grow flex-col items-start gap-2.5 text-foreground">
<div className="flex grow flex-col items-start gap-2 text-foreground">
{plan.subTitle && (
<p className="font-medium text-foreground">{plan.subTitle}</p>
)}
@@ -149,11 +136,14 @@ export const PricingCard: React.FC<PricingCardProps> = ({
<div className="flex flex-col gap-3">
{billingPlan !== "pro" ? (
<CheckoutButton
variant={cta.variant || "outline"}
buttonProps={{
variant: cta.variant || "outline",
className: "gap-2",
onClick: cta.onClick,
}}
teamSlug={teamSlug}
sku={billingPlan === "starter" ? "plan:starter" : "plan:growth"}
redirectPath={redirectPath}
redirectToCheckout={redirectToCheckout}
getBillingCheckoutUrl={getBillingCheckoutUrl}
>
{cta.title}
</CheckoutButton>
@@ -189,7 +179,7 @@ function FeatureItem({ text }: FeatureItemProps) {
const titleStr = Array.isArray(text) ? text[0] : text;
return (
<div className="flex items-center gap-2">
<div className="flex items-center gap-2 text-sm">
<CheckIcon className="size-4 shrink-0 text-green-500" />
{Array.isArray(text) ? (
<div className="flex items-center gap-2">
@@ -137,7 +137,7 @@ export const SelectWithSearch = React.forwardRef<
</PopoverTrigger>
<PopoverContent
className={cn("z-[10001] p-0", popoverContentClassName)}
className={cn("p-0", popoverContentClassName)}
align={props.align || "center"}
side={props.side}
sideOffset={10}
@@ -8,7 +8,7 @@ import {
import { useThirdwebClient } from "@/constants/thirdweb.client";
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
import { useClipboard } from "hooks/useClipboard";
import { Check, Copy } from "lucide-react";
import { Check, Copy, XIcon } from "lucide-react";
import { useMemo } from "react";
import { type ThirdwebClient, isAddress } from "thirdweb";
import { ZERO_ADDRESS } from "thirdweb";
@@ -16,6 +16,7 @@ import { Blobbie, type SocialProfile, useSocialProfiles } from "thirdweb/react";
import { cn } from "../../lib/utils";
import { Badge } from "../ui/badge";
import { Button } from "../ui/button";
import { ToolTipLabel } from "../ui/tooltip";
import { Img } from "./Img";
export function WalletAddress(props: {
@@ -44,7 +45,16 @@ export function WalletAddress(props: {
const { onCopy, hasCopied } = useClipboard(address, 2000);
if (!isAddress(address)) {
return <span>Invalid Address ({address})</span>;
return (
<ToolTipLabel label={address} hoverable>
<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">
<XIcon className="size-4 text-muted-foreground" />
</div>
Invalid Address
</span>
</ToolTipLabel>
);
}
// special case for zero address
@@ -91,13 +91,11 @@ export function DatePickerWithRange(props: {
name: "From",
onClick: () => setScreen("from"),
isActive: screen === "from",
isEnabled: true,
},
{
name: "To",
onClick: () => setScreen("to"),
isActive: screen === "to",
isEnabled: true,
},
]}
/>
@@ -15,6 +15,7 @@ export type NavButtonProps = {
action: string;
label: string;
};
onClick?: () => void;
};
export function NavLink(props: React.PropsWithChildren<NavButtonProps>) {
@@ -30,7 +31,9 @@ export function NavLink(props: React.PropsWithChildren<NavButtonProps>) {
href={props.href}
className={cn(props.className, isActive && props.activeClassName)}
target={props.href.startsWith("http") ? "_blank" : undefined}
prefetch={false}
onClick={() => {
props.onClick?.();
if (props.tracking) {
track({
category: props.tracking.category,
@@ -96,7 +96,10 @@ const BreadcrumbEllipsis = ({
<span
role="presentation"
aria-hidden="true"
className={cn("flex h-9 w-9 items-center justify-center", className)}
className={cn(
"relative flex h-9 w-9 items-center justify-center",
className,
)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
@@ -47,13 +47,16 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
const btnOnlyProps =
Comp === "button" ? { type: "button" as const } : undefined;
Comp === "button"
? { type: props.type || ("button" as const) }
: undefined;
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...btnOnlyProps}
{...props}
{...btnOnlyProps}
/>
);
},
@@ -1,5 +1,4 @@
import * as parserBabel from "prettier/plugins/babel";
import * as estree from "prettier/plugins/estree";
import { format } from "prettier/standalone";
import { type BundledLanguage, codeToHtml } from "shiki";
@@ -21,10 +20,11 @@ export async function getCodeHtml(
ignoreFormattingErrors?: boolean;
},
) {
const estreePlugin = await import("prettier/plugins/estree");
const formattedCode = isPrettierSupportedLang(lang)
? await format(code, {
parser: "babel-ts",
plugins: [parserBabel, estree],
plugins: [parserBabel, estreePlugin.default],
printWidth: 60,
}).catch((e) => {
if (!options?.ignoreFormattingErrors) {
+1 -1
View File
@@ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input
type={type}
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-foreground/10 file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-muted-foreground 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",
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background selection:bg-foreground/10 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 dark:selection:bg-foreground/20",
className,
)}
ref={ref}
@@ -94,7 +94,10 @@ const PaginationEllipsis = ({
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
className={cn(
"relative flex h-9 w-9 items-center justify-center",
className,
)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
@@ -0,0 +1,773 @@
"use client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet";
import { Skeleton } from "@/components/ui/skeleton";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useIsMobile } from "@/hooks/use-mobile";
import { cn } from "@/lib/utils";
import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority";
import { PanelLeft } from "lucide-react";
import * as React from "react";
const SIDEBAR_COOKIE_NAME = "sidebar_state";
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
const SIDEBAR_WIDTH = "16rem";
const SIDEBAR_WIDTH_MOBILE = "18rem";
const SIDEBAR_WIDTH_ICON = "3rem";
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
type SidebarContextProps = {
state: "expanded" | "collapsed";
open: boolean;
setOpen: (open: boolean) => void;
openMobile: boolean;
setOpenMobile: (open: boolean) => void;
isMobile: boolean;
toggleSidebar: () => void;
};
const SidebarContext = React.createContext<SidebarContextProps | null>(null);
function useSidebar() {
const context = React.useContext(SidebarContext);
if (!context) {
throw new Error("useSidebar must be used within a SidebarProvider.");
}
return context;
}
const SidebarProvider = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
defaultOpen?: boolean;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}
>(
(
{
defaultOpen = true,
open: openProp,
onOpenChange: setOpenProp,
className,
style,
children,
...props
},
ref,
) => {
const isMobile = useIsMobile();
const [openMobile, setOpenMobile] = React.useState(false);
// This is the internal state of the sidebar.
// We use openProp and setOpenProp for control from outside the component.
const [_open, _setOpen] = React.useState(defaultOpen);
const open = openProp ?? _open;
const setOpen = React.useCallback(
(value: boolean | ((value: boolean) => boolean)) => {
const openState = typeof value === "function" ? value(open) : value;
if (setOpenProp) {
setOpenProp(openState);
} else {
_setOpen(openState);
}
// This sets the cookie to keep the sidebar state.
// eslint-disable-next-line react-compiler/react-compiler
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
},
[setOpenProp, open],
);
// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
return isMobile
? setOpenMobile((open) => !open)
: setOpen((open) => !open);
}, [isMobile, setOpen]);
// Adds a keyboard shortcut to toggle the sidebar.
React.useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (
event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
(event.metaKey || event.ctrlKey)
) {
event.preventDefault();
toggleSidebar();
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [toggleSidebar]);
// We add a state so that we can do data-state="expanded" or "collapsed".
// This makes it easier to style the sidebar with Tailwind classes.
const state = open ? "expanded" : "collapsed";
const contextValue = React.useMemo<SidebarContextProps>(
() => ({
state,
open,
setOpen,
isMobile,
openMobile,
setOpenMobile,
toggleSidebar,
}),
[state, open, setOpen, isMobile, openMobile, toggleSidebar],
);
return (
<SidebarContext.Provider value={contextValue}>
<TooltipProvider delayDuration={0}>
<div
style={
{
"--sidebar-width": SIDEBAR_WIDTH,
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
...style,
} as React.CSSProperties
}
className={cn(
"group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar",
className,
)}
ref={ref}
{...props}
>
{children}
</div>
</TooltipProvider>
</SidebarContext.Provider>
);
},
);
SidebarProvider.displayName = "SidebarProvider";
const Sidebar = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
side?: "left" | "right";
variant?: "sidebar" | "floating" | "inset";
collapsible?: "offcanvas" | "icon" | "none";
}
>(
(
{
side = "left",
variant = "sidebar",
collapsible = "offcanvas",
className,
children,
...props
},
ref,
) => {
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
if (collapsible === "none") {
return (
<div
className={cn(
"flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground",
className,
)}
ref={ref}
{...props}
>
{children}
</div>
);
}
if (isMobile) {
return (
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
<SheetContent
data-sidebar="sidebar"
data-mobile="true"
className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
style={
{
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
} as React.CSSProperties
}
side={side}
>
<SheetHeader className="sr-only">
<SheetTitle>Sidebar</SheetTitle>
<SheetDescription>Displays the mobile sidebar.</SheetDescription>
</SheetHeader>
<div className="flex h-full w-full flex-col">{children}</div>
</SheetContent>
</Sheet>
);
}
return (
<div
ref={ref}
className="group peer relative hidden flex-shrink-0 text-sidebar-foreground md:block"
data-state={state}
data-collapsible={state === "collapsed" ? collapsible : ""}
data-variant={variant}
data-side={side}
>
{/* This is what handles the sidebar gap on desktop */}
<div
className={cn(
"relative w-[--sidebar-width] bg-transparent transition-[width] duration-200 ease-linear",
"group-data-[collapsible=offcanvas]:w-0",
"group-data-[side=right]:rotate-180",
variant === "floating" || variant === "inset"
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]",
)}
/>
<div
className={cn(
"absolute inset-y-0 z-10 hidden h-full w-[--sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex",
side === "left"
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
// Adjust the padding for floating and inset variants.
variant === "floating" || variant === "inset"
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l",
className,
)}
{...props}
>
<div
data-sidebar="sidebar"
className="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow"
>
{children}
</div>
</div>
</div>
);
},
);
Sidebar.displayName = "Sidebar";
const SidebarTrigger = React.forwardRef<
React.ElementRef<typeof Button>,
React.ComponentProps<typeof Button>
>(({ className, onClick, ...props }, ref) => {
const { toggleSidebar } = useSidebar();
return (
<Button
ref={ref}
data-sidebar="trigger"
variant="ghost"
size="icon"
className={cn("h-7 w-7", className)}
onClick={(event) => {
onClick?.(event);
toggleSidebar();
}}
{...props}
>
<PanelLeft />
<span className="sr-only">Toggle Sidebar</span>
</Button>
);
});
SidebarTrigger.displayName = "SidebarTrigger";
const SidebarRail = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<"button">
>(({ className, ...props }, ref) => {
const { toggleSidebar } = useSidebar();
return (
<button
ref={ref}
data-sidebar="rail"
aria-label="Toggle Sidebar"
tabIndex={-1}
onClick={toggleSidebar}
title="Toggle Sidebar"
className={cn(
"-translate-x-1/2 group-data-[side=left]:-right-4 absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=right]:left-0 sm:flex",
"[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize",
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:hover:bg-sidebar group-data-[collapsible=offcanvas]:after:left-full",
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
className,
)}
{...props}
/>
);
});
SidebarRail.displayName = "SidebarRail";
const SidebarInset = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"main">
>(({ className, ...props }, ref) => {
return (
<main
ref={ref}
className={cn(
"relative flex w-full flex-1 flex-col bg-background",
"md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
className,
)}
{...props}
/>
);
});
SidebarInset.displayName = "SidebarInset";
const SidebarInput = React.forwardRef<
React.ElementRef<typeof Input>,
React.ComponentProps<typeof Input>
>(({ className, ...props }, ref) => {
return (
<Input
ref={ref}
data-sidebar="input"
className={cn(
"h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring",
className,
)}
{...props}
/>
);
});
SidebarInput.displayName = "SidebarInput";
const SidebarHeader = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div">
>(({ className, ...props }, ref) => {
return (
<div
ref={ref}
data-sidebar="header"
className={cn("flex flex-col gap-2 p-2", className)}
{...props}
/>
);
});
SidebarHeader.displayName = "SidebarHeader";
const SidebarFooter = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div">
>(({ className, ...props }, ref) => {
return (
<div
ref={ref}
data-sidebar="footer"
className={cn("flex flex-col gap-2 p-2", className)}
{...props}
/>
);
});
SidebarFooter.displayName = "SidebarFooter";
const SidebarSeparator = React.forwardRef<
React.ElementRef<typeof Separator>,
React.ComponentProps<typeof Separator>
>(({ className, ...props }, ref) => {
return (
<Separator
ref={ref}
data-sidebar="separator"
className={cn("mx-2 w-auto bg-sidebar-border", className)}
{...props}
/>
);
});
SidebarSeparator.displayName = "SidebarSeparator";
const SidebarContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div">
>(({ className, ...props }, ref) => {
return (
<div
ref={ref}
data-sidebar="content"
className={cn(
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
className,
)}
{...props}
/>
);
});
SidebarContent.displayName = "SidebarContent";
const SidebarGroup = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div">
>(({ className, ...props }, ref) => {
return (
<div
ref={ref}
data-sidebar="group"
className={cn("relative flex w-full min-w-0 flex-col p-2", className)}
{...props}
/>
);
});
SidebarGroup.displayName = "SidebarGroup";
const SidebarGroupLabel = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & { asChild?: boolean }
>(({ className, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "div";
return (
<Comp
ref={ref}
data-sidebar="group-label"
className={cn(
"flex h-8 shrink-0 items-center rounded-md px-2 font-medium text-sidebar-foreground/70 text-xs outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
className,
)}
{...props}
/>
);
});
SidebarGroupLabel.displayName = "SidebarGroupLabel";
const SidebarGroupAction = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<"button"> & { asChild?: boolean }
>(({ className, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
ref={ref}
data-sidebar="group-action"
className={cn(
"absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:-inset-2 after:absolute after:md:hidden",
"group-data-[collapsible=icon]:hidden",
className,
)}
{...props}
/>
);
});
SidebarGroupAction.displayName = "SidebarGroupAction";
const SidebarGroupContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div">
>(({ className, ...props }, ref) => (
<div
ref={ref}
data-sidebar="group-content"
className={cn("w-full text-sm", className)}
{...props}
/>
));
SidebarGroupContent.displayName = "SidebarGroupContent";
const SidebarMenu = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
data-sidebar="menu"
className={cn("flex w-full min-w-0 flex-col gap-1", className)}
{...props}
/>
));
SidebarMenu.displayName = "SidebarMenu";
const SidebarMenuItem = React.forwardRef<
HTMLLIElement,
React.ComponentProps<"li">
>(({ className, ...props }, ref) => (
<li
ref={ref}
data-sidebar="menu-item"
className={cn("group/menu-item relative", className)}
{...props}
/>
));
SidebarMenuItem.displayName = "SidebarMenuItem";
const sidebarMenuButtonVariants = cva(
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
{
variants: {
variant: {
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
outline:
"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
},
size: {
default: "h-8 text-sm",
sm: "h-7 text-xs",
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
const SidebarMenuButton = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<"button"> & {
asChild?: boolean;
isActive?: boolean;
tooltip?: string | React.ComponentProps<typeof TooltipContent>;
} & VariantProps<typeof sidebarMenuButtonVariants>
>(
(
{
asChild = false,
isActive = false,
variant = "default",
size = "default",
tooltip,
className,
...props
},
ref,
) => {
const Comp = asChild ? Slot : "button";
const { isMobile, state } = useSidebar();
const button = (
<Comp
ref={ref}
data-sidebar="menu-button"
data-size={size}
data-active={isActive}
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
{...props}
/>
);
if (!tooltip) {
return button;
}
if (typeof tooltip === "string") {
tooltip = {
children: tooltip,
};
}
return (
<Tooltip>
<TooltipTrigger asChild>{button}</TooltipTrigger>
<TooltipContent
side="right"
align="center"
hidden={state !== "collapsed" || isMobile}
{...tooltip}
/>
</Tooltip>
);
},
);
SidebarMenuButton.displayName = "SidebarMenuButton";
const SidebarMenuAction = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<"button"> & {
asChild?: boolean;
showOnHover?: boolean;
}
>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
ref={ref}
data-sidebar="menu-action"
className={cn(
"absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:-inset-2 after:absolute after:md:hidden",
"peer-data-[size=sm]/menu-button:top-1",
"peer-data-[size=default]/menu-button:top-1.5",
"peer-data-[size=lg]/menu-button:top-2.5",
"group-data-[collapsible=icon]:hidden",
showOnHover &&
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0",
className,
)}
{...props}
/>
);
});
SidebarMenuAction.displayName = "SidebarMenuAction";
const SidebarMenuBadge = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div">
>(({ className, ...props }, ref) => (
<div
ref={ref}
data-sidebar="menu-badge"
className={cn(
"pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 font-medium text-sidebar-foreground text-xs tabular-nums",
"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
"peer-data-[size=sm]/menu-button:top-1",
"peer-data-[size=default]/menu-button:top-1.5",
"peer-data-[size=lg]/menu-button:top-2.5",
"group-data-[collapsible=icon]:hidden",
className,
)}
{...props}
/>
));
SidebarMenuBadge.displayName = "SidebarMenuBadge";
const SidebarMenuSkeleton = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
showIcon?: boolean;
}
>(({ className, showIcon = false, ...props }, ref) => {
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%`;
}, []);
return (
<div
ref={ref}
data-sidebar="menu-skeleton"
className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
{...props}
>
{showIcon && (
<Skeleton
className="size-4 rounded-md"
data-sidebar="menu-skeleton-icon"
/>
)}
<Skeleton
className="h-4 max-w-[--skeleton-width] flex-1"
data-sidebar="menu-skeleton-text"
style={
{
"--skeleton-width": width,
} as React.CSSProperties
}
/>
</div>
);
});
SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton";
const SidebarMenuSub = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
data-sidebar="menu-sub"
className={cn(
"mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-sidebar-border border-l px-2.5 py-0.5",
"group-data-[collapsible=icon]:hidden",
className,
)}
{...props}
/>
));
SidebarMenuSub.displayName = "SidebarMenuSub";
const SidebarMenuSubItem = React.forwardRef<
HTMLLIElement,
React.ComponentProps<"li">
>(({ ...props }, ref) => <li ref={ref} {...props} />);
SidebarMenuSubItem.displayName = "SidebarMenuSubItem";
const SidebarMenuSubButton = React.forwardRef<
HTMLAnchorElement,
React.ComponentProps<"a"> & {
asChild?: boolean;
size?: "sm" | "md";
isActive?: boolean;
}
>(({ asChild = false, size = "md", isActive, className, ...props }, ref) => {
const Comp = asChild ? Slot : "a";
return (
<Comp
ref={ref}
data-sidebar="menu-sub-button"
data-size={size}
data-active={isActive}
className={cn(
"-translate-x-px flex h-7 min-w-0 items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
size === "sm" && "text-xs",
size === "md" && "text-sm",
"group-data-[collapsible=icon]:hidden",
className,
)}
{...props}
/>
);
});
SidebarMenuSubButton.displayName = "SidebarMenuSubButton";
export {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupAction,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInput,
SidebarInset,
SidebarMenu,
SidebarMenuAction,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarRail,
SidebarSeparator,
SidebarTrigger,
useSidebar,
};
+7 -6
View File
@@ -76,10 +76,10 @@ export function TabLinks(props: {
export function TabButtons(props: {
tabs: {
name: string;
name: React.ReactNode;
onClick: () => void;
isActive: boolean;
isEnabled?: boolean;
isDisabled?: boolean;
icon?: React.FC<{ className?: string }>;
}[];
tabClassName?: string;
@@ -108,21 +108,22 @@ export function TabButtons(props: {
className={cn("flex", props.tabContainerClassName)}
ref={containerRef}
>
{props.tabs.map((tab) => {
{props.tabs.map((tab, index) => {
return (
<Button
key={tab.name}
// biome-ignore lint/suspicious/noArrayIndexKey: tabs don't change order, so index is stable
key={index}
variant="ghost"
ref={tab.isActive ? activeTabRef : undefined}
className={cn(
"relative inline-flex h-auto items-center gap-1.5 rounded-lg px-2 font-medium text-sm hover:bg-accent lg:px-3 lg:text-base",
!tab.isActive &&
"text-muted-foreground hover:text-foreground",
!tab.isEnabled && "cursor-not-allowed opacity-50",
tab.isDisabled && "cursor-not-allowed opacity-50",
props.tabClassName,
tab.isActive && props.activeTabClassName,
)}
onClick={tab.isEnabled ? tab.onClick : undefined}
onClick={!tab.isDisabled ? tab.onClick : undefined}
>
{tab.icon && (
<tab.icon className={cn("size-6", props.tabIconClassName)} />
+21
View File
@@ -0,0 +1,21 @@
import * as React from "react";
const MOBILE_BREAKPOINT = 768;
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
undefined,
);
React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
};
mql.addEventListener("change", onChange);
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
return () => mql.removeEventListener("change", onChange);
}, []);
return !!isMobile;
}
@@ -44,14 +44,6 @@ export type Account = {
// TODO - add image URL
};
interface UpdateAccountInput {
name?: string;
email?: string;
linkWallet?: boolean;
subscribeToUpdates?: boolean;
onboardSkipped?: boolean;
}
interface UpdateAccountNotificationsInput {
billing: "email" | "none";
updates: "email" | "none";
@@ -140,45 +132,40 @@ export function useAccountCredits() {
});
}
export function useUpdateAccount() {
const queryClient = useQueryClient();
const address = useActiveAccount()?.address;
type UpdateAccountParams = {
name?: string;
email?: string;
linkWallet?: boolean;
subscribeToUpdates?: boolean;
onboardSkipped?: boolean;
};
return useMutation({
mutationFn: async (input: UpdateAccountInput) => {
type Result = {
data: object;
error?: { message: string };
};
export async function updateAccountClient(input: UpdateAccountParams) {
type Result = {
data: object;
error?: { message: string };
};
const res = await apiServerProxy<Result>({
pathname: "/v1/account",
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(input),
});
if (!res.ok) {
throw new Error(res.error);
}
const json = res.data;
if (json.error) {
throw new Error(json.error.message);
}
return json.data;
},
onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: accountKeys.me(address || ""),
});
const res = await apiServerProxy<Result>({
pathname: "/v1/account",
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(input),
});
if (!res.ok) {
throw new Error(res.error);
}
const json = res.data;
if (json.error) {
throw new Error(json.error.message);
}
return json.data;
}
export function useUpdateNotifications() {
@@ -221,77 +208,61 @@ export function useUpdateNotifications() {
});
}
export function useConfirmEmail() {
return useMutation({
mutationFn: async (input: ConfirmEmailInput) => {
type Result = {
error?: { message: string };
data: { team: Team; account: Account };
};
export const verifyEmailClient = async (input: ConfirmEmailInput) => {
type Result = {
error?: { message: string };
data: { team: Team; account: Account };
};
const res = await apiServerProxy<Result>({
pathname: "/v1/account/confirmEmail",
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(input),
});
if (!res.ok) {
throw new Error(res.error);
}
const json = res.data;
if (json.error) {
throw new Error(json.error.message);
}
return json.data;
const res = await apiServerProxy<Result>({
pathname: "/v1/account/confirmEmail",
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(input),
});
}
export function useResendEmailConfirmation() {
const address = useActiveAccount()?.address;
const queryClient = useQueryClient();
if (!res.ok) {
throw new Error(res.error);
}
return useMutation({
mutationFn: async () => {
type Result = {
error?: { message: string };
data: object;
};
const json = res.data;
const res = await apiServerProxy<Result>({
pathname: "/v1/account/resendEmailConfirmation",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
if (json.error) {
throw new Error(json.error.message);
}
if (!res.ok) {
throw new Error(res.error);
}
return json.data;
};
const json = res.data;
export const resendEmailClient = async () => {
type Result = {
error?: { message: string };
data: object;
};
if (json.error) {
throw new Error(json.error.message);
}
return json.data;
},
onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: accountKeys.me(address || ""),
});
const res = await apiServerProxy<Result>({
pathname: "/v1/account/resendEmailConfirmation",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
}
if (!res.ok) {
throw new Error(res.error);
}
const json = res.data;
if (json.error) {
throw new Error(json.error.message);
}
return json.data;
};
export async function createProjectClient(
teamId: string,
@@ -371,7 +342,6 @@ export type RotateSecretKeyAPIReturnType = {
data: {
secret: string;
secretMasked: string;
secretHash: string;
};
};
@@ -232,13 +232,15 @@ export function useEngineUpdateDeployment() {
export type RemoveEngineFromDashboardIParams = {
instanceId: string;
teamIdOrSlug: string;
};
export async function removeEngineFromDashboard({
instanceId,
teamIdOrSlug,
}: RemoveEngineFromDashboardIParams) {
const res = await apiServerProxy({
pathname: `/v1/engine/${instanceId}`,
pathname: `/v1/teams/${teamIdOrSlug}/engine/${instanceId}`,
method: "DELETE",
});
@@ -273,18 +275,20 @@ export async function deleteCloudHostedEngine({
}
export type EditEngineInstanceParams = {
teamIdOrSlug: string;
instanceId: string;
name: string;
url: string;
};
export async function editEngineInstance({
teamIdOrSlug,
instanceId,
name,
url,
}: EditEngineInstanceParams) {
const res = await apiServerProxy({
pathname: `/v1/engine/${instanceId}`,
pathname: `/v1/teams/${teamIdOrSlug}/engine/${instanceId}`,
method: "PUT",
headers: {
"Content-Type": "application/json",
@@ -1628,7 +1632,7 @@ export function useEngineSubscriptionsLastBlock(params: {
interface EngineResourceMetrics {
error: string;
data: {
result: {
cpu: number;
memory: number;
errorRate: ResultItem[];
@@ -1637,7 +1641,7 @@ interface EngineResourceMetrics {
};
}
export function useEngineSystemMetrics(engineId: string) {
export function useEngineSystemMetrics(engineId: string, teamIdOrSlug: string) {
const [enabled, setEnabled] = useState(true);
return useQuery({
@@ -1645,7 +1649,7 @@ export function useEngineSystemMetrics(engineId: string) {
queryFn: async () => {
const res = await apiServerProxy({
method: "GET",
pathname: `/v1/engine/${engineId}/metrics`,
pathname: `/v1/teams/${teamIdOrSlug}/engine/${engineId}/metrics`,
});
if (!res.ok) {
@@ -1674,14 +1678,14 @@ export interface EngineAlertRule {
pausedAt: Date | null;
}
export function useEngineAlertRules(engineId: string) {
export function useEngineAlertRules(engineId: string, teamIdOrSlug: string) {
return useQuery({
queryKey: engineKeys.alertRules(engineId),
queryFn: async () => {
const res = await apiServerProxy<{
data: EngineAlertRule[];
}>({
pathname: `/v1/engine/${engineId}/alert-rules`,
pathname: `/v1/teams/${teamIdOrSlug}/engine/${engineId}/alert-rules`,
method: "GET",
});
@@ -1703,14 +1707,19 @@ export interface EngineAlert {
endsAt: Date | null;
}
export function useEngineAlerts(engineId: string, limit: number, offset = 0) {
export function useEngineAlerts(
engineId: string,
teamIdOrSlug: string,
limit: number,
offset = 0,
) {
return useQuery({
queryKey: engineKeys.alerts(engineId),
queryFn: async () => {
const res = await apiServerProxy<{
data: EngineAlert[];
}>({
pathname: `/v1/engine/${engineId}/alerts`,
pathname: `/v1/teams/${teamIdOrSlug}/engine/${engineId}/alerts`,
searchParams: {
limit: `${limit}`,
offset: `${offset}`,
@@ -1754,14 +1763,17 @@ export type EngineNotificationChannel = {
subscriptionRoutes: string[];
};
export function useEngineNotificationChannels(engineId: string) {
export function useEngineNotificationChannels(
engineId: string,
teamIdOrSlug: string,
) {
return useQuery({
queryKey: engineKeys.notificationChannels(engineId),
queryFn: async () => {
const res = await apiServerProxy<{
data: EngineNotificationChannel[];
}>({
pathname: `/v1/engine/${engineId}/notification-channels`,
pathname: `/v1/teams/${teamIdOrSlug}/engine/${engineId}/notification-channels`,
method: "GET",
});
@@ -1781,7 +1793,10 @@ export interface CreateNotificationChannelInput {
value: string;
}
export function useEngineCreateNotificationChannel(engineId: string) {
export function useEngineCreateNotificationChannel(
engineId: string,
teamIdOrSlug: string,
) {
const queryClient = useQueryClient();
return useMutation({
@@ -1789,7 +1804,7 @@ export function useEngineCreateNotificationChannel(engineId: string) {
const res = await apiServerProxy<{
data: EngineNotificationChannel;
}>({
pathname: `/v1/engine/${engineId}/notification-channels`,
pathname: `/v1/teams/${teamIdOrSlug}/engine/${engineId}/notification-channels`,
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -1812,13 +1827,16 @@ export function useEngineCreateNotificationChannel(engineId: string) {
});
}
export function useEngineDeleteNotificationChannel(engineId: string) {
export function useEngineDeleteNotificationChannel(
engineId: string,
teamIdOrSlug: string,
) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (notificationChannelId: string) => {
const res = await apiServerProxy({
pathname: `/v1/engine/${engineId}/notification-channels/${notificationChannelId}`,
pathname: `/v1/teams/${teamIdOrSlug}/engine/${engineId}/notification-channels/${notificationChannelId}`,
method: "DELETE",
});
@@ -5,6 +5,7 @@ import invariant from "tiny-invariant";
export function useWalletNFTs(params: {
chainId: number;
walletAddress?: string;
isInsightSupported: boolean;
}) {
return useQuery({
queryKey: ["walletNfts", params.chainId, params.walletAddress],
@@ -13,6 +14,7 @@ export function useWalletNFTs(params: {
return getWalletNFTs({
chainId: params.chainId,
owner: params.walletAddress,
isInsightSupported: params.isInsightSupported,
});
},
enabled: !!params.walletAddress,
@@ -1,59 +1,42 @@
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
import { NATIVE_TOKEN_ADDRESS, defineChain, getContract } from "thirdweb";
import { defineChain } from "thirdweb";
import { getChainMetadata } from "thirdweb/chains";
import { name } from "thirdweb/extensions/common";
type RouteListCardProps = {
originChainId: number;
originTokenAddress: string;
originTokenIconUri: string | null;
originTokenIconUri?: string | null;
originTokenSymbol: string;
originTokenName: string;
destinationChainId: number;
destinationTokenAddress: string;
destinationTokenIconUri: string | null;
destinationTokenIconUri?: string | null;
destinationTokenSymbol: string;
destinationTokenName: string;
};
export async function RouteListCard({
originChainId,
originTokenAddress,
originTokenIconUri,
originTokenName,
destinationChainId,
destinationTokenAddress,
destinationTokenIconUri,
destinationTokenName,
}: RouteListCardProps) {
const [
originChain,
originTokenName,
destinationChain,
destinationTokenName,
resolvedOriginTokenIconUri,
resolvedDestinationTokenIconUri,
] = await Promise.all([
// eslint-disable-next-line no-restricted-syntax
getChainMetadata(defineChain(originChainId)),
originTokenAddress.toLowerCase() === NATIVE_TOKEN_ADDRESS
? "ETH"
: name({
contract: getContract({
address: originTokenAddress,
// eslint-disable-next-line no-restricted-syntax
chain: defineChain(originChainId),
client: getThirdwebClient(),
}),
}).catch(() => undefined),
// eslint-disable-next-line no-restricted-syntax
getChainMetadata(defineChain(destinationChainId)),
destinationTokenAddress.toLowerCase() === NATIVE_TOKEN_ADDRESS
? "ETH"
: name({
contract: getContract({
address: destinationTokenAddress,
// eslint-disable-next-line no-restricted-syntax
chain: defineChain(destinationChainId),
client: getThirdwebClient(),
}),
}).catch(() => undefined),
originTokenIconUri
? resolveSchemeWithErrorHandler({
uri: originTokenIconUri,
@@ -74,19 +57,21 @@ export async function RouteListCard({
<CardHeader className="flex flex-row items-center justify-between p-4">
<div className="flex flex-row items-center gap-2">
{resolvedOriginTokenIconUri ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={resolvedOriginTokenIconUri}
alt={originTokenAddress}
className="size-8 rounded-full bg-white"
className="size-8 rounded-full border border-muted-foreground"
/>
) : (
<div className="size-8 rounded-full bg-white/10" />
<div className="size-8 rounded-full bg-muted-foreground" />
)}
{resolvedDestinationTokenIconUri ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={resolvedDestinationTokenIconUri}
alt={destinationTokenAddress}
className="-translate-x-4 size-8 rounded-full bg-white ring-2 ring-card"
className="-translate-x-4 size-8 rounded-full border border-muted-foreground ring-2 ring-card"
/>
) : (
<div className="-translate-x-4 size-8 rounded-full bg-muted-foreground ring-2 ring-card" />
@@ -2,59 +2,41 @@ import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { TableCell, TableRow } from "@/components/ui/table";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
import { NATIVE_TOKEN_ADDRESS, getContract } from "thirdweb";
import { defineChain, getChainMetadata } from "thirdweb/chains";
import { symbol } from "thirdweb/extensions/common";
type RouteListRowProps = {
originChainId: number;
originTokenAddress: string;
originTokenIconUri: string | null;
originTokenIconUri?: string | null;
originTokenSymbol?: string;
originTokenName?: string;
destinationChainId: number;
destinationTokenAddress: string;
destinationTokenIconUri: string | null;
destinationTokenIconUri?: string | null;
destinationTokenSymbol?: string;
destinationTokenName?: string;
};
export async function RouteListRow({
originChainId,
originTokenAddress,
originTokenIconUri,
originTokenSymbol,
destinationChainId,
destinationTokenAddress,
destinationTokenIconUri,
destinationTokenSymbol,
}: RouteListRowProps) {
const [
originChain,
originTokenSymbol,
destinationChain,
destinationTokenSymbol,
resolvedOriginTokenIconUri,
resolvedDestinationTokenIconUri,
] = await Promise.all([
// eslint-disable-next-line no-restricted-syntax
getChainMetadata(defineChain(originChainId)),
originTokenAddress.toLowerCase() === NATIVE_TOKEN_ADDRESS
? "ETH"
: symbol({
contract: getContract({
address: originTokenAddress,
// eslint-disable-next-line no-restricted-syntax
chain: defineChain(originChainId),
client: getThirdwebClient(),
}),
}).catch(() => undefined),
// eslint-disable-next-line no-restricted-syntax
getChainMetadata(defineChain(destinationChainId)),
destinationTokenAddress.toLowerCase() === NATIVE_TOKEN_ADDRESS
? "ETH"
: symbol({
contract: getContract({
address: destinationTokenAddress,
// eslint-disable-next-line no-restricted-syntax
chain: defineChain(destinationChainId),
client: getThirdwebClient(),
}),
}).catch(() => undefined),
originTokenIconUri
? resolveSchemeWithErrorHandler({
uri: originTokenIconUri,
@@ -76,10 +58,11 @@ export async function RouteListRow({
<div className="flex items-center gap-1">
{resolvedOriginTokenIconUri ? (
// For now we're using a normal img tag because the domain for these images is unknown
// eslint-disable-next-line @next/next/no-img-element
<img
src={resolvedOriginTokenIconUri}
alt={originTokenAddress}
className="size-6 rounded-full bg-muted-foreground"
className="size-6 rounded-full border border-muted-foreground"
/>
) : (
<div className="size-6 rounded-full bg-muted-foreground" />
@@ -110,10 +93,11 @@ export async function RouteListRow({
<div className="flex flex-row items-center gap-4">
<div className="flex items-center gap-1">
{resolvedDestinationTokenIconUri ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={resolvedDestinationTokenIconUri}
alt={destinationTokenAddress}
className="size-6 rounded-full bg-muted-foreground"
className="size-6 rounded-full border border-muted-foreground"
/>
) : (
<div className="size-6 rounded-full bg-muted-foreground" />
@@ -50,7 +50,7 @@ async function getRoutesToRender(params: SearchParams) {
}
}
// Temporary, will update this after the /routes endpoint
filters.limit = 10_000;
filters.limit = 50_000;
const routes = await getRoutes(filters);
@@ -107,9 +107,13 @@ export async function RoutesData(props: {
originChainId={route.originToken.chainId}
originTokenAddress={route.originToken.address}
originTokenIconUri={route.originToken.iconUri}
originTokenSymbol={route.originToken.symbol}
originTokenName={route.originToken.name}
destinationChainId={route.destinationToken.chainId}
destinationTokenAddress={route.destinationToken.address}
destinationTokenIconUri={route.destinationToken.iconUri}
destinationTokenSymbol={route.destinationToken.symbol}
destinationTokenName={route.destinationToken.name}
/>
))}
</TableBody>
@@ -126,9 +130,13 @@ export async function RoutesData(props: {
originChainId={route.originToken.chainId}
originTokenAddress={route.originToken.address}
originTokenIconUri={route.originToken.iconUri}
originTokenSymbol={route.originToken.symbol}
originTokenName={route.originToken.name}
destinationChainId={route.destinationToken.chainId}
destinationTokenAddress={route.destinationToken.address}
destinationTokenIconUri={route.destinationToken.iconUri}
destinationTokenSymbol={route.destinationToken.symbol}
destinationTokenName={route.destinationToken.name}
/>
</li>
))}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

@@ -22,7 +22,7 @@ export const metadata: Metadata = {
},
};
export default async function ChainListPage(props: {
export default async function RoutesPage(props: {
searchParams: Promise<SearchParams>;
}) {
const authToken = await getAuthToken();
@@ -4,11 +4,15 @@ export type Route = {
originToken: {
address: Address;
chainId: number;
iconUri: string;
iconUri?: string;
name: string;
symbol: string;
};
destinationToken: {
address: Address;
chainId: number;
iconUri: string;
iconUri?: string;
name: string;
symbol: string;
};
};
@@ -52,7 +52,7 @@ import {
useWalletBalance,
} from "thirdweb/react";
import { z } from "zod";
import { isOnboardingComplete } from "../../../../../../login/onboarding/isOnboardingRequired";
import { isAccountOnboardingComplete } from "../../../../../../login/onboarding/isOnboardingRequired";
function formatTime(seconds: number) {
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
@@ -210,7 +210,7 @@ export function FaucetButton({
);
}
if (!isOnboardingComplete(twAccount)) {
if (!isAccountOnboardingComplete(twAccount)) {
return (
<Button asChild className="w-full">
<Link
@@ -28,7 +28,7 @@ export function BuyFundsSection(props: { chain: ChainMetadata }) {
<div className="h-6" />
<h2 className="px-4 text-center font-semibold text-lg tracking-tight">
Buy Funds on {sanitizedChainName} using thirdweb Pay
Buy Funds on {sanitizedChainName} using Universal Bridge
</h2>
<div className="h-2" />
@@ -50,8 +50,9 @@ export function BuyFundsSection(props: { chain: ChainMetadata }) {
<Link
href="https://portal.thirdweb.com/connect/pay/overview"
className="inline-flex items-center gap-1.5 text-muted-foreground text-sm hover:text-foreground"
target="_blank"
>
Learn more about thirdweb Pay
Learn more about Universal Bridge
<ExternalLinkIcon className="size-3" />
</Link>
</div>
@@ -13,7 +13,6 @@ import { ListerOnly } from "@3rdweb-sdk/react/components/roles/lister-only";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { isAlchemySupported } from "lib/wallet/nfts/alchemy";
import { isMoralisSupported } from "lib/wallet/nfts/moralis";
import { useSimplehashSupport } from "lib/wallet/nfts/simpleHash";
import { PlusIcon } from "lucide-react";
import { useState } from "react";
import type { ThirdwebContract } from "thirdweb";
@@ -25,6 +24,7 @@ interface CreateListingButtonProps {
createText?: string;
type?: "direct-listings" | "english-auctions";
twAccount: Account | undefined;
isInsightSupported: boolean;
}
const LISTING_MODES = ["Select NFT", "Manual"] as const;
@@ -34,6 +34,7 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
type,
contract,
twAccount,
isInsightSupported,
...restButtonProps
}) => {
const address = useActiveAccount()?.address;
@@ -41,13 +42,12 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
const [listingMode, setListingMode] =
useState<(typeof LISTING_MODES)[number]>("Select NFT");
const simplehashQuery = useSimplehashSupport(contract.chain.id);
const isSupportedChain =
contract.chain.id &&
(simplehashQuery.data ||
(isInsightSupported ||
isAlchemySupported(contract.chain.id) ||
isMoralisSupported(contract.chain.id));
return (
<ListerOnly contract={contract}>
<Sheet open={open} onOpenChange={setOpen}>
@@ -71,7 +71,6 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
name: mode,
isActive: mode === listingMode,
onClick: () => setListingMode(mode),
isEnabled: true,
}))}
tabClassName="text-sm gap-2 !text-sm"
tabContainerClassName="gap-0.5"
@@ -84,6 +83,7 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
actionText={createText}
setOpen={setOpen}
mode={listingMode === "Select NFT" ? "automatic" : "manual"}
isInsightSupported={isInsightSupported}
/>
</div>
</>
@@ -96,6 +96,7 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
actionText={createText}
setOpen={setOpen}
mode="manual"
isInsightSupported={isInsightSupported}
/>
</div>
)}
@@ -21,7 +21,6 @@ import { useAllChainsData } from "hooks/chains/allChains";
import { useTxNotifications } from "hooks/useTxNotifications";
import { isAlchemySupported } from "lib/wallet/nfts/alchemy";
import { isMoralisSupported } from "lib/wallet/nfts/moralis";
import { useSimplehashSupport } from "lib/wallet/nfts/simpleHash";
import type { WalletNFT } from "lib/wallet/nfts/types";
import { CircleAlertIcon, InfoIcon } from "lucide-react";
import Link from "next/link";
@@ -87,6 +86,7 @@ type CreateListingsFormProps = {
mode: "automatic" | "manual";
type?: "direct-listings" | "english-auctions";
twAccount: Account | undefined;
isInsightSupported: boolean;
};
const auctionTimes = [
@@ -106,16 +106,17 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
setOpen,
twAccount,
mode,
isInsightSupported,
}) => {
const trackEvent = useTrack();
const chainId = contract.chain.id;
const { idToChain } = useAllChainsData();
const network = idToChain.get(chainId);
const [isFormLoading, setIsFormLoading] = useState(false);
const simplehashQuery = useSimplehashSupport(contract.chain.id);
const isSupportedChain =
chainId &&
(!!simplehashQuery.data ||
(isInsightSupported ||
isAlchemySupported(chainId) ||
isMoralisSupported(chainId));
@@ -124,7 +125,9 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
const { data: walletNFTs, isPending: isWalletNFTsLoading } = useWalletNFTs({
chainId,
walletAddress: account?.address,
isInsightSupported,
});
const sendAndConfirmTx = useSendAndConfirmTransaction();
const listingNotifications = useTxNotifications(
"NFT listed Successfully",
@@ -29,6 +29,7 @@ export function ContractDirectListingsPageClient(props: {
<ContractDirectListingsPage
contract={props.contract}
twAccount={props.twAccount}
isInsightSupported={metadataQuery.data.isInsightSupported}
/>
);
}
@@ -8,17 +8,19 @@ import { DirectListingsTable } from "./components/table";
interface ContractDirectListingsPageProps {
contract: ThirdwebContract;
twAccount: Account | undefined;
isInsightSupported: boolean;
}
export const ContractDirectListingsPage: React.FC<
ContractDirectListingsPageProps
> = ({ contract, twAccount }) => {
> = ({ contract, twAccount, isInsightSupported }) => {
return (
<div className="flex flex-col gap-6">
<div className="flex flex-row items-center justify-between">
<p className="text-lg">Contract Listings</p>
<div className="flex flex-row gap-4">
<CreateListingButton
isInsightSupported={isInsightSupported}
contract={contract}
type="direct-listings"
createText="Create Direct Listing"
@@ -29,15 +29,18 @@ export default async function Page(props: {
);
}
const { isDirectListingSupported } = await getContractPageMetadata(
info.contract,
);
const { isDirectListingSupported, isInsightSupported } =
await getContractPageMetadata(info.contract);
if (!isDirectListingSupported) {
redirect(`/${params.chain_id}/${params.contractAddress}`);
}
return (
<ContractDirectListingsPage contract={info.contract} twAccount={account} />
<ContractDirectListingsPage
contract={info.contract}
twAccount={account}
isInsightSupported={isInsightSupported}
/>
);
}
@@ -29,6 +29,7 @@ export function ContractEnglishAuctionsPageClient(props: {
<ContractEnglishAuctionsPage
contract={props.contract}
twAccount={props.twAccount}
isInsightSupported={metadataQuery.data.isInsightSupported}
/>
);
}
@@ -8,11 +8,12 @@ import { EnglishAuctionsTable } from "./components/table";
interface ContractEnglishAuctionsProps {
contract: ThirdwebContract;
twAccount: Account | undefined;
isInsightSupported: boolean;
}
export const ContractEnglishAuctionsPage: React.FC<
ContractEnglishAuctionsProps
> = ({ contract, twAccount }) => {
> = ({ contract, twAccount, isInsightSupported }) => {
return (
<div className="flex flex-col gap-6">
<div className="flex flex-row items-center justify-between">
@@ -23,6 +24,7 @@ export const ContractEnglishAuctionsPage: React.FC<
type="english-auctions"
createText="Create English Auction"
twAccount={twAccount}
isInsightSupported={isInsightSupported}
/>
</div>
</div>
@@ -30,9 +30,8 @@ export default async function Page(props: {
);
}
const { isEnglishAuctionSupported } = await getContractPageMetadata(
info.contract,
);
const { isEnglishAuctionSupported, isInsightSupported } =
await getContractPageMetadata(info.contract);
if (!isEnglishAuctionSupported) {
redirect(`/${params.chain_id}/${params.contractAddress}`);
@@ -42,6 +41,7 @@ export default async function Page(props: {
<ContractEnglishAuctionsPage
contract={info.contract}
twAccount={twAccount}
isInsightSupported={isInsightSupported}
/>
);
}
@@ -21,7 +21,7 @@ import {
} from "./detectedFeatures/permissions";
import { supportedERCs } from "./detectedFeatures/supportedERCs";
import { type EmbedTypeToShow, getEmbedTypeToShow } from "./getEmbedTypeToShow";
import { isAnalyticsSupportedForChain } from "./isAnalyticsSupportedForChain";
import { isInsightSupportedForChain } from "./isAnalyticsSupportedForChain";
export type ContractPageMetadata = {
supportedERCs: {
@@ -36,7 +36,7 @@ export type ContractPageMetadata = {
isPermissionsEnumerableSupported: boolean;
isModularCore: boolean;
embedType: EmbedTypeToShow;
isAnalyticsSupported: boolean;
isInsightSupported: boolean;
isSplitSupported: boolean;
isERC721ClaimConditionsSupported: boolean;
isERC20ClaimConditionsSupported: boolean;
@@ -47,7 +47,7 @@ export type ContractPageMetadata = {
};
export async function getContractPageMetadata(contract: ThirdwebContract) {
return getContractPageMetadataSetup(contract, isAnalyticsSupportedForChain);
return getContractPageMetadataSetup(contract, isInsightSupportedForChain);
}
export async function getContractPageMetadataSetup(
@@ -56,7 +56,7 @@ export async function getContractPageMetadataSetup(
): Promise<ContractPageMetadata> {
const [
functionSelectorsResult,
isAnalyticsSupportedResult,
isInsightSupportedResult,
contractTypeResult,
] = await Promise.allSettled([
resolveFunctionSelectors(contract),
@@ -69,9 +69,9 @@ export async function getContractPageMetadataSetup(
? functionSelectorsResult.value
: [];
const isAnalyticsSupported =
isAnalyticsSupportedResult.status === "fulfilled"
? isAnalyticsSupportedResult.value
const isInsightSupported =
isInsightSupportedResult.status === "fulfilled"
? isInsightSupportedResult.value
: true;
const contractType =
@@ -86,7 +86,7 @@ export async function getContractPageMetadataSetup(
isPermissionsEnumerableSupported(functionSelectors),
isModularCore: isModularCoreContract(functionSelectors),
embedType: getEmbedTypeToShow(functionSelectors),
isAnalyticsSupported: isAnalyticsSupported,
isInsightSupported: isInsightSupported,
isSplitSupported: contractType === "Split",
isVoteContract: contractType === "VoteERC20",
isERC721ClaimConditionsSupported:
@@ -47,7 +47,7 @@ export function getContractPageSidebarLinks(data: {
{
label: "Analytics",
href: `${layoutPrefix}/analytics`,
hide: !data.metadata.isAnalyticsSupported,
hide: !data.metadata.isInsightSupported,
exactMatch: true,
},
{
@@ -4,7 +4,7 @@ import { getVercelEnv } from "lib/vercel-utils";
const thirdwebDomain =
getVercelEnv() !== "production" ? "thirdweb-dev" : "thirdweb";
export async function isAnalyticsSupportedForChain(
export async function isInsightSupportedForChain(
chainId: number,
): Promise<boolean> {
try {
@@ -32,6 +32,7 @@ export function AccountPageClient(props: {
contract={props.contract}
chainMetadata={props.chainMetadata}
twAccount={props.twAccount}
isInsightSupported={metadataQuery.data.isInsightSupported}
/>
);
}
@@ -12,12 +12,14 @@ interface AccountPageProps {
contract: ThirdwebContract;
chainMetadata: ChainMetadata;
twAccount: Account | undefined;
isInsightSupported: boolean;
}
export const AccountPage: React.FC<AccountPageProps> = ({
contract,
chainMetadata,
twAccount,
isInsightSupported,
}) => {
const symbol = chainMetadata.nativeCurrency.symbol || "Native Token";
@@ -43,7 +45,7 @@ export const AccountPage: React.FC<AccountPageProps> = ({
<div className="flex flex-row items-center justify-between">
<Heading size="title.sm">NFTs owned</Heading>
</div>
<NftsOwned contract={contract} />
<NftsOwned contract={contract} isInsightSupported={isInsightSupported} />
</div>
);
};
@@ -6,12 +6,17 @@ import { NFTCards } from "../../_components/NFTCards";
interface NftsOwnedProps {
contract: ThirdwebContract;
isInsightSupported: boolean;
}
export const NftsOwned: React.FC<NftsOwnedProps> = ({ contract }) => {
export const NftsOwned: React.FC<NftsOwnedProps> = ({
contract,
isInsightSupported,
}) => {
const { data: walletNFTs, isPending: isWalletNFTsLoading } = useWalletNFTs({
chainId: contract.chain.id,
walletAddress: contract.address,
isInsightSupported: isInsightSupported,
});
const nfts = walletNFTs?.result || [];
@@ -33,7 +33,8 @@ export default async function Page(props: {
);
}
const { isAccount } = await getContractPageMetadata(contract);
const { isAccount, isInsightSupported } =
await getContractPageMetadata(contract);
if (!isAccount) {
redirect(`/${params.chain_id}/${params.contractAddress}`);
@@ -44,6 +45,7 @@ export default async function Page(props: {
contract={contract}
chainMetadata={chainMetadata}
twAccount={account}
isInsightSupported={isInsightSupported}
/>
);
}
@@ -21,7 +21,7 @@ export function ContractAnalyticsPageClient(props: {
return <ErrorPage />;
}
if (!metadataQuery.data.isAnalyticsSupported) {
if (!metadataQuery.data.isInsightSupported) {
return <RedirectToContractOverview contract={props.contract} />;
}
@@ -34,9 +34,9 @@ export default async function Page(props: {
);
}
const { isAnalyticsSupported } = await getContractPageMetadata(info.contract);
const { isInsightSupported } = await getContractPageMetadata(info.contract);
if (!isAnalyticsSupported) {
if (!isInsightSupported) {
redirect(`/${params.chain_id}/${params.contractAddress}`);
}
@@ -1,7 +1,14 @@
"use client";
import { Button } from "@/components/ui/button";
import { Form } from "@/components/ui/form";
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Table,
TableBody,
@@ -11,6 +18,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
@@ -33,7 +41,9 @@ import { useForm } from "react-hook-form";
import {
defineChain,
eth_getCode,
getContract,
getRpcClient,
prepareContractCall,
prepareTransaction,
sendAndConfirmTransaction,
} from "thirdweb";
@@ -57,19 +67,21 @@ type CrossChain = {
status: "DEPLOYED" | "NOT_DEPLOYED";
};
const interopChains = ["420120000", "420120001"];
type ChainId = "420120000" | "420120001";
const formSchema = z.object({
amounts: z.object({
"84532": z.string(),
"11155420": z.string(),
"919": z.string(),
"111557560": z.string(),
"999999999": z.string(),
"11155111": z.string(),
"421614": z.string(),
"420120000": z.string(),
"420120001": z.string(),
}),
});
type FormSchema = z.output<typeof formSchema>;
const positiveIntegerRegex = /^[0-9]\d*$/;
const superchainBridgeAddress = "0x4200000000000000000000000000000000000028";
export function DataTable({
data,
coreMetadata,
@@ -97,13 +109,21 @@ export function DataTable({
"Failed to deploy contract",
);
const isCrosschain = !!modulesMetadata?.find(
(m) => m.name === "SuperChainInterop",
);
const addRowMutation = useMutation({
mutationFn: async (chain: { chainId: number; name: string }) => {
if (coreContract.chain.id === chain.chainId) {
return;
}
// eslint-disable-next-line no-restricted-syntax
const c = defineChain(chain.chainId);
const code = await eth_getCode(
getRpcClient({
client: getThirdwebClient(),
// eslint-disable-next-line no-restricted-syntax
chain: defineChain(chain.chainId),
chain: c,
}),
{ address: coreContract.address },
);
@@ -155,19 +175,59 @@ export function DataTable({
const form = useForm<FormSchema>({
resolver: zodResolver(formSchema),
values: {
defaultValues: {
amounts: {
"84532": "", // Base
"11155420": "", // OP testnet
"919": "", // Mode Network
"111557560": "", // Cyber
"999999999": "", // Zora
"11155111": "", // Sepolia
"421614": "",
"420120000": "",
"420120001": "",
},
},
});
const crossChainTransfer = async (chainId: ChainId) => {
if (!activeAccount) {
throw new Error("Account not connected");
}
const amount = form.getValues().amounts[chainId];
if (!positiveIntegerRegex.test(amount)) {
form.setError(`amounts.${chainId}`, { message: "Invalid Amount" });
return;
}
const superChainBridge = getContract({
address: superchainBridgeAddress,
chain: coreContract.chain,
client: coreContract.client,
});
const sendErc20Tx = prepareContractCall({
contract: superChainBridge,
method:
"function sendERC20(address _token, address _to, uint256 _amount, uint256 _chainId)",
params: [
coreContract.address,
activeAccount.address,
BigInt(amount),
BigInt(chainId),
],
});
await sendAndConfirmTransaction({
account: activeAccount,
transaction: sendErc20Tx,
});
};
const crossChainTransferNotifications = useTxNotifications(
"Successfully submitted cross chain transfer",
"Failed to submit cross chain transfer",
);
const crossChainTransferMutation = useMutation({
mutationFn: crossChainTransfer,
onSuccess: crossChainTransferNotifications.onSuccess,
onError: crossChainTransferNotifications.onError,
});
const columns: ColumnDef<CrossChain>[] = [
{
accessorKey: "network",
@@ -208,6 +268,54 @@ export function DataTable({
);
},
},
{
accessorKey: "transfer",
header: "",
cell: ({ row }) => {
const chain = row.getValue("chainId");
if (
row.getValue("status") === "DEPLOYED" &&
interopChains.includes(String(chain)) &&
isCrosschain
) {
return (
<FormField
disabled={false}
control={form.control}
name={`amounts.${row.getValue("chainId") as ChainId}`}
render={({ field }) => (
<FormItem>
<FormControl>
<ToolTipLabel label="Bridge tokens">
<div className="flex">
<Input
className="w-22 rounded-r-none border-r-0"
placeholder="amount"
{...field}
/>
<Button
type="button"
disabled={false}
onClick={() =>
crossChainTransferMutation.mutate(
row.getValue("chainId"),
)
}
className="rounded-lg rounded-l-none border border-l-0"
>
Bridge
</Button>
</div>
</ToolTipLabel>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
}
},
},
];
const table = useReactTable({
@@ -268,6 +376,18 @@ export function DataTable({
crosschainContractAddress = coreContract.address;
}
} else {
if (modulesMetadata) {
for (const m of modulesMetadata) {
await getOrDeployInfraForPublishedContract({
chain,
client,
account: activeAccount,
contractId: m.name,
publisher: m.publisher,
});
}
}
crosschainContractAddress = await deployContractfromDeployMetadata({
account: activeAccount,
chain,
@@ -283,17 +403,6 @@ export function DataTable({
chain,
client,
});
if (modulesMetadata) {
for (const m of modulesMetadata) {
await getOrDeployInfraForPublishedContract({
chain,
client,
account: activeAccount,
contractId: m.name,
publisher: m.publisher,
});
}
}
}
deployStatusModal.nextStep();
deployStatusModal.setViewContractLink(
@@ -2,6 +2,7 @@ import { fetchPublishedContractsFromDeploy } from "components/contract-component
import { notFound } from "next/navigation";
import {
type ContractOptions,
eth_blockNumber,
eth_getTransactionByHash,
eth_getTransactionReceipt,
getContractEvents,
@@ -110,10 +111,16 @@ export default async function Page(props: {
let creationBlockNumber: bigint | undefined;
if (twCloneFactoryContract) {
const latestBlockNumber = await eth_blockNumber(
getRpcClient({
client: contract.client,
chain: contract.chain,
}),
);
const events = await getContractEvents({
contract: twCloneFactoryContract,
events: [ProxyDeployedEvent],
blockRange: 123456n,
blockRange: latestBlockNumber < 100000n ? latestBlockNumber : 100000n,
});
const event = events.find(
(e) =>
@@ -165,7 +172,7 @@ export default async function Page(props: {
}
}),
)
).filter((c) => c.status === "DEPLOYED");
).filter((c) => c.chainId !== contract.chain.id);
let coreMetadata: FetchDeployMetadataResult | undefined;
try {
@@ -260,10 +267,11 @@ export default async function Page(props: {
<>
<div>
<h2 className="mb-1 font-bold text-2xl tracking-tight">
Deploy Cross-chain
Cross-chain contracts
</h2>
<p className="text-muted-foreground">
Deterministically deploy your contracts on multiple networks.
Deterministically deploy and interact with your contracts on multiple
networks.
</p>
</div>
<div className="h-10" />
@@ -109,6 +109,7 @@ async function getTeamsAndProjectsIfLoggedIn() {
projects: (await getProjects(team.slug)).map((x) => ({
id: x.id,
name: x.name,
image: x.image,
})),
})),
);
@@ -133,7 +133,7 @@ export function ModuleCard(props: ModuleCardProps) {
open={isUninstallModalOpen}
onOpenChange={setIsUninstallModalOpen}
>
<DialogContent className="z-[10001]" dialogOverlayClassName="z-[10000]">
<DialogContent>
<form
onSubmit={(e) => {
e.preventDefault();
@@ -34,7 +34,7 @@ export function ContractOverviewPageClient(props: {
contractPageMetadata.isPermissionsEnumerableSupported
}
chainSlug={chainMetadata.slug}
isAnalyticsSupported={contractPageMetadata.isAnalyticsSupported}
isAnalyticsSupported={contractPageMetadata.isInsightSupported}
functionSelectors={contractPageMetadata.functionSelectors}
// TODO - create a fully client rendered version of publishedBy and ContractCard and plug it here
publishedBy={undefined}
@@ -44,7 +44,7 @@ export default async function Page(props: {
contractPageMetadata.isPermissionsEnumerableSupported
}
chainSlug={chainMetadata.slug}
isAnalyticsSupported={contractPageMetadata.isAnalyticsSupported}
isAnalyticsSupported={contractPageMetadata.isInsightSupported}
functionSelectors={contractPageMetadata.functionSelectors}
publishedBy={
<ErrorBoundary fallback={null}>
@@ -49,7 +49,7 @@ export const ProposalButton: React.FC<VoteButtonProps> = ({
Create Proposal
</Button>
</SheetTrigger>
<SheetContent className="z-[10000] w-full sm:w-[540px] sm:max-w-[90%] lg:w-[700px]">
<SheetContent className="w-full sm:w-[540px] sm:max-w-[90%] lg:w-[700px]">
<SheetHeader>
<SheetTitle>Create new proposal</SheetTitle>
</SheetHeader>
@@ -70,7 +70,7 @@ export const TokenBurnButton: React.FC<TokenBurnButtonProps> = ({
<Flame size={16} /> Burn
</Button>
</SheetTrigger>
<SheetContent className="z-[10000]">
<SheetContent>
<SheetHeader>
<SheetTitle className="text-left">Burn tokens</SheetTitle>
</SheetHeader>
@@ -60,7 +60,7 @@ export const TokenClaimButton: React.FC<TokenClaimButtonProps> = ({
<GemIcon size={16} /> Claim
</Button>
</SheetTrigger>
<SheetContent className="z-[10000]">
<SheetContent>
<SheetHeader>
<SheetTitle className="text-left">Claim tokens</SheetTitle>
</SheetHeader>
@@ -58,7 +58,7 @@ export const TokenMintButton: React.FC<TokenMintButtonProps> = ({
<PlusIcon size={16} /> Mint
</Button>
</SheetTrigger>
<SheetContent className="z-[10000]">
<SheetContent>
<SheetHeader>
<SheetTitle className="text-left">
Mint additional tokens
@@ -64,7 +64,7 @@ export const TokenTransferButton: React.FC<TokenTransferButtonProps> = ({
<SendIcon size={16} /> Transfer
</Button>
</SheetTrigger>
<SheetContent className="z-[10000]">
<SheetContent>
<SheetHeader>
<SheetTitle className="text-left">Transfer tokens</SheetTitle>
</SheetHeader>
@@ -45,7 +45,7 @@ export const products = [
link: "https://portal.thirdweb.com/connect/account-abstraction/overview",
},
{
name: "Pay",
name: "Universal Bridge",
id: "pay",
icon: PayIcon,
description: "Point of sale solution for bridging, onramping & swapping",
Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

@@ -10,6 +10,8 @@ import ancient8Banner from "./temp-assets/ancient8-banner.png";
import assetChainBanner from "./temp-assets/assetChainBanner.png";
import assetChainCTABG from "./temp-assets/assetChainCTABanner.png";
import hemiSepoliaBanner from "./temp-assets/HemiBanner.png";
import hemiSepoliaCTA from "./temp-assets/HemiCTA.png";
import appChainBanner from "./temp-assets/appChainBanner.jpg";
// TEMPORARY
import appchainCTA from "./temp-assets/appchainCTA.png";
@@ -45,6 +47,8 @@ import metalBanner from "./temp-assets/metalBanner.png";
import plumeBanner from "./temp-assets/plumeBanner.png";
import plumeCTA from "./temp-assets/plumeCTA.png";
import reactiveBanner from "./temp-assets/reactiveBanner.jpg";
import reactiveCTA from "./temp-assets/reactiveCTA.jpg";
import rivalzBanner from "./temp-assets/rivalzBanner.png";
import rivalzCTA from "./temp-assets/rivalzCTA.png";
import rootStockBanner from "./temp-assets/rootstock-banner.png";
@@ -452,6 +456,18 @@ const chainMetaRecord = {
buttonText: "Learn more",
},
},
//HemiSepolia
743111: {
headerImgUrl: hemiSepoliaBanner.src,
about:
"The Hemi Network is a modular Layer-2 protocol for superior scaling, security, and interoperability, powered by Bitcoin and Ethereum.",
cta: {
backgroundImageUrl: hemiSepoliaCTA.src,
title: "One Network, Powered by Bitcoin and Ethereum",
buttonLink: "https://hemi.xyz/",
buttonText: "Learn More",
},
},
//Hashfire
4227: {
headerImgUrl: hashfireBanner.src,
@@ -561,6 +577,18 @@ const chainMetaRecord = {
buttonText: "Learn More",
},
},
//Reactive Mainnet
1597: {
headerImgUrl: reactiveBanner.src,
about:
"Web3’s first IFTTT infrastructure. Automate multi-chain workflows with event-driven logic – execute actions without compromising decentralization.",
cta: {
backgroundImageUrl: reactiveCTA.src,
title: "Turn Smart Contracts Reactive",
buttonLink: "https://dev.reactive.network",
buttonText: "Learn More",
},
},
//Saakuru Mainnet
7225878: {
headerImgUrl: saakuruBanner.src,
@@ -36,6 +36,7 @@ export async function DeployFormForUri(props: DeployFormForUriProps) {
projects: (await getProjects(team.slug)).map((x) => ({
id: x.id,
name: x.name,
image: x.image,
})),
})),
);
@@ -10,6 +10,11 @@ import { useCallback, useState } from "react";
import { useActiveWallet, useDisconnect } from "thirdweb/react";
import { LazyCreateProjectDialog } from "../../../components/settings/ApiKeys/Create/LazyCreateAPIKeyDialog";
import { doLogout } from "../../login/auth-actions";
import {
getChangelogNotifications,
getInboxNotifications,
markNotificationAsRead,
} from "../../team/components/NotificationButton/fetch-notifications";
import {
type AccountHeaderCompProps,
AccountHeaderDesktopUI,
@@ -54,6 +59,9 @@ export function AccountHeader(props: {
account: props.account,
client,
accountAddress: props.accountAddress,
getChangelogNotifications: getChangelogNotifications,
getInboxNotifications: getInboxNotifications,
markNotificationAsRead: markNotificationAsRead,
};
return (
@@ -62,6 +62,9 @@ function Variants(props: {
email: "[email protected]",
}}
client={client}
getChangelogNotifications={() => Promise.resolve([])}
getInboxNotifications={() => Promise.resolve([])}
markNotificationAsRead={() => Promise.resolve()}
/>
</div>
</BadgeContainer>
@@ -8,6 +8,10 @@ import type { ThirdwebClient } from "thirdweb";
import { SecondaryNav } from "../../components/Header/SecondaryNav/SecondaryNav";
import { MobileBurgerMenuButton } from "../../components/MobileBurgerMenuButton";
import { ThirdwebMiniLogo } from "../../components/ThirdwebMiniLogo";
import {
NotificationButtonUI,
type NotificationMetadata,
} from "../../team/components/NotificationButton/NotificationButton";
import { TeamAndProjectSelectorPopoverButton } from "../../team/components/TeamHeader/TeamAndProjectSelectorPopoverButton";
import { TeamSelectorMobileMenuButton } from "../../team/components/TeamHeader/TeamSelectorMobileMenuButton";
@@ -20,6 +24,9 @@ export type AccountHeaderCompProps = {
account: Pick<Account, "email" | "id" | "image">;
client: ThirdwebClient;
accountAddress: string;
getChangelogNotifications: () => Promise<NotificationMetadata[]>;
getInboxNotifications: () => Promise<NotificationMetadata[]>;
markNotificationAsRead: (id: string) => Promise<void>;
};
export function AccountHeaderDesktopUI(props: AccountHeaderCompProps) {
@@ -59,6 +66,7 @@ export function AccountHeaderDesktopUI(props: AccountHeaderCompProps) {
focus="team-selection"
createProject={props.createProject}
account={props.account}
client={props.client}
/>
)}
</div>
@@ -70,6 +78,9 @@ export function AccountHeaderDesktopUI(props: AccountHeaderCompProps) {
connectButton={props.connectButton}
client={props.client}
accountAddress={props.accountAddress}
getChangelogs={props.getChangelogNotifications}
getInboxNotifications={props.getInboxNotifications}
markNotificationAsRead={props.markNotificationAsRead}
/>
</header>
);
@@ -111,13 +122,21 @@ export function AccountHeaderMobileUI(props: AccountHeaderCompProps) {
</div>
</div>
<MobileBurgerMenuButton
type="loggedIn"
email={props.account?.email}
logout={props.logout}
connectButton={props.connectButton}
accountAddress={props.accountAddress}
/>
<div className="flex items-center gap-3">
<NotificationButtonUI
getChangelogs={props.getChangelogNotifications}
getInboxNotifications={props.getInboxNotifications}
markNotificationAsRead={props.markNotificationAsRead}
/>
<MobileBurgerMenuButton
type="loggedIn"
email={props.account?.email}
logout={props.logout}
connectButton={props.connectButton}
accountAddress={props.accountAddress}
/>
</div>
</header>
);
}
@@ -13,7 +13,7 @@ export function DeployedContractsPageHeader(props: {
const [importModalOpen, setImportModalOpen] = useState(false);
return (
<div className="border-b">
<div>
<ImportModal
isOpen={importModalOpen}
onClose={() => {
@@ -23,27 +23,25 @@ export function DeployedContractsPageHeader(props: {
projectId={props.projectId}
/>
<div className="container">
<div className="flex flex-col gap-3 py-10 lg:flex-row lg:items-center lg:justify-between">
<div>
<h1 className="font-semibold text-3xl tracking-tight">Contracts</h1>
</div>
<div className="flex gap-3 [&>*]:grow">
<Button
className="gap-2 bg-card"
variant="outline"
onClick={() => setImportModalOpen(true)}
>
<DownloadIcon className="size-4" />
Import contract
</Button>
<Button asChild className="gap-2">
<Link href="/explore">
<PlusIcon className="size-4" />
Deploy contract
</Link>
</Button>
</div>
<div className="flex flex-col gap-3 pb-6 lg:flex-row lg:items-center lg:justify-between">
<div>
<h1 className="font-semibold text-3xl tracking-tight">Contracts</h1>
</div>
<div className="flex gap-3 [&>*]:grow">
<Button
className="gap-2 bg-card"
variant="outline"
onClick={() => setImportModalOpen(true)}
>
<DownloadIcon className="size-4" />
Import contract
</Button>
<Button asChild className="gap-2">
<Link href="/explore">
<PlusIcon className="size-4" />
Deploy contract
</Link>
</Button>
</div>
</div>
</div>
@@ -12,13 +12,12 @@ export function DeployedContractsPage(props: {
authToken: string;
}) {
return (
<div className="flex grow flex-col pb-20">
<div className="flex grow flex-col">
<DeployedContractsPageHeader
teamId={props.teamId}
projectId={props.projectId}
/>
<div className="h-10" />
<div className="container flex grow flex-col">
<div className="flex grow flex-col">
<Suspense fallback={<Loading />}>
<DeployedContractsPageAsync {...props} />
</Suspense>

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