Skip to content

Commit

Permalink
Merge pull request #79 from xmtp/np/app-version
Browse files Browse the repository at this point in the history
Support App Version
  • Loading branch information
nplasterer authored Jul 26, 2023
2 parents d9f5e8a + c6e48b9 commit 37f28f1
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 78 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ const { Client } = require('@xmtp/xmtp-react-native')
async function main() {
//Create a random wallet for example purposes. On the frontend you should replace it with the user's wallet (metamask, rainbow, etc)
//Initialize the xmtp client
const xmtp = await XMTP.Client.createRandom("dev");
const xmtp = await XMTP.Client.createRandom({ env: "dev" });

//In this example we are going to broadcast to the GM_BOT wallet (already activated) and a random wallet (not activated)
const GM_BOT = '0x937C0d4a6294cdfa575de17382c7076b579DC176'
Expand Down Expand Up @@ -362,7 +362,7 @@ import { Client } from '@xmtp/xmtp-react-native'
// Get the keys using a valid Signer. Save them somewhere secure.
const keys = await Client.exportKeyBundle()
// Create a client using keys returned from getKeys
const client = await Client.createFromKeyBundle(keys, "dev")
const client = await Client.createFromKeyBundle(keys, { env: "dev" })
```

The keys returned by `exportKeyBundle` should be treated with the utmost care as compromise of these keys will allow an attacker to impersonate the user on the XMTP network. Ensure these keys are stored somewhere secure and encrypted.
Expand Down
41 changes: 27 additions & 14 deletions android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,27 @@ fun Conversation.cacheKey(clientAddress: String): String {
}

class XMTPModule : Module() {
private val apiEnvironments = mapOf(
"local" to ClientOptions.Api(env = XMTPEnvironment.LOCAL, isSecure = false),
"dev" to ClientOptions.Api(env = XMTPEnvironment.DEV, isSecure = true),
"production" to ClientOptions.Api(env = XMTPEnvironment.PRODUCTION, isSecure = true)
)
private fun apiEnvironments(env: String, appVersion: String?): ClientOptions.Api {
return when (env) {
"local" -> ClientOptions.Api(
env = XMTPEnvironment.LOCAL,
isSecure = false,
appVersion = appVersion
)

"production" -> ClientOptions.Api(
env = XMTPEnvironment.PRODUCTION,
isSecure = true,
appVersion = appVersion
)

else -> ClientOptions.Api(
env = XMTPEnvironment.DEV,
isSecure = true,
appVersion = appVersion
)
}
}

private var clients: MutableMap<String, Client> = mutableMapOf()
private var xmtpPush: XMTPPush? = null
Expand All @@ -107,12 +123,11 @@ class XMTPModule : Module() {
//
// Auth functions
//
AsyncFunction("auth") { address: String, environment: String ->
AsyncFunction("auth") { address: String, environment: String, appVersion: String? ->
logV("auth")
val reactSigner = ReactNativeSigner(module = this@XMTPModule, address = address)
signer = reactSigner
val options =
ClientOptions(api = apiEnvironments[environment] ?: apiEnvironments["dev"]!!)
val options = ClientOptions(api = apiEnvironments(environment, appVersion))
clients[address] = Client().create(account = reactSigner, options = options)
signer = null
sendEvent("authed")
Expand All @@ -124,21 +139,19 @@ class XMTPModule : Module() {
}

// Generate a random wallet and set the client to that
AsyncFunction("createRandom") { environment: String ->
AsyncFunction("createRandom") { environment: String, appVersion: String? ->
logV("createRandom")
val privateKey = PrivateKeyBuilder()
val options =
ClientOptions(api = apiEnvironments[environment] ?: apiEnvironments["dev"]!!)
val options = ClientOptions(api = apiEnvironments(environment, appVersion))
val randomClient = Client().create(account = privateKey, options = options)
clients[randomClient.address] = randomClient
randomClient.address
}

AsyncFunction("createFromKeyBundle") { keyBundle: String, environment: String ->
AsyncFunction("createFromKeyBundle") { keyBundle: String, environment: String, appVersion: String? ->
try {
logV("createFromKeyBundle")
val options =
ClientOptions(api = apiEnvironments[environment] ?: apiEnvironments["dev"]!!)
val options = ClientOptions(api = apiEnvironments(environment, appVersion))
val bundle =
PrivateKeyOuterClass.PrivateKeyBundle.parseFrom(
Base64.decode(
Expand Down
14 changes: 7 additions & 7 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -409,16 +409,16 @@ PODS:
- GenericJSON (~> 2.0)
- Logging (~> 1.0.0)
- secp256k1.swift (~> 0.1)
- XMTP (0.4.0-alpha0):
- XMTP (0.4.1-alpha0):
- Connect-Swift
- GzipSwift
- web3.swift
- XMTPRust (= 0.3.0-beta0)
- XMTPRust (= 0.3.1-beta0)
- XMTPReactNative (0.1.0):
- ExpoModulesCore
- MessagePacker
- XMTP (= 0.4.0-alpha0)
- XMTPRust (0.3.0-beta0)
- XMTP (= 0.4.1-alpha0)
- XMTPRust (0.3.1-beta0)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -660,9 +660,9 @@ SPEC CHECKSUMS:
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
SwiftProtobuf: 40bd808372cb8706108f22d28f8ab4a6b9bc6989
web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959
XMTP: 8bb7ef01ba2ed0db8bbf699164d2da22dd989a25
XMTPReactNative: c8f5e3f2d2f1e9102b9fd888047b34919be057d4
XMTPRust: 233518ed46fbe3ea9e3bc3035de9a620dba09ce5
XMTP: a5be4f9bcae69a821b258c76b7aaed2701961cac
XMTPReactNative: 74eb825a1acf17580a9e6bacb0aadb15724a4dd4
XMTPRust: 78f65f77b1454392980da244961777aee955652f
Yoga: ba09b6b11e6139e3df8229238aa794205ca6a02a

PODFILE CHECKSUM: 522d88edc2d5fac4825e60a121c24abc18983367
Expand Down
10 changes: 8 additions & 2 deletions example/src/AuthView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,20 @@ function AuthView({
useEffect(() => {
(async () => {
if (signer) {
const client = await XMTP.Client.create(signer, "dev");
const client = await XMTP.Client.create(signer, {
env: "dev",
appVersion: "XMTP_RN_EX/0.0.1",
});
setClient(client);
}
})();
}, [signer]);

const generateWallet = async () => {
const client = await XMTP.Client.createRandom("dev");
const client = await XMTP.Client.createRandom({
env: "dev",
appVersion: "XMTP_RN_EX/0.0.1",
});
setClient(client);
};

Expand Down
35 changes: 18 additions & 17 deletions example/src/tests.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { content } from "@xmtp/proto";
import { randomBytes } from "crypto";

import { NumberCodec, TextCodec } from "./test_utils";
import * as XMTP from "../../src/index";
import { DecodedMessage, Query } from "../../src/index";
import { CodecError } from "../../src/lib/CodecError";
import { CodecRegistry } from "../../src/lib/CodecRegistry";
import { randomBytes } from "crypto";

function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

export type Test = {
name: string;
Expand All @@ -27,7 +23,10 @@ function test(name: string, perform: () => Promise<boolean>) {
// });

test("can make a client", async () => {
const client = await XMTP.Client.createRandom("local");
const client = await XMTP.Client.createRandom({
env: "local",
appVersion: "Testing/0.0.0",
});
return client.address.length > 0;
});

Expand All @@ -44,8 +43,8 @@ test("can send and receive a text codec", async () => {

const data = content.EncodedContent.encode(encodedContent).finish();

const bob = await XMTP.Client.createRandom("local");
const alice = await XMTP.Client.createRandom("local");
const bob = await XMTP.Client.createRandom({ env: "local" });
const alice = await XMTP.Client.createRandom({ env: "local" });

if (bob.address === alice.address) {
throw new Error("bob and alice should be different");
Expand Down Expand Up @@ -89,8 +88,8 @@ test("can pass a custom filter date and receive message objects with expected da

const data = content.EncodedContent.encode(encodedContent).finish();

const bob = await XMTP.Client.createRandom("local");
const alice = await XMTP.Client.createRandom("local");
const bob = await XMTP.Client.createRandom({ env: "local" });
const alice = await XMTP.Client.createRandom({ env: "local" });

if (bob.address === alice.address) {
throw new Error("bob and alice should be different");
Expand Down Expand Up @@ -129,8 +128,8 @@ test("can pass a custom filter date and receive message objects with expected da
});

test("canMessage", async () => {
const bob = await XMTP.Client.createRandom("local");
const alice = await XMTP.Client.createRandom("local");
const bob = await XMTP.Client.createRandom({ env: "local" });
const alice = await XMTP.Client.createRandom({ env: "local" });

const canMessage = await bob.canMessage(alice.address);
return canMessage;
Expand Down Expand Up @@ -201,8 +200,8 @@ test("can send and receive number codec", async () => {

const data = content.EncodedContent.encode(encodedContent).finish();

const bob = await XMTP.Client.createRandom("local");
const alice = await XMTP.Client.createRandom("local");
const bob = await XMTP.Client.createRandom({ env: "local" });
const alice = await XMTP.Client.createRandom({ env: "local" });

if (bob.address === alice.address) {
throw new Error("bob and alice should be different");
Expand Down Expand Up @@ -235,7 +234,9 @@ test("can send and receive number codec", async () => {
test("createFromKeyBundle throws error for non string value", async () => {
try {
const bytes = randomBytes(32);
await XMTP.Client.createFromKeyBundle(JSON.stringify(bytes), "local");
await XMTP.Client.createFromKeyBundle(JSON.stringify(bytes), {
env: "local",
});
} catch (e) {
return true;
}
Expand All @@ -255,8 +256,8 @@ test("can list batch messages", async () => {

const data = content.EncodedContent.encode(encodedContent).finish();

const bob = await XMTP.Client.createRandom("local");
const alice = await XMTP.Client.createRandom("local");
const bob = await XMTP.Client.createRandom({ env: "local" });
const alice = await XMTP.Client.createRandom({ env: "local" });

if (bob.address === alice.address) {
throw new Error("bob and alice should be different");
Expand Down
48 changes: 28 additions & 20 deletions ios/XMTPModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,28 @@ extension Conversation {
}

public class XMTPModule: Module {
var apiEnvironments = [
"local": XMTP.ClientOptions.Api(
env: XMTP.XMTPEnvironment.local,
isSecure: false
),
"dev": XMTP.ClientOptions.Api(
env: XMTP.XMTPEnvironment.dev,
isSecure: true
),
"production": XMTP.ClientOptions.Api(
env: XMTP.XMTPEnvironment.production,
isSecure: true
),
]
private func apiEnvironments(env: String, appVersion: String?) -> XMTP.ClientOptions.Api {
switch env {
case "local":
return XMTP.ClientOptions.Api(
env: XMTP.XMTPEnvironment.local,
isSecure: false,
appVersion: appVersion
)
case "production":
return XMTP.ClientOptions.Api(
env: XMTP.XMTPEnvironment.production,
isSecure: true,
appVersion: appVersion
)
default:
return XMTP.ClientOptions.Api(
env: XMTP.XMTPEnvironment.dev,
isSecure: true,
appVersion: appVersion
)
}
}

var clients: [String: XMTP.Client] = [:]
var signer: ReactNativeSigner?
Expand Down Expand Up @@ -111,10 +119,10 @@ public class XMTPModule: Module {
//
// Auth functions
//
AsyncFunction("auth") { (address: String, environment: String) in
AsyncFunction("auth") { (address: String, environment: String, appVersion: String?) in
let signer = ReactNativeSigner(module: self, address: address)
self.signer = signer
let options = XMTP.ClientOptions(api: apiEnvironments[environment] ?? apiEnvironments["local"]!)
let options = XMTP.ClientOptions(api: apiEnvironments(env: environment, appVersion: appVersion))
self.clients[address] = try await XMTP.Client.create(account: signer, options: options)
self.signer = nil
sendEvent("authed")
Expand All @@ -125,24 +133,24 @@ public class XMTPModule: Module {
}

// Generate a random wallet and set the client to that
AsyncFunction("createRandom") { (environment: String) -> String in
AsyncFunction("createRandom") { (environment: String, appVersion: String?) -> String in
let privateKey = try PrivateKey.generate()
let options = XMTP.ClientOptions(api: apiEnvironments[environment] ?? apiEnvironments["dev"]!)
let options = XMTP.ClientOptions(api: apiEnvironments(env: environment, appVersion: appVersion))
let client = try await Client.create(account: privateKey, options: options)

self.clients[client.address] = client
return client.address
}

// Create a client using its serialized key bundle.
AsyncFunction("createFromKeyBundle") { (keyBundle: String, environment: String) -> String in
AsyncFunction("createFromKeyBundle") { (keyBundle: String, environment: String, appVersion: String?) -> String in
do {
guard let keyBundleData = Data(base64Encoded: keyBundle),
let bundle = try? PrivateKeyBundle(serializedData: keyBundleData) else {
throw Error.invalidKeyBundle
}

let options = XMTP.ClientOptions(api: apiEnvironments[environment] ?? apiEnvironments["dev"]!)
let options = XMTP.ClientOptions(api: apiEnvironments(env: environment, appVersion: appVersion))
let client = try await Client.from(bundle: bundle, options: options)
self.clients[client.address] = client
return client.address
Expand Down
2 changes: 1 addition & 1 deletion ios/XMTPReactNative.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ Pod::Spec.new do |s|

s.source_files = "**/*.{h,m,swift}"
s.dependency "MessagePacker"
s.dependency "XMTP", "= 0.4.0-alpha0"
s.dependency "XMTP", "= 0.4.1-alpha0"
end
19 changes: 13 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,33 @@ export function address(): string {

export async function auth(
address: string,
environment: "local" | "dev" | "production"
environment: "local" | "dev" | "production",
appVersion?: string | undefined
) {
return await XMTPModule.auth(address, environment);
return await XMTPModule.auth(address, environment, appVersion);
}

export async function receiveSignature(requestID: string, signature: string) {
return await XMTPModule.receiveSignature(requestID, signature);
}

export async function createRandom(
environment: "local" | "dev" | "production"
environment: "local" | "dev" | "production",
appVersion?: string | undefined
): Promise<string> {
return await XMTPModule.createRandom(environment);
return await XMTPModule.createRandom(environment, appVersion);
}

export async function createFromKeyBundle(
keyBundle: string,
environment: "local" | "dev" | "production"
environment: "local" | "dev" | "production",
appVersion?: string | undefined
): Promise<string> {
return await XMTPModule.createFromKeyBundle(keyBundle, environment);
return await XMTPModule.createFromKeyBundle(
keyBundle,
environment,
appVersion
);
}

export async function exportKeyBundle(clientAddress: string): Promise<string> {
Expand Down
Loading

0 comments on commit 37f28f1

Please sign in to comment.