Skip to main content

Send token transaction hashes


Be aware that your custom content type may not be automatically recognized or supported by other applications, which could result in it being overlooked or only its fallback text being displayed.

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 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 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.

  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;
  1. Import and register the custom content type.
import {
} from "./xmtp-content-type-transaction-hash";

const xmtp = await Client.create(signer, {
env: "dev",
xmtp.registerCodec(new ContentTypeTransactionHashCodec());
  1. 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

// Set up provider for Polygon Testnet (Mumbai)
const provider = new ethers.providers.JsonRpcProvider(

// 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);
  1. 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={} encodedContent={message.content} />

const TransactionMonitor = ({ encodedContent }) => {
const [retryCount, setRetryCount] = useState(0);

const [transactionValue, setTransactionValue] = useState(null);

useEffect(() => {
const fetchTransactionReceipt = async () => {
const provider = new ethers.providers.JsonRpcProvider(
const receipt = await provider.getTransactionReceipt(encodedContent);
const tx = await provider.getTransaction(encodedContent);
if (tx && tx.value) {
}, [encodedContent, retryCount]);

return transactionValue ? (
<div>Transaction value: {transactionValue} ETH</div>
) : (
Waiting for transaction to be mined...
<button onClick={() => setRetryCount(retryCount + 1)}>
Refresh Status 🔄

Was the information on this page helpful?
powered by XMTP