Compare commits

...
Author SHA1 Message Date
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
f47aa61915 Version Packages (#4263)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-25 11:22:32 +12:00
gregfromstl a0aea23e74 Set default auth mode to prevent double popup (#4274)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR fixes a conflict between popup and window authentication modes on Firefox.

### Detailed summary
- Set the default authentication mode to "popup" if not specified
- Ensure popup authentication mode is used when not explicitly defined

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

<!-- end pr-codex -->
2024-08-24 21:08:40 +00:00
1405598e7e adding api for pay direct transfers (#4224)
Co-authored-by: Joaquim Verges <[email protected]>
Co-authored-by: Edward Sun <[email protected]>
Co-authored-by: Edward Sun <[email protected]>
2024-08-23 23:45:23 +00:00
MananTank 120d61f410 Use formatNumber util for showing token balances (#4269)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update UI components to display wallet balances with up to 5 decimal places.

### Detailed summary
- Updated `formatTokenBalance` function to use `formatNumber` with 5 decimal places
- Added `formatNumber` function to format numbers with specified decimal places
- Updated balance display in UI components to use `formatNumber` with conditional decimal places based on the value

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

<!-- end pr-codex -->
2024-08-23 22:12:08 +00:00
jnsdls 2973ba1f54 [Update] Make bundle ID regex case-insensitive (#4268)
BLOCKED BY: https://github.com/thirdweb-dev/api-server/pull/1019
2024-08-23 21:00:49 +00:00
Alec AnanianandGitHub 7a2de70310 react: add hideSwitchWallet param to wallet details modal (#4267) 2024-08-23 13:37:51 -07:00
gregfromstl 659cb03eea [Docs] Feature: Automatically unroll function inputs and outputs (#4236)
## Problem solved

Previously we made no attempt to unroll function inputs and outputs in the docs. This made things difficult to quickly scan since every function has a typed input and output object you would have to navigate to.

This PR is a first attempt at basic unrolling functionality of these typed inputs and outputs.

Before:
<img width="929" alt="Screenshot 2024-08-21 at 3 20 24 PM" src="https://github.com/user-attachments/assets/e73eae8b-98b5-40c9-8f63-f17e3a0a63d7">

After:
<img width="933" alt="Screenshot 2024-08-21 at 3 20 49 PM" src="https://github.com/user-attachments/assets/b5d8d847-9c70-45ec-aaf6-95b4215b3418">

As an additional readability bonus, the input and output sections now start as an open accordion.

Note: There are still improvements to be made especially on functions with more complex types. These functions' interfaces should be simplified in future versions.

## Next Steps
Future features should add nested input descriptions (supported by typedoc but not `typedoc-better-json`). Future SDK versions should use simpler, explicitly typed inputs and outputs to simplify the unrolling process (and easier LSP support for users).

## To Test
Open the preview URL, click around to various functions and inspect the parameter and return types

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates transaction handling and TypeScript documentation rendering.

### Detailed summary
- Added `fetchDocBySlug` function to fetch TypeScript docs by slug
- Modified `SendTransactionOptions` to use interface instead of type
- Updated TypeScript documentation rendering to include interface docs for return types and parameter types

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

<!-- end pr-codex -->
2024-08-23 18:24:48 +00:00
jnsdls 8128d127ce [Update] Refine billing alert messages and usage limit notifications (#4266) 2024-08-23 17:55:29 +00:00
MananTank d401a13cdc Add Storybook in Dashboard (#4265)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to integrate Storybook into the project, configure Storybook addons, and create a Button component story.

### Detailed summary
- Added Storybook configuration files for setup
- Integrated Storybook for UI component development
- Created a story for the Button component with light and dark themes
- Updated package.json with Storybook dependencies

> 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-08-23 17:08:31 +00:00
gregfromstl 41fbdafcf7 [TS SDK] Feature: Add account overload to signMessage (#4250)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR enhances the `signMessage` function in the `thirdweb` package by adding support to pass an account for signing messages, in addition to a private key.

### Detailed summary
- Added overload to `signMessage` to accept an account for signing messages
- Updated `signMessage` function to handle both private key and account options
- Updated tests to cover new account signing functionality

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

<!-- end pr-codex -->
2024-08-23 16:29:04 +00:00
MananTank 44e8e7bce2 Buy Fiat Currency selection updates in Pay UI (#4244)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces support for Japanese Yen (JPY) as a fiat currency option in the Pay UI.

### Detailed summary
- Added JPY as a supported fiat currency
- Updated currency selection UI components
- Added JPYIcon for visual representation
- Implemented default currency selection based on user's location

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

<!-- end pr-codex -->
2024-08-23 06:10:06 +00:00
jnsdls 201a4b3b8f [Update] Refine billing alerts based on account status and usage limits (#4261) 2024-08-23 05:59:32 +00:00
jnsdls 716a4d8c26 [Update] Expand CSP for style-src and font-src to include Vercel domains (#4262)
tl;dr: https://vercel.com/docs/workflow-collaboration/vercel-toolbar/managing-toolbar#using-a-content-security-policy
2024-08-23 05:54:30 +00:00
jnsdls 9a86559c36 [Update] Enhance billing alerts based on account plan and usage limits (#4259)
<!-- start pr-codex -->

## PR-Codex overview
This PR adds conditional alerts based on user's usage and plan in the billing section.

### Detailed summary
- Added `isFreePlan` and `isGrowthPlan` constants based on user's plan
- Updated conditions to show alerts based on usage limits and plan
- Included specific messages for different scenarios and plans

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

<!-- end pr-codex -->
2024-08-23 05:22:14 +00:00
jnsdls 3853e17656 [Refactor] Update useQuery implementation and remove unused imports (#4258) 2024-08-23 03:05:51 +00:00
jnsdls ac2bb59fa0 [Refactor] Update useQuery implementation across multiple hooks (#4257) 2024-08-23 01:20:48 +00:00
Jonas DanielsandGitHub 14696d32fc [Update] Enhance ContractOverviewPage component rendering (#4256) 2024-08-22 17:26:29 -07:00
jnsdls dc903ae029 [Refactor] Update account count fetching using thirdweb hooks (#4255)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to refactor the `sdk.ts` file by removing unused imports and simplifying the `getThirdwebSDK` function.

### Detailed summary
- Removed unused imports and type declarations in `sdk.ts`
- Simplified the `getThirdwebSDK` function parameters
- Improved performance by optimizing the `sdkKey` calculation
- Removed signer update logic in `getThirdwebSDK`
- Refactored `account-cell.tsx` by updating imports and using `thirdweb` library functions
- Updated `useAccountCount` function to use `getContract` and `useReadContract` from `thirdweb` library

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

<!-- end pr-codex -->
2024-08-23 00:14:01 +00:00
FirekeeperandGitHub be03c28b5d .NET 1.5.0 / Unity 5.0.0-beta.4 Docs (#4199) 2024-08-22 17:07:53 -07:00
kien-ngo bdf9507de8 [Dashboard] Overview page (#4222)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates various components in the contract UI to use the `useContract` hook and `ThirdwebContract` type for better contract handling.

### Detailed summary
- Updated components to use `useContract` hook and `ThirdwebContract` type
- Removed unnecessary imports and code duplication
- Refactored contract-related logic for improved readability

> The following files were skipped due to too many changes: `apps/dashboard/src/contract-ui/tabs/overview/components/ContractChecklist.tsx`

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

<!-- end pr-codex -->
2024-08-22 23:48:19 +00:00
edwardysun 86d2bafd72 Remove Coming Soon on Pay webhooks dashboard event type (#4254)
## Problem solved

Removes "Coming Soon" badge - confusing UI

<!-- start pr-codex -->

---

## PR-Codex overview
This PR removes the `Badge` component from the `CreateWebhookButton` in `webhooks.client.tsx`.

### Detailed summary
- Removed the `Badge` component from the form field in the `CreateWebhookButton`.
- Updated the `FormLabel` to remove the "Coming Soon" text.
- Adjusted the `Select` component with a default value and placeholder.

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

<!-- end pr-codex -->
2024-08-22 23:42:58 +00:00
MananTank 6b9bfb6ec7 Connect Playground UI tweaks (#4252)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to optimize the sidebar, improve accordion behavior, and enhance UI components.

### Detailed summary
- Removed unnecessary class from `Sidebar` component
- Adjusted styling in `page.tsx` for responsiveness
- Added functionality to close other accordions when one is opened
- Updated dependencies and added animations to `CustomAccordion`

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

<!-- end pr-codex -->
2024-08-22 23:38:14 +00:00
MananTank 8a618d7bd3 Migrate Mini connect playground to v5 in connect landing page (#4249)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates wallet functionality in the dashboard:

### Detailed summary
- Updated wallet options and social login methods
- Improved theme selection and modal size handling
- Removed unused components and assets

> The following files were skipped due to too many changes: `apps/dashboard/src/components/wallets/ConnectWalletMiniPlayground/MiniPlayground.tsx`

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

<!-- end pr-codex -->
2024-08-22 23:01:26 +00:00
jnsdls e97412bec8 [Refactor] Replace BigNumber with native BigInt operations (#4248)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The PR focuses on refactoring and optimizing Solidity-related functions and updating contract-related hooks and queries for ERC721 and ERC1155 in the dashboard.

### Detailed summary
- Refactored `calculateIntMinValues` and `calculateIntMaxValues` functions for better performance.
- Updated contract-related hooks and queries for ERC721 and ERC1155 in `ContractChecklist.tsx`.
- Replaced `BigNumber` with `BigInt` for improved handling of integer values.

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

<!-- end pr-codex -->
2024-08-22 22:47:30 +00:00
jnsdls 626072cc0d remove unused code (#4247)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to remove unused files, update dependencies, and refactor code structure.

### Detailed summary
- Removed unused files related to maps, gas estimator, and components
- Refactored `useHeightObserver` function
- Updated dependencies like `@tanstack/react-query` and `@stripe/react-stripe-js`
- Removed unused code related to contract code snippets and event handling
- Updated package versions and removed `idb-keyval` dependency

> 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-08-22 22:21:14 +00:00
kien-ngo 5d6b1e1d0a [Dashboard] Migrate type: ProfileMetadata + ProfileMetadataInput (#4109)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates import paths for `ProfileMetadata` and `ProfileMetadataInput` to `constants/schemas`.

### Detailed summary
- Updated import paths for `ProfileMetadata` and `ProfileMetadataInput` to `constants/schemas` in multiple files.
- Added `ProfileSchemaInput` and `ProfileSchemaOutput` in `schemas.ts`.
- Updated type-casting in `fetchPublisherProfile` function.
- Changed parameter type in `useEditProfileMutation` function.

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

<!-- end pr-codex -->
2024-08-22 21:52:16 +00:00
jnsdls b1323ea211 [Refactor] Update useQuery implementation in API hooks (#4246)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to refactor API query functions in the dashboard app to use `useQuery` with explicit query keys and query functions.

### Detailed summary
- Refactored `useApiChains` and `useFeatureContractCodeSnippetQuery` to use `useQuery` with specified query keys and query functions.
- Updated API query functions to be more explicit and maintainable.

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

<!-- end pr-codex -->
2024-08-22 21:45:56 +00:00
jnsdls cbbb414141 [Refactor] Remove query persistence and update import paths (#4245) 2024-08-22 21:33:55 +00:00
MananTank 0417ab45f1 Remove connect playground in dashboard (#4243)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update links and paths related to the Connect Wallet Playground feature.

### Detailed summary
- Updated links to direct to `https://playground.thirdweb.com/connect/sign-in/button`
- Removed references to the deleted Connect Wallet Playground feature
- Updated sidebar and navigation links
- Updated redirects to new playground URL

> The following files were skipped due to too many changes: `apps/dashboard/src/components/wallets/ConnectWalletMiniPlayground/MiniPlayground.tsx`, `pnpm-lock.yaml`

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

<!-- end pr-codex -->
2024-08-22 21:09:21 +00:00
kien-ngo c49bbd3389 [Dashboard] Remove ethers from list-form (#4241)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to refactor the CreateListingsForm component in the listings tab to handle auction creation with improved currency handling.

### Detailed summary
- Refactored currency handling for auction creation
- Added `toUnits` and `toWei` functions
- Imported `decimals` from `thirdweb/extensions/erc20`
- Updated auction creation logic to handle different currencies

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

<!-- end pr-codex -->
2024-08-22 20:55:15 +00:00
kien-ngo f192875547 [Dashboard] Remove bignumber-input since it is for Claim phase v1 (#4242)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to remove the `WaitingTimeInput` component from the `ClaimConditionsPhase` component and related imports.

### Detailed summary
- Deleted `WaitingTimeInput` component import and usage in `ClaimConditionsPhase`
- Removed `WaitingTimeInput` component from the conditional rendering based on `isClaimPhaseV1`

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

<!-- end pr-codex -->
2024-08-22 20:43:49 +00:00
MananTank 55bf1ad5a3 Add Storybook for SDK (#4230)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to add Storybook configuration, update dependencies, and improve story components.

### Detailed summary
- Added Storybook configuration for `storybook` and `build-storybook`
- Updated dependencies for Storybook addons and Vite
- Improved story components for `ConnectButton` and `PayEmbed`
- Defined absolute paths in Storybook config for addons
- Added global CSS styling for Storybook preview
- Created utility functions for story client and wallet adapter

> 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-08-22 16:36:54 +00:00
kien-ngo 01dd80fad9 [Dashboard] Hide AnnouncementBanner for Onchain Olympics [Merge after Aug 21] (#4176)
Merge this PR after Aug 21 🙏
2024-08-22 09:38:41 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
279d81ec35 Version Packages (#4223)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-22 18:43:24 +12:00
joaquim-verges d93ec63cd7 fix(inApp): fix passkeyDomain prop not being respected on login web (#4239)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on fixing the passkey domain issue in the authentication process.

### Detailed summary
- Updated `types.ts` to remove optional domain object
- Modified `web-connector.ts` to use `passkeyDomain` instead of domain object for `rp` properties

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

<!-- end pr-codex -->
2024-08-22 05:01:31 +00:00
joaquim-verges a7f4484702 smart wallet dev tests (#4226)
## Problem solved

Adding a small test preset for dev to test smart wallet dev infra

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to add support for running tests in a test environment and to enhance smart wallet functionality.

### Detailed summary
- Added support for running tests in a test environment
- Updated smart wallet functionality for policy tests
- Modified fetch logic for `localhost` and `test` environments

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

<!-- end pr-codex -->
2024-08-22 04:45:29 +00:00
gregfromstl b38308d893 [TS SDK] Feature: Enable external redirect authentication for electron support (#4237)
Adds the ability to pass a `redirectUrl` and `redirectExternally` boolean to `authenticate`. This will allow electron apps to authenticate with in-app wallet social auth.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new authentication mode "window" and enables external redirects for electron support.

### Detailed summary
- Added a new authentication mode "window" for in-app authentication
- Enabled external redirects for electron support
- Updated authentication modes in various files
- Updated authentication methods to support new mode and redirect URL

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

<!-- end pr-codex -->
2024-08-22 04:40:30 +00:00
MananTank 7ef1c721ef Update Playground (#4178)
Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

This PR introduces new authentication features, refactors UI components, adds redirect functionality, and enhances code formatting.

- Added new authentication features
- Refactored UI components
- Added redirect functionality
- Enhanced code formatting

> The following files were skipped due to too many changes: `apps/playground-web/src/app/providers.tsx`, `apps/playground-web/src/components/ui/input.tsx`, `apps/playground-web/src/app/connect/sign-in/components/types.ts`, `apps/playground-web/src/components/ui/CustomRadioGroup.tsx`, `apps/playground-web/package.json`, `apps/playground-web/src/components/ui/CopyButton.tsx`, `packages/thirdweb/src/react/web/ui/ConnectWallet/WalletSelector.tsx`, `apps/playground-web/src/components/code/RenderCode.tsx`, `apps/playground-web/src/components/code/code.client.tsx`, `apps/playground-web/src/app/connect/sign-in/components/CollapsibleSection.tsx`, `apps/playground-web/src/components/ui/checkbox.tsx`, `apps/playground-web/src/app/AppSidebar.tsx`, `apps/playground-web/src/components/ui/DynamicHeight.tsx`, `apps/playground-web/src/components/ui/switch.tsx`, `apps/playground-web/src/components/code/code-example.tsx`, `apps/playground-web/src/components/blocks/APIHeader.tsx`, `apps/playground-web/src/app/layout.tsx`, `apps/playground-web/src/components/code/code.tsx`, `apps/playground-web/src/components/ui/radio-group.tsx`, `packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx`, `apps/playground-web/src/app/connect/sign-in/components/LocaleFormControl.tsx`, `apps/playground-web/src/components/ui/button.tsx`, `apps/playground-web/src/components/ui/scroll-area.tsx`, `apps/playground-web/src/app/connect/sign-in/embed/page.tsx`, `apps/playground-web/src/app/connect/sign-in/headless/page.tsx`, `apps/playground-web/src/components/ui/tooltip.tsx`, `apps/playground-web/src/components/ui/CustomAccordion.tsx`, `apps/playground-web/src/app/MobileHeader.tsx`, `apps/playground-web/src/app/connect/sign-in/button/page.tsx`, `apps/playground-web/public/thirdweb.svg`, `apps/playground-web/src/app/connect/sign-in/components/InAppWalletFormGroup.tsx`, `apps/playground-web/src/app/globals.css`, `apps/playground-web/src/app/connect/auth/page.tsx`, `apps/playground-web/src/app/connect/pay/page.tsx`, `apps/playground-web/src/components/ui/ScrollShadow/ScrollShadow.tsx`, `apps/playground-web/src/app/connect/in-app-wallet/page.tsx`, `apps/playground-web/src/app/connect/blockchain-api/page.tsx`, `apps/playground-web/src/app/connect/account-abstraction/page.tsx`, `apps/playground-web/src/app/connect/sign-in/components/WalletsSelection.tsx`, `pnpm-lock.yaml`, `apps/playground-web/src/components/ui/sidebar.tsx`, `apps/playground-web/src/app/connect/sign-in/components/CodeGen.tsx`, `apps/playground-web/src/app/connect/sign-in/components/ColorFormGroup.tsx`, `apps/playground-web/src/components/ui/select.tsx`, `apps/playground-web/src/app/connect/sign-in/button/RightSection.tsx`, `apps/playground-web/src/app/connect/sign-in/button/LeftSection.tsx`

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

<!-- end pr-codex -->
2024-08-22 01:14:22 +05:30
MananTank c48a0b1105 Remove collapse toggle from Trusted Forwards form control in deploy page (#4231)
FIXES: DASH-167
2024-08-21 17:37:16 +00:00
kien-ngo 6b9b3efb51 [Dashboard] Account page v5 (#4219)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update the contract UI components to use the latest contract data and improve the account creation process.

### Detailed summary
- Updated `useRouteConfig.tsx` to conditionally render `LazyContractAccountsPage`
- Refactored `CreateAccountButton.tsx` to use `ThirdwebContract`
- Modified `page.tsx` to use `ThirdwebContract` and `ExtensionDetectedState` types

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

<!-- end pr-codex -->
2024-08-21 17:14:01 +00:00
kien-ngo 654cbbaa2e [Dashboard] Permission page to v5 (#4218)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to implement a feature that detects and handles permissions enumerable in contracts.

### Detailed summary
- Added detection and handling of `PermissionsEnumerable` feature in contracts
- Updated components to utilize the detected `PermissionsEnumerable` flag
- Refactored and optimized contract permission handling logic

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

<!-- end pr-codex -->
2024-08-21 17:13:58 +00:00
Joe-ThirdwebandGitHub 88bbbd5a01 Joe - Chainlist Page update 0gLabs (#4233) 2024-08-21 10:10:51 -07:00
kien-ngo 0c2fd4db98 [Dashboard] Remove ethers from int-input (#4234)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR replaces `parseEther` with `toWei` in `SolidityIntInput` component for input conversion.

### Detailed summary
- Replaced `parseEther` with `toWei` for input conversion
- Updated import from `thirdweb`
- Removed unused import of `useCallback`
- Updated `Button` import from `tw-components`

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

<!-- end pr-codex -->
2024-08-21 17:02:45 +00:00
kien-ngo 2c37732984 [SDK] Fix abi param parsing (#4229)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to enhance the parsing functionality for Solidity types in the contract UI components and transaction handling.

### Detailed summary
- Added parsing logic for `string` type in `parseAbiParams`
- Improved handling of different data types in contract transactions
- Added test cases for parsing `string` and `number` in contract transactions

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

<!-- end pr-codex -->
2024-08-21 17:02:36 +00:00
MananTank 783e448e2e Stricter correctness rules in biome (#4216)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to remove unnecessary error handling blocks in various catch statements.

### Detailed summary
- Removed error variable from catch blocks in multiple files.
- Simplified catch blocks to just `catch {}` where error handling was not required.

> The following files were skipped due to too many changes: `apps/dashboard/src/utils/fetchChain.ts`, `packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts`, `packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts`, `packages/thirdweb/src/auth/verify-hash.ts`, `packages/thirdweb/src/contract/verification/constructor-params.ts`, `apps/wallet-ui/src/components/NftModal.tsx`, `apps/playground-web/src/components/in-app-wallet/connect-button.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx`, `packages/thirdweb/src/extensions/erc721/lazyMinting/write/createDelayedRevealBatch.ts`, `packages/thirdweb/src/react/web/ui/ConnectWallet/screens/WalletConnectReceiverScreen.tsx`, `apps/dashboard/src/contract-ui/components/solidity-inputs/helpers.ts`, `packages/service-utils/src/core/api.ts`, `apps/playground-web/src/components/in-app-wallet/sponsored-tx.tsx`, `apps/playground-web/src/components/account-abstraction/sponsored-tx.tsx`, `apps/playground-web/src/app/connect/auth/page.tsx`, `apps/dashboard/src/components/contract-components/hooks.ts`, `packages/thirdweb/src/wallets/in-app/native/helpers/auth/middleware.ts`, `apps/dashboard/src/lib/sdk.ts`, `apps/playground-web/src/app/page.tsx`, `apps/dashboard/src/contexts/configured-chains.tsx`, `legacy_packages/unity-js-bridge/src/thirdweb-bridge.ts`, `packages/thirdweb/src/cli/commands/generate/generate.ts`

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

<!-- end pr-codex -->
2024-08-21 09:56:04 +00:00
MananTank 6c87d7ba3a Add useHookAtTopLevel rule in biome and fix lint errors (#4213)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to address linting issues related to the use of hooks at the top level in various components.

### Detailed summary
- Added `correctness` rule in `biome.json` for `useHookAtTopLevel`
- Added error handling and comments for `useQuery` in `useReadContract` functions
- Refactored `RNImage.tsx` to use `useMemo` for image handling
- Improved error handling in `ViewNFTs.tsx` for `fetchNFTs` function

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

<!-- end pr-codex -->
2024-08-21 01:11:23 +00:00
kien-ngo 6b8bde1abf [Dashboard] Replace useSDKChainId (#4217)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to refactor hook dependencies in the dashboard component to use `useDashboardEVMChainId` instead of `useSDKChainId`.

### Detailed summary
- Updated imports to use `useDashboardEVMChainId` from `@3rdweb-sdk/react`
- Replaced all instances of `useSDKChainId` with `useDashboardEVMChainId`

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

<!-- end pr-codex -->
2024-08-21 00:40:41 +00:00
kien-ngo 81b33c7d7c [Dashboard] useContractEvents to v5 (#4215)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates event handling in the dashboard by refactoring components, adding type definitions, and improving event filtering.

### Detailed summary
- Refactored components to handle events using `ThirdwebContract` type
- Added type definitions for `InternalTransaction` and `PreparedEvent`
- Improved event filtering and handling in `useActivity` and `EventsFeed`
- Updated `LatestEvents` and `ContractEventsPage` components to use `ThirdwebContract` type

> The following files were skipped due to too many changes: `apps/dashboard/src/contract-ui/tabs/events/components/events-feed.tsx`

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

<!-- end pr-codex -->
2024-08-21 00:40:40 +00:00
kien-ngo e37ac839b2 [Dashboard] claim-condition page (#4208)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update claim conditions for ERC721, ERC1155, and ERC20 contracts.

### Detailed summary
- Updated claim condition tabs for ERC721, ERC1155, and ERC20 contracts
- Added detection of new claim conditions for different contract types
- Refactored components to use updated contract information

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

<!-- end pr-codex -->
2024-08-21 00:24:22 +00:00
kien-ngo 462e1fd2aa [Dashboard] chore: remove getErc (1155) (#4207)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to refactor the `useNFTDrawerTabs` function in the `useNftDrawerTabs.tsx` file to use `isERC1155` instead of `erc1155` for better clarity.

### Detailed summary
- Refactored references from `erc1155` to `isERC1155` for clarity
- Updated `contractAddress` and `chainId` values to use `contract` object properties instead of `erc1155`

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

<!-- end pr-codex -->
2024-08-20 23:58:29 +00:00
kien-ngo b4d8be4f91 [Dashboard] Remove some stuff from claim-condition-form (#4206)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the Claim Conditions Form component to use the `useReadContract` hook and `decimals` extension from `thirdweb/extensions/erc20`.

### Detailed summary
- Replaced `useTokenDecimals` with `useReadContract` for token decimals retrieval
- Added `decimals` import from `thirdweb/extensions/erc20`
- Added `useReadContract` import from `thirdweb/react`

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

<!-- end pr-codex -->
2024-08-20 23:57:54 +00:00
kien-ngo 15b7e8cfb8 [Dashboard] Remove type UploadProgressEvent (#4205)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to re-export `UploadProgressEvent` type and import it in `batch-lazy-mint-button.tsx`.

### Detailed summary
- Re-exported `UploadProgressEvent` type from `progress-box.tsx`
- Imported `UploadProgressEvent` in `batch-lazy-mint-button.tsx`

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

<!-- end pr-codex -->
2024-08-20 23:57:52 +00:00
kien-ngo 886094fbac [Dashboard] Migrate /code page to v5 (#4204)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR improves contract code rendering in the dashboard.

### Detailed summary
- Updated rendering logic for contract code in various components
- Added type `AbiType` for compatibility
- Refactored `ContractCodePage` to use `ThirdwebContract` instead of `contractAddress`

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

<!-- end pr-codex -->
2024-08-20 22:36:27 +00:00
gregandGitHub 0a1bce8fa8 FIX: In-App Metadata Image Display (#4214) 2024-08-20 15:19:37 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
3ccfa981a8 chore(deps): bump @sentry/nextjs from 8.9.2 to 8.26.0 (#4182)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-20 15:10:32 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fb96e83081 chore(deps-dev): bump tailwindcss from 3.4.4 to 3.4.10 (#4183)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-20 14:57:14 -07:00
Jonas DanielsandGitHub 9768b0c0b7 [Refactor] Update account plan management and remove trial-related logic (#4221) 2024-08-20 14:55:26 -07:00
Jonas DanielsandGitHub 2312f61329 [Update] Replace abuse redirect with email link (#4220) 2024-08-20 14:54:50 -07:00
Jonas DanielsandGitHub 042439aab9 [Refactor] Remove Twitter handles from ambassadors list (#4209) 2024-08-20 14:54:04 -07:00
kien-ngo 0368ee538e [Dashboard] Add useDashboardOwnedNFTs hook (#4203)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update the `quantityOwned` type in the NFTs types, add a new hook for getting owned NFTs, and make adjustments in the Create Listings Form component.

### Detailed summary
- Updated `quantityOwned` type to `bigint` in NFTs types.
- Added `useDashboardOwnedNFTs` hook for fetching owned NFTs.
- Adjusted logic in Create Listings Form component for using the new hook and contract handling.

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

<!-- end pr-codex -->
2024-08-20 21:37:09 +00:00
gregfromstl c6d4aef91b Revert "Dashboard: Allow more domains (#4192)" (#4212)
This reverts commit 4bf8296546.

## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the domain validation logic in `validations.ts` to allow only "localhost" as a domain prefix.

### Detailed summary
- Updated domain validation to allow only "localhost" as a domain prefix.

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

<!-- end pr-codex -->
2024-08-20 21:15:09 +00:00
kien-ngo 86c953f18e [Dashboard] Remove useContract from Explorer page (#4202)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update contract-related components and hooks to use `ThirdwebContract` instead of `SmartContract`.

### Detailed summary
- Updated components and hooks to use `ThirdwebContract` instead of `SmartContract`
- Added conditional rendering based on contract and ABI availability
- Removed unnecessary imports and code snippets

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

<!-- end pr-codex -->
2024-08-20 20:33:38 +00:00
MananTank 4ce4072031 Hide Buy Funds section on testnet chain pages (#4210)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR adds a conditional rendering for a `FaucetSection` component based on the `chain.testnet` value in the `page.tsx` file of the `chainPage` in the dashboard.

### Detailed summary
- Added conditional rendering for `FaucetSection` based on `chain.testnet` value
- Renamed "Buy Funds" to "Faucet / Buy Funds"
- Updated rendering logic for `FaucetSection` and `BuyFundsSection` based on `chain.testnet` and service availability

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

<!-- end pr-codex -->
2024-08-20 20:18:41 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
bd42363602 Version Packages (#4175)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-20 11:27:36 -07:00
gregfromstl 42cdc3a2bc UI Cleanup (#4191)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the connect modal styles and adds support for wallet authentication in the authentication flow.

### Detailed summary
- Updated connect modal styles
- Added support for wallet authentication
- Modified styles for various UI components
- Added a new component for copying addresses
- Improved wallet image rendering based on wallet type

> The following files were skipped due to too many changes: `packages/thirdweb/src/wallets/custom/smart/image.ts`, `packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx`

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

<!-- end pr-codex -->
2024-08-20 17:52:34 +00:00
MananTank 4abf7a7f25 Fix Conditionally rendered hook error in Buy with Fiat flow (#4211)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on fixing a conditionally rendered hook error when buying funds using fiat.

### Detailed summary
- Added a new `onPostOnrampSuccess` function to handle successful post onramp swap
- Changed the logic to report fiat status instead of post onramp swap status after successful swap

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

<!-- end pr-codex -->
2024-08-20 17:31:14 +00:00
kien-ngo 8b0fd13adb [Dashboard] Sources page to v5 (#4201)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update contract-related components and hooks, resolve ABI types, and improve UX in the dashboard.

### Detailed summary
- Updated components and hooks related to contracts
- Resolved ABI types with `abitype`
- Improved UX in the dashboard by updating sources display

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

<!-- end pr-codex -->
2024-08-20 17:11:57 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
20bd3aa399 chore(deps): bump tailwind-merge from 2.4.0 to 2.5.2 (#4189)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-20 10:10:48 -07:00
joaquim-verges fcfd22c01e feat(adapters): add documentation for wagmi and privy integration (#4198)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update the usage of adapters in the thirdweb SDK for compatibility with different libraries.

### Detailed summary
- Updated adapter names in sidebar links
- Added new adapters in sidebar
- Modified the conversion process for ethers5 and ethers6 adapters
- Added examples for using thirdweb SDK with wagmi and privy

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

<!-- end pr-codex -->
2024-08-20 16:54:22 +00:00
kien-ngo 8dc23e376e [Dashboard] Fix import path (#4200)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the dashboard contract UI tabs page by removing unused imports and refactoring some dependencies.

### Detailed summary
- Removed unused import `thirdwebClient`
- Refactored dependencies related to `3rdweb-sdk/react` and `3rdweb-dev/react`

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

<!-- end pr-codex -->
2024-08-20 10:12:12 +00:00
Kien NgoandGitHub 542f97dc4b [Dashboard] Migrate useContractSources to v5 (#4145) 2024-08-20 21:04:19 +12:00
kien-ngo 46ca21d128 [Dashboard] Explorer page to v5 (#4174)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to enhance contract function interaction by integrating V5 features and updating contract call handling.

### Detailed summary
- Added `useV5DashboardChain` and `getContract` from `lib/v5-adapter` and `thirdweb` respectively
- Updated `ContractFunction` component to use V5 contract and handle contract calls
- Updated `InteractiveAbiFunction` component to use `ThirdwebContract` and improve contract call handling

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

<!-- end pr-codex -->
2024-08-20 08:45:43 +00:00
kien-ngo 551349cbc6 [Dashboard] envs clean-up (#4170)
- [x] Remove a lof of duplicated instances of `ThirdwebClient` & env variables across multiple files
- [x] There's only 1 main ThirdwebClient from now on which can be imported from `lib/thirdweb-client`. It can switch between clientId and secretKey depends on the environment
- [x] All env variables like `DASHBOARD_THIRDWEB_CLIENT_ID`, `DASHBOARD_THIRDWEB_SECRET_KEY`, `isProd` etc. now live inside `@/constants/env`

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates import paths and constants related to the `thirdwebClient` and `isProd` across multiple files.

### Detailed summary
- Updated import paths for `thirdwebClient` and `isProd`
- Replaced `constants/rpc` with `constants/env`
- Removed unused imports and constants

> The following files were skipped due to too many changes: `apps/dashboard/src/contract-ui/tabs/overview/page.tsx`, `apps/dashboard/src/contract-ui/tabs/nfts/components/airdrop-tab.tsx`, `apps/dashboard/src/components/app-layouts/app.tsx`, `apps/dashboard/src/components/app-layouts/provider-setup.tsx`, `apps/dashboard/src/contract-ui/tabs/manage/page.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/opengraph-image.tsx`, `apps/dashboard/src/lib/sdk.ts`, `apps/dashboard/src/@/constants/client.ts`, `apps/dashboard/src/app/(dashboard)/(chain)/components/server/chain-icon.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx`, `apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/tx/[txHash]/page.tsx`, `apps/dashboard/src/pages/api/moralis/balances.ts`

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

<!-- end pr-codex -->
2024-08-20 07:55:31 +00:00
gregfromstl 4bf8296546 Dashboard: Allow more domains (#4192)
Requested for electron app domains

<!-- start pr-codex -->

---

## PR-Codex overview
This PR loosens the domain validation in `validations.ts` to allow cases where strict validation was causing issues.

### Detailed summary
- Loosened domain validation to allow cases where strict validation was too limiting
- Updated the condition to check if the domain includes ":" instead of splitting it

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

<!-- end pr-codex -->
2024-08-20 07:34:44 +00:00
Joaquim VergesandGitHub 7efef7d9e1 feat(pay): show pay modal when insufficient balance for gas cost (#4194) 2024-08-20 19:29:52 +12:00
kien-ngo 4d85aa56c6 [Dashboard] Fix types for contract published version (#4197)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the `publish.tsx` and `index.tsx` files to handle cases where `version` is undefined by defaulting to "latest".

### Detailed summary
- In `publish.tsx`, added a default value of "latest" if `v` is undefined in the `SelectItem` component.
- In `index.tsx`, changed the `version` type from `string` to `string | undefined` and added a default value of "latest" if `version` is undefined when constructing the `PublishedContractOG` object.

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

<!-- end pr-codex -->
2024-08-20 07:18:40 +00:00
Jonas DanielsandGitHub decc1a56d2 [Fix] Handle optional versions in contract publishing (#4196) 2024-08-19 21:57:27 -07:00
joaquim-verges a60db3a057 feat(adapters): add documentation and examples for ethers5, ethers6, and viem adapters (#4195)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to introduce adapters for converting between Thirdweb and ethers.js/viem objects.

### Detailed summary
- Added `ethers5Adapter` and `ethers6Adapter` for Thirdweb to ethers.js conversion
- Added `viemAdapter` for Thirdweb to viem conversion
- Convert Thirdweb accounts, contracts, clients to ethers.js/viem and vice versa

> The following files were skipped due to too many changes: `packages/thirdweb/src/adapters/viem.ts`

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

<!-- end pr-codex -->
2024-08-20 04:51:32 +00:00
Jonas DanielsandGitHub 377f176b10 [Refactor] Replace max with min function and update import paths (#4193) 2024-08-19 20:08:13 -07:00
joaquim-verges 5ead75e41a fix(dashboard): update Expo Starter repo URL (#4190)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update the repository link in the Expo Starter template from `node-starter` to `expo-starter`.

### Detailed summary
- Updated repository link from "https://github.com/thirdweb-example/node-starter" to "https://github.com/thirdweb-example/expo-starter".

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

<!-- end pr-codex -->
2024-08-20 00:32:31 +00:00
MananTank 8836c27b8d Fix Table loading spinner position (#4187)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR adds a `className="relative"` to various `TableContainer` components in different files for improved styling consistency.

### Detailed summary
- Added `className="relative"` to `TableContainer` in `contract-table/index.tsx`
- Added `className="relative"` to `TableContainer` in `contract-table-v2/index.tsx`
- Added `className="relative"` to `TableContainer` in `deployed-contracts.tsx`
- Added `className="relative"` to `TableContainer` in `table.tsx`
- Added `className="relative"` to `TableContainer` in `marketplace-table.tsx`

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

<!-- end pr-codex -->
2024-08-20 00:04:50 +00:00
kien-ngo cbd6fd87d0 [SDK] Update Lens test (#4188)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on updating a test in `resolveAddress.test.ts` to run only if a secret key is provided.

### Detailed summary
- Updated test to run only if `process.env.TW_SECRET_KEY` is provided.

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

<!-- end pr-codex -->
2024-08-19 21:51:35 +00:00
kien-ngo ef9cc55b69 [Dashboard] Remove v4 code on some Marketplace components
(#4128)

## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR exposes `max()` and `min()` utility methods for bigints and updates marketplace-related components to use these methods for better handling of large numbers.

### Detailed summary
- Exposes `max()` and `min()` utility methods for bigints in `thirdweb/utils`
- Updates components in `dashboard` related to marketplace to use `max()` and `min()` for handling large numbers efficiently

> The following files were skipped due to too many changes: `apps/dashboard/src/contract-ui/tabs/overview/components/MarketplaceDetails.tsx`

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

<!-- end pr-codex -->
2024-08-19 20:06:26 +00:00
kien-ngo 15899ea231 [Dashboard] Fix styling for no-image NFT (#4181)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update the styling of the `NFTMedia` component by adding explicit `width` and `height` properties.

### Detailed summary
- Added inline styles for `width` and `height` based on `props`
- Updated `className` to include `border-border` class

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

<!-- end pr-codex -->
2024-08-19 19:47:32 +00:00
kien-ngo bd4669984d [Portal] Update some docs (#4177)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to introduce gasless transaction configurations and transaction annotations.

### Detailed summary
- Added gasless transaction configurations for Engine, OpenZeppelin, and Biconomy providers
- Introduced transaction annotations for Engine, OpenZeppelin, and gasless options
- Updated transaction button components to include transaction annotations
- Exposed GaslessOptions type for gasless transactions configuration

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

<!-- end pr-codex -->
2024-08-19 19:29:55 +00:00
MananTank fb799ccb6d Move publish metadata spinner inside form, show error message (#4180)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR adds an error message display for failed metadata fetch in the `CustomContractForm` component.

### Detailed summary
- Added error message display for failed metadata fetch
- Imported `InfoIcon` component
- Removed conditional rendering for loading spinner outside the form provider block

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

<!-- end pr-codex -->
2024-08-19 18:24:20 +00:00
kien-ngo c8f7a68225 [Dashboard] (partial) useContract removal (#4111)
Slowly moving everything upward. This PR mainly focuses on removing `useContract` from the page level (`page.tsx` files)

Routes to test:
- [x] direct listings page
- [x] english auction page
- [x] Split contract page
- [x] Contract account page
- [x] contract settings page
- [x] contract token page

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to update various components to use `ThirdwebContract` instead of `contractAddress` and `chainId`.

### Detailed summary
- Update components to use `ThirdwebContract` instead of `contractAddress` and `chainId`
- Replace `useContract` calls with `contract.address`
- Import `ThirdwebContract` type for type checking

> The following files were skipped due to too many changes: `apps/dashboard/src/contract-ui/tabs/shared-components/marketplace-table.tsx`, `apps/dashboard/src/contract-ui/tabs/shared-components/cancel-tab.tsx`, `apps/dashboard/src/contract-ui/tabs/shared-components/listing-drawer.tsx`, `apps/dashboard/src/contract-ui/tabs/english-auctions/components/table.tsx`, `apps/dashboard/src/contract-ui/tabs/direct-listings/components/table.tsx`, `apps/dashboard/src/contract-ui/tabs/english-auctions/page.tsx`, `apps/dashboard/src/contract-ui/tabs/direct-listings/page.tsx`, `apps/dashboard/src/contract-ui/tabs/split/page.tsx`, `apps/dashboard/src/contract-ui/tabs/overview/page.tsx`, `apps/dashboard/src/contract-ui/tabs/shared-components/list-button.tsx`, `apps/dashboard/src/contract-ui/tabs/account-permissions/page.tsx`, `apps/dashboard/src/contract-ui/tabs/embed/page.tsx`, `apps/dashboard/src/contract-ui/tabs/settings/page.tsx`, `apps/dashboard/src/contract-ui/tabs/account/page.tsx`, `apps/dashboard/src/contract-ui/tabs/tokens/page.tsx`, `apps/dashboard/src/contract-ui/hooks/useRouteConfig.tsx`

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

<!-- end pr-codex -->
2024-08-19 18:12:53 +00:00
gregfromstl 246701d615 Wallet Linking UI (#4124)
https://github.com/user-attachments/assets/abf70934-8070-4844-8f15-8db6ec40c116

<!-- start pr-codex -->

---

## PR-Codex overview
This PR adds the ability to link a wallet in the Connect UI.

### Detailed summary
- Added a new `linkWallet` field in locale types for various languages
- Updated UI components to support linking a wallet
- Introduced `WalletAuth` component for wallet authentication
- Modified wallet selection behavior in `WalletSelector` component

> The following files were skipped due to too many changes: `packages/thirdweb/src/react/web/ui/ConnectWallet/WalletSelector.tsx`, `packages/thirdweb/src/react/web/wallets/shared/PassKeyLogin.tsx`, `packages/thirdweb/src/react/web/wallets/in-app/WalletAuth.tsx`

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

<!-- end pr-codex -->
2024-08-19 17:01:20 +00:00
Joaquim VergesandGitHub cb591aa6af feat(pay): only show pay modal if the active account doesnt have enough funds (#4172) 2024-08-19 21:15:54 +12:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
09927f3ac5 Version Packages (#4162)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-19 20:17:56 +12:00
gregfromstl 7778063458 Improve Blobbie Appearance (#4173)
Blobbies now select from a subset of predefined colors to form a basic radial gradient form the bottom right corner.

![image](https://github.com/user-attachments/assets/5b708533-506d-417d-ac49-2356aa0a2247)

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the appearance of the Blobbie avatar in the ConnectWallet feature of `thirdweb`.

### Detailed summary
- Updated Blobbie appearance with unique gradient colors based on address
- Replaced complex color pairing logic with predefined color options
- Simplified Blobbie rendering logic for better performance

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

<!-- end pr-codex -->
2024-08-19 06:17:43 +00:00
Joaquim VergesandGitHub 574ff0295d feat(dashboard): update apple-app-site-association for universal links (#4171) 2024-08-19 11:54:40 +12:00
kien-ngo a43907e2df [SDK] resolveAddress for Lens handles (#4150)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to enhance the `Lens` extension in the `thirdweb` package by adding the ability to resolve addresses and handle crypto transactions efficiently.

### Detailed summary
- Added `resolveAddress` function for handling crypto transactions with lens handles
- Updated contract addresses and overrides for Lens smart contracts
- Created tests for `resolveAddress` function to ensure correct address resolution

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

<!-- end pr-codex -->
2024-08-18 23:10:46 +00:00
kien-ngo f6d6c86ec0 [Dashboard] Fix ENS resolve issue (#4168)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the `thirdwebClient` configuration in `thirdweb-client.ts` to include a `secretKey` option if `DASHBOARD_THIRDWEB_SECRET_KEY` is defined.

### Detailed summary
- Updated `thirdwebClient` configuration to include `secretKey` if `DASHBOARD_THIRDWEB_SECRET_KEY` is defined
- Added conditional logic for `secretKey` presence

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

<!-- end pr-codex -->
2024-08-18 22:21:10 +00:00
Joaquim VergesandGitHub 23041c013f feat(dashboard): add apple universal link association (#4169) 2024-08-19 10:04:20 +12:00
kien-ngo 43f9319ac7 [SDK] Add GaslessOptions to useSendAndConfirmTransaction (#4165)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to add Gasless transaction support for the `sendTransaction` and `sendAndConfirmTransaction` functions, as well as for the `useSendTransaction` and `useSendAndConfirmTransaction` hooks.

### Detailed summary
- Added `GaslessOptions` support to `sendTransaction`
- Introduced Gasless usage with `thirdweb Engine` in various transaction-related functions and hooks
- Added `GaslessOptions` configuration to `useSendTransaction` and `useSendAndConfirmTransaction` hooks
- Updated documentation and examples to showcase Gasless transaction usage

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

<!-- end pr-codex -->
2024-08-18 10:05:37 +00:00
kien-ngo fc8daaa2bc [Dashboard] Remove ethers.js (#4163)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on exporting a utility method `isBytes` and updating types to `bigint`.

### Detailed summary
- Exported `isBytes` utility method
- Updated `nextTokenIdToMint` type to `bigint`
- Updated imports for `isAddress`, `isBytes`, and `isHex`
- Refactored display balance calculations to use `toTokens` function

> The following files were skipped due to too many changes: `apps/dashboard/src/contract-ui/tabs/nfts/components/batch-lazy-mint-button.tsx`

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

<!-- end pr-codex -->
2024-08-18 07:04:05 +00:00
kien-ngo 9133b45700 [Dashboard] useVote to v5 (#4154)
<!-- start pr-codex -->

## PR-Codex overview
This PR updates TypeScript documentation and refactors Vote-related components for clarity and consistency.

### Detailed summary
- Updated TypeScript documentation for `ProposalVoteInfo`
- Refactored `VoteButton` and `DelegateButton` components to use `ThirdwebContract`
- Improved display of voting token decimals in `Proposal` component

> The following files were skipped due to too many changes: `apps/dashboard/src/@3rdweb-sdk/react/hooks/useVote.ts`

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

<!-- end pr-codex -->
2024-08-18 05:02:05 +00:00
kien-ngo 44c78f4643 [Dashboard] useRemoveContractMutation to v5 + minor cleanups (#4155)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to refactor gasless transaction handling in the Dashboard by centralizing contract addresses and relayer URLs.

### Detailed summary
- Introduced `MULTICHAIN_REGISTRY_CONTRACT` constant for contract address
- Centralized relayer URL and forwarder address in `DASHBOARD_ENGINE_RELAYER_URL` and `DASHBOARD_FORWARDER_ADDRESS`
- Updated functions to use centralized constants for contract addresses and relayer URLs

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

<!-- end pr-codex -->
2024-08-18 03:55:32 +00:00
kien-ngo 7aee37b10f [Dashboard] Replace useSDKChainId with useDashboardEVMChainId (#4144)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to replace the usage of `useSDKChainId` with `useDashboardEVMChainId` in multiple components for better compatibility and functionality.

### Detailed summary
- Replaced `useSDKChainId` with `useDashboardEVMChainId` in multiple components.
- Updated imports and dependencies accordingly.
- Improved compatibility and functionality for the dashboard.

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

<!-- end pr-codex -->
2024-08-18 03:43:32 +00:00
kien-ngo 23a665bdaa [Dashboard] Remove v4 type: OptionalPropertiesInput (#4158)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to update the type definition for `attributes` in the `IPropertyFieldValues` interface.

### Detailed summary
- Updated `OptionalPropertiesInput` type definition to be a key-value pair object
- Changed `attributes` type in `IPropertyFieldValues` interface to use the updated type definition

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

<!-- end pr-codex -->
2024-08-18 03:32:30 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
9e46b5b94e Version Packages (#4121)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-18 12:37:18 +12:00
gregfromstl e0dcc64256 Fix connect button border in firefox (#4160)
Corrects an issue with the border visibility in firefox by explicitly specifying the div height

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on fixing the connect button border issue on Firefox browser.

### Detailed summary
- Added `height: "50px"` to the button style in `Details.tsx` for proper display on Firefox.

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

<!-- end pr-codex -->
2024-08-18 00:03:46 +00:00
gregfromstl f0d6e343ee Adds EIP-1271 Signature Validation Fallback (#4156)
This PR EIP-1271 fallback for smart contract validation since not all chains support the deployless call used in 6792 flow.

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to fix smart contract wallet signature validation on older chains.

### Detailed summary
- Updated the prompt text for signing in on the wallet UI
- Added a fallback to EIP1271 validation for chains not supporting eth_call simulation
- Introduced a new function `verifyEip1271Signature` for EIP1271 signature validation

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

<!-- end pr-codex -->
2024-08-17 23:43:45 +00:00
gregfromstl cf4443a9eb Improve Blobbie Algo (#4159)
![image](https://github.com/user-attachments/assets/cfc34112-3b94-4125-8aba-4a065d7c9ec2)
![image](https://github.com/user-attachments/assets/9daf26b0-fde8-4a77-bebf-a77f94d444f2)
![image](https://github.com/user-attachments/assets/91c4ceb1-c3c0-4be6-a654-d233b699495a)
![image](https://github.com/user-attachments/assets/7a298ad2-7fa4-4967-a7ea-7e9ec2fcf7be)

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to improve the Blobbie algorithm for generating unique gradient avatars based on provided addresses.

### Detailed summary
- Updated color calculation logic for better color pairing
- Removed unused `nearestColor` function
- Refactored color and position calculations for Blobbie avatar
- Adjusted ellipse positions and sizes for better visual representation

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

<!-- end pr-codex -->
2024-08-17 23:12:15 +00:00
gregfromstl 355795a2d4 Fix text wrapping on connect button (#4157)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on fixing text wrapping on the Connect Button in the `Details.tsx` file.

### Detailed summary
- Added `whiteSpace: "nowrap"` to prevent text wrapping
- Updated styles for better button display

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

<!-- end pr-codex -->
2024-08-17 19:37:43 +00:00
kien-ngo 5fda794569 [SDK] Add more VoteERC20 extensions (for Dashboard) (#4139)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR exposes ERC20Vote extensions and adds more Vote contract extensions.

### Detailed summary
- Exposed ERC20Vote extensions
- Added more Vote contract extensions
- Updated types for vote and proposal states
- Implemented functions for proposal existence, execution, and voting counts

> The following files were skipped due to too many changes: `packages/thirdweb/src/extensions/vote/read/getAll.ts`

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

<!-- end pr-codex -->
2024-08-17 12:28:02 +00:00
kien-ngo 93b3bad783 [SDK] Expose thirdweb extensions (for Dashboard) (#4149)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to export contracts related to Publisher, Multichain Registry, and Contract Factory functionalities.

### Detailed summary
- Added Publisher contract exports for reading and writing operations
- Added Multichain Registry exports for reading and writing operations
- Added Contract Factory export for deploying proxies

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

<!-- end pr-codex -->
2024-08-17 12:02:02 +00:00
Joaquim VergesandGitHub f5952621ab feat(wallet): handle eth_sendTransaction when doing zksync direct deploys (#4153) 2024-08-17 23:52:10 +12:00
kien-ngo 1d9f4e5e3e [Dashboard] MismatchButton to v5 (#4152)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview

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

<!-- end pr-codex -->
2024-08-17 10:01:56 +00:00
gregfromstl 2e99057f90 feat(wallet-ui): initial app (#3478)
1. Created a new `.env.example` file specifying the `NEXT_PUBLIC_ROOT_DOMAIN` variable.
2. Updated `.gitignore` to include `.env*.local` instead of `.env*`.
3. Modified the `Layout` component in `[ecosystem]/layout.tsx` to accept `children` and `params` properties and display the ecosystem and children elements.
4. Updated the `Page` component in `[ecosystem]/page.tsx` to return a simple `div` with the text `Page`.
5. Added a new `middleware.ts` file to handle dynamic routing based on the request host, matching all paths except for specific routes (e.g., `/api`, `/_next`, `/_static`). Rewrites the URL to point to either the root application or the appropriate ecosystem dynamic route.

---

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on adding ecosystem-related features and improving UI components.

### Detailed summary
- Added `Ecosystem` and token types
- Updated `biome.json` schema
- Implemented revalidate function
- Added NFT loading and card components
- Updated environment variables
- Improved layout and metadata handling
- Added new UI components like `Skeleton`, `Label`, and `Input`
- Enhanced client and ecosystem API integration

> The following files were skipped due to too many changes: `apps/wallet-ui/src/lib/chains.ts`, `apps/wallet-ui/src/components/ui/sonner.tsx`, `apps/wallet-ui/src/components/ConnectButton.tsx`, `apps/wallet-ui/src/app/[ecosystem]/login/page.tsx`, `apps/wallet-ui/src/app/[ecosystem]/(ui)/layout.tsx`, `apps/wallet-ui/src/components/ui/badge.tsx`, `apps/wallet-ui/src/components/ui/popover.tsx`, `apps/wallet-ui/src/middleware.ts`, `apps/wallet-ui/src/lib/assets/erc20.ts`, `apps/wallet-ui/src/components/ui/button.tsx`, `apps/wallet-ui/src/components/NftCard.tsx`, `apps/wallet-ui/src/components/NftGallery.tsx`, `apps/wallet-ui/src/lib/auth.ts`, `apps/wallet-ui/src/lib/assets/erc721.ts`, `apps/wallet-ui/src/app/[ecosystem]/(ui)/wallet/[address]/layout.tsx`, `apps/wallet-ui/src/util/simplehash.ts`, `apps/wallet-ui/src/components/ChainCombobox.tsx`, `apps/wallet-ui/src/components/TransactionToastProvider.tsx`, `apps/wallet-ui/src/app/globals.css`, `apps/wallet-ui/src/components/ChainIcon.tsx`, `apps/wallet-ui/src/components/ui/dialog.tsx`, `apps/wallet-ui/src/components/ui/form.tsx`, `apps/wallet-ui/src/components/ui/command.tsx`, `apps/wallet-ui/src/components/NftModal.tsx`, `pnpm-lock.yaml`

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

<!-- end pr-codex -->
2024-08-17 08:08:48 +00:00
gregfromstl 13764f1abb Connect UI Profile Image Improvements (#4126)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on cosmetic improvements to the Connect UI, introduces Blobbie component for unique avatars, and updates button styles and sizes.

### Detailed summary
- Increased `minWidth` of `TransactionButton` to 165px
- Updated `defaultButtonTitle` in `en.ts`
- Added `Blobbie` component for unique avatars
- Updated button styles and sizes in `ConnectButton.tsx`
- Added `shortenAddress` import in `wallet.ts`
- Updated styles in `Details.tsx` and `DetailsModal`

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

<!-- end pr-codex -->
2024-08-17 08:03:28 +00:00
iuwqyir 997c3945a7 export usageEventSchema and UsageEvent (#4147)
## Problem solved

Almost the same as https://github.com/thirdweb-dev/js/pull/4122. Made a new PR, because hadn't setup commit signing for graphite, so it failed to merge

Exports the Zod schema for usage events. This way it can be used for validate the message before sending it to the usage tracker queue.
Moved it under core because it is not tied to worker logic and can be used outside workers.
Also exported `UsageEvent` and `usageEventSchema` under node, because I think core is not exported

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to export `usageEventSchema` for Cloudflare worker and define `UsageEvent` schema for node.

### Detailed summary
- Exported `usageEventSchema` for Cloudflare worker and defined `UsageEvent` schema for node
- Added detailed schema for usage events with various optional fields

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

<!-- end pr-codex -->
2024-08-16 21:15:34 +00:00
MananTank 2bf2e1eec4 Update Engine page header (#4125)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces UI improvements to the Engine page layout, including new components, styling updates, and a modal for updating the Engine version.

### Detailed summary
- Added `CopyTextButton`, `Button`, and `Separator` components
- Updated styling and structure of the Engine page layout
- Implemented a modal for updating the Engine version
- Added new icons and components for UI enhancements

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

<!-- end pr-codex -->
2024-08-16 20:37:12 +00:00
MananTank 9b3725a56e Migrate Engine list UI to shadcn/tailwind (#4123)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR enhances UI components in the dashboard app.

### Detailed summary
- Updated tooltip styling in `tooltip.tsx`.
- Refactored dropdown menu in `dropdown-menu.tsx`.
- Improved table UI with dropdown menus in `TWTable.tsx`.
- Implemented modals for editing and removing engine instances in `engine-instances-table.tsx`.

> The following files were skipped due to too many changes: `apps/dashboard/src/components/engine/engine-instances-table.tsx`

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

<!-- end pr-codex -->
2024-08-16 20:37:11 +00:00
MananTank beafe43f46 Move Engine import from Modal to /dashboard/engine/import (#4119)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to refactor the Engine sidebar component and improve error handling in Engine pages.

### Detailed summary
- Refactored `EngineSidebar` component
- Improved error handling with `PageLoading` and `EngineErrorPage`
- Added import functionality for Engine instances

> The following files were skipped due to too many changes: `apps/dashboard/src/pages/dashboard/engine/import.tsx`

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

<!-- end pr-codex -->
2024-08-16 20:37:11 +00:00
Winston YeoandGitHub 214a74c36a chore: add authentication method to usage tracking (#4146) 2024-08-16 20:17:57 +00:00
MananTank d0e3c4defc Fix hydration error in markdown renderer + property and ts cleanup (#4141)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR refactors the `MarkdownRenderer` component in `published-contract`.

### Detailed summary
- Refactored heading components (`h1`, `h2`, `h3`, `h4`, `h5`, `h6`) to remove explicit `any` type
- Refactored `a`, `code`, `p`, `table`, `th`, `td`, `ul`, `ol`, `li` components to use cleaned props without `node`

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

<!-- end pr-codex -->
2024-08-16 18:30:57 +00:00
MananTank 0cdf34ea98 Move Engine pages from client Router to Pages Router (#4113)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR upgrades Engine navigation links, adds new pages for Engine management, and refactors list styling.

### Detailed summary
- Updated Engine navigation links
- Added new pages for Engine management
- Refactored list styling in components

> The following files were skipped due to too many changes: `apps/dashboard/src/components/engine/engine-instances-table.tsx`, `apps/dashboard/src/pages/dashboard/engine/create.tsx`, `apps/dashboard/src/components/engine/EnginePageLayout.tsx`, `apps/dashboard/src/components/engine/engine-list.tsx`

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

<!-- end pr-codex -->
2024-08-16 15:45:43 +00:00
MananTank b1632a3df1 Add force-dynamic declaration for pages using cookies - fix sentry errors (#4140)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to add a new export `dynamic = "force-dynamic"` in three files due to the usage of `cookies()`.

### Detailed summary
- Added `export const dynamic = "force-dynamic"` in `page.tsx` under `dashboard/connect/pay`
- Added `export const dynamic = "force-dynamic"` in `page.tsx` under `dashboard/connect/ecosystem`
- Added `export const dynamic = "force-dynamic"` in `layout.tsx` under `dashboard/connect/ecosystem/[slug]/(active)`

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

<!-- end pr-codex -->
2024-08-16 15:34:13 +00:00
jnsdls d3f43a757b [Optimize] Add CI optimization step using Graphite action (#4115)
tl;dr: https://graphite.dev/docs/stacking-and-ci#reducing-ci-runs-for-github-actions

<!-- start pr-codex -->

---

## PR-Codex overview
This PR optimizes the CI workflow by adding a step to skip unnecessary processes based on a condition.

### Detailed summary
- Added `optimize_ci` job with skip condition
- Updated `build`, `lint`, `test`, `e2e`, and `size` jobs to depend on `optimize_ci` and skip if condition is met

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

<!-- end pr-codex -->
2024-08-15 21:22:24 +00:00
gregfromstl 825352476c fix: throw when no encryption key (#4120)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing security by throwing an error when no encryption key is provided for custom authentication.

### Detailed summary
- Added error throwing when encryption key is missing for custom JWT authentication
- Added error throwing when encryption key is missing for custom auth endpoint authentication

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

<!-- end pr-codex -->
2024-08-15 21:17:20 +00:00
arcoraven 8caa095a67 fix: update transaction drawer labels (#4118)
<!-- start pr-codex -->

## PR-Codex overview
The focus of this PR is to enhance transaction details display in the dashboard by adding support for effective gas price calculation and showing additional information like account address and block number.

### Detailed summary
- Added `effectiveGasPrice` field to calculate transaction fee
- Displayed account address and linked to explorer
- Differentiated recipient address and contract address
- Showed transaction value in tokens and symbol
- Linked block number to explorer

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

<!-- end pr-codex -->
2024-08-15 19:22:51 +00:00
jnsdls 997e1197e9 analytics overhaul (#4107)
<!-- start pr-codex -->

## PR-Codex overview
This PR enhances blockchain analytics in the dashboard. It introduces support for analytics on specific chains and updates chart components for better visualization.

### Detailed summary
- Added `CHAINSAW_API_KEY` environment variable for contract analytics
- Updated chart components for better visualization
- Improved analytics support for specific chains
- Refactored chart styling for better UI/UX

> The following files were skipped due to too many changes: `apps/dashboard/src/contract-ui/tabs/overview/components/Analytics.tsx`, `apps/dashboard/src/contract-ui/tabs/analytics/page.tsx`

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

<!-- end pr-codex -->
2024-08-15 15:29:50 +00:00
Jonas DanielsandGitHub c9ac25fe3f Version Packages (#4114) 2024-08-15 07:38:54 -07:00
Wyatt MufsonandGitHub 79260c8cd0 Add new auth fields for redirect (#4099) 2024-08-15 07:30:59 -07:00
MananTank cf8badbc46 Move Network Configuration Form+Modal to shadcn/tailwind (#4105)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates various UI components for consistency and usability.

### Detailed summary
- Updated styling for `ClaimConditionsForm`, `CustomChainRenderer`, `ToolTipLabel`, `RpcInput`, `ChainIdInput`, `TooltipBox`, `ConfigureNetworkModal`, `ResetClaimEligibility`, `NetworkIdInput`, `IconUpload`, and `ConfigureNetworks`.
- Replaced `TooltipBox` with `ToolTipLabel` and `CircleHelpIcon`.
- Added `Input` from `@/components/ui/input`.
- Removed unnecessary imports.
- Replaced `Modal` components with `Dialog` and `DialogContent`.
- Used `toast` for error notifications.
- Updated button styling and functionality.

> The following files were skipped due to too many changes: `apps/dashboard/src/components/configure-networks/ConfigureNetworks.tsx`, `apps/dashboard/src/pages/[chain_id]/[...paths].tsx`, `apps/dashboard/src/components/configure-networks/ConfigureNetworkForm.tsx`

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

<!-- end pr-codex -->
2024-08-15 05:59:21 +00:00
MananTank 09e4d80b89 Show Top Progress Bar on navigation initiated using useRouter hook (#4097)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates Next.js navigation usage in the dashboard app to `useDashboardRouter` from `@/lib/DashboardRouter`.

### Detailed summary
- Replaces `useRouter` with `useDashboardRouter` in multiple files
- Adds `DashboardRouterTopProgressBar` component import in layout files
- Introduces `useDashboardRouter` import in various components
- Defines `createStore` function in `@/lib/reactive.ts`

> The following files were skipped due to too many changes: `apps/dashboard/src/@/lib/DashboardRouter.tsx`

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

<!-- end pr-codex -->
2024-08-15 05:48:22 +00:00
kien-ngo 1edd6c7e4c embed-setup.tsx to v5 (#4108)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
The focus of this PR is to enhance the Contract Embed feature by integrating with `thirdwebClient` and `useV5DashboardChain`.

### Detailed summary
- Added `thirdwebClient` and `useV5DashboardChain` for contract setup
- Updated `contract` type to `ThirdwebContract`
- Refactored `buildIframeSrc` function to use `ThirdwebContract` properties

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

<!-- end pr-codex -->
2024-08-15 05:37:15 +00:00
kien-ngo 588f19639f [Dashboard] ContractMetadata settings to v5 (#4100)
<!-- start pr-codex -->

## PR-Codex overview
This PR updates the SettingsMetadata component in the contract UI to handle contract data more efficiently.

### Detailed summary
- Refactored the SettingsMetadata component to use ThirdwebContract type
- Updated schema imports and data handling
- Removed unnecessary dependencies and functions

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

<!-- end pr-codex -->
2024-08-15 05:26:34 +00:00
623 changed files with 18647 additions and 10849 deletions
+22 -1
View File
@@ -19,7 +19,21 @@ env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
jobs:
optimize_ci:
runs-on: ubuntu-latest
outputs:
skip: ${{ steps.check_skip.outputs.skip }}
steps:
- name: Optimize CI
id: check_skip
uses: withgraphite/graphite-ci-action@main
with:
graphite_token: ${{ secrets.GRAPHITE_OMTIMIZE_TOKEN }}
build:
needs: optimize_ci
if: needs.optimize_ci.outputs.skip == 'false'
runs-on: ubuntu-latest
name: Build Packages
steps:
@@ -33,6 +47,8 @@ jobs:
run: pnpm build:packages
lint:
needs: optimize_ci
if: needs.optimize_ci.outputs.skip == 'false'
timeout-minutes: 15
name: Lint Packages
runs-on: ubuntu-latest
@@ -51,6 +67,8 @@ jobs:
- run: pnpm lint
test:
needs: optimize_ci
if: needs.optimize_ci.outputs.skip == 'false'
timeout-minutes: 15
name: Unit Tests
runs-on: ubuntu-latest
@@ -77,6 +95,8 @@ jobs:
verbose: true
e2e:
needs: optimize_ci
if: needs.optimize_ci.outputs.skip == 'false'
timeout-minutes: 15
name: E2E Tests
runs-on: ubuntu-latest
@@ -144,8 +164,9 @@ jobs:
fi
size:
needs: optimize_ci
# only run on pull requests
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' && needs.optimize_ci.outputs.skip == 'false'
timeout-minutes: 15
name: "Size"
runs-on: ubuntu-latest
+19
View File
@@ -27,7 +27,20 @@ env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
jobs:
optimize_ci:
runs-on: ubuntu-latest
outputs:
skip: ${{ steps.check_skip.outputs.skip }}
steps:
- name: Optimize CI
id: check_skip
uses: withgraphite/graphite-ci-action@main
with:
graphite_token: ${{ secrets.GRAPHITE_OMTIMIZE_TOKEN }}
build:
needs: optimize_ci
if: needs.optimize_ci.outputs.skip == 'false'
runs-on: ubuntu-latest
name: Build Packages
steps:
@@ -41,6 +54,8 @@ jobs:
run: pnpm build:legacy_packages
lint:
needs: optimize_ci
if: needs.optimize_ci.outputs.skip == 'false'
timeout-minutes: 15
name: Lint Packages
runs-on: ubuntu-latest
@@ -54,6 +69,8 @@ jobs:
- run: pnpm lint:legacy
test:
needs: optimize_ci
if: needs.optimize_ci.outputs.skip == 'false'
timeout-minutes: 15
name: Unit Tests
runs-on: ubuntu-latest
@@ -74,6 +91,8 @@ jobs:
verbose: true
e2e:
needs: optimize_ci
if: needs.optimize_ci.outputs.skip == 'false'
timeout-minutes: 15
name: E2E Tests
runs-on: ubuntu-latest
+3
View File
@@ -30,3 +30,6 @@ sitemap*.xml
# typedoc
legacy_packages/*/typedoc/*
packages/*/typedoc/*
*storybook.log
storybook-static
+3
View File
@@ -40,6 +40,9 @@ NEXT_PUBLIC_STRIPE_KEY=
NEXT_PUBLIC_STRIPE_PAYMENT_METHOD_CFG_ID=
# Needed for contract analytics / blockchain data information
CHAINSAW_API_KEY=
#
# Private (server)
#
+7
View File
@@ -5,6 +5,7 @@ module.exports = {
"plugin:import/typescript",
"plugin:@next/next/recommended",
"plugin:promise/recommended",
"plugin:storybook/recommended"
],
rules: {
"react-compiler/react-compiler": "error",
@@ -80,6 +81,12 @@ module.exports = {
message:
"Import from `@chakra-ui/react` instead of `@chakra-ui/menu`.",
},
{
name: "next/navigation",
importNames: ["useRouter"],
message:
'Use `import { useDashboardRouter } from "@/lib/DashboardRouter";` instead',
},
],
},
],
+32
View File
@@ -0,0 +1,32 @@
import type { StorybookConfig } from "@storybook/nextjs";
import { dirname, join } from "node:path";
/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string): string {
return dirname(require.resolve(join(value, "package.json")));
}
const config: StorybookConfig = {
stories: ["../src/**/*.stories.tsx"],
addons: [
getAbsolutePath("@storybook/addon-onboarding"),
getAbsolutePath("@storybook/addon-links"),
getAbsolutePath("@storybook/addon-essentials"),
getAbsolutePath("@chromatic-com/storybook"),
getAbsolutePath("@storybook/addon-interactions"),
],
framework: {
name: getAbsolutePath("@storybook/nextjs"),
options: {},
},
refs: {
"@chakra-ui/react": {
disable: true,
},
},
staticDirs: ["../public"],
};
export default config;
+37
View File
@@ -0,0 +1,37 @@
import type { Preview } from "@storybook/react";
import "@/styles/globals.css";
import { Inter as interFont } from "next/font/google";
// biome-ignore lint/correctness/noUnusedImports: <explanation>
import React from "react";
import { cn } from "../src/@/lib/utils";
// Note: Wrapping the Stoy with AppRouterProviders makes the storybook server time SUPER SLOW
// so let's just not have it there - all stories should be independent from context anyway and just rely on props
const fontSans = interFont({
subsets: ["latin"],
variable: "--font-sans",
});
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
decorators: [
(Story) => {
return (
<div className={cn("font-sans antialiased", fontSans.variable)}>
<Story />
</div>
);
},
],
};
export default preview;
+2 -2
View File
@@ -5,8 +5,8 @@ const ContentSecurityPolicy = `
img-src * data: blob:;
media-src * data: blob:;
object-src 'none';
style-src 'self' 'unsafe-inline';
font-src 'self';
style-src 'self' 'unsafe-inline' https://vercel.live;
font-src 'self' https://vercel.live https://assets.vercel.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 pg.paper.xyz portal.usecontext.io;
connect-src * data: blob:;
+17 -15
View File
@@ -18,7 +18,9 @@
"build:analyze": "ANALYZE=true pnpm run build",
"knip": "knip",
"playwright": "playwright test",
"update-checkly": "npx checkly deploy"
"update-checkly": "npx checkly deploy",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@chakra-ui/react": "^2.8.2",
@@ -45,12 +47,10 @@
"@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "1.1.2",
"@react-icons/all-files": "^4.1.0",
"@sentry/nextjs": "^8.8.0",
"@sentry/nextjs": "^8.26.0",
"@stripe/react-stripe-js": "^2.7.3",
"@stripe/stripe-js": "^3.5.0",
"@tanstack/query-async-storage-persister": "^4.36.1",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-query-persist-client": "^4.36.1",
"@tanstack/react-table": "^8.17.3",
"@thirdweb-dev/react": "workspace:*",
"@thirdweb-dev/sdk": "workspace:*",
@@ -68,7 +68,6 @@
"flat": "^6.0.1",
"framer-motion": "^11.2.12",
"fuse.js": "7.0.0",
"idb-keyval": "^6.2.1",
"ioredis": "^5.4.1",
"ipaddr.js": "^2.2.0",
"lottie-react": "^2.4.0",
@@ -82,13 +81,11 @@
"pluralize": "^8.0.0",
"posthog-js": "1.128.3",
"posthog-js-opensource": "npm:[email protected]",
"prettier": "^3.3.2",
"prism-react-renderer": "^2.3.1",
"prismjs": "^1.29.0",
"qrcode": "^1.5.3",
"react": "18.3.1",
"react-children-utilities": "^2.10.0",
"react-confetti": "^6.1.0",
"react-day-picker": "^8.10.1",
"react-dom": "18.3.1",
"react-dropzone": "^14.2.3",
@@ -106,7 +103,7 @@
"sonner": "^1.5.0",
"spdx-correct": "^3.2.0",
"swagger-ui-react": "^5.17.14",
"tailwind-merge": "^2.4.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"thirdweb": "workspace:*",
"tiny-invariant": "^1.3.3",
@@ -117,9 +114,18 @@
},
"devDependencies": {
"@chakra-ui/cli": "^2.4.1",
"@chromatic-com/storybook": "^1.5.0",
"@next/bundle-analyzer": "^14.2.4",
"@next/eslint-plugin-next": "^14.2.4",
"@playwright/test": "1.44.1",
"@storybook/addon-essentials": "^8.2.9",
"@storybook/addon-interactions": "^8.2.9",
"@storybook/addon-links": "^8.2.9",
"@storybook/addon-onboarding": "^8.2.9",
"@storybook/blocks": "^8.2.9",
"@storybook/nextjs": "^8.2.9",
"@storybook/react": "^8.2.9",
"@storybook/test": "^8.2.9",
"@types/color": "^3.0.6",
"@types/node": "20.14.9",
"@types/papaparse": "^5.3.14",
@@ -134,21 +140,17 @@
"@typescript-eslint/parser": "7.14.1",
"autoprefixer": "^10.4.19",
"checkly": "^4.7.0",
"dotenv": "^16.4.5",
"eslint": "8.57.0",
"eslint-config-biome": "^1.7.3",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-inclusive-language": "^2.2.1",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-promise": "^6.2.0",
"eslint-plugin-react": "7.34.3",
"eslint-plugin-react-compiler": "0.0.0-experimental-51a85ea-20240601",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-storybook": "^0.8.0",
"knip": "^5.23.1",
"next-sitemap": "^4.2.3",
"next-unused": "^0.0.6",
"tailwindcss": "^3.4.4",
"storybook": "^8.2.9",
"tailwindcss": "^3.4.10",
"typescript": "5.5.4"
},
"next-unused": {
@@ -1,5 +1,28 @@
{
"applinks": {},
"applinks": {
"apps": ["XYHGSFAMHX.com.thirdweb.demo"],
"details": [
{
"appIDs": ["XYHGSFAMHX.com.thirdweb.demo"],
"components": [
{
"#": "no_universal_links",
"exclude": true,
"comment": "Matches any URL with a fragment that equals no_universal_links and instructs the system not to open it as a universal link."
},
{
"/": "/mobile-wallet-protocol",
"comment": "Matches any URL with a path that starts with /mobile-wallet-protocol for coinbase wallet mobile redirects."
},
{
"/": "/dashboard/*",
"exclude": true,
"comment": "no universal link for dashboard"
}
]
}
]
},
"webcredentials": {
"apps": ["XYHGSFAMHX.com.thirdweb.demo"]
},
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

+9 -9
View File
@@ -119,11 +119,6 @@ function redirects() {
destination: "/template/erc721",
permanent: false,
},
{
source: "/abuse",
destination: "https://forms.gle/v9UJtHbVw8fXypcd7",
permanent: false,
},
{
source: "/create-api-key",
destination: "/dashboard/settings/api-keys",
@@ -136,12 +131,17 @@ function redirects() {
},
{
source: "/dashboard/connect",
destination: "/dashboard/connect/playground",
destination: "/dashboard/connect/in-app-wallets",
permanent: false,
},
{
source: "/dashboard/connect/playground",
destination: "https://playground.thirdweb.com/connect/sign-in/button",
permanent: false,
},
{
source: "/dashboard/wallet",
destination: "/dashboard/connect/playground",
destination: "https://playground.thirdweb.com/connect/sign-in/button",
permanent: false,
},
{
@@ -166,7 +166,7 @@ function redirects() {
},
{
source: "/dashboard/wallets",
destination: "/dashboard/connect/playground",
destination: "https://playground.thirdweb.com/connect/sign-in/button",
permanent: false,
},
{
@@ -186,7 +186,7 @@ function redirects() {
},
{
source: "/dashboard/wallets/connect",
destination: "/dashboard/connect/playground",
destination: "https://playground.thirdweb.com/connect/sign-in/button",
permanent: false,
},
{
@@ -0,0 +1,109 @@
"use client";
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
type ChartConfig,
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { useMemo } from "react";
export type ThirdwebAreaChartProps<TConfig extends ChartConfig> = {
// metadata
title: string;
description?: string;
// chart config
config: TConfig;
data: Array<Record<keyof TConfig, number> & { time: number | string | Date }>;
showLegend?: boolean;
// chart className
chartClassName?: string;
};
export function ThirdwebAreaChart<TConfig extends ChartConfig>(
props: ThirdwebAreaChartProps<TConfig>,
) {
const configKeys = useMemo(() => Object.keys(props.config), [props.config]);
return (
<Card>
<CardHeader>
<CardTitle>{props.title}</CardTitle>
{props.description && (
<CardDescription>{props.description}</CardDescription>
)}
</CardHeader>
<CardContent>
<ChartContainer config={props.config} className={props.chartClassName}>
<AreaChart
accessibilityLayer
data={props.data}
margin={{
left: 12,
right: 12,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="time"
tickLine={false}
axisLine={false}
tickMargin={8}
tickFormatter={(value) => new Date(value).toLocaleDateString()}
/>
<ChartTooltip cursor={false} content={<ChartTooltipContent />} />
<defs>
{configKeys.map((key) => (
<linearGradient
key={key}
id={`fill_${key}`}
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="5%"
stopColor={`var(--color-${key})`}
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor={`var(--color-${key})`}
stopOpacity={0.1}
/>
</linearGradient>
))}
</defs>
{configKeys.map((key) => (
<Area
key={key}
dataKey={key}
type="natural"
fill={`url(#fill_${key})`}
fillOpacity={0.4}
stroke={`var(--color-${key})`}
stackId="a"
/>
))}
{props.showLegend && (
<ChartLegend content={<ChartLegendContent />} />
)}
</AreaChart>
</ChartContainer>
</CardContent>
</Card>
);
}
@@ -0,0 +1,91 @@
"use client";
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
type ChartConfig,
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { useMemo } from "react";
export type ThirdwebBarChartProps<TConfig extends ChartConfig> = {
// metadata
title: string;
description?: string;
// chart config
config: TConfig;
data: Array<Record<keyof TConfig, number> & { time: number | string | Date }>;
showLegend?: boolean;
variant?: "stacked" | "grouped";
// chart className
chartClassName?: string;
};
export function ThirdwebBarChart<TConfig extends ChartConfig>(
props: ThirdwebBarChartProps<TConfig>,
) {
const configKeys = useMemo(() => Object.keys(props.config), [props.config]);
// if there are more than 4 keys then we should stack them by default
const variant =
props.variant || configKeys.length > 4 ? "stacked" : "grouped";
return (
<Card>
<CardHeader>
<CardTitle>{props.title}</CardTitle>
{props.description && (
<CardDescription>{props.description}</CardDescription>
)}
</CardHeader>
<CardContent>
<ChartContainer config={props.config} className={props.chartClassName}>
<BarChart accessibilityLayer data={props.data}>
<CartesianGrid vertical={false} />
<XAxis
dataKey="time"
tickLine={false}
axisLine={false}
tickMargin={10}
tickFormatter={(value) => new Date(value).toLocaleDateString()}
/>
<ChartTooltip content={<ChartTooltipContent hideLabel />} />
{props.showLegend && (
<ChartLegend content={<ChartLegendContent />} />
)}
{configKeys.map((key, idx) => (
<Bar
key={key}
dataKey={key}
// if stacked then they should all be the same stackId
// if grouped then they should all be unique stackId (so the key works great)
stackId={variant === "stacked" ? "a" : key}
fill={`var(--color-${key})`}
// if stacked then we need to figure out the radius based on the index in the array
// if grouped then we can just use the same radius for all
radius={
variant === "stacked"
? idx === 0
? [0, 0, 4, 4]
: idx === configKeys.length - 1
? [4, 4, 0, 0]
: [0, 0, 0, 0]
: [4, 4, 4, 4]
}
/>
))}
</BarChart>
</ChartContainer>
</CardContent>
</Card>
);
}
@@ -4,7 +4,6 @@ import { Button } from "@/components/ui/button";
import { ClientOnly } from "components/ClientOnly/ClientOnly";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import * as React from "react";
import { Skeleton } from "./ui/skeleton";
export function ColorModeToggle() {
@@ -1,5 +1,4 @@
import { Highlight } from "prism-react-renderer";
import React from "react";
import { cn } from "../../lib/utils";
import { CopyButton } from "./CopyButton";
import { Card } from "./card";
@@ -34,7 +34,7 @@ export function DynamicHeight(props: {
);
}
export function useHeightObserver() {
function useHeightObserver() {
const elementRef = useRef<HTMLDivElement>(null);
const [height, setHeight] = useState<number | undefined>();
@@ -0,0 +1,25 @@
.unorderedList,
.orderedList {
@apply pl-5 leading-relaxed list-none text-secondary-foreground;
}
.unorderedList > li {
@apply pb-1 pl-2 list-disc;
}
.orderedList > li {
@apply pb-1 pl-2 list-decimal;
}
.unorderedList > li::marker {
@apply text-muted-foreground !m-0 text-left;
}
.orderedList > li::marker {
@apply text-muted-foreground text-left;
}
.unorderedList > li:hover::marker,
.orderedList > li:hover::marker {
@apply text-foreground;
}
@@ -0,0 +1,9 @@
import styles from "./List.module.css";
export function UnorderedList(props: { children?: React.ReactNode }) {
return <ul className={styles.unorderedList}>{props.children}</ul>;
}
export function OrderedList(props: { children?: React.ReactNode }) {
return <ul className={styles.orderedList}>{props.children}</ul>;
}
+1 -1
View File
@@ -9,7 +9,7 @@ const Card = React.forwardRef<
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
"rounded-lg border border-border bg-card text-card-foreground shadow-sm",
className,
)}
{...props}
+1 -1
View File
@@ -283,7 +283,7 @@ const ChartLegendContent = React.forwardRef<
<div
ref={ref}
className={cn(
"flex items-center justify-center gap-4",
"flex items-center justify-center gap-4 overflow-x-auto",
verticalAlign === "top" ? "pb-3" : "pt-3",
className,
)}
@@ -47,7 +47,7 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
@@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
@@ -31,7 +31,7 @@ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
export function ToolTipLabel(props: {
children: React.ReactNode;
label: string | undefined;
label: React.ReactNode;
rightIcon?: React.ReactNode;
leftIcon?: React.ReactNode;
}) {
@@ -47,7 +47,7 @@ export function ToolTipLabel(props: {
</TooltipTrigger>
<TooltipContent
sideOffset={10}
className="max-w-[300px] leading-relaxed"
className="max-w-[400px] leading-relaxed whitespace-normal"
>
<div className="p-2 text-sm flex items-center gap-1.5">
{props.leftIcon}
-5
View File
@@ -8,10 +8,5 @@ const LOGGED_IN_ONLY_PATHS = [
];
export function isLoginRequired(pathname: string) {
// exceptions from LOGGED_IN_ONLY_PATHS
if (pathname.startsWith("/dashboard/connect/playground")) {
return false;
}
return LOGGED_IN_ONLY_PATHS.some((path) => pathname.startsWith(path));
}
+28 -9
View File
@@ -1,11 +1,30 @@
import {
DASHBOARD_THIRDWEB_CLIENT_ID,
DASHBOARD_THIRDWEB_SECRET_KEY,
IPFS_GATEWAY_URL,
} from "@/constants/env";
import { createThirdwebClient } from "thirdweb";
import { DASHBOARD_THIRDWEB_CLIENT_ID, IPFS_GATEWAY_URL } from "./env";
export const thirdwebClient = createThirdwebClient({
clientId: DASHBOARD_THIRDWEB_CLIENT_ID,
config: {
storage: {
gatewayUrl: IPFS_GATEWAY_URL,
},
},
});
export const thirdwebClient = createThirdwebClient(
DASHBOARD_THIRDWEB_SECRET_KEY
? {
secretKey: DASHBOARD_THIRDWEB_SECRET_KEY,
config: {
storage: {
gatewayUrl: IPFS_GATEWAY_URL,
},
},
}
: {
clientId: DASHBOARD_THIRDWEB_CLIENT_ID,
config: {
storage: {
gatewayUrl: IPFS_GATEWAY_URL,
},
},
},
);
/**
* DO NOT ADD ANYTHING TO THIS FILE IF YOU ARE NOT ABSOLUTELY SURE IT IS OK
*/
+6
View File
@@ -10,3 +10,9 @@ export const IPFS_GATEWAY_URL =
export const THIRDWEB_ENGINE_FAUCET_WALLET =
process.env.NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET || "";
export const isProd =
(process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV) ===
"production";
export const PROD_OR_DEV_URL = isProd ? "thirdweb.com" : "thirdweb-dev.com";
@@ -0,0 +1,139 @@
"use client";
// eslint-disable-next-line no-restricted-imports
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import {
Suspense,
useEffect,
useMemo,
useRef,
useState,
useSyncExternalStore,
} from "react";
import { createStore } from "./reactive";
// Using useDashboardRouter instead of useRouter gives us a nice progress bar on top of the page when navigating using router.push or router.replace
// using a store instead of context to avoid triggering re-renders on root component
export const LoadingRouteHref = createStore<string | undefined>(undefined);
export function useDashboardRouter() {
const router = useRouter();
return useMemo(() => {
return {
...router,
replace(href: string, options?: { scroll?: boolean }) {
LoadingRouteHref.setValue(href);
router.replace(href, options);
},
push(href: string, options?: { scroll?: boolean }) {
LoadingRouteHref.setValue(href);
router.push(href, options);
},
};
}, [router]);
}
function useRouterLoadingStatus() {
const loadingHref = useSyncExternalStore(
LoadingRouteHref.subscribe,
LoadingRouteHref.getValue,
LoadingRouteHref.getValue,
);
const pathname = usePathname();
const searchParams = useSearchParams();
const searchParamsStr = searchParams?.toString();
const routerHref = pathname + (searchParamsStr ? `?${searchParamsStr}` : "");
const isLoading = loadingHref && loadingHref !== routerHref;
// reset loading on route load
// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
if (!isLoading) {
LoadingRouteHref.setValue(undefined);
}
}, [isLoading]);
return isLoading;
}
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Render this on root
function DashboardRouterTopProgressBarInner() {
const isLoading = useRouterLoadingStatus();
const [progress, setProgress] = useState(0);
const progressStartedRef = useRef(false);
// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
if (!isLoading) {
setProgress(0);
progressStartedRef.current = false;
return;
}
if (progressStartedRef.current) {
return;
}
let isMounted = true;
progressStartedRef.current = true;
async function updateProgressBar(progress: number, delay: number) {
if (!isMounted) {
if (!isLoading) {
setProgress(100);
}
return;
}
setProgress(progress);
await wait(delay);
// increase progress by 10% of the remaining progress
// increase the delay by 5% to slow down number of updates for slower progress
updateProgressBar(progress + (100 - progress) / 10, delay * 1.05);
}
async function startEffect() {
// if the loading state remains for atleast 500ms start the progress bar
await wait(500);
if (isMounted) {
updateProgressBar(0, 100);
}
}
startEffect();
return () => {
isMounted = false;
};
}, [isLoading]);
const width = isLoading ? progress : 100;
return (
<span
className="block fixed top-0 h-[2px] bg-blue-500"
style={{
opacity: isLoading ? "100" : "0",
width: `${width}%`,
transition: width === 0 ? "none" : "width 0.2s ease, opacity 0.3s ease",
zIndex: "100000000",
pointerEvents: "none",
}}
/>
);
}
// need to wrap with suspense because of useSearchParams usage
export function DashboardRouterTopProgressBar() {
return (
<Suspense fallback={null}>
<DashboardRouterTopProgressBarInner />
</Suspense>
);
}
+47
View File
@@ -0,0 +1,47 @@
export type Store<T> = {
getValue(): T;
setValue(newValue: T): void;
subscribe(listener: () => void): () => void;
};
/**
* Create a reactive value store
* @param initialValue - The initial value to store
* @example
* ```ts
* const store = createStore(0);
* ```
* @returns A store object
* @internal
*/
export function createStore<T>(initialValue: T): Store<T> {
type Listener = () => void;
const listeners = new Set<Listener>();
let value = initialValue;
const notify = () => {
for (const listener of listeners) {
listener();
}
};
return {
getValue() {
return value;
},
setValue(newValue: T) {
if (newValue === value) {
return;
}
value = newValue;
notify();
},
subscribe(listener: Listener) {
listeners.add(listener);
return () => {
listeners.delete(listener);
};
},
};
}
+15 -10
View File
@@ -3,7 +3,8 @@
@tailwind utilities;
@layer base {
:root {
:root,
[data-theme="light"] {
/* bg - neutral */
--background: 0 0% 100%;
--secondary: 0 0% 97%;
@@ -44,7 +45,8 @@
--radius: 0.5rem;
}
.dark {
.dark,
[data-theme="dark"] {
/* bg - neutral */
--background: 0 0% 4.5%;
--secondary: 0 0% 6.5%;
@@ -94,19 +96,22 @@
/* shadcn charts */
@layer base {
:root {
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}
.dark {
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
/* below is ai generated based on the initial */
--chart-6: 200 65% 50%;
--chart-7: 120 70% 45%;
--chart-8: 45 75% 55%;
--chart-9: 260 60% 60%;
--chart-10: 0 70% 50%;
--chart-11: 180 60% 50%;
--chart-12: 100 65% 55%;
--chart-13: 360 70% 55%;
--chart-14: 240 65% 50%;
--chart-15: 80 60% 50%;
}
}
@@ -3,12 +3,16 @@
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Button } from "@/components/ui/button";
import { thirdwebClient } from "@/constants/client";
import { useSupportedChains } from "@thirdweb-dev/react";
import { getSDKTheme } from "app/components/sdk-component-theme";
import { CustomChainRenderer } from "components/selects/CustomChainRenderer";
import { mapV4ChainToV5Chain } from "contexts/map-chains";
import { useTrack } from "hooks/analytics/useTrack";
import { useSupportedChains } from "hooks/chains/configureChains";
import {
useAddRecentlyUsedChainId,
useRecentlyUsedChains,
} from "hooks/chains/recentlyUsedChains";
import { useSetIsNetworkConfigModalOpen } from "hooks/networkConfigModal";
import { useTheme } from "next-themes";
import Image from "next/image";
import Link from "next/link";
@@ -16,10 +20,6 @@ import { usePathname } from "next/navigation";
import { useCallback, useMemo } from "react";
import type { Chain } from "thirdweb";
import { AutoConnect, ConnectButton, useConnectModal } from "thirdweb/react";
import { getSDKTheme } from "../../../../app/components/sdk-component-theme";
import { CustomChainRenderer } from "../../../../components/selects/CustomChainRenderer";
import { mapV4ChainToV5Chain } from "../../../../contexts/map-chains";
import { useSetIsNetworkConfigModalOpen } from "../../../../hooks/networkConfigModal";
import { useFavoriteChains } from "../../hooks/useFavoriteChains";
import { useLoggedInUser } from "../../hooks/useLoggedInUser";
import { popularChains } from "../popularChains";
@@ -1,56 +1,70 @@
import { useContract, useContractEvents } from "@thirdweb-dev/react";
import type { ContractEvent } from "@thirdweb-dev/sdk";
import { type AbiEvent, formatAbiItem } from "abitype";
import { useMemo } from "react";
import {
type PreparedEvent,
type ThirdwebContract,
prepareEvent,
} from "thirdweb";
import { useContractEvents } from "thirdweb/react";
import { useResolveContractAbi } from "./useResolveContractAbi";
interface InternalTransaction {
transactionHash: ContractEvent["transaction"]["transactionHash"];
blockNumber: ContractEvent["transaction"]["blockNumber"];
events: ContractEvent[];
export interface InternalTransaction {
transactionHash: string;
blockNumber: bigint;
events: Array<{
logIndex: number;
address: string;
eventName: string;
args: readonly unknown[] | Record<string, unknown>;
}>;
}
export function useActivity(contractAddress?: string, autoUpdate?: boolean) {
// const provider = useProvider();
const contractQuery = useContract(contractAddress);
export function useActivity(contract: ThirdwebContract, autoUpdate?: boolean) {
const abiQuery = useResolveContractAbi(contract);
const eventsQuery = useContractEvents(contractQuery.contract, undefined, {
subscribe: autoUpdate,
queryFilter: {
fromBlock: -20000,
},
// Get all the PreprareEvents from the contract abis
const events: PreparedEvent<AbiEvent>[] = useMemo(() => {
const eventsItems = (abiQuery.data || []).filter((o) => o.type === "event");
const eventSignatures = eventsItems.map((event) => formatAbiItem(event));
return eventSignatures.map((signature) =>
prepareEvent({ signature: signature as `event ${string}` }),
);
}, [abiQuery.data]);
const eventsQuery = useContractEvents({
contract,
events,
blockRange: 20000,
watch: autoUpdate,
});
return useMemo(() => {
if (!eventsQuery.data) {
return [];
}
const obj = eventsQuery.data.slice(0, 100).reduce(
(acc, curr) => {
if (acc[curr.transaction.transactionHash]) {
acc[curr.transaction.transactionHash].events.push(curr);
acc[curr.transaction.transactionHash].events.sort(
// biome-ignore lint/suspicious/noExplicitAny: FIXME
(a: any, b: any) => b.transaction.logIndex - a.transaction.logIndex,
if (acc[curr.transactionHash]) {
acc[curr.transactionHash].events.push(curr);
acc[curr.transactionHash].events.sort(
(a, b) => b.logIndex - a.logIndex,
);
if (
acc[curr.transaction.transactionHash].blockNumber >
curr.transaction.blockNumber
) {
acc[curr.transaction.transactionHash].blockNumber =
curr.transaction.blockNumber;
if (acc[curr.transactionHash].blockNumber > curr.blockNumber) {
acc[curr.transactionHash].blockNumber = curr.blockNumber;
}
} else {
acc[curr.transaction.transactionHash] = {
transactionHash: curr.transaction.transactionHash,
blockNumber: curr.transaction.blockNumber,
acc[curr.transactionHash] = {
transactionHash: curr.transactionHash,
blockNumber: curr.blockNumber,
events: [curr],
};
}
return acc;
},
{} as Record<string, InternalTransaction>,
);
return Object.values(obj).sort((a, b) => b.blockNumber - a.blockNumber);
return Object.values(obj).sort((a, b) =>
a.blockNumber > b.blockNumber ? -1 : 1,
);
}, [eventsQuery.data]);
}
@@ -73,7 +73,6 @@ export type Account = {
interface UpdateAccountInput {
name?: string;
email?: string;
plan?: AccountPlan;
linkWallet?: boolean;
subscribeToUpdates?: boolean;
onboardSkipped?: boolean;
@@ -271,9 +270,9 @@ interface UseAccountInput {
export function useAccount({ refetchInterval }: UseAccountInput = {}) {
const { user, isLoggedIn } = useLoggedInUser();
return useQuery(
accountKeys.me(user?.address as string),
async () => {
return useQuery({
queryKey: accountKeys.me(user?.address as string),
queryFn: async () => {
const res = await fetch(`${THIRDWEB_API_HOST}/v1/account/me`, {
method: "GET",
headers: {
@@ -288,19 +287,17 @@ export function useAccount({ refetchInterval }: UseAccountInput = {}) {
return json.data as Account;
},
{
enabled: !!user?.address && isLoggedIn,
refetchInterval: refetchInterval ?? false,
},
);
enabled: !!user?.address && isLoggedIn,
refetchInterval: refetchInterval ?? false,
});
}
export function useAccountUsage() {
const { user, isLoggedIn } = useLoggedInUser();
return useQuery(
accountKeys.usage(user?.address as string),
async () => {
return useQuery({
queryKey: accountKeys.usage(user?.address as string),
queryFn: async () => {
const res = await fetch(`${THIRDWEB_API_HOST}/v1/account/usage`, {
method: "GET",
headers: {
@@ -315,16 +312,16 @@ export function useAccountUsage() {
return json.data as UsageBillableByService;
},
{ enabled: !!user?.address && isLoggedIn },
);
enabled: !!user?.address && isLoggedIn,
});
}
export function useAccountCredits() {
const { user, isLoggedIn } = useLoggedInUser();
return useQuery(
accountKeys.credits(user?.address as string),
async () => {
return useQuery({
queryKey: accountKeys.credits(user?.address as string),
queryFn: async () => {
const res = await fetch(`${THIRDWEB_API_HOST}/v1/account/credits`, {
method: "GET",
@@ -347,16 +344,19 @@ export function useAccountCredits() {
return credits;
},
{ enabled: !!user?.address && isLoggedIn },
);
enabled: !!user?.address && isLoggedIn,
});
}
export function useWalletStats(clientId: string | undefined) {
const { user, isLoggedIn } = useLoggedInUser();
return useQuery(
accountKeys.walletStats(user?.address as string, clientId as string),
async () => {
return useQuery({
queryKey: accountKeys.walletStats(
user?.address as string,
clientId as string,
),
queryFn: async () => {
const res = await fetch(
`${THIRDWEB_API_HOST}/v1/account/wallets?clientId=${clientId}`,
{
@@ -375,8 +375,8 @@ export function useWalletStats(clientId: string | undefined) {
return json.data as WalletStats;
},
{ enabled: !!clientId && !!user?.address && isLoggedIn },
);
enabled: !!clientId && !!user?.address && isLoggedIn,
});
}
export function useUpdateAccount() {
@@ -419,7 +419,7 @@ export function useUpdateAccountPlan(waitForWebhook?: boolean) {
const queryClient = useQueryClient();
return useMutationWithInvalidate(
async (input: { plan: string; feedback?: string; useTrial?: boolean }) => {
async (input: { plan: string; feedback?: string }) => {
invariant(user?.address, "walletAddress is required");
const res = await fetch(`${THIRDWEB_API_HOST}/v1/account/plan`, {
@@ -495,9 +495,9 @@ export function useUpdateNotifications() {
export function useCreateBillingSession(enabled = false) {
const { user } = useLoggedInUser();
return useQuery(
accountKeys.billingSession(user?.address as string),
async () => {
return useQuery({
queryKey: accountKeys.billingSession(user?.address as string),
queryFn: async () => {
invariant(user?.address, "walletAddress is required");
const res = await fetch(
@@ -518,8 +518,8 @@ export function useCreateBillingSession(enabled = false) {
return json.data;
},
{ enabled },
);
enabled,
});
}
export function useConfirmEmail() {
@@ -637,9 +637,9 @@ export function useCreatePaymentMethod() {
export function useApiKeys() {
const { user, isLoggedIn } = useLoggedInUser();
return useQuery(
apiKeys.keys(user?.address as string),
async () => {
return useQuery({
queryKey: apiKeys.keys(user?.address as string),
queryFn: async () => {
const res = await fetch(`${THIRDWEB_API_HOST}/v1/keys`, {
method: "GET",
@@ -654,8 +654,8 @@ export function useApiKeys() {
}
return json.data as ApiKey[];
},
{ enabled: !!user?.address && isLoggedIn },
);
enabled: !!user?.address && isLoggedIn,
});
}
export function useCreateApiKey() {
@@ -885,9 +885,9 @@ export function useRevokeAuthorizedWallet() {
export function useAuthorizedWallets() {
const { user, isLoggedIn } = useLoggedInUser();
return useQuery(
authorizedWallets.authorizedWallets(user?.address as string),
async () => {
return useQuery({
queryKey: authorizedWallets.authorizedWallets(user?.address as string),
queryFn: async () => {
const res = await fetch(`${THIRDWEB_API_HOST}/v1/authorized-wallets`, {
method: "GET",
@@ -903,8 +903,9 @@ export function useAuthorizedWallets() {
return json.data as AuthorizedWallet[];
},
{ enabled: !!user?.address && isLoggedIn, cacheTime: 0 },
);
enabled: !!user?.address && isLoggedIn,
cacheTime: 0,
});
}
/**
@@ -924,7 +925,10 @@ export async function fetchChainsFromApi() {
}
export function useApiChains() {
return useQuery(["all-chains"], async () => {
return fetchChainsFromApi();
return useQuery({
queryKey: ["all-chains"],
queryFn: () => {
return fetchChainsFromApi();
},
});
}
@@ -12,9 +12,9 @@ import { getContractMetadata, name, symbol } from "thirdweb/extensions/common";
* 2. our API endpoint
*/
export function useDashboardContractMetadata(contract: ThirdwebContract) {
return useQuery(
["contract-metadata-header", contract.chain.id, contract.address],
async () => {
return useQuery({
queryKey: ["contract-metadata-header", contract.chain.id, contract.address],
queryFn: async () => {
const [contractMetadata, _name, _symbol, compilerMetadata] =
await Promise.all([
getContractMetadata({ contract }).catch(() => ({
@@ -50,5 +50,5 @@ export function useDashboardContractMetadata(contract: ThirdwebContract) {
contractType: compilerMetadata?.name || "",
};
},
);
});
}
@@ -0,0 +1,41 @@
import { useQuery } from "@tanstack/react-query";
import type { NFT, ThirdwebContract } from "thirdweb";
import {
getOwnedNFTs as getOwnedERC721,
isERC721,
} from "thirdweb/extensions/erc721";
import { getOwnedNFTs as getOwnedERC1155 } from "thirdweb/extensions/erc1155";
import invariant from "tiny-invariant";
/**
* This hook is currently used for getting owned NFTs for Create DirectListing/English Auction
* It should only be used if our indexing solutions don't support the current network
*/
export function useDashboardOwnedNFTs({
contract,
owner,
disabled,
}: { owner?: string; contract?: ThirdwebContract; disabled?: boolean }) {
return useQuery({
queryKey: [
"owned-nfts",
contract?.chain.id || "",
contract?.address || "",
owner || "",
],
queryFn: async (): Promise<NFT[]> => {
invariant(contract, "Contract is required");
invariant(owner, "owner address is required");
const is721 = await isERC721({ contract });
if (is721) {
return getOwnedERC721({
contract,
owner,
});
}
// Else default to 1155
return getOwnedERC1155({ contract, address: owner });
},
enabled: !!contract && !!owner && !disabled,
});
}
@@ -25,12 +25,12 @@ export type EmbeddedWalletUser = {
export function useEmbeddedWallets(clientId: string) {
const { user, isLoggedIn } = useLoggedInUser();
return useQuery(
embeddedWalletsKeys.embeddedWallets(
return useQuery({
queryKey: embeddedWalletsKeys.embeddedWallets(
user?.address as string,
clientId as string,
),
async () => {
queryFn: async () => {
const res = await fetch(
`${THIRDWEB_EWS_API_HOST}/api/thirdweb/embedded-wallet?clientId=${clientId}&lastAccessedAt=0`,
{
@@ -46,6 +46,6 @@ export function useEmbeddedWallets(clientId: string) {
return json.walletUsers as EmbeddedWalletUser[];
},
{ enabled: !!user?.address && isLoggedIn && !!clientId },
);
enabled: !!user?.address && isLoggedIn && !!clientId,
});
}
@@ -55,9 +55,9 @@ const getEngineRequestHeaders = (token: string | null): HeadersInit => {
export function useEngineInstances() {
const { user, isLoggedIn } = useLoggedInUser();
return useQuery(
engineKeys.instances(user?.address as string),
async (): Promise<EngineInstance[]> => {
return useQuery({
queryKey: engineKeys.instances(user?.address as string),
queryFn: async (): Promise<EngineInstance[]> => {
const res = await fetch(`${THIRDWEB_API_HOST}/v1/engine`, {
method: "GET",
});
@@ -79,8 +79,8 @@ export function useEngineInstances() {
};
});
},
{ enabled: !!user?.address && isLoggedIn },
);
enabled: !!user?.address && isLoggedIn,
});
}
// GET Requests
@@ -100,9 +100,9 @@ export type BackendWallet = {
export function useEngineBackendWallets(instance: string) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
[engineKeys.backendWallets(instance)],
async () => {
return useQuery({
queryKey: [engineKeys.backendWallets(instance)],
queryFn: async () => {
const res = await fetch(`${instance}backend-wallet/get-all?limit=50`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -112,8 +112,8 @@ export function useEngineBackendWallets(instance: string) {
return (json.result as BackendWallet[]) || [];
},
{ enabled: !!instance && !!token },
);
enabled: !!instance && !!token,
});
}
export interface EngineSystemHealth {
@@ -333,6 +333,7 @@ export type Transaction = {
gasPrice?: string | null;
maxFeePerGas?: string | null;
maxPriorityFeePerGas?: string | null;
effectiveGasPrice?: string | null;
transactionType?: number | null;
transactionHash?: string | null;
queuedAt?: string | null;
@@ -381,9 +382,9 @@ type TransactionResponse = {
export function useEngineTransactions(instance: string, autoUpdate: boolean) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
engineKeys.transactions(instance),
async () => {
return useQuery({
queryKey: engineKeys.transactions(instance),
queryFn: async () => {
const res = await fetch(`${instance}transaction/get-all`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -393,11 +394,10 @@ export function useEngineTransactions(instance: string, autoUpdate: boolean) {
return (json.result as TransactionResponse) || {};
},
{
enabled: !!instance && !!token,
refetchInterval: autoUpdate ? 4_000 : false,
},
);
enabled: !!instance && !!token,
refetchInterval: autoUpdate ? 4_000 : false,
});
}
type WalletConfig =
@@ -422,9 +422,9 @@ type WalletConfig =
export function useEngineWalletConfig(instance: string) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
engineKeys.walletConfig(instance),
async () => {
return useQuery({
queryKey: engineKeys.walletConfig(instance),
queryFn: async () => {
const res = await fetch(`${instance}configuration/wallets`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -434,8 +434,8 @@ export function useEngineWalletConfig(instance: string) {
return (json.result as WalletConfig) || {};
},
{ enabled: !!instance && !!token },
);
enabled: !!instance && !!token,
});
}
type CurrencyValue = {
@@ -455,9 +455,9 @@ export function useEngineBackendWalletBalance(
invariant(chainId, "chainId is required");
return useQuery(
engineKeys.backendWalletBalance(address, chainId),
async () => {
return useQuery({
queryKey: engineKeys.backendWalletBalance(address, chainId),
queryFn: async () => {
const res = await fetch(
`${instance}backend-wallet/${chainId}/${address}/get-balance`,
{
@@ -470,8 +470,8 @@ export function useEngineBackendWalletBalance(
return (json.result as CurrencyValue) || {};
},
{ enabled: !!instance && !!address && !!chainId && !!token },
);
enabled: !!instance && !!address && !!chainId && !!token,
});
}
export type EngineAdmin = {
@@ -484,9 +484,9 @@ export function useEnginePermissions(instance: string) {
const token = useLoggedInUser().user?.jwt ?? null;
const address = useActiveAccount()?.address;
return useQuery(
engineKeys.permissions(instance),
async () => {
return useQuery({
queryKey: engineKeys.permissions(instance),
queryFn: async () => {
const res = await fetch(`${instance}auth/permissions/get-all`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -500,10 +500,9 @@ export function useEnginePermissions(instance: string) {
return (json.result as EngineAdmin[]) || [];
},
{
enabled: !!instance && !!token && !!address,
},
);
enabled: !!instance && !!token && !!address,
});
}
export type AccessToken = {
@@ -518,9 +517,9 @@ export type AccessToken = {
export function useEngineAccessTokens(instance: string) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
engineKeys.accessTokens(instance),
async () => {
return useQuery({
queryKey: engineKeys.accessTokens(instance),
queryFn: async () => {
const res = await fetch(`${instance}auth/access-tokens/get-all`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -530,8 +529,8 @@ export function useEngineAccessTokens(instance: string) {
return (json.result as AccessToken[]) || [];
},
{ enabled: !!instance && !!token },
);
enabled: !!instance && !!token,
});
}
export type KeypairAlgorithm = "ES256" | "RS256" | "PS256";
@@ -548,9 +547,9 @@ export type Keypair = {
export function useEngineKeypairs(instance: string) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
engineKeys.keypairs(instance),
async () => {
return useQuery({
queryKey: engineKeys.keypairs(instance),
queryFn: async () => {
const res = await fetch(`${instance}auth/keypair/get-all`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -560,8 +559,8 @@ export function useEngineKeypairs(instance: string) {
return (json.result as Keypair[]) || [];
},
{ enabled: !!instance && !!token },
);
enabled: !!instance && !!token,
});
}
type AddKeypairInput = {
@@ -644,9 +643,9 @@ export type EngineRelayer = {
export function useEngineRelayer(instance: string) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
engineKeys.relayers(instance),
async () => {
return useQuery({
queryKey: engineKeys.relayers(instance),
queryFn: async () => {
const res = await fetch(`${instance}relayer/get-all`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -656,8 +655,8 @@ export function useEngineRelayer(instance: string) {
return (json.result as EngineRelayer[]) || [];
},
{ enabled: !!instance && !!token },
);
enabled: !!instance && !!token,
});
}
export type CreateRelayerInput = {
@@ -781,9 +780,9 @@ export interface EngineWebhook {
export function useEngineWebhooks(instance: string) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
engineKeys.webhooks(instance),
async () => {
return useQuery({
queryKey: engineKeys.webhooks(instance),
queryFn: async () => {
const res = await fetch(`${instance}webhooks/get-all`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -793,8 +792,8 @@ export function useEngineWebhooks(instance: string) {
return (json.result as EngineWebhook[]) || [];
},
{ enabled: !!instance && !!token },
);
enabled: !!instance && !!token,
});
}
// POST REQUESTS
@@ -1240,9 +1239,9 @@ export function useEngineSendTokens(instance: string) {
export function useEngineCorsConfiguration(instance: string) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
engineKeys.corsUrls(instance),
async () => {
return useQuery({
queryKey: engineKeys.corsUrls(instance),
queryFn: async () => {
const res = await fetch(`${instance}configuration/cors`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -1252,8 +1251,8 @@ export function useEngineCorsConfiguration(instance: string) {
return (json.result as string[]) || [];
},
{ enabled: !!instance && !!token },
);
enabled: !!instance && !!token,
});
}
interface SetCorsUrlInput {
@@ -1296,9 +1295,9 @@ export function useEngineIpAllowlistConfiguration(instance: string) {
// if engine instance is not updated to have IP_ALLOWLIST
const { data: health } = useEngineSystemHealth(instance);
return useQuery(
engineKeys.ipAllowlist(instance),
async () => {
return useQuery({
queryKey: engineKeys.ipAllowlist(instance),
queryFn: async () => {
const res = await fetch(`${instance}configuration/ip-allowlist`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -1307,11 +1306,10 @@ export function useEngineIpAllowlistConfiguration(instance: string) {
const json = await res.json();
return (json.result as string[]) || [];
},
{
enabled:
!!instance && !!token && health?.features?.includes("IP_ALLOWLIST"),
},
);
enabled:
!!instance && !!token && health?.features?.includes("IP_ALLOWLIST"),
});
}
interface SetIpAllowlistInput {
@@ -1364,9 +1362,9 @@ export interface EngineContractSubscription {
export function useEngineContractSubscription(instance: string) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
engineKeys.contractSubscriptions(instance),
async () => {
return useQuery({
queryKey: engineKeys.contractSubscriptions(instance),
queryFn: async () => {
const res = await fetch(`${instance}contract-subscriptions/get-all`, {
method: "GET",
headers: getEngineRequestHeaders(token),
@@ -1375,10 +1373,9 @@ export function useEngineContractSubscription(instance: string) {
const json = await res.json();
return json.result as EngineContractSubscription[];
},
{
enabled: !!instance && !!token,
},
);
enabled: !!instance && !!token,
});
}
export interface AddContractSubscriptionInput {
@@ -1464,9 +1461,9 @@ export function useEngineSubscriptionsLastBlock(
) {
const token = useLoggedInUser().user?.jwt ?? null;
return useQuery(
engineKeys.contractSubscriptionsLastBlock(instanceUrl, chainId),
async () => {
return useQuery({
queryKey: engineKeys.contractSubscriptionsLastBlock(instanceUrl, chainId),
queryFn: async () => {
const response = await fetch(
`${instanceUrl}contract-subscriptions/last-block?chain=${chainId}`,
{
@@ -1478,11 +1475,10 @@ export function useEngineSubscriptionsLastBlock(
const json = await response.json();
return json.result.lastBlock as number;
},
{
enabled: !!instanceUrl && !!token,
refetchInterval: autoUpdate ? 5_000 : false,
},
);
enabled: !!instanceUrl && !!token,
refetchInterval: autoUpdate ? 5_000 : false,
});
}
export interface EngineResourceMetrics {
@@ -1499,9 +1495,9 @@ export interface EngineResourceMetrics {
export function useEngineSystemMetrics(engineId: string) {
const [enabled, setEnabled] = useState(true);
return useQuery(
engineKeys.systemMetrics(engineId),
async () => {
return useQuery({
queryKey: engineKeys.systemMetrics(engineId),
queryFn: async () => {
const res = await fetch(
`${THIRDWEB_API_HOST}/v1/engine/${engineId}/metrics`,
);
@@ -1512,10 +1508,9 @@ export function useEngineSystemMetrics(engineId: string) {
const json = (await res.json()) as EngineResourceMetrics;
return json;
},
{
// Poll every 5s unless disabled.
enabled,
refetchInterval: 5_000,
},
);
// Poll every 5s unless disabled.
enabled,
refetchInterval: 5_000,
});
}
@@ -1,5 +1,5 @@
import { useSupportedChains } from "@thirdweb-dev/react";
import { useFavouriteChainIds } from "app/(dashboard)/(chain)/components/client/star-button";
import { useSupportedChains } from "hooks/chains/configureChains";
import { useMemo } from "react";
import type { ChainMetadata } from "thirdweb/chains";
@@ -1,16 +0,0 @@
import { useQuery } from "@tanstack/react-query";
export type GasEstimate = { gasPrice: number; ethPrice: number };
export function useGas() {
return useQuery(
["gas-price", "ethereum"],
async () => {
const res = await fetch("/api/gas");
return res.json() as Promise<GasEstimate>;
},
{
refetchInterval: 60_000,
},
);
}
@@ -1,7 +1,8 @@
import { isLoginRequired } from "@/constants/auth";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { useQuery } from "@tanstack/react-query";
import type { EnsureLoginResponse } from "app/api/auth/ensure-login/route";
import { usePathname, useRouter } from "next/navigation";
import { usePathname } from "next/navigation";
import { useRef } from "react";
import {
useActiveAccount,
@@ -20,7 +21,7 @@ export function useLoggedInUser(): {
isLoggedIn: boolean;
user: { address: string; jwt?: string } | null;
} {
const router = useRouter();
const router = useDashboardRouter();
const pathname = usePathname();
const connectedAddress = useActiveAccount()?.address;
const connectionStatus = useActiveWalletConnectionStatus();
@@ -1,47 +1,35 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useSigner } from "@thirdweb-dev/react";
import { addContractToMultiChainRegistry } from "components/contract-components/utils";
import { MULTICHAIN_REGISTRY_CONTRACT } from "constants/contracts";
import {
addContractToMultiChainRegistry,
getGaslessPolygonSDK,
} from "components/contract-components/utils";
DASHBOARD_ENGINE_RELAYER_URL,
DASHBOARD_FORWARDER_ADDRESS,
} from "constants/misc";
import type { BasicContract } from "contract-ui/types/types";
import { getAllMultichainRegistry } from "dashboard-extensions/common/read/getAllMultichainRegistry";
import { useAllChainsData } from "hooks/chains/allChains";
import { useSupportedChainsRecord } from "hooks/chains/configureChains";
import { defineDashboardChain } from "lib/defineDashboardChain";
import { thirdwebClient } from "lib/thirdweb-client";
import { useMemo } from "react";
import { getContract } from "thirdweb";
import { polygon } from "thirdweb/chains";
import { sendAndConfirmTransaction } from "thirdweb";
import { remove } from "thirdweb/extensions/thirdweb";
import { useActiveAccount } from "thirdweb/react";
import invariant from "tiny-invariant";
const MULTICHAIN_REGISTRY_ADDRESS =
"0xcdAD8FA86e18538aC207872E8ff3536501431B73";
const registryContract = getContract({
client: thirdwebClient,
address: MULTICHAIN_REGISTRY_ADDRESS,
// eslint-disable-next-line no-restricted-syntax
chain: defineDashboardChain(polygon.id, undefined),
});
function useMultiChainRegContractList(walletAddress?: string) {
return useQuery(
["dashboard-registry", walletAddress, "multichain-contract-list"],
async () => {
return useQuery({
queryKey: ["dashboard-registry", walletAddress, "multichain-contract-list"],
queryFn: async () => {
invariant(walletAddress, "walletAddress is required");
const contracts = await getAllMultichainRegistry({
contract: registryContract,
contract: MULTICHAIN_REGISTRY_CONTRACT,
address: walletAddress,
});
return contracts;
},
{
enabled: !!walletAddress,
},
);
enabled: !!walletAddress,
});
}
interface Options {
@@ -97,28 +85,30 @@ type RemoveContractParams = {
};
export function useRemoveContractMutation() {
const walletAddress = useActiveAccount()?.address;
const signer = useSigner();
const { chainIdToChainRecord } = useAllChainsData();
const queryClient = useQueryClient();
const account = useActiveAccount();
return useMutation(
async (data: RemoveContractParams) => {
invariant(
walletAddress,
"cannot add a contract without a wallet address",
);
invariant(chainIdToChainRecord, "chains not initialzed yet");
invariant(signer, "no wallet connected");
invariant(data.chainId, "chainId not provided");
invariant(account, "No wallet connected");
const { contractAddress, chainId } = data;
const gaslessPolygonSDK = getGaslessPolygonSDK(signer);
return await gaslessPolygonSDK.multiChainRegistry?.removeContract({
address: contractAddress,
chainId,
const transaction = remove({
contract: MULTICHAIN_REGISTRY_CONTRACT,
deployer: account.address,
deployment: contractAddress,
chainId: BigInt(chainId),
});
return await sendAndConfirmTransaction({
transaction,
account,
gasless: {
experimentalChainlessSupport: true,
provider: "engine",
relayerUrl: DASHBOARD_ENGINE_RELAYER_URL,
relayerForwarderAddress: DASHBOARD_FORWARDER_ADDRESS,
},
});
},
{
@@ -0,0 +1,21 @@
import { useQuery } from "@tanstack/react-query";
import type { Abi } from "abitype";
import type { ThirdwebContract } from "thirdweb";
import { resolveContractAbi } from "thirdweb/contract";
import invariant from "tiny-invariant";
export function useResolveContractAbi(contract?: ThirdwebContract) {
return useQuery({
queryKey: [
"full-contract-abi",
contract?.chain.id || "",
contract?.address || "",
],
queryFn: async () => {
invariant(contract, "contract is required");
const abi = await resolveContractAbi<Abi>(contract);
return abi;
},
enabled: !!contract,
});
}
@@ -1,18 +1,36 @@
import { useContract, useContractMetadata, useSDK } from "@thirdweb-dev/react";
import type { Vote, VoteType } from "@thirdweb-dev/sdk";
import { useActiveAccount } from "thirdweb/react";
import { thirdwebClient } from "@/constants/client";
import { type ThirdwebContract, getContract, toTokens } from "thirdweb";
import {
balanceOf,
decimals,
delegate,
delegates,
} from "thirdweb/extensions/erc20";
import {
type VoteType,
canExecute,
castVoteWithReason,
executeProposal,
getAll,
hasVoted,
propose,
token,
} from "thirdweb/extensions/vote";
import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react";
import invariant from "tiny-invariant";
import type { RequiredParam } from "utils/types";
import { voteKeys } from "../cache-keys";
import {
useMutationWithInvalidate,
useQueryWithNetwork,
} from "./query/useQueryWithNetwork";
export function useVoteProposalList(contract?: Vote) {
export function useVoteProposalList(contract?: ThirdwebContract) {
return useQueryWithNetwork(
voteKeys.proposals(contract?.getAddress()),
async () => await contract?.getAll(),
voteKeys.proposals(contract?.address || ""),
async () => {
invariant(contract, "contract is required");
return await getAll({ contract });
},
{
enabled: !!contract,
},
@@ -20,17 +38,20 @@ export function useVoteProposalList(contract?: Vote) {
}
export function useHasVotedOnProposal(
contract: RequiredParam<Vote>,
proposalId: string,
contract: ThirdwebContract,
proposalId: bigint,
) {
const address = useActiveAccount()?.address;
const userAddress = useActiveAccount()?.address;
return useQueryWithNetwork(
voteKeys.userHasVotedOnProposal(
proposalId,
contract?.getAddress(),
address,
proposalId.toString(),
contract.address,
userAddress,
),
async () => await contract?.hasVoted(proposalId, address),
async () => {
invariant(userAddress, "address is required");
return await hasVoted({ contract, proposalId, account: userAddress });
},
{
enabled: !!contract,
},
@@ -38,65 +59,83 @@ export function useHasVotedOnProposal(
}
export function useCanExecuteProposal(
contract: RequiredParam<Vote>,
proposalId: string,
contract: ThirdwebContract,
proposalId: bigint,
) {
return useQueryWithNetwork(
voteKeys.canExecuteProposal(proposalId, contract?.getAddress()),
async () => await contract?.canExecute(proposalId),
voteKeys.canExecuteProposal(proposalId.toString(), contract.address),
async () => await canExecute({ contract, proposalId }),
{
enabled: !!contract,
},
);
}
export function useTokensDelegated(contract?: Vote) {
const sdk = useSDK();
const address = useActiveAccount()?.address;
export function useTokensDelegated(contract: ThirdwebContract) {
const userAddress = useActiveAccount()?.address;
return useQueryWithNetwork(
voteKeys.delegation(contract?.getAddress(), address),
voteKeys.delegation(contract.address, userAddress),
async () => {
invariant(address, "address is required");
invariant(contract, "vote contract is required");
const metadata = await contract?.metadata.get();
const tokenAddress = metadata?.voting_token_address;
const tokenContract = await sdk?.getToken(tokenAddress);
const delegation = await tokenContract?.getDelegationOf(address);
return delegation?.toLowerCase() === address.toLowerCase();
invariant(userAddress, "wallet address is required");
const tokenAddress = await token({ contract });
if (!tokenAddress) {
throw new Error("Expected a delegated token address");
}
const tokenContract = getContract({
address: tokenAddress,
chain: contract.chain,
client: thirdwebClient,
});
const delegatedAddress = await delegates({
contract: tokenContract,
account: userAddress,
});
return delegatedAddress?.toLowerCase() === userAddress.toLowerCase();
},
{
enabled: !!contract && !!address,
enabled: !!contract && !!userAddress,
},
);
}
export function useVoteTokenBalances(contract?: Vote, addresses?: string[]) {
const { data } = useContractMetadata(contract);
const tokenContract = useContract(
data?.voting_token_address,
"token",
).contract;
export function useVoteTokenBalances(
contract: ThirdwebContract,
addresses: string[],
) {
return useQueryWithNetwork(
voteKeys.balances(contract?.getAddress(), addresses),
async () => {
invariant(data, "contract metadata is required");
invariant(tokenContract, "voting contract is required");
invariant(addresses, "addresses are required");
const balances = addresses.map(async (address) => {
voteKeys.balances(contract.address, addresses),
async (): Promise<
Array<{ address: string; balance: string; decimals: number }>
> => {
invariant(addresses.length, "addresses are required");
const tokenAddress = await token({ contract });
if (!tokenAddress) {
throw new Error("Expected a delegated token address");
}
const tokenContract = getContract({
address: tokenAddress,
chain: contract.chain,
client: thirdwebClient,
});
const _decimals = await decimals({ contract: tokenContract });
const balanceData = await Promise.all(
addresses.map((address) =>
balanceOf({ contract: tokenContract, address }),
),
);
const balances = addresses.map((address, index) => {
const balance = balanceData[index] || 0n;
return {
address,
balance: (await tokenContract.balanceOf(address)).displayValue,
balance: toTokens(balance, _decimals),
decimals: _decimals,
};
});
return await Promise.all(balances);
},
{
enabled: !!data && !!contract && !!addresses?.length,
enabled: addresses.length > 0,
},
);
}
@@ -105,94 +144,143 @@ export interface IProposalInput {
description: string;
}
export function useProposalCreateMutation(contractAddress?: string) {
const voteContract = useContract(contractAddress, "vote").contract;
/**
* This hook is a simplified version meant to use on the Dashboard
* since _currently_ the dashboard only supports creating proposals with descriptions
*
* todo: make an extension in the SDK that is more robust and user-friendly ?
*/
export function useProposalCreateMutation(contract: ThirdwebContract) {
const { mutateAsync } = useSendAndConfirmTransaction();
return useMutationWithInvalidate(
(proposal: IProposalInput) => {
invariant(voteContract, "contract is required");
async (proposal: IProposalInput) => {
const { description } = proposal;
return voteContract.propose(description);
const transaction = propose({
contract,
description,
targets: [contract.address],
values: [0n],
calldatas: ["0x"],
});
return await mutateAsync(transaction);
},
{
onSuccess: (_data, _options, _variables, invalidate) => {
return invalidate([voteKeys.proposals(contractAddress)]);
return invalidate([voteKeys.proposals(contract.address)]);
},
},
);
}
export function useDelegateMutation(contract?: Vote) {
const sdk = useSDK();
const address = useActiveAccount()?.address;
const contractAddress = contract?.getAddress();
export function useDelegateMutation(contract: ThirdwebContract) {
const userAddress = useActiveAccount()?.address;
const { mutateAsync } = useSendAndConfirmTransaction();
return useMutationWithInvalidate(
async () => {
invariant(address, "address is required");
invariant(contractAddress, "contract address is required");
invariant(contract, "vote contract is required");
const metadata = await contract?.metadata.get();
const tokenAddress = metadata?.voting_token_address;
const tokenContract = await sdk?.getToken(tokenAddress);
return tokenContract?.delegateTo(address);
invariant(userAddress, "address is required");
const tokenAddress = await token({ contract });
if (!tokenAddress) {
throw new Error("Expected a delegated token address");
}
const tokenContract = getContract({
address: tokenAddress,
chain: contract.chain,
client: thirdwebClient,
});
const transaction = delegate({
contract: tokenContract,
delegatee: userAddress,
});
return await mutateAsync(transaction);
},
{
onSuccess: (_data, _options, _variables, invalidate) => {
return invalidate([voteKeys.delegation(contractAddress, address)]);
return invalidate([voteKeys.delegation(contract.address, userAddress)]);
},
},
);
}
/**
* Get the decimals of the voting erc20 token
*/
export function useVotingTokenDecimals(contract: ThirdwebContract) {
return useMutationWithInvalidate(async () => {
const tokenAddress = await token({ contract });
if (!tokenAddress) {
throw new Error("Expected a delegated token address");
}
const tokenContract = getContract({
address: tokenAddress,
chain: contract.chain,
client: thirdwebClient,
});
const _decimals = await decimals({ contract: tokenContract });
return _decimals;
});
}
interface IVoteCast {
voteType: VoteType;
reason?: string;
}
export function useCastVoteMutation(
contract: RequiredParam<Vote>,
proposalId: string,
contract: ThirdwebContract,
proposalId: bigint,
) {
const address = useActiveAccount()?.address;
const contractAddress = contract?.getAddress();
const contractAddress = contract.address;
const { mutateAsync } = useSendAndConfirmTransaction();
return useMutationWithInvalidate(
async (vote: IVoteCast) => {
async (voteItem: IVoteCast) => {
invariant(contract, "contract is required");
invariant(address, "address is required");
const { voteType, reason } = vote;
return contract.vote(proposalId, voteType, reason);
const { voteType, reason } = voteItem;
const transaction = castVoteWithReason({
contract,
proposalId,
support: voteType,
reason: reason ?? "",
});
return await mutateAsync(transaction);
},
{
onSuccess: (_data, _options, _variables, invalidate) => {
return invalidate([
voteKeys.proposals(contractAddress),
voteKeys.userHasVotedOnProposal(proposalId, contractAddress, address),
voteKeys.canExecuteProposal(proposalId, contractAddress),
voteKeys.userHasVotedOnProposal(
proposalId.toString(),
contractAddress,
address,
),
voteKeys.canExecuteProposal(proposalId.toString(), contractAddress),
]);
},
},
);
}
/**
* This hook is a simplified version for the Dashboard
* It doesn't pass any extra info to the execute function
*/
export function useExecuteProposalMutation(
contract: RequiredParam<Vote>,
proposalId: string,
contract: ThirdwebContract,
proposalId: bigint,
) {
const contractAddress = contract?.getAddress();
const contractAddress = contract.address;
const mutation = useSendAndConfirmTransaction();
return useMutationWithInvalidate(
async () => {
invariant(contract, "contract is required");
return contract.execute(proposalId);
const transaction = executeProposal({ contract, proposalId });
return await mutation.mutateAsync(transaction);
},
{
onSuccess: (_data, _options, _variables, invalidate) => {
return invalidate([
voteKeys.proposals(contractAddress),
voteKeys.canExecuteProposal(proposalId, contractAddress),
voteKeys.canExecuteProposal(proposalId.toString(), contractAddress),
]);
},
},
@@ -2,13 +2,13 @@
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Button } from "@/components/ui/button";
import { thirdwebClient } from "@/constants/client";
import { THIRDWEB_ENGINE_FAUCET_WALLET } from "@/constants/env";
import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet";
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 { thirdwebClient } from "lib/thirdweb-client";
import { toast } from "sonner";
import { toUnits } from "thirdweb";
import type { ChainMetadata } from "thirdweb/chains";
@@ -3,9 +3,9 @@
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { Skeleton } from "@/components/ui/skeleton";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { thirdwebClient } from "@/constants/client";
import { isProd } from "@/constants/env";
import { useQuery } from "@tanstack/react-query";
import { isProd } from "constants/rpc";
import { thirdwebClient } from "lib/thirdweb-client";
import { CircleCheck, XIcon } from "lucide-react";
import { useState } from "react";
import { hostnameEndsWith } from "utils/url";
@@ -1,7 +1,6 @@
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { ExternalLinkIcon } from "lucide-react";
import Link from "next/link";
import React from "react";
import type { ChainMetadata } from "thirdweb/chains";
import { ChainLiveStats } from "../client/live-stats";
import { SectionTitle } from "./SectionTitle";
@@ -1,10 +1,6 @@
/* eslint-disable @next/next/no-img-element */
import {
DASHBOARD_THIRDWEB_SECRET_KEY,
IPFS_GATEWAY_URL,
} from "@/constants/env";
import { thirdwebClient } from "@/constants/client";
import { ImageResponse } from "next/og";
import { createThirdwebClient } from "thirdweb";
import { download } from "thirdweb/storage";
import { fetchChain } from "utils/fetchChain";
@@ -66,15 +62,6 @@ const TWLogo: React.FC = () => (
export default async function Image({
params,
}: { params: { chain_id: string } }) {
const thirdwebClient = createThirdwebClient({
secretKey: DASHBOARD_THIRDWEB_SECRET_KEY,
config: {
storage: {
gatewayUrl: IPFS_GATEWAY_URL,
},
},
});
const chain = await fetchChain(params.chain_id);
if (!chain) {
return new Response("Chain not found", { status: 400 });
@@ -45,13 +45,12 @@ export default async function Page(props: {
</section>
)}
{/* Buy Funds */}
{chain.services.find((c) => c.service === "pay" && c.enabled) && (
{/* Faucet / Buy Funds */}
{chain.testnet ? (
<FaucetSection chain={chain} />
) : chain.services.find((c) => c.service === "pay" && c.enabled) ? (
<BuyFundsSection chain={chain} />
)}
{/* Faucet */}
{chain.testnet && <FaucetSection chain={chain} />}
) : null}
{/* Chain Overview */}
<ChainOverviewSection chain={chain} />
@@ -1,6 +1,5 @@
import { type SortBy, fetchTopContracts } from "lib/search";
import { CircleAlertIcon } from "lucide-react";
import React from "react";
import { TrendingContractSection } from "../../../../trending/components/trending-table";
import { getChain } from "../../../utils";
@@ -1,8 +1,7 @@
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import { IPFS_GATEWAY_URL } from "@/constants/env";
import { DASHBOARD_THIRDWEB_SECRET_KEY } from "constants/rpc";
import { ZERO_ADDRESS, createThirdwebClient, toTokens } from "thirdweb";
import { thirdwebClient } from "@/constants/client";
import { ZERO_ADDRESS, toTokens } from "thirdweb";
import {
eth_getBlockByHash,
eth_getTransactionByHash,
@@ -16,14 +15,6 @@ import { getChain } from "../../../utils";
export default async function Page(props: {
params: { chain_id: string; txHash: `0x${string}` };
}) {
const thirdwebClient = createThirdwebClient({
secretKey: DASHBOARD_THIRDWEB_SECRET_KEY,
config: {
storage: {
gatewayUrl: IPFS_GATEWAY_URL,
},
},
});
const chain = await getChain(props.params.chain_id);
const rpcRequest = getRpcClient({
@@ -10,8 +10,9 @@ import {
} from "@/components/ui/popover";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Separator } from "@/components/ui/separator";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { ChevronDownIcon, Filter, XIcon } from "lucide-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { usePathname, useSearchParams } from "next/navigation";
import type React from "react";
import { type PropsWithChildren, useCallback, useMemo } from "react";
import { products } from "../../../components/server/products";
@@ -73,7 +74,7 @@ type FilterResetButtonProps = {
const FilterResetButton: React.FC<
PropsWithChildren<FilterResetButtonProps>
> = ({ filters, children }) => {
const router = useRouter();
const router = useDashboardRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const mutableSearchParams = useMemo(() => {
@@ -124,7 +125,7 @@ type ChainTypeFilterProps = { sectionOnly?: boolean };
export const ChainTypeFilter: React.FC<ChainTypeFilterProps> = ({
sectionOnly,
}) => {
const router = useRouter();
const router = useDashboardRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
@@ -228,7 +229,7 @@ type ChainOptionsFilterProps = { sectionOnly?: boolean };
export const ChainOptionsFilter: React.FC<ChainOptionsFilterProps> = ({
sectionOnly,
}) => {
const router = useRouter();
const router = useDashboardRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const mutableSearchParams = useMemo(() => {
@@ -332,7 +333,7 @@ type ChainServiceFilterProps = { sectionOnly?: boolean };
export const ChainServiceFilter: React.FC<ChainServiceFilterProps> = ({
sectionOnly,
}) => {
const router = useRouter();
const router = useDashboardRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const mutableSearchParams = useMemo(() => {
@@ -1,7 +1,8 @@
"use client";
import { PaginationButtons } from "@/components/pagination-buttons";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { usePathname, useSearchParams } from "next/navigation";
import { useCallback } from "react";
type ChainlistPaginationProps = {
@@ -15,7 +16,7 @@ export const ChainlistPagination: React.FC<ChainlistPaginationProps> = ({
}) => {
const pathname = usePathname();
const searchParams = useSearchParams();
const router = useRouter();
const router = useDashboardRouter();
const createPageURL = useCallback(
(pageNumber: number) => {
@@ -2,8 +2,9 @@
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { SearchIcon, XCircleIcon } from "lucide-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { usePathname, useSearchParams } from "next/navigation";
import { useRef } from "react";
import { useDebouncedCallback } from "use-debounce";
@@ -15,7 +16,7 @@ function cleanUrl(url: string) {
}
export const SearchInput: React.FC = () => {
const router = useRouter();
const router = useDashboardRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
@@ -1,8 +1,9 @@
"use client";
import { Button } from "@/components/ui/button";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { Grid2X2Icon, ListIcon } from "lucide-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { usePathname, useSearchParams } from "next/navigation";
import { useCallback } from "react";
type hainListViewProps = {
@@ -12,7 +13,7 @@ type hainListViewProps = {
export const ChainListView: React.FC<hainListViewProps> = ({ activeView }) => {
const pathname = usePathname();
const searchParams = useSearchParams();
const router = useRouter();
const router = useDashboardRouter();
const createPageURL = useCallback(
(view: "grid" | "table") => {
@@ -1,12 +1,9 @@
/* eslint-disable @next/next/no-img-element */
import "server-only";
import {
DASHBOARD_THIRDWEB_SECRET_KEY,
IPFS_GATEWAY_URL,
} from "@/constants/env";
import { thirdwebClient } from "@/constants/client";
import { DASHBOARD_THIRDWEB_SECRET_KEY } from "@/constants/env";
import { cn } from "@/lib/utils";
import { createThirdwebClient } from "thirdweb";
import { resolveScheme } from "thirdweb/storage";
const fallbackChainIcon =
@@ -17,16 +14,8 @@ export async function ChainIcon(props: {
className?: string;
}) {
if (props.iconUrl) {
const client = createThirdwebClient({
secretKey: DASHBOARD_THIRDWEB_SECRET_KEY,
config: {
storage: {
gatewayUrl: IPFS_GATEWAY_URL,
},
},
});
const resolved = resolveScheme({
client,
client: thirdwebClient,
uri: props.iconUrl,
});
const res = await fetch(resolved, {
Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 KiB

@@ -2,6 +2,8 @@ import "server-only";
import { redirect } from "next/navigation";
import type { ChainCTAProps } from "./[chain_id]/(chainPage)/components/server/cta-card";
import zeroGCTA from "./temp-assets/0gCTA.png";
import zeroGBanner from "./temp-assets/0gLabsBanner.png";
import alephZeroBaner from "./temp-assets/AlephZeroBanner.jpg";
import alephZeroCTA from "./temp-assets/AlephZeroCTA.jpg";
import ancient8Banner from "./temp-assets/ancient8-banner.png";
@@ -302,13 +304,13 @@ const chainMetaRecord = {
},
//0G-Newton-Testnet
16600: {
headerImgUrl: thirdwebBanner.src,
headerImgUrl: zeroGBanner.src,
about:
"0G is an infinitely scalable data availability layer and data storage system that provides the necessary infrastructure to scale Web3 and bring novel use cases on-chain.",
"ZeroGravity (0G) is the first infinitely scalable and decentralized data availability layer with a built-in general-purpose storage layer.",
cta: {
backgroundImageUrl: thirdwebCTA.src,
title: "",
buttonLink: "https://0g.ai/",
backgroundImageUrl: zeroGCTA.src,
title: "Unlock web3's full potential with 0G",
buttonLink: "https://docs.0g.ai/0g-doc",
buttonText: "Learn more",
},
},
@@ -51,3 +51,6 @@ export default async function Layout({
</div>
);
}
// because cookies() is used
export const dynamic = "force-dynamic";
@@ -15,11 +15,11 @@ import { ImageUpload } from "@/components/ui/image-upload";
import { Input } from "@/components/ui/input";
import { RadioGroup, RadioGroupItemButton } from "@/components/ui/radio-group";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { AccountStatus, useAccount } from "@3rdweb-sdk/react/hooks/useApi";
import { zodResolver } from "@hookform/resolvers/zod";
import { Loader2 } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
@@ -49,7 +49,7 @@ export function CreateEcosystemForm() {
>();
const { data: billingAccountInfo } = useAccount();
const router = useRouter();
const router = useDashboardRouter();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
@@ -113,3 +113,6 @@ export default async function Page() {
</main>
);
}
// because cookies() is used
export const dynamic = "force-dynamic";
@@ -7,20 +7,15 @@ const SIDEBAR_SECTIONS = [
title: "Connect",
items: [
{
id: "playground",
title: "Playground",
href: "/dashboard/connect/playground",
id: "embedded-wallets",
title: "In-App Wallets",
href: "/dashboard/connect/in-app-wallets",
},
{
id: "analytics",
title: "Analytics",
href: "/dashboard/connect/analytics",
},
{
id: "embedded-wallets",
title: "In-App Wallets",
href: "/dashboard/connect/in-app-wallets",
},
{
id: "ecosystem",
title: "Ecosystem Wallets",
@@ -36,6 +31,11 @@ const SIDEBAR_SECTIONS = [
title: "Pay",
href: "/dashboard/connect/pay",
},
{
id: "playground",
title: "Playground",
href: "https://playground.thirdweb.com/connect/sign-in/button",
},
],
},
] satisfies Array<SidebarNavSection>;
@@ -1,6 +1,5 @@
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -247,9 +246,7 @@ function CreateWebhookButton(props: PropsWithChildren<WebhooksPageProps>) {
{/* Note: this is a "fake" form field since there is nothing to select yet */}
<FormItem>
<FormLabel>
Event Type <Badge variant="outline">Coming Soon</Badge>
</FormLabel>
<FormLabel>Event Type</FormLabel>
<Select disabled defaultValue="purchase_complete">
<SelectTrigger className="w-full">
<SelectValue placeholder="Purchase Complete" />
@@ -79,3 +79,6 @@ export default async function DashboardConnectPayPage() {
</div>
);
}
// because cookies() is used
export const dynamic = "force-dynamic";
@@ -73,7 +73,7 @@ function prepareEmailBody(
}
export async function createTicketAction(
previousState: State,
_previousState: State,
formData: FormData,
) {
const cookieManager = cookies();
@@ -21,7 +21,7 @@ export const HexConverter = () => {
setHex(toHex(args.dec));
setDec(args.dec);
}
} catch (e) {}
} catch {}
};
return (
@@ -9,8 +9,8 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { thirdwebClient } from "@/constants/client";
import type { Abi, AbiFunction } from "abitype";
import { thirdwebClient } from "lib/thirdweb-client";
import { useV5DashboardChain } from "lib/v5-adapter";
import { ArrowDown } from "lucide-react";
import { useState } from "react";
@@ -26,7 +26,7 @@ export const WeiConverter = () => {
setWei(_wei.toString());
setGwei(toTokens(_wei, 9));
setEther(toEther(_wei));
} catch (e) {}
} catch {}
};
return (
@@ -6,10 +6,11 @@ import {
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { usePathname, useSearchParams } from "next/navigation";
export function TablePagination() {
const router = useRouter();
const router = useDashboardRouter();
const path = usePathname();
const searchParams = useSearchParams();
const page = Number(searchParams?.get("page") || 1);
@@ -1,7 +1,8 @@
"use client";
import { Button } from "@/components/ui/button";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { ChevronDown } from "lucide-react";
import { usePathname, useRouter } from "next/navigation";
import { usePathname } from "next/navigation";
import type { SortBy, TimeRange } from "../../../../lib/search";
export type SortingHeaderProps = {
@@ -13,7 +14,7 @@ export type SortingHeaderProps = {
};
export function SortingHeader(props: SortingHeaderProps) {
const router = useRouter();
const router = useDashboardRouter();
const path = usePathname();
const enableSorting = !!props.searchParams;
const { timeRange, page, sortBy } = props.searchParams || {};
@@ -1,10 +1,11 @@
"use client";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import type { TimeRange } from "lib/search";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { usePathname, useSearchParams } from "next/navigation";
export function TimeRangeSwitcher() {
const router = useRouter();
const router = useDashboardRouter();
const path = usePathname();
const searchParams = useSearchParams();
const currentSort = searchParams?.get("sortBy") || "transactionCount";
+2
View File
@@ -1,5 +1,6 @@
import "@/styles/globals.css";
import { Toaster } from "@/components/ui/sonner";
import { DashboardRouterTopProgressBar } from "@/lib/DashboardRouter";
import { cn } from "@/lib/utils";
import type { Metadata } from "next";
import PlausibleProvider from "next-plausible";
@@ -70,6 +71,7 @@ export default function RootLayout({
<PostHogPageView />
<AppRouterProviders>{children}</AppRouterProviders>
<Toaster richColors />
<DashboardRouterTopProgressBar />
<NextTopLoader
color="hsl(var(--primary))"
height={2}
+1 -1
View File
@@ -141,7 +141,7 @@ function isValidRedirectPath(encodedPath: string): boolean {
// ensure the path always starts with a _single_ slash
// dobule slash could be interpreted as `//example.com` which is not allowed
return decodedPath.startsWith("/") && !decodedPath.startsWith("//");
} catch (e) {
} catch {
// If decoding fails, return false
return false;
}
@@ -79,7 +79,7 @@ export const slides: Slide[] = [
<ListItem>
<TrackedLink
category={TRACK_CATEGORY}
href="/dashboard/connect/playground"
href="https://playground.thirdweb.com/connect/sign-in/button"
isExternal
label="auth"
color="blue.500"
@@ -1,177 +0,0 @@
import { useTheme } from "next-themes";
import { useId, useMemo } from "react";
import {
Bar,
BarChart as RechartsBarChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import { CustomToolTip } from "./custom-tooltip";
type GenericDataType = Record<string, string | number>;
type IndexType = "date";
interface BarChartProps<
TData extends GenericDataType,
TIndexKey extends keyof TData,
> {
data: TData[];
index: {
id: TIndexKey;
label?: string;
type?: IndexType;
format?: (index: TData[TIndexKey]) => string;
};
categories: Array<{
id: keyof TData;
label?: string;
color?: string;
format?: (value: number) => string;
}>;
showXAxis?: boolean;
showYAxis?: boolean;
}
export const BarChart = <
TData extends GenericDataType,
TIndexKey extends keyof TData,
>({
data,
index,
categories,
showXAxis,
showYAxis,
}: BarChartProps<TData, TIndexKey>) => {
const { theme } = useTheme();
const id = useId();
const defaultBarColor = useMemo(() => {
if (theme === "light") {
return "#649CDD";
}
return "#3682DA";
}, [theme]);
if (!data.length) {
return null;
}
return (
<ResponsiveContainer width="100%" height="100%">
<RechartsBarChart data={data}>
<defs>
{categories.map((cat) => (
<linearGradient
key={`${cat.id as string}`}
id={`bar_color_${id}_${cat.id as string}`}
x1="0"
y1="0"
x2="0"
y2="100%"
gradientUnits="userSpaceOnUse"
>
<stop
offset="0"
stopColor={cat.color || defaultBarColor}
stopOpacity={1}
/>
<stop
offset="1"
stopColor={cat.color || defaultBarColor}
stopOpacity={0.75}
/>
</linearGradient>
))}
</defs>
{categories.map((cat) => (
<Bar
key={`${cat.id as string}`}
dataKey={cat.id as string}
stackId="a"
stroke={cat.color || defaultBarColor}
fill={`url(#bar_color_${id}_${cat.id as string})`}
strokeWidth={0}
/>
))}
<Tooltip
wrapperStyle={{ outline: "none" }}
content={({ active, payload }) => {
const payloadKey = payload?.[0]?.dataKey;
const category = categories.find((cat) => cat.id === payloadKey);
return (
<CustomToolTip
active={active}
payload={payload}
valueLabel={category?.label || ""}
valueFormatter={category?.format}
/>
);
}}
cursor={{
stroke: defaultBarColor,
fill: defaultBarColor,
opacity: 0.2,
strokeDasharray: 2,
strokeWidth: 0,
}}
/>
<XAxis
hide={!showXAxis}
dataKey={index.id as string}
tickFormatter={(payload) =>
index.format
? index.format(payload)
: index.type === "date"
? new Date(payload).toLocaleDateString(undefined, {
day: "2-digit",
month: "2-digit",
year: "2-digit",
})
: payload
}
style={{
fontSize: "12px",
fontFamily: "var(--chakra-fonts-body)",
}}
stroke="var(--chakra-colors-paragraph)"
tickLine={false}
axisLine={false}
interval="preserveStartEnd"
minTickGap={5}
domain={["dataMin - 86400000", "dataMax + 86400000"]}
type="number"
tick={{ transform: "translate(0, 6)" }}
ticks={[
data[0][index.id],
data[Math.floor(data.length / 2)][index.id],
data[data.length - 1][index.id],
]}
/>
<YAxis
hide={!showYAxis}
width={60}
tickFormatter={(payload) => {
const category = categories[0];
return category?.format
? category.format(payload)
: payload.toLocaleString();
}}
className="font-sans text-xs"
stroke="hsl(var(--muted-foreground))"
type="number"
tickLine={false}
axisLine={false}
/>
</RechartsBarChart>
</ResponsiveContainer>
);
};
@@ -1,26 +1,21 @@
import { isProd } from "@/constants/env";
import {
type EVMContractInfo,
EVMContractInfoProvider,
} from "@3rdweb-sdk/react";
import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet";
import { Flex, SimpleGrid } from "@chakra-ui/react";
import { createAsyncStoragePersister } from "@tanstack/query-async-storage-persister";
import {
type DehydratedState,
Hydrate,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import {
PersistQueryClientProvider,
type Persister,
} from "@tanstack/react-query-persist-client";
import { shouldNeverPersistQuery } from "@thirdweb-dev/react";
import { ConfigureNetworkModal } from "components/configure-networks/ConfigureNetworkModal";
import { AppShell, type AppShellProps } from "components/layout/app-shell";
import { Onboarding as OnboardingModal } from "components/onboarding";
import { OpCreditsGrantedModalWrapper } from "components/onboarding/OpCreditsGrantedModalWrapper";
import { PosthogIdentifier } from "components/wallets/PosthogIdentifier";
import { isProd } from "constants/rpc";
import { AllChainsProvider } from "contexts/all-chains";
import { ChainsProvider } from "contexts/configured-chains";
import { ErrorProvider } from "contexts/error-handler";
@@ -30,66 +25,12 @@ import {
useIsNetworkConfigModalOpen,
useSetIsNetworkConfigModalOpen,
} from "hooks/networkConfigModal";
import { del, get, set } from "idb-keyval";
import { useEffect, useMemo, useState } from "react";
import { useActiveAccount } from "thirdweb/react";
import { Heading } from "tw-components";
import type { ComponentWithChildren } from "types/component-with-children";
import { bigNumberReplacer } from "utils/bignumber";
import { isBrowser } from "utils/isBrowser";
import { DashboardThirdwebProvider } from "./providers";
const __CACHE_BUSTER = "3.14.40-nightly-1e6f9dcc-20230831023648";
interface AsyncStorage {
getItem: (key: string) => Promise<string | null>;
setItem: (key: string, value: string) => Promise<void>;
removeItem: (key: string) => Promise<void>;
}
let currStorage: AsyncStorage | undefined;
function getStorage() {
if (!isBrowser()) {
return undefined;
}
if (currStorage) {
return currStorage;
}
currStorage = {
getItem: async (key) => {
const i = await get(key);
if (!i) {
return null;
}
return i as string | null;
},
setItem: set,
removeItem: del,
} as AsyncStorage;
return currStorage;
}
const persister: Persister = createAsyncStoragePersister({
storage: getStorage(),
serialize: (data) => {
return JSON.stringify(
{
...data,
clientState: {
...data.clientState,
queries: data.clientState.queries.filter(
(q) => !shouldNeverPersistQuery(q.queryKey),
),
},
},
bigNumberReplacer,
);
},
key: "tw-query-cache",
});
interface AppLayoutProps extends AppShellProps {
dehydratedState?: DehydratedState;
contractInfo?: EVMContractInfo;
@@ -112,16 +53,7 @@ export const AppLayout: ComponentWithChildren<AppLayoutProps> = (props) => {
}, []);
return (
<PersistQueryClientProvider
client={queryClient}
persistOptions={{
persister,
buster: __CACHE_BUSTER,
dehydrateOptions: {
shouldDehydrateQuery: (q) => !shouldNeverPersistQuery(q.queryKey),
},
}}
>
<QueryClientProvider client={queryClient}>
<Hydrate state={props.dehydratedState}>
<ErrorProvider>
<AllChainsProvider>
@@ -143,7 +75,7 @@ export const AppLayout: ComponentWithChildren<AppLayoutProps> = (props) => {
</AllChainsProvider>
</ErrorProvider>
</Hydrate>
</PersistQueryClientProvider>
</QueryClientProvider>
);
};
@@ -1,16 +1,16 @@
"use client";
import { thirdwebClient } from "@/constants/client";
import {
DASHBOARD_THIRDWEB_CLIENT_ID,
DASHBOARD_THIRDWEB_SECRET_KEY,
} from "@/constants/env";
import {
type EVMContractInfo,
useEVMContractInfo,
} from "@3rdweb-sdk/react/hooks/useActiveChainId";
import { useQueryClient } from "@tanstack/react-query";
import { ThirdwebSDKProvider } from "@thirdweb-dev/react";
import {
DASHBOARD_THIRDWEB_CLIENT_ID,
DASHBOARD_THIRDWEB_SECRET_KEY,
} from "constants/rpc";
import type { Signer } from "ethers";
import { useSupportedChains } from "hooks/chains/configureChains";
import { useNativeColorMode } from "hooks/useNativeColorMode";
@@ -10,6 +10,7 @@ import {
} from "@/components/ui/dialog";
import { thirdwebClient } from "@/constants/client";
import { cn } from "@/lib/utils";
import { useDashboardEVMChainId } from "@3rdweb-sdk/react";
import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet";
import {
Box,
@@ -25,7 +26,6 @@ import {
} from "@chakra-ui/react";
import { AiOutlineWarning } from "@react-icons/all-files/ai/AiOutlineWarning";
import { useQuery } from "@tanstack/react-query";
import { useSDK, useSDKChainId } from "@thirdweb-dev/react";
import { FaucetButton } from "app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton";
import { GiftIcon } from "app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/icons/GiftIcon";
import type {
@@ -33,6 +33,7 @@ import type {
ChainServices,
} from "app/(dashboard)/(chain)/types/chain";
import { getSDKTheme } from "app/components/sdk-component-theme";
import { LOCAL_NODE_PKEY } from "constants/misc";
import { useTrack } from "hooks/analytics/useTrack";
import { useSupportedChain } from "hooks/chains/configureChains";
import { ExternalLinkIcon } from "lucide-react";
@@ -41,6 +42,7 @@ import Link from "next/link";
import { forwardRef, useCallback, useMemo, useRef, useState } from "react";
import { VscDebugDisconnect } from "react-icons/vsc";
import { toast } from "sonner";
import { prepareTransaction, sendTransaction, toWei } from "thirdweb";
import { type Chain, type ChainMetadata, localhost } from "thirdweb/chains";
import {
PayEmbed,
@@ -51,6 +53,7 @@ import {
useSwitchActiveWalletChain,
useWalletBalance,
} from "thirdweb/react";
import { privateKeyToAccount } from "thirdweb/wallets";
import { Button, type ButtonProps, Card, Heading, Text } from "tw-components";
import { THIRDWEB_API_HOST } from "../../constants/urls";
import { useV5DashboardChain } from "../../lib/v5-adapter";
@@ -62,7 +65,7 @@ const GAS_FREE_CHAINS = [
function useNetworkMismatchAdapter() {
const walletChainId = useActiveWalletChain()?.id;
const v4SDKChainId = useSDKChainId();
const v4SDKChainId = useDashboardEVMChainId();
if (!walletChainId || !v4SDKChainId) {
// simply not ready yet, assume false
return false;
@@ -397,7 +400,7 @@ const MismatchNotice: React.FC<{
onClose: (hasSwitched: boolean) => void;
}> = ({ initialFocusRef, onClose }) => {
const connectedChainId = useActiveWalletChain()?.id;
const desiredChainId = useSDKChainId();
const desiredChainId = useDashboardEVMChainId();
const switchNetwork = useSwitchActiveWalletChain();
const connectionStatus = useActiveWalletConnectionStatus();
const activeWallet = useActiveWallet();
@@ -414,7 +417,7 @@ const MismatchNotice: React.FC<{
try {
await switchNetwork(chainV5);
onClose(true);
} catch (e) {
} catch {
// failed to switch network
onClose(false);
}
@@ -485,16 +488,30 @@ const MismatchNotice: React.FC<{
};
const GetLocalHostTestnetFunds: React.FC = () => {
const sdk = useSDK();
const address = useActiveAccount()?.address;
const requestFunds = async () => {
if (sdk) {
try {
await sdk.wallet.requestFunds(10);
toast.success("Successfully got funds from localhost");
} catch (e) {
toast.error("Failed to get funds from localhost");
}
if (!address) {
return toast.error("No active account detected");
}
const faucet = privateKeyToAccount({
privateKey: LOCAL_NODE_PKEY,
client: thirdwebClient,
});
const transaction = prepareTransaction({
to: address,
chain: localhost,
client: thirdwebClient,
value: toWei("10"),
});
const promise = sendTransaction({
account: faucet,
transaction,
});
toast.promise(promise, {
error: "Failed to get funds from localhost",
success: "Successfully got funds from localhost",
loading: "Requesting funds",
});
};
return (
@@ -1,13 +1,11 @@
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Alert,
AlertIcon,
Flex,
FormControl,
Input,
Radio,
RadioGroup,
SimpleGrid,
Stack,
} from "@chakra-ui/react";
import { ChainIcon } from "components/icons/ChainIcon";
import type { StoredChain } from "contexts/configured-chains";
@@ -16,7 +14,7 @@ import { useSupportedChainsNameRecord } from "hooks/chains/configureChains";
import { useRemoveChainModification } from "hooks/chains/useModifyChain";
import { getDashboardChainRpc } from "lib/rpc";
import { useForm } from "react-hook-form";
import { Button, FormErrorMessage, FormLabel, Text } from "tw-components";
import { FormErrorMessage, FormLabel } from "tw-components";
import { ChainIdInput } from "./Form/ChainIdInput";
import { ConfirmationPopover } from "./Form/ConfirmationPopover";
import { IconUpload } from "./Form/IconUpload";
@@ -217,11 +215,7 @@ export const ConfigureNetworkForm: React.FC<NetworkConfigFormProps> = ({
const submitBtn = (
<Button
background="bgBlack"
color="bgWhite"
_hover={{
background: "bgBlack",
}}
variant="primary"
type={disableSubmit ? "submit" : overwritingChain ? "button" : "submit"}
disabled={disableSubmit}
>
@@ -243,9 +237,6 @@ export const ConfigureNetworkForm: React.FC<NetworkConfigFormProps> = ({
<Input
autoComplete="off"
placeholder="e.g. My Network"
_placeholder={{
fontWeight: 500,
}}
type="text"
onChange={(e) => {
const value = e.target.value;
@@ -272,9 +263,9 @@ export const ConfigureNetworkForm: React.FC<NetworkConfigFormProps> = ({
{/* Slug URL */}
<NetworkIDInput form={form} disabled={!isFullyEditable} />
<Flex direction="column" gap={6} mt={6}>
<div className="flex flex-col gap-6 mt-6">
{/* Chain ID + Currency Symbol */}
<SimpleGrid columns={{ md: 2, base: 1 }} gap={4}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<ChainIdInput form={form} disabled={!isFullyEditable} />
{/* Currency Symbol */}
@@ -284,33 +275,29 @@ export const ConfigureNetworkForm: React.FC<NetworkConfigFormProps> = ({
disabled={!isFullyEditable}
placeholder="e.g. ETH"
autoComplete="off"
_placeholder={{
fontWeight: 500,
}}
type="text"
{...form.register("currencySymbol", { required: true })}
/>
</FormControl>
</SimpleGrid>
</div>
<SimpleGrid columns={{ md: 2, base: 1 }} gap={4}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Testnet / Mainnet */}
<FormControl>
<FormLabel display="flex">
<FormLabel className="!flex gap-1 items-center">
Network type
<TooltipBox
content={
<>
<Text color="heading" display="inline-block" mb={2}>
{" "}
<span className="mb-2 block">
The network type indicates whether it is intended for
production or testing.
</Text>
</span>
<Text color="heading">
<span className="block">
It{`'`}s only used for displaying network type on the
dashboard and does not affect functionality.
</Text>
</span>
</>
}
/>
@@ -324,10 +311,10 @@ export const ConfigureNetworkForm: React.FC<NetworkConfigFormProps> = ({
}}
value={form.watch("type")}
>
<Stack direction="row" gap={4} mt={3}>
<div className="flex gap-4 mt-3">
<Radio value="mainnet">Mainnet</Radio>
<Radio value="testnet">Testnet</Radio>
</Stack>
</div>
</RadioGroup>
</FormControl>
@@ -335,19 +322,19 @@ export const ConfigureNetworkForm: React.FC<NetworkConfigFormProps> = ({
<FormControl isInvalid={!!form.formState.errors.icon}>
<FormLabel>Icon</FormLabel>
<Flex gap={3} alignItems="center">
<div className="flex gap-1 items-center">
<ChainIcon size={24} ipfsSrc={form.watch("icon")} />
<IconUpload
onUpload={(uri) => {
form.setValue("icon", uri, { shouldDirty: true });
}}
/>
</Flex>
</div>
</FormControl>
</SimpleGrid>
</div>
{editingChain?.status === "deprecated" && (
<SimpleGrid columns={{ md: 2, base: 1 }} gap={4}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Active / Deprecated */}
<FormControl>
<FormLabel display="flex">Network status</FormLabel>
@@ -360,13 +347,13 @@ export const ConfigureNetworkForm: React.FC<NetworkConfigFormProps> = ({
}}
value={form.watch("status")}
>
<Stack direction="row" gap={4} mt={3}>
<div className="flex mt-3 gap-4">
<Radio value="active">Live</Radio>
<Radio value="deprecated">Deprecated</Radio>
</Stack>
</div>
</RadioGroup>
</FormControl>
</SimpleGrid>
</div>
)}
{/* RPC URL */}
@@ -382,11 +369,7 @@ export const ConfigureNetworkForm: React.FC<NetworkConfigFormProps> = ({
)}
{/* Buttons */}
<Flex
gap={4}
direction={{ base: "column", md: "row" }}
justifyContent={{ base: "center", md: "flex-end" }}
>
<div className="flex gap-4 flex-col md:flex-row justify-end">
{/* Add / Update Button */}
{overwritingChain && !disableSubmit ? (
<ConfirmationPopover
@@ -417,8 +400,8 @@ export const ConfigureNetworkForm: React.FC<NetworkConfigFormProps> = ({
Reset
</Button>
)}
</Flex>
</Flex>
</div>
</div>
</form>
);
};
@@ -1,10 +1,4 @@
import {
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalOverlay,
} from "@chakra-ui/react";
import { Dialog, DialogContent } from "@/components/ui/dialog";
import type { StoredChain } from "contexts/configured-chains";
import { useSetEditChain } from "hooks/networkConfigModal";
import { ConfigureNetworks } from "./ConfigureNetworks";
@@ -24,31 +18,26 @@ export const ConfigureNetworkModal: React.FC<AddNetworkModalProps> = (
};
return (
<Modal isOpen={true} onClose={onModalClose} isCentered>
<ModalOverlay />
<ModalContent
borderRadius="xl"
overflow="hidden"
w="500px"
mt={{ base: 8, md: 16 }}
mb={{ base: 0, md: 16 }}
maxW={{ base: "100vw", md: "calc(100vw - 40px)" }}
<Dialog
open={true}
onOpenChange={(v) => {
if (!v) {
onModalClose();
}
}}
>
<DialogContent
className="p-0 z-[10001] max-w-[480px]"
dialogOverlayClassName="z-[10000]"
>
<ModalCloseButton />
<ModalBody
p={0}
_dark={{ background: "backgroundBody" }}
_light={{ background: "white" }}
>
<ConfigureNetworks
onNetworkAdded={(chain) => {
props.onNetworkAdded?.(chain);
onModalClose();
}}
onNetworkConfigured={onModalClose}
/>
</ModalBody>
</ModalContent>
</Modal>
<ConfigureNetworks
onNetworkAdded={(chain) => {
props.onNetworkAdded?.(chain);
onModalClose();
}}
onNetworkConfigured={onModalClose}
/>
</DialogContent>
</Dialog>
);
};
@@ -1,10 +1,9 @@
import { Box, useToast } from "@chakra-ui/react";
import type { StoredChain } from "contexts/configured-chains";
import { useTrack } from "hooks/analytics/useTrack";
import { useAddRecentlyUsedChainId } from "hooks/chains/recentlyUsedChains";
import { useModifyChain } from "hooks/chains/useModifyChain";
import { useEditChain } from "hooks/networkConfigModal";
import { Heading } from "tw-components";
import { toast } from "sonner";
import { ConfigureNetworkForm } from "./ConfigureNetworkForm";
function useChainConfigTrack() {
@@ -31,17 +30,6 @@ export const ConfigureNetworks: React.FC<ConfigureNetworksProps> = (props) => {
const modifyChain = useModifyChain();
const editChain = useEditChain();
const toast = useToast();
const successToast = (message: string) => {
toast({
title: message,
status: "success",
duration: 3000,
isClosable: true,
});
};
const handleSubmit = (chain: StoredChain) => {
modifyChain(chain);
addRecentlyUsedChainId(chain.chainId);
@@ -52,44 +40,29 @@ export const ConfigureNetworks: React.FC<ConfigureNetworksProps> = (props) => {
}
trackChainConfig("add", chain);
successToast("Network Added Successfully");
toast.success("Network Added Successfully");
} else {
if (props.onNetworkConfigured) {
props.onNetworkConfigured(chain);
}
trackChainConfig("update", chain);
successToast("Network Updated Successfully");
toast.success("Network Updated Successfully");
}
};
return (
<Box
p={{ base: 4, md: 8 }}
minH="600px"
borderTop={{ md: "none", base: "1px solid" }}
marginTop={{ base: 6, md: 0 }}
borderColor="inputBg"
>
<Heading
as={"h3"}
size="label.xl"
mb={8}
display="flex"
gap={2}
alignItems="center"
>
<div className="p-6">
<h3 className="text-2xl font-semibold tracking-tight mb-5">
{editChain ? "Edit Network" : "Add Custom Network"}
</Heading>
</h3>
{/* Modify the given chain */}
{editChain && (
<Box mt={9}>
<ConfigureNetworkForm
editingChain={editChain}
onSubmit={handleSubmit}
/>
</Box>
<ConfigureNetworkForm
editingChain={editChain}
onSubmit={handleSubmit}
/>
)}
{/* Custom chain */}
@@ -100,6 +73,6 @@ export const ConfigureNetworks: React.FC<ConfigureNetworksProps> = (props) => {
onSubmit={handleSubmit}
/>
)}
</Box>
</div>
);
};
@@ -1,11 +1,11 @@
import { FormControl, Input } from "@chakra-ui/react";
import { Input } from "@/components/ui/input";
import { FormControl } from "@chakra-ui/react";
import type { UseFormReturn } from "react-hook-form";
import { FormLabel } from "tw-components";
import type { NetworkConfigFormData } from "../ConfigureNetworkForm";
export const ChainIdInput: React.FC<{
// biome-ignore lint/suspicious/noExplicitAny: FIXME
form: UseFormReturn<NetworkConfigFormData, any>;
form: UseFormReturn<NetworkConfigFormData>;
disabled: boolean;
}> = ({ form, disabled }) => {
return (
@@ -18,9 +18,6 @@ export const ChainIdInput: React.FC<{
disabled={disabled}
placeholder="e.g. 152"
autoComplete="off"
_placeholder={{
fontWeight: 500,
}}
onKeyDown={(e) => {
// prevent typing e, +, -
if (e.key === "e" || e.key === "+" || e.key === "-") {
@@ -1,23 +1,22 @@
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Button } from "@/components/ui/button";
import { useDashboardStorageUpload } from "@3rdweb-sdk/react/hooks/useDashboardStorageUpload";
import { Icon } from "@chakra-ui/react";
import { useQueryClient } from "@tanstack/react-query";
import { FileInput } from "components/shared/FileInput";
import { PINNED_FILES_QUERY_KEY_ROOT } from "components/storage/your-files";
import { useErrorHandler } from "contexts/error-handler";
import { FiUpload } from "react-icons/fi";
import { Button } from "tw-components";
import { toast } from "sonner";
export const IconUpload: React.FC<{ onUpload: (url: string) => void }> = ({
onUpload,
}) => {
const errorHandler = useErrorHandler();
const storageUpload = useDashboardStorageUpload();
const queryClient = useQueryClient();
const handleIconUpload = (file: File) => {
// if file size is larger than 5000kB, show error
if (file.size > 5000 * 1024) {
errorHandler.onError("Icon image can not be larger than 5MB");
toast.error("Icon image can not be larger than 5MB");
return;
}
@@ -28,21 +27,20 @@ export const IconUpload: React.FC<{ onUpload: (url: string) => void }> = ({
queryClient.invalidateQueries([PINNED_FILES_QUERY_KEY_ROOT]);
},
onError(error) {
errorHandler.onError(error, "Failed to upload file");
console.error(error);
toast.error("Failed to upload file");
},
});
};
return (
<FileInput setValue={handleIconUpload} accept={{ "image/*": [] }}>
<Button
bg="inputBg"
size="sm"
variant="solid"
aria-label="Upload to IPFS"
rightIcon={<Icon as={FiUpload} />}
isLoading={storageUpload.isLoading}
>
<Button variant="ghost" className="gap-2 px-2" size="sm">
{storageUpload.isLoading ? (
<Spinner className="size-4" />
) : (
<FiUpload className="size-4" />
)}
Upload Icon
</Button>
</FileInput>
@@ -1,19 +1,13 @@
import { FormControl, Input } from "@chakra-ui/react";
import { Input } from "@/components/ui/input";
import { FormControl } from "@chakra-ui/react";
import { useAllChainsData } from "hooks/chains/allChains";
import type { UseFormReturn } from "react-hook-form";
import {
CodeBlock,
FormErrorMessage,
FormLabel,
Heading,
Text,
} from "tw-components";
import { FormErrorMessage, FormLabel } from "tw-components";
import type { NetworkConfigFormData } from "../ConfigureNetworkForm";
import { TooltipBox } from "./TooltipBox";
export const NetworkIDInput: React.FC<{
// biome-ignore lint/suspicious/noExplicitAny: FIXME
form: UseFormReturn<NetworkConfigFormData, any>;
form: UseFormReturn<NetworkConfigFormData>;
disabled?: boolean;
}> = ({ form, disabled }) => {
const slug = form.watch("slug");
@@ -25,23 +19,16 @@ export const NetworkIDInput: React.FC<{
mt={6}
isInvalid={form.formState.errors.slug?.type === "taken"}
>
<FormLabel display="flex">
<FormLabel className="!flex items-center gap-1">
Network ID
<TooltipBox
content={
<>
<Text color="heading" mb={4}>
Network ID is used to identify the network in the URL{" "}
</Text>
<Heading fontSize="14px" mb={3}>
Example
</Heading>
<CodeBlock
fontSize="14px"
code="thirdweb.com/<network-id>/<contract-address>"
language="bash"
canCopy={false}
/>
Network ID is used to identify the network in the URL
<p className="font-semibold mt-2 mb-1">Example</p>
<p className="text-link-foreground text-xs">
{"thirdweb.com/<network-id>/<contract-address>"}
</p>
</>
}
/>
@@ -50,9 +37,6 @@ export const NetworkIDInput: React.FC<{
disabled={disabled}
autoComplete="off"
placeholder="e.g. ethereum"
_placeholder={{
fontWeight: 500,
}}
onKeyDown={(e) => {
// only allow alphanumeric characters and dashes
if (!/^[a-z0-9-]*$/i.test(e.key)) {
@@ -1,4 +1,5 @@
import { Alert, AlertIcon, FormControl, Input } from "@chakra-ui/react";
import { Input } from "@/components/ui/input";
import { Alert, AlertIcon, FormControl } from "@chakra-ui/react";
import type { UseFormReturn } from "react-hook-form";
import { IoWarning } from "react-icons/io5";
import { FormErrorMessage, FormLabel } from "tw-components";
@@ -14,7 +15,7 @@ export const RpcInput: React.FC<{
try {
new URL(value);
return true;
} catch (e) {
} catch {
return false;
}
},
@@ -28,15 +29,7 @@ export const RpcInput: React.FC<{
isDisabled={form.watch("status") !== "active"}
>
<FormLabel>RPC URL</FormLabel>
<Input
autoComplete="off"
placeholder="https://"
type="url"
_placeholder={{
fontWeight: 500,
}}
{...reg}
/>
<Input autoComplete="off" placeholder="https://" type="url" {...reg} />
<FormErrorMessage fontSize="12px">Invalid RPC URL</FormErrorMessage>
@@ -1,34 +1,12 @@
import { Flex, Icon, Tooltip } from "@chakra-ui/react";
import { BsQuestionCircle } from "react-icons/bs";
import { Card } from "tw-components";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { CircleHelpIcon } from "lucide-react";
export const TooltipBox: React.FC<{
content: React.ReactNode;
iconColor?: string;
}> = ({ content, iconColor = "accent.600" }) => {
}> = ({ content }) => {
return (
<Tooltip
placement="top-start"
borderRadius="md"
bg="transparent"
boxShadow="none"
p={4}
minW={{ md: "450px" }}
label={
<Card py={2} px={4} bgColor="backgroundCardHighlight">
{content}
</Card>
}
>
<Flex alignItems="center" justifyContent="center">
<Icon
ml={2}
mr={1}
as={BsQuestionCircle}
color={iconColor}
boxSize={4}
/>
</Flex>
</Tooltip>
<ToolTipLabel label={<div>{content}</div>}>
<CircleHelpIcon className="size-4 text-muted-foreground" />
</ToolTipLabel>
);
};
@@ -10,7 +10,7 @@ const showcaseMenus = [
{
id: "connect-icon",
title: "Onboard users",
href: "/dashboard/connect/playground",
href: "https://playground.thirdweb.com/connect/sign-in/button",
description: "Onboard anyone with every sign-in option for web2 & web3.",
image: require("../../../public/assets/product-pages/connect/icon-connect.png"),
},
@@ -21,7 +21,7 @@ import { useTrack } from "hooks/analytics/useTrack";
import { useSupportedChain } from "hooks/chains/configureChains";
import { useTxNotifications } from "hooks/useTxNotifications";
import { replaceTemplateValues } from "lib/deployment/template-values";
import { ExternalLinkIcon } from "lucide-react";
import { ExternalLinkIcon, InfoIcon } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { useMemo } from "react";
@@ -349,15 +349,6 @@ const CustomContractForm: React.FC<CustomContractFormProps> = ({
(isErc721SharedMetadadata ? 1 : 0);
const isCreate2Deployment = form.watch("deployDeterministic");
if (fullPublishMetadata.isLoading) {
return (
<div className="min-h-[200px] flex items-center justify-center">
<Spinner className="size-10" />
</div>
);
}
const advancedParams = Object.keys(formDeployParams)
.map((paramKey) => {
const deployParam = deployParams.find((p) => p.name === paramKey);
@@ -382,6 +373,19 @@ const CustomContractForm: React.FC<CustomContractFormProps> = ({
return (
<FormProvider {...form}>
{fullPublishMetadata.isLoading && (
<div className="min-h-[200px] flex items-center justify-center">
<Spinner className="size-10" />
</div>
)}
{fullPublishMetadata.isError && (
<div className="bg-destructive p-4 border border-destructive-text/30 rounded-lg text-destructive-text flex gap-2 items-center">
<InfoIcon className="size-4" />
Failed to fetch Publish metadata
</div>
)}
<Flex
flexGrow={1}
minH="full"
@@ -454,7 +458,7 @@ const CustomContractForm: React.FC<CustomContractFormProps> = ({
contractAddress: deployedContractAddress,
chainId: selectedChain,
});
} catch (e) {
} catch {
// ignore
}
@@ -2,10 +2,7 @@ import { Button } from "@/components/ui/button";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { Flex, FormControl, InputGroup } from "@chakra-ui/react";
import { SolidityInput } from "contract-ui/components/solidity-inputs";
import { useState } from "react";
import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components";
import { DynamicHeight } from "../../../@/components/ui/DynamicHeight";
import { Switch } from "../../../@/components/ui/switch";
import { useDefaultForwarders } from "../hooks";
import { Fieldset } from "./common";
import type { CustomContractDeploymentForm } from "./custom-contract";
@@ -18,86 +15,75 @@ export const TrustedForwardersFieldset: React.FC<
TrustedForwardersFieldsetProps
> = ({ form }) => {
const defaultForwarders = useDefaultForwarders();
const [showFull, setShowFull] = useState(false);
return (
<Fieldset legend="Gasless">
<DynamicHeight>
<FormControl
isRequired
isInvalid={
!!form.getFieldState(
"deployParams._trustedForwarders",
form.formState,
).error
}
>
<div className="flex gap-6 justify-between items-center">
{/* left */}
<div>
<FormLabel>Trusted Forwarders</FormLabel>
<FormControl
isRequired
isInvalid={
!!form.getFieldState(
"deployParams._trustedForwarders",
form.formState,
).error
}
>
<div className="flex gap-6 justify-between items-center">
{/* left */}
<div>
<FormLabel>Trusted Forwarders</FormLabel>
<FormHelperText className="!text-sm text-muted-foreground">
<span className="text-muted-foreground text-sm mb-1 block">
Trusted forwarder addresses to enable ERC-2771 transactions
(i.e. gasless).
</span>
<FormHelperText className="!text-sm text-muted-foreground">
<span className="text-muted-foreground text-sm mb-1 block">
Trusted forwarder addresses to enable ERC-2771 transactions
(i.e. gasless).
</span>
<span className="text-muted-foreground text-sm block">
You can provide your own forwarder, or click the button below
to use default forwarders provided by thirdweb. Leave empty if
not needed.
</span>
</FormHelperText>
</div>
{/* Right */}
<Switch checked={showFull} onCheckedChange={setShowFull} />
<span className="text-muted-foreground text-sm block">
You can provide your own forwarder, or click the button below to
use default forwarders provided by thirdweb. Leave empty if not
needed.
</span>
</FormHelperText>
</div>
</div>
<div
className={
!showFull
? "hidden"
: "block pt-3 fade-in-0 animate-in duration-400"
}
>
<InputGroup size="md">
<Flex flexDir="column" w="full">
<SolidityInput
value={form.watch("deployParams._trustedForwarders")}
solidityType="address[]"
{...form.register("deployParams._trustedForwarders")}
/>
</Flex>
</InputGroup>
<div className="block pt-3 fade-in-0 animate-in duration-400">
<InputGroup size="md">
<Flex flexDir="column" w="full">
<SolidityInput
value={form.watch("deployParams._trustedForwarders")}
solidityType="address[]"
{...form.register("deployParams._trustedForwarders")}
/>
</Flex>
</InputGroup>
<ToolTipLabel label="Click to apply default trusted forwarders">
<Button
type="button"
variant="outline"
className="mt-2"
onClick={() =>
form.setValue(
"deployParams._trustedForwarders",
JSON.stringify(defaultForwarders.data),
)
}
>
Get Default
</Button>
</ToolTipLabel>
<FormErrorMessage>
{
form.getFieldState(
<ToolTipLabel label="Click to apply default trusted forwarders">
<Button
type="button"
variant="outline"
className="mt-2"
onClick={() =>
form.setValue(
"deployParams._trustedForwarders",
form.formState,
).error?.message
JSON.stringify(defaultForwarders.data),
)
}
</FormErrorMessage>
</div>
</FormControl>
</DynamicHeight>
>
Get Default
</Button>
</ToolTipLabel>
<FormErrorMessage>
{
form.getFieldState(
"deployParams._trustedForwarders",
form.formState,
).error?.message
}
</FormErrorMessage>
</div>
</FormControl>
</Fieldset>
);
};
@@ -171,7 +171,7 @@ export const PublishedContractTable: ComponentWithChildren<
data: rows,
});
return (
<TableContainer>
<TableContainer className="relative">
{isFetching && (
<Spinner
color="primary"
@@ -56,7 +56,7 @@ export const DeployableContractTable: ComponentWithChildren<
data: contractIds.map((contractId) => ({ contractId })),
});
return (
<TableContainer>
<TableContainer className="relative">
{isFetching && (
<Spinner
color="primary"
@@ -1,4 +1,8 @@
import { contractKeys, networkKeys } from "@3rdweb-sdk/react";
import {
contractKeys,
networkKeys,
useDashboardEVMChainId,
} from "@3rdweb-sdk/react";
import { useMutationWithInvalidate } from "@3rdweb-sdk/react/hooks/query/useQueryWithNetwork";
import {
type QueryClient,
@@ -6,15 +10,13 @@ import {
useQuery,
useQueryClient,
} from "@tanstack/react-query";
import { useSDK, useSDKChainId, useSigner } from "@thirdweb-dev/react";
import { useSDK, useSigner } from "@thirdweb-dev/react";
import {
type Abi,
type ContractInfoSchema,
type DeploymentTransaction,
type ExtraPublishMetadata,
type FeatureName,
type FeatureWithEnabled,
type ProfileMetadata,
type PublishedContract,
type ThirdwebSDK,
detectFeatures,
@@ -33,13 +35,12 @@ import {
getZkTransactionsForDeploy,
zkDeployContractFromUri,
} from "@thirdweb-dev/sdk/evm/zksync";
import type { SnippetApiResponse } from "components/contract-tabs/code/types";
import type { ProfileMetadata, ProfileMetadataInput } from "constants/schemas";
import type { providers } from "ethers";
import { useSupportedChain } from "hooks/chains/configureChains";
import { isEnsName, resolveEns } from "lib/ens";
import { getDashboardChainRpc } from "lib/rpc";
import { StorageSingleton, getThirdwebSDK } from "lib/sdk";
import type { StaticImageData } from "next/image";
import { useRouter } from "next/router";
import { useMemo } from "react";
import {
@@ -57,7 +58,6 @@ import {
import { isAddress } from "thirdweb/utils";
import invariant from "tiny-invariant";
import { Web3Provider } from "zksync-ethers";
import type { z } from "zod";
import type { CustomContractDeploymentFormData } from "./contract-deploy-form/custom-contract";
import type {
DeployModalStep,
@@ -67,31 +67,6 @@ import { uploadContractMetadata } from "./contract-deploy-form/deploy-form-utils
import type { ContractId } from "./types";
import { addContractToMultiChainRegistry } from "./utils";
interface ContractPublishMetadata {
image: string | StaticImageData;
name: string;
description?: string;
abi?: Abi;
bytecode?: string;
deployDisabled?: boolean;
info?: z.infer<typeof ContractInfoSchema>;
licenses?: string[];
// biome-ignore lint/suspicious/noExplicitAny: FIXME
compilerMetadata?: Record<string, any>;
// biome-ignore lint/suspicious/noExplicitAny: FIXME
analytics?: Record<string, any>;
}
interface RawPredeployMetadata {
name: string;
metadataUri: string;
bytecodeUri: string;
// biome-ignore lint/suspicious/noExplicitAny: FIXME
analytics?: Record<string, any>;
// biome-ignore lint/suspicious/noExplicitAny: FIXME
compilers?: Record<string, any>;
}
function isChainIdZkSync(chainId?: number) {
switch (chainId) {
case zkSync.id:
@@ -140,13 +115,12 @@ export async function fetchRawPredeployMetadataFromURI(contractId: ContractId) {
}
export function useContractRawPredeployMetadataFromURI(contractId: ContractId) {
return useQuery<RawPredeployMetadata>(
["raw-predeploy-metadata", contractId],
() => fetchRawPredeployMetadataFromURI(contractId),
{
enabled: !!contractId,
},
);
return useQuery({
queryKey: ["raw-predeploy-metadata", contractId],
queryFn: () => fetchRawPredeployMetadataFromURI(contractId),
enabled: !!contractId,
});
}
// biome-ignore lint/suspicious/noExplicitAny: FIXME
@@ -200,13 +174,11 @@ export async function fetchContractPublishMetadataFromURI(
}
export function useContractPublishMetadataFromURI(contractId: ContractId) {
return useQuery<ContractPublishMetadata>(
["publish-metadata", contractId],
() => fetchContractPublishMetadataFromURI(contractId),
{
enabled: !!contractId,
},
);
return useQuery({
queryKey: ["publish-metadata", contractId],
queryFn: () => fetchContractPublishMetadataFromURI(contractId),
enabled: !!contractId,
});
}
export function useDefaultForwarders() {
@@ -214,11 +186,13 @@ export function useDefaultForwarders() {
const provider = sdk?.getProvider();
invariant(provider, "Require provider");
const chainId = useSDKChainId();
const chainId = useDashboardEVMChainId();
return useQuery(["default-forwarders", chainId], async () => {
const forwarders = await getTrustedForwarders(provider, StorageSingleton);
return forwarders;
return useQuery({
queryKey: ["default-forwarders", chainId],
queryFn: () => {
return getTrustedForwarders(provider, StorageSingleton);
},
});
}
@@ -227,9 +201,9 @@ export function useDefaultForwarders() {
export function useContractPrePublishMetadata(uri: string, address?: string) {
const contractIdIpfsHash = toContractIdIpfsHash(uri);
return useQuery(
["pre-publish-metadata", uri, address],
async () => {
return useQuery({
queryKey: ["pre-publish-metadata", uri, address],
queryFn: async () => {
invariant(address, "address is not defined");
// TODO: Make this nicer.
invariant(uri !== "ipfs://undefined", "uri can't be undefined");
@@ -241,10 +215,9 @@ export function useContractPrePublishMetadata(uri: string, address?: string) {
?.getPublisher()
.fetchPrePublishMetadata(contractIdIpfsHash, address);
},
{
enabled: !!uri && !!address,
},
);
enabled: !!uri && !!address,
});
}
async function fetchFullPublishMetadata(
@@ -276,9 +249,9 @@ export function useContractFullPublishMetadata(uri: string) {
);
const queryClient = useQueryClient();
return useQuery(
["full-publish-metadata", uri],
async () => {
return useQuery({
queryKey: ["full-publish-metadata", uri],
queryFn: async () => {
invariant(sdk, "sdk is not defined");
// TODO: Make this nicer.
invariant(uri !== "ipfs://undefined", "uri can't be undefined");
@@ -288,10 +261,9 @@ export function useContractFullPublishMetadata(uri: string) {
queryClient,
);
},
{
enabled: !!uri && !!sdk,
},
);
enabled: !!uri && !!sdk,
});
}
async function fetchPublisherProfile(publisherAddress?: string | null) {
@@ -300,7 +272,10 @@ async function fetchPublisherProfile(publisherAddress?: string | null) {
getDashboardChainRpc(polygon.id, undefined),
);
invariant(publisherAddress, "address is not defined");
return await sdk.getPublisher().getPublisherProfile(publisherAddress);
return (await sdk
.getPublisher()
// todo: remove type-casting once we have replaced this method
.getPublisherProfile(publisherAddress)) as ProfileMetadata;
}
export function publisherProfileQuery(publisherAddress?: string) {
@@ -372,29 +347,28 @@ export function useAllVersions(
polygon.id,
getDashboardChainRpc(polygon.id, undefined),
);
return useQuery(
["all-releases", publisherAddress, contractName],
() => fetchAllVersions(sdk, publisherAddress, contractName),
{
enabled: !!publisherAddress && !!contractName && !!sdk,
},
);
return useQuery({
queryKey: ["all-releases", publisherAddress, contractName],
queryFn: () => fetchAllVersions(sdk, publisherAddress, contractName),
enabled: !!publisherAddress && !!contractName && !!sdk,
});
}
export function usePublishedContractsFromDeploy(
contractAddress?: string,
chainId?: number,
) {
const activeChainId = useSDKChainId();
const activeChainId = useDashboardEVMChainId();
const cId = chainId || activeChainId;
const chainInfo = useSupportedChain(cId || -1);
return useQuery(
(networkKeys.chain(cId) as readonly unknown[]).concat([
return useQuery({
queryKey: (networkKeys.chain(cId) as readonly unknown[]).concat([
"release-from-deploy",
contractAddress,
]),
async () => {
queryFn: async () => {
invariant(contractAddress, "contractAddress is not defined");
invariant(cId, "chain not defined");
@@ -418,11 +392,10 @@ export function usePublishedContractsFromDeploy(
.getPublisher()
.resolvePublishMetadataFromCompilerMetadata(contractUri);
},
{
enabled: !!contractAddress && !!cId && !!chainInfo,
retry: false,
},
);
enabled: !!contractAddress && !!cId && !!chainInfo,
retry: false,
});
}
export async function fetchPublishedContractInfo(
@@ -439,13 +412,12 @@ export function usePublishedContractInfo(contract: PublishedContract) {
polygon.id,
getDashboardChainRpc(polygon.id, undefined),
);
return useQuery(
["released-contract", contract],
() => fetchPublishedContractInfo(sdk, contract),
{
enabled: !!contract,
},
);
return useQuery({
queryKey: ["released-contract", contract],
queryFn: () => fetchPublishedContractInfo(sdk, contract),
enabled: !!contract,
});
}
export function usePublishedContractFunctions(contract: PublishedContract) {
const publishedContractInfo = usePublishedContractInfo(contract);
@@ -569,7 +541,7 @@ export function useEditProfileMutation() {
const address = useActiveAccount()?.address;
return useMutationWithInvalidate(
async (data: ProfileMetadata) => {
async (data: ProfileMetadataInput) => {
invariant(sdk, "sdk not provided");
await sdk.getPublisher().updatePublisherProfile(data);
},
@@ -750,6 +722,20 @@ export function useCustomContractDeployMutation(options: {
message,
);
}
case "eth_sendTransaction": {
const tx = params?.[0];
if (!tx) {
throw new Error("No transaction provided");
}
return signer.sendTransaction(tx);
}
case "personal_sign": {
const data = params?.[0];
if (!data) {
throw new Error("Nothing to sign");
}
return signer.signMessage(data);
}
default: {
return (signer as providers.JsonRpcSigner)?.provider?.send(
method,
@@ -887,7 +873,7 @@ export function useCustomContractDeployMutation(options: {
});
deployStatusModal.nextStep();
} catch (e) {
} catch {
// failed to set metadata - for now just close the modal
deployStatusModal.close();
// not re-throwing the error, this is not technically a failure to deploy, just to set metadata - the contract is deployed already at this stage
@@ -909,7 +895,7 @@ export function useCustomContractDeployMutation(options: {
deployStatusModal.nextStep();
}
} catch (e) {
} catch {
// failed to add to dashboard - for now just close the modal
deployStatusModal.close();
router.replace(`/${chainId}/${contractAddress}`);
@@ -935,9 +921,9 @@ export function useTransactionsForDeploy(publishMetadataOrUri: string) {
const sdk = useSDK();
const chainId = useActiveWalletChain()?.id;
const queryResult = useQuery<DeploymentTransaction[]>(
["transactions-for-deploy", publishMetadataOrUri, chainId],
async () => {
const queryResult = useQuery<DeploymentTransaction[]>({
queryKey: ["transactions-for-deploy", publishMetadataOrUri, chainId],
queryFn: async () => {
invariant(sdk, "sdk not provided");
// Handle separately for ZkSync
@@ -951,10 +937,9 @@ export function useTransactionsForDeploy(publishMetadataOrUri: string) {
: `ipfs://${publishMetadataOrUri}`,
);
},
{
enabled: !!publishMetadataOrUri && !!sdk,
},
);
enabled: !!publishMetadataOrUri && !!sdk,
});
return queryResult;
}
@@ -1024,18 +1009,16 @@ export function usePublishedContractsQuery(
getDashboardChainRpc(polygon.id, undefined),
);
const queryClient = useQueryClient();
return useQuery<PublishedContractDetails[]>(
["published-contracts", address, feature],
() => {
return useQuery<PublishedContractDetails[]>({
queryKey: ["published-contracts", address, feature],
queryFn: () => {
invariant(sdk, "sdk not provided");
return feature && feature.length > 0
? fetchPublishedContractsWithFeature(sdk, queryClient, feature, address)
: fetchPublishedContracts(sdk, queryClient, address);
},
{
enabled: !!address && !!sdk,
},
);
enabled: !!address && !!sdk,
});
}
const ALWAYS_SUGGESTED = ["ContractMetadata", "Permissions"];
@@ -1163,42 +1146,15 @@ export function useContractEvents(abi: Abi) {
return abi ? extractEventsFromAbi(abi) : undefined;
}
// TODO: this points to very old snippets, we need to update this!
export function useFeatureContractCodeSnippetQuery(language: string) {
if (language === "javascript") {
// biome-ignore lint/style/noParameterAssign: FIXME
language = "sdk";
}
if (language === "react-native") {
// biome-ignore lint/style/noParameterAssign: FIXME
language = "react";
}
return useQuery(["feature-code-snippet", language], async () => {
// only allow specific languages
if (
["go", "python", "react", "sdk", "unity"].includes(language) === false
) {
throw new Error("Invalid language");
}
const res = await fetch(
`https://raw.githubusercontent.com/thirdweb-dev/docs/main/docs/feature_snippets_${language}.json`,
);
return (await res.json()) as SnippetApiResponse;
});
}
export function useCustomFactoryAbi(contractAddress: string, chainId: number) {
const chain = useSupportedChain(chainId);
return useQuery(
["custom-factory-abi", { contractAddress, chainId }],
async () => {
return useQuery({
queryKey: ["custom-factory-abi", { contractAddress, chainId }],
queryFn: async () => {
const sdk = getThirdwebSDK(chainId, getDashboardChainRpc(chainId, chain));
return (await sdk.getContract(contractAddress)).abi;
},
{
enabled: !!contractAddress && !!chainId,
},
);
enabled: !!contractAddress && !!chainId,
});
}
@@ -59,7 +59,7 @@ export interface ExtendedPublishedContract extends PublishedContractType {
name: string;
displayName?: string;
description: string;
version: string;
version: string | undefined;
publisher: string;
tags?: string[];
logo?: string;
@@ -146,7 +146,7 @@ export const PublishedContract: React.FC<PublishedContractProps> = ({
PublishedContractOG.toUrl({
name: publishedContractName,
description: contract.description,
version: contract.version,
version: contract.version || "latest",
publisher: publisherEnsOrAddress,
extension: extensionNames,
license: licenses,
@@ -179,9 +179,9 @@ Deploy it in one click`,
return url.href;
}, [publishedContractName, currentRoute]);
const sources = useQuery(
["sources", contract],
async () => {
const sources = useQuery({
queryKey: ["sources", contract],
queryFn: async () => {
invariant(
contractPublishMetadata.data?.compilerMetadata?.sources,
"no compilerMetadata sources available",
@@ -205,8 +205,8 @@ Deploy it in one click`,
.slice()
.reverse();
},
{ enabled: !!contractPublishMetadata.data?.compilerMetadata?.sources },
);
enabled: !!contractPublishMetadata.data?.compilerMetadata?.sources,
});
const title = useMemo(() => {
let clearType = "";
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
Box,
type BoxProps,
@@ -35,8 +34,7 @@ export const MarkdownRenderer: React.FC<
{...restProps}
remarkPlugins={[remarkGfm]}
components={{
// biome-ignore lint/suspicious/noExplicitAny: FIXME
h1: (props: any) => (
h1: (props) => (
<Heading
as="h2"
size="title.lg"
@@ -44,11 +42,11 @@ export const MarkdownRenderer: React.FC<
borderBottomColor="borderColor"
{...commonHeadingProps}
mb={4}
{...props}
{...cleanedProps(props)}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
h2: (props: any) => (
h2: (props) => (
<Heading
as="h3"
size="title.md"
@@ -57,51 +55,51 @@ export const MarkdownRenderer: React.FC<
{...commonHeadingProps}
mt={8}
mb={4}
{...props}
{...cleanedProps(props)}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
h3: (props: any) => (
h3: (props) => (
<Heading
as="h4"
size="title.sm"
{...commonHeadingProps}
{...props}
{...cleanedProps(props)}
mt={4}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
h4: (props: any) => (
h4: (props) => (
<Heading
as="h5"
size="subtitle.md"
{...commonHeadingProps}
{...props}
{...cleanedProps(props)}
mt={4}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
h5: (props: any) => (
h5: (props) => (
<Heading
as="h6"
size="subtitle.sm"
{...commonHeadingProps}
{...props}
{...cleanedProps(props)}
mt={4}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
h6: (props: any) => (
h6: (props) => (
<Heading
as="p"
size="label.md"
{...commonHeadingProps}
{...props}
{...cleanedProps(props)}
mt={4}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
a: (props: any) => (
a: (props) => (
<Link
_dark={{
color: "blue.400",
@@ -116,13 +114,12 @@ export const MarkdownRenderer: React.FC<
},
}}
isExternal
{...props}
{...cleanedProps(props)}
mt={4}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
code: ({ inline, ...props }: any) => {
code: ({ ...props }) => {
if (props?.className) {
const language = props.className.replace("language-", "");
return (
@@ -130,7 +127,7 @@ export const MarkdownRenderer: React.FC<
code={onlyText(props.children).trim()}
language={language}
mb={4}
{...props}
{...cleanedProps(props)}
/>
);
}
@@ -145,16 +142,21 @@ export const MarkdownRenderer: React.FC<
_dark={{ bg: "whiteAlpha.100" }}
borderRadius="md"
fontFamily="mono"
{...props}
{...cleanedProps(props)}
/>
);
},
// biome-ignore lint/suspicious/noExplicitAny: FIXME
p: (props: any) => (
<Text size="body.md" mb={4} {...props} lineHeight={1.5} />
p: (props) => (
<Text
size="body.md"
mb={4}
{...cleanedProps(props)}
lineHeight={1.5}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
table: (props: any) => (
table: (props) => (
<Box
maxW="100%"
overflowX="auto"
@@ -164,53 +166,44 @@ export const MarkdownRenderer: React.FC<
mb={4}
borderTopRadius="lg"
>
<Table {...props} />
<Table {...cleanedProps(props)} />
</Box>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
th: ({ children: c, ...props }: any) => (
<Th
// biome-ignore lint/suspicious/noExplicitAny: FIXME
{...(props as unknown as any)}
textAlign="left!important"
border="none"
>
<Text as="label" size="label.sm" color="faded">
th: ({ children: c, ...props }) => (
<Th {...cleanedProps(props)} textAlign="left" border="none">
<Text as="span" size="label.sm" color="faded">
{c}
</Text>
</Th>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
td: (props: any) => (
td: (props) => (
<Td
// biome-ignore lint/suspicious/noExplicitAny: FIXME
{...(props as unknown as any)}
{...cleanedProps(props)}
borderColor="borderColor"
textAlign="left!important"
textAlign="left"
borderBottomWidth={"inherit"}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
thead: (props: any) => <Thead {...props} />,
// biome-ignore lint/suspicious/noExplicitAny: FIXME
tbody: (props: any) => <Tbody {...props} />,
// biome-ignore lint/suspicious/noExplicitAny: FIXME
tr: (props: any) => (
thead: (props) => <Thead {...cleanedProps(props)} />,
tbody: (props) => <Tbody {...cleanedProps(props)} />,
tr: (props) => (
<Tr
{...props}
{...cleanedProps(props)}
transition="all 0.1s"
borderBottomWidth={1}
_last={{ borderBottomWidth: 0 }}
/>
),
// biome-ignore lint/suspicious/noExplicitAny: FIXME
ul: ({ ordered, ...props }: any) => <UnorderedList {...props} mb={4} />,
// biome-ignore lint/suspicious/noExplicitAny: FIXME
ol: ({ ordered, ...props }: any) => <OrderedList {...props} mb={4} />,
// biome-ignore lint/suspicious/noExplicitAny: FIXME
li: ({ children: c, ordered, ...props }: any) => (
<ListItem {...props}>
<Text>{c}</Text>
ul: (props) => {
return <UnorderedList {...cleanedProps(props)} mb={4} />;
},
ol: (props) => <OrderedList {...cleanedProps(props)} mb={4} />,
li: ({ children: c, ...props }) => (
<ListItem {...cleanedProps(props)}>
{/* hydration error - can not render p > p, so use span instead - remove extra span when we move to tailwind */}
<Text as="span">{c}</Text>
</ListItem>
),
}}
@@ -219,3 +212,11 @@ export const MarkdownRenderer: React.FC<
</ChakraReactMarkdown>
);
};
function cleanedProps<T extends object & { node?: unknown }>(
props: T,
): Omit<T, "node"> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { node, ...rest } = props;
return rest;
}
@@ -7,7 +7,7 @@ import { SiMedium } from "@react-icons/all-files/si/SiMedium";
import { SiReddit } from "@react-icons/all-files/si/SiReddit";
import { SiTelegram } from "@react-icons/all-files/si/SiTelegram";
import { SiTwitter } from "@react-icons/all-files/si/SiTwitter";
import type { ProfileMetadata } from "@thirdweb-dev/sdk";
import type { ProfileMetadata } from "constants/schemas";
import { FiGlobe } from "react-icons/fi";
import { LinkButton, TrackedIconButton } from "tw-components";
import { hostnameEndsWith } from "../../../utils/url";
@@ -10,8 +10,8 @@ import {
import { SiDiscord } from "@react-icons/all-files/si/SiDiscord";
import { SiGithub } from "@react-icons/all-files/si/SiGithub";
import { SiTwitter } from "@react-icons/all-files/si/SiTwitter";
import type { ProfileMetadata, ProfileMetadataInput } from "@thirdweb-dev/sdk";
import { FileInput } from "components/shared/FileInput";
import type { ProfileMetadata, ProfileMetadataInput } from "constants/schemas";
import { useTrack } from "hooks/analytics/useTrack";
import { useImageFileOrUrl } from "hooks/useImageFileOrUrl";
import { useTxNotifications } from "hooks/useTxNotifications";
@@ -5,7 +5,7 @@ import {
AccordionItem,
AccordionPanel,
} from "@chakra-ui/react";
import type { Abi } from "@thirdweb-dev/sdk";
import type { Abi } from "abitype";
import { CodeBlock, Heading } from "tw-components";
import type { SourceFile } from "../types";
@@ -20,6 +20,34 @@ export const SourcesAccordion: React.FC<SourcesAccordionProps> = ({
}) => {
return (
<Accordion allowMultiple defaultIndex={[]}>
{/* ABI Accordion is put at the top for better UX */}
{abi && (
<AccordionItem
gap={4}
flexDirection="column"
borderColor="borderColor"
_first={{ borderTopWidth: 0 }}
_last={{ borderBottomWidth: 0 }}
>
{({ isExpanded }) => (
<>
<AccordionButton justifyContent="space-between" py={2}>
<Heading size="label.md">ABI</Heading>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>
{isExpanded && (
<CodeBlock
code={JSON.stringify(abi, null, 2)}
language="json"
overflow="auto"
/>
)}
</AccordionPanel>
</>
)}
</AccordionItem>
)}
{sources.map((signature) => (
<AccordionItem
gap={4}
@@ -48,33 +76,6 @@ export const SourcesAccordion: React.FC<SourcesAccordionProps> = ({
)}
</AccordionItem>
))}
{abi && (
<AccordionItem
gap={4}
flexDirection="column"
borderColor="borderColor"
_first={{ borderTopWidth: 0 }}
_last={{ borderBottomWidth: 0 }}
>
{({ isExpanded }) => (
<>
<AccordionButton justifyContent="space-between" py={2}>
<Heading size="label.md">ABI</Heading>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>
{isExpanded && (
<CodeBlock
code={JSON.stringify(abi, null, 2)}
language="json"
overflow="auto"
/>
)}
</AccordionPanel>
</>
)}
</AccordionItem>
)}
</Accordion>
);
};
@@ -1,5 +1,5 @@
import { Flex } from "@chakra-ui/react";
import type { Abi } from "@thirdweb-dev/sdk";
import type { Abi } from "abitype";
import { Link, Text } from "tw-components";
import type { SourceFile } from "../types";
import { SourcesAccordion } from "./sources-accordion";

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