Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Flags } from "@oclif/core";
import { BaseCommand } from "../../baseCommand.js";
import { validateIdentifier } from "../../utils/client.js";
import { identifierKindMap } from "../../utils/enums.js";

export default class ClientChangeRecoveryIdentifier extends BaseCommand {
Expand Down Expand Up @@ -55,9 +56,12 @@ The recovery identifier must be an Ethereum address.`;
const { flags } = await this.parse(ClientChangeRecoveryIdentifier);
const client = await this.initClient();

const identifierKind = identifierKindMap[flags.kind];
validateIdentifier(flags.identifier, identifierKind);

// Build identifier before confirming so invalid input fails fast
const identifier = {
identifierKind: identifierKindMap[flags.kind],
identifierKind,
identifier: flags.identifier.toLowerCase(),
};

Expand Down
6 changes: 5 additions & 1 deletion packages/xmtp-cli/src/commands/client/inbox-id.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Flags } from "@oclif/core";
import { BaseCommand } from "../../baseCommand.js";
import { validateIdentifier } from "../../utils/client.js";
import { identifierKindMap } from "../../utils/enums.js";

export default class ClientInboxId extends BaseCommand {
Expand Down Expand Up @@ -51,8 +52,11 @@ Returns null if the identifier has no associated inbox ID (not registered).`;
const { flags } = await this.parse(ClientInboxId);
const client = await this.initClient();

const identifierKind = identifierKindMap[flags.kind];
validateIdentifier(flags.identifier, identifierKind);

const identifier = {
identifierKind: identifierKindMap[flags.kind],
identifierKind,
identifier: flags.identifier.toLowerCase(),
};

Expand Down
6 changes: 5 additions & 1 deletion packages/xmtp-cli/src/commands/client/remove-account.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Flags } from "@oclif/core";
import { BaseCommand } from "../../baseCommand.js";
import { validateIdentifier } from "../../utils/client.js";
import { identifierKindMap } from "../../utils/enums.js";

export default class ClientRemoveAccount extends BaseCommand {
Expand Down Expand Up @@ -56,9 +57,12 @@ this client's inbox.`;
const { flags } = await this.parse(ClientRemoveAccount);
const client = await this.initClient();

const identifierKind = identifierKindMap[flags.kind];
validateIdentifier(flags.identifier, identifierKind);

// Build identifier before confirming so invalid input fails fast
const identifier = {
identifierKind: identifierKindMap[flags.kind],
identifierKind,
identifier: flags.identifier.toLowerCase(),
};

Expand Down
12 changes: 8 additions & 4 deletions packages/xmtp-cli/src/commands/conversation/add-members.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Args } from "@oclif/core";
import { IdentifierKind } from "@xmtp/node-sdk";
import { BaseCommand } from "../../baseCommand.js";
import { validateIdentifier } from "../../utils/client.js";
import { requireGroup } from "../../utils/conversation.js";

export default class ConversationAddMembers extends BaseCommand {
Expand Down Expand Up @@ -63,10 +64,13 @@ Requires appropriate permissions to add members (based on group settings).`;
this.error(`Conversation not found: ${args.id}`);
}

const identifiers = addresses.map((address) => ({
identifier: address.toLowerCase(),
identifierKind: IdentifierKind.Ethereum,
}));
const identifiers = addresses.map((address) => {
validateIdentifier(address, IdentifierKind.Ethereum);
return {
identifier: address.toLowerCase(),
identifierKind: IdentifierKind.Ethereum,
};
});

const group = requireGroup(conversation);
await group.addMembersByIdentifiers(identifiers);
Expand Down
12 changes: 8 additions & 4 deletions packages/xmtp-cli/src/commands/conversation/remove-members.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Args } from "@oclif/core";
import { IdentifierKind } from "@xmtp/node-sdk";
import { BaseCommand } from "../../baseCommand.js";
import { validateIdentifier } from "../../utils/client.js";
import { requireGroup } from "../../utils/conversation.js";

export default class ConversationRemoveMembers extends BaseCommand {
Expand Down Expand Up @@ -64,10 +65,13 @@ Requires appropriate permissions to remove members (based on group settings).`;
this.error(`Conversation not found: ${args.id}`);
}

const identifiers = addresses.map((address) => ({
identifier: address.toLowerCase(),
identifierKind: IdentifierKind.Ethereum,
}));
const identifiers = addresses.map((address) => {
validateIdentifier(address, IdentifierKind.Ethereum);
return {
identifier: address.toLowerCase(),
identifierKind: IdentifierKind.Ethereum,
};
});

const group = requireGroup(conversation);
await group.removeMembersByIdentifiers(identifiers);
Expand Down
6 changes: 5 additions & 1 deletion packages/xmtp-cli/src/commands/conversations/create-dm.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Args, Flags } from "@oclif/core";
import { BaseCommand } from "../../baseCommand.js";
import { validateIdentifier } from "../../utils/client.js";
import { identifierKindMap } from "../../utils/enums.js";

export default class ConversationsCreateDm extends BaseCommand {
Expand Down Expand Up @@ -50,9 +51,12 @@ Returns the DM's ID and details.`;
const { args, flags } = await this.parse(ConversationsCreateDm);
const client = await this.initClient();

const identifierKind = identifierKindMap[flags["identifier-kind"]];
validateIdentifier(args.identifier, identifierKind);

const identifier = {
identifier: args.identifier.toLowerCase(),
identifierKind: identifierKindMap[flags["identifier-kind"]],
identifierKind,
};

const dm = await client.conversations.createDmWithIdentifier(identifier);
Expand Down
12 changes: 8 additions & 4 deletions packages/xmtp-cli/src/commands/conversations/create-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
type CreateGroupOptions,
} from "@xmtp/node-sdk";
import { BaseCommand } from "../../baseCommand.js";
import { validateIdentifier } from "../../utils/client.js";

export default class ConversationsCreateGroup extends BaseCommand {
static description = `Create a new group conversation.
Expand Down Expand Up @@ -88,10 +89,13 @@ Returns the new group's ID and details.`;

const client = await this.initClient();

const identifierObjects = identifiers.map((id) => ({
identifier: id.toLowerCase(),
identifierKind: IdentifierKind.Ethereum,
}));
const identifierObjects = identifiers.map((id) => {
validateIdentifier(id, IdentifierKind.Ethereum);
return {
identifier: id.toLowerCase(),
identifierKind: IdentifierKind.Ethereum,
};
});

const permissionsMap: Record<string, GroupPermissionsOptions> = {
"all-members": GroupPermissionsOptions.Default,
Expand Down
11 changes: 10 additions & 1 deletion packages/xmtp-cli/src/utils/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
type NetworkOptions,
type Signer,
} from "@xmtp/node-sdk";
import { isHex, toBytes } from "viem";
import { isAddress, isHex, toBytes } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import type { XmtpConfig } from "./config.js";

Expand Down Expand Up @@ -66,6 +66,15 @@ export function hexToBytes(value: string): Uint8Array {
return toBytes(hex);
}

export function validateIdentifier(
identifier: string,
kind: IdentifierKind,
): void {
if (kind === IdentifierKind.Ethereum && !isAddress(identifier)) {
throw new Error(`Invalid Ethereum address: ${identifier}`);
}
}

export async function createClient(config: XmtpConfig): Promise<Client> {
if (!config.walletKey) {
throw new Error(
Expand Down
17 changes: 17 additions & 0 deletions packages/xmtp-cli/test/commands/conversations/create-dm.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import {
createRegisteredIdentity,
createTestIdentity,
parseJsonOutput,
runWithIdentity,
} from "../../helpers.js";
Expand Down Expand Up @@ -128,6 +129,22 @@ describe("conversations create-dm", () => {
expect(result.exitCode).not.toBe(0);
});

it("fails with an invalid ethereum address", async () => {
// Note: Use createTestIdentity so it validates fast locally and does not
// hit the docker network limit when running isolated testing
const sender = createTestIdentity();

const result = await runWithIdentity(sender, [
"conversations",
"create-dm",
"invalid-address",
"--json",
]);

expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain("Invalid Ethereum address");
});

it("both parties can see the DM", async () => {
const sender = await createRegisteredIdentity();
const recipient = await createRegisteredIdentity();
Expand Down