Compare commits

..
Author SHA1 Message Date
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
8861982877 Version Packages (#5188)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-29 14:13:02 +13:00
Joaquim VergesandGitHub 07ea65bcd8 feat: improve ecosystem smart account handling (#5212) 2024-10-29 13:25:21 +13:00
jnsdls 6cb66c675a remove all farcaster frame logic for the moment (#5213)
<!-- start pr-codex -->

## PR-Codex overview
This PR primarily focuses on the removal of multiple files related to Farcaster frames and the `@coinbase/onchainkit` package, as well as some metadata updates in the `package.json` and `pnpm-lock.yaml` files.

### Detailed summary
- Deleted various files related to Farcaster frames and their functionality.
- Removed the `shortenAddress` function from `apps/dashboard/src/utils/string.ts`.
- Updated `package.json` to remove `@coinbase/onchainkit` dependency.
- Cleaned up `pnpm-lock.yaml` by removing references to deleted packages and dependencies.

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

<!-- end pr-codex -->
2024-10-28 23:59:34 +00:00
jnsdls ddec3dadcc Refactor Farcaster frame validation (#5211) 2024-10-28 23:24:52 +00:00
jnsdls ac78881711 update dependencies (#5210)
<!-- start pr-codex -->

## PR-Codex overview
This PR primarily focuses on updating various package dependencies across multiple `package.json` files, ensuring compatibility with newer versions and fixing potential issues.

### Detailed summary
- Updated `vitest` from `2.1.3` to `2.1.4`.
- Updated `lucide-react` from `0.453.0` to `0.454.0` in multiple apps.
- Updated `shiki` from `1.22.0` to `1.22.2` in multiple apps.
- Updated AWS SDK packages to `3.679.0` for `client-kms`, `3.680.0` for `client-lambda`, and `3.679.0` for `credential-providers`.
- Updated `tsx` from `4.19.1` to `4.19.2`.
- Updated `@playwright/test` from `1.47.2` to `1.48.2`.
- Updated `@types/color` from `^3.0.6` to `4.2.0`.
- Updated `knip` from `^5.33.3` to `5.34.2`.
- Updated `@vitest/coverage-v8` and `@vitest/ui` to `2.1.4`.
- Updated `msw` from `^2.4.12` to `^2.5.2`.
- Updated `react-native-svg` from `15.7.1` to `15.8.0`.
- Updated `viem` from `2.21.34` to `2.21.35`.
- Adjusted various package resolutions in `pnpm-lock.yaml`.

> 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 -->
2024-10-28 23:24:50 +00:00
MananTank be0a4fcf90 Remove unused envs from dashboard .env.example (#5209)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the `.env.example` file for the `dashboard` application by removing deprecated or unnecessary environment variable entries.

### Detailed summary
- Removed the comment regarding `PAYMENTS_ADMIN_SECRET` and its associated empty value.
- Deleted the line to disable telemetry (`NEXT_TELEMETRY_DISABLED`).
- Removed the comment and empty value for `NEXT_PUBLIC_TRENCH_2_API_ENDPOINT`.

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

<!-- end pr-codex -->
2024-10-28 22:27:38 +00:00
MananTank 0b3fe51184 Fix turbo env variable warnings for dashboard build on vercel (#5208)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the `env` configuration in the `turbo.json` file to include additional environment variables necessary for the build process.

### Detailed summary
- Added multiple environment variables to the `env` array in the `thirdweb-dashboard#build` section:
  - `MORALIS_API_KEY`
  - `ETHERSCAN_KEY`
  - `SSR_ALCHEMY_KEY`
  - `BEEHIIV_API_KEY`
  - `GITHUB_API_TOKEN`
  - `LUMA_API_KEY`
  - `HUBSPOT_ACCESS_TOKEN`
  - `API_ROUTES_CLIENT_ID`
  - `DASHBOARD_SECRET_KEY`
  - `SIMPLEHASH_API_KEY`
  - `NEYNAR_API_KEY`
  - `THIRDWEB_ENGINE_URL`
  - `THIRDWEB_ACCESS_TOKEN`
  - `THIRDWEB_ENGINE_ACCESS_TOKEN`
  - `DEGEN_THIRDWEB_ENGINE_WALLET`
  - `THIRDWEB_ENGINE_WALLET`
  - `UNTHREAD_TRIAGE_CHANNEL_ID`
  - `UNTHREAD_EMAIL_INBOX_ID`
  - `UNTHREAD_FREE_TIER_ID`
  - `UNTHREAD_GROWTH_TIER_ID`
  - `UNTHREAD_PRO_TIER_ID`
  - `REDIS_URL`
  - `UNTHREAD_API_KEY`
  - `CHAINSAW_API_KEY`
  - `ANALYTICS_SERVICE_URL`
  - `ANALYTICS_SERVICE_API_KEY`
  - `DISABLE_FAUCET_CHAIN_IDS`
  - `API_SERVER_SECRET`
  - `TURNSTILE_SECRET_KEY`

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

<!-- end pr-codex -->
2024-10-28 22:14:56 +00:00
Joaquim VergesandGitHub adeda1ef76 feat: enhance TransactionButton and add buy support check (#5207) 2024-10-29 10:41:01 +13:00
0xFirekeeper fb5115a90f [Docs] Unity as of 5.7.0 (#5195)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on updating the documentation for the `Unity SDK`, including changes to wallet options, sidebar navigation, and the introduction of build instructions. It also replaces an image and adds new sections for configuration and debugging.

### Detailed summary
- Removed `thirdwebmanager.png` and updated image imports.
- Changed title in `page.mdx` from "Unity SDK (v5)" to "Unity SDK".
- Expanded wallet options to include `Github` and `Twitch`.
- Added new `Build Instructions` section in `page.mdx`.
- Updated sidebar to include `Build Instructions` and `Migrate from v4`.
- Revised `Client Settings` and added `Preferences`, `Misc`, and `Debug` sections.

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

<!-- end pr-codex -->
2024-10-28 21:28:29 +00:00
MananTank a5ffc54a25 Fix error thrown from PublishedBy card on localhost chain (#5198)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on refactoring the `ContractOverviewPageClient` component by removing the `PublishedByClient` function and its associated logic, while setting the `publishedBy` prop to `undefined`. This prepares for a future implementation of a fully client-rendered version.

### Detailed summary
- Removed the import statements for `useThirdwebClient`, `useQuery`, and `useActiveAccount`.
- Deleted the `PublishedByClient` function and its logic.
- Updated the `publishedBy` prop in `ContractOverviewPageClient` to `undefined`.
- Added a TODO comment for future implementation of a fully client-rendered version of `publishedBy` and `ContractCard`.

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

<!-- end pr-codex -->
2024-10-28 21:16:01 +00:00
MananTank 286aa30bfc Include chain overrides for all chains and not just custom chains in RSCs (#5197)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on refactoring the code in the `getContractFromParams.ts` file to improve readability. It mainly involves reordering and aligning the code without changing the logic.

### Detailed summary
- Removed unnecessary line breaks for better readability.
- Maintained the structure of the `if` statements and the `try-catch` block.
- Ensured consistent formatting for the `chainOverrides.find` function.

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

<!-- end pr-codex -->
2024-10-28 21:15:59 +00:00
joaquim-verges bb4435233f feat: set default domain for thirdweb auth (#5196)
## Problem solved

Addresses CNCT-2132

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the `domain` configuration in the `createAuth` function for the `thirdwebAuth` object, changing its default value to "thirdweb.com".

### Detailed summary
- Updated the `domain` property in `createAuth` from an empty string to "thirdweb.com".

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

<!-- end pr-codex -->
2024-10-28 20:49:42 +00:00
Captain UnknownandGitHub aa0f397778 Fixed a redundant return statement in isLoggedIn() & a typo (#5194)
Signed-off-by: Captain Unknown <[email protected]>
2024-10-28 13:47:24 -07:00
MananTank 7185364e4b redirect /team/~/... to /team/<first-team-slug>/... (#5158)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR adds functionality to redirect requests from `/team/~/...` to `/team/<first_team_slug>/...`, enhancing user navigation by ensuring that users are directed to the first team's slug instead of a placeholder.

### Detailed summary
- Added a check for the path starting with `team` and `~`.
- Implemented a call to `getTeams()` to retrieve the list of teams.
- If a team exists, modified the path to use the first team's slug.
- Redirects the request to the new path format.

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

<!-- end pr-codex -->
2024-10-28 18:37:45 +00:00
MananTank efc8bb7fd1 Fix hsl color preview and improved color-picker UI for connect playground (#5193)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the color selection functionality in the `ColorInput` component by integrating a new `Popover` UI from `@radix-ui/react-popover` and a color picker from `react-pick-color`. It also updates the dependencies in `package.json`.

### Detailed summary
- Removed the `ColorInput.module.css` file.
- Added `@radix-ui/react-popover` and `react-pick-color` to `package.json`.
- Updated `ColorInput` component to use `Popover` for color selection.
- Integrated `ColorPicker` within the `PopoverContent`.
- Refactored `onChange` handling in `ColorInput`.

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

<!-- end pr-codex -->
2024-10-28 18:25:26 +00:00
MananTank 74d113cf16 Move engine page components to app router folder in teams layout (#5145)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on restructuring the file organization and import paths for various components within the dashboard application, specifically related to engine functionalities. It also includes updates to stylesheets and components for better modularity.

### Detailed summary
- Updated import paths for several components to a new directory structure.
- Refactored components like `EngineAdmins`, `EngineRelayer`, `EngineExplorer`, etc., to use relative imports from the new structure.
- Adjusted CSS imports for `swagger-ui.css` and other styles.
- Updated story files for `RecentEngineAlerts` and `ManageEngineAlerts` to reflect new paths.
- Modified `EngineSystemMetrics` component imports for better organization.

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

<!-- end pr-codex -->
2024-10-28 18:12:36 +00:00
MananTank 486cd4a255 Fix API key creation on embed page (#5192)
Fixes: DASH-340

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `CreateAPIKey` functionality by introducing prefill options for the API key creation process, improving the user interface for creating API keys, and adding a modal for better user experience.

### Detailed summary
- Added `CreateAPIKeyPrefillOptions` type.
- Introduced `DesktopAPIKeyWordingWithPrefill` story.
- Updated `CreateAPIKeyDialogProps` to include `prefill`.
- Modified `CreateAPIKeyDialogUI` to accept and use `prefill`.
- Enhanced `CreateAPIKeyForm` to utilize prefill values.
- Integrated `LazyCreateAPIKeyDialog` in the `EmbedSetup` component.
- Updated button behavior to show modal for creating API keys.
- Improved user prompts for signing in when no active account is detected.

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

<!-- end pr-codex -->
2024-10-28 18:00:39 +00:00
MananTank 68011d5c76 Add Logged Out Variant of Team Header (#5160)
## Problem solved

Short description of the bug fixed or feature added
2024-10-28 17:48:34 +00:00
MananTank 939e7ab820 Render contract pages on client side for localhost chain (#5185)
Fixes: DASH-348
2024-10-28 17:36:30 +00:00
MananTank 59953f714a Fix MintSupplyTab Amount form field error (#5191)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the validation and input handling for the minting amount in the `MintSupplyTab` component. It ensures that the amount is coerced to a number type and specifies the input type for better user experience.

### Detailed summary
- Changed `amount` validation from `z.number()` to `z.coerce.number()` to ensure the input is coerced to a number.
- Updated the `Input` component to include `type="number"` for the amount field, enhancing user input handling.

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

<!-- end pr-codex -->
2024-10-28 16:35:17 +00:00
GWSzeto 7aab5caf98 Change Modules table to cards UI (#5051)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new `ModuleCard` component and its associated storybook entries, enhancing the UI for displaying module information in a dashboard. It also refactors the `InstalledModulesTable` to utilize the new `ModuleCard` component for better modularity and readability.

### Detailed summary
- Added `ModuleCard` component in `module-card.tsx`.
- Created storybook entries for `ModuleCard` in `module-card.stories.tsx`.
- Refactored `InstalledModulesTable` to render `ModuleCard` instead of a table.
- Removed unused skeleton and row components from `InstalledModulesTable`.

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

<!-- end pr-codex -->
2024-10-28 14:28:48 +00:00
Joaquim VergesandGitHub 83a2412941 Revert "[Dashboard] Feature: Adds analytics to in-app-wallets page (#5186) (#5189) 2024-10-28 20:47:08 +13:00
Joaquim VergesandGitHub 84571efaf8 feat: redesign payment selection flow for Buy screen (#5176) 2024-10-28 20:15:19 +13:00
771acd1abe Fix op estimation edge case (#5184)
Co-authored-by: Joaquim Verges <[email protected]>
2024-10-28 15:42:14 +13:00
gregfromstl 2bf65c05b2 [Dashboard] Feature: Adds analytics to in-app-wallets page (#5186)
<img width="1106" alt="Screenshot 2024-10-25 at 4 02 59 PM" src="https://github.com/user-attachments/assets/8b559170-2d5e-4810-8a29-55f155448e40">

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the in-app wallets feature by adding tracking capabilities, restructuring components, and improving analytics. It introduces new interfaces, updates existing components, and removes unnecessary elements to streamline functionality and improve user experience.

### Detailed summary
- Added `InAppWalletStats` interface.
- Introduced `TRACKING_CATEGORY` constants for analytics.
- Updated components to utilize new tracking categories.
- Removed `AnalyticsCallout` from layout.
- Replaced `Separator` with `<div className="h-5" />` for spacing.
- Enhanced analytics pages to fetch and display wallet usage data.
- Implemented new routing for in-app wallet analytics.
- Updated `InAppWalletUsersChartCard` for better data visualization.
- Improved the structure of the `Tabs` component for navigation.

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

<!-- end pr-codex -->
2024-10-28 01:30:34 +00:00
Edward SunandGitHub a365dd8ee9 Add a case study to project showcase (#5183) 2024-10-25 12:17:05 -07:00
Edward SunandGitHub 363b63384a [Very minor patch] Add case study button to card (#5181) 2024-10-25 11:45:47 -07:00
Edward SunandGitHub 4ec2aa8dbd Project showcase patches (#5178) 2024-10-25 10:45:34 -07:00
MananTank 63bbb4c42a Fix page crash when using useTxNotifications on pages without ChakraProvider, restore latest sentry version (#5179)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating dependencies and modifying the error message presentation in the `errorParser.tsx` file. It upgrades several `@sentry` packages and refines the structure of the error messages displayed to users.

### Detailed summary
- Updated `@sentry/nextjs` from `8.34.0` to `8.35.0`.
- Changed the `PLEASE_REACH_OUT_MESSAGE` structure from using `<Text>` to a `<span>`.
- Updated the class name for the `<Link>` from `font-bold` to `font-semibold`.
- Modified error message rendering in `parseErrorToMessage` to use `<span>` instead of `<Text>`.
- Updated several `@sentry` internal dependencies to version `8.35.0` across `pnpm-lock.yaml`.

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

<!-- end pr-codex -->
2024-10-25 17:38:21 +00:00
kien-ngo 4cb7f8f97f [Dashboard] DASH-347 | Claim issue with EditionDrop (#5177)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `ClaimTabERC1155` component by removing the `useTxNotifications` hook and replacing success/error handling with `toast.promise`. This improves user feedback during transaction processes.

### Detailed summary
- Removed the `useTxNotifications` hook and its related success/error callbacks.
- Introduced `toast.promise` for handling promises related to approving ERC20 tokens and claiming NFTs.
- Improved error handling with `console.error` for better debugging.
- Updated success/error messages for user feedback.

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

<!-- end pr-codex -->
2024-10-25 17:10:53 +00:00
9dfe4af679 Biz hackweek - Project Showcase (#5118)
Co-authored-by: samina <[email protected]>
2024-10-25 09:52:30 -07:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
b5bbecd182 Version Packages (#5173)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-25 21:26:53 +13:00
Joaquim VergesandGitHub bafa0e83c3 feat: set initial balance for Anvil proxy in tests (#5174) 2024-10-25 20:58:30 +13:00
kien-ngo 5dc1899541 [SDK] Improve Vote contract tests (#5156)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing validation for the `minVoteQuorumRequiredPercent` parameter in the `deploy-vote.ts` file and updating corresponding tests to ensure correct functionality.

### Detailed summary
- Added validation for `minVoteQuorumRequiredPercent` in `deploy-vote.ts`.
- Included checks for NaN, range (0-100), and integer-like values.
- Updated tests in `deploy-vote.test.ts` to handle new validation logic.
- Adjusted test descriptions and assertions in `vote.test.ts`.

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

<!-- end pr-codex -->
2024-10-25 03:59:18 +00:00
kien-ngo 4b183518af [SDK] Improve Split contract tests (#5155)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on adding tests for the `split` functionality of a smart contract in the `thirdweb` package, ensuring that the contract behaves as expected when deployed and queried for recipient addresses and their share percentages.

### Detailed summary
- Deleted several test files related to `deploy-split` and recipient functions.
- Introduced a new test suite for the `split` contract in `split.test.ts`.
- Added tests for:
  - Deployment of the split contract.
  - Retrieving all recipient addresses.
  - Retrieving all recipients and their share percentages.
  - Retrieving the split percentage for an individual recipient.

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

<!-- end pr-codex -->
2024-10-25 03:59:17 +00:00
joaquim-verges e8f952a191 feat: always add URI to SIWE payload (#5172)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on ensuring that a URI is always included in the SIWE payload by modifying the logic in the `generate-login-payload.ts` file and updating the corresponding test.

### Detailed summary
- In `generate-login-payload.ts`, the `uri` field is updated to default to `options.domain` if `options.login?.uri` is not provided.
- In `generate-login-payload.test.ts`, the test case for `uri` is changed to use `"example.com"` instead of `undefined`.

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

<!-- end pr-codex -->
2024-10-25 02:45:35 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
132ccd3535 Version Packages (#5171)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-25 13:06:16 +13:00
gregfromstl f23911e1da chore: service utils changeset (#5169)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on reducing the strictness of the parsing for published event actions in the `@thirdweb-dev/service-utils` package.

### Detailed summary
- Updated the `@thirdweb-dev/service-utils` package to a patch version.
- Modified the parsing logic for published event actions to be less strict, allowing for more flexible handling of events.

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

<!-- end pr-codex -->
2024-10-24 23:30:33 +00:00
gregfromstl cd22bca3d8 [Dashboard] Fix: Exclude testnet $ from total sponsored (#5166)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on improving the handling of analytics data in the `useUserOpUsageAggregate` function by incorporating chain data and refining the conditional rendering in the `stat.tsx` component.

### Detailed summary
- In `stat.tsx`, changed the conditional rendering to check for `value !== undefined` instead of `value &&`.
- In `useApi.ts`, imported `useAllChainsData` for accessing chain data.
- Added `chainStore` to hold all chains data.
- Modified `userOpStats` type to include `chainId`.
- Implemented a check to skip testnets in aggregated stats based on `chainId`.

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

<!-- end pr-codex -->
2024-10-24 23:05:37 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
e4b6d87dd8 Version Packages (#5168)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-25 12:04:19 +13:00
Joaquim VergesandGitHub 158c2b6e8e feat: respect theme accentColor for default connect UI icons (#5163) 2024-10-25 11:52:08 +13:00
MananTank 52229be96a Revert @sentry/nextjs to 8.34.0 to fix page crash on tx rejection (#5167)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on downgrading the `@sentry/nextjs` package from version `8.35.0` to `8.34.0` and updating the corresponding dependencies in the `pnpm-lock.yaml` file.

### Detailed summary
- Downgraded `@sentry/nextjs` from `8.35.0` to `8.34.0` in `apps/dashboard/package.json`.
- Updated related dependencies in `pnpm-lock.yaml` to reflect the version change for `@sentry/nextjs` and its internal packages.
- Adjusted versions for several other `@sentry` packages to `8.34.0`.
- Removed unnecessary version specifications in `pnpm-lock.yaml`.

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

<!-- end pr-codex -->
2024-10-24 22:37:50 +00:00
gregfromstl 012f3defda Update service-utils event publishing to accept any source (#5162)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the `usageEventSchema` in `packages/service-utils/src/core/usage.ts` by changing the type of the `source` field from an enumeration to a string, allowing for more flexibility in the values that can be assigned to `source`.

### Detailed summary
- Changed the `source` field in `usageEventSchema` from `z.enum` to `z.string()`.

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

<!-- end pr-codex -->
2024-10-24 22:25:10 +00:00
MananTank 8e43e340a7 Handle isContractDeployed error on contract page (#5165)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving error handling when checking if a contract is deployed. It ensures that if the contract check fails, it will not throw an error but instead return `false`.

### Detailed summary
- Modified `isValidContract` assignment to handle errors gracefully by using `.catch(() => false)` instead of letting the promise reject.
- Ensured that the flow continues to call `notFound()` if the contract is not valid.

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

<!-- end pr-codex -->
2024-10-24 21:49:22 +00:00
gregfromstl 74e52c000e [Dashboard] Feature: Adds chain ID to total sponsored analytics (#5161)
<!-- start pr-codex -->

## PR-Codex overview
This PR introduces an interface `UserOpStatsByChain` and updates the `TotalSponsoredChartCard` component to handle user operation statistics by blockchain. It enhances chart data handling and visualization, allowing for better representation of sponsored gas across different chains.

### Detailed summary
- Added `UserOpStatsByChain` interface in `useApi.ts`.
- Updated `TotalSponsoredChartCard` to use `UserOpStatsByChain[]`.
- Modified chart data structure to accommodate multiple chains.
- Implemented logic to categorize and display "others" in charts.
- Adjusted CSV export to include unique chain data.
- Improved chart rendering with dynamic colors and data keys based on chains.

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

<!-- end pr-codex -->
2024-10-24 21:02:08 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
120f965941 Version Packages (#5117)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-25 09:03:57 +13:00
kien-ngo cf36a33c3f [Dashboard] Update erc1155 airdrop form (#5127)
- [x] Remove tw-component's Drawer
- [x] Consolidate some shared code
- [x] Remove an useEffect

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on refactoring the airdrop functionality in a dashboard application, improving the handling of token and NFT airdrops by consolidating interfaces and components.

### Detailed summary
- Renamed `ERC20AirdropAddressInput` to `AirdropAddressInput`.
- Updated `AirdropUploadERC20` to `AirdropUpload`.
- Adjusted state management for airdrop data.
- Simplified CSV data processing.
- Enhanced error handling during transaction processes.
- Replaced modal with `Sheet` component for airdrop interface.

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

<!-- end pr-codex -->
2024-10-24 19:39:52 +00:00
joaquim-verges 2fdb69dd44 optimize: improve mintAdditionalSupplyTo efficiency (#5148)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on optimizing the `mintAdditionalSupplyTo` function in the `erc1155` extension by modifying how the token URI is handled during the minting process.

### Detailed summary
- Removed the retrieval of the existing token URI from the contract.
- Set the `uri` field to an empty string, indicating the contract will maintain the existing token URI.
- Kept the `to`, `tokenId`, and `amount` fields unchanged.

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

<!-- end pr-codex -->
2024-10-24 08:41:20 +00:00
Joaquim VergesandGitHub 06c0cf3898 chore: update @mobile-wallet-protocol/client to 0.1.1 (#5151) 2024-10-24 21:07:55 +13:00
Joaquim VergesandGitHub 47b1bbbd7c feat: enable sign in with wallet for ecosystems (#5153) 2024-10-24 21:06:47 +13:00
kien-ngo 53e9d8bc1d [SDK] Consolidate ERC1155 Drop tests (#5137)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on refactoring tests for the `erc1155` extension, updating account references, and adding new test cases for metadata updates and claim conditions.

### Detailed summary
- Deleted `claimTo.test.ts` and `updateMetadata.test.ts`.
- Updated account references from `TEST_ACCOUNT_A` to `TEST_ACCOUNT_C`.
- Added a test case for updating metadata.
- Added a test case to verify claiming without payment for the primary fee recipient.
- Ensured `getActiveClaimCondition` works correctly with new test cases.

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

<!-- end pr-codex -->
2024-10-24 06:01:54 +00:00
jnsdls 9fadbcc17a update dependencies (#5150)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on updating dependencies across multiple packages, including `@thirdweb-dev/react-native-adapter`, `@thirdweb-dev/service-utils`, and `thirdweb`, along with making type exports consistent in various modules.

### Detailed summary
- Updated `@cloudflare/workers-types` from `4.20241011.0` to `4.20241022.0`.
- Updated `@biomejs/biome` from `1.9.3` to `1.9.4`.
- Updated `turbo` from `2.1.3` to `2.2.3`.
- Updated `@tanstack/react-query` from `5.59.13` to `5.59.16` across multiple packages.
- Updated `lucide-react` from `0.452.0` to `0.453.0` across multiple packages.
- Updated `tailwindcss` from `3.4.13` to `3.4.14` across multiple packages.
- Updated `react-native` from `0.75.3` to `0.76.0`.
- Changed exports from `export { type ... }` to `export type { ... }` in several files for consistency.
- Updated multiple AWS SDK dependencies to `3.678.0`.
- Updated `viem` from `2.21.25` to `2.21.34`.

> 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 -->
2024-10-24 01:15:46 +00:00
gregfromstl c04b93e705 [Dashboard] Feature: AA analytics tab (#5149)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on enhancing the analytics dashboard for account abstraction by updating components, improving data handling, and adding new features for user operation statistics and wallet usage.

### Detailed summary
- Updated `clientId` handling in `ConnectAnalyticsDashboard` and `AccountAbstractionPage`.
- Introduced `connectLayoutSlug` prop in `ConnectAnalyticsDashboard`.
- Added `IntervalSelector` and `DateRangeSelector` components for better date management.
- Refactored `ConnectAnalyticsDashboardUI` to include `AccountAbstractionSummary`.
- Enhanced `TotalSponsoredChartCard` and `SponsoredTransactionsChartCard` with new chart states.
- Moved `createUserOpStatsStub` to a new location and cleaned up unused code.
- Improved data aggregation for user operation statistics in `useUserOpUsageAggregate`.

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

<!-- end pr-codex -->
2024-10-24 00:25:00 +00:00
Jonas DanielsandGitHub e6e994479f move landing page to framer (#5146) 2024-10-23 17:00:11 -07:00
arcoraven cf61115064 chore: add knowledge base link (#5147)
<!-- start pr-codex -->

## PR-Codex overview
This PR updates the `supportLinks` array in the `Header.tsx` file to improve the naming of existing links and to add a new link for the Knowledge Base.

### Detailed summary
- Changed the name of the first link from `"Support Site"` to `"Get thirdweb support"`.
- Added a new link for the Knowledge Base with the name `"Knowledge Base"` and the URL `https://support.thirdweb.com`.

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

<!-- end pr-codex -->
2024-10-23 23:31:43 +00:00
Joaquim VergesandGitHub 60d2190e95 feat: smart account options for ecosystems (#5136) 2024-10-24 12:16:52 +13:00
MananTank 3cf0039c58 Move support ticket creation to /support/create-ticket page (#5144)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on restructuring the support ticket creation process in the dashboard application. It removes the old `CreateTicket` component and replaces it with a new implementation, enhancing user navigation and support form functionality.

### Detailed summary
- Deleted `create-ticket.client.tsx`.
- Updated `SupportPage` to replace `<CreateTicket />` with a button linking to `/support/create-ticket`.
- Created a new `Page` component for the support ticket creation, including authentication check and breadcrumb navigation.
- Introduced a new `CreateTicket` component with dynamic support form imports and form handling.
- Added form reset and submission handling in `CreateTicket`.

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

<!-- end pr-codex -->
2024-10-23 20:01:35 +00:00
kien-ngo 4841ff382a [Dashboard] Add some informative badges to the Explorer (#5138)
![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/VtULqGKSp33n0VHOzNJK/1670002e-3123-4770-b29c-fdc84dce88dd.png)
2024-10-23 18:51:06 +00:00
MananTank d580fe05e3 Customize faucet claim amount for various chains (#5143)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on updating the `FaucetButton` and related components to use dynamic claim amounts based on the `chainId`, replacing hardcoded values with a function that retrieves the appropriate amount.

### Detailed summary
- Changed `amount` type from `string` to `number` in `FaucetButton.tsx`.
- Introduced `customClaimAmounts` and `defaultClaimAmount` in `claim-amount.ts`.
- Updated `FaucetSection` and `GetFundsFromFaucet` to use `getFaucetClaimAmount`.
- Modified API response in `route.ts` to return dynamic claim amounts.

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

<!-- end pr-codex -->
2024-10-23 17:33:34 +00:00
MananTank 7a1099b601 Make rows in contract table actual links to allow opening in new tab with cmd click (#5142)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `DeployedContractsPageHeader` and related components by improving the link handling for contract names and refining the UI elements in the contract table.

### Detailed summary
- Updated `DeployedContractsPageHeader` to use a new `Button` import.
- Added `linkOverlay` prop to `AsyncContractNameCell`.
- Enhanced link styling in `AsyncContractNameCell` with conditional classes.
- Passed `linkOverlay` prop when rendering `AsyncContractNameCell` in the contract table.
- Simplified event handling in `ContractTableRow`.

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

<!-- end pr-codex -->
2024-10-23 17:09:16 +00:00
MananTank 4e5fdcf415 Fix claim condition loading state + UI tweaks (#5141)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on refactoring the `ClaimConditionsForm` component in the `claim-conditions` files to improve layout and loading state handling.

### Detailed summary
- Updated `Flex` component properties in `claim-conditions.tsx` for layout consistency.
- Added a loading spinner in `claim-conditions-form/index.tsx` to handle fetching state.
- Removed the previous spinner implementation for a cleaner loading experience.
- Adjusted `flexDir` property for better responsiveness.

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

<!-- end pr-codex -->
2024-10-23 16:57:37 +00:00
MananTank eaeb53218c Show 404 on invalid contract address in contract page (#5140)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR enhances the `Layout` component by adding a validation check to ensure that the `contractAddress` parameter is a valid address using `isAddress`. If the address is invalid, it returns a "not found" response.

### Detailed summary
- Imported `isAddress` from `thirdweb/utils`.
- Added a check to validate `props.params.contractAddress` using `isAddress`.
- If the address is invalid, the function returns `notFound()`.

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

<!-- end pr-codex -->
2024-10-23 16:52:11 +00:00
MananTank 09b7404985 Show 404 on deprecated chain contract page (#5139)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving the handling of contract metadata and checking for deprecated chains in the `layout.tsx` file. It ensures that the correct contract information is used and adds a condition to handle deprecated chain statuses.

### Detailed summary
- Added a check for `chainMetadata.status` to call `notFound()` if the status is "deprecated".
- Updated the `isValidContract` check to use `contract` from `info` instead of the previous variable.
- Rearranged the destructuring of `contract` and `chainMetadata` for clarity.

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

<!-- end pr-codex -->
2024-10-23 16:52:10 +00:00
Joaquim VergesandGitHub 3c77c6b2c8 fix: correct indentation in useSendTransaction example (#5133) 2024-10-23 19:46:54 +13:00
kien-ngo 71aa2578c3 [SDK] Consolidate tests to avoid excessive deployERC1155Contract (#5135)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on refactoring and enhancing the test coverage for the `erc1155` extension of the `thirdweb` package. It removes outdated tests, introduces new tests, and updates existing ones to improve functionality verification.

### Detailed summary
- Removed multiple test files related to `erc1155` functions.
- Updated `lazyMint.test.ts` to use `DROP1155_CONTRACT`.
- Added a test for `isGetNFTsSupported` in `drop1155.test.ts`.
- Simplified and enhanced tests in `mintAdditionalSupplyToBatch.test.ts`.
- Introduced new tests for `updateTokenURI`, minting, and `getOwnedTokenIds` in `token1155.test.ts`.

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

<!-- end pr-codex -->
2024-10-23 06:37:09 +00:00
kien-ngo c32576fb47 [SDK] ERC721 claimToBatch extension (#5124)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces the `claimToBatch` functionality for the `ERC721` extension, allowing multiple claims in a single transaction. It includes tests to ensure the optimization of claim content and the correct processing of batch claims.

### Detailed summary
- Added `claimToBatch` function in `claimToBatch.ts` for batch claiming NFTs.
- Introduced `ClaimToBatchParams` type to define batch claim structure.
- Implemented `optimizeClaimContent` function to consolidate identical recipient claims.
- Created tests for `claimToBatch` and `optimizeClaimContent` in `claimToBatch.test.ts`.
- Verified the functionality of `isClaimToSupported` in the test suite.

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

<!-- end pr-codex -->
2024-10-23 03:39:12 +00:00
kien-ngo d522343e04 [SDK] Optimize mintAdditionalSupplyToBatch (#5125)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on optimizing the `mintAdditionalSupplyToBatch` extension by introducing a new function to batch minting requests that share the same recipient and token ID, reducing the number of transactions required.

### Detailed summary
- Added `WithOverrides` to `MintAdditionalSupplyToBatchParams`.
- Implemented `optimizeMintBatchContent` function to consolidate minting requests.
- Updated `mintAdditionalSupplyToBatch` to use optimized NFT data.
- Added a test for `optimizeMintBatchContent` to verify its functionality.

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

<!-- end pr-codex -->
2024-10-23 03:15:05 +00:00
gregfromstl 4f22e2b190 [SDK] Feature: Track transactions and SIWE (#5086)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving the analytics tracking for wallet connections and transactions within the `thirdweb` library. It refines the tracking methods by introducing specific tracking functions for connections and payments, enhancing the data sent for analytics.

### Detailed summary
- Deleted `track.ts` file.
- Updated import paths for `trackConnect` and `trackPayEvent` in several files.
- Introduced `trackConnect` and `trackPayEvent` functions with detailed parameters.
- Added tracking functionality to wallet connection methods in `in-app` and `coinbase` wallets.
- Enhanced error handling and data structure in tracking functions.
- Added tests for `trackConnect`, `trackPayEvent`, and `trackLogin` functionalities.

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

<!-- end pr-codex -->
2024-10-23 00:37:33 +00:00
gregfromstl 319a203a4e [SDK] Fix: Prevent Duplicated RDNS Values from WalletConnect (#5129)
Fixes #5116

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating wallet metadata and image assets for various wallets in the `thirdweb` project. It includes changes to wallet IDs, homepages, image IDs, and mobile app links, ensuring accurate information for wallet integration.

### Detailed summary
- Removed the file `lovely-items-live.md`.
- Updated `native` and `universal` links for `finance.plena` and `finance.openwallet`.
- Changed `image_id` for `Crypto.com`, `Status`, `Coin98`, and others.
- Added new wallet entries for `com.daffione`, `com.beexo`, `id.plumaa`, `io.owallet`, and `com.ipmb`.
- Updated `getWalletInfo` method to include new wallets and remove `org.nitrogen` and `com.brave.wallet`.
- Added new images for several wallets as base64 encoded data.
- Updated the count of supported wallets in `wallet-ids.ts` and `wallet-infos.ts`.

> The following files were skipped due to too many changes: `packages/thirdweb/src/wallets/__generated__/wallet/com.coin98/image.ts`, `packages/thirdweb/src/wallets/__generated__/wallet/app.status/image.ts`

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

<!-- end pr-codex -->
2024-10-23 00:25:29 +00:00
MananTank cdb907f175 Remove Gas estimator link in footer (#5134)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on modifying the `app-footer.tsx` component by removing the `Gas Estimator` link from the footer.

### Detailed summary
- Removed the `Link` component for `Gas Estimator`.
- The remaining `Link` component for `chainlist` retains its class names and attributes.

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

<!-- end pr-codex -->
2024-10-22 23:50:57 +00:00
joaquim-verges 364d97e97e feat: auto-resolve entrypoint address from factory (#5123)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the `smartWallet` integration to automatically resolve the entrypoint address from the account factory when available, enhancing usability and simplifying the configuration for users.

### Detailed summary
- Replaced `ENTRYPOINT_ADDRESS_v0_7` with `DEFAULT_ACCOUNT_FACTORY_V0_7` in the `smartWallet` configuration.
- Updated documentation to reflect changes in how to use the `smartWallet` with the factory.
- Added logic to resolve the entrypoint address from the factory if not provided.
- Introduced a new function `getEntrypointFromFactory` to fetch the entrypoint address from the account factory contract.

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

<!-- end pr-codex -->
2024-10-22 21:54:20 +00:00
gregfromstl 795e9c13a4 [Service Utils] Feature: add ecosystem headers to extractAuthorizationData (#5083)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces new ecosystem headers to the `extractAuthorizationData` function in the `service-utils` package, enhancing the authorization process by including `ecosystemId` and `ecosystemPartnerId`.

### Detailed summary
- Added `ecosystemId` and `ecosystemPartnerId` to the type definitions in `index.ts`.
- Updated `extractAuthorizationData` to retrieve `ecosystemId` and `ecosystemPartnerId` from headers and URL search parameters in `cf-worker/index.ts` and `node/index.ts`.

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

<!-- end pr-codex -->
2024-10-22 21:24:33 +00:00
MananTank 0b510f881b Handle contract page crash if contract is not deployed (#5132)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving error handling in the `ContractOverviewPage` and validating contract existence in the `layout.tsx`. It introduces an `ErrorBoundary` around the `PublishedBy` component and checks if a contract is deployed before rendering the layout.

### Detailed summary
- Wrapped the `PublishedBy` component in an `ErrorBoundary` in `ContractOverviewPage.tsx`.
- Added a check for contract deployment using `isContractDeployed` in `layout.tsx`.
- Implemented a `notFound()` call if the contract is not deployed, with a TODO for future improvements.

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

<!-- end pr-codex -->
2024-10-22 21:09:06 +00:00
kien-ngo 5351d4e407 [SDK] minor fix for test (#5122)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR refactors the test for the `mintToBatch` function, changing its execution context and updating the minting logic to utilize `mintAdditionalSupplyToBatch`. It also modifies the structure of the NFT data being tested.

### Detailed summary
- Changed `describe` to use `describe.runIf(process.env.TW_SECRET_KEY)`.
- Updated minting function from `mintToBatch` to `mintAdditionalSupplyToBatch`.
- Adjusted parameters for minting NFTs.
- Modified the expected NFT details in the final assertion.

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

<!-- end pr-codex -->
2024-10-22 20:26:10 +00:00
gregfromstl 0cafa33a18 [SDK] Feature: GitHub Auth (#5062)
<!-- start pr-codex -->

## PR-Codex overview
This PR introduces GitHub authentication support across various components of the application, enhancing the social login options for users.

### Detailed summary
- Added `GitHub` to authentication options in `types.ts`.
- Included `GitHub` in the social login forms and icons.
- Updated the `InAppWebConnector` to handle `GitHub` authentication.
- Defined `GITHUB_ICON` SVG for branding.
- Integrated `githubIconUri` for wallet icons.

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

<!-- end pr-codex -->
2024-10-22 19:44:00 +00:00
gregfromstl dd77c7f57c Analytics UI Empty States (#5121)
<img width="968" alt="Screenshot 2024-10-21 at 6 23 23 PM" src="https://github.com/user-attachments/assets/86000f33-2041-4d3a-b084-b6367e61f032">

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `DocLink` component for better link handling and integrating it into various chart components for improved user guidance on wallet connections and transactions.

### Detailed summary
- Added `DocLink` component in `DocLink.tsx`.
- Refactored `ConnectSDKCard` to use `DocLink` instead of its previous implementation.
- Modified `EmptyChartState` to accept `children` for customizable content.
- Integrated `DocLink` into multiple chart components, providing links for wallet integration guidance.

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

<!-- end pr-codex -->
2024-10-22 19:21:27 +00:00
MananTank 9c3ff94f29 Move ContractCard and PublishedBy components to RSC (#5097) 2024-10-22 18:44:47 +00:00
Joaquim VergesandGitHub 45fcfb1dae feat: support smart account options for ecosystem wallets (#5091) 2024-10-22 22:25:00 +13:00
Joaquim VergesandGitHub 788eefd1d5 refactor: simplify API fetch error handling in aa-chains route (#5119) 2024-10-22 22:24:06 +13:00
kien-ngo f8c981c0ca [SDK] ERC1155 extension: mintAdditionalSupplyToBatch (#5036)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new ERC1155 extension called `mintAdditionalSupplyToBatch`, allowing users to mint additional supplies of NFTs in a single transaction. It also modifies existing minting logic for better handling of asynchronous operations.

### Detailed summary
- Added new extension `mintAdditionalSupplyToBatch` in `mintAdditionalSupplyToBatch.ts`.
- Updated `mintToBatch.ts` for improved async handling and data processing.
- Created tests for `mintAdditionalSupplyToBatch` in `mintAdditionalSupplyToBatch.test.ts`.
- Updated export statements in `erc1155.ts` to include the new function.

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

<!-- end pr-codex -->
2024-10-22 06:30:29 +00:00
kien-ngo 1628fa2a79 [SDK] Improve general test cases (#5054)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces new tests for resolving Unstoppable Domains and updates the `mintToBatch` test for `ERC1155` contracts, including the handling of NFT retrieval and ensuring proper error handling.

### Detailed summary
- Added a test case in `resolveName.test.ts` to check error handling for addresses without a domain.
- Updated the `mintToBatch.test.ts` to use `describe.runIf` for conditional execution.
- Refactored the `mintToBatch` test to include NFT retrieval and validation after minting.

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

<!-- end pr-codex -->
2024-10-22 06:18:22 +00:00
joaquim-verges 0e26a88e7f fix: resolve ecosystem image URL using thirdweb storage (#5120)
## Problem solved

Fixes CNCT-2087

<!-- start pr-codex -->

---

## PR-Codex overview
This PR enhances the `Layout` component in the `layout.tsx` file by improving the way the ecosystem image URL is resolved using `resolveScheme`, ensuring proper handling of the image source.

### Detailed summary
- Added import for `resolveScheme` from `thirdweb/storage`.
- Added import for `client` from `../../../lib/client`.
- Updated the `src` attribute of the `<img>` tag to use `resolveScheme({ uri: ecosystem.imageUrl, client })` instead of directly using `ecosystem.imageUrl`.

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

<!-- end pr-codex -->
2024-10-22 00:52:15 +00:00
joaquim-verges 50f98d7486 portal: show all supported chains in AA docs (#5090)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `Document` component by adding the `AAChainList` feature, which fetches and displays account abstraction chains. It also improves the API route for fetching chain data.

### Detailed summary
- Added `AAChainList` component in `apps/portal/src/components/Document/AAChainList.tsx`.
- Implemented `getChains` function to fetch account abstraction chains.
- Updated API route in `apps/portal/src/app/api/aa-chains/route.ts` to fetch services and chains.
- Integrated `AAChainList` in the `page.mdx` for displaying available chains.

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

<!-- end pr-codex -->
2024-10-21 23:14:11 +00:00
joaquim-verges 40aeb529ac test: add environment check for ERC1155 mintToBatch tests (#5115)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR modifies the test for the `mintToBatch` function in the `ERC1155 Edition` to include conditional execution based on the presence of a `TW_SECRET_KEY`. It also refactors the test structure, improving readability and organization.

### Detailed summary
- Changed `describe` to conditionally run based on `process.env.TW_SECRET_KEY`.
- Refactored the test structure for `mintToBatch`.
- Moved the `sendAndConfirmTransaction` call inside the test.
- Added detailed NFT validation after minting.
- Removed redundant code and improved clarity.

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

<!-- end pr-codex -->
2024-10-21 23:00:13 +00:00
kumaryash90 e7bbc321c2 Fix auto verification of contracts (#5099)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces the export of the `verifyContract` function from `ContractSourcesPage.tsx` and integrates it into the `CustomContractForm` component to send a verification request after deploying a contract.

### Detailed summary
- Changed `verifyContract` from a local function to an exported function in `ContractSourcesPage.tsx`.
- Imported `verifyContract` into `custom-contract.tsx`.
- Added a call to `verifyContract` in the `CustomContractForm` after contract deployment.

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

<!-- end pr-codex -->
2024-10-21 22:09:39 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8f157aa43f chore(deps-dev): bump @vitejs/plugin-react from 4.3.2 to 4.3.3 (#5114)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-21 14:54:09 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a437baa57a chore(deps): bump @shazow/whatsabi from 0.15.3 to 0.15.4 (#5113)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-21 14:27:29 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4331ed1f04 chore(deps): bump framer-motion from 11.11.8 to 11.11.9 (#5112)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-21 14:12:37 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5c896b0fd5 chore(deps-dev): bump msw from 2.4.11 to 2.4.12 (#5107)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-21 14:06:36 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
16aa7b7918 chore(deps-dev): bump typedoc from 0.26.9 to 0.26.10 (#5106)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-21 14:01:41 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
341d312679 chore(deps-dev): bump @storybook/test from 8.3.5 to 8.3.6 (#5100)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-21 14:00:25 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
3aff548a10 chore(deps-dev): bump @types/papaparse from 5.3.14 to 5.3.15 (#5103)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-21 14:00:13 -07:00
MananTank 002c545d0e Handle published contract page server side errors (#5098)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving error handling and null checks in the `published-contract` related components of the dashboard application. It ensures that appropriate actions are taken when data is missing, enhancing the application's robustness.

### Detailed summary
- Added null checks for `publishedContracts` in multiple files.
- Implemented `notFound()` calls when `publishedContractVersions` or `publishedContracts` are missing.
- Enhanced error handling in `getPublishedContractsWithPublisherMapping.ts` with try-catch.
- Updated `publisherAddress` resolution logic for better clarity.

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

<!-- end pr-codex -->
2024-10-21 20:29:44 +00:00
MananTank bc5b543269 Fix all resolveScheme unhandled errors (#5096)
Fixes: DASH-333
2024-10-21 19:36:51 +00:00
MananTank 1a45f21abb Handle page crash when rendering NFTs with unexpected title, description values (#5095)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces error handling components to display messages for unexpected values in the NFT-related pages, enhancing user experience by providing feedback when data does not meet expectations.

### Detailed summary
- Added `react-error-boundary` to handle errors.
- Created `UnexpectedValueErrorMessage` component for displaying error messages.
- Integrated error handling in `TokenIdPage`, `NFTName`, and `NFTDescription` components.
- Updated `NFTGetAllTable` to use `UnexpectedValueErrorMessage` for invalid data.
- Wrapped NFT cell rendering in `ErrorBoundary` to catch errors.

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

<!-- end pr-codex -->
2024-10-21 16:42:20 +00:00
kien-ngo d59ab13ce8 [Dashboard] Add Media URI to token page (#5088)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR enhances the `TokenIdPage` component by adding a section to display the media URI of an NFT, including a button to copy the URI and a link to view it externally.

### Detailed summary
- Added a conditional rendering block to display the media URI if `nft.metadata.image` exists.
- Included a `GridItem` for the "Media URI" heading.
- Added a `CopyTextButton` to copy the media URI.
- Added a button with a link to the media URI, featuring an `ExternalLinkIcon`.

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

<!-- end pr-codex -->
2024-10-21 13:44:06 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
221f95b4f8 Version Packages (#5093)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-21 23:17:57 +13:00
Joaquim VergesandGitHub 223c497f49 fix: ensure smart accounts are deployed before validating signatures (#5092) 2024-10-21 23:08:43 +13:00
Joaquim VergesandGitHub 541bee5b34 fix: update useProfiles when connecting to different account (#5089) 2024-10-21 22:40:31 +13:00
MananTank df128bfc17 Open Share Modal after applying freewallets coupon (#5085)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new modal component, `ShareFreeWalletsModal`, to facilitate sharing free wallet offers via social media or email. It also updates the `CouponCard` to handle coupon applications and trigger the modal.

### Detailed summary
- Added `ShareFreeWalletsModal` component for sharing options.
- Created Storybook stories for `ShareFreeWalletsModal` in `share-free-wallets-modal.stories.tsx`.
- Updated `ApplyCouponCard` to include an additional parameter for coupon application callback.
- Integrated lazy loading for `ShareFreeWalletsModal` in `CouponCard`.
- Implemented state management for modal visibility in `CouponSection`.

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

<!-- end pr-codex -->
2024-10-19 18:31:07 +00:00
kien-ngo f2d10853f1 [Dashboard] Remove AddressCopyButton (#5077)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on removing the `AddressCopyButton` component from various files and replacing it with `CopyTextButton` and `CopyAddressButton` components to optimize bundle size and improve code consistency.

### Detailed summary
- Deleted `AddressCopyButton.tsx` file.
- Removed all imports of `AddressCopyButton` from multiple files.
- Replaced instances of `AddressCopyButton` with `CopyTextButton` in `webhooks-table.tsx` and `token-id.tsx`.
- Replaced `AddressCopyButton` with `CopyAddressButton` in `transaction-timeline.tsx` and `contract-subscriptions-table.tsx`.

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

<!-- end pr-codex -->
2024-10-19 08:52:15 +00:00
kien-ngo a68830ca67 [Dashboard] Edition's Mint tab to have custom recipient (#4981)
<img width="1211" alt="image" src="https://github.com/user-attachments/assets/35a4a06f-6170-4757-a901-73eb43e0af89">

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `mint-supply-tab` component by integrating a new form validation schema using `zod`, improving the user experience with better form handling, and updating the imports for consistency.

### Detailed summary
- Added `zod` for form validation.
- Created `mintAdditionalSupplyFormSchema` to validate recipient address and mint amount.
- Refactored form handling to use the new validation schema.
- Updated the form structure with `Form`, `FormField`, and related components.
- Replaced old form submission logic with a new `onSubmit` function.
- Improved user feedback with toast notifications for transaction status.

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

<!-- end pr-codex -->
2024-10-19 08:40:16 +00:00
MananTank 5da500edca Add noUncheckedIndexedAccess: true in dashboard tsconfig and fix all issues (#5084)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces various enhancements and bug fixes across multiple files, focusing on improving type safety, handling undefined values, and refining conditions to prevent errors. It also updates the handling of cookies, contract metadata, and native token definitions.

### Detailed summary
- Added `"noUncheckedIndexedAccess": true` in `tsconfig.json`.
- Updated cookie retrieval to check for `_val` in `SyncStoreToCookies.tsx`.
- Modified ecosystem redirection condition in `EcosystemLandingPage.tsx`.
- Added checks for undefined chains in `allChains.ts`.
- Improved handling of optional values in various components.
- Enhanced error handling for uploaded files in `IconUpload.tsx`.
- Refactored native token definitions for better clarity and maintainability.
- Checked for existence of items before accessing properties in several components.
- Ensured consistent handling of optional chaining to avoid runtime errors.
- Updated metadata handling in various contract-related components.

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

<!-- end pr-codex -->
2024-10-19 02:46:44 +00:00
kien-ngo 4542a48933 [Dashboard] Add tokenURI section to the NFT page (#5035)
<img width="1172" alt="image" src="https://github.com/user-attachments/assets/bef8ec5b-b453-43e1-bc90-f7156b3dd578">

<!-- start pr-codex -->

---

## PR-Codex overview
This PR enhances the `TokenIdPage` component by adding functionality for displaying and copying the token URI of an NFT. It introduces a `CopyTextButton` and a link that opens the token URI in a new tab, improving user interaction and accessibility.

### Detailed summary
- Added import for `CopyTextButton`.
- Added import for `ExternalLinkIcon`.
- Introduced `Link` from `next/link`.
- Added `token URI` section in the UI.
- Implemented `CopyTextButton` to copy the `nft.tokenURI`.
- Added a button with a link that opens the `nft.tokenURI` in a new tab.

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

<!-- end pr-codex -->
2024-10-18 23:58:59 +00:00
Vasiliy GualotoandGitHub 17127d7763 Docs(update): Correcting the Modular contract getting started tutoria… (#5074) 2024-10-18 16:14:38 -07:00
MananTank 0dbb174e50 Mention missing Unreal Engine in various copy (#5082)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the descriptions in two files to include `Unreal Engine` as a supported platform alongside `Unity` and `React Native` for social logins and smart contract integrations.

### Detailed summary
- Updated the text in `apps/dashboard/src/components/embedded-wallets/Configure/index.tsx` to include `Unreal Engine` in the supported platforms for social logins.
- Modified the `description` in `apps/dashboard/src/pages/interact.tsx` to add `Unreal Engine` as a supported platform for smart contract integrations.

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

<!-- end pr-codex -->
2024-10-18 17:18:50 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
0a1d59d789 Version Packages (#5069)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-18 10:18:08 -07:00
MananTank a04cda1e5a Improve chain page Open graph metadata + image (#5081)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the Open Graph metadata and improving the layout of the `chainPage` component in the dashboard application.

### Detailed summary
- In `layout.tsx`, added an `openGraph` object containing `title` and `description`.
- In `opengraph-image.tsx`, added the `objectFit` property with the value `"contain"` for image styling.

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

<!-- end pr-codex -->
2024-10-18 17:00:19 +00:00
gregfromstl 67a363f624 [SDK] Fix: ConnectEmbed SIWE Revalidation (#5080)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on fixing revalidation issues related to `siwe` authentication in the `ConnectEmbed` component by updating mutation keys to include the `activeAccount` address.

### Detailed summary
- Updated `mutationKey` for the `login` mutation to include `activeAccount?.address`.
- Updated `mutationKey` for the `logout` mutation to include `activeAccount?.address`.
- Added `gcTime`, `placeholderData`, and `refetchOnWindowFocus` options in the hook configuration.

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

<!-- end pr-codex -->
2024-10-18 16:23:33 +00:00
kien-ngo 1cd11a86f7 [Dashboard] Remove unnecessary chakra-ui Tabs in Listing sheet (#5076)
Remove unnecessary Tabs, just put the button at the bottom, since there's ever been only 1 tab anyway

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/VtULqGKSp33n0VHOzNJK/53a75b30-20b2-471c-9566-4b022a96b616.png)

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on refactoring the marketplace components by removing unused files and consolidating the listing cancellation functionality into a single component, `CancelTab`. It also enhances the `ListingDrawer` component with improved UI elements for displaying asset information.

### Detailed summary
- Deleted `CancelDirectListing.tsx` and `CancelEnglishAuction.tsx`.
- Introduced `CancelTab` for handling cancellation of listings and auctions.
- Updated `ListingDrawer` to use `CancelTab`.
- Replaced `useTxNotifications` with `toast` for transaction feedback.
- Added `CopyAddressButton` and `CopyTextButton` for better user interaction.
- Refactored asset details display in `ListingDrawer` for improved readability.

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

<!-- end pr-codex -->
2024-10-18 07:46:39 +00:00
iuwqyir 9da5cd7d49 Add insight service and expose cacheOptions for node auth (#5072)
## Problem solved

This update adds the "insight" scope to API keys and exposes cacheOptions for node authentication. It also includes the "insight" service in the SERVICE_DEFINITIONS object and adds it to the HIDDEN_SERVICES array. The authorizeNode function now accepts an optional cacheOptions parameter, allowing for more flexible caching strategies in node authentication.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `@thirdweb-dev/service-utils` by adding an `insight` service, modifying the `CacheOptions` type, and updating the `authorizeNode` function to accept `cacheOptions`. It also includes changes to the `HIDDEN_SERVICES` constant.

### Detailed summary
- Added `insight` service with properties `name`, `title`, and `description`.
- Updated `HIDDEN_SERVICES` to include `insight`.
- Changed `CacheOptions` from a local type to an exported type.
- Modified `authorizeNode` to accept `cacheOptions` as an argument.
- Updated the call to `authorize` to include `cacheOptions`.

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

<!-- end pr-codex -->
2024-10-18 07:12:12 +00:00
kien-ngo 0dd1438012 [Dashboard] Add Cloudflare Turnstile to Faucet page (#5067)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces support for Cloudflare Turnstile captcha validation in the faucet functionality of the dashboard application, enhancing security during fund claims. It also updates dependencies and configuration for the new feature.

### Detailed summary
- Added `TURNSTILE_SITE_KEY` constant to `env.ts`.
- Updated `package.json` to include `@marsidev/react-turnstile`.
- Modified `.env.example` to add `NEXT_PUBLIC_TURNSTILE_SITE_KEY` and `TURNSTILE_SECRET_KEY`.
- Updated `next.config.js` to allow scripts from Cloudflare.
- Enhanced `POST` handler in `route.ts` to validate `turnstileToken`.
- Updated `FaucetButton.tsx` to integrate Turnstile captcha and form handling.
- Introduced `claimFaucetSchema` for form validation using `zod`.

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

<!-- end pr-codex -->
2024-10-18 05:42:42 +00:00
joaquim-verges 0f4e6622e0 feat: add custom auth options to ecosystem settings (#5075)
## Problem solved

![CleanShot 2024-10-18 at 14.59.26.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/GGPGC1k4LEcpDtkOsVBe/73287a0a-c367-4a6e-9897-5df7c2935df6.png)

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `Ecosystem` type and its related components by adding support for custom authentication options, including headers and endpoints, and updating the mutation logic to accommodate these changes.

### Detailed summary
- Added `customAuthOptions` to the `Ecosystem` type.
- Updated `useUpdateEcosystem` to accept `Ecosystem` directly.
- Modified mutation logic to handle the new `Ecosystem` structure.
- Introduced `CustomAuthOptionsForm` for managing custom authentication endpoints and headers.
- Enhanced UI components to reflect new functionality.

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

<!-- end pr-codex -->
2024-10-18 04:47:34 +00:00
kien-ngo e67a300410 [Dashboard] Update support email to include wallet address (DASH-75) (#5055)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR refactors the `prepareEmailBody` function to accept a single `props` object instead of multiple parameters. It also updates the function to include `name` and `walletAddress`, while modifying the email body to reflect these changes.

### Detailed summary
- Changed `prepareEmailBody` to accept a single `props` object.
- Added `name` and `walletAddress` to the `props` object.
- Updated the email body to include `# Name: ${name}` and `# Wallet address: ${walletAddress}`.
- Modified the call to `prepareEmailBody` to pass a structured object instead of separate arguments.

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

<!-- end pr-codex -->
2024-10-17 21:23:34 +00:00
MananTank 00a6a56158 Move Contract page components to app router folder #2 (no code change) (#5073)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR refactors the structure of the dashboard application, changing the import paths for several components and pages to a more localized format. It also moves certain components related to contract management and permissions to new locations.

### Detailed summary
- Updated import paths for various pages and components to local paths.
- Moved `ContractSplitPage` to `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.tsx`.
- Moved `DistributeButton` to `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx`.
- Moved `ContractProposalsPage` to `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.tsx`.
- Moved `ContractEditModulesPage` to `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.tsx`.
- Moved `ContractPermissionsPage` to `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.tsx`.
- Moved `ContractExplorerPage` to `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx`.
- Moved `ContractSettingsPage` to `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx`.
- Updated import statements in `events-feed.tsx` and `permissions/index.tsx` to reflect new paths.
- Adjusted type imports in various components to align with the new structure.

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

<!-- end pr-codex -->
2024-10-17 21:03:42 +00:00
MananTank 7855c3ea8a Move Contract page components to app router folder (no code change) (#5071)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on restructuring the file organization and imports within the `dashboard` application. It moves components to a more cohesive structure, enhancing maintainability and clarity.

### Detailed summary
- Updated imports to use relative paths instead of absolute paths.
- Renamed several component files for consistency.
- Moved `CancelDirectListing` and `CancelEnglishAuction` to a new structure under `components`.
- Adjusted imports in various `page.tsx` files to reflect new component paths.
- Consolidated marketplace-related components under a unified structure.

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

<!-- end pr-codex -->
2024-10-17 20:22:08 +00:00
GWSzeto 6597cb6525 fix: small typo (#5070)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on correcting the links in the `page.mdx` file related to modular contracts in the portal documentation.

### Detailed summary
- Fixed the link for `Create Core Contract` from `create-core-contact` to `create-core-contract`.
- Fixed the link for `Create Module Contract` from `create-module-contact` to `create-module-contract`.
- Removed the link for `Deploy Modular Contract`.

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

<!-- end pr-codex -->
2024-10-17 19:19:16 +00:00
MananTank fbd9853697 Add extra info on page crashing errors to identify them in Sentry dashboard (#5068)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR enhances error handling in the application by integrating `Sentry` with additional context for captured exceptions, specifically tagging the errors and providing extra information about the crashed page and router.

### Detailed summary
- In `global-error.tsx`, modified `Sentry.captureException` to use `Sentry.withScope` for better error context.
- Added tags and extra data (e.g., `crashedPage`, `boundary`, `router`) to the captured exceptions.
- In `_error.tsx`, implemented similar changes for handling errors with `contextData`.

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

<!-- end pr-codex -->
2024-10-17 19:02:15 +00:00
MananTank b01faacd99 Catch failed to post tracking errors (#5065)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving error handling in the `track.ts` file by catching errors during tracking operations to prevent them from being reported to error services.

### Detailed summary
- Modified the promise chain in `track.ts` to catch errors by adding a `.catch(() => {});` statement.
- Updated the changelog in `changeset/rotten-weeks-raise.md` to reflect the change in error handling for tracking operations.

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

<!-- end pr-codex -->
2024-10-17 18:48:28 +00:00
MananTank ef6b4f48ef Add reason string on abort() method in fetch util (#5063)
Fixes: DASH-323

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving error handling in the `abort` method of the `AbortController` by adding a reason string. This change aims to prevent the `AbortError: signal is aborted without reason` errors and updates tests to reflect the new error message.

### Detailed summary
- Updated the `abort` method in `fetch.ts` to include a reason string: `controller?.abort("timeout")`.
- Changed the expected error message in `download.test.ts` from "This operation was aborted" to "timeout" for two test cases.

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

<!-- end pr-codex -->
2024-10-17 18:35:23 +00:00
MananTank c5a2cdb3b6 Fix Cannot read properties of undefined (reading 'message') in errorParser.tsx (#5066)
Fixes: DASH-326
2024-10-17 18:24:12 +00:00
MananTank 5dd0c91d3e catch and handle error in TokenClaimButton (#5064)
Fixes: DASH-324
2024-10-17 18:12:22 +00:00
kien-ngo f8f90fd3ab [Dashboard] Fix prepareEvent code snippet (#5052)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on removing redundant lines of code related to the `preparedEvent` declaration in the `code-overview.tsx` file.

### Detailed summary
- Removed duplicate declarations of `preparedEvent` that included `contract` and `signature: "{{function}}"`.
- Streamlined the `getContractEvents` function call by eliminating unnecessary lines.

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

<!-- end pr-codex -->
2024-10-17 11:22:13 +00:00
joaquim-verges 63037f1bf1 fix: ensure valid block explorer URL for address links (#5061)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the `href` attribute in the `metadata-header.tsx` file to ensure that the URL for the block explorer correctly appends the address, handling cases where the URL may or may not end with a trailing slash.

### Detailed summary
- Modified the `href` attribute to check if `validBlockExplorer.url` ends with a `/`.
- If it does not, a `/` is added before appending `address/${address}`.

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

<!-- end pr-codex -->
2024-10-17 05:45:07 +00:00
625 changed files with 16526 additions and 17545 deletions
+2
View File
@@ -29,3 +29,5 @@ packages/*/typedoc/*
*storybook.log
storybook-static
.aider*
tsconfig.tsbuildinfo
+5 -10
View File
@@ -75,15 +75,6 @@ GITHUB_API_TOKEN="ghp_..."
# Upload server url
NEXT_PUBLIC_DASHBOARD_UPLOAD_SERVER="https://storage.thirdweb-preview.com"
# Payments Admin Secret. For dev, get this value from paper-web-dev (on Zeet)'s "HASURA_ADMIN_SECRET_KEY" env var
PAYMENTS_ADMIN_SECRET=""
# Disable telemetry
NEXT_TELEMETRY_DISABLED=1
# Use trench for KYC
NEXT_PUBLIC_TRENCH_2_API_ENDPOINT=""
# Unthread variables - only required for submitting the support form in /support
UNTHREAD_API_KEY=""
UNTHREAD_TRIAGE_CHANNEL_ID=""
@@ -96,4 +87,8 @@ UNTHREAD_PRO_TIER_ID=""
NEXT_PUBLIC_DEMO_ENGINE_URL=""
# API server secret (required for thirdweb.com SIWE login). Copy from Vercel.
API_SERVER_SECRET=""
API_SERVER_SECRET=""
# Used for the Faucet page (/<chain_id>)
NEXT_PUBLIC_TURNSTILE_SITE_KEY=""
TURNSTILE_SECRET_KEY=""
+5
View File
@@ -34,6 +34,11 @@ module.exports = {
message:
"Use useV5DashboardChain instead if you are using it inside a component",
},
{
selector: "CallExpression[callee.name='resolveScheme']",
message:
"resolveScheme can throw error if resolution fails. Either catch the error and ignore the lint warning or Use `resolveSchemeWithErrorHandler` / `replaceIpfsUrl` utility in dashboard instead",
},
],
"no-restricted-imports": [
"error",
+7
View File
@@ -0,0 +1,7 @@
// add framer paths here
module.exports = [
"/",
"/connect/sign-in",
"/contracts/modular-contracts",
"/unlimited-wallets",
];
+9
View File
@@ -1,4 +1,5 @@
// @ts-check
/**
*
*/
@@ -77,8 +78,16 @@ module.exports = {
};
},
additionalPaths: async (config) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const FRAMER_PATHS = require("./framer-rewrites");
const allChains = await fetchChainsFromApi();
return [
...FRAMER_PATHS.map((path) => ({
loc: path,
changefreq: config.changefreq,
priority: config.priority,
lastmod: config.autoLastmod ? new Date().toISOString() : undefined,
})),
...allChains.map((chain) => {
return {
loc: `/${chain.slug}`,
+9 -10
View File
@@ -6,9 +6,9 @@ const ContentSecurityPolicy = `
media-src * data: blob:;
object-src 'none';
style-src 'self' 'unsafe-inline' vercel.live;
font-src 'self' vercel.live assets.vercel.com framerusercontent.com;
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;
script-src 'self' 'unsafe-eval' 'unsafe-inline' 'wasm-unsafe-eval' 'inline-speculation-rules' *.thirdweb.com *.thirdweb-dev.com vercel.live js.stripe.com framerusercontent.com events.framer.com challenges.cloudflare.com;
connect-src * data: blob:;
worker-src 'self' blob:;
block-all-mixed-content;
@@ -38,13 +38,7 @@ const securityHeaders = [
];
const redirects = require("./redirects");
// add framer paths here
const FRAMER_PATHS = [
"/connect/sign-in",
"/contracts/modular-contracts",
"/unlimited-wallets",
];
const FRAMER_PATHS = require("./framer-rewrites");
/**
* @returns {import('next').RemotePattern[]}
@@ -135,6 +129,11 @@ const moduleExports = {
source: "/thirdweb.eth/:path*",
destination: "/deployer.thirdweb.eth/:path*",
},
// 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: "/",
},
...FRAMER_PATHS.map((path) => ({
source: path,
destination: `https://landing.thirdweb.com${path}`,
@@ -191,7 +190,7 @@ module.exports = withBundleAnalyzer(
// An auth token is required for uploading source maps.
authToken: process.env.SENTRY_AUTH_TOKEN,
// Suppresses source map uploading logs during build
silent: false,
silent: true,
// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
+22 -22
View File
@@ -25,10 +25,10 @@
"@chakra-ui/react": "^2.8.2",
"@chakra-ui/styled-system": "^2.9.2",
"@chakra-ui/theme-tools": "^2.1.2",
"@coinbase/onchainkit": "^0.14.2",
"@emotion/react": "11.13.3",
"@emotion/styled": "11.13.0",
"@hookform/resolvers": "^3.9.0",
"@marsidev/react-turnstile": "^1.0.2",
"@n8tb1t/use-scroll-position": "^2.0.3",
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-avatar": "^1.1.1",
@@ -45,11 +45,11 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tooltip": "1.1.3",
"@sentry/nextjs": "8.34.0",
"@shazow/whatsabi": "^0.15.3",
"@sentry/nextjs": "8.35.0",
"@shazow/whatsabi": "^0.15.4",
"@stripe/react-stripe-js": "^2.8.1",
"@stripe/stripe-js": "^3.5.0",
"@tanstack/react-query": "5.59.13",
"@tanstack/react-query": "5.59.16",
"@tanstack/react-table": "^8.17.3",
"@thirdweb-dev/service-utils": "workspace:*",
"@vercel/og": "^0.6.2",
@@ -61,13 +61,13 @@
"compare-versions": "^6.1.0",
"date-fns": "4.1.0",
"flat": "^6.0.1",
"framer-motion": "11.11.8",
"framer-motion": "11.11.10",
"fuse.js": "7.0.0",
"input-otp": "^1.2.4",
"ioredis": "^5.4.1",
"ipaddr.js": "^2.2.0",
"lottie-react": "^2.4.0",
"lucide-react": "0.452.0",
"lucide-react": "0.454.0",
"next": "14.2.15",
"next-plausible": "^3.12.0",
"next-seo": "^6.5.0",
@@ -84,8 +84,8 @@
"react-day-picker": "^8.10.1",
"react-dom": "18.3.1",
"react-dropzone": "^14.2.9",
"react-error-boundary": "^4.1.2",
"react-hook-form": "7.52.0",
"react-intersection-observer": "^9.10.3",
"react-markdown": "^9.0.1",
"react-responsive-carousel": "^3.2.23",
"react-table": "^7.8.0",
@@ -107,19 +107,19 @@
"@chromatic-com/storybook": "2.0.2",
"@next/bundle-analyzer": "14.2.15",
"@next/eslint-plugin-next": "14.2.15",
"@playwright/test": "1.47.2",
"@storybook/addon-essentials": "8.3.5",
"@storybook/addon-interactions": "8.3.5",
"@storybook/addon-links": "8.3.5",
"@storybook/addon-onboarding": "8.3.5",
"@storybook/addon-viewport": "8.3.5",
"@storybook/blocks": "8.3.5",
"@storybook/nextjs": "8.3.5",
"@storybook/react": "8.3.5",
"@storybook/test": "8.3.5",
"@types/color": "^3.0.6",
"@playwright/test": "1.48.2",
"@storybook/addon-essentials": "8.3.6",
"@storybook/addon-interactions": "8.3.6",
"@storybook/addon-links": "8.3.6",
"@storybook/addon-onboarding": "8.3.6",
"@storybook/addon-viewport": "8.3.6",
"@storybook/blocks": "8.3.6",
"@storybook/nextjs": "8.3.6",
"@storybook/react": "8.3.6",
"@storybook/test": "8.3.6",
"@types/color": "4.2.0",
"@types/node": "20.14.9",
"@types/papaparse": "^5.3.14",
"@types/papaparse": "^5.3.15",
"@types/pluralize": "^0.0.33",
"@types/qrcode": "^1.5.5",
"@types/react": "^18.3.11",
@@ -135,11 +135,11 @@
"eslint-config-biome": "1.9.3",
"eslint-plugin-react-compiler": "0.0.0-experimental-fa06e2c-20241014",
"eslint-plugin-storybook": "^0.9.0",
"knip": "^5.33.3",
"knip": "5.34.2",
"next-sitemap": "^4.2.3",
"postcss": "8.4.47",
"storybook": "8.3.5",
"tailwindcss": "3.4.13",
"storybook": "8.3.6",
"tailwindcss": "3.4.14",
"typescript": "5.6.3"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

@@ -119,13 +119,7 @@ export function AppFooter(props: AppFooterProps) {
>
Terms of Service
</Link>
<Link
className="px-[10px] py-[6px] text-muted-foreground text-sm hover:underline"
href="https://thirdweb.com/gas"
target="_blank"
>
Gas Estimator
</Link>
<Link
className="px-[10px] py-[6px] text-muted-foreground text-sm hover:underline"
href="https://thirdweb.com/chainlist"
@@ -0,0 +1,44 @@
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
import { useMemo } from "react";
export function UnexpectedValueErrorMessage(props: {
value: unknown;
title: string;
description: string;
className?: string;
}) {
const stringifiedValue = useMemo(() => {
try {
return JSON.stringify(props.value, null, 2);
} catch {
return undefined;
}
}, [props.value]);
return (
<div className={props.className}>
<p className="text-base text-destructive-text"> {props.title} </p>
<p className="text-foreground text-sm">{props.description}</p>
{stringifiedValue && (
<div className="mt-3">
<p className="mb-0.5 text-muted-foreground text-sm">Value Received</p>
<ScrollShadow className="rounded-lg bg-muted/50">
<code className="block whitespace-pre p-4 font-mono text-xs">
{stringifiedValue}
</code>
</ScrollShadow>
<CopyTextButton
copyIconPosition="left"
textToShow="Copy value"
textToCopy={stringifiedValue}
tooltip="Copy value"
className="-translate-x-2 mt-1 text-muted-foreground"
variant="ghost"
/>
</div>
)}
</div>
);
}
@@ -119,13 +119,18 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
if (filteredOptions.length >= itemsToShow) {
break;
}
const option = options[i];
if (!option) {
continue;
}
if (overrideSearchFn) {
if (overrideSearchFn(options[i], searchValLowercase)) {
filteredOptions.push(options[i]);
if (overrideSearchFn(option, searchValLowercase)) {
filteredOptions.push(option);
}
} else {
if (options[i].label.toLowerCase().includes(searchValLowercase)) {
filteredOptions.push(options[i]);
if (option.label.toLowerCase().includes(searchValLowercase)) {
filteredOptions.push(option);
}
}
}
@@ -63,14 +63,18 @@ export const SelectWithSearch = React.forwardRef<
if (filteredOptions.length >= itemsToShow) {
break;
}
const option = options[i];
if (!option) {
continue;
}
if (overrideSearchFn) {
if (overrideSearchFn(options[i], searchValLowercase)) {
filteredOptions.push(options[i]);
if (overrideSearchFn(option, searchValLowercase)) {
filteredOptions.push(option);
}
} else {
if (options[i].label.toLowerCase().includes(searchValLowercase)) {
filteredOptions.push(options[i]);
if (option.label.toLowerCase().includes(searchValLowercase)) {
filteredOptions.push(option);
}
}
}
@@ -1,5 +1,4 @@
"use client";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
HoverCard,
@@ -7,6 +6,7 @@ import {
HoverCardTrigger,
} from "@/components/ui/hover-card";
import { useThirdwebClient } from "@/constants/thirdweb.client";
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
import { useClipboard } from "hooks/useClipboard";
import { Check, Copy, ExternalLinkIcon } from "lucide-react";
import { useMemo } from "react";
@@ -18,7 +18,6 @@ import {
type SocialProfile,
useSocialProfiles,
} from "thirdweb/react";
import { resolveScheme } from "thirdweb/storage";
import { cn } from "../../lib/utils";
import { Badge } from "../ui/badge";
import { Button } from "../ui/button";
@@ -113,22 +112,20 @@ export function WalletAddress(props: {
) : !profiles.data?.length ? (
<p className="text-muted-foreground text-sm">No profiles found</p>
) : (
profiles.data?.map((profile) => (
<div
className="flex flex-row items-center gap-2"
key={profile.type + profile.name}
>
{profile.avatar &&
(profile.avatar.startsWith("http") ||
profile.avatar?.startsWith("ipfs")) && (
profiles.data?.map((profile) => {
const walletAvatarLink = resolveSchemeWithErrorHandler({
client: thirdwebClient,
uri: profile.avatar,
});
return (
<div
className="flex flex-row items-center gap-2"
key={profile.type + profile.name}
>
{walletAvatarLink && (
<Avatar>
<AvatarImage
src={resolveScheme({
client: thirdwebClient,
uri: profile.avatar,
})}
alt={profile.name}
/>
<AvatarImage src={walletAvatarLink} alt={profile.name} />
{profile.name && (
<AvatarFallback>
{profile.name.slice(0, 2)}
@@ -136,19 +133,20 @@ export function WalletAddress(props: {
)}
</Avatar>
)}
<div className="flex w-full flex-col gap-1">
<div className="flex w-full flex-row items-center justify-between gap-4">
<h4 className="font-semibold text-md">{profile.name}</h4>
<Badge variant="outline">{profile.type}</Badge>
<div className="flex w-full flex-col gap-1">
<div className="flex w-full flex-row items-center justify-between gap-4">
<h4 className="font-semibold text-md">{profile.name}</h4>
<Badge variant="outline">{profile.type}</Badge>
</div>
{profile.bio && (
<p className="line-clamp-1 whitespace-normal text-muted-foreground text-sm">
{profile.bio}
</p>
)}
</div>
{profile.bio && (
<p className="line-clamp-1 whitespace-normal text-muted-foreground text-sm">
{profile.bio}
</p>
)}
</div>
</div>
))
);
})
)}
<Button
asChild
+1 -1
View File
@@ -143,7 +143,7 @@ const ChartTooltipContent = React.forwardRef<
}
const [item] = payload;
const key = `${labelKey || item.dataKey || item.name || "value"}`;
const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key);
const value =
!labelKey && typeof label === "string"
@@ -0,0 +1,36 @@
"use client";
import { useClipboard } from "hooks/useClipboard";
import { CheckIcon, CopyIcon } from "lucide-react";
import { cn } from "../../lib/utils";
import { Button } from "./button";
export function PlainTextCodeBlock(props: {
code: string;
copyButtonClassName?: string;
}) {
const { hasCopied, onCopy } = useClipboard(props.code);
return (
<div className="relative">
<code className="block whitespace-pre rounded-lg border border-border bg-muted/50 p-4">
{props.code}
</code>
<Button
size="sm"
variant="outline"
onClick={onCopy}
className={cn(
"absolute top-3.5 right-3.5 h-auto p-2",
props.copyButtonClassName,
)}
>
{hasCopied ? (
<CheckIcon className="size-3 text-green-500" />
) : (
<CopyIcon className="size-3 text-muted-foreground" />
)}
</Button>
</div>
);
}
@@ -16,7 +16,9 @@ const ImageUpload = React.forwardRef<HTMLInputElement, ImageUploadProps>(
const [activeFile, setActiveFile] = React.useState<File | null>(null);
const onDrop = React.useCallback(
(acceptedFiles: File[]) => {
setActiveFile(acceptedFiles[0]);
if (acceptedFiles[0]) {
setActiveFile(acceptedFiles[0]);
}
onUpload?.(acceptedFiles);
},
[onUpload],
@@ -35,7 +35,8 @@ const InputOTPSlot = React.forwardRef<
React.ComponentPropsWithoutRef<"div"> & { index: number }
>(({ index, className, ...props }, ref) => {
const inputOTPContext = React.useContext(OTPInputContext);
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index];
// biome-ignore lint/style/noNonNullAssertion: pure shadcn component - it works
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]!;
return (
<div
@@ -62,7 +63,7 @@ const InputOTPSeparator = React.forwardRef<
React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div">
>(({ ...props }, ref) => (
// biome-ignore lint/a11y/useFocusableInteractive: <explanation>
// biome-ignore lint/a11y/useFocusableInteractive: pure shadcn component - it works
<div ref={ref} role="separator" {...props}>
<Dot />
</div>
@@ -51,12 +51,12 @@ const RadioGroupItemButton = React.forwardRef<
<RadioGroupPrimitive.Item
ref={ref}
className={cn(
"group flex min-w-32 cursor-pointer items-center space-x-3 space-y-0 rounded-md border-2 px-3 py-4 font-medium transition-all hover:border-foreground/25 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 peer-hover:border-nonce data-[state=checked]:border-inverted",
"group flex min-w-32 cursor-pointer items-center space-x-3 space-y-0 rounded-lg border px-4 py-3 font-medium transition-all hover:border-foreground/25 hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 peer-hover:border-nonce data-[state=checked]:border-muted-foreground",
className,
)}
{...props}
>
<div className="flex aspect-square h-4 w-4 items-center justify-center rounded-full border-2 text-inverted ring-offset-background transition-all focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 group-hover:border-foreground/25 group-data-[state=checked]:border-inverted">
<div className="flex aspect-square h-4 w-4 items-center justify-center rounded-full border text-inverted ring-offset-background transition-all focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 group-hover:border-foreground/25 group-data-[state=checked]:border-inverted">
{/* Show on checked */}
<RadioGroupPrimitive.Indicator className="flex items-center justify-center rounded-full transition-all">
<Circle className="h-2 w-2 fill-current text-current" />
-1
View File
@@ -5,7 +5,6 @@ const LOGGED_IN_ONLY_PATHS = [
"/team",
// anything that _starts_ with /cli is logged in only
"/cli",
"/support",
// publish page
"/contracts/publish",
];
+3
View File
@@ -23,3 +23,6 @@ export const DASHBOARD_STORAGE_URL =
export const API_SERVER_URL =
process.env.NEXT_PUBLIC_THIRDWEB_API_HOST || "https://api.thirdweb.com";
export const TURNSTILE_SITE_KEY =
process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || "";
@@ -0,0 +1,21 @@
import type { ThirdwebClient } from "thirdweb";
import { resolveScheme } from "thirdweb/storage";
export function resolveSchemeWithErrorHandler(options: {
uri: string | undefined;
client: ThirdwebClient;
}) {
if (!options.uri) {
return undefined;
}
try {
// eslint-disable-next-line no-restricted-syntax
return resolveScheme({
uri: options.uri,
client: options.client,
});
} catch (err) {
console.error("error resolving ipfs url", options.uri, err);
return undefined;
}
}
@@ -44,13 +44,12 @@ export function useActivity(contract: ThirdwebContract, autoUpdate?: boolean) {
}
const obj = eventsQuery.data.slice(0, 100).reduce(
(acc, curr) => {
if (acc[curr.transactionHash]) {
acc[curr.transactionHash].events.push(curr);
acc[curr.transactionHash].events.sort(
(a, b) => b.logIndex - a.logIndex,
);
if (acc[curr.transactionHash].blockNumber > curr.blockNumber) {
acc[curr.transactionHash].blockNumber = curr.blockNumber;
const internalTx = acc[curr.transactionHash];
if (internalTx) {
internalTx.events.push(curr);
internalTx.events.sort((a, b) => b.logIndex - a.logIndex);
if (internalTx.blockNumber > curr.blockNumber) {
internalTx.blockNumber = curr.blockNumber;
}
} else {
acc[curr.transactionHash] = {
@@ -5,6 +5,7 @@ import {
useQueryClient,
} from "@tanstack/react-query";
import { THIRDWEB_ANALYTICS_API_HOST, THIRDWEB_API_HOST } from "constants/urls";
import { useAllChainsData } from "hooks/chains/allChains";
import invariant from "tiny-invariant";
import { accountKeys, apiKeys, authorizedWallets } from "../cache-keys";
import { useLoggedInUser } from "./useLoggedInUser";
@@ -241,6 +242,14 @@ export interface UserOpStats {
sponsoredUsd: number;
}
export interface UserOpStatsByChain {
date: string;
successful: number;
failed: number;
sponsoredUsd: number;
chainId?: string;
}
interface BillingProduct {
name: string;
id: string;
@@ -434,8 +443,9 @@ export function useUserOpUsageAggregate(args: {
}) {
const { clientId, from, to } = args;
const { user, isLoggedIn } = useLoggedInUser();
const chainStore = useAllChainsData();
return useQuery({
return useQuery<UserOpStats>({
queryKey: accountKeys.userOpStats(
user?.address as string,
clientId as string,
@@ -444,12 +454,37 @@ export function useUserOpUsageAggregate(args: {
"all",
),
queryFn: async () => {
return getUserOpUsage({
clientId,
from,
to,
period: "all",
});
const userOpStats: (UserOpStats & { chainId?: string })[] =
await getUserOpUsage({
clientId,
from,
to,
period: "all",
});
// Aggregate stats across wallet types
return userOpStats.reduce(
(acc, curr) => {
// Skip testnets from the aggregated stats
if (curr.chainId) {
const chain = chainStore.idToChain.get(Number(curr.chainId));
if (chain?.testnet) {
return acc;
}
}
acc.successful += curr.successful;
acc.failed += curr.failed;
acc.sponsoredUsd += curr.sponsoredUsd;
return acc;
},
{
date: (from || new Date()).toISOString(),
successful: 0,
failed: 0,
sponsoredUsd: 0,
},
);
},
enabled: !!clientId && !!user?.address && isLoggedIn,
});
@@ -18,7 +18,7 @@ export function useDashboardContractMetadata(contract: ThirdwebContract) {
});
}
type DashboardContractMetadata = {
export type DashboardContractMetadata = {
name: string;
symbol: string;
image: string;
@@ -48,7 +48,7 @@ export async function fetchDashboardContractMetadata(
if (compilerMetadata?.name.includes(":")) {
const _name = compilerMetadata.name.split(":")[1];
if (_name) {
compilerMetadata.name = compilerMetadata.name.split(":")[1];
compilerMetadata.name = _name;
}
}
@@ -1,7 +1,7 @@
"use client";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type { ResultItem } from "components/engine/system-metrics/components/StatusCodes";
import type { ResultItem } from "app/team/[team_slug]/[project_slug]/engine/(instance)/[engineId]/metrics/components/StatusCodes";
import { THIRDWEB_API_HOST } from "constants/urls";
import type { EngineBackendWalletType } from "lib/engine";
import { useState } from "react";
@@ -2,17 +2,24 @@
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Button } from "@/components/ui/button";
import { THIRDWEB_ENGINE_FAUCET_WALLET } from "@/constants/env";
import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
import {
THIRDWEB_ENGINE_FAUCET_WALLET,
TURNSTILE_SITE_KEY,
} from "@/constants/env";
import { useThirdwebClient } from "@/constants/thirdweb.client";
import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet";
import { Turnstile } from "@marsidev/react-turnstile";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type { CanClaimResponseType } from "app/api/testnet-faucet/can-claim/CanClaimResponseType";
import { mapV4ChainToV5Chain } from "contexts/map-chains";
import { useTrack } from "hooks/analytics/useTrack";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { toUnits } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { useActiveAccount, useWalletBalance } from "thirdweb/react";
import { z } from "zod";
function formatTime(seconds: number) {
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
@@ -29,12 +36,18 @@ function formatTime(seconds: number) {
return rtf.format(+seconds, "second");
}
const claimFaucetSchema = z.object({
turnstileToken: z.string().min(1, {
message: "Captcha validation is required.",
}),
});
export function FaucetButton({
chain,
amount,
}: {
chain: ChainMetadata;
amount: string;
amount: number;
}) {
const client = useThirdwebClient();
const address = useActiveAccount()?.address;
@@ -52,7 +65,7 @@ export function FaucetButton({
const queryClient = useQueryClient();
const claimMutation = useMutation({
mutationFn: async () => {
mutationFn: async (turnstileToken: string) => {
trackEvent({
category: "faucet",
action: "claim",
@@ -67,6 +80,7 @@ export function FaucetButton({
body: JSON.stringify({
chainId: chainId,
toAddress: address,
turnstileToken,
}),
});
@@ -117,6 +131,8 @@ export function FaucetButton({
faucetWalletBalanceQuery.data !== undefined &&
faucetWalletBalanceQuery.data.value < toUnits("1", 17);
const form = useForm<z.infer<typeof claimFaucetSchema>>();
// loading state
if (faucetWalletBalanceQuery.isPending || canClaimFaucetQuery.isPending) {
return (
@@ -161,28 +177,46 @@ export function FaucetButton({
);
}
const claimFunds = (values: z.infer<typeof claimFaucetSchema>) => {
// Instead of having a dedicated endpoint (/api/verify-token),
// we can just attach the token in the payload and send it to the claim-faucet endpoint, to avoid a round-trip request
const claimPromise = claimMutation.mutateAsync(values.turnstileToken);
toast.promise(claimPromise, {
success: `${amount} ${chain.nativeCurrency.symbol} sent successfully`,
error: `Failed to claim ${amount} ${chain.nativeCurrency.symbol}`,
});
};
// eligible to claim and faucet has balance
return (
<div className="flex w-full flex-col text-center">
<Button
variant="primary"
className="w-full gap-2"
onClick={() => {
const claimPromise = claimMutation.mutateAsync();
toast.promise(claimPromise, {
success: `${amount} ${chain.nativeCurrency.symbol} sent successfully`,
error: `Failed to claim ${amount} ${chain.nativeCurrency.symbol}`,
});
}}
>
{claimMutation.isPending ? (
<>
Claiming <Spinner className="size-3" />
</>
) : (
`Get ${amount} ${chain.nativeCurrency.symbol}`
)}
</Button>
<Form {...form}>
<form onSubmit={form.handleSubmit(claimFunds)}>
<Button variant="primary" className="w-full gap-2" type="submit">
{claimMutation.isPending ? (
<>
Claiming <Spinner className="size-3" />
</>
) : (
`Get ${amount} ${chain.nativeCurrency.symbol}`
)}
</Button>
<FormField
control={form.control}
name="turnstileToken"
render={({ field }) => (
<FormItem>
<FormControl>
<Turnstile
siteKey={TURNSTILE_SITE_KEY}
onSuccess={(token) => field.onChange(token)}
/>
</FormControl>
</FormItem>
)}
/>
</form>
</Form>
{faucetWalletBalanceQuery.data && (
<p className="mt-3 text-muted-foreground text-xs">
@@ -1,16 +1,16 @@
import type { ChainMetadata } from "thirdweb/chains";
import { getFaucetClaimAmount } from "../../../../../../api/testnet-faucet/claim/claim-amount";
import { ChainIcon } from "../../../../components/server/chain-icon";
import { FaucetButton } from "../client/FaucetButton";
import { GiftIcon } from "../icons/GiftIcon";
import { SectionTitle } from "./SectionTitle";
const amountToGive = "0.01";
export async function FaucetSection(props: { chain: ChainMetadata }) {
const { chain } = props;
// Check eligibilty.
const sanitizedChainName = chain.name.replace("Mainnet", "").trim();
const amountToGive = getFaucetClaimAmount(props.chain.chainId);
return (
<section>
@@ -46,6 +46,10 @@ export async function generateMetadata({
return {
title,
description,
openGraph: {
title,
description,
},
};
}
@@ -122,6 +122,7 @@ export default async function Image({
right: -2,
height: 150,
width: 150,
objectFit: "contain",
}}
/>
)}
@@ -2,11 +2,11 @@
import { TransactionButton } from "components/buttons/TransactionButton";
import { useTrack } from "hooks/analytics/useTrack";
import { useTxNotifications } from "hooks/useTxNotifications";
import { useAllChainsData } from "hooks/chains/allChains";
import { toast } from "sonner";
import type { ThirdwebContract } from "thirdweb";
import { cancelAuction, cancelListing } from "thirdweb/extensions/marketplace";
import { useSendAndConfirmTransaction } from "thirdweb/react";
import { useAllChainsData } from "../../../hooks/chains/allChains";
interface CancelTabProps {
id: string;
@@ -25,14 +25,9 @@ export const CancelTab: React.FC<CancelTabProps> = ({
const transaction = isAuction
? cancelAuction({ contract, auctionId: BigInt(id) })
: cancelListing({ contract, listingId: BigInt(id) });
const { onSuccess, onError } = useTxNotifications(
"Listing cancelled",
"Error cancelling listing",
);
const cancelQuery = useSendAndConfirmTransaction();
return (
<div className="flex flex-col gap-3 pt-3">
{/* maybe some text? */}
<TransactionButton
txChainID={contract.chain.id}
transactionCount={1}
@@ -43,7 +38,7 @@ export const CancelTab: React.FC<CancelTabProps> = ({
action: "cancel-listing",
label: "attempt",
});
cancelQuery.mutate(transaction, {
const promise = cancelQuery.mutateAsync(transaction, {
onSuccess: () => {
trackEvent({
category: "marketplace",
@@ -51,7 +46,6 @@ export const CancelTab: React.FC<CancelTabProps> = ({
label: "success",
network,
});
onSuccess();
},
onError: (error) => {
trackEvent({
@@ -61,14 +55,19 @@ export const CancelTab: React.FC<CancelTabProps> = ({
network,
error,
});
onError(error);
console.error(error);
},
});
toast.promise(promise, {
loading: `Cancelling ${isAuction ? "auction" : "listing"}`,
success: "Item cancelled successfully",
error: "Failed to cancel",
});
}}
colorScheme="primary"
alignSelf="flex-end"
>
Cancel Listing
Cancel {isAuction ? "Auction" : "Listing"}
</TransactionButton>
</div>
);
@@ -8,7 +8,7 @@ import { useState } from "react";
import type { ThirdwebContract } from "thirdweb";
import { useActiveAccount } from "thirdweb/react";
import { Button, Drawer } from "tw-components";
import { CreateListingsForm } from "../listings/components/list-form";
import { CreateListingsForm } from "./list-form";
const LIST_FORM_ID = "marketplace-list-form";
@@ -15,6 +15,7 @@ import {
import { CurrencySelector } from "components/shared/CurrencySelector";
import { SolidityInput } from "contract-ui/components/solidity-inputs";
import { useTrack } from "hooks/analytics/useTrack";
import { useAllChainsData } from "hooks/chains/allChains";
import { useTxNotifications } from "hooks/useTxNotifications";
import { isAlchemySupported } from "lib/wallet/nfts/alchemy";
import { isMoralisSupported } from "lib/wallet/nfts/moralis";
@@ -57,8 +58,6 @@ import {
import { NFTMediaWithEmptyState } from "tw-components/nft-media";
import { shortenIfAddress } from "utils/usedapp-external";
import { useAllChainsData } from "../../../../hooks/chains/allChains";
type ListForm =
| (Omit<CreateListingParams, "quantity" | "currencyContractAddress"> & {
currencyContractAddress: string;
@@ -0,0 +1,180 @@
import { WalletAddress } from "@/components/blocks/wallet-address";
import { CopyAddressButton } from "@/components/ui/CopyAddressButton";
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { Sheet, SheetContent } from "@/components/ui/sheet";
import { Flex, GridItem, SimpleGrid, usePrevious } from "@chakra-ui/react";
import type { ThirdwebContract } from "thirdweb";
import type {
DirectListing,
EnglishAuction,
} from "thirdweb/extensions/marketplace";
import { useActiveAccount } from "thirdweb/react";
import { Badge, Card, CodeBlock, Heading, Text } from "tw-components";
import { NFTMediaWithEmptyState } from "tw-components/nft-media";
import { CancelTab } from "./cancel-tab";
import { LISTING_STATUS } from "./types";
interface NFTDrawerProps {
contract: ThirdwebContract;
isOpen: boolean;
onClose: () => void;
data: DirectListing | EnglishAuction | null;
}
export const ListingDrawer: React.FC<NFTDrawerProps> = ({
contract,
isOpen,
onClose,
data,
}) => {
const address = useActiveAccount()?.address;
const prevData = usePrevious(data);
const renderData = data || prevData;
const isOwner =
address?.toLowerCase() === renderData?.creatorAddress.toLowerCase();
const tokenId = renderData?.asset.id.toString() || "";
if (!renderData) {
return null;
}
return (
<Sheet open={isOpen} onOpenChange={onClose}>
<SheetContent className="flex w-full flex-col gap-6 py-6 md:min-w-[600px]">
<div className="flex flex-row gap-6">
<NFTMediaWithEmptyState
metadata={renderData.asset.metadata}
requireInteraction
width="150px"
height="150px"
/>
<Flex flexDir="column" gap={2} w="70%">
<Heading size="title.lg">{renderData.asset.metadata.name}</Heading>
<Text size="label.md" noOfLines={6}>
{renderData.asset.metadata.name}
</Text>
</Flex>
</div>
<Flex flexDir="column" gap={4}>
<Card as={Flex} flexDir="column" gap={3}>
<SimpleGrid rowGap={3} columns={12} placeItems="center left">
<GridItem colSpan={3}>
<Heading size="label.md">Asset contract address</Heading>
</GridItem>
<GridItem colSpan={9}>
<CopyAddressButton
address={renderData.assetContractAddress}
copyIconPosition="right"
/>
</GridItem>
<GridItem colSpan={3}>
<Heading size="label.md">Token ID</Heading>
</GridItem>
<GridItem colSpan={9}>
<CopyTextButton
textToCopy={tokenId}
textToShow={tokenId}
copyIconPosition="right"
tooltip="Token ID"
/>
</GridItem>
<GridItem colSpan={3}>
<Heading size="label.md">Seller</Heading>
</GridItem>
<GridItem colSpan={9}>
<WalletAddress address={renderData.creatorAddress} />
</GridItem>
<GridItem colSpan={3}>
<Heading size="label.md">Listing ID</Heading>
</GridItem>
<GridItem colSpan={9}>
<CopyTextButton
textToCopy={renderData.id.toString()}
textToShow={renderData.id.toString()}
copyIconPosition="right"
tooltip="Listing ID"
/>
</GridItem>
<GridItem colSpan={3}>
<Heading size="label.md">Type</Heading>
</GridItem>
<GridItem colSpan={9}>
<Text fontFamily="mono" size="body.md">
{renderData.asset.type}
</Text>
</GridItem>
<GridItem colSpan={3}>
<Heading size="label.md">Status</Heading>
</GridItem>
<GridItem colSpan={9}>
<Badge
size="label.sm"
variant="subtle"
textTransform="capitalize"
>
{LISTING_STATUS[renderData.status]}
</Badge>
</GridItem>
<GridItem colSpan={3}>
<Heading size="label.md">Quantity</Heading>
</GridItem>
<GridItem colSpan={9}>
<Text fontFamily="mono" size="body.md">
{(renderData.quantity || 0n).toString()}{" "}
{/* For listings that are completed, the `quantity` would be `0`
So we show this text to make it clear */}
{LISTING_STATUS[renderData.status] === "Completed"
? "(Sold out)"
: ""}
</Text>
</GridItem>
{renderData.type === "direct-listing" && (
<>
<GridItem colSpan={3}>
<Heading size="label.md">Price</Heading>
</GridItem>
<GridItem colSpan={9}>
<Text fontFamily="mono" size="body.md">
{renderData.currencyValuePerToken.displayValue}{" "}
{renderData.currencyValuePerToken.symbol}
</Text>
</GridItem>
</>
)}
{/*
Todo: Add a Buy button somewhere in this section once the Dashboard is fully migrated to v5 (?)
Kien already shipped a prebuilt component for the Marketplace Buy Button in SDK v5
*/}
</SimpleGrid>
</Card>
{data?.asset.metadata.properties ? (
<Card as={Flex} flexDir="column" gap={4}>
<Heading size="label.md">Attributes</Heading>
<CodeBlock
code={
JSON.stringify(data.asset.metadata.properties, null, 2) || ""
}
language="json"
canCopy={false}
wrap={false}
overflow="auto"
/>
</Card>
) : null}
</Flex>
{isOwner && (
<CancelTab
contract={contract}
id={renderData.id.toString()}
isAuction={renderData.type === "english-auction"}
/>
)}
</SheetContent>
</Sheet>
);
};
@@ -17,7 +17,6 @@ import {
} from "@chakra-ui/react";
import type { UseQueryResult } from "@tanstack/react-query";
import { MediaCell } from "components/contract-pages/table/table-columns/cells/media-cell";
import { ListingDrawer } from "contract-ui/tabs/shared-components/listing-drawer";
import {
ChevronFirstIcon,
ChevronLastIcon,
@@ -40,6 +39,7 @@ import type {
} from "thirdweb/extensions/marketplace";
import { min } from "thirdweb/utils";
import { Button, Text } from "tw-components";
import { ListingDrawer } from "./listing-drawer";
import { LISTING_STATUS } from "./types";
const tableColumns: Column<DirectListing | EnglishAuction>[] = [
@@ -100,7 +100,6 @@ interface MarketplaceTableProps {
start: number;
}>
>;
type: "direct-listings" | "english-auctions";
}
const DEFAULT_QUERY_STATE = { count: 50, start: 0 };
@@ -112,7 +111,6 @@ export const MarketplaceTable: React.FC<MarketplaceTableProps> = ({
totalCountQuery,
queryParams,
setQueryParams,
type,
}) => {
const [listingsToShow, setListingsToShow_] = useState<"all" | "valid">("all");
@@ -215,7 +213,6 @@ export const MarketplaceTable: React.FC<MarketplaceTableProps> = ({
data={tokenRow}
isOpen={!!tokenRow}
onClose={() => setTokenRow(null)}
type={type}
/>
<Table {...getTableProps()}>
<Thead>
@@ -0,0 +1,27 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../../_components/page-skeletons";
import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client";
import { useContractPageMetadata } from "../../_hooks/useContractPageMetadata";
import { ContractDirectListingsPage } from "./ContractDirectListingsPage";
export function ContractDirectListingsPageClient(props: {
contract: ThirdwebContract;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
if (!metadataQuery.data.isDirectListingSupported) {
return <RedirectToContractOverview contract={props.contract} />;
}
return <ContractDirectListingsPage contract={props.contract} />;
}
@@ -2,7 +2,7 @@
import type { ThirdwebContract } from "thirdweb";
import { Heading } from "tw-components";
import { CreateListingButton } from "../shared-components/list-button";
import { CreateListingButton } from "../components/list-button";
import { DirectListingsTable } from "./components/table";
interface ContractDirectListingsPageProps {
@@ -1,6 +1,5 @@
"use client";
import { MarketplaceTable } from "contract-ui/tabs/shared-components/marketplace-table";
import { useState } from "react";
import type { ThirdwebContract } from "thirdweb";
import {
@@ -9,6 +8,7 @@ import {
totalListings,
} from "thirdweb/extensions/marketplace";
import { useReadContract } from "thirdweb/react";
import { MarketplaceTable } from "../../components/marketplace-table";
interface DirectListingsTableProps {
contract: ThirdwebContract;
@@ -40,7 +40,6 @@ export const DirectListingsTable: React.FC<DirectListingsTableProps> = ({
totalCountQuery={totalCountQuery}
queryParams={queryParams}
setQueryParams={setQueryParams}
type="direct-listings"
/>
);
};
@@ -1,7 +1,9 @@
import { notFound, redirect } from "next/navigation";
import { ContractDirectListingsPage } from "../../../../../../contract-ui/tabs/direct-listings/page";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
import { localhost } from "thirdweb/chains";
import { getContractPageParamsInfo } from "../../_utils/getContractFromParams";
import { getContractPageMetadata } from "../../_utils/getContractPageMetadata";
import { ContractDirectListingsPage } from "./ContractDirectListingsPage";
import { ContractDirectListingsPageClient } from "./ContractDirectListingsPage.client";
export default async function Page(props: {
params: {
@@ -15,6 +17,10 @@ export default async function Page(props: {
notFound();
}
if (info.chainMetadata.chainId === localhost.id) {
return <ContractDirectListingsPageClient contract={info.contract} />;
}
const { isDirectListingSupported } = await getContractPageMetadata(
info.contract,
);
@@ -0,0 +1,27 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../../_components/page-skeletons";
import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client";
import { useContractPageMetadata } from "../../_hooks/useContractPageMetadata";
import { ContractEnglishAuctionsPage } from "./ContractEnglishAuctionsPage";
export function ContractEnglishAuctionsPageClient(props: {
contract: ThirdwebContract;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
if (!metadataQuery.data.isEnglishAuctionSupported) {
return <RedirectToContractOverview contract={props.contract} />;
}
return <ContractEnglishAuctionsPage contract={props.contract} />;
}
@@ -2,7 +2,7 @@
import type { ThirdwebContract } from "thirdweb";
import { Heading } from "tw-components";
import { CreateListingButton } from "../shared-components/list-button";
import { CreateListingButton } from "../components/list-button";
import { EnglishAuctionsTable } from "./components/table";
interface ContractEnglishAuctionsProps {
@@ -1,6 +1,5 @@
"use client";
import { MarketplaceTable } from "contract-ui/tabs/shared-components/marketplace-table";
import { useState } from "react";
import type { ThirdwebContract } from "thirdweb";
import {
@@ -9,6 +8,7 @@ import {
totalAuctions,
} from "thirdweb/extensions/marketplace";
import { useReadContract } from "thirdweb/react";
import { MarketplaceTable } from "../../components/marketplace-table";
interface EnglishAuctionsTableProps {
contract: ThirdwebContract;
@@ -40,7 +40,6 @@ export const EnglishAuctionsTable: React.FC<EnglishAuctionsTableProps> = ({
totalCountQuery={totalCountQuery}
queryParams={queryParams}
setQueryParams={setQueryParams}
type="english-auctions"
/>
);
};
@@ -1,7 +1,9 @@
import { notFound, redirect } from "next/navigation";
import { ContractEnglishAuctionsPage } from "../../../../../../contract-ui/tabs/english-auctions/page";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
import { localhost } from "thirdweb/chains";
import { getContractPageParamsInfo } from "../../_utils/getContractFromParams";
import { getContractPageMetadata } from "../../_utils/getContractPageMetadata";
import { ContractEnglishAuctionsPage } from "./ContractEnglishAuctionsPage";
import { ContractEnglishAuctionsPageClient } from "./ContractEnglishAuctionsPage.client";
export default async function Page(props: {
params: {
@@ -15,6 +17,10 @@ export default async function Page(props: {
notFound();
}
if (info.chainMetadata.chainId === localhost.id) {
return <ContractEnglishAuctionsPageClient contract={info.contract} />;
}
const { isEnglishAuctionSupported } = await getContractPageMetadata(
info.contract,
);
@@ -1,5 +1,6 @@
"use client";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only";
import { useIsAdmin } from "@3rdweb-sdk/react/hooks/useContractRoles";
@@ -13,7 +14,6 @@ import {
Menu,
MenuButton,
MenuList,
Spinner,
} from "@chakra-ui/react";
import { TransactionButton } from "components/buttons/TransactionButton";
import { useTrack } from "hooks/analytics/useTrack";
@@ -445,19 +445,16 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
};
}, [claimConditionsQuery.data, controlledFields, isMultiPhase]);
if (isFetchingData) {
return (
<div className="flex h-[400px] w-full items-center justify-center rounded-lg border border-border">
<Spinner className="size-10" />
</div>
);
}
return (
<>
{/* spinner */}
{isFetchingData && (
<Spinner
color="primary"
size="xs"
position="absolute"
top={2}
right={4}
/>
)}
<Flex onSubmit={handleFormSubmit} direction="column" as="form" gap={10}>
<Flex direction="column" gap={6}>
{/* Show the reason why the form is disabled */}
@@ -553,7 +550,7 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
<Flex
justifyContent="space-between"
flexDir={{ base: "column", md: isColumn ? "column" : "row" }}
flexDir={{ base: "column", md: "row" }}
gap={2}
>
<div className="flex flex-row gap-2">
@@ -19,12 +19,7 @@ export const ClaimConditions: React.FC<ClaimConditionsProps> = ({
return (
<div className="flex w-full flex-col gap-8">
<Flex p={0} position="relative">
<Flex
pt={{ base: isColumn ? 0 : 6, md: 6 }}
direction="column"
gap={8}
w="full"
>
<Flex direction="column" gap={6} w="full">
{/* Info */}
<section>
<h2 className="mb-1 font-semibold text-xl tracking-tight">
@@ -0,0 +1,17 @@
import { Spinner } from "@/components/ui/Spinner/Spinner";
export function LoadingPage() {
return (
<div className="flex min-h-[300px] grow items-center justify-center">
<Spinner className="size-10" />
</div>
);
}
export function ErrorPage() {
return (
<div className="flex min-h-[300px] grow items-center justify-center">
<p className="text-destructive-text">Failed to load contract</p>
</div>
);
}
@@ -0,0 +1,24 @@
"use client";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { useEffect, useRef } from "react";
import type { ThirdwebContract } from "thirdweb";
import { LoadingPage } from "./page-skeletons";
export function RedirectToContractOverview(props: {
contract: ThirdwebContract;
}) {
const router = useDashboardRouter();
const redirected = useRef(false);
// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
if (redirected.current) {
return;
}
redirected.current = true;
router.replace(`/${props.contract.chain.id}/${props.contract.address}`);
}, [router, props.contract]);
return <LoadingPage />;
}
@@ -0,0 +1,18 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import type { ThirdwebContract } from "thirdweb";
import { getContractPageMetadataSetup } from "../_utils/getContractPageMetadata";
export function useContractPageMetadata(contract: ThirdwebContract) {
return useQuery({
queryKey: ["getContractPageMetadataSetup", contract],
queryFn: () => {
return getContractPageMetadataSetup(contract, () =>
Promise.resolve(false),
);
},
retry: false,
refetchOnWindowFocus: false,
});
}
@@ -0,0 +1,13 @@
import { useQuery } from "@tanstack/react-query";
import { type ThirdwebContract, resolveContractAbi } from "thirdweb/contract";
export function useResolveContractABI(contract: ThirdwebContract) {
return useQuery({
queryKey: ["resolveContractAbi", contract],
queryFn: () => {
return resolveContractAbi(contract);
},
retry: false,
refetchOnWindowFocus: false,
});
}
@@ -4,8 +4,8 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { CheckIcon, CircleAlertIcon, RotateCcwIcon } from "lucide-react";
import { useState } from "react";
import { ConfigureNetworks } from "../../../../../components/configure-networks/ConfigureNetworks";
import { addChainOverrides } from "../../../../../stores/chainStores";
import { addChainOverrides } from "stores/chainStores";
import { ConfigureNetworks } from "../../../../../../components/configure-networks/ConfigureNetworks";
export function ConfigureCustomChain(props: {
chainSlug: string;
@@ -1,5 +1,8 @@
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { fetchDashboardContractMetadata } from "@3rdweb-sdk/react/hooks/useDashboardContractMetadata";
import {
type DashboardContractMetadata,
fetchDashboardContractMetadata,
} from "@3rdweb-sdk/react/hooks/useDashboardContractMetadata";
import { fetchPublishedContractsFromDeploy } from "components/contract-components/fetchPublishedContractsFromDeploy";
import type { ThirdwebContract } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
@@ -8,12 +11,34 @@ import { MetadataHeader } from "./metadata-header";
interface ContractMetadataProps {
contract: ThirdwebContract;
chain: ChainMetadata;
contractMetadata: DashboardContractMetadata | undefined;
externalLinks:
| {
name: string;
url: string;
}[]
| undefined;
}
export async function ContractMetadata({
export function ContractMetadata({
contract,
chain,
contractMetadata,
externalLinks,
}: ContractMetadataProps) {
return (
<MetadataHeader
data={contractMetadata}
chain={chain}
address={contract.address}
externalLinks={externalLinks}
/>
);
}
export async function getContractMetadataHeaderData(
contract: ThirdwebContract,
) {
const settledResults = await Promise.allSettled([
fetchDashboardContractMetadata(contract),
fetchPublishedContractsFromDeploy({
@@ -34,12 +59,8 @@ export async function ContractMetadata({
const latestPublished = publishedContractsFromDeploy?.slice(-1)[0];
return (
<MetadataHeader
data={contractMetadata}
chain={chain}
address={contract.address}
externalLinks={latestPublished?.externalLinks}
/>
);
return {
contractMetadata,
externalLinks: latestPublished?.externalLinks,
};
}
@@ -0,0 +1,49 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import type { ThirdwebContract } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
import { getContractPageSidebarLinks } from "../_utils/getContractPageSidebarLinks";
import { getContractMetadataHeaderData } from "./contract-metadata";
import { ContractPageLayout } from "./contract-page-layout";
export function ContractPageLayoutClient(props: {
chainMetadata: ChainMetadata;
contract: ThirdwebContract;
children: React.ReactNode;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
const headerMetadataQuery = useQuery({
queryKey: ["getContractMetadataHeaderData", props.contract],
queryFn: async () => {
return await getContractMetadataHeaderData(props.contract);
},
retry: false,
refetchOnWindowFocus: false,
});
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
const sidebarLinks = getContractPageSidebarLinks({
chainSlug: props.chainMetadata.slug,
contractAddress: props.contract.address,
metadata: metadataQuery.data,
});
return (
<ContractPageLayout
{...props}
sidebarLinks={sidebarLinks}
dashboardContractMetadata={headerMetadataQuery.data?.contractMetadata}
externalLinks={headerMetadataQuery.data?.externalLinks}
/>
);
}
@@ -0,0 +1,62 @@
import { ChakraProviderSetup } from "@/components/ChakraProviderSetup";
import type { SidebarLink } from "@/components/blocks/Sidebar";
import { SidebarLayout } from "@/components/blocks/SidebarLayout";
import type { DashboardContractMetadata } from "@3rdweb-sdk/react/hooks/useDashboardContractMetadata";
import { DeprecatedAlert } from "components/shared/DeprecatedAlert";
import type { ThirdwebContract } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { ContractMetadata } from "./contract-metadata";
import { PrimaryDashboardButton } from "./primary-dashboard-button";
export function ContractPageLayout(props: {
chainMetadata: ChainMetadata;
contract: ThirdwebContract;
children: React.ReactNode;
sidebarLinks: SidebarLink[];
dashboardContractMetadata: DashboardContractMetadata | undefined;
externalLinks:
| {
name: string;
url: string;
}[]
| undefined;
}) {
const {
chainMetadata,
contract,
sidebarLinks,
dashboardContractMetadata,
externalLinks,
} = props;
return (
<ChakraProviderSetup>
<SidebarLayout sidebarLinks={sidebarLinks}>
<div className="border-border border-b pb-8">
<div className="flex flex-col gap-4 ">
<div className="flex flex-col justify-between gap-4 md:flex-row">
<ContractMetadata
contract={contract}
chain={chainMetadata}
contractMetadata={dashboardContractMetadata}
externalLinks={externalLinks}
/>
<PrimaryDashboardButton
contractAddress={contract.address}
chain={contract.chain}
contractInfo={{
chain: chainMetadata,
chainSlug: chainMetadata.slug,
contractAddress: contract.address,
}}
/>
</div>
<DeprecatedAlert chain={chainMetadata} />
</div>
</div>
<div className="h-8" />
<div className="pb-10">{props.children}</div>
</SidebarLayout>
</ChakraProviderSetup>
);
}
@@ -117,7 +117,7 @@ export const MetadataHeader: React.FC<MetadataHeaderProps> = ({
<BadgeLink
key={validBlockExplorer.url}
name={validBlockExplorer.name}
href={`${validBlockExplorer.url}/address/${address}`}
href={`${validBlockExplorer.url.endsWith("/") ? validBlockExplorer.url : `${validBlockExplorer.url}/`}address/${address}`}
/>
))}
@@ -20,27 +20,25 @@ export async function getContractPageParamsInfo(params: {
// move on
}
if (!chainMetadata) {
const cookieStore = cookies();
const localChainStoreValue = cookieStore.get(TW_LOCAL_CHAIN_STORE)?.value;
const cookieStore = cookies();
const localChainStoreValue = cookieStore.get(TW_LOCAL_CHAIN_STORE)?.value;
if (localChainStoreValue) {
try {
const chains = JSON.parse(decodeURIComponent(localChainStoreValue));
if (typeof chains === "object" && Array.isArray(chains)) {
const chainOverrides = chains as ChainMetadata[];
const chain = chainOverrides.find(
(c) =>
c.slug === chainSlugOrId ||
c.chainId === Number.parseInt(chainSlugOrId),
);
if (chain) {
chainMetadata = chain;
}
if (localChainStoreValue) {
try {
const chains = JSON.parse(decodeURIComponent(localChainStoreValue));
if (typeof chains === "object" && Array.isArray(chains)) {
const chainOverrides = chains as ChainMetadata[];
const chain = chainOverrides.find(
(c) =>
c.slug === chainSlugOrId ||
c.chainId === Number.parseInt(chainSlugOrId),
);
if (chain) {
chainMetadata = chain;
}
} catch {
// noop
}
} catch {
// noop
}
}
@@ -46,8 +46,13 @@ export type ContractPageMetadata = {
functionSelectors: string[];
};
export async function getContractPageMetadata(
export async function getContractPageMetadata(contract: ThirdwebContract) {
return getContractPageMetadataSetup(contract, isAnalyticsSupportedForChain);
}
export async function getContractPageMetadataSetup(
contract: ThirdwebContract,
isAnalyticsSupportedFn: (chainId: number) => Promise<boolean>,
): Promise<ContractPageMetadata> {
const [
functionSelectorsResult,
@@ -55,7 +60,7 @@ export async function getContractPageMetadata(
contractTypeResult,
] = await Promise.allSettled([
resolveFunctionSelectors(contract),
isAnalyticsSupportedForChain(contract.chain.id),
isAnalyticsSupportedFn(contract.chain.id),
getContractType({ contract }),
]);
@@ -0,0 +1,27 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
import { AccountSigners } from "./components/account-signers";
export function AccountSignersClient(props: {
contract: ThirdwebContract;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
if (!metadataQuery.data.isAccountPermissionsSupported) {
return <RedirectToContractOverview contract={props.contract} />;
}
return <AccountSigners contract={props.contract} />;
}
@@ -31,14 +31,19 @@ export const AccountSigners: React.FC<AccountSignersProps> = ({ contract }) => {
);
const data = transformedAdmins.concat(transformedSigners || []);
return (
<div className="flex flex-col gap-6">
{data.map((item) => (
<AccountSigner
key={item.signer}
item={item}
contractChainId={contract.chain.id}
/>
))}
<div>
<h1 className="mb-4 font-semibold text-2xl tracking-tight">
Account Signers
</h1>
<div className="flex flex-col gap-6">
{data.map((item) => (
<AccountSigner
key={item.signer}
item={item}
contractChainId={contract.chain.id}
/>
))}
</div>
</div>
);
};
@@ -1,6 +1,8 @@
import { notFound, redirect } from "next/navigation";
import { localhost } from "thirdweb/chains";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
import { AccountSignersClient } from "./AccountSigners.client";
import { AccountSigners } from "./components/account-signers";
export default async function Page(props: {
@@ -15,7 +17,12 @@ export default async function Page(props: {
notFound();
}
const { contract } = info;
const { contract, chainMetadata } = info;
if (chainMetadata.chainId === localhost.id) {
return <AccountSignersClient contract={contract} />;
}
const { isAccountPermissionsSupported } =
await getContractPageMetadata(contract);
@@ -23,12 +30,5 @@ export default async function Page(props: {
redirect(`/${props.params.chain_id}/${props.params.contractAddress}`);
}
return (
<div>
<h1 className="mb-4 font-semibold text-2xl tracking-tight">
Account Signers
</h1>
<AccountSigners contract={contract} />
</div>
);
return <AccountSigners contract={contract} />;
}
@@ -0,0 +1,34 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
import { AccountPage } from "./AccountPage";
export function AccountPageClient(props: {
contract: ThirdwebContract;
chainMetadata: ChainMetadata;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
if (!metadataQuery.data.isAccount) {
return <RedirectToContractOverview contract={props.contract} />;
}
return (
<AccountPage
contract={props.contract}
chainMetadata={props.chainMetadata}
/>
);
}
@@ -1,7 +1,9 @@
import { notFound, redirect } from "next/navigation";
import { localhost } from "thirdweb/chains";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
import { AccountPage } from "./AccountPage";
import { AccountPageClient } from "./AccountPage.client";
export default async function Page(props: {
params: {
@@ -16,6 +18,12 @@ export default async function Page(props: {
}
const { contract, chainMetadata } = info;
if (contract.chain.id === localhost.id) {
return (
<AccountPageClient contract={contract} chainMetadata={chainMetadata} />
);
}
const { isAccount } = await getContractPageMetadata(contract);
if (!isAccount) {
@@ -0,0 +1,27 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
import { AccountsPage } from "./AccountsPage";
export function AccountsPageClient(props: {
contract: ThirdwebContract;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
if (!metadataQuery.data.isAccountFactory) {
return <RedirectToContractOverview contract={props.contract} />;
}
return <AccountsPage contract={props.contract} />;
}
@@ -1,7 +1,9 @@
import { notFound, redirect } from "next/navigation";
import { localhost } from "thirdweb/chains";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
import { AccountsPage } from "./AccountsPage";
import { AccountsPageClient } from "./AccountsPage.client";
export default async function Page(props: {
params: {
@@ -15,7 +17,12 @@ export default async function Page(props: {
notFound();
}
const { contract } = info;
const { contract, chainMetadata } = info;
if (chainMetadata.chainId === localhost.id) {
return <AccountsPageClient contract={contract} />;
}
const { isAccountFactory } = await getContractPageMetadata(contract);
if (!isAccountFactory) {
@@ -0,0 +1,27 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
import { ContractAnalyticsPage } from "./ContractAnalyticsPage";
export function ContractAnalyticsPageClient(props: {
contract: ThirdwebContract;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
if (!metadataQuery.data.isAnalyticsSupported) {
return <RedirectToContractOverview contract={props.contract} />;
}
return <ContractAnalyticsPage contract={props.contract} />;
}
@@ -1,7 +1,9 @@
import { notFound, redirect } from "next/navigation";
import { localhost } from "thirdweb/chains";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
import { ContractAnalyticsPage } from "./ContractAnalyticsPage";
import { ContractAnalyticsPageClient } from "./ContractAnalyticsPage.client";
export default async function Page(props: {
params: {
@@ -15,6 +17,10 @@ export default async function Page(props: {
notFound();
}
if (info.chainMetadata.chainId === localhost.id) {
return <ContractAnalyticsPageClient contract={info.contract} />;
}
const { isAnalyticsSupported } = await getContractPageMetadata(info.contract);
if (!isAnalyticsSupported) {
@@ -0,0 +1,38 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import { ClaimConditions } from "../_components/claim-conditions/claim-conditions";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
export function ClaimConditionsClient(props: {
contract: ThirdwebContract;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
const {
isERC20ClaimConditionsSupported,
isERC721ClaimConditionsSupported,
supportedERCs,
} = metadataQuery.data;
if (!isERC20ClaimConditionsSupported && !isERC721ClaimConditionsSupported) {
return <RedirectToContractOverview contract={props.contract} />;
}
return (
<ClaimConditions
contract={props.contract}
isERC20={supportedERCs.isERC20}
/>
);
}
@@ -1,7 +1,9 @@
import { notFound, redirect } from "next/navigation";
import { localhost } from "thirdweb/chains";
import { ClaimConditions } from "../_components/claim-conditions/claim-conditions";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
import { ClaimConditionsClient } from "./ClaimConditions.client";
export default async function Page(props: {
params: {
@@ -14,8 +16,12 @@ export default async function Page(props: {
if (!info) {
notFound();
}
const { contract, chainMetadata } = info;
if (chainMetadata.chainId === localhost.id) {
return <ClaimConditionsClient contract={contract} />;
}
const { contract } = info;
const {
isERC20ClaimConditionsSupported,
isERC721ClaimConditionsSupported,
@@ -0,0 +1,26 @@
"use client";
import type { ChainMetadata } from "thirdweb/chains";
import type { ThirdwebContract } from "thirdweb/contract";
import { LoadingPage } from "../_components/page-skeletons";
import { useResolveContractABI } from "../_hooks/useResolveContractABI";
import { ContractCodePage } from "./contract-code-page";
export function ContractCodePageClient(props: {
contract: ThirdwebContract;
chainMetadata: ChainMetadata;
}) {
const abiQuery = useResolveContractABI(props.contract);
if (abiQuery.isPending) {
return <LoadingPage />;
}
return (
<ContractCodePage
abi={abiQuery.data}
contract={props.contract}
chainMetadata={props.chainMetadata}
/>
);
}
@@ -0,0 +1,33 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import type { Abi } from "abitype";
import { CircleAlertIcon } from "lucide-react";
import type { ChainMetadata } from "thirdweb/chains";
import type { ThirdwebContract } from "thirdweb/contract";
import { CodeOverview } from "../../../../../../contract-ui/tabs/code/components/code-overview";
export function ContractCodePage(props: {
abi: Abi | undefined;
contract: ThirdwebContract;
chainMetadata: ChainMetadata;
}) {
if (props.abi) {
return (
<CodeOverview
abi={props.abi}
contractAddress={props.contract.address}
chainId={props.contract.chain.id}
/>
);
}
return (
<Alert variant="destructive">
<CircleAlertIcon className="size-5" />
<AlertTitle>Failed to resolve contract ABI</AlertTitle>
<AlertDescription>
Please verify that contract address is correct and deployed on "
{props.chainMetadata.name}" chain.
</AlertDescription>
</Alert>
);
}
@@ -1,9 +1,9 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { CircleAlertIcon } from "lucide-react";
import { notFound } from "next/navigation";
import { localhost } from "thirdweb/chains";
import { resolveContractAbi } from "thirdweb/contract";
import { CodeOverview } from "../../../../../../contract-ui/tabs/code/components/code-overview";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { ContractCodePage } from "./contract-code-page";
import { ContractCodePageClient } from "./contract-code-page.client";
export default async function Page(props: {
params: {
@@ -19,26 +19,22 @@ export default async function Page(props: {
const { contract, chainMetadata } = info;
try {
const abi = await resolveContractAbi(contract);
if (contract.chain.id === localhost.id) {
return (
<CodeOverview
abi={abi}
contractAddress={contract.address}
chainId={contract.chain.id}
<ContractCodePageClient
contract={contract}
chainMetadata={chainMetadata}
/>
);
} catch {
return (
<Alert variant="destructive">
<CircleAlertIcon className="size-5" />
<AlertTitle>Failed to resolve contract ABI</AlertTitle>
<AlertDescription>
Please verify that contract address is correct and deployed on "
{chainMetadata.name}" chain.
</AlertDescription>
</Alert>
);
}
const abi = await resolveContractAbi(contract).then(() => undefined);
return (
<ContractCodePage
abi={abi}
contract={contract}
chainMetadata={chainMetadata}
/>
);
}
@@ -0,0 +1,32 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
import { EmbedSetup } from "./embed-setup";
export function EmbedSetupClient(props: {
contract: ThirdwebContract;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
if (metadataQuery.data.embedType === null) {
return <RedirectToContractOverview contract={props.contract} />;
}
return (
<EmbedSetup
contract={props.contract}
ercOrMarketplace={metadataQuery.data.embedType}
/>
);
}
@@ -1,17 +1,22 @@
"use client";
import { Button } from "@/components/ui/button";
import { useApiKeys, useCreateApiKey } from "@3rdweb-sdk/react/hooks/useApi";
import { Flex, FormControl, Input, Link, Select } from "@chakra-ui/react";
import { Flex, FormControl, Input, Select } from "@chakra-ui/react";
import { LazyCreateAPIKeyDialog } from "components/settings/ApiKeys/Create/LazyCreateAPIKeyDialog";
import { useTrack } from "hooks/analytics/useTrack";
import { useAllChainsData } from "hooks/chains/allChains";
import { useClipboard } from "hooks/useClipboard";
import { useTxNotifications } from "hooks/useTxNotifications";
import { CheckIcon, CopyIcon } from "lucide-react";
import { useMemo } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useMemo, useState } from "react";
import { useForm } from "react-hook-form";
import type { StoredChain } from "stores/chainStores";
import type { ThirdwebContract } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { useActiveAccount } from "thirdweb/react";
import {
Button,
Card,
CodeBlock,
FormHelperText,
@@ -19,8 +24,6 @@ import {
Heading,
Text,
} from "tw-components";
import { useAllChainsData } from "../../../../hooks/chains/allChains";
import type { StoredChain } from "../../../../stores/chainStores";
interface EmbedSetupProps {
contract: ThirdwebContract;
@@ -171,7 +174,7 @@ function minimizeChain(
return {
name: chain.name,
chain: chain.chain,
rpc: [firstRpc],
rpc: [firstRpc || ""],
nativeCurrency: chain.nativeCurrency,
shortName: chain.shortName,
chainId: chain.chainId,
@@ -211,10 +214,6 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
const apiKeys = useApiKeys();
const createKeyMutation = useCreateApiKey();
const { onSuccess, onError } = useTxNotifications(
"API key created",
"Failed to create API key",
);
const validApiKey = (apiKeys.data || []).find(
(apiKey) =>
@@ -298,14 +297,35 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
);
const { hasCopied, onCopy } = useClipboard(embedCode, 3000);
const [showCreateAPIKeyModal, setShowCreateAPIKeyModal] = useState(false);
const activeAccount = useActiveAccount();
const pathname = usePathname();
return (
<Flex gap={8} direction="column">
<LazyCreateAPIKeyDialog
wording="api-key"
prefill={{
name: "Embed API Key",
domains: "embed.ipfscdn.io",
}}
open={showCreateAPIKeyModal}
onOpenChange={setShowCreateAPIKeyModal}
onCreateAndComplete={() => {
trackEvent({
category: "api-keys",
action: "create",
label: "success",
fromEmbed: true,
});
apiKeys.refetch();
}}
/>
<Flex gap={8} direction={{ base: "column", md: "row" }}>
<Card className="flex w-full flex-col gap-2 md:w-1/2">
<Heading size="title.sm" mb={4}>
Configuration
</Heading>
<Card className="flex w-full flex-col gap-5 md:w-1/2">
<Heading size="title.sm">Configuration</Heading>
{ercOrMarketplace === "marketplace" ? (
<FormControl>
<FormLabel>Listing ID</FormLabel>
@@ -315,6 +335,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
</FormHelperText>
</FormControl>
) : null}
{ercOrMarketplace === "marketplace-v3" ? (
<FormControl>
<FormLabel>Listing type</FormLabel>
@@ -327,6 +348,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
</FormHelperText>
</FormControl>
) : null}
{ercOrMarketplace === "marketplace-v3" &&
watch("listingType") === "direct-listing" ? (
<FormControl>
@@ -337,6 +359,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
</FormHelperText>
</FormControl>
) : null}
{ercOrMarketplace === "marketplace-v3" &&
watch("listingType") === "english-auction" ? (
<FormControl>
@@ -347,6 +370,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
</FormHelperText>
</FormControl>
) : null}
{ercOrMarketplace === "erc1155" ? (
<FormControl>
<FormLabel>Token ID</FormLabel>
@@ -356,9 +380,20 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
</FormHelperText>
</FormControl>
) : null}
<FormControl>
<FormLabel>Client ID</FormLabel>
{validApiKey ? (
{!activeAccount ? (
<Button asChild className="w-full">
<Link
href={`/login${
pathname ? `?next=${encodeURIComponent(pathname)}` : ""
}`}
>
Sign in to create a client ID
</Link>
</Button>
) : validApiKey ? (
<Input
readOnly
disabled
@@ -366,11 +401,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
/>
) : (
<Button
bgColor="bgBlack"
color="bgWhite"
_hover={{
opacity: 0.8,
}}
className="w-full"
onClick={() => {
trackEvent({
category: "api-keys",
@@ -379,44 +410,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
fromEmbed: true,
});
createKeyMutation.mutate(
{
name: "Embed API key",
domains: ["embed.ipfscdn.io"],
services: [
{
name: "rpc",
targetAddresses: ["*"],
},
{
name: "storage",
targetAddresses: ["*"],
actions: ["read"],
},
],
},
{
onSuccess: () => {
onSuccess();
trackEvent({
category: "api-keys",
action: "create",
label: "success",
fromEmbed: true,
});
},
onError: (err) => {
onError(err);
trackEvent({
category: "api-keys",
action: "create",
label: "error",
error: err,
fromEmbed: true,
});
},
},
);
setShowCreateAPIKeyModal(true);
}}
disabled={createKeyMutation.isPending}
>
@@ -428,14 +422,15 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
You need a client ID to use embeds.{" "}
<Link
href="https://portal.thirdweb.com/account/api-keys"
color="primary.500"
isExternal
className="text-link-foreground hover:text-foreground"
target="_blank"
>
Learn more
</Link>
.
</FormHelperText>
</FormControl>
<FormControl>
<FormLabel>RPC Url</FormLabel>
<Input type="url" {...register("rpcUrl")} />
@@ -477,8 +472,8 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
A relayer can be used to make the transaction gasless for the
end user.{" "}
<Link
isExternal
color="blue.500"
target="_blank"
className="text-link-foreground hover:text-foreground"
href="https://blog.thirdweb.com/guides/setup-gasless-transactions"
>
Learn more
@@ -486,6 +481,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
</FormHelperText>
</FormControl>
)}
<FormControl>
<Heading size="title.sm" my={4}>
Customization
@@ -501,6 +497,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
on the user system&apos;s preferences.
</FormHelperText>
</FormControl>
<FormControl>
<FormLabel>Primary Color</FormLabel>
<Select {...register("primaryColor")}>
@@ -509,7 +506,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
)}
{colorOptions.map((color) => (
<option key={color} value={color}>
{color[0].toUpperCase() + color.substring(1)}
{color[0]?.toUpperCase() + color.substring(1)}
</option>
))}
</Select>
@@ -517,6 +514,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
Used for the main actions button backgrounds.
</FormHelperText>
</FormControl>
{ercOrMarketplace === "marketplace" ||
ercOrMarketplace === "marketplace-v3" ? (
<FormControl>
@@ -524,7 +522,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
<Select {...register("secondaryColor")}>
{colorOptions.map((color) => (
<option key={color} value={color}>
{color[0].toUpperCase() + color.substring(1)}
{color[0]?.toUpperCase() + color.substring(1)}
</option>
))}
</Select>
@@ -535,6 +533,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
</FormControl>
) : null}
</Card>
<Card className="flex w-full flex-col gap-2 md:w-1/2">
<Heading size="title.sm">Embed Code</Heading>
<CodeBlock
@@ -545,8 +544,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
language="markup"
/>
<Button
colorScheme="purple"
w="auto"
className="w-auto gap-2"
variant="outline"
onClick={() => {
onCopy();
@@ -558,14 +556,12 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
chainId,
});
}}
leftIcon={
hasCopied ? (
<CheckIcon className="size-4" />
) : (
<CopyIcon className="size-4" />
)
}
>
{hasCopied ? (
<CheckIcon className="size-4" />
) : (
<CopyIcon className="size-4" />
)}
{hasCopied ? "Copied!" : "Copy to clipboard"}
</Button>
</Card>
@@ -1,7 +1,9 @@
import { notFound, redirect } from "next/navigation";
import { EmbedSetup } from "../../../../../../contract-ui/tabs/embed/components/embed-setup";
import { localhost } from "thirdweb/chains";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
import { EmbedSetupClient } from "./EmbedSetup.client";
import { EmbedSetup } from "./embed-setup";
export default async function Page(props: {
params: {
@@ -15,11 +17,16 @@ export default async function Page(props: {
notFound();
}
const { embedType } = await getContractPageMetadata(info.contract);
const { contract } = info;
if (contract.chain.id === localhost.id) {
return <EmbedSetupClient contract={contract} />;
}
const { embedType } = await getContractPageMetadata(contract);
if (embedType === null) {
redirect(`/${props.params.chain_id}/${props.params.contractAddress}`);
}
return <EmbedSetup contract={info.contract} ercOrMarketplace={embedType} />;
return <EmbedSetup contract={contract} ercOrMarketplace={embedType} />;
}
@@ -24,6 +24,7 @@ import {
Tooltip,
} from "@chakra-ui/react";
import { AnimatePresence, motion } from "framer-motion";
import { useChainSlug } from "hooks/chains/chainSlug";
import { useClipboard } from "hooks/useClipboard";
import { ChevronDownIcon, CircleHelpIcon, CopyIcon } from "lucide-react";
import { useSearchParams } from "next/navigation";
@@ -39,7 +40,6 @@ import {
Heading,
Text,
} from "tw-components";
import { useChainSlug } from "../../../../hooks/chains/chainSlug";
interface EventsFeedProps {
contract: ThirdwebContract;
@@ -1,6 +1,6 @@
import { notFound } from "next/navigation";
import { EventsFeed } from "../../../../../../contract-ui/tabs/events/components/events-feed";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { EventsFeed } from "./events-feed";
export default async function Page(props: {
params: {
@@ -0,0 +1,31 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
import { LoadingPage } from "../_components/page-skeletons";
import { useResolveContractABI } from "../_hooks/useResolveContractABI";
import { ContractExplorerPage } from "./ContractExplorerPage";
interface ContractExplorePageProps {
contract: ThirdwebContract;
chainMetadata: ChainMetadata;
}
export const ContractExplorerPageClient: React.FC<ContractExplorePageProps> = ({
contract,
chainMetadata,
}) => {
const abiQuery = useResolveContractABI(contract);
if (abiQuery.isPending) {
return <LoadingPage />;
}
return (
<ContractExplorerPage
abi={abiQuery.data}
contract={contract}
chainMetadata={chainMetadata}
/>
);
};
@@ -1,21 +1,36 @@
"use client";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Flex } from "@chakra-ui/react";
import type { Abi } from "abitype";
import { getContractFunctionsFromAbi } from "components/contract-components/getContractFunctionsFromAbi";
import { ContractFunctionsOverview } from "components/contract-functions/contract-functions";
import { CircleAlertIcon } from "lucide-react";
import type { ThirdwebContract } from "thirdweb";
// TODO - figure out why we have to add "use client" here and it does not work without it
import type { ChainMetadata } from "thirdweb/chains";
interface ContractExplorePageProps {
contract: ThirdwebContract;
abi: Abi;
abi: Abi | undefined;
chainMetadata: ChainMetadata;
}
export const ContractExplorerPage: React.FC<ContractExplorePageProps> = ({
contract,
abi,
chainMetadata,
}) => {
if (!abi) {
return (
<Alert variant="destructive">
<CircleAlertIcon className="size-5" />
<AlertTitle>Failed to resolve contract ABI</AlertTitle>
<AlertDescription>
Please verify that contract address is correct and deployed on "
{chainMetadata.name}" chain.
</AlertDescription>
</Alert>
);
}
const functions = getContractFunctionsFromAbi(abi);
return (
<Flex direction="column" h="70vh">
@@ -1,9 +1,9 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { CircleAlertIcon } from "lucide-react";
import { notFound } from "next/navigation";
import { localhost } from "thirdweb/chains";
import { resolveContractAbi } from "thirdweb/contract";
import { ContractExplorerPage } from "../../../../../../contract-ui/tabs/explorer/page";
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
import { ContractExplorerPage } from "./ContractExplorerPage";
import { ContractExplorerPageClient } from "./ContractExplorerPage.client";
export default async function Page(props: {
params: {
@@ -19,19 +19,22 @@ export default async function Page(props: {
const { contract, chainMetadata } = info;
try {
const abi = await resolveContractAbi(contract);
return <ContractExplorerPage contract={contract} abi={abi} />;
} catch {
if (contract.chain.id === localhost.id) {
return (
<Alert variant="destructive">
<CircleAlertIcon className="size-5" />
<AlertTitle>Failed to resolve contract ABI</AlertTitle>
<AlertDescription>
Please verify that contract address is correct and deployed on "
{chainMetadata.name}" chain.
</AlertDescription>
</Alert>
<ContractExplorerPageClient
contract={contract}
chainMetadata={chainMetadata}
/>
);
}
const abi = await resolveContractAbi(contract).catch(() => undefined);
return (
<ContractExplorerPage
contract={contract}
abi={abi}
chainMetadata={chainMetadata}
/>
);
}
@@ -1,13 +1,14 @@
import { ChakraProviderSetup } from "@/components/ChakraProviderSetup";
import { SidebarLayout } from "@/components/blocks/SidebarLayout";
import { ContractMetadata } from "components/custom-contract/contract-header/contract-metadata";
import { DeprecatedAlert } from "components/shared/DeprecatedAlert";
import { PrimaryDashboardButton } from "contract-ui/components/primary-dashboard-button";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { localhost } from "thirdweb/chains";
import { getContractMetadata } from "thirdweb/extensions/common";
import { isAddress, isContractDeployed } from "thirdweb/utils";
import { resolveFunctionSelectors } from "../../../../../lib/selectors";
import { shortenIfAddress } from "../../../../../utils/usedapp-external";
import { ConfigureCustomChain } from "./ConfigureCustomChain";
import { ConfigureCustomChain } from "./_layout/ConfigureCustomChain";
import { getContractMetadataHeaderData } from "./_layout/contract-metadata";
import { ContractPageLayout } from "./_layout/contract-page-layout";
import { ContractPageLayoutClient } from "./_layout/contract-page-layout.client";
import { supportedERCs } from "./_utils/detectedFeatures/supportedERCs";
import { getContractPageParamsInfo } from "./_utils/getContractFromParams";
import { getContractPageMetadata } from "./_utils/getContractPageMetadata";
@@ -20,6 +21,10 @@ export default async function Layout(props: {
};
children: React.ReactNode;
}) {
if (!isAddress(props.params.contractAddress)) {
return notFound();
}
const info = await getContractPageParamsInfo(props.params);
if (!info) {
@@ -27,37 +32,49 @@ export default async function Layout(props: {
}
const { contract, chainMetadata } = info;
if (chainMetadata.status === "deprecated") {
notFound();
}
if (contract.chain.id === localhost.id) {
return (
<ContractPageLayoutClient
chainMetadata={chainMetadata}
contract={contract}
>
{props.children}
</ContractPageLayoutClient>
);
}
// check if the contract exists
const isValidContract = await isContractDeployed(contract).catch(() => false);
if (!isValidContract) {
// TODO - replace 404 with a better page to upsale deploy or other thirdweb products
notFound();
}
const contractPageMetadata = await getContractPageMetadata(contract);
const sidebarLinks = getContractPageSidebarLinks({
chainSlug: chainMetadata.slug,
contractAddress: contract.address,
metadata: contractPageMetadata,
});
const { contractMetadata, externalLinks } =
await getContractMetadataHeaderData(contract);
return (
<ChakraProviderSetup>
<SidebarLayout sidebarLinks={sidebarLinks}>
<div className="border-border border-b pb-8">
<div className="flex flex-col gap-4 ">
<div className="flex flex-col justify-between gap-4 md:flex-row">
<ContractMetadata contract={contract} chain={chainMetadata} />
<PrimaryDashboardButton
contractAddress={contract.address}
chain={contract.chain}
contractInfo={{
chain: chainMetadata,
chainSlug: chainMetadata.slug,
contractAddress: contract.address,
}}
/>
</div>
<DeprecatedAlert chain={chainMetadata} />
</div>
</div>
<div className="h-8" />
<div className="pb-10">{props.children}</div>
</SidebarLayout>
</ChakraProviderSetup>
<ContractPageLayout
chainMetadata={chainMetadata}
contract={contract}
sidebarLinks={sidebarLinks}
dashboardContractMetadata={contractMetadata}
externalLinks={externalLinks}
>
{props.children}
</ContractPageLayout>
);
}
@@ -0,0 +1,27 @@
"use client";
import type { ThirdwebContract } from "thirdweb";
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
import { ContractEditModulesPage } from "./ContractEditModulesPage";
export function ContractEditModulesPageClient(props: {
contract: ThirdwebContract;
}) {
const metadataQuery = useContractPageMetadata(props.contract);
if (metadataQuery.isPending) {
return <LoadingPage />;
}
if (metadataQuery.isError) {
return <ErrorPage />;
}
if (!metadataQuery.data.isModularCore) {
return <RedirectToContractOverview contract={props.contract} />;
}
return <ContractEditModulesPage contract={props.contract} />;
}
@@ -0,0 +1,59 @@
"use client";
import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
import { Alert, AlertTitle } from "@/components/ui/alert";
import { CircleSlash } from "lucide-react";
import type { ContractOptions } from "thirdweb";
import type { Account } from "thirdweb/wallets";
import { ModuleCard } from "./module-card";
export const InstalledModulesTable = (props: {
contract: ContractOptions;
installedModules: {
data?: string[];
isPending: boolean;
};
refetchModules: () => void;
ownerAccount: Account | undefined;
}) => {
const { installedModules, ownerAccount } = props;
const sectionTitle = (
<h2 className="mb-3 font-bold text-2xl tracking-tight">
Installed Modules
</h2>
);
if (!installedModules.isPending && installedModules.data?.length === 0) {
return (
<>
{sectionTitle}
<Alert variant="destructive">
<div className="flex items-center gap-3">
<CircleSlash className="size-6 text-red-400" />
<AlertTitle className="mb-0">No modules installed</AlertTitle>
</div>
</Alert>
</>
);
}
return (
<>
{sectionTitle}
<ScrollShadow scrollableClassName="rounded-lg">
<div className="flex flex-col gap-6">
{installedModules.data?.map((moduleAddress) => (
<ModuleCard
key={moduleAddress}
moduleAddress={moduleAddress}
contract={props.contract}
onRemoveModule={props.refetchModules}
ownerAccount={ownerAccount}
/>
))}
</div>
</ScrollShadow>
</>
);
};

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