Skip to content
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

Refactor signing in browser and node SDKs #718

Merged
merged 9 commits into from
Nov 15, 2024
Merged

Conversation

rygine
Copy link
Collaborator

@rygine rygine commented Nov 15, 2024

Summary

This PR brings back the Signer interface to simplify the signing process. Client.create now requires passing in a Signer.

Changes

  • Renamed address property of Client to accountAddress in the browser SDK to match the node SDK
  • Removed direct access to all signature functions
  • Added automatic registration of clients after they are created
  • Added disableAutoRegister to ClientOptions to allow disabling of client registration after creation
  • Added Client.register method for registering a client
  • Added Client.addAccount method for adding another account to an installation
  • Added Client.removeAccount method for removing an account from an installation
  • Added Client.revokeInstallations method for revoking all other installations

Other changes

  • Added static Client.canMessage for checking if an address is on the network without a client
  • Ensured that all test DBs are removed after running tests
  • Added environment to DB path in the node SDK
  • Ignored @xmtp/react-vite-browser-sdk-example example for releases

Summary by CodeRabbit

  • New Features

    • Introduced new methods for account management: addAccount, removeAccount, and revokeInstallations.
    • Added a static method canMessage for checking messaging capabilities without an instance of the client.
    • New optional property disableAutoRegister added to client options.
    • Enhanced the client creation process by integrating a signer object for signing messages.
    • Added new types and functions related to signing, improving the signing interface.
  • Bug Fixes

    • Improved clarity of action names in the client and worker classes.
  • Documentation

    • Updated test cases to reflect changes in method names and functionality, enhancing clarity and maintainability.
  • Chores

    • Streamlined import statements and removed unnecessary functions in test files.

@rygine rygine requested a review from a team as a code owner November 15, 2024 16:28
Copy link

changeset-bot bot commented Nov 15, 2024

🦋 Changeset detected

Latest commit: a1a16a0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@xmtp/browser-sdk Patch
@xmtp/node-sdk Patch

Not sure what this means? Click here to learn what changesets are.

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

Copy link

coderabbitai bot commented Nov 15, 2024

Walkthrough

The pull request introduces changes across multiple files in both the browser and node SDKs. Key modifications include updating the changeset configuration to ignore a specific package, enhancing the Client class with new methods and refactored signatures, and renaming methods for clarity. New types and functions related to signers are defined, and test cases are updated to reflect these changes. Overall, the changes streamline functionality and improve the clarity of method names and signatures.

Changes

File Path Change Summary
.changeset/config.json Added "@xmtp/react-vite-browser-sdk-example" to the "ignore" array.
sdks/browser-sdk/src/Client.ts Updated constructor to accept Signer and accountAddress, renamed methods, added addAccount and removeAccount, and introduced canMessage.
sdks/browser-sdk/src/WorkerClient.ts Renamed methods for clarity: getCreateInboxSignatureText to createInboxSignatureText, etc.
sdks/browser-sdk/src/index.ts Added new exports: isSmartContractSigner, Signer, and SmartContractSigner.
sdks/browser-sdk/src/types/clientEvents.ts Renamed action identifiers for clarity and updated property names.
sdks/browser-sdk/src/types/options.ts Added optional property disableAutoRegister to OtherOptions.
sdks/browser-sdk/src/utils/signer.ts Defined new types and a type guard function for signer interface.
sdks/browser-sdk/src/workers/client.ts Renamed action cases in onmessage event for clarity.
sdks/browser-sdk/test/Client.test.ts Updated tests to reflect method changes and simplified signature handling.
sdks/browser-sdk/test/Utils.test.ts Updated parameter in getInboxIdForAddress test from client.address to client.accountAddress.
sdks/browser-sdk/test/helpers.ts Introduced createSigner function and updated client creation logic.
sdks/node-sdk/src/Client.ts Updated constructor to accept Signer, added methods for account management, and refactored signature handling.
sdks/node-sdk/src/helpers/signer.ts Defined new types and a type guard function for signer interface.
sdks/node-sdk/src/index.ts Added exports for isSmartContractSigner, Signer, and SmartContractSigner.
sdks/node-sdk/test/Client.test.ts Simplified wallet association tests and added tests for canMessage.
sdks/node-sdk/test/helpers.ts Introduced createSigner function and updated client creation logic.
sdks/node-sdk/vitest.setup.ts Modified teardown function to broaden .db3 file search scope.
examples/react-vite-browser-sdk/src/createClient.ts Removed getSignature, introduced inline signer object for signing messages.

Possibly related PRs

  • Add 1:1 messaging to Browser SDK #703: The main PR modifies the changeset/config.json to ignore a specific package, while this PR adds new methods to the Client class and updates existing ones, which may relate to how the SDK handles certain packages or functionalities.
  • Update Browser SDK #713: This PR introduces an encryption key requirement for creating a client, which aligns with the changes in the main PR regarding configuration management and package handling.
  • Update Node SDK #715: Similar to the main PR, this update enhances the Node SDK with an encryption key requirement and updates the canMessage method, indicating a direct relationship in terms of security and functionality improvements.

Suggested reviewers

  • nplasterer
  • insipx

🐰 In the code, we hop and play,
With signatures and accounts, we find our way.
Methods renamed, clarity in sight,
A new signer here, oh what a delight!
With each little change, our code grows bright,
In the land of SDKs, we take flight! 🌟


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 78bfcc2 and a1a16a0.

📒 Files selected for processing (1)
  • .changeset/lovely-swans-bow.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changeset/lovely-swans-bow.md

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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

Copy link

@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: 6

🧹 Outside diff range and nitpick comments (33)
.changeset/config.json (1)

15-15: LGTM! Consider adding a comment to document the ignore reason.

The addition of "@xmtp/react-vite-browser-sdk-example" to the ignore list is appropriate as example packages typically don't need versioning.

Consider adding a comment to document why this package is ignored:

-  "ignore": ["@xmtp/react-vite-browser-sdk-example"]
+  // Ignore example packages as they don't need versioning
+  "ignore": ["@xmtp/react-vite-browser-sdk-example"]
sdks/node-sdk/vitest.setup.ts (1)

8-11: Consider enhancing test cleanup strategy

Given the PR's introduction of new Client features (account management, installations), the test cleanup strategy should be more comprehensive:

  1. Consider implementing a test context manager to track created databases
  2. Add cleanup for both account and installation-specific databases
  3. Implement proper cleanup between test cases to prevent interference

Would you like assistance in implementing a more robust test cleanup strategy that handles these scenarios?

sdks/browser-sdk/src/utils/signer.ts (2)

6-12: Consider enhancing documentation for smart contract wallet fields.

The Signer type is well-designed with a clear separation between core and smart contract wallet functionality. However, the documentation could be more detailed.

Consider expanding the comment to explain why these fields are specific to smart contract wallets:

-  // these fields indicate that the signer is a smart contract wallet
+  /**
+   * The following fields are specific to smart contract wallets:
+   * - getBlockNumber: Required for tracking on-chain state
+   * - getChainId: Required for network identification and signature validation
+   */

14-19: Consider strengthening the type guard implementation.

While the current implementation is clean, it might give false positives if properties exist but are undefined.

Consider this more robust implementation:

 export const isSmartContractSigner = (
   signer: Signer,
 ): signer is SmartContractSigner =>
-  "getBlockNumber" in signer && "getChainId" in signer;
+  typeof signer.getBlockNumber === 'function' && 
+  typeof signer.getChainId === 'function';

This change ensures the properties are actually callable functions rather than just existing properties.

sdks/node-sdk/src/helpers/signer.ts (3)

1-4: LGTM! Consider adding JSDoc comments.

The type definitions are well-structured and handle both synchronous and asynchronous cases appropriately. The return types are suitable for their respective blockchain operations.

Consider adding JSDoc comments to document the purpose and usage of each type:

+/** Signs a message and returns the signature as a byte array */
 export type SignMessage = (message: string) => Promise<Uint8Array> | Uint8Array;
+/** Returns the account address associated with the signer */
 export type GetAddress = () => Promise<string> | string;
+/** Returns the current chain ID */
 export type GetChainId = () => bigint;
+/** Returns the current block number */
 export type GetBlockNumber = () => bigint;

6-12: LGTM! Consider adding method constraints.

The Signer type is well-structured with a clear separation between basic and smart contract wallet functionality.

Consider adding constraints to prevent invalid combinations:

 export type Signer = {
   getAddress: GetAddress;
   signMessage: SignMessage;
   // these fields indicate that the signer is a smart contract wallet
-  getBlockNumber?: GetBlockNumber;
-  getChainId?: GetChainId;
+  getBlockNumber?: GetBlockNumber & (GetChainId extends undefined ? never : GetBlockNumber);
+  getChainId?: GetChainId & (GetBlockNumber extends undefined ? never : GetChainId);
 };

This ensures that either both smart contract methods are present or neither is present, preventing partial implementations.


14-19: LGTM! Consider adding runtime validation.

The SmartContractSigner type and type guard are well-implemented using TypeScript's type system features.

Consider enhancing the type guard with runtime validation:

 export const isSmartContractSigner = (
   signer: Signer,
-): signer is SmartContractSigner =>
-  "getBlockNumber" in signer && "getChainId" in signer;
+): signer is SmartContractSigner => {
+  if (!("getBlockNumber" in signer && "getChainId" in signer)) {
+    return false;
+  }
+  return typeof signer.getBlockNumber === 'function' && 
+         typeof signer.getChainId === 'function';
+};

This ensures that the methods not only exist but are also functions, providing stronger runtime guarantees.

sdks/browser-sdk/src/types/options.ts (1)

51-54: Enhance documentation for the disableAutoRegister option

The implementation looks good and aligns with the PR objectives. However, consider enhancing the documentation to be more explicit about the default behavior and implications.

Consider updating the JSDoc comment to be more descriptive:

  /**
-  * Disable automatic registration when creating a client
+  * Disable automatic registration when creating a client.
+  * By default (false), clients are automatically registered upon creation.
+  * When set to true, you must manually register the client using Client.register().
   */
  disableAutoRegister?: boolean;
sdks/browser-sdk/test/helpers.ts (2)

31-40: Add error handling for signature failures

While the implementation is clean, consider adding error handling for potential signature failures. Users might reject signing requests or the wallet connection might fail.

 export const createSigner = (user: User): Signer => {
   return {
     getAddress: () => user.account.address,
     signMessage: async (message: string) => {
+      try {
         const signature = await user.wallet.signMessage({
           message,
         });
         return toBytes(signature);
+      } catch (error) {
+        throw new Error(`Failed to sign message: ${error.message}`);
+      }
     },
   };
 };

61-68: Reduce duplication in environment option handling

The environment option spreading logic is duplicated between createClient and createRegisteredClient. Consider extracting this common logic.

+const getDefaultOptions = (options?: ClientOptions) => ({
+  ...options,
+  env: options?.env ?? "local",
+});
+
 export const createClient = async (user: User, options?: ClientOptions) => {
-  const opts = {
-    ...options,
-    env: options?.env ?? "local",
-  };
+  const opts = getDefaultOptions(options);
   return Client.create(createSigner(user), testEncryptionKey, {
     ...opts,
     disableAutoRegister: true,
     dbPath: `./test-${user.uuid}.db3`,
   });
 };

 export const createRegisteredClient = async (
   user: User,
   options?: ClientOptions,
 ) => {
-  const opts = {
-    ...options,
-    env: options?.env ?? "local",
-  };
+  const opts = getDefaultOptions(options);
   return Client.create(createSigner(user), testEncryptionKey, {
     ...opts,
     dbPath: `./test-${user.uuid}.db3`,
   });
 };
sdks/browser-sdk/src/WorkerClient.ts (3)

Line range hint 49-84: Consider enhancing error handling to provide more context.

Currently, all methods silently return undefined on error. Consider either:

  1. Logging errors for debugging
  2. Propagating specific error types to callers
  3. Adding error context for better troubleshooting

Example implementation:

  async createInboxSignatureText() {
    try {
      return await this.#client.createInboxSignatureText();
    } catch (error) {
-     return undefined;
+     console.error('Failed to create inbox signature text:', error);
+     throw new Error('Failed to create inbox signature text: ' + error.message);
    }
  }

Line range hint 57-66: Align parameter naming between Worker and WASM client layers.

The Worker layer uses accountAddress while the WASM client uses wallet terminology (e.g., addWalletSignatureText). Consider aligning these terms for better maintainability.

Example alignment:

  async addAccountSignatureText(accountAddress: string) {
    try {
-     return await this.#client.addWalletSignatureText(
+     return await this.#client.addAccountSignatureText(
        this.#accountAddress,
        accountAddress,
      );
    } catch {
      return undefined;
    }
  }

Found an outdated method call that needs updating

The example code in examples/react-vite-browser-sdk/src/createClient.ts is still using the old method name getCreateInboxSignatureText(), while the SDK has been updated to use createInboxSignatureText().

  • examples/react-vite-browser-sdk/src/createClient.ts: Update getCreateInboxSignatureText() to createInboxSignatureText()
🔗 Analysis chain

Line range hint 49-84: Verify all consumers are updated to use new method names.

Since these methods are part of the public API and have been renamed, we should ensure all consumers are updated.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for old method names to ensure they're not being used
rg -l "getCreateInboxSignatureText|getAddWalletSignatureText|getRevokeWalletSignatureText|getRevokeInstallationsSignatureText" \
   --type ts \
   --type tsx

Length of output: 177


Script:

#!/bin/bash
# Search for old method names to ensure they're not being used, fixing the file type
rg -l "getCreateInboxSignatureText|getAddWalletSignatureText|getRevokeWalletSignatureText|getRevokeInstallationsSignatureText" \
   --type typescript

# Also search for current method names to understand usage
rg -l "createInboxSignatureText|addWalletSignatureText|revokeWalletSignatureText|revokeInstallationsSignatureText" \
   --type typescript

# Look for any class/interface that might be extending/implementing WorkerClient
ast-grep --pattern 'class $_ extends WorkerClient'
ast-grep --pattern 'interface $_ extends WorkerClient'

Length of output: 461


Script:

#!/bin/bash
# Search for old method names without file type restriction
rg "getCreateInboxSignatureText|getAddWalletSignatureText|getRevokeWalletSignatureText|getRevokeInstallationsSignatureText"

# Search for current method names and their usages with context
rg -A 2 "createInboxSignatureText|addWalletSignatureText|revokeWalletSignatureText|revokeInstallationsSignatureText"

# Find files that import or use WorkerClient
rg "WorkerClient"

Length of output: 5961

sdks/node-sdk/test/Client.test.ts (3)

38-45: Consider parameterizing the network value.

While the test effectively covers the new static canMessage method, consider parameterizing the "local" network value to make the test more maintainable and reusable across different environments.

-  const canMessage = await Client.canMessage([user.account.address], "local");
+  const network = process.env.XMTP_NETWORK || "local";
+  const canMessage = await Client.canMessage([user.account.address], network);

Line range hint 108-129: Consider adding edge case tests for account management.

While the basic functionality is well tested, consider adding the following test cases to improve coverage:

  1. Attempting to add an already associated account
  2. Attempting to remove a non-existent account
  3. Attempting to add/remove accounts with invalid addresses

Example test case:

it("should handle duplicate account addition gracefully", async () => {
  const user = createUser();
  const user2 = createUser();
  const client = await createRegisteredClient(user);
  const signer2 = createSigner(user2);
  
  await client.addAccount(signer2);
  // Attempt to add the same account again
  await expect(client.addAccount(signer2)).rejects.toThrow();
});

Line range hint 1-200: Add test coverage for automatic registration features.

The test suite is missing coverage for some key features introduced in the PR:

  1. Automatic client registration upon creation
  2. The disableAutoRegister option in ClientOptions
  3. Manual registration using Client.register

Example test cases to add:

it("should auto-register client by default", async () => {
  const user = createUser();
  const client = await Client.create(createSigner(user));
  expect(client.isRegistered).toBe(true);
});

it("should not auto-register when disabled", async () => {
  const user = createUser();
  const client = await Client.create(createSigner(user), {
    disableAutoRegister: true
  });
  expect(client.isRegistered).toBe(false);
  
  // Test manual registration
  await client.register();
  expect(client.isRegistered).toBe(true);
});
sdks/browser-sdk/src/types/clientEvents.ts (1)

Update needed in example code to match new signature method names

The type changes are implemented consistently across the SDKs, but there's an outdated reference in the example code:

  • examples/react-vite-browser-sdk/src/createClient.ts still uses the old method name getCreateInboxSignatureText instead of the new createInboxSignatureText

The browser and node SDKs have been properly updated with the new method names, showing consistent implementation of:

  • createInboxSignatureText
  • addAccountSignatureText
  • removeAccountSignatureText
  • revokeInstallationsSignatureText
🔗 Analysis chain

Line range hint 46-72: LGTM! The renaming changes improve consistency.

The changes align well with the PR objectives by:

  • Removing redundant "get" prefixes
  • Standardizing terminology from "Wallet" to "Account"
  • Using more descriptive parameter names (e.g., newAccountAddress)

Let's verify the consistency of these naming changes across the codebase:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that old action names are completely removed and new ones are consistently used

# Check for any remaining old action names
echo "Checking for old action names..."
rg -l "getCreateInboxSignatureText|getAddWalletSignatureText|getRevokeWalletSignatureText|getRevokeInstallationsSignatureText"

# Check usage of new action names
echo "Checking usage of new action names..."
rg -l "createInboxSignatureText|addAccountSignatureText|removeAccountSignatureText|revokeInstallationsSignatureText"

Length of output: 637


Script:

#!/bin/bash
# Let's check the content of the example file that still contains old names
echo "Content of example file with old names:"
rg -A 2 -B 2 "getCreateInboxSignatureText|getAddWalletSignatureText|getRevokeWalletSignatureText|getRevokeInstallationsSignatureText" examples/react-vite-browser-sdk/src/createClient.ts

# Let's also verify the implementation in Client.ts files to ensure consistency
echo -e "\nRelevant sections from browser-sdk Client.ts:"
rg -A 2 -B 2 "createInboxSignatureText|addAccountSignatureText|removeAccountSignatureText|revokeInstallationsSignatureText" sdks/browser-sdk/src/Client.ts

echo -e "\nRelevant sections from node-sdk Client.ts:"
rg -A 2 -B 2 "createInboxSignatureText|addAccountSignatureText|removeAccountSignatureText|revokeInstallationsSignatureText" sdks/node-sdk/src/Client.ts

Length of output: 3458

sdks/browser-sdk/src/workers/client.ts (2)

Line range hint 76-115: Add JSDoc comments to document the signature actions.

Consider adding JSDoc comments to document the purpose and parameters of each signature action. This would improve code maintainability and help developers understand when to use each type of signature.

Example for the first action:

+      /**
+       * Generates the signature text for creating a new inbox.
+       * This is part of the client initialization process.
+       * @returns {Promise<string>} The text to be signed
+       */
       case "createInboxSignatureText": {
         const result = await client.createInboxSignatureText();
         postMessage({

Update required in example code for signature action rename

The signature action renaming is incomplete. While the main SDK code has been updated, there's still a reference to the old method name in the example code:

  • examples/react-vite-browser-sdk/src/createClient.ts still uses getCreateInboxSignatureText instead of the new createInboxSignatureText
🔗 Analysis chain

Line range hint 76-115: Verify client-side code updates for renamed signature actions.

The renaming of signature-related actions improves clarity and consistency. However, we should ensure that all client-side code has been updated to use these new action names:

  • createInboxSignatureText
  • addAccountSignatureText
  • removeAccountSignatureText
  • revokeInstallationsSignatureText
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any remaining references to old action names
# Expected: No matches should be found for old names

echo "Searching for old action names..."
rg -l "getCreateInboxSignatureText|getAddWalletSignatureText|getRevokeWalletSignatureText|getRevokeInstallationsSignatureText" \
   --type ts \
   --type tsx

Length of output: 254


Script:

#!/bin/bash
# Description: Search for any remaining references to old action names
# Expected: No matches should be found for old names

echo "Searching for old action names..."
rg -l "getCreateInboxSignatureText|getAddWalletSignatureText|getRevokeWalletSignatureText|getRevokeInstallationsSignatureText"

# Also search for the new action names to verify they are properly used
echo -e "\nSearching for new action names..."
rg -l "createInboxSignatureText|addAccountSignatureText|removeAccountSignatureText|revokeInstallationsSignatureText"

Length of output: 637


Script:

#!/bin/bash
# Let's check the specific content in the example file that still uses old names
echo "Content of file still using old names:"
rg "getCreateInboxSignatureText|getAddWalletSignatureText|getRevokeWalletSignatureText|getRevokeInstallationsSignatureText" -A 2 -B 2 examples/react-vite-browser-sdk/src/createClient.ts

Length of output: 487

sdks/browser-sdk/test/Client.test.ts (4)

17-17: Ensure consistent address casing in expectations

To prevent potential case sensitivity issues, consider normalizing addresses by converting them to lowercase when performing comparisons.

Apply this diff to adjust the assertion:

-    expect(client.accountAddress).toBe(user.account.address);
+    expect(client.accountAddress.toLowerCase()).toBe(user.account.address.toLowerCase());

87-87: Ensure added account's functionality is tested

While the test confirms that the account addresses include the newly added account, consider adding assertions to verify that the new account (user2) can perform expected operations, such as sending messages.

For example, you could attempt to initiate a conversation or send a message using the added account to confirm its proper integration.


104-105: Consider verifying the effects of account removal

To fully validate the removal of the wallet association, consider adding a test that ensures user2 can no longer interact with the client after removeAccount is called.

For instance, try to perform an action with user2 that should fail or expect a specific error or result indicating the account has been removed.


130-130: Enhance test coverage after revoking installations

To comprehensively verify the effect of revokeInstallations(), consider adding assertions to confirm that prior installations (client and client2) are no longer able to access or interact with the inbox or perform client operations.

For example, you could attempt to perform client operations using client or client2 and assert that they fail as expected due to the revocation.

sdks/browser-sdk/src/Client.ts (4)

35-35: Storing Signer instance may have security implications

Storing the Signer instance (#signer) as a private field might lead to security concerns if the Signer contains sensitive information. Consider whether it's necessary to store it long-term, or if you can limit its scope to the methods that require it.


127-146: Consider handling exceptions during signing and message sending

In the #addSignature method, both the signing process and sending messages may fail. It's important to handle exceptions to provide meaningful error messages and to avoid unhandled promise rejections.

Consider wrapping the signing and sending in try-catch blocks:

try {
  const signature = await signer.signMessage(signatureText);

  if (isSmartContractSigner(signer)) {
    await this.sendMessage("addScwSignature", {
      type: signatureType,
      bytes: signature,
      chainId: await signer.getChainId(),
      blockNumber: await signer.getBlockNumber(),
    });
  } else {
    await this.sendMessage("addSignature", {
      type: signatureType,
      bytes: signature,
    });
  }
} catch (error) {
  throw new Error(`Failed to add signature: ${error.message}`);
}

170-192: Handle potential errors when adding a new account

In the addAccount method, if any of the steps fail, such as obtaining the signature text or applying signatures, the errors might not be properly handled. Consider adding error handling to inform users of failures.


235-250: Ensure Client.canMessage cleans up resources to prevent memory leaks

In the static Client.canMessage method, an instance of Client is created but not explicitly disposed of after use. Ensure any resources or listeners are properly cleaned up.

sdks/node-sdk/src/Client.ts (6)

82-85: Ensure default 'disableAutoRegister' behavior is clearly documented

The disableAutoRegister option defaults to false, causing clients to automatically register upon creation. Please ensure this default behavior is prominently documented to prevent any unintended usage by clients unaware of the automatic registration process.


Line range hint 170-176: Consider logging exceptions in '#createInboxSignatureText' for better debugging

The method catches all exceptions and returns null, potentially obscuring underlying errors. Including logging within the catch block can aid in debugging by providing visibility into any issues encountered during execution.

Apply this diff to log exceptions:

 async #createInboxSignatureText() {
   try {
     const signatureText = await this.#innerClient.createInboxSignatureText();
     return signatureText;
   } catch (error) {
+    console.error('Error creating inbox signature text:', error);
     return null;
   }
 }

Line range hint 180-192: Consider logging exceptions in signature text methods for better error tracking

Methods like #addAccountSignatureText, #removeAccountSignatureText, and #revokeInstallationsSignatureText silently return null on failure. Logging the caught exceptions can provide valuable insights during debugging and error monitoring.

For example, modify #addAccountSignatureText:

 async #addAccountSignatureText(
   existingAccountAddress: string,
   newAccountAddress: string,
 ) {
   try {
     const signatureText = await this.#innerClient.addWalletSignatureText(
       existingAccountAddress,
       newAccountAddress,
     );
     return signatureText;
   } catch (error) {
+    console.error('Error generating add account signature text:', error);
     return null;
   }
 }

260-262: Enhance error message in 'addAccount' method for clarity

Including the newAccountAddress in the error message provides more context, aiding in troubleshooting when the signature text cannot be generated.

Apply this diff to enhance the error message:

 if (!signatureText) {
-  throw new Error("Unable to generate add account signature text");
+  throw new Error(`Unable to generate add account signature text for account ${await newAccountSigner.getAddress()}`);
 }

283-285: Enhance error message in 'removeAccount' method for clarity

Including the accountAddress in the error message offers better context for debugging when the signature text generation fails.

Apply this diff:

 if (!signatureText) {
-  throw new Error("Unable to generate remove account signature text");
+  throw new Error(`Unable to generate remove account signature text for account ${accountAddress}`);
 }

299-301: Enhance error message in 'revokeInstallations' method for clarity

Providing additional context in the error message can assist in identifying issues during the revocation process.

Apply this diff:

 if (!signatureText) {
-  throw new Error("Unable to generate revoke installations signature text");
+  throw new Error("Unable to generate signature text for revoking installations");
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 083aba0 and d7e39b8.

📒 Files selected for processing (17)
  • .changeset/config.json (1 hunks)
  • sdks/browser-sdk/src/Client.ts (6 hunks)
  • sdks/browser-sdk/src/WorkerClient.ts (2 hunks)
  • sdks/browser-sdk/src/index.ts (1 hunks)
  • sdks/browser-sdk/src/types/clientEvents.ts (1 hunks)
  • sdks/browser-sdk/src/types/options.ts (1 hunks)
  • sdks/browser-sdk/src/utils/signer.ts (1 hunks)
  • sdks/browser-sdk/src/workers/client.ts (3 hunks)
  • sdks/browser-sdk/test/Client.test.ts (5 hunks)
  • sdks/browser-sdk/test/Utils.test.ts (1 hunks)
  • sdks/browser-sdk/test/helpers.ts (3 hunks)
  • sdks/node-sdk/src/Client.ts (10 hunks)
  • sdks/node-sdk/src/helpers/signer.ts (1 hunks)
  • sdks/node-sdk/src/index.ts (1 hunks)
  • sdks/node-sdk/test/Client.test.ts (5 hunks)
  • sdks/node-sdk/test/helpers.ts (2 hunks)
  • sdks/node-sdk/vitest.setup.ts (1 hunks)
🔇 Additional comments (20)
sdks/node-sdk/vitest.setup.ts (1)

9-10: ⚠️ Potential issue

Consider adding safeguards for database cleanup

The broader glob pattern "**/*.db3*" could potentially delete non-test databases. Consider these improvements:

  1. Add a prefix/suffix pattern to identify test databases (e.g., "test-.db3" or ".test.db3")
  2. Restrict cleanup to a specific test directory to prevent accidental deletion of databases outside the test scope

Here's a suggested implementation:

-  const files = await glob("**/*.db3*", { cwd: __dirname });
-  await Promise.all(files.map((file) => unlink(join(__dirname, file))));
+  const testDbDir = join(__dirname, 'test-dbs');
+  const files = await glob("*.test.db3*", { cwd: testDbDir });
+  await Promise.all(files.map((file) => unlink(join(testDbDir, file))));

Let's verify if there are any database files that might be affected:

sdks/browser-sdk/src/utils/signer.ts (1)

1-4: LGTM! Well-structured type definitions.

The type definitions are well-designed with the following good practices:

  • Proper handling of both async and sync operations for signing and address retrieval
  • Correct use of bigint for blockchain-related numbers
  • Clear and descriptive type names
sdks/browser-sdk/test/Utils.test.ts (1)

17-20: LGTM! Property rename is correctly implemented.

The change from client.address to client.accountAddress aligns with the PR objective of ensuring consistency between browser and node SDKs.

Let's verify that this property rename has been consistently applied across all test files:

✅ Verification successful

Property rename has been consistently applied across test files

The verification results show that:

  • No instances of client.address remain in the test files
  • client.accountAddress is used consistently in both Utils.test.ts and Client.test.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining instances of client.address in test files
# and ensure client.accountAddress is used consistently.

# Search for any remaining instances of client.address
echo "Checking for remaining instances of client.address:"
rg "client\.address" "sdks/browser-sdk/test"

# Search for new instances of client.accountAddress to verify consistent usage
echo -e "\nVerifying usage of client.accountAddress:"
rg "client\.accountAddress" "sdks/browser-sdk/test"

Length of output: 472

sdks/browser-sdk/src/index.ts (1)

37-41: LGTM! The new signer exports align with the PR objectives.

The exports support the new signing architecture and enable smart contract signing capabilities.

Let's verify the documentation and implementation:

✅ Verification successful

Let me try to find the correct path for the signer file first.


The signer exports are well-documented and correctly implemented

The verification confirms that:

  • The Signer interface is properly defined with essential methods (getAddress and signMessage)
  • SmartContractSigner extends Signer with additional required fields (getBlockNumber and getChainId)
  • isSmartContractSigner function correctly type guards between regular and smart contract signers
  • All types are well-documented with clear comments explaining the smart contract wallet indicators

The exports in the index file correctly reflect these implementations and align with the PR objectives.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check documentation and implementation of signer types

# Check for documentation in the source file
echo "Checking signer documentation..."
rg -A 10 "interface Signer" "./utils/signer"

# Verify smart contract signer implementation
echo "Checking smart contract signer implementation..."
ast-grep --pattern 'function isSmartContractSigner($_) {
  $$$
}'

Length of output: 349


Script:

#!/bin/bash
# Find the signer file
echo "Looking for signer file..."
fd "signer.ts"

# Once we find it, let's check its content
echo "Checking file content..."
fd "signer.ts" --exec cat {}

Length of output: 1674

sdks/node-sdk/src/index.ts (1)

45-49: LGTM! New exports align with the signing refactor.

The addition of Signer and SmartContractSigner types along with the isSmartContractSigner function supports the PR's objective of streamlining the signing process.

sdks/browser-sdk/test/helpers.ts (2)

12-12: LGTM: Import of Signer type

The addition of the Signer type import aligns with the PR's objective of introducing the Signer interface.


43-43: LGTM: Type export placement

The User type export is correctly placed and maintains its functionality.

sdks/browser-sdk/src/WorkerClient.ts (1)

Line range hint 49-84: LGTM! Method names are now more intuitive and aligned with domain terminology.

The renaming of these methods improves clarity and maintains consistency with the broader SDK changes.

sdks/node-sdk/test/Client.test.ts (1)

1-11: LGTM! Import changes align with the new signing architecture.

The simplified imports and removal of signature-related imports reflect the architectural changes to handle signatures through the new Signer interface.

sdks/node-sdk/test/helpers.ts (4)

33-42: createSigner Function Implementation Approved

The createSigner function correctly encapsulates the signing logic by returning a Signer object with getAddress and signMessage methods. This enhances code modularity and readability.


45-46: User Type Export Relocated Appropriately

Moving the User type export improves code organization and clarity. This change helps in maintaining a logical structure within the file.


52-54: Confirm the Use of disableAutoRegister: true in createClient

In the createClient function, disableAutoRegister is explicitly set to true. Verify that this aligns with the intended test behavior, ensuring that clients created with this function should indeed avoid automatic registration.


63-70: createRegisteredClient Function Updated Correctly

The createRegisteredClient function now utilizes createSigner(user) without setting disableAutoRegister, allowing automatic registration as expected. This aligns with the purpose of creating a registered client.

sdks/browser-sdk/test/Client.test.ts (2)

1-9: Imports and helper functions are correctly updated

The import statements and helper functions are appropriately updated to reflect the refactored codebase.


39-46: New test case for static method Client.canMessage

The added test case effectively verifies the functionality of Client.canMessage without requiring a client instance.

sdks/browser-sdk/src/Client.ts (1)

86-87: Possibility of leaking encryptionKey if exceptions occur

In the Client.create method, if client.init() or client.register() throw exceptions, the encryptionKey may be left in memory without the client being usable. Consider whether you need to securely handle or clear the encryptionKey in such scenarios.

sdks/node-sdk/src/Client.ts (4)

109-112: Verify backward compatibility of 'Client.create' method signature change

The create method now requires a signer as the first parameter instead of the accountAddress. This alteration may impact clients using the previous method signature. Ensure that this change is communicated clearly, and consider updating documentation and versioning to reflect any breaking changes.


329-331: Ensure that the dummy signer in 'static canMessage' method is appropriate

The signMessage function returns an empty Uint8Array, which may not be valid if any signing operations are inadvertently triggered. Verify that the client does not perform any signing that would fail due to the dummy signer, to prevent unexpected errors.


317-338: Verify the use of zero address and potential database conflicts in 'static canMessage' method

The method uses the zero address (0x0000000000000000000000000000000000000000) and constructs a database path based on it. This could lead to conflicts if multiple instances are created simultaneously or if the database is shared unintentionally. Ensure that using the zero address does not cause side effects and consider using a unique, temporary address or in-memory database if persistence is not required.

Run the following script to check for other usages of the zero address and assess potential conflicts:

#!/bin/bash
# Description: Search for usages of the zero address and potential dbPath conflicts.

# Find all instances of the zero address
rg -A 2 $'"0x0000000000000000000000000000000000000000"'

# Search for client instantiations using the zero address
rg -A 5 $'createClient\(.*"0x0000000000000000000000000000000000000000"'

99-102: Assess potential impact of 'Client' constructor signature change

The Client constructor now includes a signer parameter, which may affect external code if the constructor is used directly. Verify that all client instantiations occur through the Client.create method to prevent any breaking changes.

Run the following script to identify any direct usages of the Client constructor:

sdks/node-sdk/src/index.ts Show resolved Hide resolved
sdks/browser-sdk/test/helpers.ts Show resolved Hide resolved
sdks/node-sdk/test/Client.test.ts Show resolved Hide resolved
sdks/browser-sdk/src/Client.ts Show resolved Hide resolved
sdks/browser-sdk/src/Client.ts Show resolved Hide resolved
sdks/browser-sdk/src/Client.ts Show resolved Hide resolved
Copy link

cloudflare-workers-and-pages bot commented Nov 15, 2024

Deploying xmtp-js-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: a1a16a0
Status: ✅  Deploy successful!
Preview URL: https://0a6269ab.xmtp-js.pages.dev
Branch Preview URL: https://rygine-update-signing-api.xmtp-js.pages.dev

View logs

@rygine rygine requested a review from a team as a code owner November 15, 2024 16:59
@rygine rygine merged commit 6418271 into main Nov 15, 2024
15 checks passed
@rygine rygine deleted the rygine/update-signing-api branch November 15, 2024 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants