Skip to main content

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.

  1. Create the custom content type by creating a new file, xmtp-content-type-multiply-number.tsx. This file hosts the MultiplyCodec class for encoding and decoding the custom content type.

    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;
    }
    }
  2. Import and register the custom content type.

    import {
    ContentTypeMultiplyNumber,
    ContentTypeMultiplyNumberCodec,
    } from "./xmtp-content-type-multiply-number";

    const xmtp = await Client.create(wallet, {
    env: "dev",
    });
    xmtp.registerCodec(new ContentTypeMultiplyNumberCodec());
  3. Send a message using the custom content type. This code sample demonstrates how to use the MultiplyCodec custom content type to perform multiplication operations.

    const numbersToMultiply = { a: 3, b: 7 };

    conversation.send(numbersToMultiply, {
    contentType: ContentTypeMultiplyNumbers,
    });
  4. To use the result of the multiplication operation, add a renderer for the custom content type.

    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.

  1. Create the custom content type by creating a new file, xmtp-content-type-transaction-hash.tsx. This file hosts the TransactionHash class for encoding and decoding the custom content type.

    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;
    }
    }
  2. Import and register the custom content type.

    import {
    ContentTypeTransactionHash,
    ContentTypeTransactionHashCodec,
    } from "./xmtp-content-type-transaction-hash";

    const xmtp = await Client.create(wallet, {
    env: "dev",
    });
    xmtp.registerCodec(new ContentTypeTransactionHashCodec());
  3. Send a message using the custom content type. This code sample demonstrates how to use the TransactionHash content type to send a transaction.

    // 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);
    });
  4. To use the result of the hash, add an async renderer for the custom content type.

    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>
    );
    };

Was the information on this page helpful?
powered by XMTP