From 0bc999714f7bfe752c42a538e0252f1776534272 Mon Sep 17 00:00:00 2001 From: Isaac Hartford Date: Tue, 2 Dec 2025 14:16:17 +0800 Subject: [PATCH 1/4] Initial --- helpers/versions.ts | 70 +++++++++++++++++++++++++++++++++------------ workers/manager.ts | 7 +++-- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/helpers/versions.ts b/helpers/versions.ts index 5b805c4ea..958f0f979 100644 --- a/helpers/versions.ts +++ b/helpers/versions.ts @@ -172,11 +172,6 @@ export const regressionClient = async ( const loggingLevel = (process.env.LOGGING_LEVEL || "warn") as unknown as LogLevel; const apiUrl = apiURL; - if (apiUrl) { - console.debug( - `Creating API client with: SDK version: ${nodeBindings} walletKey: ${String(walletKey)} API URL: ${String(apiUrl)}`, - ); - } // Ensure the database directory exists const dbDir = path.dirname(dbPath); @@ -193,18 +188,41 @@ export const regressionClient = async ( const signer = createSigner(walletKey); + // D14N support: For bindings >= 1.6, use d14nHost parameter instead of apiUrl + // Parse version carefully to handle rc/dev versions + const versionNumber = parseFloat(nodeBindings.split('-')[0]); + const supportsD14N = versionNumber >= 1.6; + + const clientOptions: any = { + dbEncryptionKey, + dbPath, + env: env as unknown as XmtpEnv, + loggingLevel, + appVersion: APP_VERSION, + disableDeviceSync: true, + codecs: [new ReactionCodec(), new ReplyCodec()], + }; + + // Add D14N or legacy API URL parameter based on SDK version + if (supportsD14N && apiUrl) { + clientOptions.d14nHost = apiUrl; // For 1.6+: Use d14nHost for D14N gateway + console.log( + `[SDK ${nodeBindings}] Using D14N mode with gateway: ${apiUrl}`, + ); + } else if (apiUrl) { + clientOptions.apiUrl = apiUrl; // For older versions: Use apiUrl + console.log( + `[SDK ${nodeBindings}] Using legacy apiUrl override: ${apiUrl}`, + ); + } else { + console.log( + `[SDK ${nodeBindings}] Using default network endpoint for env: ${env}`, + ); + } + try { // @ts-expect-error - TODO: fix this - client = await ClientClass.create(signer, { - dbEncryptionKey, - dbPath, - env: env as unknown as XmtpEnv, - loggingLevel, - apiUrl, - appVersion: APP_VERSION, - disableDeviceSync: true, - codecs: [new ReactionCodec(), new ReplyCodec()], - }); + client = await ClientClass.create(signer, clientOptions); } catch (error) { // If database file is corrupted, try using a different path if ( @@ -222,17 +240,31 @@ export const regressionClient = async ( console.debug(`Using alternative database path: ${alternativeDbPath}`); // Try to create the client with the alternative path - // @ts-expect-error - TODO: fix this - client = await ClientClass.create(signer, { + const retryOptions: any = { dbEncryptionKey, dbPath: alternativeDbPath, env, loggingLevel, - apiUrl, appVersion: APP_VERSION, disableDeviceSync: true, codecs: [new ReactionCodec(), new ReplyCodec()], - }); + }; + + // Add D14N or legacy API URL parameter for retry + if (supportsD14N && apiUrl) { + retryOptions.d14nHost = apiUrl; + console.log( + `[SDK ${nodeBindings}] Retry: Using D14N mode with gateway: ${apiUrl}`, + ); + } else if (apiUrl) { + retryOptions.apiUrl = apiUrl; + console.log( + `[SDK ${nodeBindings}] Retry: Using legacy apiUrl override: ${apiUrl}`, + ); + } + + // @ts-expect-error - TODO: fix this + client = await ClientClass.create(signer, retryOptions); } else { throw error; } diff --git a/workers/manager.ts b/workers/manager.ts index e2890f3a3..ed08a5dc5 100644 --- a/workers/manager.ts +++ b/workers/manager.ts @@ -153,7 +153,7 @@ export class WorkerManager implements IWorkerManager { for (const worker of this.getAll()) { const groups = await worker.client.conversations.list(); await Promise.all( - groups.flat().map(async (g) => { + groups.flat().map(async (g: Group) => { const debugInfo = await g.debugInfo(); if (debugInfo.maybeForked || debugInfo.isCommitLogForked) { throw new Error( @@ -450,12 +450,15 @@ export class WorkerManager implements IWorkerManager { encryptionKey, }; + // Use provided apiUrl, or fallback to XMTP_API_URL environment variable + const effectiveApiUrl = apiUrl || process.env.XMTP_API_URL; + // Create and initialize the worker const workerClient = new WorkerClient( workerData, this.env, {}, - apiUrl, + effectiveApiUrl, undefined, ); From 59d1ecd4a43238208ea2868e93477a9a28168c9a Mon Sep 17 00:00:00 2001 From: Isaac Hartford Date: Wed, 3 Dec 2025 18:38:41 +0800 Subject: [PATCH 2/4] Gate v3/v4 on env var switch --- .env.example | 9 ++++++++ helpers/versions.ts | 52 ++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/.env.example b/.env.example index ce4ec8799..b410bf3fd 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,12 @@ # MAIN XMTP_ENV="dev" # xmtp environment (dev,production,local,multinode) LOGGING_LEVEL="error" # rust library logs + +# D14N MODE (V4) +# Set both to enable D14N mode: +# XMTP_D14N="true" # Enable D14N mode +# XMTP_API_URL="https://grpc.testnet-staging.xmtp.network:443" # D14N gateway URL + +# V3 MODE (default) +# Set only XMTP_API_URL without XMTP_D14N to use custom V3 endpoint: +# XMTP_API_URL="https://custom-v3-endpoint.example.com" diff --git a/helpers/versions.ts b/helpers/versions.ts index 958f0f979..0e65202a0 100644 --- a/helpers/versions.ts +++ b/helpers/versions.ts @@ -161,6 +161,15 @@ export const checkNoNameContains = (versionList: typeof VersionList) => { } }; +/** + * Check if D14N mode is enabled via environment variable + * Set XMTP_D14N=true to enable D14N mode + */ +export const isD14NEnabled = (): boolean => { + const d14nEnv = process.env.XMTP_D14N; + return d14nEnv === "true" || d14nEnv === "1"; +}; + export const regressionClient = async ( nodeBindings: string, walletKey: `0x${string}`, @@ -171,7 +180,6 @@ export const regressionClient = async ( ): Promise => { const loggingLevel = (process.env.LOGGING_LEVEL || "warn") as unknown as LogLevel; - const apiUrl = apiURL; // Ensure the database directory exists const dbDir = path.dirname(dbPath); @@ -188,10 +196,9 @@ export const regressionClient = async ( const signer = createSigner(walletKey); - // D14N support: For bindings >= 1.6, use d14nHost parameter instead of apiUrl - // Parse version carefully to handle rc/dev versions - const versionNumber = parseFloat(nodeBindings.split('-')[0]); - const supportsD14N = versionNumber >= 1.6; + // Check if D14N mode is explicitly enabled + const d14nEnabled = isD14NEnabled(); + const apiUrl = apiURL || process.env.XMTP_API_URL; const clientOptions: any = { dbEncryptionKey, @@ -203,21 +210,21 @@ export const regressionClient = async ( codecs: [new ReactionCodec(), new ReplyCodec()], }; - // Add D14N or legacy API URL parameter based on SDK version - if (supportsD14N && apiUrl) { - clientOptions.d14nHost = apiUrl; // For 1.6+: Use d14nHost for D14N gateway - console.log( - `[SDK ${nodeBindings}] Using D14N mode with gateway: ${apiUrl}`, - ); + // D14N mode: Use d14nHost parameter + // V3 mode: Use apiUrl parameter (or default endpoints) + if (d14nEnabled) { + if (!apiUrl) { + throw new Error( + "XMTP_D14N=true requires XMTP_API_URL to be set with the D14N gateway URL", + ); + } + clientOptions.d14nHost = apiUrl; + console.log(`[D14N] Using D14N gateway: ${apiUrl}`); } else if (apiUrl) { - clientOptions.apiUrl = apiUrl; // For older versions: Use apiUrl - console.log( - `[SDK ${nodeBindings}] Using legacy apiUrl override: ${apiUrl}`, - ); + clientOptions.apiUrl = apiUrl; + console.log(`[V3] Using custom API URL: ${apiUrl}`); } else { - console.log( - `[SDK ${nodeBindings}] Using default network endpoint for env: ${env}`, - ); + console.log(`[V3] Using default network endpoint for env: ${env}`); } try { @@ -250,17 +257,10 @@ export const regressionClient = async ( codecs: [new ReactionCodec(), new ReplyCodec()], }; - // Add D14N or legacy API URL parameter for retry - if (supportsD14N && apiUrl) { + if (d14nEnabled && apiUrl) { retryOptions.d14nHost = apiUrl; - console.log( - `[SDK ${nodeBindings}] Retry: Using D14N mode with gateway: ${apiUrl}`, - ); } else if (apiUrl) { retryOptions.apiUrl = apiUrl; - console.log( - `[SDK ${nodeBindings}] Retry: Using legacy apiUrl override: ${apiUrl}`, - ); } // @ts-expect-error - TODO: fix this From f87f8de9871995b26fa9424873723cf82235395e Mon Sep 17 00:00:00 2001 From: Isaac Hartford Date: Wed, 3 Dec 2025 19:01:20 +0800 Subject: [PATCH 3/4] eslint --- helpers/versions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers/versions.ts b/helpers/versions.ts index 0e65202a0..156d52837 100644 --- a/helpers/versions.ts +++ b/helpers/versions.ts @@ -228,7 +228,7 @@ export const regressionClient = async ( } try { - // @ts-expect-error - TODO: fix this + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument client = await ClientClass.create(signer, clientOptions); } catch (error) { // If database file is corrupted, try using a different path @@ -263,7 +263,7 @@ export const regressionClient = async ( retryOptions.apiUrl = apiUrl; } - // @ts-expect-error - TODO: fix this + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument client = await ClientClass.create(signer, retryOptions); } else { throw error; From fa2dba4303ff5240685e7b747208c5b257912e84 Mon Sep 17 00:00:00 2001 From: Isaac Hartford Date: Wed, 3 Dec 2025 23:19:30 +0800 Subject: [PATCH 4/4] Linter --- helpers/versions.ts | 4 ++-- workers/manager.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/helpers/versions.ts b/helpers/versions.ts index 156d52837..8c7972e82 100644 --- a/helpers/versions.ts +++ b/helpers/versions.ts @@ -229,7 +229,7 @@ export const regressionClient = async ( try { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - client = await ClientClass.create(signer, clientOptions); + client = await ClientClass.create(signer as any, clientOptions); } catch (error) { // If database file is corrupted, try using a different path if ( @@ -264,7 +264,7 @@ export const regressionClient = async ( } // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - client = await ClientClass.create(signer, retryOptions); + client = await ClientClass.create(signer as any, retryOptions); } else { throw error; } diff --git a/workers/manager.ts b/workers/manager.ts index ed08a5dc5..ca160c649 100644 --- a/workers/manager.ts +++ b/workers/manager.ts @@ -153,8 +153,9 @@ export class WorkerManager implements IWorkerManager { for (const worker of this.getAll()) { const groups = await worker.client.conversations.list(); await Promise.all( - groups.flat().map(async (g: Group) => { - const debugInfo = await g.debugInfo(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + groups.flat().map(async (g: any) => { + const debugInfo = await (g as Group).debugInfo(); if (debugInfo.maybeForked || debugInfo.isCommitLogForked) { throw new Error( `${forkDetectedString} Stopping test, group id ${g.id} may have forked`,