XMTP (Extensible Message Transport Protocol) is an open protocol and network for secure and private web3 messaging. For example, you can build an app with XMTP to send messages between blockchain accounts, including chat/DMs, alerts, announcements, and more.
This repository demonstrates the implementation of these concepts within a simple chat app.
git clone git@github.com:fabriguespe/xmtp-quickstart-reactjs.git
cd xmtp-quickstart-reactjs
npm install
npm run dev
- Setting up the ConnectWallet button
- Signing in with XMTP
- Loading a conversation
- Sending a message
The first step involves creating and configuring the Next.js application.
To generate a new Next.js app, execute the following command in your terminal:
npx create-react-app xmtp-quickstart-reactjs
Next, navigate into the newly created directory and install the necessary dependencies:
npm i @dynamic-labs/sdk-react-core @dynamic-labs/ethereum-all @xmtp/xmtp-js
//When using client-side libraries, additional polyfills are required.
npm i assert stream -D
//To ignore the sourcemap warnings, create a .env file with the following in your root directory:
GENERATE_SOURCEMAP=false
The Node Buffer API must be polyfilled in some cases. To do so, add the buffer dependency to your project and then polyfill it in your entry file.
- Create a
polyfills.js
file with the following code
import { Buffer } from "buffer";
window.Buffer = window.Buffer ?? Buffer;
- Insert it into your
index.js
on the first line
import "./polyfills";
First we need to initialize XMTP client using as signer our wallet connection of choice.
import "./App.css";
import Home from "./components/Home";
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum-all";
function App() {
return (
<DynamicContextProvider
settings={{
environmentId: "f0b977d0-b712-49f1-af89-2a24c47674da",
walletConnectors: [EthereumWalletConnectors],
}}
>
<Home />
</DynamicContextProvider>
);
}
export default App;
Now that we have the wrapper we can add a button that will sign our user in with XMTP.
{
isConnected && !isOnNetwork && (
<div className={styles.xmtp}>
<DynamicWidget />
<button onClick={initXmtp} className={styles.btnXmtp}>
Connect to XMTP
</button>
</div>
);
}
// Function to initialize the XMTP client
const initXmtp = async function () {
// Create the XMTP client
const xmtp = await Client.create(signer, { env: "production" });
//Create or load conversation with Gm bot
newConversation(xmtp, PEER_ADDRESS);
// Set the XMTP client in state for later use
setIsOnNetwork(!!xmtp.address);
//Set the client in the ref
clientRef.current = xmtp;
};
Now using our hooks we are going to use the state to listen whan XMTP is connected.
Later we are going to load our conversations and we are going to simulate starting a conversation with one of our bots
useEffect(() => {
if (isOnNetwork && convRef.current) {
// Function to stream new messages in the conversation
const streamMessages = async () => {
const newStream = await convRef.current.streamMessages();
for await (const msg of newStream) {
const exists = messages.find((m) => m.id === msg.id);
if (!exists) {
setMessages((prevMessages) => {
const msgsnew = [...prevMessages, msg];
return msgsnew;
});
}
}
};
streamMessages();
}
}, [messages, isOnNetwork]);
In your component initialize the hook to listen to conversations
const [history, setHistory] = useState(null);
const { messages } = useMessages(conversation);
// Stream messages
const onMessage = useCallback((message) => {
setHistory((prevMessages) => {
const msgsnew = [...prevMessages, message];
return msgsnew;
});
}, []);
useStreamMessages(conversation, onMessage);