Basic: Building a Custom Content Types
In this tutorials we are going to learn how to build a custom content type for the XMTP framework. We will start with a basic example of a custom content type that multiplies two numbers and then we will move on to a more advanced example of a custom content type that sends transaction hashes on the polygon blockchain. The advanced example will also show how to use the custom content type to render the transaction hash.
Basic: Multiplying Numbers
This tutorial will walk you through the process of building a custom content type dedicated to multiplying numbers. For demonstration purposes, we'll create a MultiplyCodec custom content type.
Step 1: Creating the MultiplyCodec Content Type
Let's start by creating a new file xmtp-content-type-multiply-number.tsx. This file will host the MultiplyCodec class which is responsible for encoding and decoding our 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;
}
}
Step 2: Using the Custom Content Type
Once the MultiplyCodec content type is defined, it's time to put it to use.
Import and Register Custom Content Type
Begin by importing and registering the MultiplyCodec content type.
- JavaScript
import {
ContentTypeMultiplyNumber,
ContentTypeMultiplyNumberCodec,
} from "./xmtp-content-type-multiply-number";
const xmtp = await Client.create(wallet, {
env: "dev",
});
xmtp.registerCodec(new ContentTypeMultiplyNumberCodec());
Sending a Message Using Custom Content Type
With your custom content type registered, you can now utilize it to send messages. This code snippet shows how to use the MultiplyCodec content type to perform multiplication operations.
- JavaScript
const numbersToMultiply = { a: 3, b: 7 };
conversation.send(numbersToMultiply, {
contentType: ContentTypeMultiplyNumbers,
});
Rendering the Custom Content Type
To make use of the result of the multiplication operation, you need to add a renderer for your custom content type.
- JavaScript
if (message.contentType.sameAs(ContentTypeMultiplyNumber)) {
return message.content; // 21
}
Keep in mind that any other application that intends to use your custom content type must implement it as per your definition.
If your MultiplyCodec content type generates interest within the developer community, consider proposing it as a standard through the XIP process.
To sum up, creating a custom content type opens up new avenues for handling data within the XMTP framework, giving you the power to manage data in a more personalized or specialized way.
Advanced: Sending token transaction hashes
This tutorial will walk you through the process of building a custom content type dedicated to send transaction hashes on the polygon blockchain.
Step 1: Creating the TransactionHash Content Type
Let's start by creating a new file xmtp-content-type-transaction-hash.tsx. This file will host the TransactionHash class which is responsible for encoding and decoding our 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;
}
}
Step 2: Using the Custom Content Type
Once the TransactionHash content type is defined, it's time to put it to use.
Import and Register Custom Content Type
Begin by importing and registering the TransactionHash content type.
- JavaScript
import {
ContentTypeTransactionHash,
ContentTypeTransactionHashCodec,
} from "./xmtp-content-type-transaction-hash";
const xmtp = await Client.create(wallet, {
env: "dev",
});
xmtp.registerCodec(new ContentTypeTransactionHashCodec());
Sending a Transaction Hash
With your custom content type registered, you can now utilize it to send hashes. This code snippet shows 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);
});
Rendering the Custom Content Type
To make use of the result of the hash, you need to add an async renderer for your 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>
);
};
Keep in mind that any other application that intends to use your custom content type must implement it as per your definition.
If your TransactionHash content type generates interest within the developer community, consider proposing it as a standard through the XIP process.
To sum up, creating a custom content type opens up new avenues for handling data within the XMTP framework, giving you the power to manage data in a more personalized or specialized way.