Skip to content

[SDK] Init new API namespaces #7637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

joaquim-verges
Copy link
Member

@joaquim-verges joaquim-verges commented Jul 16, 2025


PR-Codex overview

This PR focuses on enhancing the @thirdweb-dev/api package by adding new features, improving existing functionalities, and updating configurations to support better client-server interactions.

Detailed summary

  • Added new type ContractCall in packages/thirdweb/src/v2/contracts/types.ts.
  • Exported ContractCall and ReadOptions from index.ts.
  • Introduced new authentication methods in packages/thirdweb/src/v2/wallets.
  • Enhanced error handling in OTP verification.
  • Updated TypeScript configurations across multiple files.
  • Added new functions for sending and verifying OTP in packages/thirdweb/src/v2/wallets/otp.ts.
  • Created a new API client configuration in packages/api/src/client/client.gen.ts.
  • Improved the handling of OAuth providers in packages/thirdweb/src/v2/wallets/oauth.ts.
  • Updated README with example usages of the API.
  • Added new utility functions for request serialization in packages/api/src/client/core/bodySerializer.ts.

The following files were skipped due to too many changes: packages/api/src/client/client/utils.ts, packages/api/src/client/sdk.gen.ts, packages/api/src/client/types.gen.ts

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

Summary by CodeRabbit

  • New Features

    • Added @thirdweb-dev/api package with a fully typed OpenAPI-based TypeScript client for blockchain contracts, wallets, transactions, and token operations.
    • Introduced comprehensive wallet authentication methods: OAuth, OTP, passkey, SIWE, guest login, and external wallet connections.
    • Added utilities for reading and writing blockchain contracts, managing wallets, and handling transactions with strong typing and error handling.
    • Enabled domain configuration for API endpoints and centralized exports for contracts and wallets modules.
    • Provided detailed usage documentation and examples for all new API client and wallet authentication features.
  • Bug Fixes

    • Enhanced error reporting with detailed messages for OTP sending failures.
  • Chores

    • Updated workspace dependencies and scripts to include the new API package.
    • Added TypeScript, linting, and build configurations for the new package.
    • Extended domain overrides and configuration utilities to support API client setup.

Copy link

vercel bot commented Jul 16, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
docs-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 18, 2025 0:11am
nebula ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 18, 2025 0:11am
thirdweb_playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 18, 2025 0:11am
thirdweb-www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 18, 2025 0:11am
wallet-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 18, 2025 0:11am

Copy link

changeset-bot bot commented Jul 16, 2025

⚠️ No Changeset found

Latest commit: 4608466

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Jul 16, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

A new API client package, @thirdweb-dev/api, was introduced, providing a TypeScript OpenAPI client for thirdweb's API. The package includes configuration, build scripts, and comprehensive type definitions. The thirdweb core library was updated to add support for this API, including new contract read/write modules, wallet authentication flows (OTP, OAuth, passkey, SIWE, guest), and related types and utilities. Several configuration and documentation files were added or updated to support the new API integration and wallet features.

Changes

File(s) Change Summary
packages/api/* New @thirdweb-dev/api package: OpenAPI TypeScript client, config, scripts, type definitions, client implementation, utilities, and documentation added.
packages/thirdweb/package.json, packages/thirdweb/knip.json Added @thirdweb-dev/api as a workspace dependency and to ignore list.
packages/thirdweb/src/utils/domains.ts Added api domain override support and default API URL constant.
packages/thirdweb/src/v2/contracts/* Added contract read/write modules and types; new index for exports.
packages/thirdweb/src/v2/wallets/* Added wallet authentication modules: OTP, OAuth, passkey, SIWE, guest, external wallet connection, and consolidated exports.
packages/thirdweb/src/v2/wallets/login.test.ts Added skipped tests for OTP login and active test for guest login with example usage.
packages/thirdweb/src/v2/wallets/user.ts Added createUserWallet function to construct user wallet objects with balance and profile fetching.
packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts Added fetchUserProfiles function for retrieving user profiles using auth token.
packages/thirdweb/src/wallets/in-app/core/authentication/types.ts Extended AuthDetails type with optional walletAddress.
packages/thirdweb/src/wallets/in-app/web/lib/auth/otp.ts Enhanced OTP sending with secret key header and improved error reporting.
packages/thirdweb/src/exports/thirdweb.ts Added namespace exports for Contracts and Wallets.
package.json Updated npm scripts to include ./packages/api in relevant filters.
packages/api/tsconfig*.json Added base, build, and local development TypeScript configurations.

Sequence Diagram(s)

Example: Wallet Login Flow (OTP, Guest, OAuth, SIWE, Passkey)

sequenceDiagram
    participant User
    participant App
    participant ThirdwebClient
    participant API
    participant Wallet

    User->>App: Initiate login (OTP/OAuth/Passkey/SIWE/Guest)
    App->>ThirdwebClient: Prepare login options
    alt OTP
        App->>API: sendCode (email/phone)
        API-->>App: OTP sent
        User->>App: Enter OTP code
        App->>API: loginWithCode (verify OTP)
        API-->>App: Auth result
    else OAuth
        App->>API: loginWithOauthRedirect
        API-->>User: Redirect to OAuth provider
        User->>API: Complete OAuth flow
        API-->>App: Auth result
    else Passkey
        App->>API: loginWithPasskey/registerPasskey
        API-->>App: Auth result
    else SIWE
        App->>Wallet: Sign SIWE message
        Wallet-->>App: Signature
        App->>API: loginWithWallet (SIWE)
        API-->>App: Auth result
    else Guest
        App->>API: guestAuthenticate
        API-->>App: Auth result
    end
    App->>App: createUserWallet(authResult)
    App-->>User: UserWallet instance
Loading

Example: Contract Write Operation

sequenceDiagram
    participant User
    participant App
    participant UserWallet
    participant API

    User->>App: Request contract write
    App->>UserWallet: write({ wallet, chainId, calls })
    UserWallet->>API: writeContract (with auth token)
    API-->>UserWallet: Transaction result or error
    UserWallet-->>App: Transaction ID or error
    App-->>User: Result
Loading

Example: Fetching User Profiles

sequenceDiagram
    participant App
    participant UserWallet
    participant API

    App->>UserWallet: getProfiles()
    UserWallet->>API: fetchUserProfiles (with auth token)
    API-->>UserWallet: Profile list
    UserWallet-->>App: Profile list
Loading

Suggested labels

Ecosystem Portal

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch _SDK_Add_authentication_methods_for_v2_wallets

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added packages SDK Involves changes to the thirdweb SDK labels Jul 16, 2025
Copy link
Member Author


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@joaquim-verges joaquim-verges changed the title [SDK] Add authentication methods for v2 wallets [SDK] Init new API namespaces Jul 16, 2025
@joaquim-verges joaquim-verges marked this pull request as ready for review July 16, 2025 21:49
@joaquim-verges joaquim-verges requested review from a team as code owners July 16, 2025 21:49
Copy link
Contributor

github-actions bot commented Jul 16, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 65 KB (+2.77% 🔺) 1.3 s (+2.77% 🔺) 186 ms (+138.16% 🔺) 1.5 s
thirdweb (cjs) 365.79 KB (+3.54% 🔺) 7.4 s (+3.54% 🔺) 681 ms (+13.66% 🔺) 8 s
thirdweb (minimal + tree-shaking) 5.71 KB (+0.19% 🔺) 115 ms (+0.19% 🔺) 80 ms (+1489.12% 🔺) 194 ms
thirdweb/chains (tree-shaking) 532 B (+1.15% 🔺) 11 ms (+1.15% 🔺) 29 ms (+1942.39% 🔺) 39 ms
thirdweb/react (minimal + tree-shaking) 19.58 KB (-0.1% 🔽) 392 ms (-0.1% 🔽) 97 ms (+524.42% 🔺) 488 ms

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🧹 Nitpick comments (22)
packages/thirdweb/package.json (1)

26-26: Add @thirdweb-dev/api to size-limit & bundle-budget tracking

@thirdweb-dev/api can pull in large OpenAPI payloads.
Please update packages/thirdweb/size-limit (or equivalent) so CI fails if the added code bloats the bundle.

packages/thirdweb/knip.json (1)

15-17: Alphabetise ignoreDependencies to avoid merge churn

Keeping this list sorted makes future diffs smaller and prevents accidental duplicates (engine/insight flipped order across PRs previously).

packages/thirdweb/src/wallets/in-app/web/lib/auth/otp.ts (1)

55-59: LGTM - Enhanced error handling with actionable suggestion.

The improved error handling provides much better debugging information with status code, status text, and response body. However, consider applying the same detailed error handling to the verifyOtp function for consistency.

Apply similar error handling to verifyOtp function:

  if (!response.ok) {
-    throw new Error("Failed to verify verification code");
+    const error = await response.text();
+    throw new Error(
+      `Failed to verify verification code: ${response.status} ${response.statusText} ${error}`,
+    );
  }
packages/api/openapi-ts.config.ts (1)

1-1: Missing file-level comment disables clarity

A short header describing what this config does and how to regenerate (e.g. pnpm api:generate) would help future maintainers.

packages/thirdweb/src/v2/contracts/types.ts (1)

1-6: params: unknown[] sacrifices type-safety

Instead of forcing all callers to cast, turn the type into a generic:

-export type ContractCall = {
-  contractAddress: string;
-  method: string;
-  params: unknown[];
-  value?: string;
-};
+export type ContractCall<Args extends readonly unknown[] = readonly unknown[]> = {
+  contractAddress: string;
+  method: string;
+  params: Args;
+  value?: string;
+};

This keeps flexibility while preserving inference for most call sites.

packages/api/src/exports/thirdweb.ts (1)

1-8: Public exports lack TSDoc and exports grouping

Barrel files under src/exports form the library’s public interface; adding TSDoc (and grouping per feature) prevents accidental API surface creep and satisfies our guidelines.

Consider annotating at least the top-level symbols:

/**
 * Re-export the generated client plus helper utilities.
 *
 * @example
 * ```ts
 * import { configure } from "@thirdweb-dev/api";
 * ```
 * @beta
 */
packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts (1)

158-187: Consider refactoring to reduce code duplication.

The fetchUserProfiles function is nearly identical to getLinkedProfilesInternal (lines 119-156), with the main difference being that it accepts an authToken parameter instead of retrieving it from storage.

While the implementation is correct and follows established patterns, consider refactoring to reduce duplication:

+async function fetchLinkedProfiles(options: {
+  client: ThirdwebClient;
+  ecosystem?: Ecosystem;
+  authToken: string;
+}): Promise<Profile[]> {
+  const clientFetch = getClientFetch(options.client, options.ecosystem);
+  const IN_APP_URL = getThirdwebBaseUrl("inAppWallet");
+
+  const headers: Record<string, string> = {
+    Authorization: `Bearer iaw-auth-token:${options.authToken}`,
+    "Content-Type": "application/json",
+  };
+
+  const linkedAccountsResp = await clientFetch(
+    `${IN_APP_URL}/api/2024-05-05/accounts`,
+    {
+      headers,
+      method: "GET",
+    },
+  );
+
+  if (!linkedAccountsResp.ok) {
+    const body = await linkedAccountsResp.json();
+    throw new Error(body.message || "Failed to get linked accounts.");
+  }
+
+  const { linkedAccounts } = await linkedAccountsResp.json();
+  return (linkedAccounts ?? []) satisfies Profile[];
+}

 export async function getLinkedProfilesInternal({
   client,
   ecosystem,
   storage,
 }: {
   client: ThirdwebClient;
   ecosystem?: Ecosystem;
   storage: ClientScopedStorage;
 }): Promise<Profile[]> {
-  const clientFetch = getClientFetch(client, ecosystem);
-  const IN_APP_URL = getThirdwebBaseUrl("inAppWallet");
   const currentAccountToken = await storage.getAuthCookie();
   if (!currentAccountToken) {
     throw new Error("Failed to get linked accounts, no user logged in");
   }
-
-  const headers: Record<string, string> = {
-    Authorization: `Bearer iaw-auth-token:${currentAccountToken}`,
-    "Content-Type": "application/json",
-  };
-
-  const linkedAccountsResp = await clientFetch(
-    `${IN_APP_URL}/api/2024-05-05/accounts`,
-    {
-      headers,
-      method: "GET",
-    },
-  );
-
-  if (!linkedAccountsResp.ok) {
-    const body = await linkedAccountsResp.json();
-    throw new Error(body.message || "Failed to get linked accounts.");
-  }
-
-  const { linkedAccounts } = await linkedAccountsResp.json();
-
-  return (linkedAccounts ?? []) satisfies Profile[];
+  return fetchLinkedProfiles({
+    client,
+    ecosystem,
+    authToken: currentAccountToken,
+  });
 }

-export async function fetchUserProfiles(options: {
-  client: ThirdwebClient;
-  ecosystem?: Ecosystem;
-  authToken: string;
-}): Promise<Profile[]> {
-  const clientFetch = getClientFetch(options.client, options.ecosystem);
-  const IN_APP_URL = getThirdwebBaseUrl("inAppWallet");
-
-  const headers: Record<string, string> = {
-    Authorization: `Bearer iaw-auth-token:${options.authToken}`,
-    "Content-Type": "application/json",
-  };
-
-  const linkedAccountsResp = await clientFetch(
-    `${IN_APP_URL}/api/2024-05-05/accounts`,
-    {
-      headers,
-      method: "GET",
-    },
-  );
-
-  if (!linkedAccountsResp.ok) {
-    const body = await linkedAccountsResp.json();
-    throw new Error(body.message || "Failed to get linked accounts.");
-  }
-
-  const { linkedAccounts } = await linkedAccountsResp.json();
-
-  return (linkedAccounts ?? []) satisfies Profile[];
-}
+export const fetchUserProfiles = fetchLinkedProfiles;
packages/thirdweb/src/v2/contracts/write.ts (1)

28-45: Consider validating calls array is not empty

Add validation to ensure the calls array contains at least one call before making the API request.

export async function write(options: WriteOptions) {
+  if (!options.calls || options.calls.length === 0) {
+    throw new Error("At least one contract call is required");
+  }
+
  const result = await writeContract({
    baseUrl: getThirdwebBaseUrl("api"),
packages/thirdweb/src/v2/wallets/passkey.ts (3)

46-46: Address the TODO comment for React Native support

The TODO comment indicates that React Native support is needed. Consider creating a follow-up issue to implement the React Native version of this functionality.

Would you like me to create a GitHub issue to track React Native support for passkey authentication?


70-77: Duplicate relying party configuration

The same relying party configuration is duplicated for both "sign-in" and "sign-up" cases. This violates the DRY principle and increases maintenance burden.

+ const rp = {
+   id: options.passkeyDomain ?? window.location.hostname,
+   name: window.document.title,
+ };
+ const storage = options.storeLastUsedPasskey ? storage : undefined;

  const authResult = await (async () => {
    switch (options.action) {
      case "sign-in":
        return loginWithPasskey({
          client: options.client,
          passkeyClient,
-         rp: {
-           id: options.passkeyDomain ?? window.location.hostname,
-           name: options.passkeyDomain ?? window.document.title,
-         },
-         storage: options.storeLastUsedPasskey ? storage : undefined,
+         rp,
+         storage,
          ecosystem: options.ecosystem,
        });
      case "sign-up":
        return registerPasskey({
          client: options.client,
          passkeyClient,
-         rp: {
-           id: options.passkeyDomain ?? window.location.hostname,
-           name: options.passkeyDomain ?? window.document.title,
-         },
-         storage: options.storeLastUsedPasskey ? storage : undefined,
+         rp,
+         storage,
          ecosystem: options.ecosystem,
        });
    }
  })();

38-86: Consider adding error handling for WebAuthn unsupported environments

The function doesn't handle cases where WebAuthn is not supported in the browser environment. This could lead to runtime errors in older browsers or environments without WebAuthn support.

Add a WebAuthn availability check:

export async function loginWithPasskey(options: LoginWithPasskeyOptions) {
+ if (!window.navigator.credentials || !window.PublicKeyCredential) {
+   throw new Error("WebAuthn is not supported in this environment");
+ }

  const { PasskeyWebClient } = await import(
    "../../wallets/in-app/web/lib/auth/passkeys.js"
  );
  // ... rest of implementation
}
packages/thirdweb/src/v2/wallets/otp.ts (1)

27-27: Inconsistent function naming in example

The example shows Wallets.verifyCode but the actual function is named loginWithCode. This could confuse users.

- const wallet = await Wallets.verifyCode({
+ const wallet = await Wallets.loginWithCode({
packages/thirdweb/src/v2/wallets/login.test.ts (4)

24-24: Use realistic test phone number

The phone number "+1111111111" may not be valid for testing. Consider using a proper test phone number format.

- phoneNumber: "+1111111111",
+ phoneNumber: "+1234567890", // Use a valid test format

40-47: Clean up commented placeholder code

The commented code contains placeholder descriptions that should either be implemented or removed to keep the test file clean.

- // rest of the sdk maps to HTTP API as close as possible:
- 
- // Contract.prepareCall()
- // Contract.write({ wallet, contractAddress, chainId, calls })
- // Contract.read({ contractAddress, chainId, calls })
- 
- // Transactions.send({ wallet, chainId, transaction })
- // Transactions.get(id)

Either implement these test cases or remove the comments entirely.


61-68: Remove or implement server wallet test code

The commented server wallet code suggests incomplete functionality. Either implement the test or remove the commented code.

- // const serverWalletAddress = await Wallets.createServerWallet({
- //   client,
- //   identifier: "test",
- // })
- // const serverWallet = Wallets.serverWallet({
- //   client,
- //   serverWalletAddress,
- // });

If this functionality is planned, consider adding a TODO comment instead.


89-90: Remove incomplete transaction polling comment

The commented transaction polling code should be implemented or removed to keep the test clean.

- // poll for status
- // Transactions.get(transactionId)
packages/thirdweb/src/v2/wallets/user.ts (2)

20-28: Address the TODO comment about gateway API

The TODO comment indicates this wallet generation logic will move to the gateway API. Consider tracking this as a technical debt item.

Would you like me to create a GitHub issue to track the migration of wallet generation to the gateway API?


21-21: Consider adding error handling for wallet generation

The wallet generation call doesn't have explicit error handling. While the underlying generateWallet function likely handles errors, consider adding context-specific error handling.

  if (!address) {
-   const wallet = await generateWallet({
-     client: args.client,
-     ecosystem: args.ecosystem,
-     authToken,
-   });
+   try {
+     const wallet = await generateWallet({
+       client: args.client,
+       ecosystem: args.ecosystem,
+       authToken,
+     });
+   } catch (error) {
+     throw new Error(`Failed to generate wallet: ${error.message}`);
+   }
    address = wallet.address;
  }
packages/api/src/configure.ts (1)

23-26: Consider adding type annotations for better clarity

While the biome-ignore comments explain the any usage, consider adding JSDoc comments to document the parameter types more clearly.

/**
 * Custom JSON.stringify that handles bigint serialization
 * @param value - The value to stringify
 * @param replacer - Optional replacer function
 * @param space - Optional spacing parameter
 */
function stringify(
  // biome-ignore lint/suspicious/noExplicitAny: JSON.stringify signature
  value: any,
packages/api/src/client/core/params.ts (1)

74-141: Complex but correct parameter mapping implementation

The function handles all parameter mapping scenarios correctly, including direct assignment, field mapping, special prefixes, and extra parameters. Consider adding inline comments to clarify the different mapping strategies for future maintainability.

packages/api/src/client/client/utils.ts (1)

296-301: Document JSON stringification behavior for object headers

The automatic JSON stringification of object header values assumes all objects should be serialized as JSON. This might not be appropriate for all use cases.

Consider making this behavior configurable or at least documenting it clearly in the function's JSDoc:

/**
 * Merges multiple header sources into a single Headers object.
 * Note: Object values are automatically JSON.stringify'd.
 * ...
 */
packages/api/src/client/client/types.ts (1)

201-222: Consider simplifying OptionsLegacyParser type

The OptionsLegacyParser type has deeply nested conditional types that are hard to understand and maintain. Consider refactoring into smaller, composable types for better readability.

type WithBody<T> = T extends { body?: any } ? T : T & Pick<RequestOptions, "body">;
type WithHeaders<T> = T extends { headers?: any } ? T : T & Pick<RequestOptions, "headers">;

export type OptionsLegacyParser<
    TData = unknown,
    ThrowOnError extends boolean = boolean,
    TResponseStyle extends ResponseStyle = "fields",
> = OmitKeys<RequestOptions<TResponseStyle, ThrowOnError>, "url"> &
    WithBody<WithHeaders<TData>>;

Comment on lines +4 to +18
// Incremental builds
// NOTE: Enabling incremental builds speeds up `tsc`. Keep in mind though that it does not reliably bust the cache when the `tsconfig.json` file changes.
"allowJs": false,
"allowSyntheticDefaultImports": true,
"checkJs": false,

// Interop constraints
"esModuleInterop": false,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"importHelpers": true,
// Incremental builds
// NOTE: Enabling incremental builds speeds up `tsc`. Keep in mind though that it does not reliably bust the cache when the `tsconfig.json` file changes.
"incremental": false,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Duplicated incremental-build note & contradictory setting

The “Incremental builds” comment appears twice and claims the option is enabled, yet "incremental" is set to false.
That’s misleading for future maintainers.

-    // Incremental builds
-    // NOTE: Enabling incremental builds speeds up `tsc`. …
-    "incremental": false,
+    // Incremental builds are disabled because the cache does not bust reliably.
+    "incremental": false,

Delete the earlier duplicate comment block (lines 4-6) or enable the flag instead.

🤖 Prompt for AI Agents
In packages/api/tsconfig.base.json between lines 4 and 18, there is a duplicated
comment about incremental builds and the "incremental" option is set to false,
which contradicts the comment. To fix this, remove the first duplicate comment
block around lines 4-6 or alternatively set "incremental" to true to match the
comment. Ensure the comments and settings are consistent to avoid confusion for
future maintainers.

Comment on lines +18 to +21
```ts
import { writeContract } from "@thirdweb-dev/api";

const result = await sendTransaction({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix the import/function call mismatch.

The example imports writeContract but calls sendTransaction, which will cause a runtime error.

Apply this fix:

-import { writeContract } from "@thirdweb-dev/api";
+import { sendTransaction } from "@thirdweb-dev/api";

Or alternatively, if writeContract is the correct function:

-const result = await sendTransaction({
+const result = await writeContract({
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```ts
import { writeContract } from "@thirdweb-dev/api";
const result = await sendTransaction({
import { sendTransaction } from "@thirdweb-dev/api";
const result = await sendTransaction({
🤖 Prompt for AI Agents
In packages/api/README.md around lines 18 to 21, the code imports writeContract
but calls sendTransaction, causing a runtime error. Fix this by either changing
the import to import sendTransaction from the correct module or update the
function call to writeContract if that is the intended function. Ensure the
import and function call match to avoid runtime issues.

Comment on lines +6 to +12
"useImportExtensions": {
"fix": "safe",
"level": "error",
"options": {
"forceJsExtensions": true
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

forceJsExtensions may break non-relative (bare module specifier) imports

The rule is applied globally, so it will enforce adding .js even on bare-specifier paths such as "react" or "@thirdweb-dev/sdk".
Biome’s own docs warn that the rule is only safe when "ignorePackageImports": true is set (or when limited to relative imports).

Consider narrowing the rule scope or adding

{
  "forceJsExtensions": true,
  "ignorePackageImports": true
}

to avoid turning valid bare imports into invalid ones.

🤖 Prompt for AI Agents
In packages/api/biome.json around lines 6 to 12, the "forceJsExtensions" option
is set to true globally, which incorrectly adds ".js" extensions to bare module
specifiers like "react" or "@thirdweb-dev/sdk". To fix this, add
"ignorePackageImports": true to the options object to ensure the rule only
applies to relative imports and does not break bare imports.

Comment on lines +3 to +6
export default defineConfig({
input: "https://api.thirdweb-dev.com/openapi.json",
output: { format: "biome", lint: "biome", path: "src/client" },
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Pin the OpenAPI schema version to guarantee reproducible builds

Fetching https://api.thirdweb-dev.com/openapi.json on every generation ties build determinism to a live, mutable endpoint.
If the spec changes, all generated client types will drift without code review.

Store a copy in-repo (or pin to a tagged commit URL) and update intentionally.

-  input: "https://api.thirdweb-dev.com/openapi.json",
+  // Prefer a pinned revision for reproducibility
+  input: "./schemas/[email protected]",

🏁 Script executed:

#!/bin/bash
# Search for a local 'schemas' directory
fd --type d --max-depth 3 schemas

# List all JSON files in the repo
fd --extension json

# Find JSON files mentioning 'openapi'
rg -l 'openapi' -g '*.json' || true

Length of output: 13458


Pin OpenAPI schema for reproducible builds

A live URL will produce drifting client types whenever the remote spec changes. To guarantee deterministic generations:

  • Add a copy of the OpenAPI spec under packages/api/schemas/, e.g.
    packages/api/schemas/[email protected]
  • Update the config in packages/api/openapi-ts.config.ts:
 export default defineConfig({
-  input: "https://api.thirdweb-dev.com/openapi.json",
+  // Point to a pinned, in-repo spec for stable builds
+  input: "./schemas/[email protected]",
   output: { format: "biome", lint: "biome", path: "src/client" },
 });

• Create the schemas folder next to openapi-ts.config.ts if it doesn’t exist.
• Commit the downloaded JSON with a clear date-or-version suffix.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default defineConfig({
input: "https://api.thirdweb-dev.com/openapi.json",
output: { format: "biome", lint: "biome", path: "src/client" },
});
export default defineConfig({
// Point to a pinned, in-repo spec for stable builds
input: "./schemas/[email protected]",
output: { format: "biome", lint: "biome", path: "src/client" },
});
🤖 Prompt for AI Agents
In packages/api/openapi-ts.config.ts around lines 3 to 6, the OpenAPI schema is
currently referenced via a live URL, which causes non-reproducible builds due to
changes in the remote spec. To fix this, download the OpenAPI JSON spec and save
it under packages/api/schemas/ with a clear date or version suffix, such as
[email protected]. Then update the input path in the config to
point to this local file instead of the URL. Also, create the schemas folder if
it does not exist and commit the downloaded JSON file to the repo.

Comment on lines +300 to 304
export * as Contracts from "../v2/contracts/index.js";

// namespaced API
export * as Wallets from "../v2/wallets/index.js";
export { deploySmartAccount } from "../wallets/smart/lib/signing.js";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Wildcard namespace re-exports balloon bundle size

export * as Contracts / Wallets forwards every internal symbol, including low-level helpers that were never meant for public API.
Once published these names are permanent, making future de-scoping a breaking change.

Audit the two index files and create explicit export lists instead, e.g.

-export * as Contracts from "../v2/contracts/index.js";
+export {
+  readContract,
+  writeContract,
+  type ContractCall,
+} from "../v2/contracts/index.js";

Same for Wallets.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/thirdweb/src/exports/thirdweb.ts around lines 300 to 304, the use of
wildcard namespace exports for Contracts and Wallets forwards all internal
symbols, including unintended low-level helpers, which increases bundle size and
risks breaking changes later. To fix this, audit the exports in
../v2/contracts/index.js and ../v2/wallets/index.js, then replace the wildcard
exports with explicit named exports listing only the intended public API symbols
for both Contracts and Wallets.

Comment on lines +16 to +19
it.skip("login with OTP", { retry: 0 }, async () => {
const client = createThirdwebClient({
clientId: "...",
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace placeholder client ID with test constant

The test uses a placeholder client ID ("..."), which would cause the test to fail if enabled. Use a proper test client ID or the existing TEST_CLIENT.

- const client = createThirdwebClient({
-   clientId: "...",
- });
+ const client = TEST_CLIENT;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it.skip("login with OTP", { retry: 0 }, async () => {
const client = createThirdwebClient({
clientId: "...",
});
it.skip("login with OTP", { retry: 0 }, async () => {
- const client = createThirdwebClient({
- clientId: "...",
- });
+ const client = TEST_CLIENT;
🤖 Prompt for AI Agents
In packages/thirdweb/src/v2/wallets/login.test.ts around lines 16 to 19, replace
the placeholder client ID string "..." with the proper test client ID constant,
such as TEST_CLIENT, to ensure the test runs correctly when enabled.

}
}

finalError = finalError || ({} as string);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Type inconsistency in error default assignment

The fallback assigns an empty object to finalError which is expected to be a string. Consider using an empty string or a more appropriate default error value.

-finalError = finalError || ({} as string);
+finalError = finalError || "";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
finalError = finalError || ({} as string);
finalError = finalError || "";
🤖 Prompt for AI Agents
In packages/api/src/client/client/client.ts at line 158, the fallback assignment
sets finalError to an empty object cast as a string, causing a type
inconsistency. Change the fallback to assign an empty string or another suitable
string value to finalError to maintain type consistency.

Comment on lines +49 to +51
if (value === undefined || value === null) {
continue;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle undefined/null path parameters more explicitly

When path parameters are undefined or null, the code continues without replacing the placeholder, which could leave {paramName} in the final URL. Consider throwing an error or providing a default value to ensure valid URLs.

 if (value === undefined || value === null) {
-    continue;
+    throw new Error(`Required path parameter '${name}' is ${value}`);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (value === undefined || value === null) {
continue;
}
if (value === undefined || value === null) {
throw new Error(`Required path parameter '${name}' is ${value}`);
}
🤖 Prompt for AI Agents
In packages/api/src/client/client/utils.ts around lines 49 to 51, the code
currently skips replacing path parameters if their value is undefined or null,
which can leave placeholders like {paramName} in the final URL. To fix this,
modify the code to explicitly check for undefined or null path parameters and
either throw a descriptive error indicating the missing parameter or assign a
sensible default value before continuing. This ensures the final URL is always
valid and complete.

Comment on lines +148 to +187
export const getParseAs = (
contentType: string | null,
): Exclude<Config["parseAs"], "auto"> => {
if (!contentType) {
// If no Content-Type header is provided, the best we can do is return the raw response body,
// which is effectively the same as the 'stream' option.
return "stream";
}

const cleanContent = contentType.split(";")[0]?.trim();

if (!cleanContent) {
return;
}

if (
cleanContent.startsWith("application/json") ||
cleanContent.endsWith("+json")
) {
return "json";
}

if (cleanContent === "multipart/form-data") {
return "formData";
}

if (
["application/", "audio/", "image/", "video/"].some((type) =>
cleanContent.startsWith(type),
)
) {
return "blob";
}

if (cleanContent.startsWith("text/")) {
return "text";
}

return;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix return type inconsistency

The function signature indicates it returns Exclude<Config["parseAs"], "auto"> but it can return undefined on lines 160 and 186. This violates the type contract and could cause runtime errors.

 export const getParseAs = (
     contentType: string | null,
-): Exclude<Config["parseAs"], "auto"> => {
+): Exclude<Config["parseAs"], "auto"> | undefined => {

Alternatively, provide a default return value like "stream" to match the type signature.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const getParseAs = (
contentType: string | null,
): Exclude<Config["parseAs"], "auto"> => {
if (!contentType) {
// If no Content-Type header is provided, the best we can do is return the raw response body,
// which is effectively the same as the 'stream' option.
return "stream";
}
const cleanContent = contentType.split(";")[0]?.trim();
if (!cleanContent) {
return;
}
if (
cleanContent.startsWith("application/json") ||
cleanContent.endsWith("+json")
) {
return "json";
}
if (cleanContent === "multipart/form-data") {
return "formData";
}
if (
["application/", "audio/", "image/", "video/"].some((type) =>
cleanContent.startsWith(type),
)
) {
return "blob";
}
if (cleanContent.startsWith("text/")) {
return "text";
}
return;
};
export const getParseAs = (
contentType: string | null,
): Exclude<Config["parseAs"], "auto"> | undefined => {
if (!contentType) {
// If no Content-Type header is provided, the best we can do is return the raw response body,
// which is effectively the same as the 'stream' option.
return "stream";
}
const cleanContent = contentType.split(";")[0]?.trim();
if (!cleanContent) {
return;
}
if (
cleanContent.startsWith("application/json") ||
cleanContent.endsWith("+json")
) {
return "json";
}
if (cleanContent === "multipart/form-data") {
return "formData";
}
if (
["application/", "audio/", "image/", "video/"].some((type) =>
cleanContent.startsWith(type),
)
) {
return "blob";
}
if (cleanContent.startsWith("text/")) {
return "text";
}
return;
};
🤖 Prompt for AI Agents
In packages/api/src/client/client/utils.ts between lines 148 and 187, the
function getParseAs has a return type excluding undefined, but it currently
returns undefined on lines 160 and 186. To fix this, ensure the function always
returns a valid value of the expected type by replacing those undefined returns
with a default value like "stream" to maintain type consistency and avoid
runtime errors.

Comment on lines +205 to +211
switch (auth.in) {
case "query":
if (!options.query) {
options.query = {};
}
options.query[name] = token;
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Consider security implications of query parameter authentication

Adding authentication tokens as query parameters (lines 206-211) can expose sensitive data in server logs, browser history, and proxy logs. Consider logging a warning or documenting this security consideration.

 case "query":
     if (!options.query) {
         options.query = {};
     }
+    // Security warning: Tokens in query parameters may be logged
+    console.warn(`Adding auth token '${name}' to query parameters. This may expose sensitive data in logs.`);
     options.query[name] = token;
     break;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
switch (auth.in) {
case "query":
if (!options.query) {
options.query = {};
}
options.query[name] = token;
break;
switch (auth.in) {
case "query":
if (!options.query) {
options.query = {};
}
// Security warning: Tokens in query parameters may be logged
console.warn(`Adding auth token '${name}' to query parameters. This may expose sensitive data in logs.`);
options.query[name] = token;
break;
🤖 Prompt for AI Agents
In packages/api/src/client/client/utils.ts around lines 205 to 211, the code
adds authentication tokens as query parameters, which can expose sensitive data
in logs and browser history. To address this, add a warning log message when
tokens are added to query parameters to highlight the security risk, or include
a clear comment documenting this security consideration for future maintainers.

* ```typescript
* import { createThirdwebClient } from "thirdweb/v2";
*
* const thirdwebClient = createThirdwebClient({
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reminder to fix this example

*
* ## Get a transaction by ID
* ```typescript
* import { Client, Transactions } from "thirdweb/v2";
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thinking more and more that we can get away with just import from "thirdweb" with the new namespaces. Just so much simpler. over time we can deprecate/move things out of the main export

confirmedAt: transaction.confirmedAt,
confirmedAtBlockNumber: transaction.confirmedAtBlockNumber,
cancelledAt: transaction.cancelledAt,
errorMessage: transaction.errorMessage,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thinking we probably need a 'status' top level here, in the API too

}));
}

export declare namespace list {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uppercase?

* import { Client, Transactions, Wallets } from "thirdweb/v2";
*
* const thirdwebClient = Client.init({
* clientId: "YOUR_CLIENT_ID",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're not initializing the client in the other 2 examples

if (!transactionIds) {
throw new Error("Failed to send transactions: no transaction ids");
}
return transactionIds;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tempted to change the API to be a single transaction id, feels simpler

@@ -0,0 +1 @@
export type EthereumAddress = `0x${string}`; // Joaquim do you think we should use the types from the existing SDK or create entirely new ones so we don't cross over at all?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think if we keep things namespaced we can get away with new types.

but tbh for addresses i still fill like 0x{string} is overkill. super annoying when you get back nested structs from api that you're just trying to feed back into a typed function. I would just use string, and validate at runtime

type SendCodeOptions,
sendCode,
type VerifyCodeOptions,
verifyLoginCode,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not loginWithCode?

type VerifyCodeOptions,
verifyLoginCode,
type SendLoginCodeOptions,
sendLoginCode,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can do sendVerificationCode if you think sendCode is confusing

Copy link
Contributor

This PR has been inactive for 7 days. It is now marked as stale and will be closed in 2 days if no further activity occurs.

@github-actions github-actions bot added the Stale label Jul 26, 2025
@github-actions github-actions bot closed this Jul 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
packages SDK Involves changes to the thirdweb SDK Stale
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants