Skip to content

Commit

Permalink
Merge pull request #508 from xmtp/np/smart-contract-sdk-support
Browse files Browse the repository at this point in the history
Smart contract sdk support
  • Loading branch information
nplasterer authored Oct 24, 2024
2 parents 398b5c4 + 7d6d62d commit 8199e76
Show file tree
Hide file tree
Showing 14 changed files with 300 additions and 51 deletions.
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:0.15.12"
implementation "org.xmtp:android:0.16.0"
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 @@ -47,6 +47,7 @@ import org.xmtp.android.library.PreEventCallback
import org.xmtp.android.library.PreparedMessage
import org.xmtp.android.library.SendOptions
import org.xmtp.android.library.SigningKey
import org.xmtp.android.library.WalletType
import org.xmtp.android.library.XMTPEnvironment
import org.xmtp.android.library.XMTPException
import org.xmtp.android.library.codecs.Attachment
Expand All @@ -55,6 +56,7 @@ import org.xmtp.android.library.codecs.EncodedContent
import org.xmtp.android.library.codecs.EncryptedEncodedContent
import org.xmtp.android.library.codecs.RemoteAttachment
import org.xmtp.android.library.codecs.decoded
import org.xmtp.android.library.hexToByteArray
import org.xmtp.android.library.messages.EnvelopeBuilder
import org.xmtp.android.library.messages.InvitationV1ContextBuilder
import org.xmtp.android.library.messages.MessageDeliveryStatus
Expand All @@ -69,7 +71,6 @@ import org.xmtp.proto.message.api.v1.MessageApiOuterClass
import org.xmtp.proto.message.contents.Invitation.ConsentProofPayload
import org.xmtp.proto.message.contents.PrivateKeyOuterClass
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.GroupPermissionPreconfiguration
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.InboxState
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionOption
import java.io.BufferedReader
import java.io.File
Expand All @@ -80,8 +81,16 @@ import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

class ReactNativeSigner(var module: XMTPModule, override var address: String) : SigningKey {

class ReactNativeSigner(
var module: XMTPModule,
override var address: String,
override var type: WalletType = WalletType.EOA,
override var chainId: Long? = null,
override var blockNumber: Long? = null,
) : SigningKey {
private val continuations: MutableMap<String, Continuation<Signature>> = mutableMapOf()
private val scwContinuations: MutableMap<String, Continuation<ByteArray>> = mutableMapOf()

fun handle(id: String, signature: String) {
val continuation = continuations[id] ?: return
Expand All @@ -101,6 +110,20 @@ class ReactNativeSigner(var module: XMTPModule, override var address: String) :
continuations.remove(id)
}

fun handleSCW(id: String, signature: String) {
val continuation = scwContinuations[id] ?: return
continuation.resume(signature.hexToByteArray())
scwContinuations.remove(id)
}

override suspend fun signSCW(message: String): ByteArray {
val request = SignatureRequest(message = message)
module.sendEvent("sign", mapOf("id" to request.id, "message" to request.message))
return suspendCancellableCoroutine { continuation ->
scwContinuations[request.id] = continuation
}
}

override suspend fun sign(data: ByteArray): Signature {
val request = SignatureRequest(message = String(data, Charsets.UTF_8))
module.sendEvent("sign", mapOf("id" to request.id, "message" to request.message))
Expand Down Expand Up @@ -330,6 +353,11 @@ class XMTPModule : Module() {
signer?.handle(id = requestID, signature = signature)
}

Function("receiveSCWSignature") { requestID: String, signature: String ->
logV("receiveSCWSignature")
signer?.handleSCW(id = requestID, signature = signature)
}

// Generate a random wallet and set the client to that
AsyncFunction("createRandom") Coroutine { hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasPreAuthenticateToInboxCallback: Boolean?, dbEncryptionKey: List<Int>?, authParams: String ->
withContext(Dispatchers.IO) {
Expand Down Expand Up @@ -375,10 +403,17 @@ class XMTPModule : Module() {
}
}

AsyncFunction("createOrBuild") Coroutine { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasAuthInboxCallback: Boolean?, dbEncryptionKey: List<Int>?, authParams: String ->
AsyncFunction("createV3") Coroutine { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasAuthInboxCallback: Boolean?, dbEncryptionKey: List<Int>?, authParams: String ->
withContext(Dispatchers.IO) {
logV("createOrBuild")
val reactSigner = ReactNativeSigner(module = this@XMTPModule, address = address)
logV("createV3")
val authOptions = AuthParamsWrapper.authParamsFromJson(authParams)
val reactSigner = ReactNativeSigner(
module = this@XMTPModule,
address = address,
type = authOptions.walletType,
chainId = authOptions.chainId,
blockNumber = authOptions.blockNumber
)
signer = reactSigner
val options = clientOptions(
dbEncryptionKey,
Expand All @@ -387,14 +422,29 @@ class XMTPModule : Module() {
hasEnableIdentityCallback,
hasAuthInboxCallback,
)
val client = Client().createOrBuild(account = reactSigner, options = options)
val client = Client().createV3(account = reactSigner, options = options)
clients[client.inboxId] = client
ContentJson.Companion
signer = null
sendEvent("authedV3", ClientWrapper.encodeToObj(client))
}
}

AsyncFunction("buildV3") Coroutine { address: String, dbEncryptionKey: List<Int>?, authParams: String ->
withContext(Dispatchers.IO) {
logV("buildV3")
val authOptions = AuthParamsWrapper.authParamsFromJson(authParams)
val options = clientOptions(
dbEncryptionKey,
authParams,
)
val client = Client().buildV3(address = address, options = options)
ContentJson.Companion
clients[client.inboxId] = client
ClientWrapper.encodeToObj(client)
}
}

AsyncFunction("createRandomV3") Coroutine { hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasPreAuthenticateToInboxCallback: Boolean?, dbEncryptionKey: List<Int>?, authParams: String ->
withContext(Dispatchers.IO) {
logV("createRandomV3")
Expand All @@ -406,7 +456,7 @@ class XMTPModule : Module() {
hasEnableIdentityCallback,
hasPreAuthenticateToInboxCallback,
)
val randomClient = Client().createOrBuild(account = privateKey, options = options)
val randomClient = Client().createV3(account = privateKey, options = options)

ContentJson.Companion
clients[randomClient.inboxId] = randomClient
Expand Down Expand Up @@ -635,7 +685,7 @@ class XMTPModule : Module() {
val sortedGroupList = if (order == ConversationOrder.LAST_MESSAGE) {
client.conversations.listGroups()
.sortedByDescending { group ->
group.decryptedMessages().firstOrNull()?.sentAt
group.decryptedMessages(limit = 1).firstOrNull()?.sentAt
}
.let { groups ->
if (limit != null && limit > 0) groups.take(limit) else groups
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package expo.modules.xmtpreactnativesdk.wrappers

import com.google.gson.JsonParser
import org.xmtp.android.library.WalletType

class AuthParamsWrapper(
val environment: String,
val appVersion: String?,
val enableV3: Boolean = false,
val dbDirectory: String?,
val historySyncUrl: String?,
val walletType: WalletType = WalletType.EOA,
val chainId: Long?,
val blockNumber: Long?,
) {
companion object {
fun authParamsFromJson(authParams: String): AuthParamsWrapper {
Expand All @@ -17,8 +21,18 @@ class AuthParamsWrapper(
if (jsonOptions.has("appVersion")) jsonOptions.get("appVersion").asString else null,
if (jsonOptions.has("enableV3")) jsonOptions.get("enableV3").asBoolean else false,
if (jsonOptions.has("dbDirectory")) jsonOptions.get("dbDirectory").asString else null,
if (jsonOptions.has("historySyncUrl")) jsonOptions.get("historySyncUrl").asString else null
)
if (jsonOptions.has("historySyncUrl")) jsonOptions.get("historySyncUrl").asString else null,
if (jsonOptions.has("walletType")) {
when (jsonOptions.get("walletType").asString) {
"SCW" -> WalletType.SCW
else -> WalletType.EOA
}
} else {
WalletType.EOA
},
if (jsonOptions.has("chainId")) jsonOptions.get("chainId").asLong else null,
if (jsonOptions.has("blockNumber")) jsonOptions.get("blockNumber").asLong else null,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class GroupWrapper {
put("consentState", consentStateToString(group.consentState()))
}
if (groupParams.lastMessage) {
val lastMessage = group.decryptedMessages().firstOrNull()
val lastMessage = group.decryptedMessages(limit = 1).firstOrNull()
if (lastMessage != null) {
put("lastMessage", DecodedMessageWrapper.encode(lastMessage))
}
Expand Down
14 changes: 7 additions & 7 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ PODS:
- hermes-engine/Pre-built (= 0.71.14)
- hermes-engine/Pre-built (0.71.14)
- libevent (2.1.12)
- LibXMTP (0.5.9-beta0)
- LibXMTP (0.5.10)
- Logging (1.0.0)
- MessagePacker (0.4.7)
- MMKV (2.0.0):
Expand Down Expand Up @@ -449,16 +449,16 @@ PODS:
- GenericJSON (~> 2.0)
- Logging (~> 1.0.0)
- secp256k1.swift (~> 0.1)
- XMTP (0.15.0):
- XMTP (0.15.2):
- Connect-Swift (= 0.12.0)
- GzipSwift
- LibXMTP (= 0.5.9-beta0)
- LibXMTP (= 0.5.10)
- web3.swift
- XMTPReactNative (0.1.0):
- ExpoModulesCore
- MessagePacker
- secp256k1.swift
- XMTP (= 0.15.0)
- XMTP (= 0.15.2)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -711,7 +711,7 @@ SPEC CHECKSUMS:
GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa
hermes-engine: d7cc127932c89c53374452d6f93473f1970d8e88
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
LibXMTP: 5a38722a68a9469be2e711857a5e7d9dd3aa8a61
LibXMTP: 3b64b0b1e4157ff73c37cde60fe943f89e6f8693
Logging: 9ef4ecb546ad3169398d5a723bc9bea1c46bef26
MessagePacker: ab2fe250e86ea7aedd1a9ee47a37083edd41fd02
MMKV: f7d1d5945c8765f97f39c3d121f353d46735d801
Expand Down Expand Up @@ -763,8 +763,8 @@ SPEC CHECKSUMS:
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1
web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959
XMTP: 09faa347569b092005997364f7fe787ccc33f3d5
XMTPReactNative: 6404c11e6dd11820742d4af899daeea389fc442f
XMTP: 7d47e6bc507db66dd01116ce2b4ed04dd3560a4f
XMTPReactNative: 1a946cd697598fb4bc560a637094e63c4d553df3
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9

PODFILE CHECKSUM: 0e6fe50018f34e575d38dc6a1fdf1f99c9596cdd
Expand Down
15 changes: 13 additions & 2 deletions example/src/tests/v3OnlyTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,20 @@ test('can make a V3 only client', async () => {
client.inboxId === inboxId,
`inboxIds should match but were ${client.inboxId} and ${inboxId}`
)
const canMessageV3 = await client.canGroupMessage([client.address])

const client2 = await Client.buildV3(client.address, {
env: 'local',
appVersion: 'Testing/0.0.0',
enableV3: true,
dbEncryptionKey: keyBytes,
})

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

const canMessageV3 = await client.canGroupMessage([client.address])
assert(
canMessageV3[client.address.toLowerCase()] === true,
`canMessageV3 should be true`
Expand All @@ -51,7 +63,6 @@ test('can make a V3 only client', async () => {
} catch (error) {
return true
}

throw new Error('should throw error when hitting V2 api')
})

Expand Down
31 changes: 30 additions & 1 deletion ios/ReactNativeSigner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ class ReactNativeSigner: NSObject, XMTP.SigningKey {

var module: XMTPModule
var address: String
var type: WalletType
var chainId: Int64?
var blockNumber: Int64?
var continuations: [String: CheckedContinuation<XMTP.Signature, Swift.Error>] = [:]
var scwContinuations: [String: CheckedContinuation<Data, Swift.Error>] = [:]

init(module: XMTPModule, address: String) {
init(module: XMTPModule, address: String, walletType: WalletType = WalletType.EOA, chainId: Int64? = nil, blockNumber: Int64? = nil) {
self.module = module
self.address = address
self.type = walletType
self.chainId = chainId
self.blockNumber = blockNumber
}

func handle(id: String, signature: String) throws {
Expand All @@ -40,6 +47,28 @@ class ReactNativeSigner: NSObject, XMTP.SigningKey {
continuation.resume(returning: signature)
continuations.removeValue(forKey: id)
}

func handleSCW(id: String, signature: String) throws {
guard let continuation = scwContinuations[id] else {
return
}

continuation.resume(returning: signature.hexToData)
scwContinuations.removeValue(forKey: id)
}

func signSCW(message: String) async throws -> Data {
let request = SignatureRequest(message: message)

module.sendEvent("sign", [
"id": request.id,
"message": request.message,
])

return try await withCheckedThrowingContinuation { continuation in
scwContinuations[request.id] = continuation
}
}

func sign(_ data: Data) async throws -> XMTP.Signature {
let request = SignatureRequest(message: String(data: data, encoding: .utf8)!)
Expand Down
29 changes: 25 additions & 4 deletions ios/Wrappers/AuthParamsWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,54 @@ struct AuthParamsWrapper {
let enableV3: Bool
let dbDirectory: String?
let historySyncUrl: String?

init(environment: String, appVersion: String?, enableV3: Bool, dbDirectory: String?, historySyncUrl: String?) {
let walletType: WalletType
let chainId: Int64?
let blockNumber: Int64?

init(environment: String, appVersion: String?, enableV3: Bool, dbDirectory: String?, historySyncUrl: String?, walletType: WalletType, chainId: Int64?, blockNumber: Int64?) {
self.environment = environment
self.appVersion = appVersion
self.enableV3 = enableV3
self.dbDirectory = dbDirectory
self.historySyncUrl = historySyncUrl
self.walletType = walletType
self.chainId = chainId
self.blockNumber = blockNumber
}

static func authParamsFromJson(_ authParams: String) -> AuthParamsWrapper {
guard let data = authParams.data(using: .utf8),
let jsonOptions = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return AuthParamsWrapper(environment: "dev", appVersion: nil, enableV3: false, dbDirectory: nil, historySyncUrl: nil)
return AuthParamsWrapper(environment: "dev", appVersion: nil, enableV3: false, dbDirectory: nil, historySyncUrl: nil, walletType: WalletType.EOA, chainId: nil, blockNumber: nil)
}

let environment = jsonOptions["environment"] as? String ?? "dev"
let appVersion = jsonOptions["appVersion"] as? String
let enableV3 = jsonOptions["enableV3"] as? Bool ?? false
let dbDirectory = jsonOptions["dbDirectory"] as? String
let historySyncUrl = jsonOptions["historySyncUrl"] as? String
let walletTypeString = jsonOptions["walletType"] as? String ?? "EOA"
let chainId = jsonOptions["chainId"] as? Int64
let blockNumber = jsonOptions["blockNumber"] as? Int64

let walletType = { switch walletTypeString {
case "SCW":
return WalletType.SCW
default:
return WalletType.EOA
}
}()


return AuthParamsWrapper(
environment: environment,
appVersion: appVersion,
enableV3: enableV3,
dbDirectory: dbDirectory,
historySyncUrl: historySyncUrl
historySyncUrl: historySyncUrl,
walletType: walletType,
chainId: chainId,
blockNumber: blockNumber
)
}
}
2 changes: 1 addition & 1 deletion ios/Wrappers/GroupWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct GroupWrapper {
result["consentState"] = ConsentWrapper.consentStateToString(state: try group.consentState())
}
if groupParams.lastMessage {
if let lastMessage = try await group.decryptedMessages().first {
if let lastMessage = try await group.decryptedMessages(limit: 1).first {
result["lastMessage"] = try DecodedMessageWrapper.encode(lastMessage, client: client)
}
}
Expand Down
Loading

0 comments on commit 8199e76

Please sign in to comment.