From e88cf517b8a585530c7032edf85a26e8a7dd89a9 Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Wed, 5 Mar 2025 18:25:39 -0800
Subject: [PATCH 01/38] first pass
---
docs/pages/inboxes/build-inbox.mdx | 361 +++++++++---------
.../inboxes/content-types/attachments.mdx | 4 +-
docs/pages/inboxes/content-types/custom.md | 4 +
docs/pages/inboxes/disappearing-messages.md | 4 +
docs/pages/inboxes/group-metadata.md | 26 +-
docs/pages/inboxes/group-permissions.mdx | 86 +----
docs/pages/inboxes/manage-inboxes.mdx | 109 +++---
.../push-notifs/understand-push-notifs.md | 6 +-
docs/pages/inboxes/use-signatures.md | 4 +
.../user-consent/support-user-consent.md | 12 +
.../inboxes/user-consent/user-consent.mdx | 4 +-
docs/pages/intro/faq.mdx | 4 +
docs/pages/upgrade-to-v3.md | 101 ++++-
13 files changed, 417 insertions(+), 308 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 626bb9c..eeef60c 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -30,17 +30,22 @@ Learn to build a chat inbox with the help of [XMTP.chat](https://xmtp.chat/), an
This code defines two functions that convert different types of Ethereum accounts—Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs)—into a unified `Signer` interface. This ensures that both account types conform to a common interface for message signing and deriving shared secrets as per MLS (Message Layer Security) requirements.
-- For an EOA, the `convertEOAToSigner` function creates a signer that can get the account address and sign messages and has placeholder methods for wallet type, chain ID, and block number.
+jhaaaa these EOA and SCW code samples include "address" in the comments to callout when the kind is Ethereum and the identifier is an Ethereum address. Is this OK or have I misunderstood?
+
+- For an EOA, the `convertEOAToSigner` function creates a signer that can get the account identity and sign messages and has placeholder methods for chain ID and block number.
:::code-group
```tsx [Browser]
import type { Signer } from "@xmtp/browser-sdk";
- const accountAddress = "0x...";
+ const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Ethereum address as the identifier
+ };
const signer: Signer = {
- getAddress: () => accountAddress,
+ getIdentity: () => accountIdentity,
signMessage: async (message) => {
// return value from a signing method here
},
@@ -50,10 +55,13 @@ This code defines two functions that convert different types of Ethereum account
```tsx [Node]
import type { Signer } from "@xmtp/node-sdk";
- const accountAddress = "0x...";
+ const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Ethereum address as the identifier
+ };
const signer: Signer = {
- getAddress: () => accountAddress,
+ getIdentity: () => accountIdentity,
signMessage: async (message) => {
// return value from a signing method here
},
@@ -61,15 +69,17 @@ This code defines two functions that convert different types of Ethereum account
```
```tsx [React Native]
- // Example EOA
+ // Example EOA Signer
export function convertEOAToSigner(eoaAccount: EOAAccount): Signer {
return {
- getAddress: async () => eoaAccount.address,
+ getIdentity: async () => ({
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: eoaAccount.address, // Ethereum address as the identifier
+ }),
signMessage: async (message: string | Uint8Array) =>
eoaAccount.signMessage({
message: typeof message === "string" ? message : { raw: message },
}),
- walletType: () => undefined, // Default: 'EOA'
getChainId: () => undefined,
getBlockNumber: () => undefined,
};
@@ -77,150 +87,144 @@ This code defines two functions that convert different types of Ethereum account
```
```kotlin [Kotlin]
- class EOAWallet : SigningKey {
- override val address: String
- get() = walletAddress
-
- override suspend fun sign(message: String): Signature {
- val signature = key.sign(data)
- return signature
- }
-
- override suspend fun sign(data: ByteArray): Signature {
- val signature = key.sign(message: message)
- return signature
- }
+ class EOAWallet(override val identity: Identity) : SigningKey {
+ override val type: SignerType
+ get() = SignerType.EOA
+
+ override suspend fun sign(message: String): SignatureOuterClass.Signature {
+ return key.sign(message)
+ }
+
+ override suspend fun sign(data: ByteArray): SignatureOuterClass.Signature {
+ return key.sign(data)
+ }
}
```
```swift [Swift]
public struct EOAWallet: SigningKey {
- public var address: String {
- walletAddress
- }
-
- public func sign(_ data: Data) async throws -> XMTPiOS.Signature {
- let signature = try await key.sign(data)
- return signature
- }
-
- public func sign(message: String) async throws -> XMTPiOS.Signature {
- let signature = try await key.sign(message: message)
- return signature
- }
+ public var identity: Identity
+
+ public func sign(_ data: Data) async throws -> XMTPiOS.Signature {
+ let signature = try await key.sign(data)
+ return signature
+ }
+
+ public func sign(message: String) async throws -> XMTPiOS.Signature {
+ let signature = try await key.sign(message: message)
+ return signature
+ }
}
```
:::
-- For an SCW, the `convertSCWToSigner` function similarly creates a signer but includes specific implementations for wallet type and chain ID, and an optional block number computation.
+- For an SCW, the `convertSCWToSigner` function similarly creates a signer but includes a specific implementation for chain ID and an optional block number computation.
:::code-group
```tsx [Browser]
import type { Signer } from "@xmtp/browser-sdk";
- const accountAddress = "0x...";
+ const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Smart Contract Wallet address
+ };
const signer: Signer = {
- getAddress: () => accountAddress,
+ getIdentity: () => accountIdentity,
signMessage: async (message) => {
// return value from a signing method here
},
- // these methods are required for smart contract wallets
- // block number is optional
- getBlockNumber: () => undefined,
- // this example uses the Base chain
- getChainId: () => BigInt(8453),
+ // These methods are required for smart contract wallets
+ getBlockNumber: () => undefined, // Optional block number
+ getChainId: () => BigInt(8453), // Example: Base chain ID
};
```
```tsx [Node]
import type { Signer } from "@xmtp/node-sdk";
- const accountAddress = "0x...";
+ const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Smart Contract Wallet address
+ };
const signer: Signer = {
- getAddress: () => accountAddress,
+ getIdentity: () => accountIdentity,
signMessage: async (message) => {
// return value from a signing method here
},
- // these methods are required for smart contract wallets
- // block number is optional
- getBlockNumber: () => undefined,
- // this example uses the Base chain
- getChainId: () => BigInt(8453),
+ // These methods are required for smart contract wallets
+ getBlockNumber: () => undefined, // Optional block number
+ getChainId: () => BigInt(8453), // Example: Base chain ID
};
```
```tsx [React Native]
- // Example SCW
+ // Example SCW Signer
export function convertSCWToSigner(scwAccount: SCWAccount): Signer {
return {
- getAddress: async () => scwAccount.address,
+ getIdentity: async () => ({
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: scwAccount.address, // Smart Contract Wallet address
+ }),
signMessage: async (message: string) => {
const byteArray = await scwAccount.signMessage(message);
return ethers.utils.hexlify(byteArray); // Convert to hex string
},
- walletType: () => "SCW",
getChainId: async () => 8453, // https://chainlist.org/
- getBlockNumber: async () => undefined, // Optional: will be computed at run
+ getBlockNumber: async () => undefined, // Optional: will be computed at runtime
};
}
```
```kotlin [Kotlin]
- class SCWallet : SigningKey {
- override val address: String
- get() = walletAddress
-
- override val type: WalletType
- get() = WalletType.SCW
-
- override var chainId: Long? = 8453, // https://chainlist.org/
-
- override var blockNumber: Long? = null, // Optional: will be computed at run
-
- override suspend fun signSCW(message: String): ByteArray {
- val digest = Signature.newBuilder().build().ethHash(message)
- val replaySafeHash = smartWallet.replaySafeHash(digest).send()
- val signature =
- Sign.signMessage(replaySafeHash, contractDeployerCredentials.ecKeyPair, false)
- val signatureBytes = signature.r + signature.s + signature.v
- val tokens = listOf(
- Uint(BigInteger.ZERO),
- DynamicBytes(signatureBytes)
- )
- val encoded = FunctionEncoder.encodeConstructor(tokens)
- val encodedBytes = Numeric.hexStringToByteArray(encoded)
-
- return encodedBytes
- }
+ class SCWallet(override val identity: Identity) : SigningKey {
+ override val type: SignerType
+ get() = SignerType.SCW
+
+ override var chainId: Long? = 8453 // https://chainlist.org/
+ override var blockNumber: Long? = null // Optional: will be computed at runtime
+
+ override suspend fun signSCW(message: String): ByteArray {
+ val digest = Signature.newBuilder().build().ethHash(message)
+ val replaySafeHash = smartWallet.replaySafeHash(digest).send()
+ val signature =
+ Sign.signMessage(replaySafeHash, contractDeployerCredentials.ecKeyPair, false)
+ val signatureBytes = signature.r + signature.s + signature.v
+ val tokens = listOf(
+ Uint(BigInteger.ZERO),
+ DynamicBytes(signatureBytes)
+ )
+ val encoded = FunctionEncoder.encodeConstructor(tokens)
+ val encodedBytes = Numeric.hexStringToByteArray(encoded)
+
+ return encodedBytes
+ }
}
```
```swift [Swift]
public struct SCWallet: SigningKey {
- public var address: String {
- walletAddress
- }
-
- public var chainId: Int64? {
- 8453
- }
-
- public var blockNumber: Int64? {
- nil
- }
-
- public var type: WalletType {
- .SCW
- }
-
- public func signSCW(message: String) async throws -> Data {
- let signature = try await key.sign(message: message)
- return signature.hexStringToByteArray
- }
+ public var identity: Identity
+
+ public var chainId: Int64? {
+ 8453
+ }
+
+ public var blockNumber: Int64? {
+ nil
+ }
+
+ public var type: SignerType {
+ .SCW
+ }
+
+ public func signSCW(message: String) async throws -> Data {
+ let signature = try await key.sign(message: message)
+ return signature.hexStringToByteArray
+ }
}
```
@@ -243,15 +247,19 @@ This video provides a walkthrough of creating a client, covering the key ideas d
```tsx [Browser]
import { Client, type Signer } from "@xmtp/browser-sdk";
-const accountAddress = "0x...";
+const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Ethereum address as the identifier
+};
+
const signer: Signer = {
- getAddress: () => accountAddress,
+ getIdentity: () => accountIdentity,
signMessage: async (message) => {
// return value from a signing method here
},
};
-// this value should be generated once per installation and stored securely
+// This value should be generated once per installation and stored securely
const encryptionKey = window.crypto.getRandomValues(new Uint8Array(32));
const client = await Client.create(
@@ -265,15 +273,19 @@ const client = await Client.create(
import { Client, type Signer } from "@xmtp/node-sdk";
import { getRandomValues } from "node:crypto";
-const accountAddress = "0x...";
+const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Ethereum address as the identifier
+};
+
const signer: Signer = {
- getAddress: () => accountAddress,
+ getIdentity: () => accountIdentity,
signMessage: async (message) => {
// return value from a signing method here
},
};
-// this value should be generated once per installation and stored securely
+// This value should be generated once per installation and stored securely
const encryptionKey = getRandomValues(new Uint8Array(32));
const client = await Client.create(
@@ -323,13 +335,16 @@ To learn more about database operations, see the [XMTP MLS protocol spec](https
You can configure an XMTP client with these parameters of `Client.create`:
+jhaaaa how about keystoreProviders, persistConversations, skipContactPublishing, codecs, maxContentSize, preCreateIdentityCallback, preEnableIdentityCallback, basePersistence, apiClientFactory
+
| Parameter | Default | Description |
| --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| env | `DEV` | Connect to the specified XMTP network environment. Valid values include `DEV`, `PRODUCTION`, or `LOCAL`. For important details about working with these environments, see [XMTP DEV, PRODUCTION, and LOCAL network environments](#xmtp-dev-production-and-local-network-environments). |
-| appContext | `REQUIRED` | The app context used to create and access the local database. |
-| dbEncryptionKey | `REQUIRED` | A 32-byte `ByteArray` used to encrypt the local database. |
-| historySyncUrl | `https://message-history.dev.ephemera.network/` | The history sync URL used to specify where history can be synced from other devices on the network. For production apps, use `https://message-history.production.ephemera.network` |
-| appVersion | `undefined` | Add a client app version identifier that's included with API requests. For example, you can use the following format: `appVersion: APP_NAME + '/' + APP_VERSION`. Setting this value provides telemetry that shows which apps are using the XMTP client SDK. This information can help XMTP core developers provide support to app developers, especially around communicating important SDK updates, including deprecations and required updates. |
+| env | `DEV` | Connect to the specified XMTP network environment. Valid values include `DEV`, `PRODUCTION`, or `LOCAL`. For important details about working with these environments, see [XMTP DEV, PRODUCTION, and LOCAL network environments](#xmtp-dev-production-and-local-network-environments). |
+| apiURL | undefined | Manually specify an API URL to use. If specified, value of `env` will be ignored. |
+| appContext (Android-only) | null | Required. The app context used to create and access the local database. |
+| dbEncryptionKey | null | Required. A 32-byte `ByteArray` used to encrypt the local database. |
+| dbDirectory | xmtp_db | Optional. Specify a database directory. If no directory is specified, the value is set to `xmtp_db` by default. |
+| historySyncUrl | `https://message-history.dev.ephemera.network/` | The history sync URL used to specify where history can be synced from other devices on the network. For production apps, use `https://message-history.production.ephemera.network`. |
#### XMTP DEV, PRODUCTION, and LOCAL network environments
@@ -360,7 +375,7 @@ Build, or resume, an existing client that's logged in and has an existing local
:::code-group
```tsx [React Native]
-Client.build(address, {
+Client.build(identity, {
env: "production", // 'local' | 'dev' | 'production'
dbEncryptionKey: keyBytes, // 32 bytes
});
@@ -373,7 +388,7 @@ val options = ClientOptions(
dbEncryptionKey = keyBytes
)
val client = Client().build(
- address = address,
+ identity = identity,
options = options
)
```
@@ -384,7 +399,7 @@ let options = ClientOptions.init(
dbEncryptionKey: keyBytes // 32 bytes
)
let client = try await Client.build(
- address: address,
+ identity: identity,
options: options
)
```
@@ -416,104 +431,104 @@ If the user chooses to delete their local database, they will have to create a n
:::
-## Check if an address is reachable
+## Check if an identity is reachable
-The first step to creating a conversation is to verify that participants’ addresses are reachable on XMTP. The `canMessage` method checks each address’ compatibility, returning a response indicating whether each address can receive messages.
+The first step to creating a conversation is to verify that participants’ identities are reachable on XMTP. The `canMessage` method checks each identity's compatibility, returning a response indicating whether each identity can receive messages.
-Once you have the verified addresses, you can create a new conversation, whether it's a group chat or direct message (DM).
+Once you have the verified identities, you can create a new conversation, whether it's a group chat or direct message (DM).
:::code-group
```js [Browser]
import { Client } from "@xmtp/browser-sdk";
-// response is a Map of string (address) => boolean (is reachable)
-const response = await Client.canMessage([bo.address, caro.address]);
+// response is a Map of string (identity) => boolean (is reachable)
+const response = await Client.canMessage([bo.identity, caro.identity]);
```
```js [Node]
import { Client } from "@xmtp/node-sdk";
-// response is a Map of string (address) => boolean (is reachable)
-const response = await Client.canMessage([bo.address, caro.address]);
+// response is a Map of string (identity) => boolean (is reachable)
+const response = await Client.canMessage([bo.identity, caro.identity]);
```
```tsx [React Native]
// Request
const canMessage = await client.canMessage([
- '0xboAddress',
- '0xV2OnlyAddress',
- '0xBadAddress',
+ '0xboIdentity',
+ '0xV2OnlyIdentity',
+ '0xBadIdentity',
])
// Response
{
- "0xboAddress": true,
- "0xV2OnlyAddress": false,
- "0xBadAddress": false,
+ "0xboIdentity": true,
+ "0xV2OnlyIdentity": false,
+ "0xBadIdentity": false,
}
```
```kotlin [Kotlin]
// Request
-val canMessage = client.canMessage(listOf('0xboAddress', '0xV2OnlyAddress','0xBadAddress'))
+val canMessage = client.canMessage(listOf('0xboIdentity', '0xV2OnlyIdentity','0xBadIdentity'))
// Response
[
- "0xboAddress": true,
- "0xV2OnlyAddress": false,
- "0xBadAddress": false,
+ "0xboIdentity": true,
+ "0xV2OnlyIdentity": false,
+ "0xBadIdentity": false,
]
```
```swift [Swift]
// Request
-let canMessage = try await client.canMessage(["0xboAddress", "0xV2OnlyAddress","0xBadAddress"])
+let canMessage = try await client.canMessage(["0xboIdentity", "0xV2OnlyIdentity","0xBadIdentity"])
// Response
[
- "0xboAddress": true,
- "0xV2OnlyAddress": false,
- "0xBadAddress": false,
+ "0xboIdentity": true,
+ "0xV2OnlyIdentity": false,
+ "0xBadIdentity": false,
]
```
:::
:::tip
-Regarding how to handle addresses that aren’t reachable, the XMTP V3.0.0 release notes will outline the next steps to ensure smooth onboarding for all participants.
+Regarding how to handle identities that aren’t reachable, the XMTP V3.0.0 release notes will outline the next steps to ensure smooth onboarding for all participants.
:::
## Create a conversation
### Create a new group chat
-Once you have the verified addresses, create a new group chat:
+Once you have the verified identities, create a new group chat:
:::code-group
```js [Browser]
const group = await client.conversations.newGroup(
- [bo.address, caro.address],
+ [bo.identity, caro.identity],
createGroupOptions /* optional */
);
```
```js [Node]
const group = await client.conversations.newGroup(
- [bo.address, caro.address],
+ [bo.identity, caro.identity],
createGroupOptions /* optional */
);
```
```tsx [React Native]
// New Group
-const group = await alix.conversations.newGroup([bo.address, caro.address]);
+const group = await alix.conversations.newGroup([bo.identity, caro.identity]);
// New Group with Metadata
-const group = await alix.conversations.newGroup([bo.address, caro.address], {
+const group = await alix.conversations.newGroup([bo.identity, caro.identity], {
name: "The Group Name",
- imageUrlSquare: "www.groupImage.com",
+ imageUrl: "www.groupImage.com",
description: "The description of the group",
permissionLevel: "admin_only", // 'all_members' | 'admin_only'
});
@@ -521,27 +536,27 @@ const group = await alix.conversations.newGroup([bo.address, caro.address], {
```kotlin [Kotlin]
// New Group
-val group = alix.conversations.newGroup(listOf(bo.address, caro.address))
+val group = alix.conversations.newGroup(listOf(bo.identity, caro.identity))
// New Group with Metadata
-val group = alix.conversations.newGroup(listOf(bo.address, caro.address),
+val group = alix.conversations.newGroup(listOf(bo.identity, caro.identity),
permissionLevel = GroupPermissionPreconfiguration.ALL_MEMBERS, // ALL_MEMBERS | ADMIN_ONLY
- groupName = "The Group Name",
- groupImageUrlSquare = "www.groupImage.com",
- groupDescription = "The description of the group",
+ Name = "The Group Name",
+ ImageUrl = "www.groupImage.com",
+ Description = "The description of the group",
)
```
```swift [Swift]
// New Group
-let group = try await alix.conversations.newGroup([bo.address, caro.address])
+let group = try await alix.conversations.newGroup([bo.identity, caro.identity])
// New Group with Metadata
-let group = try await alix.conversations.newGroup([bo.address, caro.address],
+let group = try await alix.conversations.newGroup([bo.identity, caro.identity],
permissionLevel: .admin_only, // .all_members | .admin_only
- groupName: "The Group Name",
- groupImageUrlSquare: "www.groupImage.com",
- groupDescription: "The description of the group",
+ Name: "The Group Name",
+ ImageUrl: "www.groupImage.com",
+ Description: "The description of the group",
)
```
@@ -549,34 +564,34 @@ let group = try await alix.conversations.newGroup([bo.address, caro.address],
### Create a new DM
-Once you have the verified addresses, create a new DM:
+Once you have the verified identity, get its inbox ID and create a new DM:
:::code-group
```js [Browser]
-const group = await client.conversations.newDm(bo.address);
+const group = await client.conversations.newDm(bo.inboxId);
```
```js [Node]
-const group = await client.conversations.newDm(bo.address);
+const group = await client.conversations.newDm(bo.inboxId);
```
```tsx [React Native]
-const dm = await alix.conversations.findOrCreateDm(bo.address);
+const dm = await alix.conversations.findOrCreateDm(bo.inboxId);
```
```kotlin [Kotlin]
-val dm = alix.conversations.findOrCreateDm(bo.address)
+val dm = alix.conversations.findOrCreateDm(bo.inboxId)
// calls the above function under the hood but returns a type conversation instead of a dm
-val conversation = client.conversations.newConversation(address)
+val conversation = client.conversations.newConversation(inboxId)
```
```swift [Swift]
-let dm = try await alix.conversations.findOrCreateDm(with: bo.address)
+let dm = try await alix.conversations.findOrCreateDm(with: bo.inboxId)
// calls the above function under the hood but returns a type conversation instead of a dm
-let conversation = try await client.conversations.newConversation(address)
+let conversation = try await client.conversations.newConversation(inboxId)
```
:::
@@ -655,7 +670,7 @@ try await client.conversations.syncAllConversations()
As more [custom](/inboxes/content-types/content-types#create-a-custom-content-type) and [standards-track](/inboxes/content-types/content-types#standards-track-content-types) content types are introduced into the XMTP ecosystem, your app may encounter content types it does not support. This situation, if not handled properly, could lead to app crashes.
-Each message is accompanied by a `contentFallback` property, which offers a descriptive string representing the content type's expected value. It's important to note that content fallbacks are immutable and are predefined in the content type specification. In instances where `contentFallback` is `undefined`, such as read receipts, it indicates that the content is not intended to be rendered. If you're venturing into creating custom content types, you're provided with the flexibility to specify a custom fallback string. For a deeper dive into this, see [Build custom content types](/inboxes/content-types/custom).
+Each message is accompanied by a `fallback` property, which offers a descriptive string representing the content type's expected value. It's important to note that fallbacks are immutable and are predefined in the content type specification. In instances where `fallback` is `undefined`, such as read receipts, it indicates that the content is not intended to be rendered. If you're venturing into creating custom content types, you're provided with the flexibility to specify a custom fallback string. For a deeper dive into this, see [Build custom content types](/inboxes/content-types/custom).
:::code-group
@@ -663,8 +678,8 @@ Each message is accompanied by a `contentFallback` property, which offers a desc
const codec = client.codecFor(content.contentType);
if (!codec) {
/*Not supported content type*/
- if (message.contentFallback !== undefined) {
- return message.contentFallback;
+ if (message.fallback !== undefined) {
+ return message.fallback;
}
// Handle other types like ReadReceipts which are not meant to be displayed
}
@@ -674,8 +689,8 @@ if (!codec) {
const codec = client.codecFor(content.contentType);
if (!codec) {
/*Not supported content type*/
- if (message.contentFallback !== undefined) {
- return message.contentFallback;
+ if (message.fallback !== undefined) {
+ return message.fallback;
}
// Handle other types like ReadReceipts which are not meant to be displayed
}
@@ -697,8 +712,8 @@ if (!isRegistered) {
val codec = client.codecRegistry.find(options?.contentType)
if (!codec) {
/*Not supported content type*/
- if (message.contentFallback != null) {
- return message.contentFallback
+ if (message.fallback != null) {
+ return message.fallback
}
// Handle other types like ReadReceipts which are not meant to be displayed
}
@@ -708,8 +723,8 @@ if (!codec) {
let codec = client.codecRegistry.find(for: contentType)
if (!codec) {
/*Not supported content type*/
- if (message.contentFallback != null) {
- return message.contentFallback
+ if (message.fallback != null) {
+ return message.fallback
}
// Handle other types like ReadReceipts which are not meant to be displayed
}
@@ -885,7 +900,9 @@ for await message in try await alix.conversations.streamAllMessages(type: /* OPT
### Conversation helper methods
-Use these helper methods to quickly locate and access specific conversations—whether by ID, topic, group ID, or DM address—returning the appropriate ConversationContainer, group, or DM object.
+Use these helper methods to quickly locate and access specific conversations—whether by conversation ID, topic, group ID, or DM identity—returning the appropriate ConversationContainer, group, or DM object.
+
+jhaaaa ConvoId should be renamed to conversationId - jha: docs did not include any instances of convoId
:::code-group
@@ -922,7 +939,7 @@ await alix.conversations.findConversationByTopic(conversation.topic);
// Returns a Group
await alix.conversations.findGroup(group.id);
// Returns a DM
-await alix.conversations.findDmByAddress(bo.address);
+await alix.conversations.findDmByIdentity(bo.identity);
```
```kotlin [Kotlin]
@@ -932,7 +949,7 @@ alix.conversations.findConversationByTopic(conversation.topic)
// Returns a Group
alix.conversations.findGroup(group.id)
// Returns a DM
-alix.conversations.findDm(bo.address)
+alix.conversations.findDm(bo.identity);
```
```swift [Swift]
@@ -942,7 +959,7 @@ try alix.conversations.findConversationByTopic(conversation.topic)
// Returns a Group
try alix.conversations.findGroup(group.id)
// Returns a DM
-try alix.conversations.findDm(bo.address)
+try alix.conversations.findDm(bo.identity)
```
:::
diff --git a/docs/pages/inboxes/content-types/attachments.mdx b/docs/pages/inboxes/content-types/attachments.mdx
index 6dd4fcd..26484f3 100644
--- a/docs/pages/inboxes/content-types/attachments.mdx
+++ b/docs/pages/inboxes/content-types/attachments.mdx
@@ -202,7 +202,7 @@ remoteAttachment.filename = attachment.filename
Send a remote attachment and set the `contentType`:
```kotlin [Kotlin]
-val newConversation = client.conversations.newConversation(walletAddress)
+val newConversation = client.conversations.newConversation(accountIdentity)
newConversation.send(
content = remoteAttachment,
@@ -269,7 +269,7 @@ try await conversation.send(
content: remoteAttachment,
options: .init(
contentType: ContentTypeRemoteAttachment,
- contentFallback: "a description of the image"
+ fallback: "a description of the image"
)
)
```
diff --git a/docs/pages/inboxes/content-types/custom.md b/docs/pages/inboxes/content-types/custom.md
index b2fe3bd..4bbe18d 100644
--- a/docs/pages/inboxes/content-types/custom.md
+++ b/docs/pages/inboxes/content-types/custom.md
@@ -438,6 +438,8 @@ xmtp.registerCodec(new ContentTypeTransactionHashCodec());
This code sample demonstrates how to use the `TransactionHash` content type to send a transaction.
+jhaaaa
+
```jsx [Browser]
// Create a wallet from a known private key
const wallet = new ethers.Wallet(privateKey);
@@ -480,6 +482,8 @@ await conversation
});
```
+jhaaaa
+
### Use the result of the hash
Add an async renderer for the custom content type.
diff --git a/docs/pages/inboxes/disappearing-messages.md b/docs/pages/inboxes/disappearing-messages.md
index b752be8..471d986 100644
--- a/docs/pages/inboxes/disappearing-messages.md
+++ b/docs/pages/inboxes/disappearing-messages.md
@@ -39,6 +39,8 @@ To learn more see [conversation.rs](https://github.com/xmtp/libxmtp/blob/main/bi
For example:
+jhaaaa
+
:::code-group
```tsx [React Native]
@@ -107,6 +109,8 @@ try await client.conversations.newGroup(
:::
+jhaaaa
+
### Update disappearing message settings for an existing conversation
For example:
diff --git a/docs/pages/inboxes/group-metadata.md b/docs/pages/inboxes/group-metadata.md
index 7641e9e..2d98e48 100644
--- a/docs/pages/inboxes/group-metadata.md
+++ b/docs/pages/inboxes/group-metadata.md
@@ -45,11 +45,11 @@ await group.updateName("New Group Name");
```
```kotlin [Kotlin]
-group.updateGroupName("New Group Name")
+group.updateName("New Group Name")
```
```swift [Swift]
-try await group.updateGroupName(groupname: "New Group Name")
+try await group.updateName(groupname: "New Group Name")
```
:::
@@ -85,23 +85,23 @@ try group.groupDescription()
:::code-group
```js [Browser]
-await group.updateGroupDescription("New Group Description");
+await group.updateDescription("New Group Description");
```
```js [Node]
-await group.updateGroupDescription("New Group Description");
+await group.updateDescription("New Group Description");
```
```tsx [React Native]
-await group.updateGroupDescription("New Group Description");
+await group.updateDescription("New Group Description");
```
```kotlin [Kotlin]
-group.updateGroupDescription("New Group Description")
+group.updateDescription("New Group Description")
```
```swift [Swift]
-try await group.updateGroupDescription(groupDescription: "New Group Description")
+try await group.updateDescription(Description: "New Group Description")
```
:::
@@ -119,15 +119,15 @@ const groupImageUrl = group.imageUrl;
```
```tsx [React Native]
-const groupName = await group.imageUrlSquare();
+const groupName = await group.imageUrl();
```
```kotlin [Kotlin]
-group.imageURLSquare
+group.imageURL
```
```swift [Swift]
-try group.groupImageUrlSquare()
+try group.groupImageUrl()
```
:::
@@ -145,15 +145,15 @@ await group.updateImageUrl("newurl.com");
```
```tsx [React Native]
-await group.updateImageUrlSquare("ImageURL");
+await group.updateImageUrl("ImageURL");
```
```kotlin [Kotlin]
-group.updateGroupImageUrlSquare("newurl.com")
+group.updateImageUrl("newurl.com")
```
```swift [Swift]
-try await group.updateGroupImageUrlSquare(imageUrlSquare: "newurl.com")
+try await group.updateImageUrl(imageUrl: "newurl.com")
```
:::
diff --git a/docs/pages/inboxes/group-permissions.mdx b/docs/pages/inboxes/group-permissions.mdx
index 1ffa10a..a7b82ce 100644
--- a/docs/pages/inboxes/group-permissions.mdx
+++ b/docs/pages/inboxes/group-permissions.mdx
@@ -31,7 +31,7 @@ Permissions are the actions a group chat participant can be allowed to take. The
- Add a member to the group
- Remove a member from the group
- Update [group metadata](/inboxes/group-metadata), such as group name, description, and image
-- Update group permissions on an item-by-item basis, such as calling `updateGroupNamePermission` or`updateAddMemberPermission` . To learn more, see [Group.kt](https://github.com/xmtp/xmtp-android/blob/main/library/src/main/java/org/xmtp/android/library/Group.kt#L251-L313) in the xmtp-android SDK repo.
+- Update group permissions on an item-by-item basis, such as calling `updateNamePermission` or`updateAddMemberPermission` . To learn more, see [Group.kt](https://github.com/xmtp/xmtp-android/blob/main/library/src/main/java/org/xmtp/android/library/Group.kt#L251-L313) in the xmtp-android SDK repo.
The following permissions can be assigned by super admins only. This helps ensure that a “regular” admin cannot remove the super admin or otherwise destroy a group.
@@ -82,8 +82,6 @@ To learn more about the `All_Members` and `Admin_Only` policy sets, see [group_p
## Manage group chat admins
-By design, checking admin permission status by wallet address is not supported. Instead, look up the `inboxID` for that wallet address, then use the calls below.
-
### Check if inbox ID is an admin
:::code-group
@@ -327,32 +325,6 @@ try await group.addMembersByInboxId(inboxIds: [inboxId])
:::
-### Add members by address
-
-:::code-group
-
-```js [Browser]
-await group.addMembers([walletAddress]);
-```
-
-```js [Node]
-await group.addMembers([walletAddress]);
-```
-
-```tsx [React Native]
-await group.addMembers([walletAddress]);
-```
-
-```kotlin [Kotlin]
-group.addMembers(listOf(walletAddress))
-```
-
-```swift [Swift]
-try await group.addMembers(addresses: [walletAddress])
-```
-
-:::
-
### Remove members by inbox ID
:::code-group
@@ -379,42 +351,16 @@ try await group.removeMemberInboxIds(inboxIds: [inboxId])
:::
-### Remove members by address
-
-:::code-group
-
-```js [Browser]
-await group.removeMembers([walletAddress]);
-```
-
-```js [Node]
-await group.removeMembers([walletAddress]);
-```
-
-```tsx [React Native]
-await group.removeMembers([walletAddress]);
-```
-
-```kotlin [Kotlin]
-group.removeMembers(listOf(walletAddress))
-```
-
-```swift [Swift]
-try await group.removeMembers(addresses: [walletAddress])
-```
-
-:::
-
### Get inbox IDs for members
:::code-group
```js [Browser]
-const inboxId = await client.findInboxIdByAddress(address);
+const inboxId = await client.findInboxIdByIdentity(identity);
```
```js [Node]
-const inboxId = await client.getInboxIdByAddress(address);
+const inboxId = await client.getInboxIdByIdentity(identity);
```
```tsx [React Native]
@@ -427,7 +373,7 @@ val inboxIds = members.map { it.inboxId }
OR
-val inboxId = client.inboxIdFromAddress(peerAddress)
+val inboxId = client.inboxIdFromIdentity(peerIdentity)
```
```swift [Swift]
@@ -435,12 +381,12 @@ let members = try group.members.map(\.inboxId).sorted()
OR
-try await client.inboxIdFromAddress(address: peerAddress)
+try await client.inboxIdFromIdentity(identity: peerIdentity)
```
:::
-### Get addresses for members
+### Get identities for members
:::code-group
@@ -451,9 +397,9 @@ await group.sync();
// get group members
const members = await group.members();
-// map inbox ID to account addresses
-const inboxIdAddressMap = new Map(
- members.map((member) => [member.inboxId, member.accountAddresses])
+// map inbox ID to account identity
+const inboxIdIdentityMap = new Map(
+ members.map((member) => [member.inboxId, member.accountIdentity])
);
```
@@ -464,24 +410,24 @@ await group.sync();
// get group members
const members = group.members;
-// map inbox ID to account addresses
-const inboxIdAddressMap = new Map(
- members.map((member) => [member.inboxId, member.accountAddresses])
+// map inbox ID to account identity
+const inboxIdIdentityMap = new Map(
+ members.map((member) => [member.inboxId, member.accountIdentity])
);
```
```tsx [React Native]
const members = await group.members();
-const addresses = members.map((member) => member.addresses);
+const identities = members.map((member) => member.identities);
```
```kotlin [Kotlin]
val members = group.members()
-val addresses = members.map { it.addresses }
+val identities = members.map { it.identities }
```
```swift [Swift]
-let peerMembers = try Conversation.group(group).peerAddresses.sorted()
+let peerMembers = try Conversation.group(group).peerIdentities.sorted()
```
:::
@@ -505,7 +451,7 @@ const addedByInboxId = await group.addedByInboxId();
```
```kotlin [Kotlin]
-val addedByAddress = group.addedByInboxId();
+val addedByInboxId = group.addedByInboxId();
```
```swift [Swift]
diff --git a/docs/pages/inboxes/manage-inboxes.mdx b/docs/pages/inboxes/manage-inboxes.mdx
index 054374d..388b8c3 100644
--- a/docs/pages/inboxes/manage-inboxes.mdx
+++ b/docs/pages/inboxes/manage-inboxes.mdx
@@ -1,67 +1,69 @@
# Manage XMTP inboxes
-With XMTP, a user can have one or more inboxes they use to access their messages. An inbox can have multiple wallet addresses associated with it. All messages associated with these addresses flow through the one inbox ID and are accessible in any XMTP app.
+jhaaa fix diagrams and references to addresses that use 0x values
+
+With XMTP, a user can have one or more inboxes they use to access their messages. An inbox can have multiple identities associated with it. All messages associated with these identities flow through the one inbox ID and are accessible in any XMTP app.
For an overview of inbox IDs and their future in XMTP, see [Identity in XMTP](https://xmtp.org/identity).
-The first time someone uses your app with a wallet address they've never used with any app built with XMTP, your app creates an inbox ID and installation ID associated with the wallet address. To do this, you [create a client](/inboxes/build-inbox#create-an-xmtp-client) for their wallet address.
+The first time someone uses your app with an identity they've never used with any app built with XMTP, your app creates an inbox ID and installation ID associated with the identity. To do this, you [create a client](/inboxes/build-inbox#create-an-xmtp-client) for their identity.
-The client creates an inbox ID and installation ID associated with the wallet address. By default, this wallet address is designated as the recovery address. A recovery address will always have the same inbox ID and cannot be reassigned to a different inbox ID.
+The client creates an inbox ID and installation ID associated with the identity. By default, this identity is designated as the recovery identity. A recovery identity will always have the same inbox ID and cannot be reassigned to a different inbox ID.
-When you make subsequent calls to create a client for the same wallet address and a local database is not present, the client uses the same inbox ID, but creates a new installation ID.
+When you make subsequent calls to create a client for the same identity and a local database is not present, the client uses the same inbox ID, but creates a new installation ID.
-You can enable a user to add multiple wallet addresses to their inbox. Added addresses use the same inbox ID and the installation ID of the installation used to add the address.
+You can enable a user to add multiple identities to their inbox. Added identities use the same inbox ID and the installation ID of the installation used to add the identity.
-You can enable a user to remove a wallet address from their inbox. You cannot remove the recovery wallet address.
+You can enable a user to remove an identity from their inbox. You cannot remove the recovery identity.
-## Add a wallet address to an inbox
+## Add an identity to an inbox
:::code-group
```jsx [React Native]
-await client.addAccount(walletToAdd)
+await client.addAccount(identityToAdd)
```
```kotlin [Kotlin]
-client.addAccount(walletToAdd)
+client.addAccount(identityToAdd)
```
```swift [Swift]
-try await client.addAccount(newAccount: walletToAdd)
+try await client.addAccount(newAccount: identityToAdd)
```
:::
-## Remove a wallet address from an inbox
+## Remove an identity from an inbox
:::tip[Note]
- A recovery address cannot be removed. For example, if an inbox has only one associated wallet address, that address serves as the recovery address and cannot be removed.
+ A recovery identity cannot be removed. For example, if an inbox has only one associated identity, that identity serves as the recovery identity and cannot be removed.
:::
:::code-group
```jsx [React Native]
-await client.removeAccount(recoveryWallet, addressToRemove)
+await client.removeAccount(recoveryIdentity, identityToRemove)
```
```kotlin [Kotlin]
-client.removeAccount(recoveryWallet, addressToRemove)
+client.removeAccount(recoveryIdentity, identityToRemove)
```
```swift [Swift]
-try await client.removeAccount(recoveryAccount: recoveryWallet, addressToRemove: addressToRemove)
+try await client.removeAccount(recoveryIdentity: recoveryIdentity, identityToRemove: identityToRemove)
```
:::
@@ -93,42 +95,42 @@ When the user revokes all other installations, the action removes all installati
:::code-group
```jsx [React Native]
-await client.revokeAllOtherInstallations(recoveryWallet)
+await client.revokeAllOtherInstallations(recoveryIdentity)
```
```kotlin [Kotlin]
-client.revokeAllOtherInstallations(recoveryWallet)
+client.revokeAllOtherInstallations(recoveryIdentity)
```
```swift [Swift]
-try await client.revokeAllOtherInstallations(signingKey: recoveryWallet)
+try await client.revokeAllOtherInstallations(signingKey: recoveryIdentity)
```
:::
## View the inbox state
-Find an `inboxId` for an address:
+Find an `inboxId` for an identity:
:::code-group
```jsx [React Native]
-const inboxId = await client.inboxIdFromAddress(address)
+const inboxId = await client.inboxIdFromIdentity(identity)
```
```kotlin [Kotlin]
-val inboxId = client.inboxIdFromAddress(address)
+val inboxId = client.inboxIdFromIdentity(identity)
```
```swift [Swift]
-let inboxId = try await client.inboxIdFromAddress(address: address)
+let inboxId = try await client.inboxIdFromIdentity(identity: identity)
```
:::
-View the state of any inbox to see the addresses, installations, and other information associated with the `inboxId`.
+View the state of any inbox to see the identities, installations, and other information associated with the `inboxId`.
-**Request**
+**Sample request**
:::code-group
@@ -151,79 +153,90 @@ let states = try await client.inboxStatesForInboxIds(
```
:::
-**Response**
+**Sample response**
```json
InboxState
{
- recoveryAddress: string,
- addresses: string[],
- installations: string[],
- inboxId: string,
+ "recoveryIdentity": "string",
+ "identities": [
+ {
+ "kind": "ETHEREUM",
+ "identifier": "string",
+ "relyingPartner": "string"
+ },
+ {
+ "kind": "PASSKEY",
+ "identifier": "string",
+ "relyingPartner": "string"
+ }
+ ],
+ "installations": ["string"],
+ "inboxId": "string"
}
```
## FAQ
-### What happens when a user removes a wallet address?
+### What happens when a user removes an identity?
-Consider an inbox with four associated wallet addresses:
+Consider an inbox with four associated identities:
-If the user removes an address from the inbox, the address no longer has access to the inbox it was removed from.
+If the user removes an identity from the inbox, the identity no longer has access to the inbox it was removed from.
-The address can no longer be added to or used to access conversations in that inbox. If someone sends a message to the address, the message is not associated with the original inbox. If the user logs in to a new installation with the address, this will create a new inbox ID.
+The identity can no longer be added to or used to access conversations in that inbox. If someone sends a message to the identity, the message is not associated with the original inbox. If the user logs in to a new installation with the identity, this will create a new inbox ID.
-### How is the recovery address used?
+### How is the recovery identity used?
-The recovery address and its signer can be used to sign transactions that remove addresses and revoke installations.
+The recovery identity and its signer can be used to sign transactions that remove identities and revoke installations.
For example, Alix can give Bo access to their inbox so Bo can see their groups and conversations and respond for Alix.
-If Alix decides they no longer want Bo have access to their inbox, Alix can use their recovery address signer to remove Bo.
+If Alix decides they no longer want Bo have access to their inbox, Alix can use their recovery identity signer to remove Bo.
-However, while Bo has access to Alix's inbox, Bo cannot remove Alix from their own inbox because Bo does not have access to Alix's recovery address signer.
+However, while Bo has access to Alix's inbox, Bo cannot remove Alix from their own inbox because Bo does not have access to Alix's recovery identity signer.
-### If a user created two inboxes using two wallet addresses, is there a way to combine the inboxes?
+### If a user created two inboxes using two identities, is there a way to combine the inboxes?
-If a user logs in with wallet address 0x62EE...309c and creates inbox 1 and then logs in with wallet address 0xd0e4...DCe8 and creates inbox 2; there is no way to combine inbox 1 and 2.
+If a user logs in with address 0x62EE...309c and creates inbox 1 and then logs in with address 0xd0e4...DCe8 and creates inbox 2; there is no way to combine inbox 1 and 2.
-You can add wallet address 0xd0e4...DCe8 to inbox 1, but both address 0x62EE...309c and 0xd0e4...DCe8 would then have access to inbox 1 only. Wallet address 0xd0e4...DCe8 would no longer be able to access inbox 2.
+You can add address 0xd0e4...DCe8 to inbox 1, but both address 0x62EE...309c and 0xd0e4...DCe8 would then have access to inbox 1 only. Address 0xd0e4...DCe8 would no longer be able to access inbox 2.
-To help users avoid this state, ensure that your UX surfaces their ability to add multiple wallet addresses to a single inbox.
+To help users avoid this state, ensure that your UX surfaces their ability to add multiple identities to a single inbox.
-### What happens if I remove a wallet address from an inbox ID and then initiate a client with the private key of the removed address?
+### What happens if I remove an identity from an inbox ID and then initiate a client with the private key of the removed identity?
-**Does the client create a new inbox ID or does it match it with the original inbox ID the address was removed from?**
+**Does the client create a new inbox ID or does it match it with the original inbox ID the identity was removed from?**
-The wallet address used to initiate a client should be matched to its original inbox ID.
+The identity used to initiate a client should be matched to its original inbox ID.
-You do have the ability to rotate inbox IDs if a user reaches the limit of 257 identity actions (adding, removing, or revoking addresses or installations). Hopefully, users won’t reach this limit, but if they do, inbox IDs have a nonce and can be created an infinite number of times.
+You do have the ability to rotate inbox IDs if a user reaches the limit of 257 identity actions (adding, removing, or revoking identities or installations). Hopefully, users won’t reach this limit, but if they do, inbox IDs have a nonce and can be created an infinite number of times.
-However, anytime a new inbox ID is created for an address, the conversations and messages in any existing inbox ID associated with the address are lost.
+However, anytime a new inbox ID is created for an identity, the conversations and messages in any existing inbox ID associated with the identity are lost.
-### I have multiple addresses associated with one inbox ID. If I log in with any one of these addresses, does it access that inbox ID, or does it create a new inbox ID?
+### I have multiple identities associated with one inbox ID. If I log in with any one of these identities, does it access that inbox ID, or does it create a new inbox ID?
-The address accesses that inbox ID and does not create a new inbox ID.
+The identity accesses that inbox ID and does not create a new inbox ID.
For example, let's say that you create a client with an address 0x62EE...309c. Inbox ID 1 is generated from that address 0x62EE...309c.
diff --git a/docs/pages/inboxes/push-notifs/understand-push-notifs.md b/docs/pages/inboxes/push-notifs/understand-push-notifs.md
index 3464b89..0c733cb 100644
--- a/docs/pages/inboxes/push-notifs/understand-push-notifs.md
+++ b/docs/pages/inboxes/push-notifs/understand-push-notifs.md
@@ -102,12 +102,16 @@ This is one of the jobs of the [history sync](/inboxes/history-sync) feature. It
## Understand DM stitching and push notifications
+jhaaaa
+
Consider a scenario where `alix.eth` using an existing installation #1 to create a conversation with `bo.eth` and sends them a DM. And then `alix.eth` creates a new installation #2, and instead of waiting for [history sync](https://docs.xmtp.org/inboxes/history-sync) to bring in their existing conversations, `alix.eth` creates a new conversation with `bo.eth` and sends them a DM. Under the hood, this results in two DM conversations (or two MLS groups) with the same pair of addresses, `alix.eth` and `bo.eth`, resulting in a confusing DM UX like this one:

XMTP implements DM stitching to ensure that even if there are multiple DMs with the same pair of addresses under the hood, your users see only one DM conversation with messages displayed appropriately.
+jhaaaa
+
For example, with DM stitching, instead of seeing two separate DM conversations between `alix.eth` and `bo.eth` with one message each, `alix.eth` sees one DM conversation between `alix.eth` and `bo.eth` with two messages in both installations #1 and #2

@@ -129,7 +133,7 @@ For example, with DM stitching, instead of seeing two separate DM conversations
### DM stitching considerations for push notifications
-DM stitching addresses provides a unified UX in the app. However, the multiple DM conversations under the hood must still be addressed for push notifications.
+DM stitching provides a unified UX in the app. However, the multiple DM conversations under the hood must still be addressed for push notifications.
Let’s take DM conversations alix-bo-1 and alix-bo-3 between `alix.eth` and `bo.eth`. With DM stitching, these two conversations display as one conversation. However, we must remember that they have two different conversation IDs, and thus two different topics.
diff --git a/docs/pages/inboxes/use-signatures.md b/docs/pages/inboxes/use-signatures.md
index d131411..cc654b7 100644
--- a/docs/pages/inboxes/use-signatures.md
+++ b/docs/pages/inboxes/use-signatures.md
@@ -4,8 +4,12 @@ With XMTP, you can use various types of signatures to sign and verify payloads.
## Sign with an external wallet
+jhaaaa
+
When a user creates, adds, removes, or revokes an XMTP inbox’s address or installation, a signature from an external wallet is required.
+jhaaaa
+
## Sign with an XMTP key
You can sign something with XMTP keys. For example, you can sign with XMTP keys to send a payload to a backend.
diff --git a/docs/pages/inboxes/user-consent/support-user-consent.md b/docs/pages/inboxes/user-consent/support-user-consent.md
index 28c6db9..dd9eb01 100644
--- a/docs/pages/inboxes/user-consent/support-user-consent.md
+++ b/docs/pages/inboxes/user-consent/support-user-consent.md
@@ -329,16 +329,24 @@ You can filter these unknown contacts to:
### Identify contacts the user might know
+jhaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
+
To identify contacts the user might know or want to know, you can look for signals in onchain data that imply an affinity between addresses. You can then display appropriate messages on a **You might know** tab, for example.
+jhaaaa
+
### Identify contacts the user might not know, including spammy or scammy requests
+jhaaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
+
To identify contacts the user might not know or not want to know, which might include spam, you can consciously decide to scan messages in an unencrypted state to find messages that might contain spammy or scammy content. You can also look for an absence of onchain interaction data between the addresses, which might indicate that there is no affinity between addresses. You can then filter the appropriate messages to display on a **Hidden requests** tab, for example.
+jhaaaa
+
@@ -353,8 +361,12 @@ The decision to scan unencrypted messages is yours as the app developer. If you
XMTP is a decentralized, open protocol built to ensure private, secure, and censorship-resistant communication. As such, XMTP can't read unencrypted messages, and therefore, it also can't scan or filter message contents for spammy or scammy material.
+jhaaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
+
The protocol can analyze onchain data signals, such as shared activity between wallet addresses, to infer potential affinities between addresses. However, because all XMTP repositories are open source, malicious actors could inspect these methods and develop workarounds to bypass them.
+jhaaaa
+
Additionally, applying spam filtering or content moderation directly at the protocol level would introduce centralization, which goes against the decentralized, permissionless, and open ethos of XMTP and web3. A protocol-driven approach could limit interoperability and trust by imposing subjective rules about content across all apps.
Instead, content filtering and moderation should be implemented at the app layer. Apps can decide how opinionated or lenient they want to be, tailoring their filtering approach to the needs of their users. For example, one app may choose to aggressively scan and block spam to provide a highly curated experience, attracting users who value more protection. Another app may opt for minimal or no filtering, appealing to users who prioritize full control and unfiltered communication.
diff --git a/docs/pages/inboxes/user-consent/user-consent.mdx b/docs/pages/inboxes/user-consent/user-consent.mdx
index 0ed809d..020c5df 100644
--- a/docs/pages/inboxes/user-consent/user-consent.mdx
+++ b/docs/pages/inboxes/user-consent/user-consent.mdx
@@ -12,7 +12,7 @@ This video provides a walkthrough of consent, covering the key ideas discussed i
## How user consent preferences work
-With user consent preferences, a wallet address registered on the XMTP network can have one of three user consent preference values in relation to another user's wallet address:
+With user consent preferences, an identity registered on the XMTP network can have one of three user consent preference values in relation to another user's identity:
- Unknown
- Allowed
@@ -20,6 +20,8 @@ With user consent preferences, a wallet address registered on the XMTP network c
For example:
+jhaaaa how to handle examples that currently use ENS names?
+
1. `alix.eth` starts a conversation with `bo.eth`. At this time, `alix.eth` is unknown to `bo.eth` and the conversation displays in a message requests UI.
2. When `bo.eth` views the message request, they express their user consent preference to **Block** or **Accept** `alix.eth` as a contact.
diff --git a/docs/pages/intro/faq.mdx b/docs/pages/intro/faq.mdx
index 2325972..4029142 100644
--- a/docs/pages/intro/faq.mdx
+++ b/docs/pages/intro/faq.mdx
@@ -40,12 +40,16 @@ The XMTP SDK **does not** include a wallet app abstraction, as XMTP assumes that
### Chains
+jhaaaa
+
XMTP can work with signatures from any private public key pair and currently supports EOAs and SCWs on Ethereum and Ethereum side-chains and L2s.
Because all Ethereum Virtual Machine (EVM) chains share the same Ethereum wallet and address format and XMTP messages are stored off-chain, XMTP is interoperable across EVM chains, including testnets. (XMTP itself does not use EVMs.)
For example, whether a user has their wallet app connected to Ethereum or an Ethereum side-chain or L2, their private key can generate and retrieve their XMTP key pair to give them access to XMTP.
+jhaaaa
+
XMTP is also chain-agnostic, so multi-chain support is possible.
Here are just a few chains that work with XMTP:
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index 954126a..51803ec 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -3,6 +3,7 @@
The process to upgrade an app built with XMTP V2 to V3 is designed to be straightforward, with most functions in V3 working as they did in V2. However, there are some notable differences, which we cover here.
:::info[Key takeaways]
+- **Primary XMTP identifier is now a flexible identity object, not an Ethereum address** as covered in this document.
- **Most core methods from V2 work in a similar way in V3**, with some notable differences that are covered in this document.
- **We recommend that apps upgrade directly to V3**, giving people access to a pure V3+ messaging experience with stronger encryption and laying the foundation for decentralization of the network. To learn more, see the [FAQ](/upgrade-to-v3#faq).
- ⛔️ **Rolling brownouts of the V2 network start on April 1, 2025. V2 will be deprecated on May 1, 2025**, after which all V2 conversations and messages will become read-only. To learn more, see [XIP 53: XIP V2 deprecation plan](https://community.xmtp.org/t/xip-53-xmtp-v2-deprecation-plan/867). Users will still be able to access their V2 communications in read-only format using [https://legacy.xmtp.chat/](https://legacy.xmtp.chat/).
@@ -16,11 +17,98 @@ The process to upgrade an app built with XMTP V2 to V3 is designed to be straigh
- 🟢 **The agent upgrade path is ready**. For detailed guidance, [open an issue](https://github.com/xmtp/xmtp-js/issues) in the Node SDK GitHub repo.
+## Primary XMTP identifier is now a flexible identity object, not an Ethereum address
+
+XMTP is evolving from using Ethereum account addresses (0x...) as the primary identifier to a more flexible identity model. This change allows for broader support of different authentication mechanisms, including the currently supported Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs), as well as future support for Passkeys.
+
+Instead of assuming an Ethereum address as the unique identifier, developers now define an identity object that explicitly includes the identity type (kind) and the identifier.
+
+With this new model, apps reference identities or inbox IDs, rather than Ethereum addresses. Some identity types (like Passkeys) do not have an associated address, so identities and inbox IDs provide a consistent way to identify users across different authentication methods.
+
+This change ensures that XMTP identities are more extensible and adaptable, accommodating future improvements in authentication methods while maintaining backward compatibility for Ethereum-based accounts.
+
+### Example: Supporting multiple identity types
+
+With this new model, an app can now distinguish different identity types when creating a signer
+
+:::tip
+
+Passkeys are not yet supported and are referenced here just to illustrate the flexibility of the new identity model.
+
+:::
+
+```tsx
+function createSigner(account): Signer {
+ return {
+ getIdentity: async () => ({
+ kind: account.isSCW ? "ETHEREUM" : "PASSKEY",
+ identifier: account.address || account.passkeyId,
+ }),
+ signMessage: async (message) => {
+ return account.signMessage(message);
+ },
+ getChainId: account.isSCW ? () => BigInt(8453) : undefined,
+ getBlockNumber: account.isSCW ? () => undefined : undefined,
+ };
+}
+```
+
+### Before: Using getAddress()
+
+Previously, developers used `getAddress()` to retrieve an account’s Ethereum address:
+
+```tsx
+const signer: Signer = {
+ getAddress: () => "0x123...",
+ signMessage: async (message) => {
+ // return signed message
+ },
+};
+```
+
+While this approach worked for EOAs, it assumed that all accounts were Ethereum addresses and did not allow for other identity types.
+
+### After: Using getIdentity()
+
+Now, `getIdentity()` returns an identity object, allowing for multiple types of accounts:
+
+```tsx
+const signer: Signer = {
+ getIdentity: () => ({
+ kind: "ETHEREUM", // Identity type (ETHEREUM, PASSKEY, etc.)
+ identifier: "0x123...", // Account identifier
+ }),
+ signMessage: async (message) => {
+ // return signed message
+ },
+};
+```
+
+### Before: newConveration()
+
+Previously, developers used an Ethereum address to create a new DM conversation:
+
+```tsx
+const dm = await alix.conversations.findOrCreateDm(bo.address);
+```
+
+### After: newConveration()
+
+Now, developers can use `inboxId` to create a new DM conversation because with the new flexible identity model, they cannot rely on the existence of an address.
+
+```tsx
+const dm = await alix.conversations.findOrCreateDm(bo.inboxId);
+```
+
## Core methods from V2 work in a similar way in V3
Most core methods from V2, such as `newConversation`, `list`, `stream`, and `streamAllMessages`, work in a similar way in V3.
-However, a key difference is that prior to V3, a conversation could represent a V1 or V2 conversation. In V3, a conversation can represent a group chat or direct message (DM).
+However, key differences include:
+
+- `newConversation` no longer takes addresses, but rather inbox IDs, as covered in [Primary XMTP identifier is now a flexible identity object, not an Ethereum address](#primary-xmtp-identifier-is-now-a-flexible-identity-object-not-an-ethereum-address)
+
+- Prior to V3, a conversation could represent a V1 or V2 conversation. In V3, a conversation can represent a group chat or direct message (DM).
To learn more, see [Build a chat inbox](/inboxes/build-inbox).
@@ -72,8 +160,19 @@ In V3, we have installation-specific key bundles that are stored securely in the
- In V2, we managed consent via `client.contacts.consentList`.
- In V3, we can manage consent via `client.preferences.getAddressConsent(address)`. However, we recommend that you now manage consent at the conversation level by `conversationId`. To learn more, see [Support user consent preferences](/inboxes/user-consent/support-user-consent#support-user-consent-preferences-to-provide-spam-free-inboxes).
+<<<<<<<<>>>>>>>>
+
## Summary of notable changes
+<<<<<<<<<<<>>>>>>>>>>>
+
| Purpose | V2 method | V3 equivalent |
| --- | --- | --- |
| Loading messages | `listBatchMessages` | `list()` |
From c23205df47c531c7f8c8efa246fc04e406af0a4a Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Thu, 6 Mar 2025 12:03:00 -0800
Subject: [PATCH 02/38] more updates
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
docs/pages/inboxes/content-types/custom.md | 505 ------------------
docs/pages/inboxes/disappearing-messages.md | 18 +-
docs/pages/inboxes/manage-inboxes.mdx | 6 +
.../push-notifs/understand-push-notifs.mdx | 16 +-
docs/pages/inboxes/use-signatures.md | 6 +-
.../user-consent/support-user-consent.md | 12 +-
.../inboxes/user-consent/user-consent.mdx | 10 +-
docs/pages/intro/faq.mdx | 12 +-
9 files changed, 32 insertions(+), 555 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index eeef60c..5acb08d 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -30,7 +30,7 @@ Learn to build a chat inbox with the help of [XMTP.chat](https://xmtp.chat/), an
This code defines two functions that convert different types of Ethereum accounts—Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs)—into a unified `Signer` interface. This ensures that both account types conform to a common interface for message signing and deriving shared secrets as per MLS (Message Layer Security) requirements.
-jhaaaa these EOA and SCW code samples include "address" in the comments to callout when the kind is Ethereum and the identifier is an Ethereum address. Is this OK or have I misunderstood?
+jhaaaa these EOA and SCW code samples include "address" in the comments to call out when the kind is Ethereum and the identifier is an Ethereum address. Is this OK or have I misunderstood?
- For an EOA, the `convertEOAToSigner` function creates a signer that can get the account identity and sign messages and has placeholder methods for chain ID and block number.
diff --git a/docs/pages/inboxes/content-types/custom.md b/docs/pages/inboxes/content-types/custom.md
index 4bbe18d..9c90b3e 100644
--- a/docs/pages/inboxes/content-types/custom.md
+++ b/docs/pages/inboxes/content-types/custom.md
@@ -23,508 +23,3 @@ If another app wants to display your custom content type, they must implement yo
For more common content types, you can usually find a [standard](/inboxes/content-types/content-types#standard-content-types) or [standards-track](/inboxes/content-types/content-types#standards-track-content-types) content type to serve your needs.
If your custom content type generates interest within the developer community, consider proposing it as a standard content type through the [XIP process](/intro/xips).
-
-This document describes how to build custom content types using two examples:
-
-- Build a basic custom content type that multiplies numbers
-- Build an advanced custom content type that sends a transaction hash
-
-## Basic: Build a custom content type that multiplies numbers
-
-### Create the content type
-
-Create the custom content type by creating a new file
-
-:::code-group
-
-```jsx [Browser]
-// A test of this content type can be found in the following PR: https://github.com/xmtp/xmtp-js/pull/509/files
-import { ContentTypeId } from "@xmtp/content-type-primitives";
-import type { ContentCodec, EncodedContent } from "@xmtp/content-type-primitives";
-
-// Create a unique identifier for your content type
-export const ContentTypeMultiplyNumbers = new ContentTypeId({
- authorityId: 'your.domain',
- typeId: 'multiply-number',
- versionMajor: 1,
- versionMinor: 0,
-})
-
-// Define the MultiplyNumbers class
-export class MultiplyNumbers {
- public num1: number
- public num2: number
- public result: number | undefined
-
- constructor(num1: number, num2: number, result?: number) {
- this.num1 = num1
- this.num2 = num2
- this.result = result
- }
-}
-
-// Define the MultiplyCodec class
-export class ContentTypeMultiplyNumberCodec
- implements ContentCodec
-{
- get contentType(): ContentTypeId {
- return ContentTypeMultiplyNumbers
- }
-
- // The encode method accepts an object of MultiplyNumbers and encodes it as a byte array
- encode(numbers: MultiplyNumbers): EncodedContent {
- const { num1, num2 } = numbers
- return {
- type: ContentTypeMultiplyNumbers,
- parameters: {},
- content: new TextEncoder().encode(JSON.stringify({ num1, num2 })),
- }
- }
-
- // The decode method decodes the byte array, parses the string into numbers (num1, num2), and returns their product
- decode(encodedContent: EncodedContent): MultiplyNumbers {
- const decodedContent = new TextDecoder().decode(encodedContent.content)
- const { num1, num2 } = JSON.parse(decodedContent)
- return new MultiplyNumbers(num1, num2, num1 * num2)
- }
-
- fallback(content: MultiplyNumbers): string | undefined {
- return `Can’t display number content types. Number was ${JSON.stringify(
- content
- )}`
- // return undefined to indicate that this content type should not be displayed if it's not supported by a client
- }
-
- // Set to true to enable push notifications for interoperable content types.
- // Receiving clients must handle this field appropriately.
- shouldPush(): boolean {
- return true;
- }
-}
-```
-
-```jsx [React Native]
-import { content } from '@xmtp/proto'
-
-import {JSContentCodec, Client, Conversation, DecodedMessage } from '@xmtp/react-native-sdk';
-
-type EncodedContent = content.EncodedContent
-type ContentTypeId = content.ContentTypeId
-
-const ContentTypeMultiplyNumbers: ContentTypeId = {
- authorityId: 'com.example',
- typeId: 'multiplyNumbers',
- versionMajor: 1,
- versionMinor: 1,
-}
-class MultiplyNumbers {
- public readonly num1: number
- public readonly num2: number
- public readonly result: number
-
- constructor(num1: number, num2: number, result: number) {
- this.num1 = num1
- this.num2 = num2
- this.result = result
- }
-}
-
-class ContentTypeMultiplyNumberCodec
- implements JSContentCodec
-{
- get contentType() {
- return ContentTypeMultiplyNumbers
- }
-
- encode(decoded: MultiplyNumbers): EncodedContent {
- return {
- type: ContentTypeMultiplyNumbers,
- parameters: {
- num1: decoded.num1.toString(),
- num2: decoded.num2.toString(),
- },
- content: new Uint8Array(),
- }
- }
-
- decode(encoded: EncodedContent): MultiplyNumbers {
- const num1 = parseFloat(encoded.parameters['num1'] ?? '0')
- const num2 = parseFloat(encoded.parameters['num2'] ?? '0')
- return new MultiplyNumbers(num1, num2, num1 * num2)
- }
-
- fallback(content: MultiplyNumbers): string {
- return `MultiplyNumbersCodec is not supported`
- }
-
- // This method is optional and can be used to determine if the content type should trigger a push notification
- shouldPush(): boolean {
- return true;
- }
-}
-```
-
-```kotlin [Kotlin]
-import org.json.JSONObject
-import org.xmtp.android.library.codecs.ContentTypeId
-import org.xmtp.android.library.codecs.ContentTypeIdBuilder
-import org.xmtp.android.library.codecs.ContentCodec
-import org.xmtp.android.library.codecs.EncodedContent
-
-data class NumberPair(val a: Double, val b: Double, var result: Double = 0.0)
-
-data class MultiplyNumberCodec(
- override var contentType: ContentTypeId = ContentTypeIdBuilder.builderFromAuthorityId(
- authorityId = "your.domain",
- typeId = "multiply-number",
- versionMajor = 1,
- versionMinor = 0
- )
-) : ContentCodec {
- override fun encode(content: NumberPair): EncodedContent {
- val jsonObject = JSONObject()
- jsonObject.put("a", content.a)
- jsonObject.put("b", content.b)
- return EncodedContent.newBuilder().also {
- it.type = contentType
- it.content = jsonObject.toString().toByteStringUtf8()
- }.build()
- }
-
- override fun decode(content: EncodedContent): NumberPair {
- val jsonObject = JSONObject(content.content.toStringUtf8())
- val a = jsonObject.getDouble("a")
- val b = jsonObject.getDouble("b")
- val numberPair = NumberPair(a, b, a * b)
- return numberPair
- }
-
- override fun fallback(content: NumberPair): String? {
- return "Error: This app does not support numbers."
- }
-
- // This method is optional and can be used to determine if the content type should have a push notification
- override fun shouldPush(): Boolean {
- return true;
- }
-}
-```
-
-```swift [Swift]
-// A test of this content type can be found in the following PR: https://github.com/xmtp/xmtp-ios/pull/211
-import XMTP
-
-public struct MultiplyNumbers {
- public var num1: Double
- public var num2: Double
- public var result: Double?
-
- public init(num1: Double, num2: Double, result: Double? = nil) {
- self.num1 = num1
- self.num2 = num2
- self.result = result
- }
-}
-
-public struct MultiplyNumbersCodec: ContentCodec {
- public typealias T = MultiplyNumbers
-
- public var contentType: ContentTypeID {
- ContentTypeID(authorityID: "example.com", typeID: "number", versionMajor: 1, versionMinor: 1)
- }
-
- public func encode(content: MultiplyNumbers, client: Client) throws -> EncodedContent {
- var encodedContent = EncodedContent()
- encodedContent.type = contentType
- encodedContent.parameters["num1"] = String(content.num1)
- encodedContent.parameters["num2"] = String(content.num2)
- return encodedContent
- }
-
- public func decode(content: EncodedContent, client _: Client) throws -> MultiplyNumbers {
- guard let num1Str = content.parameters["num1"], let num1 = Double(num1Str),
- let num2Str = content.parameters["num2"], let num2 = Double(num2Str) else {
- throw CodecError.invalidContent
- }
- return MultiplyNumbers(num1: num1, num2: num2, result: num1 * num2)
- }
-
- public func fallback(content: MultiplyNumbers) throws -> String? {
- return "MultiplyNumbersCodec is not supported"
- }
-
-
- // This method is optional and can be used to determine if the content type should have a push notification
- public func shouldPush() -> Bool {
- return true;
- }
-}
-```
-
-:::
-
-### Configure the content type
-
-Import and register the custom content type.
-
-:::code-group
-
-```jsx [Browser]
-import { ContentTypeMultiplyNumberCodec } from "./xmtp-content-type-multiply-number";
-
-const client = await Client.create({
- env: "production",
- codecs: [new ContentTypeMultiplyNumberCodec()],
-});
-//or
-client.registerCodec(new ContentTypeMultiplyNumberCodec());
-```
-
-```jsx [React Native]
-import { ContentTypeMultiplyNumberCodec } from "./xmtp-content-type-number";
-
-const client = await Client.create({
- env: "production",
- codecs: [new ContentTypeMultiplyNumberCodec()],
-});
-```
-
-```kotlin [Kotlin]
-import org.xmtp.android.library.codecs.ContentTypeMultiplyNumberCodec
-
-Client.register(codec = ContentTypeMultiplyNumberCodec())
-```
-
-```swift [Swift]
-Client.register(codec: ContentTypeMultiplyNumberCodec())
-```
-
-:::
-
-### Send the content
-
-Send a message using the custom content type. This code sample demonstrates how to use the `MultiplyCodec` custom content type to perform multiplication operations.
-
-:::code-group
-
-```jsx [Browser]
-const numbersToMultiply = new MultiplyNumbers(2, 3);
-
-conversation.send(numbersToMultiply, {
- contentType: ContentTypeMultiplyNumbers,
-});
-```
-
-```jsx [React Native]
-const multiplyNumbers = new MultiplyNumbers(3, 7);
-await bobConvo.send(multiplyNumbers, {
- contentType: ContentTypeMultiplyNumbers,
-});
-```
-
-```kotlin [Kotlin]
-import org.xmtp.android.library.codecs.ContentTypeMultiplyNumberCodec
-
-val numbers = NumberPair(
- a = 3,
- b = 7,
-)
-
-conversation.send(
- content = numbers,
- options = SendOptions(contentType = ContentTypeMultiplyNumberCodec().contentType),
-)
-```
-
-```swift [Swift]
-let multiplyNumbers = MultiplyNumbers(num1: 3, num2: 2)
-try await aliceConversation.send(content: multiplyNumbers, options: .init(contentType: ContentTypeMultiplyNumberCodec().contentType))
-```
-
-:::
-
-### Receive the content
-
-To use the result of the multiplication operation, add a renderer for the custom content type.
-
-To handle unsupported content types, see the [fallback](/inboxes/build-inbox/#handle-unsupported-content-types) section.
-
-:::code-group
-
-```jsx [Browser]
-if (message.contentType.sameAs(ContentTypeMultiplyNumber)) {
- return message.content; // 21
-}
-```
-
-```jsx [React Native]
-// Because of this message content is now a function which returns the actual content.
-// You can get that content by call `message.content()` now instead of message.content.
-// This may involve more filtering on the message side to make sure you are handling different contentTypes appropriately.
-
-if (message.contentTypeId === "com.example/multiplyNumbers:1.1") {
- return message.content(); // 21
-}
-```
-
-```swift [Swift]
-if let content: MultiplyNumbers = try? messages[0].content() {
- //content.result
-}
-```
-
-:::
-
-## Advanced: Build a custom content type to send a transaction hash
-
-This tutorial covers how to build a custom content type that sends transaction hashes on the Polygon blockchain. This example also describes how to use the custom content type to render the transaction hash.
-
-:::warning
-
-Be aware that a custom content type may not be automatically recognized or supported by other applications, which could result in it being overlooked or only its fallback text being displayed.
-
-:::
-
-### Create the custom content type
-
-Create a new file, `xmtp-content-type-transaction-hash.tsx`. This file hosts the `TransactionHash` class for encoding and decoding the custom content type.
-
-```jsx [Browser]
-import { ContentTypeId } from "@xmtp/xmtp-js";
-
-export const ContentTypeTransactionHash = new ContentTypeId({
- authorityId: "your.domain",
- typeId: "transaction-hash",
- versionMajor: 1,
- versionMinor: 0,
-});
-
-export class ContentTypeTransactionHashCodec {
- get contentType() {
- return ContentTypeTransactionHash;
- }
-
- encode(hash) {
- return {
- type: ContentTypeTransactionHash,
- parameters: {},
- content: new TextEncoder().encode(hash),
- };
- }
-
- decode(content: { content: any }) {
- const uint8Array = content.content;
- const hash = new TextDecoder().decode(uint8Array);
- return hash;
- }
-}
-```
-
-### Import and register the custom content type
-
-```jsx [Browser]
-import {
- ContentTypeTransactionHash,
- ContentTypeTransactionHashCodec,
-} from "./xmtp-content-type-transaction-hash";
-
-const xmtp = await Client.create(signer, {
- env: "dev",
-});
-xmtp.registerCodec(new ContentTypeTransactionHashCodec());
-```
-
-### Send a message using the custom content type
-
-This code sample demonstrates how to use the `TransactionHash` content type to send a transaction.
-
-jhaaaa
-
-```jsx [Browser]
-// Create a wallet from a known private key
-const wallet = new ethers.Wallet(privateKey);
-console.log(`Wallet address: ${wallet.address}`);
-
-//im using a burner wallet with MATIC from a faucet
-//https://faucet.polygon.technology/
-
-// Set up provider for Polygon Testnet (Mumbai)
-const provider = new ethers.providers.JsonRpcProvider(
- "https://rpc-mumbai.maticvigil.com",
-);
-
-// Connect the wallet to the provider
-const signer = wallet.connect(provider);
-
-// Define the recipient address and amount
-const amount = ethers.utils.parseEther("0.01"); // Amount in ETH (0.01 in this case)
-
-// Create a transaction
-const transaction = {
- to: recipientAddress,
- value: amount,
-};
-
-// Sign and send the transaction
-const tx = await signer.sendTransaction(transaction);
-console.log(`Transaction hash: ${tx.hash}`);
-
-const conversation = await xmtp.conversations.newConversation(WALLET_TO);
-await conversation
- .send(tx.hash, {
- contentType: ContentTypeTransactionHash,
- })
- .then(() => {
- console.log("Transaction data sent", tx.hash);
- })
- .catch((error) => {
- console.log("Error sending transaction data: ", error);
- });
-```
-
-jhaaaa
-
-### Use the result of the hash
-
-Add an async renderer for the custom content type.
-
-```jsx [Browser]
-if (message.contentType.sameAs(ContentTypeTransactionHash)) {
- // Handle ContentTypeAttachment
- return (
-
- );
-}
-
-const TransactionMonitor = ({ encodedContent }) => {
- const [retryCount, setRetryCount] = useState(0);
-
- const [transactionValue, setTransactionValue] = useState(null);
-
- useEffect(() => {
- const fetchTransactionReceipt = async () => {
- console.log(encodedContent);
- const provider = new ethers.providers.JsonRpcProvider(
- "https://rpc-mumbai.maticvigil.com",
- );
- const receipt = await provider.getTransactionReceipt(encodedContent);
- const tx = await provider.getTransaction(encodedContent);
- if (tx && tx.value) {
- setTransactionValue(ethers.utils.formatEther(tx.value));
- }
- };
- fetchTransactionReceipt();
- }, [encodedContent, retryCount]);
-
- return transactionValue ? (
-
Transaction value: {transactionValue} ETH
- ) : (
-
- Waiting for transaction to be mined...
-
-
- );
-};
-```
\ No newline at end of file
diff --git a/docs/pages/inboxes/disappearing-messages.md b/docs/pages/inboxes/disappearing-messages.md
index 471d986..ceba844 100644
--- a/docs/pages/inboxes/disappearing-messages.md
+++ b/docs/pages/inboxes/disappearing-messages.md
@@ -39,15 +39,13 @@ To learn more see [conversation.rs](https://github.com/xmtp/libxmtp/blob/main/bi
For example:
-jhaaaa
-
:::code-group
```tsx [React Native]
// DM
await client.conversations.newConversation(
- address,
- {
+ identity,
+ {
disappearingMessageSettings: DisappearingMessageSettings(
disappearStartingAtNs: 1738620126404999936,
retentionDurationInNs: 1800000000000000
@@ -57,7 +55,7 @@ await client.conversations.newConversation(
// Group
await client.conversations.newGroup(
- [address],
+ [identity],
{
disappearingMessageSettings: DisappearingMessageSettings(
disappearStartingAtNs: 1738620126404999936,
@@ -70,7 +68,7 @@ await client.conversations.newGroup(
```kotlin [Kotlin]
// DM
client.conversations.newConversation(
- address,
+ identity,
disappearingMessageSettings = DisappearingMessageSettings(
disappearStartingAtNs = 1738620126404999936,
retentionDurationInNs = 1800000000000000
@@ -79,7 +77,7 @@ client.conversations.newConversation(
// Group
client.conversations.newGroup(
- [address],
+ [identity],
disappearingMessageSettings = DisappearingMessageSettings(
disappearStartingAtNs = 1738620126404999936,
retentionDurationInNs = 1800000000000000
@@ -90,7 +88,7 @@ client.conversations.newGroup(
```swift [Swift]
// DM
try await client.conversations.newConversation(
- with: address,
+ with: identity,
disappearingMessageSettings: DisappearingMessageSettings(
disappearStartingAtNs: 1738620126404999936,
retentionDurationInNs: 1800000000000000
@@ -99,7 +97,7 @@ try await client.conversations.newConversation(
// Group
try await client.conversations.newGroup(
- with: [address],
+ with: [identity],
disappearingMessageSettings: DisappearingMessageSettings(
disappearStartingAtNs: 1738620126404999936,
retentionDurationInNs: 1800000000000000
@@ -109,8 +107,6 @@ try await client.conversations.newGroup(
:::
-jhaaaa
-
### Update disappearing message settings for an existing conversation
For example:
diff --git a/docs/pages/inboxes/manage-inboxes.mdx b/docs/pages/inboxes/manage-inboxes.mdx
index 388b8c3..d9d0015 100644
--- a/docs/pages/inboxes/manage-inboxes.mdx
+++ b/docs/pages/inboxes/manage-inboxes.mdx
@@ -30,6 +30,12 @@ You can enable a user to remove an identity from their inbox. You cannot remove
## Add an identity to an inbox
+:::warning[Warning]
+
+This function is delicate and should be used with caution. Adding an identity to an inbox ID B when it's already associated with an inbox ID A will cause the identity to lose access to inbox ID A.
+
+:::
+
:::code-group
```jsx [React Native]
diff --git a/docs/pages/inboxes/push-notifs/understand-push-notifs.mdx b/docs/pages/inboxes/push-notifs/understand-push-notifs.mdx
index fee94f4..9d8dda1 100644
--- a/docs/pages/inboxes/push-notifs/understand-push-notifs.mdx
+++ b/docs/pages/inboxes/push-notifs/understand-push-notifs.mdx
@@ -108,17 +108,13 @@ This video provides a walkthrough of direct message (DM) stitching, covering the
-jhaaaa how to handle examples where we use ens names
-
-Consider a scenario where `alix.eth` using an existing installation #1 to create a conversation with `bo.eth` and sends them a DM. And then `alix.eth` creates a new installation #2, and instead of waiting for [history sync](https://docs.xmtp.org/inboxes/history-sync) to bring in their existing conversations, `alix.eth` creates a new conversation with `bo.eth` and sends them a DM. Under the hood, this results in two DM conversations (or two MLS groups) with the same pair of addresses, `alix.eth` and `bo.eth`, resulting in a confusing DM UX like this one:
+Consider a scenario where `alix.id` using an existing installation #1 to create a conversation with `bo.id` and sends them a DM. And then `alix.id` creates a new installation #2, and instead of waiting for [history sync](https://docs.xmtp.org/inboxes/history-sync) to bring in their existing conversations, `alix.id` creates a new conversation with `bo.id` and sends them a DM. Under the hood, this results in two DM conversations (or two MLS groups) with the same pair of identities, `alix.id` and `bo.id`, resulting in a confusing DM UX like this one:

-XMTP implements DM stitching to ensure that even if there are multiple DMs with the same pair of addresses under the hood, your users see only one DM conversation with messages displayed appropriately.
-
-jhaaaa
+XMTP implements DM stitching to ensure that even if there are multiple DMs with the same pair of identities under the hood, your users see only one DM conversation with messages displayed appropriately.
-For example, with DM stitching, instead of seeing two separate DM conversations between `alix.eth` and `bo.eth` with one message each, `alix.eth` sees one DM conversation between `alix.eth` and `bo.eth` with two messages in both installations #1 and #2
+For example, with DM stitching, instead of seeing two separate DM conversations between `alix.id` and `bo.id` with one message each, `alix.id` sees one DM conversation between `alix.id` and `bo.id` with two messages in both installations #1 and #2

@@ -132,16 +128,16 @@ For example, with DM stitching, instead of seeing two separate DM conversations
Any messages sent in any of these DM conversations will display in all of these DM conversations.
- For example, `alix.eth` sends a message in alix-bo-1. `bo.eth` is on alix-bo-3, but can still see messages sent in alix-bo-1.
+ For example, `alix.id` sends a message in alix-bo-1. `bo.id` is on alix-bo-3, but can still see messages sent in alix-bo-1.
2. When sending messages in a DM conversation, all installations in that DM will eventually converge to sending them to the same MLS group, even if they originally start off using different ones.
- - For example, `bo.eth` sends a message in alix-bo-3. `alix.eth` is on alix-bo-1, but can still see messages from alix-bo-3. When `alix.eth` sends a reply to `bo.eth`, it uses the most recently used DM conversation, alix-bo-3. In this way, all messaging will eventually move to alix-bo-3, and 1 and 2 will slowly fade away due to non-use.
+ - For example, `bo.id` sends a message in alix-bo-3. `alix.id` is on alix-bo-1, but can still see messages from alix-bo-3. When `alix.id` sends a reply to `bo.id`, it uses the most recently used DM conversation, alix-bo-3. In this way, all messaging will eventually move to alix-bo-3, and 1 and 2 will slowly fade away due to non-use.
### DM stitching considerations for push notifications
DM stitching provides a unified UX in the app. However, the multiple DM conversations under the hood must still be addressed for push notifications.
-Let’s take DM conversations alix-bo-1 and alix-bo-3 between `alix.eth` and `bo.eth`. With DM stitching, these two conversations display as one conversation. However, we must remember that they have two different conversation IDs, and thus two different topics.
+Let’s take DM conversations alix-bo-1 and alix-bo-3 between `alix.id` and `bo.id`. With DM stitching, these two conversations display as one conversation. However, we must remember that they have two different conversation IDs, and thus two different topics.
Therefore, when subscribing a DM conversations to push notifications, you should subscribe to a list of topics because every DM conversation can potentially have multiple topics. You will miss push notifications for messages if you are not listening to every potential topic for a DM conversation that messages could potentially be sent on.
diff --git a/docs/pages/inboxes/use-signatures.md b/docs/pages/inboxes/use-signatures.md
index cc654b7..baf93c9 100644
--- a/docs/pages/inboxes/use-signatures.md
+++ b/docs/pages/inboxes/use-signatures.md
@@ -4,11 +4,7 @@ With XMTP, you can use various types of signatures to sign and verify payloads.
## Sign with an external wallet
-jhaaaa
-
-When a user creates, adds, removes, or revokes an XMTP inbox’s address or installation, a signature from an external wallet is required.
-
-jhaaaa
+When a user creates, adds, removes, or revokes an XMTP inbox’s identity or installation, a signature is required.
## Sign with an XMTP key
diff --git a/docs/pages/inboxes/user-consent/support-user-consent.md b/docs/pages/inboxes/user-consent/support-user-consent.md
index dd9eb01..6f5ef72 100644
--- a/docs/pages/inboxes/user-consent/support-user-consent.md
+++ b/docs/pages/inboxes/user-consent/support-user-consent.md
@@ -331,9 +331,7 @@ You can filter these unknown contacts to:
jhaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
-To identify contacts the user might know or want to know, you can look for signals in onchain data that imply an affinity between addresses. You can then display appropriate messages on a **You might know** tab, for example.
-
-jhaaaa
+To identify contacts the user might know or want to know, you can look for signals in onchain data that imply an affinity between addresses. You can then display appropriate messages on a **You might know** tab, for example.
@@ -343,9 +341,7 @@ jhaaaa
jhaaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
-To identify contacts the user might not know or not want to know, which might include spam, you can consciously decide to scan messages in an unencrypted state to find messages that might contain spammy or scammy content. You can also look for an absence of onchain interaction data between the addresses, which might indicate that there is no affinity between addresses. You can then filter the appropriate messages to display on a **Hidden requests** tab, for example.
-
-jhaaaa
+To identify contacts the user might not know or not want to know, which might include spam, you can consciously decide to scan messages in an unencrypted state to find messages that might contain spammy or scammy content. You can also look for an absence of onchain interaction data between the addresses, which might indicate that there is no affinity between addresses. You can then filter the appropriate messages to display on a **Hidden requests** tab, for example.
@@ -363,9 +359,7 @@ XMTP is a decentralized, open protocol built to ensure private, secure, and cens
jhaaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
-The protocol can analyze onchain data signals, such as shared activity between wallet addresses, to infer potential affinities between addresses. However, because all XMTP repositories are open source, malicious actors could inspect these methods and develop workarounds to bypass them.
-
-jhaaaa
+The protocol can analyze onchain data signals, such as shared activity between wallet addresses, to infer potential affinities between addresses. However, because all XMTP repositories are open source, malicious actors could inspect these methods and develop workarounds to bypass them.
Additionally, applying spam filtering or content moderation directly at the protocol level would introduce centralization, which goes against the decentralized, permissionless, and open ethos of XMTP and web3. A protocol-driven approach could limit interoperability and trust by imposing subjective rules about content across all apps.
diff --git a/docs/pages/inboxes/user-consent/user-consent.mdx b/docs/pages/inboxes/user-consent/user-consent.mdx
index 020c5df..18a44a2 100644
--- a/docs/pages/inboxes/user-consent/user-consent.mdx
+++ b/docs/pages/inboxes/user-consent/user-consent.mdx
@@ -20,15 +20,13 @@ With user consent preferences, an identity registered on the XMTP network can ha
For example:
-jhaaaa how to handle examples that currently use ENS names?
+1. `alix.id` starts a conversation with `bo.id`. At this time, `alix.id` is unknown to `bo.id` and the conversation displays in a message requests UI.
-1. `alix.eth` starts a conversation with `bo.eth`. At this time, `alix.eth` is unknown to `bo.eth` and the conversation displays in a message requests UI.
+2. When `bo.id` views the message request, they express their user consent preference to **Block** or **Accept** `alix.id` as a contact.
-2. When `bo.eth` views the message request, they express their user consent preference to **Block** or **Accept** `alix.eth` as a contact.
+ - If `bo.id` accepts `alix.id` as a contact, their conversation displays in `bo.id`'s main inbox. Because only contacts `bo.id` accepts display in their main inbox, their inbox remains spam-free.
- - If `bo.eth` accepts `alix.eth` as a contact, their conversation displays in `bo.eth`'s main inbox. Because only contacts `bo.eth` accepts display in their main inbox, their inbox remains spam-free.
-
- - If `bo.eth` blocks contact with `alix.eth`, remove the conversation from `bo.eth`'s view. In an appropriate location in your app, give the user the option to unblock the contact.
+ - If `bo.id` blocks contact with `alix.id`, remove the conversation from `bo.id`'s view. In an appropriate location in your app, give the user the option to unblock the contact.
Your app should aim to handle consent preferences appropriately because they are an expression of user intent.
diff --git a/docs/pages/intro/faq.mdx b/docs/pages/intro/faq.mdx
index 4029142..df7db01 100644
--- a/docs/pages/intro/faq.mdx
+++ b/docs/pages/intro/faq.mdx
@@ -38,17 +38,13 @@ XMTP can be used with EVM-compatible wallet apps that support ECDSA signing on t
The XMTP SDK **does not** include a wallet app abstraction, as XMTP assumes that developers have a way to obtain a wallet app connection.
-### Chains
+### Chains
-jhaaaa
+XMTP can work with signatures from any private public key pair and currently supports EOAs and SCWs on Ethereum and Ethereum side-chains and L2s.
-XMTP can work with signatures from any private public key pair and currently supports EOAs and SCWs on Ethereum and Ethereum side-chains and L2s.
+Because all Ethereum Virtual Machine (EVM) chains share the same Ethereum wallet and address format and XMTP messages are stored off-chain, XMTP is interoperable across EVM chains, including testnets. (XMTP itself does not use EVMs.)
-Because all Ethereum Virtual Machine (EVM) chains share the same Ethereum wallet and address format and XMTP messages are stored off-chain, XMTP is interoperable across EVM chains, including testnets. (XMTP itself does not use EVMs.)
-
-For example, whether a user has their wallet app connected to Ethereum or an Ethereum side-chain or L2, their private key can generate and retrieve their XMTP key pair to give them access to XMTP.
-
-jhaaaa
+For example, whether a user has their wallet app connected to Ethereum or an Ethereum side-chain or L2, their private key can generate and retrieve their XMTP key pair to give them access to XMTP.
XMTP is also chain-agnostic, so multi-chain support is possible.
From 1dd14bc2ae425c3a6c204479743b0243ecbc46b9 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Fri, 7 Mar 2025 18:10:34 -0800
Subject: [PATCH 03/38] Update docs/pages/upgrade-to-v3.md
---
docs/pages/upgrade-to-v3.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index 51803ec..576066e 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -3,7 +3,7 @@
The process to upgrade an app built with XMTP V2 to V3 is designed to be straightforward, with most functions in V3 working as they did in V2. However, there are some notable differences, which we cover here.
:::info[Key takeaways]
-- **Primary XMTP identifier is now a flexible identity object, not an Ethereum address** as covered in this document.
+- **Primary XMTP identifier is now an inboxId instead of an Ethereum address**. As covered further in this document, this inbox can have a list of identities including Ethereum addresses as well as other types in the future, such as Passkeys and Bitcoin**.
- **Most core methods from V2 work in a similar way in V3**, with some notable differences that are covered in this document.
- **We recommend that apps upgrade directly to V3**, giving people access to a pure V3+ messaging experience with stronger encryption and laying the foundation for decentralization of the network. To learn more, see the [FAQ](/upgrade-to-v3#faq).
- ⛔️ **Rolling brownouts of the V2 network start on April 1, 2025. V2 will be deprecated on May 1, 2025**, after which all V2 conversations and messages will become read-only. To learn more, see [XIP 53: XIP V2 deprecation plan](https://community.xmtp.org/t/xip-53-xmtp-v2-deprecation-plan/867). Users will still be able to access their V2 communications in read-only format using [https://legacy.xmtp.chat/](https://legacy.xmtp.chat/).
From d0f97fb0598c10a2e392dbb9c2464efeef1846b4 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Fri, 7 Mar 2025 18:11:41 -0800
Subject: [PATCH 04/38] Update docs/pages/upgrade-to-v3.md
Co-authored-by: Naomi Plasterer
---
docs/pages/upgrade-to-v3.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index 576066e..3396bad 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -19,7 +19,7 @@ The process to upgrade an app built with XMTP V2 to V3 is designed to be straigh
## Primary XMTP identifier is now a flexible identity object, not an Ethereum address
-XMTP is evolving from using Ethereum account addresses (0x...) as the primary identifier to a more flexible identity model. This change allows for broader support of different authentication mechanisms, including the currently supported Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs), as well as future support for Passkeys.
+XMTP is evolving from using Ethereum account addresses (0x...) as the primary identifier to an inbox-based identity model. This change allows for broader support of different authentication mechanisms, including the currently supported Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs), as well as future support for Passkeys.
Instead of assuming an Ethereum address as the unique identifier, developers now define an identity object that explicitly includes the identity type (kind) and the identifier.
From 25dbab5bbac3bd5b65ad56f76b542e04ee9c1643 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Fri, 7 Mar 2025 18:12:34 -0800
Subject: [PATCH 05/38] Update docs/pages/upgrade-to-v3.md
Co-authored-by: Naomi Plasterer
---
docs/pages/upgrade-to-v3.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index 3396bad..c9294d1 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -21,7 +21,7 @@ The process to upgrade an app built with XMTP V2 to V3 is designed to be straigh
XMTP is evolving from using Ethereum account addresses (0x...) as the primary identifier to an inbox-based identity model. This change allows for broader support of different authentication mechanisms, including the currently supported Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs), as well as future support for Passkeys.
-Instead of assuming an Ethereum address as the unique identifier, developers now define an identity object that explicitly includes the identity type (kind) and the identifier.
+Instead of assuming an Ethereum address as the unique identifier, developers should default to using the `inboxId`, where possible. Inboxes will have a list of identity objects that explicitly includes the identity type (kind) and the identifier.
With this new model, apps reference identities or inbox IDs, rather than Ethereum addresses. Some identity types (like Passkeys) do not have an associated address, so identities and inbox IDs provide a consistent way to identify users across different authentication methods.
From 3b3f19c96479c8b534121d37bfdc059f9e4846f7 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Fri, 7 Mar 2025 18:14:11 -0800
Subject: [PATCH 06/38] Update docs/pages/upgrade-to-v3.md
Co-authored-by: Naomi Plasterer
---
docs/pages/upgrade-to-v3.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index c9294d1..39bc569 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -23,7 +23,7 @@ XMTP is evolving from using Ethereum account addresses (0x...) as the primary id
Instead of assuming an Ethereum address as the unique identifier, developers should default to using the `inboxId`, where possible. Inboxes will have a list of identity objects that explicitly includes the identity type (kind) and the identifier.
-With this new model, apps reference identities or inbox IDs, rather than Ethereum addresses. Some identity types (like Passkeys) do not have an associated address, so identities and inbox IDs provide a consistent way to identify users across different authentication methods.
+With this new model, apps reference users by inbox IDs, rather than Ethereum addresses. An `inboxId` will have a list of identities. Some identity types, like Passkeys, do not have an associated onchain address, so using the `inboxId` provides a consistent way to identify users across different authentication methods.
This change ensures that XMTP identities are more extensible and adaptable, accommodating future improvements in authentication methods while maintaining backward compatibility for Ethereum-based accounts.
From 54cc9b211b8304391d4d4a8ac37545ed8c41d6ac Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Fri, 7 Mar 2025 18:16:44 -0800
Subject: [PATCH 07/38] Update docs/pages/upgrade-to-v3.md
---
docs/pages/upgrade-to-v3.md | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index 39bc569..02400eb 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -25,6 +25,21 @@ Instead of assuming an Ethereum address as the unique identifier, developers sho
With this new model, apps reference users by inbox IDs, rather than Ethereum addresses. An `inboxId` will have a list of identities. Some identity types, like Passkeys, do not have an associated onchain address, so using the `inboxId` provides a consistent way to identify users across different authentication methods.
+For example:
+
+```json
+[
+ {
+ "kind": "ETHEREUM",
+ "identifier": "0x1234567890abcdef1234567890abcdef12345678",
+ "relyingPartner": null
+ },
+ {
+ "kind": "PASSKEY",
+ "identifier": "AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMk",
+ "relyingPartner": "NameOfAppUsedToCreatePasskey"
+ }
+]
This change ensures that XMTP identities are more extensible and adaptable, accommodating future improvements in authentication methods while maintaining backward compatibility for Ethereum-based accounts.
### Example: Supporting multiple identity types
From 6b46459f368bd840ac4e9dd336ea57b7961ce82e Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Fri, 7 Mar 2025 18:20:45 -0800
Subject: [PATCH 08/38] fix code fence
---
docs/pages/upgrade-to-v3.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index 02400eb..f5dc95c 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -40,6 +40,8 @@ For example:
"relyingPartner": "NameOfAppUsedToCreatePasskey"
}
]
+```
+
This change ensures that XMTP identities are more extensible and adaptable, accommodating future improvements in authentication methods while maintaining backward compatibility for Ethereum-based accounts.
### Example: Supporting multiple identity types
From 3e290c24630907335c0e00c68e520997e69082c6 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Sat, 8 Mar 2025 12:18:18 -0800
Subject: [PATCH 09/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 5acb08d..86ce3a3 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -471,7 +471,11 @@ const canMessage = await client.canMessage([
```kotlin [Kotlin]
// Request
-val canMessage = client.canMessage(listOf('0xboIdentity', '0xV2OnlyIdentity','0xBadIdentity'))
+val boIdentity = Identity(ETHEREUM, '0xboAddress')
+val v2Identity = Identity(ETHEREUM, '0xV2OnlyAddress')
+val badIdentity = Identity(ETHEREUM, '0xBadAddress')
+
+val canMessage = client.canMessage(listOf(boIdentity, v2Identity, badIdentity))
// Response
[
From 288df82a453c313c16de2ea395818684879742cf Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Sat, 8 Mar 2025 12:19:51 -0800
Subject: [PATCH 10/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 86ce3a3..e9fd95f 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -479,9 +479,9 @@ val canMessage = client.canMessage(listOf(boIdentity, v2Identity, badIdentity))
// Response
[
- "0xboIdentity": true,
- "0xV2OnlyIdentity": false,
- "0xBadIdentity": false,
+ boIdentity: true,
+ v2Identity: false,
+ badIdentity: false,
]
```
From e56f7aeae1076ebcc598d5b478d6e98f242ffab6 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Sat, 8 Mar 2025 12:20:37 -0800
Subject: [PATCH 11/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index e9fd95f..2353b61 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -487,7 +487,7 @@ val canMessage = client.canMessage(listOf(boIdentity, v2Identity, badIdentity))
```swift [Swift]
// Request
-let canMessage = try await client.canMessage(["0xboIdentity", "0xV2OnlyIdentity","0xBadIdentity"])
+let canMessage = try await client.canMessage([boIdentity, v2OnlyIdentity, badIdentity])
// Response
[
From e67e2254b9a2f78685fc47b7fcae72ce2b30de0a Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Sat, 8 Mar 2025 12:21:21 -0800
Subject: [PATCH 12/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 2353b61..4711ea3 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -513,7 +513,7 @@ Once you have the verified identities, create a new group chat:
```js [Browser]
const group = await client.conversations.newGroup(
- [bo.identity, caro.identity],
+ [bo.inboxId, caro.inboxId],
createGroupOptions /* optional */
);
```
From 238ac7df6768f86a1d23f8cf6000e08bdcbcc214 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Sat, 8 Mar 2025 13:58:02 -0800
Subject: [PATCH 13/38] Update docs/pages/inboxes/build-inbox.mdx
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 4711ea3..a17007d 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -520,7 +520,7 @@ const group = await client.conversations.newGroup(
```js [Node]
const group = await client.conversations.newGroup(
- [bo.identity, caro.identity],
+ [bo.inboxId, caro.inboxId],
createGroupOptions /* optional */
);
```
From dcabdb999616af37f3db815c308383d00999c04d Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Sat, 8 Mar 2025 14:04:31 -0800
Subject: [PATCH 14/38] updates
---
docs/pages/inboxes/build-inbox.mdx | 24 ++++++++-----------
.../user-consent/support-user-consent.md | 14 ++++++++---
2 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 4711ea3..768a412 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -30,8 +30,6 @@ Learn to build a chat inbox with the help of [XMTP.chat](https://xmtp.chat/), an
This code defines two functions that convert different types of Ethereum accounts—Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs)—into a unified `Signer` interface. This ensures that both account types conform to a common interface for message signing and deriving shared secrets as per MLS (Message Layer Security) requirements.
-jhaaaa these EOA and SCW code samples include "address" in the comments to call out when the kind is Ethereum and the identifier is an Ethereum address. Is this OK or have I misunderstood?
-
- For an EOA, the `convertEOAToSigner` function creates a signer that can get the account identity and sign messages and has placeholder methods for chain ID and block number.
:::code-group
@@ -520,17 +518,17 @@ const group = await client.conversations.newGroup(
```js [Node]
const group = await client.conversations.newGroup(
- [bo.identity, caro.identity],
+ [bo.inboxId, caro.inboxId],
createGroupOptions /* optional */
);
```
```tsx [React Native]
// New Group
-const group = await alix.conversations.newGroup([bo.identity, caro.identity]);
+const group = await alix.conversations.newGroup([bo.inboxId, caro.inboxId]);
// New Group with Metadata
-const group = await alix.conversations.newGroup([bo.identity, caro.identity], {
+const group = await alix.conversations.newGroup([bo.inboxId, caro.inboxId], {
name: "The Group Name",
imageUrl: "www.groupImage.com",
description: "The description of the group",
@@ -540,10 +538,10 @@ const group = await alix.conversations.newGroup([bo.identity, caro.identity], {
```kotlin [Kotlin]
// New Group
-val group = alix.conversations.newGroup(listOf(bo.identity, caro.identity))
+val group = alix.conversations.newGroup(listOf(bo.inboxId, caro.inboxId))
// New Group with Metadata
-val group = alix.conversations.newGroup(listOf(bo.identity, caro.identity),
+val group = alix.conversations.newGroup(listOf(bo.inboxId, caro.inboxId),
permissionLevel = GroupPermissionPreconfiguration.ALL_MEMBERS, // ALL_MEMBERS | ADMIN_ONLY
Name = "The Group Name",
ImageUrl = "www.groupImage.com",
@@ -553,10 +551,10 @@ val group = alix.conversations.newGroup(listOf(bo.identity, caro.identity),
```swift [Swift]
// New Group
-let group = try await alix.conversations.newGroup([bo.identity, caro.identity])
+let group = try await alix.conversations.newGroup([bo.inboxId, caro.inboxId])
// New Group with Metadata
-let group = try await alix.conversations.newGroup([bo.identity, caro.identity],
+let group = try await alix.conversations.newGroup([bo.inboxId, caro.inboxId],
permissionLevel: .admin_only, // .all_members | .admin_only
Name: "The Group Name",
ImageUrl: "www.groupImage.com",
@@ -906,8 +904,6 @@ for await message in try await alix.conversations.streamAllMessages(type: /* OPT
Use these helper methods to quickly locate and access specific conversations—whether by conversation ID, topic, group ID, or DM identity—returning the appropriate ConversationContainer, group, or DM object.
-jhaaaa ConvoId should be renamed to conversationId - jha: docs did not include any instances of convoId
-
:::code-group
```js [Browser]
@@ -943,7 +939,7 @@ await alix.conversations.findConversationByTopic(conversation.topic);
// Returns a Group
await alix.conversations.findGroup(group.id);
// Returns a DM
-await alix.conversations.findDmByIdentity(bo.identity);
+await alix.conversations.findDmByInboxId(bo.inboxId);
```
```kotlin [Kotlin]
@@ -953,7 +949,7 @@ alix.conversations.findConversationByTopic(conversation.topic)
// Returns a Group
alix.conversations.findGroup(group.id)
// Returns a DM
-alix.conversations.findDm(bo.identity);
+alix.conversations.findDm(bo.inboxId);
```
```swift [Swift]
@@ -963,7 +959,7 @@ try alix.conversations.findConversationByTopic(conversation.topic)
// Returns a Group
try alix.conversations.findGroup(group.id)
// Returns a DM
-try alix.conversations.findDm(bo.identity)
+try alix.conversations.findDm(bo.inboxId)
```
:::
diff --git a/docs/pages/inboxes/user-consent/support-user-consent.md b/docs/pages/inboxes/user-consent/support-user-consent.md
index 6f5ef72..646e30a 100644
--- a/docs/pages/inboxes/user-consent/support-user-consent.md
+++ b/docs/pages/inboxes/user-consent/support-user-consent.md
@@ -331,7 +331,17 @@ You can filter these unknown contacts to:
jhaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
-To identify contacts the user might know or want to know, you can look for signals in onchain data that imply an affinity between addresses. You can then display appropriate messages on a **You might know** tab, for example.
+To identify contacts the user might know or want to know, you can look for signals in onchain data that imply an affinity between addresses.
+
+```kotlin
+val inboxState = inboxStateForInboxId(inboxId)
+val identities = inboxState.identities
+val ethAddresses = identities
+ .filter { it.kind == ETHEREUM }
+ .map { it.identifier }
+```
+
+You can then display appropriate messages on a **You might know** tab, for example.
@@ -339,8 +349,6 @@ To identify contacts the user might know or want to know, you can look for signa
### Identify contacts the user might not know, including spammy or scammy requests
-jhaaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
-
To identify contacts the user might not know or not want to know, which might include spam, you can consciously decide to scan messages in an unencrypted state to find messages that might contain spammy or scammy content. You can also look for an absence of onchain interaction data between the addresses, which might indicate that there is no affinity between addresses. You can then filter the appropriate messages to display on a **Hidden requests** tab, for example.
From d12074de0aed72c104f8ef134007c64a65a0d840 Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Sat, 8 Mar 2025 14:16:43 -0800
Subject: [PATCH 15/38] updates
---
docs/pages/inboxes/group-permissions.mdx | 4 ++--
docs/pages/inboxes/manage-inboxes.mdx | 2 ++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/docs/pages/inboxes/group-permissions.mdx b/docs/pages/inboxes/group-permissions.mdx
index a7b82ce..ebf4d47 100644
--- a/docs/pages/inboxes/group-permissions.mdx
+++ b/docs/pages/inboxes/group-permissions.mdx
@@ -356,11 +356,11 @@ try await group.removeMemberInboxIds(inboxIds: [inboxId])
:::code-group
```js [Browser]
-const inboxId = await client.findInboxIdByIdentity(identity);
+const inboxId = await client.findInboxIdByIdentities([bo.identity, caro.identity]);
```
```js [Node]
-const inboxId = await client.getInboxIdByIdentity(identity);
+const inboxId = await client.getInboxIdByIdentities([bo.identity, caro.identity]);
```
```tsx [React Native]
diff --git a/docs/pages/inboxes/manage-inboxes.mdx b/docs/pages/inboxes/manage-inboxes.mdx
index d9d0015..d4d938f 100644
--- a/docs/pages/inboxes/manage-inboxes.mdx
+++ b/docs/pages/inboxes/manage-inboxes.mdx
@@ -161,6 +161,8 @@ let states = try await client.inboxStatesForInboxIds(
**Sample response**
+jhaaaa add note that passkeys not yet supported - just an example
+
```json
InboxState
{
From e1fbe9765c632dfc9514715162aa70e90a35469e Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Sat, 8 Mar 2025 22:53:11 -0800
Subject: [PATCH 16/38] update diagrams
---
docs/pages/inboxes/manage-inboxes.mdx | 52 +++++++++++++--------------
1 file changed, 25 insertions(+), 27 deletions(-)
diff --git a/docs/pages/inboxes/manage-inboxes.mdx b/docs/pages/inboxes/manage-inboxes.mdx
index d4d938f..264e289 100644
--- a/docs/pages/inboxes/manage-inboxes.mdx
+++ b/docs/pages/inboxes/manage-inboxes.mdx
@@ -1,29 +1,27 @@
# Manage XMTP inboxes
-jhaaa fix diagrams and references to addresses that use 0x values
+With XMTP, a user can have one or more inboxes they use to access their messages. An inbox can have multiple identities associated with it. An identity has a kind, such as EOA or SCW, and a string, which in the case of an EOA or SCW, is an Ethereum address. However, this extensible inbox-based model means that support for additional kinds of identities, such as Passkey or Bitcoin, can be added in the future.
-With XMTP, a user can have one or more inboxes they use to access their messages. An inbox can have multiple identities associated with it. All messages associated with these identities flow through the one inbox ID and are accessible in any XMTP app.
-
-For an overview of inbox IDs and their future in XMTP, see [Identity in XMTP](https://xmtp.org/identity).
+All messages associated with these identities flow through the one inbox ID and are accessible in any XMTP app.
The first time someone uses your app with an identity they've never used with any app built with XMTP, your app creates an inbox ID and installation ID associated with the identity. To do this, you [create a client](/inboxes/build-inbox#create-an-xmtp-client) for their identity.
The client creates an inbox ID and installation ID associated with the identity. By default, this identity is designated as the recovery identity. A recovery identity will always have the same inbox ID and cannot be reassigned to a different inbox ID.
-
+
When you make subsequent calls to create a client for the same identity and a local database is not present, the client uses the same inbox ID, but creates a new installation ID.
-
+
You can enable a user to add multiple identities to their inbox. Added identities use the same inbox ID and the installation ID of the installation used to add the identity.
-
+
You can enable a user to remove an identity from their inbox. You cannot remove the recovery identity.
@@ -92,7 +90,7 @@ For example, consider a user using this current installation:
-When the user revokes all other installations, the action removes all installations other than the current installation:
+When the user revokes all other installations, the action removes their identity's access to all installations other than the current installation:
@@ -188,16 +186,16 @@ InboxState
### What happens when a user removes an identity?
-Consider an inbox with four associated identities:
+Consider an inbox with three associated identities:
-
+
If the user removes an identity from the inbox, the identity no longer has access to the inbox it was removed from.
-
+
The identity can no longer be added to or used to access conversations in that inbox. If someone sends a message to the identity, the message is not associated with the original inbox. If the user logs in to a new installation with the identity, this will create a new inbox ID.
@@ -218,16 +216,16 @@ However, while Bo has access to Alix's inbox, Bo cannot remove Alix from their o
### If a user created two inboxes using two identities, is there a way to combine the inboxes?
-If a user logs in with address 0x62EE...309c and creates inbox 1 and then logs in with address 0xd0e4...DCe8 and creates inbox 2; there is no way to combine inbox 1 and 2.
+If a user logs in with an identity with address 0x62EE...309c and creates inbox 1 and then logs in with an identity with address 0xd0e4...DCe8 and creates inbox 2; there is no way to combine inbox 1 and 2.
-
+
-You can add address 0xd0e4...DCe8 to inbox 1, but both address 0x62EE...309c and 0xd0e4...DCe8 would then have access to inbox 1 only. Address 0xd0e4...DCe8 would no longer be able to access inbox 2.
+You can add an identity with address 0xd0e4...DCe8 to inbox 1, but both identities with addresses 0x62EE...309c and 0xd0e4...DCe8 would then have access to inbox 1 only. The identity with address 0xd0e4...DCe8 would no longer be able to access inbox 2.
-
+
To help users avoid this state, ensure that your UX surfaces their ability to add multiple identities to a single inbox.
@@ -246,34 +244,34 @@ However, anytime a new inbox ID is created for an identity, the conversations an
The identity accesses that inbox ID and does not create a new inbox ID.
-For example, let's say that you create a client with an address 0x62EE...309c. Inbox ID 1 is generated from that address 0x62EE...309c.
+For example, let's say that you create a client with an identity with address 0x62EE...309c. Inbox ID 1 is generated from that identity.
-
+
-If you then add a different address 0xd0e4...DCe8 to inbox ID 1, address 0xd0e4...DCe8 is also associated with that inbox ID 1.
+If you then add an identity with address 0xd0e4...DCe8 to inbox ID 1, the identity is also associated with inbox ID 1.
-If you then log into a new app installation with address 0xd0e4...DCe8, it accesses inbox ID 1 and does not create a new inbox ID.
+If you then log into a new app installation with the identity with address 0xd0e4...DCe8, it accesses inbox ID 1 and does not create a new inbox ID.
-Once address 0xd0e4...DCe8 has been associated with inbox ID 1, it can then be used to log into inbox ID 1 using a new app installation.
+Once the identity with address 0xd0e4...DCe8 has been associated with inbox ID 1, it can then be used to log into inbox ID 1 using a new app installation.
-
+
-The inverse is also true. Let's say address 0xd0e4...DCe8 was previously used to create and log into inbox ID 2.
+The inverse is also true. Let's say an identity with address 0xd0e4...DCe8 was previously used to create and log into inbox ID 2.
-
+
-If address 0xd0e4...DCe8 is then added as an associated address to inbox ID 1, address 0xd0e4...DCe8 will no longer be able to log into inbox ID 2.
+If the identity is then added as an associated identity to inbox ID 1, the identity will no longer be able to log into inbox ID 2.
-
+
-To enable the user of address 0xd0e4...DCe8 to log into inbox ID 2 again, you can use the recovery address for inbox ID 2 to add a different address to inbox ID 2 and have the user use that address access it.
+To enable the user of the identity with address 0xd0e4...DCe8 to log into inbox ID 2 again, you can use the recovery identity for inbox ID 2 to add a different identity to inbox ID 2 and have the user use that identity access it.
-If you are interested in providing this functionality in your app and want some guidance, post to [Ideas and Improvements](https://community.xmtp.org/c/development/ideas/54) in the XMTP Community Forums.
\ No newline at end of file
+If you are interested in providing this functionality in your app and want some guidance, post to the [XMTP Community Forums](https://community.xmtp.org).
From d69a0c041780f15c3c72ae259607e52a3cec2d4e Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Sun, 9 Mar 2025 00:04:11 -0800
Subject: [PATCH 17/38] updates
---
docs/pages/inboxes/build-inbox.mdx | 20 +++++++++----------
docs/pages/inboxes/manage-inboxes.mdx | 12 +++++++----
.../user-consent/support-user-consent.md | 4 ----
docs/pages/upgrade-to-v3.md | 17 +++-------------
4 files changed, 20 insertions(+), 33 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 768a412..3a9dec1 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -333,8 +333,6 @@ To learn more about database operations, see the [XMTP MLS protocol spec](https
You can configure an XMTP client with these parameters of `Client.create`:
-jhaaaa how about keystoreProviders, persistConversations, skipContactPublishing, codecs, maxContentSize, preCreateIdentityCallback, preEnableIdentityCallback, basePersistence, apiClientFactory
-
| Parameter | Default | Description |
| --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| env | `DEV` | Connect to the specified XMTP network environment. Valid values include `DEV`, `PRODUCTION`, or `LOCAL`. For important details about working with these environments, see [XMTP DEV, PRODUCTION, and LOCAL network environments](#xmtp-dev-production-and-local-network-environments). |
@@ -454,16 +452,16 @@ const response = await Client.canMessage([bo.identity, caro.identity]);
```tsx [React Native]
// Request
const canMessage = await client.canMessage([
- '0xboIdentity',
- '0xV2OnlyIdentity',
- '0xBadIdentity',
+ 'boIdentity',
+ 'v2OnlyIdentity',
+ 'badIdentity',
])
// Response
{
- "0xboIdentity": true,
- "0xV2OnlyIdentity": false,
- "0xBadIdentity": false,
+ "boIdentity": true,
+ "v2OnlyIdentity": false,
+ "badIdentity": false,
}
```
@@ -489,9 +487,9 @@ let canMessage = try await client.canMessage([boIdentity, v2OnlyIdentity, badIde
// Response
[
- "0xboIdentity": true,
- "0xV2OnlyIdentity": false,
- "0xBadIdentity": false,
+ "boIdentity": true,
+ "v2OnlyIdentity": false,
+ "badIdentity": false,
]
```
diff --git a/docs/pages/inboxes/manage-inboxes.mdx b/docs/pages/inboxes/manage-inboxes.mdx
index 264e289..a5a80f0 100644
--- a/docs/pages/inboxes/manage-inboxes.mdx
+++ b/docs/pages/inboxes/manage-inboxes.mdx
@@ -87,13 +87,13 @@ You can revoke all installations other than the currently accessed installation.
For example, consider a user using this current installation:
-
+
When the user revokes all other installations, the action removes their identity's access to all installations other than the current installation:
-
+
:::code-group
@@ -159,7 +159,11 @@ let states = try await client.inboxStatesForInboxIds(
**Sample response**
-jhaaaa add note that passkeys not yet supported - just an example
+:::tip
+
+Passkeys are not yet supported and are referenced here just to illustrate the flexibility of the inbox-based model.
+
+:::
```json
InboxState
@@ -201,7 +205,7 @@ If the user removes an identity from the inbox, the identity no longer has acces
The identity can no longer be added to or used to access conversations in that inbox. If someone sends a message to the identity, the message is not associated with the original inbox. If the user logs in to a new installation with the identity, this will create a new inbox ID.
-
+
### How is the recovery identity used?
diff --git a/docs/pages/inboxes/user-consent/support-user-consent.md b/docs/pages/inboxes/user-consent/support-user-consent.md
index 646e30a..23a8ac7 100644
--- a/docs/pages/inboxes/user-consent/support-user-consent.md
+++ b/docs/pages/inboxes/user-consent/support-user-consent.md
@@ -329,8 +329,6 @@ You can filter these unknown contacts to:
### Identify contacts the user might know
-jhaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
-
To identify contacts the user might know or want to know, you can look for signals in onchain data that imply an affinity between addresses.
```kotlin
@@ -365,8 +363,6 @@ The decision to scan unencrypted messages is yours as the app developer. If you
XMTP is a decentralized, open protocol built to ensure private, secure, and censorship-resistant communication. As such, XMTP can't read unencrypted messages, and therefore, it also can't scan or filter message contents for spammy or scammy material.
-jhaaaa for identities that don't have addresses -- what kind of onchain data signals can devs use?
-
The protocol can analyze onchain data signals, such as shared activity between wallet addresses, to infer potential affinities between addresses. However, because all XMTP repositories are open source, malicious actors could inspect these methods and develop workarounds to bypass them.
Additionally, applying spam filtering or content moderation directly at the protocol level would introduce centralization, which goes against the decentralized, permissionless, and open ethos of XMTP and web3. A protocol-driven approach could limit interoperability and trust by imposing subjective rules about content across all apps.
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index f5dc95c..fae2d15 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -50,7 +50,7 @@ With this new model, an app can now distinguish different identity types when cr
:::tip
-Passkeys are not yet supported and are referenced here just to illustrate the flexibility of the new identity model.
+Passkeys are not yet supported and are referenced here just to illustrate the flexibility of the new inbox-based model.
:::
@@ -175,21 +175,10 @@ In V3, we have installation-specific key bundles that are stored securely in the
## Managing consent
- In V2, we managed consent via `client.contacts.consentList`.
-- In V3, we can manage consent via `client.preferences.getAddressConsent(address)`. However, we recommend that you now manage consent at the conversation level by `conversationId`. To learn more, see [Support user consent preferences](/inboxes/user-consent/support-user-consent#support-user-consent-preferences-to-provide-spam-free-inboxes).
-
-<<<<<<<<>>>>>>>>
+- In V3, we can manage consent via `client.preferences.getInboxIdConsent(inboxId)`. However, we recommend that you now manage consent at the conversation level by `conversationId`. To learn more, see [Support user consent preferences](/inboxes/user-consent/support-user-consent#support-user-consent-preferences-to-provide-spam-free-inboxes).
## Summary of notable changes
-<<<<<<<<<<<>>>>>>>>>>>
-
| Purpose | V2 method | V3 equivalent |
| --- | --- | --- |
| Loading messages | `listBatchMessages` | `list()` |
@@ -198,7 +187,7 @@ maybe it's in this section. I can. I can do the code snippets of like the differ
| Create client | `client.createFromKeyBundle` | `client.build` |
| Sign a message | `client.privateKeyBundle.sign(message)` | `client.signWithInstallationKey(message)` |
| Verify a signature | `Signature.verify(signature)` | `client.verifyInstallationSignature(message, signature, installationId)` |
-| Manage consent | `client.contacts.consentList` | `client.preferences.getAddressConsent(address)` |
+| Manage consent | `client.contacts.consentList` | `client.preferences.getInboxIdConsent(inboxId)` |
## FAQ
From 24b65743b92601c168479f70a18547d1e75f9d03 Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Mon, 10 Mar 2025 12:02:08 -0700
Subject: [PATCH 18/38] updates
---
docs/pages/inboxes/manage-inboxes.mdx | 10 ++------
docs/pages/upgrade-to-v3.md | 33 +++++++++++----------------
2 files changed, 15 insertions(+), 28 deletions(-)
diff --git a/docs/pages/inboxes/manage-inboxes.mdx b/docs/pages/inboxes/manage-inboxes.mdx
index a5a80f0..f02581b 100644
--- a/docs/pages/inboxes/manage-inboxes.mdx
+++ b/docs/pages/inboxes/manage-inboxes.mdx
@@ -1,6 +1,6 @@
# Manage XMTP inboxes
-With XMTP, a user can have one or more inboxes they use to access their messages. An inbox can have multiple identities associated with it. An identity has a kind, such as EOA or SCW, and a string, which in the case of an EOA or SCW, is an Ethereum address. However, this extensible inbox-based model means that support for additional kinds of identities, such as Passkey or Bitcoin, can be added in the future.
+With XMTP, a user can have one or more inboxes they use to access their messages. An inbox can have multiple identities associated with it. An identity has a kind, such as EOA or SCW, and a string, which in the case of an EOA or SCW, is an Ethereum address. However, this extensible inbox-based identity model means that support for additional kinds of identities, such as Passkey or Bitcoin, can be added in the future.
All messages associated with these identities flow through the one inbox ID and are accessible in any XMTP app.
@@ -159,12 +159,6 @@ let states = try await client.inboxStatesForInboxIds(
**Sample response**
-:::tip
-
-Passkeys are not yet supported and are referenced here just to illustrate the flexibility of the inbox-based model.
-
-:::
-
```json
InboxState
{
@@ -176,7 +170,7 @@ InboxState
"relyingPartner": "string"
},
{
- "kind": "PASSKEY",
+ "kind": "PASSKEY", // not yet supported; provided as an example only.
"identifier": "string",
"relyingPartner": "string"
}
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index fae2d15..f5b37b7 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -3,9 +3,9 @@
The process to upgrade an app built with XMTP V2 to V3 is designed to be straightforward, with most functions in V3 working as they did in V2. However, there are some notable differences, which we cover here.
:::info[Key takeaways]
-- **Primary XMTP identifier is now an inboxId instead of an Ethereum address**. As covered further in this document, this inbox can have a list of identities including Ethereum addresses as well as other types in the future, such as Passkeys and Bitcoin**.
+- **Primary XMTP identifier is now an inbox ID, not an Ethereum address**. As covered in this document, this inbox can have a list of identities including Ethereum addresses as well as other types in the future, such as Passkeys and Bitcoin**.
- **Most core methods from V2 work in a similar way in V3**, with some notable differences that are covered in this document.
-- **We recommend that apps upgrade directly to V3**, giving people access to a pure V3+ messaging experience with stronger encryption and laying the foundation for decentralization of the network. To learn more, see the [FAQ](/upgrade-to-v3#faq).
+- **We recommend that apps upgrade directly to XMTP V3**, giving people access to a pure V3+ messaging experience with stronger encryption and laying the foundation for decentralization of the network. To learn more, see the [FAQ](/upgrade-to-v3#faq).
- ⛔️ **Rolling brownouts of the V2 network start on April 1, 2025. V2 will be deprecated on May 1, 2025**, after which all V2 conversations and messages will become read-only. To learn more, see [XIP 53: XIP V2 deprecation plan](https://community.xmtp.org/t/xip-53-xmtp-v2-deprecation-plan/867). Users will still be able to access their V2 communications in read-only format using [https://legacy.xmtp.chat/](https://legacy.xmtp.chat/).
:::
@@ -17,13 +17,11 @@ The process to upgrade an app built with XMTP V2 to V3 is designed to be straigh
- 🟢 **The agent upgrade path is ready**. For detailed guidance, [open an issue](https://github.com/xmtp/xmtp-js/issues) in the Node SDK GitHub repo.
-## Primary XMTP identifier is now a flexible identity object, not an Ethereum address
+## Primary XMTP identifier is now an inbox ID, not an Ethereum address
-XMTP is evolving from using Ethereum account addresses (0x...) as the primary identifier to an inbox-based identity model. This change allows for broader support of different authentication mechanisms, including the currently supported Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs), as well as future support for Passkeys.
+XMTP is evolving from using Ethereum account addresses (0x...) as the primary identifier to an inbox-based identity model. This change allows for broader support of different authentication mechanisms, including the currently supported [Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs)](/inboxes/build-inbox#create-an-account-signer), as well as future support for Passkeys and other identity types.
-Instead of assuming an Ethereum address as the unique identifier, developers should default to using the `inboxId`, where possible. Inboxes will have a list of identity objects that explicitly includes the identity type (kind) and the identifier.
-
-With this new model, apps reference users by inbox IDs, rather than Ethereum addresses. An `inboxId` will have a list of identities. Some identity types, like Passkeys, do not have an associated onchain address, so using the `inboxId` provides a consistent way to identify users across different authentication methods.
+Instead of assuming an Ethereum address as the unique identifier, developers should default to using the `inboxId`, where possible. An `inboxId` has a list of identity objects that explicitly includes the identity type (kind) and identifier. Some identity types, like Passkeys, do not have an associated onchain address, so using the `inboxId` provides a consistent way to identify users across different authentication methods.
For example:
@@ -35,7 +33,7 @@ For example:
"relyingPartner": null
},
{
- "kind": "PASSKEY",
+ "kind": "PASSKEY", // not yet supported; provided as an example only.
"identifier": "AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMk",
"relyingPartner": "NameOfAppUsedToCreatePasskey"
}
@@ -46,19 +44,13 @@ This change ensures that XMTP identities are more extensible and adaptable, acco
### Example: Supporting multiple identity types
-With this new model, an app can now distinguish different identity types when creating a signer
-
-:::tip
-
-Passkeys are not yet supported and are referenced here just to illustrate the flexibility of the new inbox-based model.
-
-:::
+With this new model, an app can now distinguish different identity types when creating a signer.
```tsx
function createSigner(account): Signer {
return {
getIdentity: async () => ({
- kind: account.isSCW ? "ETHEREUM" : "PASSKEY",
+ kind: account.isSCW ? "ETHEREUM" : "PASSKEY", // Passkeys are not yet supported; provided as an example only.
identifier: account.address || account.passkeyId,
}),
signMessage: async (message) => {
@@ -92,7 +84,7 @@ Now, `getIdentity()` returns an identity object, allowing for multiple types of
```tsx
const signer: Signer = {
getIdentity: () => ({
- kind: "ETHEREUM", // Identity type (ETHEREUM, PASSKEY, etc.)
+ kind: "ETHEREUM", // Identity type [ETHEREUM, PASSKEY (passkeys are not yet supported), etc.]
identifier: "0x123...", // Account identifier
}),
signMessage: async (message) => {
@@ -111,7 +103,7 @@ const dm = await alix.conversations.findOrCreateDm(bo.address);
### After: newConveration()
-Now, developers can use `inboxId` to create a new DM conversation because with the new flexible identity model, they cannot rely on the existence of an address.
+Now, developers can use `inboxId` to create a new DM conversation because with the new flexible identity model, they cannot rely on the existence of an Ethereum address.
```tsx
const dm = await alix.conversations.findOrCreateDm(bo.inboxId);
@@ -123,7 +115,7 @@ Most core methods from V2, such as `newConversation`, `list`, `stream`, and `str
However, key differences include:
-- `newConversation` no longer takes addresses, but rather inbox IDs, as covered in [Primary XMTP identifier is now a flexible identity object, not an Ethereum address](#primary-xmtp-identifier-is-now-a-flexible-identity-object-not-an-ethereum-address)
+- `newConversation` no longer takes addresses, but rather inbox IDs, as covered in [Primary XMTP identifier is now an inboxId, not an Ethereum address](#primary-xmtp-identifier-is-now-an-inbox-id-not-an-ethereum-address)
- Prior to V3, a conversation could represent a V1 or V2 conversation. In V3, a conversation can represent a group chat or direct message (DM).
@@ -181,6 +173,7 @@ In V3, we have installation-specific key bundles that are stored securely in the
| Purpose | V2 method | V3 equivalent |
| --- | --- | --- |
+| Create a new conversation | `findOrCreateDm(bo.address);` | `findOrCreateDm(bo.inboxId);` |
| Loading messages | `listBatchMessages` | `list()` |
| Push notification decryption | `fromInvite`, `fromIntro` | `fromWelcome` |
| Get topic IDs for push notifications | `/xmtp/0/invite-$address/proto` | `/xmtp/mls/1/w-$installationId/proto` |
@@ -221,7 +214,7 @@ V3 delivers an enhanced encryption scheme that is even more secure than V2, layi
We recommend that apps upgrade directly to V3, giving people access to a pure V3+ messaging experience.
-To ensure continuity, Ephemera plans to provide a website where people can sign in with their V2 identities to access their V2 conversations and messages in a read-only format.
+To ensure continuity, users will still be able to access their V2 communications in read-only format using [https://legacy.xmtp.chat/](https://legacy.xmtp.chat/).
### Can I use V2 conversations to seed the conversation list in my app built with V3?
From f41dff56d1b381a990f1634b21f84dc5e5fdbafd Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Mon, 10 Mar 2025 18:43:39 -0700
Subject: [PATCH 19/38] updates
---
docs/pages/inboxes/build-inbox.mdx | 274 ++++++++++----------
docs/pages/inboxes/disappearing-messages.md | 12 +-
docs/pages/inboxes/group-permissions.mdx | 12 +-
3 files changed, 149 insertions(+), 149 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 3a9dec1..0a18502 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -26,92 +26,92 @@ Learn to build a chat inbox with the help of [XMTP.chat](https://xmtp.chat/), an
## Create or build a client
-### Create an account signer
+### Create an account signer
-This code defines two functions that convert different types of Ethereum accounts—Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs)—into a unified `Signer` interface. This ensures that both account types conform to a common interface for message signing and deriving shared secrets as per MLS (Message Layer Security) requirements.
+This code defines two functions that convert different types of Ethereum accounts—Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs)—into a unified `Signer` interface. This ensures that both account types conform to a common interface for message signing and deriving shared secrets as per MLS (Message Layer Security) requirements.
-- For an EOA, the `convertEOAToSigner` function creates a signer that can get the account identity and sign messages and has placeholder methods for chain ID and block number.
+- For an EOA, the `convertEOAToSigner` function creates a signer that can get the account identity and sign messages and has placeholder methods for chain ID and block number.
- :::code-group
+ :::code-group
- ```tsx [Browser]
- import type { Signer } from "@xmtp/browser-sdk";
+ ```tsx [Browser]
+ import type { Signer } from "@xmtp/browser-sdk";
- const accountIdentity = {
- kind: "ETHEREUM", // Specifies the identity type
- identifier: "0x...", // Ethereum address as the identifier
- };
+ const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Ethereum address as the identifier
+ };
- const signer: Signer = {
- getIdentity: () => accountIdentity,
- signMessage: async (message) => {
- // return value from a signing method here
- },
- };
+ const signer: Signer = {
+ getIdentity: () => accountIdentity,
+ signMessage: async (message) => {
+ // return value from a signing method here
+ },
+ };
```
- ```tsx [Node]
- import type { Signer } from "@xmtp/node-sdk";
+ ```tsx [Node]
+ import type { Signer } from "@xmtp/node-sdk";
- const accountIdentity = {
- kind: "ETHEREUM", // Specifies the identity type
- identifier: "0x...", // Ethereum address as the identifier
- };
+ const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Ethereum address as the identifier
+ };
- const signer: Signer = {
- getIdentity: () => accountIdentity,
- signMessage: async (message) => {
- // return value from a signing method here
- },
- };
+ const signer: Signer = {
+ getIdentity: () => accountIdentity,
+ signMessage: async (message) => {
+ // return value from a signing method here
+ },
+ };
```
- ```tsx [React Native]
- // Example EOA Signer
- export function convertEOAToSigner(eoaAccount: EOAAccount): Signer {
- return {
- getIdentity: async () => ({
- kind: "ETHEREUM", // Specifies the identity type
- identifier: eoaAccount.address, // Ethereum address as the identifier
- }),
- signMessage: async (message: string | Uint8Array) =>
- eoaAccount.signMessage({
- message: typeof message === "string" ? message : { raw: message },
- }),
- getChainId: () => undefined,
- getBlockNumber: () => undefined,
- };
- }
+ ```tsx [React Native]
+ // Example EOA Signer
+ export function convertEOAToSigner(eoaAccount: EOAAccount): Signer {
+ return {
+ getIdentity: async () => ({
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: eoaAccount.address, // Ethereum address as the identifier
+ }),
+ signMessage: async (message: string | Uint8Array) =>
+ eoaAccount.signMessage({
+ message: typeof message === "string" ? message : { raw: message },
+ }),
+ getChainId: () => undefined,
+ getBlockNumber: () => undefined,
+ };
+ }
```
- ```kotlin [Kotlin]
- class EOAWallet(override val identity: Identity) : SigningKey {
- override val type: SignerType
- get() = SignerType.EOA
+ ```kotlin [Kotlin]
+ class EOAWallet(override val identity: Identity) : SigningKey {
+ override val type: SignerType
+ get() = SignerType.EOA
- override suspend fun sign(message: String): SignatureOuterClass.Signature {
+ override suspend fun sign(message: String): SignatureOuterClass.Signature {
return key.sign(message)
}
- override suspend fun sign(data: ByteArray): SignatureOuterClass.Signature {
+ override suspend fun sign(data: ByteArray): SignatureOuterClass.Signature {
return key.sign(data)
- }
+ }
}
```
- ```swift [Swift]
- public struct EOAWallet: SigningKey {
- public var identity: Identity
+ ```swift [Swift]
+ public struct EOAWallet: SigningKey {
+ public var identity: Identity
- public func sign(_ data: Data) async throws -> XMTPiOS.Signature {
- let signature = try await key.sign(data)
- return signature
+ public func sign(_ data: Data) async throws -> XMTPiOS.Signature {
+ let signature = try await key.sign(data)
+ return signature
}
- public func sign(message: String) async throws -> XMTPiOS.Signature {
- let signature = try await key.sign(message: message)
- return signature
- }
+ public func sign(message: String) async throws -> XMTPiOS.Signature {
+ let signature = try await key.sign(message: message)
+ return signature
+ }
}
```
@@ -119,111 +119,111 @@ This code defines two functions that convert different types of Ethereum account
- For an SCW, the `convertSCWToSigner` function similarly creates a signer but includes a specific implementation for chain ID and an optional block number computation.
- :::code-group
+ :::code-group
- ```tsx [Browser]
- import type { Signer } from "@xmtp/browser-sdk";
+ ```tsx [Browser]
+ import type { Signer } from "@xmtp/browser-sdk";
- const accountIdentity = {
- kind: "ETHEREUM", // Specifies the identity type
- identifier: "0x...", // Smart Contract Wallet address
- };
+ const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Smart Contract Wallet address
+ };
- const signer: Signer = {
- getIdentity: () => accountIdentity,
- signMessage: async (message) => {
- // return value from a signing method here
- },
- // These methods are required for smart contract wallets
- getBlockNumber: () => undefined, // Optional block number
- getChainId: () => BigInt(8453), // Example: Base chain ID
+ const signer: Signer = {
+ getIdentity: () => accountIdentity,
+ signMessage: async (message) => {
+ // return value from a signing method here
+ },
+ // These methods are required for smart contract wallets
+ getBlockNumber: () => undefined, // Optional block number
+ getChainId: () => BigInt(8453), // Example: Base chain ID
};
```
```tsx [Node]
import type { Signer } from "@xmtp/node-sdk";
- const accountIdentity = {
- kind: "ETHEREUM", // Specifies the identity type
- identifier: "0x...", // Smart Contract Wallet address
+ const accountIdentity = {
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: "0x...", // Smart Contract Wallet address
};
- const signer: Signer = {
- getIdentity: () => accountIdentity,
- signMessage: async (message) => {
- // return value from a signing method here
+ const signer: Signer = {
+ getIdentity: () => accountIdentity,
+ signMessage: async (message) => {
+ // return value from a signing method here
},
- // These methods are required for smart contract wallets
- getBlockNumber: () => undefined, // Optional block number
- getChainId: () => BigInt(8453), // Example: Base chain ID
- };
+ // These methods are required for smart contract wallets
+ getBlockNumber: () => undefined, // Optional block number
+ getChainId: () => BigInt(8453), // Example: Base chain ID
+ };
```
- ```tsx [React Native]
- // Example SCW Signer
- export function convertSCWToSigner(scwAccount: SCWAccount): Signer {
- return {
- getIdentity: async () => ({
- kind: "ETHEREUM", // Specifies the identity type
- identifier: scwAccount.address, // Smart Contract Wallet address
+ ```tsx [React Native]
+ // Example SCW Signer
+ export function convertSCWToSigner(scwAccount: SCWAccount): Signer {
+ return {
+ getIdentity: async () => ({
+ kind: "ETHEREUM", // Specifies the identity type
+ identifier: scwAccount.address, // Smart Contract Wallet address
}),
- signMessage: async (message: string) => {
- const byteArray = await scwAccount.signMessage(message);
- return ethers.utils.hexlify(byteArray); // Convert to hex string
+ signMessage: async (message: string) => {
+ const byteArray = await scwAccount.signMessage(message);
+ return ethers.utils.hexlify(byteArray); // Convert to hex string
},
- getChainId: async () => 8453, // https://chainlist.org/
- getBlockNumber: async () => undefined, // Optional: will be computed at runtime
- };
- }
+ getChainId: async () => 8453, // https://chainlist.org/
+ getBlockNumber: async () => undefined, // Optional: will be computed at runtime
+ };
+ }
```
- ```kotlin [Kotlin]
- class SCWallet(override val identity: Identity) : SigningKey {
- override val type: SignerType
- get() = SignerType.SCW
-
- override var chainId: Long? = 8453 // https://chainlist.org/
- override var blockNumber: Long? = null // Optional: will be computed at runtime
-
- override suspend fun signSCW(message: String): ByteArray {
- val digest = Signature.newBuilder().build().ethHash(message)
- val replaySafeHash = smartWallet.replaySafeHash(digest).send()
- val signature =
- Sign.signMessage(replaySafeHash, contractDeployerCredentials.ecKeyPair, false)
- val signatureBytes = signature.r + signature.s + signature.v
- val tokens = listOf(
- Uint(BigInteger.ZERO),
- DynamicBytes(signatureBytes)
+ ```kotlin [Kotlin]
+ class SCWallet(override val identity: Identity) : SigningKey {
+ override val type: SignerType
+ get() = SignerType.SCW
+
+ override var chainId: Long? = 8453 // https://chainlist.org/
+ override var blockNumber: Long? = null // Optional: will be computed at runtime
+
+ override suspend fun signSCW(message: String): ByteArray {
+ val digest = Signature.newBuilder().build().ethHash(message)
+ val replaySafeHash = smartWallet.replaySafeHash(digest).send()
+ val signature =
+ Sign.signMessage(replaySafeHash, contractDeployerCredentials.ecKeyPair, false)
+ val signatureBytes = signature.r + signature.s + signature.v
+ val tokens = listOf(
+ Uint(BigInteger.ZERO),
+ DynamicBytes(signatureBytes)
)
- val encoded = FunctionEncoder.encodeConstructor(tokens)
- val encodedBytes = Numeric.hexStringToByteArray(encoded)
+ val encoded = FunctionEncoder.encodeConstructor(tokens)
+ val encodedBytes = Numeric.hexStringToByteArray(encoded)
- return encodedBytes
- }
+ return encodedBytes
+ }
}
```
- ```swift [Swift]
- public struct SCWallet: SigningKey {
- public var identity: Identity
+ ```swift [Swift]
+ public struct SCWallet: SigningKey {
+ public var identity: Identity
- public var chainId: Int64? {
- 8453
- }
+ public var chainId: Int64? {
+ 8453
+ }
- public var blockNumber: Int64? {
- nil
- }
+ public var blockNumber: Int64? {
+ nil
+ }
- public var type: SignerType {
- .SCW
+ public var type: SignerType {
+ .SCW
}
- public func signSCW(message: String) async throws -> Data {
+ public func signSCW(message: String) async throws -> Data {
let signature = try await key.sign(message: message)
- return signature.hexStringToByteArray
- }
- }
+ return signature.hexStringToByteArray
+ }
+ }
```
:::
diff --git a/docs/pages/inboxes/disappearing-messages.md b/docs/pages/inboxes/disappearing-messages.md
index ceba844..77afee3 100644
--- a/docs/pages/inboxes/disappearing-messages.md
+++ b/docs/pages/inboxes/disappearing-messages.md
@@ -44,7 +44,7 @@ For example:
```tsx [React Native]
// DM
await client.conversations.newConversation(
- identity,
+ inboxId,
{
disappearingMessageSettings: DisappearingMessageSettings(
disappearStartingAtNs: 1738620126404999936,
@@ -55,7 +55,7 @@ await client.conversations.newConversation(
// Group
await client.conversations.newGroup(
- [identity],
+ [inboxId],
{
disappearingMessageSettings: DisappearingMessageSettings(
disappearStartingAtNs: 1738620126404999936,
@@ -68,7 +68,7 @@ await client.conversations.newGroup(
```kotlin [Kotlin]
// DM
client.conversations.newConversation(
- identity,
+ inboxId,
disappearingMessageSettings = DisappearingMessageSettings(
disappearStartingAtNs = 1738620126404999936,
retentionDurationInNs = 1800000000000000
@@ -77,7 +77,7 @@ client.conversations.newConversation(
// Group
client.conversations.newGroup(
- [identity],
+ [inboxId],
disappearingMessageSettings = DisappearingMessageSettings(
disappearStartingAtNs = 1738620126404999936,
retentionDurationInNs = 1800000000000000
@@ -88,7 +88,7 @@ client.conversations.newGroup(
```swift [Swift]
// DM
try await client.conversations.newConversation(
- with: identity,
+ with: inboxId,
disappearingMessageSettings: DisappearingMessageSettings(
disappearStartingAtNs: 1738620126404999936,
retentionDurationInNs: 1800000000000000
@@ -97,7 +97,7 @@ try await client.conversations.newConversation(
// Group
try await client.conversations.newGroup(
- with: [identity],
+ with: [inboxId],
disappearingMessageSettings: DisappearingMessageSettings(
disappearStartingAtNs: 1738620126404999936,
retentionDurationInNs: 1800000000000000
diff --git a/docs/pages/inboxes/group-permissions.mdx b/docs/pages/inboxes/group-permissions.mdx
index ebf4d47..37c90cb 100644
--- a/docs/pages/inboxes/group-permissions.mdx
+++ b/docs/pages/inboxes/group-permissions.mdx
@@ -325,28 +325,28 @@ try await group.addMembersByInboxId(inboxIds: [inboxId])
:::
-### Remove members by inbox ID
+### Remove member by inbox ID
:::code-group
```js [Browser]
-await group.removeMembersByInboxId([inboxId]);
+await group.removeMemberByInboxId([inboxId]);
```
```js [Node]
-await group.removeMembersByInboxId([inboxId]);
+await group.removeMemberByInboxId([inboxId]);
```
```tsx [React Native]
-await group.removeMemberInboxIds([inboxId]);
+await group.removeMemberByInboxIds([inboxId]);
```
```kotlin [Kotlin]
-group.removeMemberInboxIds(listOf(inboxId))
+group.removeMemberByInboxIds(listOf(inboxId))
```
```swift [Swift]
-try await group.removeMemberInboxIds(inboxIds: [inboxId])
+try await group.removeMemberByInboxIds(inboxIds: [inboxId])
```
:::
From ab2856e5c54cbb2be42266cf89b22af2d8903965 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 14:47:08 -0700
Subject: [PATCH 20/38] Update docs/pages/upgrade-to-v3.md
---
docs/pages/upgrade-to-v3.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index f5b37b7..088e446 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -30,7 +30,6 @@ For example:
{
"kind": "ETHEREUM",
"identifier": "0x1234567890abcdef1234567890abcdef12345678",
- "relyingPartner": null
},
{
"kind": "PASSKEY", // not yet supported; provided as an example only.
From 92b7c211fc001dc791c1ac5aac4827bc9af34ece Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 14:47:24 -0700
Subject: [PATCH 21/38] Update docs/pages/upgrade-to-v3.md
---
docs/pages/upgrade-to-v3.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index 088e446..38bf270 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -34,7 +34,6 @@ For example:
{
"kind": "PASSKEY", // not yet supported; provided as an example only.
"identifier": "AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMk",
- "relyingPartner": "NameOfAppUsedToCreatePasskey"
}
]
```
From 7f4be363d86ed1ca6bf586b25ce4adb87402c292 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 14:48:13 -0700
Subject: [PATCH 22/38] Update docs/pages/inboxes/group-metadata.md
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/group-metadata.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/group-metadata.md b/docs/pages/inboxes/group-metadata.md
index 2d98e48..faae633 100644
--- a/docs/pages/inboxes/group-metadata.md
+++ b/docs/pages/inboxes/group-metadata.md
@@ -127,7 +127,7 @@ group.imageURL
```
```swift [Swift]
-try group.groupImageUrl()
+try group.imageUrl()
```
:::
From d76918fa27b561e81c7b80e2a3b805214a4252b8 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 14:48:37 -0700
Subject: [PATCH 23/38] Update docs/pages/inboxes/group-permissions.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/group-permissions.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/group-permissions.mdx b/docs/pages/inboxes/group-permissions.mdx
index 37c90cb..13ac868 100644
--- a/docs/pages/inboxes/group-permissions.mdx
+++ b/docs/pages/inboxes/group-permissions.mdx
@@ -427,7 +427,7 @@ val identities = members.map { it.identities }
```
```swift [Swift]
-let peerMembers = try Conversation.group(group).peerIdentities.sorted()
+let peerMembers = try Conversation.group(group).peerInboxIds.sorted()
```
:::
From 47bd4cee36e4fc46c63cc7adc7421fdca2b3a1fc Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 14:49:09 -0700
Subject: [PATCH 24/38] Update docs/pages/inboxes/group-permissions.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/group-permissions.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/group-permissions.mdx b/docs/pages/inboxes/group-permissions.mdx
index 13ac868..2eecabc 100644
--- a/docs/pages/inboxes/group-permissions.mdx
+++ b/docs/pages/inboxes/group-permissions.mdx
@@ -334,7 +334,7 @@ await group.removeMemberByInboxId([inboxId]);
```
```js [Node]
-await group.removeMemberByInboxId([inboxId]);
+await group.removeMember([inboxId]);
```
```tsx [React Native]
From 7185bebc95e125472591bf9cf223a206044e6fc7 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 14:49:23 -0700
Subject: [PATCH 25/38] Update docs/pages/inboxes/group-permissions.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/group-permissions.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/group-permissions.mdx b/docs/pages/inboxes/group-permissions.mdx
index 2eecabc..5a491a0 100644
--- a/docs/pages/inboxes/group-permissions.mdx
+++ b/docs/pages/inboxes/group-permissions.mdx
@@ -330,7 +330,7 @@ try await group.addMembersByInboxId(inboxIds: [inboxId])
:::code-group
```js [Browser]
-await group.removeMemberByInboxId([inboxId]);
+await group.removeMember([inboxId]);
```
```js [Node]
From ce2e5ba9a6df1eccdebcae46315edb1c896bcc2d Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 14:58:58 -0700
Subject: [PATCH 26/38] Update docs/pages/inboxes/content-types/attachments.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/content-types/attachments.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/content-types/attachments.mdx b/docs/pages/inboxes/content-types/attachments.mdx
index 27c3e11..362c4dd 100644
--- a/docs/pages/inboxes/content-types/attachments.mdx
+++ b/docs/pages/inboxes/content-types/attachments.mdx
@@ -206,7 +206,7 @@ remoteAttachment.filename = attachment.filename
Send a remote attachment and set the `contentType`:
```kotlin [Kotlin]
-val newConversation = client.conversations.newConversation(accountIdentity)
+val newConversation = client.conversations.newConversation(inboxId)
newConversation.send(
content = remoteAttachment,
From 5292a4e5f477b85df5a5f37d34836f7fbbe86967 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 14:59:35 -0700
Subject: [PATCH 27/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 0a18502..97d5fac 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -452,9 +452,9 @@ const response = await Client.canMessage([bo.identity, caro.identity]);
```tsx [React Native]
// Request
const canMessage = await client.canMessage([
- 'boIdentity',
- 'v2OnlyIdentity',
- 'badIdentity',
+ boIdentity,
+ v2OnlyIdentity,
+ badIdentity,
])
// Response
From 0c65ce0754c758489628a7027670ed338873e561 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:00:17 -0700
Subject: [PATCH 28/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 97d5fac..81ecd43 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -459,9 +459,9 @@ const canMessage = await client.canMessage([
// Response
{
- "boIdentity": true,
- "v2OnlyIdentity": false,
- "badIdentity": false,
+ "0xboAddress": true,
+ "0xV2OnlyAddress": false,
+ "0xBadAddress": false,
}
```
From 983189241597b8170878d65d262422c578f617df Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:00:47 -0700
Subject: [PATCH 29/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 81ecd43..af7e25c 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -475,9 +475,9 @@ val canMessage = client.canMessage(listOf(boIdentity, v2Identity, badIdentity))
// Response
[
- boIdentity: true,
- v2Identity: false,
- badIdentity: false,
+ "0xboAddress": true,
+ "0xV2OnlyAddress": false,
+ "0xBadAddress": false,
]
```
From 344757f3fe7f0f5bd9f97de0fed526c8199d2cdd Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:01:11 -0700
Subject: [PATCH 30/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index af7e25c..7c8947d 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -487,9 +487,9 @@ let canMessage = try await client.canMessage([boIdentity, v2OnlyIdentity, badIde
// Response
[
- "boIdentity": true,
- "v2OnlyIdentity": false,
- "badIdentity": false,
+ "0xboAddress": true,
+ "0xV2OnlyAddress": false,
+ "0xBadAddress": false,
]
```
From 6f8e2a4652d88b52c6692d9d3dddbd01b00e8519 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:01:41 -0700
Subject: [PATCH 31/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 7c8947d..83f0c03 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -541,9 +541,9 @@ val group = alix.conversations.newGroup(listOf(bo.inboxId, caro.inboxId))
// New Group with Metadata
val group = alix.conversations.newGroup(listOf(bo.inboxId, caro.inboxId),
permissionLevel = GroupPermissionPreconfiguration.ALL_MEMBERS, // ALL_MEMBERS | ADMIN_ONLY
- Name = "The Group Name",
- ImageUrl = "www.groupImage.com",
- Description = "The description of the group",
+ name = "The Group Name",
+ imageUrl = "www.groupImage.com",
+ description = "The description of the group",
)
```
From 0c47e2219ed9c55e646b18fcd902768a8882b3e3 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:02:07 -0700
Subject: [PATCH 32/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 83f0c03..c803be2 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -554,9 +554,9 @@ let group = try await alix.conversations.newGroup([bo.inboxId, caro.inboxId])
// New Group with Metadata
let group = try await alix.conversations.newGroup([bo.inboxId, caro.inboxId],
permissionLevel: .admin_only, // .all_members | .admin_only
- Name: "The Group Name",
- ImageUrl: "www.groupImage.com",
- Description: "The description of the group",
+ name: "The Group Name",
+ imageUrl: "www.groupImage.com",
+ description: "The description of the group",
)
```
From a67296b123c8db23e1282761a8b6387d4408f976 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:03:47 -0700
Subject: [PATCH 33/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index c803be2..3e2f827 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -937,7 +937,7 @@ await alix.conversations.findConversationByTopic(conversation.topic);
// Returns a Group
await alix.conversations.findGroup(group.id);
// Returns a DM
-await alix.conversations.findDmByInboxId(bo.inboxId);
+await alix.conversations.findDmByIdentity(bo.identity);
```
```kotlin [Kotlin]
From e4ccdaf39aef67489a301ee17adbe7a01a2b0f43 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:04:42 -0700
Subject: [PATCH 34/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 3e2f827..92ba5c6 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -947,7 +947,7 @@ alix.conversations.findConversationByTopic(conversation.topic)
// Returns a Group
alix.conversations.findGroup(group.id)
// Returns a DM
-alix.conversations.findDm(bo.inboxId);
+alix.conversations.findDmbyInboxId(bo.inboxId);
```
```swift [Swift]
From 83d1fff7ecb2dddefd2e85935c494b0def8f783a Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:05:02 -0700
Subject: [PATCH 35/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 92ba5c6..9b3990a 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -957,7 +957,7 @@ try alix.conversations.findConversationByTopic(conversation.topic)
// Returns a Group
try alix.conversations.findGroup(group.id)
// Returns a DM
-try alix.conversations.findDm(bo.inboxId)
+try alix.conversations. findDmbyInboxId(bo.inboxId)
```
:::
From dba57b8602c86710e5095b069eb02e826fa9cb54 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:30:59 -0700
Subject: [PATCH 36/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 9b3990a..2dc53bb 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -185,7 +185,7 @@ This code defines two functions that convert different types of Ethereum account
override var chainId: Long? = 8453 // https://chainlist.org/
override var blockNumber: Long? = null // Optional: will be computed at runtime
- override suspend fun signSCW(message: String): ByteArray {
+ override suspend fun sign(message: String): ByteArray {
val digest = Signature.newBuilder().build().ethHash(message)
val replaySafeHash = smartWallet.replaySafeHash(digest).send()
val signature =
From 435e818850152a70f81bd246aa5e0e82b462d243 Mon Sep 17 00:00:00 2001
From: J-Ha <5481259+jhaaaa@users.noreply.github.com>
Date: Tue, 11 Mar 2025 15:31:25 -0700
Subject: [PATCH 37/38] Update docs/pages/inboxes/build-inbox.mdx
Co-authored-by: Naomi Plasterer
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 2dc53bb..1cbf30f 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -219,7 +219,7 @@ This code defines two functions that convert different types of Ethereum account
.SCW
}
- public func signSCW(message: String) async throws -> Data {
+ public func sign(message: String) async throws -> Data {
let signature = try await key.sign(message: message)
return signature.hexStringToByteArray
}
From eb95f4e040683b3d22dfb2b3c99a8c1871be4c87 Mon Sep 17 00:00:00 2001
From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com>
Date: Wed, 12 Mar 2025 09:08:39 -0700
Subject: [PATCH 38/38] signer updates
---
docs/pages/inboxes/build-inbox.mdx | 2 +-
docs/pages/upgrade-to-v3.md | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/pages/inboxes/build-inbox.mdx b/docs/pages/inboxes/build-inbox.mdx
index 1cbf30f..7efeefa 100644
--- a/docs/pages/inboxes/build-inbox.mdx
+++ b/docs/pages/inboxes/build-inbox.mdx
@@ -28,7 +28,7 @@ Learn to build a chat inbox with the help of [XMTP.chat](https://xmtp.chat/), an
### Create an account signer
-This code defines two functions that convert different types of Ethereum accounts—Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs)—into a unified `Signer` interface. This ensures that both account types conform to a common interface for message signing and deriving shared secrets as per MLS (Message Layer Security) requirements.
+This code defines two functions that convert different types of Ethereum accounts—Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs)—into a unified `Signer` interface. This ensures that both account types conform to a common interface for message signing and deriving shared secrets as per MLS (Message Layer Security) requirements. `SigningKey` now supports only one sign method: `sign(signatureText: String): ByteArray`.
- For an EOA, the `convertEOAToSigner` function creates a signer that can get the account identity and sign messages and has placeholder methods for chain ID and block number.
diff --git a/docs/pages/upgrade-to-v3.md b/docs/pages/upgrade-to-v3.md
index 38bf270..a86517c 100644
--- a/docs/pages/upgrade-to-v3.md
+++ b/docs/pages/upgrade-to-v3.md
@@ -6,7 +6,7 @@ The process to upgrade an app built with XMTP V2 to V3 is designed to be straigh
- **Primary XMTP identifier is now an inbox ID, not an Ethereum address**. As covered in this document, this inbox can have a list of identities including Ethereum addresses as well as other types in the future, such as Passkeys and Bitcoin**.
- **Most core methods from V2 work in a similar way in V3**, with some notable differences that are covered in this document.
- **We recommend that apps upgrade directly to XMTP V3**, giving people access to a pure V3+ messaging experience with stronger encryption and laying the foundation for decentralization of the network. To learn more, see the [FAQ](/upgrade-to-v3#faq).
-- ⛔️ **Rolling brownouts of the V2 network start on April 1, 2025. V2 will be deprecated on May 1, 2025**, after which all V2 conversations and messages will become read-only. To learn more, see [XIP 53: XIP V2 deprecation plan](https://community.xmtp.org/t/xip-53-xmtp-v2-deprecation-plan/867). Users will still be able to access their V2 communications in read-only format using [https://legacy.xmtp.chat/](https://legacy.xmtp.chat/).
+- ⛔️ **Rolling brownouts of the V2 network start on April 1, 2025. V2 will be deprecated on May 1, 2025**, after which all V2 conversations and messages will become read-only. To learn more, see [XIP-53: XIP V2 deprecation plan](https://community.xmtp.org/t/xip-53-xmtp-v2-deprecation-plan/867). Users will still be able to access their V2 communications in read-only format using [https://legacy.xmtp.chat/](https://legacy.xmtp.chat/).
:::
## Upgrade availability
@@ -15,7 +15,7 @@ The process to upgrade an app built with XMTP V2 to V3 is designed to be straigh
- 🟢 **The web app upgrade path is ready**. For detailed guidance, try [xmtp.chat](https://xmtp.chat/), an interactive developer tool and chat app built with XMTP V3.
-- 🟢 **The agent upgrade path is ready**. For detailed guidance, [open an issue](https://github.com/xmtp/xmtp-js/issues) in the Node SDK GitHub repo.
+- 🟢 **The agent upgrade path is ready**. For detailed guidance, explore [example agents](https://github.com/ephemeraHQ/xmtp-agent-examples) built with the XMTP Node SDK.
## Primary XMTP identifier is now an inbox ID, not an Ethereum address