Skip to content

Commit

Permalink
SDK optimizations (#798)
Browse files Browse the repository at this point in the history
* Add tsx to node SDK dev deps

* Add performance testing scripts to node SDK

* Use || to ensure string values

* Optimize safe conversation conversion

* Ensure URL values in Browser SDK

* Create late-tips-pump.md
  • Loading branch information
rygine authored Jan 23, 2025
1 parent db36e20 commit 25e0e15
Show file tree
Hide file tree
Showing 10 changed files with 493 additions and 40 deletions.
6 changes: 6 additions & 0 deletions .changeset/late-tips-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@xmtp/browser-sdk": patch
"@xmtp/node-sdk": patch
---

SDK optimizations
75 changes: 44 additions & 31 deletions sdks/browser-sdk/src/utils/conversions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,38 +312,51 @@ export type SafeConversation = {

export const toSafeConversation = async (
conversation: WorkerConversation,
): Promise<SafeConversation> => ({
id: conversation.id,
name: conversation.name,
imageUrl: conversation.imageUrl,
description: conversation.description,
pinnedFrameUrl: conversation.pinnedFrameUrl,
permissions: {
policyType: conversation.permissions.policyType,
policySet: {
addAdminPolicy: conversation.permissions.policySet.addAdminPolicy,
addMemberPolicy: conversation.permissions.policySet.addMemberPolicy,
removeAdminPolicy: conversation.permissions.policySet.removeAdminPolicy,
removeMemberPolicy: conversation.permissions.policySet.removeMemberPolicy,
updateGroupDescriptionPolicy:
conversation.permissions.policySet.updateGroupDescriptionPolicy,
updateGroupImageUrlSquarePolicy:
conversation.permissions.policySet.updateGroupImageUrlSquarePolicy,
updateGroupNamePolicy:
conversation.permissions.policySet.updateGroupNamePolicy,
updateGroupPinnedFrameUrlPolicy:
conversation.permissions.policySet.updateGroupPinnedFrameUrlPolicy,
updateMessageExpirationPolicy:
conversation.permissions.policySet.updateMessageExpirationPolicy,
): Promise<SafeConversation> => {
const id = conversation.id;
const name = conversation.name;
const imageUrl = conversation.imageUrl;
const description = conversation.description;
const pinnedFrameUrl = conversation.pinnedFrameUrl;
const permissions = conversation.permissions;
const isActive = conversation.isActive;
const addedByInboxId = conversation.addedByInboxId;
const metadata = await conversation.metadata();
const admins = conversation.admins;
const superAdmins = conversation.superAdmins;
const createdAtNs = conversation.createdAtNs;
const policyType = permissions.policyType;
const policySet = permissions.policySet;
return {
id,
name,
imageUrl,
description,
pinnedFrameUrl,
permissions: {
policyType,
policySet: {
addAdminPolicy: policySet.addAdminPolicy,
addMemberPolicy: policySet.addMemberPolicy,
removeAdminPolicy: policySet.removeAdminPolicy,
removeMemberPolicy: policySet.removeMemberPolicy,
updateGroupDescriptionPolicy: policySet.updateGroupDescriptionPolicy,
updateGroupImageUrlSquarePolicy:
policySet.updateGroupImageUrlSquarePolicy,
updateGroupNamePolicy: policySet.updateGroupNamePolicy,
updateGroupPinnedFrameUrlPolicy:
policySet.updateGroupPinnedFrameUrlPolicy,
updateMessageExpirationPolicy: policySet.updateMessageExpirationPolicy,
},
},
},
isActive: conversation.isActive,
addedByInboxId: conversation.addedByInboxId,
metadata: await conversation.metadata(),
admins: conversation.admins,
superAdmins: conversation.superAdmins,
createdAtNs: conversation.createdAtNs,
});
isActive,
addedByInboxId,
metadata,
admins,
superAdmins,
createdAtNs,
};
};

export type SafeInstallation = {
bytes: Uint8Array;
Expand Down
6 changes: 3 additions & 3 deletions sdks/browser-sdk/src/utils/createClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export const createClient = async (
// initialize WASM module
await init();

const host = options?.apiUrl ?? ApiUrls[options?.env ?? "dev"];
const host = options?.apiUrl || ApiUrls[options?.env || "dev"];
const dbPath =
options?.dbPath ?? `xmtp-${options?.env ?? "dev"}-${accountAddress}.db3`;
options?.dbPath || `xmtp-${options?.env || "dev"}-${accountAddress}.db3`;

const inboxId =
(await getInboxIdForAddress(host, accountAddress)) ||
Expand All @@ -30,7 +30,7 @@ export const createClient = async (
options.performanceLogging);

const historySyncUrl =
options?.historySyncUrl ?? HistorySyncUrls[options?.env ?? "dev"];
options?.historySyncUrl || HistorySyncUrls[options?.env || "dev"];

return createWasmClient(
host,
Expand Down
1 change: 1 addition & 0 deletions sdks/node-sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scripts/accounts.json
3 changes: 3 additions & 0 deletions sdks/node-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"clean:dist": "rimraf dist",
"clean:tests": "rimraf test/*.db3* ||:",
"dev": "yarn build --watch",
"generate:accounts": "tsx scripts/accounts.ts",
"generate:groups": "tsx scripts/groups.ts",
"test": "vitest run",
"test:cov": "vitest run --coverage",
"typecheck": "tsc"
Expand All @@ -66,6 +68,7 @@
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-filesize": "^10.0.0",
"rollup-plugin-tsconfig-paths": "^1.5.2",
"tsx": "^4.19.2",
"typescript": "^5.7.3",
"uint8array-extras": "^1.4.0",
"uuid": "^11.0.3",
Expand Down
29 changes: 29 additions & 0 deletions sdks/node-sdk/scripts/accounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { writeFile } from "node:fs/promises";
import path from "node:path";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";

type Account = [key: string, address: string];
type AccountsMap = Record<string, string>;

const createRandomAccount = (): Account => {
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
return [privateKey, account.address];
};

const writeAccountsJson = async (accounts: AccountsMap) => {
console.log("Writing accounts to file...");
const accountsJsonPath = path.join(import.meta.dirname, "accounts.json");
await writeFile(accountsJsonPath, JSON.stringify(accounts, null, 2));
console.log(`Accounts data written to '${accountsJsonPath}'`);
};

const main = async () => {
console.log("Creating 1000 accounts...");
const accounts = Object.fromEntries(
Array.from({ length: 1000 }, () => createRandomAccount()),
);
await writeAccountsJson(accounts);
};

await main();
116 changes: 116 additions & 0 deletions sdks/node-sdk/scripts/groups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { randomBytes } from "node:crypto";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { createWalletClient, http, toBytes } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
import { Client } from "@/Client";

export const createUser = (key: string) => {
const account = privateKeyToAccount(key as `0x${string}`);
return {
key,
account,
wallet: createWalletClient({
account,
chain: sepolia,
transport: http(),
}),
};
};

type User = ReturnType<typeof createUser>;

export const createSigner = (user: User) => {
return {
getAddress: () => user.account.address,
signMessage: async (message: string) => {
const signature = await user.wallet.signMessage({
message,
});
return toBytes(signature);
},
};
};

export const createRegisteredClient = async (user: User, dbPath?: string) => {
return Client.create(createSigner(user), randomBytes(32), {
env: "local",
dbPath,
});
};

const accountsJsonPath = path.join(import.meta.dirname, "accounts.json");
const parsedAccounts = JSON.parse(
await readFile(accountsJsonPath, "utf-8"),
) as Record<string, string>;

type Account = { key: string; address: string };
const accounts: Account[] = Object.entries(parsedAccounts).map(
([key, address]) => ({ key, address }),
);

const primaryAccount = accounts.shift() as Account;

const primaryAccountClient = await createRegisteredClient(
createUser(primaryAccount.key),
"./test.db3",
);

console.log("Registering accounts...");

for (const a of accounts) {
await createRegisteredClient(createUser(a.key));
}

const groups = [];

console.log("Creating groups...");

// create a bunch of groups
while (accounts.length > 200) {
const groupsAccounts = accounts.splice(0, 4);
const group = await primaryAccountClient.conversations.newGroup(
groupsAccounts.map((a) => a.address),
);
groups.push(group);
}

console.log(`Created ${groups.length} groups`);

console.log(`Sending "gm" message into each group...`);

for (const group of groups) {
await group.send("gm");
}

console.log("Creating DM groups...");

const dmGroups = [];

while (accounts.length > 0) {
const dmGroup = await primaryAccountClient.conversations.newDm(
(accounts.pop() as Account).address,
);
dmGroups.push(dmGroup);
}

console.log(`Created ${dmGroups.length} DM groups`);

console.log("Sending 'gm' message into each DM group...");

for (const dmGroup of dmGroups) {
await dmGroup.send("gm");
}

console.log("Syncing all conversations...");

await primaryAccountClient.conversations.syncAll();

console.log("Querying DM groups...");

const groupConvos = primaryAccountClient.conversations.listGroups();
const dmConvos = primaryAccountClient.conversations.listDms();

console.log(`Found ${dmConvos.length} DM conversations`);
console.log(`Found ${groupConvos.length} group conversations`);
10 changes: 5 additions & 5 deletions sdks/node-sdk/src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@ export class Client {
options?: ClientOptions,
) {
const accountAddress = await signer.getAddress();
const host = options?.apiUrl ?? ApiUrls[options?.env ?? "dev"];
const host = options?.apiUrl || ApiUrls[options?.env || "dev"];
const isSecure = host.startsWith("https");
const dbPath =
options?.dbPath ??
options?.dbPath ||
join(
process.cwd(),
`xmtp-${options?.env ?? "dev"}-${accountAddress}.db3`,
`xmtp-${options?.env || "dev"}-${accountAddress}.db3`,
);

const inboxId =
Expand All @@ -140,7 +140,7 @@ export class Client {
};

const historySyncUrl =
options?.historySyncUrl ?? HistorySyncUrls[options?.env ?? "dev"];
options?.historySyncUrl || HistorySyncUrls[options?.env || "dev"];

const client = new Client(
await createClient(
Expand Down Expand Up @@ -353,7 +353,7 @@ export class Client {

static async canMessage(accountAddresses: string[], env?: XmtpEnv) {
const accountAddress = "0x0000000000000000000000000000000000000000";
const host = ApiUrls[env ?? "dev"];
const host = ApiUrls[env || "dev"];
const isSecure = host.startsWith("https");
const inboxId =
(await getInboxIdForAddress(host, isSecure, accountAddress)) ||
Expand Down
3 changes: 2 additions & 1 deletion sdks/node-sdk/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
"types": ["vitest/globals"]
},
"include": [
"rollup.config.js",
"scripts/**/*",
"src/**/*",
"test/**/*",
"rollup.config.js",
"vitest.config.ts",
"vitest.setup.ts"
]
Expand Down
Loading

0 comments on commit 25e0e15

Please sign in to comment.