Интеграция с XMTP (Web3-мессенджер)
Обычный чат в dApp — это Firebase Realtime Database или Websocket с JWT авторизацией. Проблема: централизованное хранение сообщений, зависимость от вашего сервера, нет шифрования на уровне протокола. XMTP (Extensible Message Transport Protocol) — это децентрализованный мессенджинг, где сообщения шифруются end-to-end ключами кошелька и хранятся в децентрализованной сети нод. Ethereum адрес = идентификатор пользователя, приватный ключ = ключ шифрования.
Как работает XMTP
При первом использовании пользователь создаёт XMTP identity: подписывает сообщение своим кошельком, из подписи генерируется детерминированный identity key. Этот ключ регистрируется в XMTP сети. Сообщения шифруются публичным ключом получателя через X3DH (Extended Triple Diffie-Hellman) — тот же протокол, что используется в Signal.
Сообщения хранятся в XMTP нодах, а не на вашем сервере. Пользователь может открыть вашу интеграцию, Coinbase Wallet, Converse или любой XMTP-совместимый клиент — и увидеть все свои сообщения.
Установка и базовая интеграция
npm install @xmtp/browser-sdk
import { Client } from "@xmtp/browser-sdk"
// Инициализация клиента
async function initXMTP(signer: WalletSigner) {
const client = await Client.create(signer, { env: "production" })
return client
}
// Проверка, зарегистрирован ли адрес в XMTP
async function canMessage(client: Client, address: string): Promise<boolean> {
return await client.canMessage(address)
}
// Создание или открытие разговора
async function getOrCreateConversation(client: Client, recipientAddress: string) {
const conversation = await client.conversations.newConversation(recipientAddress)
return conversation
}
Нюанс: Client.create требует подписи сообщения кошельком пользователя. Первый раз — две подписи (создание identity), повторные запуски — одна. Это нужно правильно коммуницировать в UI, иначе пользователи путаются.
Интеграция с wagmi/viem
XMTP ожидает Signer объект с методом signMessage. Адаптер для wagmi:
import { useWalletClient } from "wagmi"
import { Client } from "@xmtp/browser-sdk"
function useXMTPClient() {
const { data: walletClient } = useWalletClient()
const [xmtp, setXmtp] = useState<Client | null>(null)
const connect = async () => {
if (!walletClient) return
// Адаптер: wagmi WalletClient -> XMTP Signer
const signer = {
getAddress: () => walletClient.account.address,
signMessage: (message: string) =>
walletClient.signMessage({ message })
}
const client = await Client.create(signer, {
env: "production",
// Персистентное хранение ключей в localStorage
persistConversations: true
})
setXmtp(client)
}
return { xmtp, connect }
}
Список разговоров и сообщений
function ConversationList({ client }: { client: Client }) {
const [conversations, setConversations] = useState<Conversation[]>([])
useEffect(() => {
const loadConversations = async () => {
const convos = await client.conversations.list()
setConversations(convos)
}
loadConversations()
// Слушаем новые разговоры
const stream = client.conversations.stream()
const subscription = stream.then(s => {
;(async () => {
for await (const convo of s) {
setConversations(prev => [convo, ...prev])
}
})()
})
return () => { subscription.then(s => s.return?.()) }
}, [client])
return (
<ul>
{conversations.map(convo => (
<ConversationItem key={convo.peerAddress} conversation={convo} />
))}
</ul>
)
}
function MessageThread({ conversation }: { conversation: Conversation }) {
const [messages, setMessages] = useState<DecodedMessage[]>([])
useEffect(() => {
const loadMessages = async () => {
const msgs = await conversation.messages({ limit: 50 })
setMessages(msgs)
}
loadMessages()
// Realtime streaming новых сообщений
const startStream = async () => {
for await (const message of await conversation.streamMessages()) {
setMessages(prev => [...prev, message])
}
}
startStream()
}, [conversation])
const sendMessage = async (text: string) => {
await conversation.send(text)
}
return (
<div>
{messages.map(msg => (
<MessageBubble
key={msg.id}
text={msg.content}
isMine={msg.senderAddress === conversation.client.address}
timestamp={msg.sent}
/>
))}
</div>
)
}
Content Types: расширенный контент
XMTP поддерживает не только текст. Content Types — это стандартизированный механизм для произвольных типов сообщений:
import { ContentTypeAttachment, AttachmentCodec } from "@xmtp/content-type-attachments"
import { ContentTypeReaction, ReactionCodec } from "@xmtp/content-type-reaction"
// Инициализация с кастомными content types
const client = await Client.create(signer, {
env: "production",
codecs: [new AttachmentCodec(), new ReactionCodec()]
})
// Отправка файла (до 1MB, больше — через remote attachments)
await conversation.send({
filename: "contract.pdf",
mimeType: "application/pdf",
data: pdfBytes
}, { contentType: ContentTypeAttachment })
// Реакция на сообщение
await conversation.send({
reference: messageId,
action: "added",
content: "👍",
schema: "unicode"
}, { contentType: ContentTypeReaction })
Существующие стандартные content types: text/plain, attachment, remote-attachment (файлы в IPFS/S3), reaction, reply (ответы на сообщения), read-receipt. Для кастомных нужд — регистрируем свой namespace.
Frames и боты
XMTP поддерживает Frames (аналог Farcaster Frames) — интерактивные карточки в сообщениях с кнопками. Бот на XMTP — это просто Node.js процесс с XMTP клиентом, который слушает входящие сообщения и отвечает:
const botClient = await Client.create(botSigner, { env: "production" })
for await (const message of await botClient.conversations.streamAllMessages()) {
if (message.senderAddress === botClient.address) continue // не отвечаем сами себе
const response = await generateBotResponse(message.content)
await message.conversation.send(response)
}
Используется для transaction notifications, price alerts, customer support.
Сроки разработки
День 1-2: XMTP SDK интеграция, адаптер для кошелька, базовый UI разговоров и сообщений.
День 3: Realtime streaming, отправка сообщений, поиск по адресу.
День 4-5: Расширенные content types, уведомления, мобильная адаптация.
Базовый мессенджер с текстовыми сообщениями и realtime обновлениями — 2-3 дня. Полнофункциональный чат с вложениями, реакциями и XMTP ботом — 4-5 дней.







