Skip to content
Draft
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
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ repositories {
dependencies {
implementation project(':expo-modules-core')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
implementation "org.xmtp:android:4.5.6"
implementation "org.xmtp:android:4.6.0-dev.c78f91f"
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.facebook.react:react-native:0.71.3'
implementation "com.daveanthonythomas.moshipack:moshipack:1.0.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,20 +163,22 @@ class XMTPModule : Module() {
return reactContext
}

private fun apiEnvironments(env: String, customLocalUrl: String? = null, appVersion: String? = null): ClientOptions.Api {
private fun apiEnvironments(env: String, customLocalUrl: String? = null, appVersion: String? = null, gatewayUrl: String? = null): ClientOptions.Api {
return when (env) {
"local" -> {
if (customLocalUrl.isNullOrBlank()) {
ClientOptions.Api(
env = XMTPEnvironment.LOCAL,
isSecure = false,
appVersion = appVersion,
gatewayUrl = gatewayUrl,
)
} else {
ClientOptions.Api(
env = XMTPEnvironment.LOCAL.withValue(customLocalUrl),
isSecure = false,
appVersion = appVersion,
gatewayUrl = gatewayUrl,
)
}
}
Expand All @@ -185,12 +187,14 @@ class XMTPModule : Module() {
env = XMTPEnvironment.PRODUCTION,
isSecure = true,
appVersion = appVersion,
gatewayUrl = gatewayUrl,
)

else -> ClientOptions.Api(
env = XMTPEnvironment.DEV,
isSecure = true,
appVersion = appVersion,
gatewayUrl = gatewayUrl,
)
}
}
Expand All @@ -216,7 +220,7 @@ class XMTPModule : Module() {
else -> "https://message-history.dev.ephemera.network/"
}
return ClientOptions(
api = apiEnvironments(authOptions.environment, authOptions.customLocalUrl, authOptions.appVersion),
api = apiEnvironments(authOptions.environment, authOptions.customLocalUrl, authOptions.appVersion, authOptions.gatewayUrl),
preAuthenticateToInboxCallback = preAuthenticateToInboxCallback,
appContext = context,
dbEncryptionKey = encryptionKeyBytes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,27 @@ class AuthParamsWrapper(
val deviceSyncEnabled: Boolean,
val debugEventsEnabled: Boolean,
val appVersion: String?,
val gatewayUrl: String?
) {
companion object {
fun authParamsFromJson(authParams: String): AuthParamsWrapper {
val jsonOptions = JsonParser.parseString(authParams).asJsonObject
return AuthParamsWrapper(
jsonOptions.get("environment").asString,
if (jsonOptions.has("dbDirectory")) jsonOptions.get("dbDirectory").asString else null,
if (jsonOptions.has("historySyncUrl")) jsonOptions.get("historySyncUrl").asString else null,
if (jsonOptions.has("customLocalUrl")) jsonOptions.get("customLocalUrl").asString else null,
if (jsonOptions.has("dbDirectory")) stringOrNull(jsonOptions.get("dbDirectory").asString) else null,
Copy link

Choose a reason for hiding this comment

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

jsonOptions.has(key) returns true even if the value is JsonNull. Calling asString on such values throws IllegalStateException. This can crash when dbDirectory, historySyncUrl, customLocalUrl, appVersion, or gatewayUrl are present but explicitly null in the JSON.

Consider guarding these reads with an isJsonNull check (e.g., only call asString when the value exists and is not JsonNull), and otherwise treat them as null so they flow through stringOrNull(...) or default to null.

-                if (jsonOptions.has("dbDirectory")) stringOrNull(jsonOptions.get("dbDirectory").asString) else null,
-                if (jsonOptions.has("historySyncUrl")) stringOrNull(jsonOptions.get("historySyncUrl").asString) else null,
-                if (jsonOptions.has("customLocalUrl")) stringOrNull(jsonOptions.get("customLocalUrl").asString) else null,
+                if (jsonOptions.has("dbDirectory") && !jsonOptions.get("dbDirectory").isJsonNull) stringOrNull(jsonOptions.get("dbDirectory").asString) else null,
+                if (jsonOptions.has("historySyncUrl") && !jsonOptions.get("historySyncUrl").isJsonNull) stringOrNull(jsonOptions.get("historySyncUrl").asString) else null,
+                if (jsonOptions.has("customLocalUrl") && !jsonOptions.get("customLocalUrl").isJsonNull) stringOrNull(jsonOptions.get("customLocalUrl").asString) else null,
                 if (jsonOptions.has("deviceSyncEnabled")) jsonOptions.get("deviceSyncEnabled").asBoolean else true,
                 if (jsonOptions.has("debugEventsEnabled")) jsonOptions.get("debugEventsEnabled").asBoolean else false,
-                if (jsonOptions.has("appVersion")) stringOrNull(jsonOptions.get("appVersion").asString) else null,
-                if (jsonOptions.has("gatewayUrl")) stringOrNull(jsonOptions.get("gatewayUrl").asString) else null,
+                if (jsonOptions.has("appVersion") && !jsonOptions.get("appVersion").isJsonNull) stringOrNull(jsonOptions.get("appVersion").asString) else null,
+                if (jsonOptions.has("gatewayUrl") && !jsonOptions.get("gatewayUrl").isJsonNull) stringOrNull(jsonOptions.get("gatewayUrl").asString) else null,
                 )

🚀 Reply to ask Macroscope to explain or update this suggestion.

👍 Helpful? React to give us feedback.

if (jsonOptions.has("historySyncUrl")) stringOrNull(jsonOptions.get("historySyncUrl").asString) else null,
if (jsonOptions.has("customLocalUrl")) stringOrNull(jsonOptions.get("customLocalUrl").asString) else null,
if (jsonOptions.has("deviceSyncEnabled")) jsonOptions.get("deviceSyncEnabled").asBoolean else true,
if (jsonOptions.has("debugEventsEnabled")) jsonOptions.get("debugEventsEnabled").asBoolean else false,
if (jsonOptions.has("appVersion")) jsonOptions.get("appVersion").asString else null,
if (jsonOptions.has("appVersion")) stringOrNull(jsonOptions.get("appVersion").asString) else null,
if (jsonOptions.has("gatewayUrl")) stringOrNull(jsonOptions.get("gatewayUrl").asString) else null,
)
}

// Helper function to convert empty strings to null
private fun stringOrNull(value: String?): String? {
return if (value.isNullOrEmpty()) null else value
}
}
}

Expand Down
1 change: 1 addition & 0 deletions example/EXAMPLE.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
TEST_PRIVATE_KEY=INSERT_TEST_PRIVATE_KEY_HERE
THIRD_WEB_CLIENT_ID=INSERT_CLIENT_ID_HERE
GATEWAY_URL=GATEWAY_PAYER_URL_HERE
10 changes: 5 additions & 5 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1736,16 +1736,16 @@ PODS:
- SQLCipher/standard (4.5.7):
- SQLCipher/common
- SwiftProtobuf (1.28.2)
- XMTP (4.5.5):
- XMTP (4.6.0-dev.4a8ee5d):
- Connect-Swift (= 1.0.0)
- CryptoSwift (= 1.8.3)
- SQLCipher (= 4.5.7)
- XMTPReactNative (5.0.3):
- XMTPReactNative (5.1.0-dev):
- CSecp256k1 (~> 0.2)
- ExpoModulesCore
- MessagePacker
- SQLCipher (= 4.5.7)
- XMTP (= 4.5.5)
- XMTP (= 4.6.0-dev.4a8ee5d)
- Yoga (0.0.0)

DEPENDENCIES:
Expand Down Expand Up @@ -2155,8 +2155,8 @@ SPEC CHECKSUMS:
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
SQLCipher: 5e6bfb47323635c8b657b1b27d25c5f1baf63bf5
SwiftProtobuf: 4dbaffec76a39a8dc5da23b40af1a5dc01a4c02d
XMTP: dc1eede674b3a4bd05d5ceee19ee002c95961f0f
XMTPReactNative: 421dfe638ba0c587a03fb5dd420512242b93ac1f
XMTP: a0ec6e29b9a643198103eff675e098635c146ebe
XMTPReactNative: 7b8f2bdd675e885e46b8a17d5a14e22fb5a87832
Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a

PODFILE CHECKSUM: 283c313cbc1ba9857a692b5901eb740dad922eca
Expand Down
50 changes: 49 additions & 1 deletion example/src/tests/clientTests.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ethers, Wallet } from 'ethers'
import RNFS from 'react-native-fs'
import Config from 'react-native-config'

Check warning on line 3 in example/src/tests/clientTests.ts

View workflow job for this annotation

GitHub Actions / lint

`react-native-config` import should occur before import of `react-native-fs`
import { ArchiveOptions } from 'xmtp-react-native-sdk/lib/ArchiveOptions'
import { InstallationId } from 'xmtp-react-native-sdk/lib/Client'

Check failure on line 5 in example/src/tests/clientTests.ts

View workflow job for this annotation

GitHub Actions / lint

'xmtp-react-native-sdk/lib/Client' imported multiple times

import { XMTPEnvironment } from 'xmtp-react-native-sdk/lib/Client'

Check failure on line 6 in example/src/tests/clientTests.ts

View workflow job for this annotation

GitHub Actions / lint

'xmtp-react-native-sdk/lib/Client' imported multiple times

Check warning on line 6 in example/src/tests/clientTests.ts

View workflow job for this annotation

GitHub Actions / lint

There should be at least one empty line between import groups
import {
Test,
assert,
Expand Down Expand Up @@ -293,6 +294,7 @@
dbEncryptionKey: keyBytes,
deviceSyncEnabled: false,
appVersion: '0.0.0',
gatewayUrl: 'https://xmtp3.org'

Check warning on line 297 in example/src/tests/clientTests.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `,`
})

const inboxId = await Client.getOrCreateInboxId(
Expand All @@ -307,6 +309,52 @@
return true
})

test('can make a new client and build existing from existing db using d14n staging testnet', async () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const keyBytes = new Uint8Array([
233, 120, 198, 96, 154, 65, 132, 17, 132, 96, 250, 40, 103, 35, 125, 64,
166, 83, 208, 224, 254, 44, 205, 227, 175, 49, 234, 129, 74, 252, 135, 145,
])
const dbDirPath = `${RNFS.DocumentDirectoryPath}/xmtp_db`
const directoryExists = await RNFS.exists(dbDirPath)
if (!directoryExists) {
await RNFS.mkdir(dbDirPath)
}
if (!Config.GATEWAY_URL) {
throw new Error('GATEWAY_URL environment variable is required. Please set it in your .env file (copy from EXAMPLE.env if needed).')

Check warning on line 324 in example/src/tests/clientTests.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `'GATEWAY_URL·environment·variable·is·required.·Please·set·it·in·your·.env·file·(copy·from·EXAMPLE.env·if·needed).'` with `⏎······'GATEWAY_URL·environment·variable·is·required.·Please·set·it·in·your·.env·file·(copy·from·EXAMPLE.env·if·needed).'⏎····`
}

const options = {
env: 'dev' as XMTPEnvironment,
dbEncryptionKey: keyBytes,
dbDirectory: dbDirPath,
deviceSyncEnabled: false,
appVersion: '0.0.0',
gatewayUrl: Config.GATEWAY_URL

Check warning on line 333 in example/src/tests/clientTests.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `,`
}

Check warning on line 335 in example/src/tests/clientTests.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `··`
const client = await Client.createRandom(options)

const inboxId = await Client.getOrCreateInboxId(

Check warning on line 338 in example/src/tests/clientTests.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `⏎····client.publicIdentity,⏎····'dev'⏎··` with `client.publicIdentity,·'dev'`
client.publicIdentity,
'dev'
)

assert(
client.inboxId === inboxId,
`inboxIds should match but were ${client.inboxId} and ${inboxId}`
)

const clientFromBundle = await Client.build(client.publicIdentity, options)

assert(
clientFromBundle.inboxId === client.inboxId,
`inboxIds should match but were ${clientFromBundle.inboxId} and ${client.inboxId}`
)

return true
})

test('static can message', async () => {
const [alix, bo] = await createClients(2)

Expand Down
1 change: 1 addition & 0 deletions example/src/types/react-native-config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ declare module 'react-native-config' {
THIRD_WEB_CLIENT_ID?: string
TEST_PRIVATE_KEY?: string
TEST_V3_PRIVATE_KEY?: string
GATEWAY_URL?: string
}

export const Config: NativeConfig
Expand Down
24 changes: 17 additions & 7 deletions ios/Wrappers/AuthParamsWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ struct AuthParamsWrapper {
let deviceSyncEnabled: Bool
let debugEventsEnabled: Bool
let appVersion: String?
let gatewayUrl: String?

init(
environment: String, dbDirectory: String?,
historySyncUrl: String?, customLocalUrl: String?,
deviceSyncEnabled: Bool, debugEventsEnabled: Bool,
appVersion: String?
appVersion: String?, gatewayUrl: String?
) {
self.environment = environment
self.dbDirectory = dbDirectory
Expand All @@ -30,6 +31,7 @@ struct AuthParamsWrapper {
self.deviceSyncEnabled = deviceSyncEnabled
self.debugEventsEnabled = debugEventsEnabled
self.appVersion = appVersion
self.gatewayUrl = gatewayUrl
}

static func authParamsFromJson(_ authParams: String) -> AuthParamsWrapper {
Expand All @@ -41,18 +43,19 @@ struct AuthParamsWrapper {
environment: "dev", dbDirectory: nil,
historySyncUrl: nil, customLocalUrl: nil,
deviceSyncEnabled: true, debugEventsEnabled: false,
appVersion: nil)
appVersion: nil, gatewayUrl: nil)
}

let environment = jsonOptions["environment"] as? String ?? "dev"
let dbDirectory = jsonOptions["dbDirectory"] as? String
let historySyncUrl = jsonOptions["historySyncUrl"] as? String
let customLocalUrl = jsonOptions["customLocalUrl"] as? String
let dbDirectory = Self.stringOrNil(jsonOptions["dbDirectory"] as? String)
let historySyncUrl = Self.stringOrNil(jsonOptions["historySyncUrl"] as? String)
let customLocalUrl = Self.stringOrNil(jsonOptions["customLocalUrl"] as? String)
let deviceSyncEnabled =
jsonOptions["deviceSyncEnabled"] as? Bool ?? true
let debugEventsEnabled =
jsonOptions["debugEventsEnabled"] as? Bool ?? false
let appVersion = jsonOptions["appVersion"] as? String
let appVersion = Self.stringOrNil(jsonOptions["appVersion"] as? String)
let gatewayUrl = Self.stringOrNil(jsonOptions["gatewayUrl"] as? String)

return AuthParamsWrapper(
environment: environment,
Expand All @@ -61,9 +64,16 @@ struct AuthParamsWrapper {
customLocalUrl: customLocalUrl,
deviceSyncEnabled: deviceSyncEnabled,
debugEventsEnabled: debugEventsEnabled,
appVersion: appVersion
appVersion: appVersion,
gatewayUrl: gatewayUrl
)
}

// Helper function to convert empty strings to nil
private static func stringOrNil(_ value: String?) -> String? {
guard let value = value, !value.isEmpty else { return nil }
return value
}
}

struct WalletParamsWrapper {
Expand Down
14 changes: 9 additions & 5 deletions ios/XMTPModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2987,7 +2987,7 @@ public class XMTPModule: Module {
case revokeInstallations = "revokeInstallations"
}

func createApiClient(env: String, customLocalUrl: String? = nil, appVersion: String? = nil)
func createApiClient(env: String, customLocalUrl: String? = nil, appVersion: String? = nil, gatewayUrl: String? = nil)
-> XMTP.ClientOptions.Api
{
switch env {
Expand All @@ -2998,19 +2998,22 @@ public class XMTPModule: Module {
return XMTP.ClientOptions.Api(
env: XMTP.XMTPEnvironment.local,
isSecure: false,
appVersion: appVersion
appVersion: appVersion,
gatewayUrl: gatewayUrl
)
case "production":
return XMTP.ClientOptions.Api(
env: XMTP.XMTPEnvironment.production,
isSecure: true,
appVersion: appVersion
appVersion: appVersion,
gatewayUrl: gatewayUrl
)
default:
return XMTP.ClientOptions.Api(
env: XMTP.XMTPEnvironment.dev,
isSecure: true,
appVersion: appVersion
appVersion: appVersion,
gatewayUrl: gatewayUrl
)
}
}
Expand All @@ -3025,7 +3028,8 @@ public class XMTPModule: Module {
api: createApiClient(
env: authOptions.environment,
customLocalUrl: authOptions.customLocalUrl,
appVersion: authOptions.appVersion
appVersion: authOptions.appVersion,
gatewayUrl: authOptions.gatewayUrl
),
preAuthenticateToInboxCallback: preAuthenticateToInboxCallback,
dbEncryptionKey: dbEncryptionKey,
Expand Down
2 changes: 1 addition & 1 deletion ios/XMTPReactNative.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Pod::Spec.new do |s|
s.source_files = "**/*.{h,m,swift}"

s.dependency "MessagePacker"
s.dependency "XMTP", "= 4.5.6"
s.dependency "XMTP", "= 4.6.0-dev.4a8ee5d"
s.dependency 'CSecp256k1', '~> 0.2'
s.dependency "SQLCipher", "= 4.5.7"
end
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@xmtp/react-native-sdk",
"version": "5.0.5",
"version": "5.1.0-dev",
"description": "Wraps for native xmtp sdks for react native",
"main": "build/index.js",
"types": "build/index.d.ts",
Expand Down
Loading
Loading