Build custom content types for use with XMTP
Building a custom content type enables you to manage data in a way that is more personalized or specialized to the needs of your app.
For more common content types, you can usually find a standard or standards-track content type to serve your needs.
If another developer wants to display your custom content type in their app, they must implement it exactly as you've defined it in your code.
If your custom content type generates interest within the developer community, consider proposing it as a standard content type through the XIP process.
This tutorial covers how to build two example custom content types:
A basic example of a custom content type that multiplies two numbers
An advanced example of 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.
Basic: Multiply numbers using a custom content type
Build a custom content type to multiply numbers.
Create the custom content type by creating a new file,
xmtp-content-type-multiply-number.tsx
. This file hosts theMultiplyCodec
class for encoding and decoding the custom content type.- JavaScript
import { ContentTypeId } from "@xmtp/xmtp-js";
// Create a unique identifier for your content type
const ContentTypeMultiplyNumbers = new ContentTypeId({
authorityId: "your.domain",
typeId: "multiply-number",
versionMajor: 1,
versionMinor: 0,
});
// Define the MultiplyCodec class
class ContentTypeMultiplyNumberCodec {
get contentType() {
return ContentTypeMultiplyNumbers;
}
// The encode method accepts an object with two numbers (a, b) and encodes it as a byte array
encode({ a, b }) {
return {
type: ContentTypeMultiplyNumbers,
parameters: {},
content: new TextEncoder().encode(JSON.stringify({ a, b })),
};
}
// The decode method decodes the byte array, parses the string into numbers (a, b), and returns their product
decode(content: { content: any }) {
const uint8Array = content.content;
const { a, b } = JSON.parse(new TextDecoder().decode(uint8Array));
return a * b;
}
}Import and register the custom content type.
- JavaScript
import {
ContentTypeMultiplyNumber,
ContentTypeMultiplyNumberCodec,
} from "./xmtp-content-type-multiply-number";
const xmtp = await Client.create(wallet, {
env: "dev",
});
xmtp.registerCodec(new ContentTypeMultiplyNumberCodec());Send a message using the custom content type. This code sample demonstrates how to use the
MultiplyCodec
custom content type to perform multiplication operations.- JavaScript
const numbersToMultiply = { a: 3, b: 7 };
conversation.send(numbersToMultiply, {
contentType: ContentTypeMultiplyNumbers,
});To use the result of the multiplication operation, add a renderer for the custom content type.
- JavaScript
if (message.contentType.sameAs(ContentTypeMultiplyNumber)) {
return message.content; // 21
}
Advanced: Send token transaction hashes
Build a custom content type to send transaction hashes on the Polygon blockchain.
Create the custom content type by creating a new file,
xmtp-content-type-transaction-hash.tsx
. This file hosts theTransactionHash
class for encoding and decoding the custom content type.- JavaScript
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.
- JavaScript
import {
ContentTypeTransactionHash,
ContentTypeTransactionHashCodec,
} from "./xmtp-content-type-transaction-hash";
const xmtp = await Client.create(wallet, {
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.- JavaScript
// 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);
});To use the result of the hash, add an async renderer for the custom content type.
- JavaScript
if (message.contentType.sameAs(ContentTypeTransactionHash)) {
// Handle ContentTypeAttachment
return (
<TransactionMonitor key={message.id} encodedContent={message.content} />
);
}
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 ? (
<div>Transaction value: {transactionValue} ETH</div>
) : (
<div>
Waiting for transaction to be mined...
<button onClick={() => setRetryCount(retryCount + 1)}>
Refresh Status 🔄
</button>
</div>
);
};