Skip to content

Commit

Permalink
add ability to create from key bundle with signer
Browse files Browse the repository at this point in the history
  • Loading branch information
nplasterer committed Nov 1, 2024
1 parent c08ebf8 commit bbb4e60
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,34 @@ class XMTPModule : Module() {
}
}

AsyncFunction("createFromKeyBundleWithSigner") Coroutine { address: String, keyBundle: String, dbEncryptionKey: List<Int>?, authParams: String ->
withContext(Dispatchers.IO) {
logV("createFromKeyBundleWithSigner")
try {
val options = clientOptions(
dbEncryptionKey,
authParams
)
val bundle =
PrivateKeyOuterClass.PrivateKeyBundle.parseFrom(
Base64.decode(
keyBundle,
NO_WRAP
)
)
val reactSigner = ReactNativeSigner(module = this@XMTPModule, address = address)
signer = reactSigner
val client = Client().buildFromBundle(bundle = bundle, options = options, account = reactSigner)
clients[client.inboxId] = client
ContentJson.Companion
signer = null
sendEvent("authed", ClientWrapper.encodeToObj(client))
} catch (e: Exception) {
throw XMTPException("Failed to create client: $e")
}
}
}

AsyncFunction("createV3") Coroutine { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasAuthInboxCallback: Boolean?, dbEncryptionKey: List<Int>?, authParams: String ->
withContext(Dispatchers.IO) {
logV("createV3")
Expand Down
29 changes: 29 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,35 @@ export async function createFromKeyBundle(
)
}

export async function createFromKeyBundleWithSigner(
address: string,
keyBundle: string,
environment: 'local' | 'dev' | 'production',
appVersion?: string | undefined,
enableV3?: boolean | undefined,
dbEncryptionKey?: Uint8Array | undefined,
dbDirectory?: string | undefined,
historySyncUrl?: string | undefined
): Promise<string> {
const encryptionKey = dbEncryptionKey
? Array.from(dbEncryptionKey)
: undefined

const authParams: AuthParams = {
environment,
appVersion,
enableV3,
dbDirectory,
historySyncUrl,
}
return await XMTPModule.createFromKeyBundleWithSigner(
address,
keyBundle,
encryptionKey,
JSON.stringify(authParams)
)
}

export async function createRandomV3(
environment: 'local' | 'dev' | 'production',
appVersion?: string | undefined,
Expand Down
107 changes: 89 additions & 18 deletions src/lib/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ export class Client<
ContentCodecs extends DefaultContentTypes = [],
>(
keyBundle: string,
options: ClientOptions & { codecs?: ContentCodecs }
options: ClientOptions & { codecs?: ContentCodecs },
wallet?: Signer | WalletClient | undefined
): Promise<Client<ContentCodecs>> {
if (
options.enableV3 === true &&
Expand All @@ -234,23 +235,93 @@ export class Client<
) {
throw new Error('Must pass an encryption key that is exactly 32 bytes.')
}
const client = await XMTPModule.createFromKeyBundle(
keyBundle,
options.env,
options.appVersion,
Boolean(options.enableV3),
options.dbEncryptionKey,
options.dbDirectory,
options.historySyncUrl
)

return new Client(
client['address'],
client['inboxId'],
client['installationId'],
client['dbPath'],
options.codecs || []
)
if (!wallet) {
const client = await XMTPModule.createFromKeyBundle(
keyBundle,
options.env,
options.appVersion,
Boolean(options.enableV3),
options.dbEncryptionKey,
options.dbDirectory,
options.historySyncUrl
)

return new Client(
client['address'],
client['inboxId'],
client['installationId'],
client['dbPath'],
options.codecs || []
)
} else {
const signer = getSigner(wallet)
if (!signer) {
throw new Error('Signer is not configured')
}
return new Promise<Client<ContentCodecs>>((resolve, reject) => {
;(async () => {
this.signSubscription = XMTPModule.emitter.addListener(
'sign',
async (message: { id: string; message: string }) => {
const request: { id: string; message: string } = message
try {
const signatureString = await signer.signMessage(
request.message
)
const eSig = splitSignature(signatureString)
const r = hexToBytes(eSig.r)
const s = hexToBytes(eSig.s)
const sigBytes = new Uint8Array(65)
sigBytes.set(r)
sigBytes.set(s, r.length)
sigBytes[64] = eSig.recoveryParam

const signature = Buffer.from(sigBytes).toString('base64')

await XMTPModule.receiveSignature(request.id, signature)
} catch (e) {
const errorMessage = 'ERROR in create. User rejected signature'
console.info(errorMessage, e)
reject(errorMessage)
}
}
)

this.authSubscription = XMTPModule.emitter.addListener(
'authed',
async (message: {
inboxId: string
address: string
installationId: string
dbPath: string
}) => {
resolve(
new Client(
message.address,
message.inboxId as InboxId,
message.installationId,
message.dbPath,
options.codecs || []
)
)
}
)
await XMTPModule.createFromKeyBundleWithSigner(
await signer.getAddress(),
keyBundle,
options.env,
options.appVersion,
Boolean(options.enableV3),
options.dbEncryptionKey,
options.dbDirectory,
options.historySyncUrl
)
})().catch((error) => {
console.error('ERROR in create: ', error)
})
})
}
}

/**
Expand Down Expand Up @@ -412,7 +483,7 @@ export class Client<
})
}

/**
/**
* Builds a V3 ONLY instance of the Client class using the provided address and chainId if SCW.
*
* @param {string} address - The address of the account to build
Expand Down

0 comments on commit bbb4e60

Please sign in to comment.