@wasserstoff/tribes-sdk
Version:
SDK for integrating with Tribes by Astrix platform on any EVM compatible chain
806 lines (805 loc) • 34.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContentModule = void 0;
const ethers_1 = require("ethers");
const BaseModule_1 = require("../core/BaseModule");
const core_1 = require("../types/core");
const content_1 = require("../types/content");
const contracts_1 = require("../types/contracts");
const axios_1 = __importDefault(require("axios"));
// Import ABIs
const PostMinter_json_1 = __importDefault(require("../../abis/PostMinter.json"));
const PostFeedManager_json_1 = __importDefault(require("../../abis/PostFeedManager.json"));
/**
* Module for managing content (posts, comments, etc.)
*/
class ContentModule extends BaseModule_1.BaseModule {
/**
* Get the PostMinter contract
* @param useSigner Whether to use the signer
*/
getPostMinterContract(useSigner = false) {
return this.getContract(this.config.contracts.postMinter || '', PostMinter_json_1.default, useSigner);
}
/**
* Get the PostFeedManager contract
* @param useSigner Whether to use the signer
*/
getPostFeedManagerContract(useSigner = false) {
return this.getContract(this.config.contracts.postFeedManager || '', PostFeedManager_json_1.default, useSigner);
}
/**
* Create a new post
* @param params Post creation parameters
* @returns Post ID of the created post
*/
async createPost(params) {
try {
const postMinter = this.getPostMinterContract(true);
const tx = await postMinter.createPost(params.tribeId, params.metadata, params.isGated || false, params.collectibleContract || ethers_1.ethers.ZeroAddress, params.collectibleId || 0);
const receipt = await tx.wait();
if (!receipt) {
throw new Error('Transaction receipt was null');
}
// Find the PostCreated event in the receipt
const event = receipt.logs.find((log) => (0, contracts_1.isPostCreatedEvent)(log));
if (!event || !(0, contracts_1.isPostCreatedEvent)(event)) {
throw new Error('Post creation event not found');
}
// Use type assertion because we've verified it's the right type of event
const postId = Number(event.args[0]);
this.log(`Created post`, {
postId,
tribeId: params.tribeId,
txHash: receipt.hash
});
// Invalidate relevant caches
this.invalidateTribePostsCache(params.tribeId);
return postId;
}
catch (error) {
return this.handleError(error, 'Failed to create post', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Create multiple posts in a batch to save gas
* @param params Batch post creation parameters
* @returns Array of created post IDs
*/
async createBatchPosts(params) {
try {
const postMinter = this.getPostMinterContract(true);
// Map BatchPostData to contract-compatible format
const batchData = params.posts.map(post => ({
metadata: post.metadata,
isGated: post.isGated || false,
collectibleContract: post.collectibleContract || ethers_1.ethers.ZeroAddress,
collectibleId: post.collectibleId || 0,
postType: this.getPostTypeValue(post.postType || content_1.PostType.TEXT)
}));
const tx = await postMinter.createBatchPosts(params.tribeId, batchData);
const receipt = await tx.wait();
if (!receipt) {
throw new Error('Transaction receipt was null');
}
// Find the BatchPostsCreated event in the receipt
const event = receipt.logs.find((log) => (0, contracts_1.isBatchPostsCreatedEvent)(log));
if (!event || !(0, contracts_1.isBatchPostsCreatedEvent)(event)) {
throw new Error('Batch post creation event not found');
}
// Use type assertion because we've verified it's the right type of event
const postIds = event.args[2].map((id) => Number(id));
this.log(`Created batch posts`, {
tribeId: params.tribeId,
count: postIds.length,
txHash: receipt.hash
});
return postIds;
}
catch (error) {
return this.handleError(error, 'Failed to create batch posts', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Create an encrypted post
* @param params Encrypted post creation parameters
* @returns Post ID of the created encrypted post
*/
async createEncryptedPost(params) {
try {
const postMinter = this.getPostMinterContract(true);
const tx = await postMinter.createEncryptedPost(params.tribeId, params.metadata, params.encryptionKeyHash, params.accessSigner);
const receipt = await tx.wait();
if (!receipt) {
throw new Error('Transaction receipt was null');
}
// Find the EncryptedPostCreated event in the receipt
const event = receipt.logs.find((log) => (0, contracts_1.isEncryptedPostCreatedEvent)(log));
if (!event || !(0, contracts_1.isEncryptedPostCreatedEvent)(event)) {
throw new Error('Encrypted post creation event not found');
}
// Use type assertion because we've verified it's the right type of event
const postId = Number(event.args[0]);
this.log(`Created encrypted post`, {
postId,
tribeId: params.tribeId,
txHash: receipt.hash
});
return postId;
}
catch (error) {
return this.handleError(error, 'Failed to create encrypted post', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Create a signature-gated post that requires both collectible ownership and encryption
* @param params Signature gated post creation parameters
* @returns Post ID of the created signature-gated post
*/
async createSignatureGatedPost(params) {
try {
const postMinter = this.getPostMinterContract(true);
const tx = await postMinter.createSignatureGatedPost(params.tribeId, params.metadata, params.encryptionKeyHash, params.accessSigner, params.collectibleContract, params.collectibleId);
const receipt = await tx.wait();
if (!receipt) {
throw new Error('Transaction receipt was null');
}
// Find the SignatureGatedPostCreated event in the receipt
const event = receipt.logs.find((log) => (0, contracts_1.isSignatureGatedPostCreatedEvent)(log));
if (!event || !(0, contracts_1.isSignatureGatedPostCreatedEvent)(event)) {
throw new Error('Signature gated post creation event not found');
}
// Use type assertion because we've verified it's the right type of event
const postId = Number(event.args[0]);
this.log(`Created signature gated post`, {
postId,
tribeId: params.tribeId,
txHash: receipt.hash
});
return postId;
}
catch (error) {
return this.handleError(error, 'Failed to create signature gated post', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Delete a post (can only be done by the creator)
* @param postId ID of the post to delete
* @returns Transaction receipt
*/
async deletePost(postId) {
try {
const postMinter = this.getPostMinterContract(true);
const tx = await postMinter.deletePost(postId);
const receipt = await tx.wait();
// Invalidate post cache
this.invalidatePostCache(postId);
// Get the post to find the tribe ID
const post = await this.getPost(postId);
this.invalidateTribePostsCache(post.tribeId);
this.log(`Deleted post ${postId}`);
return receipt;
}
catch (error) {
return this.handleError(error, `Failed to delete post ${postId}`, core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Report a post for inappropriate content
* @param postId ID of the post to report
* @param reason Reason for reporting
* @returns Transaction receipt
*/
async reportPost(postId, reason) {
try {
const postMinter = this.getPostMinterContract(true);
const tx = await postMinter.reportPost(postId, reason);
const receipt = await tx.wait();
this.log(`Reported post`, {
postId,
reason,
txHash: receipt.hash
});
return receipt;
}
catch (error) {
return this.handleError(error, 'Failed to report post', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Interact with a post (like, share, etc.)
* @param postId ID of the post to interact with
* @param interactionType Type of interaction
* @returns Transaction receipt
*/
async interactWithPost(postId, interactionType) {
try {
const postMinter = this.getPostMinterContract(true);
// Convert from string enum to numeric enum for the contract
const interactionTypeValue = this.getInteractionTypeValue(interactionType);
const tx = await postMinter.interactWithPost(postId, interactionTypeValue);
const receipt = await tx.wait();
if (!receipt) {
throw new Error('Transaction receipt was null');
}
// Find the PostInteraction event in the receipt
const event = receipt.logs.find((log) => (0, contracts_1.isPostInteractionEvent)(log));
if (!event || !(0, contracts_1.isPostInteractionEvent)(event)) {
throw new Error('Post interaction event not found');
}
// Extract event data if needed
const eventPostId = Number(event.args[0]);
const eventUser = event.args[1];
const eventInteractionType = Number(event.args[2]);
this.log(`Interacted with post`, {
postId: eventPostId,
user: eventUser,
interactionType,
interactionTypeValue: eventInteractionType,
txHash: receipt.hash
});
// Invalidate post cache to refresh interaction counts
this.invalidatePostCache(postId);
return receipt;
}
catch (error) {
return this.handleError(error, 'Failed to interact with post', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Get the numeric value of a post type for the contract
* @param postType Post type enum
* @returns Numeric value for the contract
*/
getPostTypeValue(postType) {
const postTypeMap = {
[content_1.PostType.TEXT]: 0,
[content_1.PostType.RICH_MEDIA]: 1,
[content_1.PostType.EVENT]: 2,
[content_1.PostType.POLL]: 3,
[content_1.PostType.PROJECT_UPDATE]: 4,
[content_1.PostType.COMMUNITY_UPDATE]: 5,
[content_1.PostType.ENCRYPTED]: 6
};
return postTypeMap[postType] || 0;
}
/**
* Get the numeric value of an interaction type for the contract
* @param interactionType Interaction type enum
* @returns Numeric value for the contract
*/
getInteractionTypeValue(interactionType) {
const interactionTypeMap = {
[content_1.InteractionType.LIKE]: 0,
[content_1.InteractionType.COMMENT]: 1,
[content_1.InteractionType.SHARE]: 2,
[content_1.InteractionType.BOOKMARK]: 3,
[content_1.InteractionType.REPORT]: 4,
[content_1.InteractionType.REPLY]: 5,
[content_1.InteractionType.MENTION]: 6,
[content_1.InteractionType.REPOST]: 7,
[content_1.InteractionType.TIP]: 8
};
return interactionTypeMap[interactionType] || 0;
}
/**
* Authorize a viewer to access an encrypted post
* @param postId ID of the post
* @param viewer Address of the viewer to authorize
* @returns Transaction receipt
*/
async authorizeViewer(postId, viewer) {
try {
const postMinter = this.getPostMinterContract(true);
const tx = await postMinter.authorizeViewer(postId, viewer);
const receipt = await tx.wait();
this.log(`Authorized viewer for post`, {
postId,
viewer,
txHash: receipt.hash
});
return receipt;
}
catch (error) {
return this.handleError(error, 'Failed to authorize viewer', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Validate post metadata format and required fields
* @param params Validation parameters
* @returns True if metadata is valid
*/
async validatePostMetadata(params) {
try {
const postMinter = this.getPostMinterContract();
const postTypeValue = this.getPostTypeValue(params.postType);
return await postMinter.validateMetadata(params.metadata, postTypeValue);
}
catch (error) {
return this.handleError(error, 'Failed to validate post metadata', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Get a post by ID
* @param postId Post ID
* @returns Post details
*/
async getPost(postId) {
return this.getWithCache(`post:${postId}`, async () => {
try {
const postMinter = this.getPostMinterContract();
const post = await postMinter.getPost(postId);
return {
id: Number(post.id),
tribeId: Number(post.tribeId),
creator: post.creator,
metadata: post.metadata,
isGated: post.isGated,
collectibleContract: post.collectibleContract,
collectibleId: Number(post.collectibleId),
isEncrypted: post.isEncrypted,
accessSigner: post.accessSigner,
createdAt: Number(post.timestamp),
reportCount: Number(post.reportCount),
interactionCounts: {
likes: Number(post.interactionCounts[0]),
dislikes: Number(post.interactionCounts[1]),
shares: Number(post.interactionCounts[2]),
comments: Number(post.interactionCounts[3]),
saves: Number(post.interactionCounts[4])
}
};
}
catch (error) {
return this.handleError(error, `Failed to get post ${postId}`, core_1.ErrorType.CONTRACT_ERROR);
}
}, { blockBased: true } // Use block-based caching for contract state
);
}
/**
* Filter posts by post type
* @param postIds Array of post IDs
* @param postType Post type to filter by
* @returns Filtered post IDs and fetched post details if any were retrieved
*/
async filterPostsByType(postIds, postType, existingPosts) {
if (!postType || postIds.length === 0) {
return { filteredIds: postIds, filteredPosts: existingPosts };
}
// If we don't have post details yet, we need to fetch them
let posts = existingPosts;
if (!posts) {
posts = await this.getPostDetailsByIds(postIds);
}
// Filter posts by type
const filteredPosts = posts.filter(post => {
try {
const metadata = JSON.parse(post.metadata);
return metadata.type === postType;
}
catch (error) {
// If metadata can't be parsed or doesn't have a type, exclude it
return false;
}
});
// Return the filtered post IDs and posts
return {
filteredIds: filteredPosts.map(post => post.id),
filteredPosts
};
}
/**
* Get posts by tribe with pagination
* @param params Query parameters
* @returns Paginated posts
*/
async getPostsByTribe(params) {
const { tribeId, offset = 0, limit = 10, postType } = params;
return this.getWithCache(`posts:tribe:${tribeId}:${offset}:${limit}:${postType || 'all'}`, async () => {
try {
const postMinter = this.getPostMinterContract();
const [postIds, total] = await postMinter.getPostsByTribe(tribeId, offset, limit);
const mappedIds = postIds.map((id) => Number(id));
// Check if we need to filter by post type
if (postType !== undefined) {
const { filteredIds } = await this.filterPostsByType(mappedIds, postType);
// Get post details for the filtered IDs
const posts = await this.getPostDetailsByIds(filteredIds);
return {
postIds: filteredIds,
posts,
total: Number(total)
};
}
// No filtering needed, get all post details
const posts = await this.getPostDetailsByIds(mappedIds);
return {
postIds: mappedIds,
posts,
total: Number(total)
};
}
catch (error) {
return this.handleError(error, `Failed to get posts for tribe ${tribeId}`, core_1.ErrorType.CONTRACT_ERROR);
}
}, { blockBased: true } // Use block-based caching
);
}
/**
* Get posts by user with pagination
* @param params Query parameters
* @returns Paginated posts
*/
async getPostsByUser(params) {
try {
const postMinter = this.getPostMinterContract();
const result = await postMinter.getPostsByUser(params.user, params.offset || 0, params.limit || 10);
const postIds = result.postIds.map((id) => Number(id));
const total = Number(result.total);
// If requested, fetch full post details for each post ID
let posts;
if ((params.includeDetails || params.postType) && postIds.length > 0) {
posts = await this.getPostDetailsByIds(postIds);
}
// Apply post type filtering if needed
if (params.postType && posts) {
const { filteredIds, filteredPosts } = await this.filterPostsByType(postIds, params.postType, posts);
return {
postIds: filteredIds,
total: params.postType ? filteredIds.length : total, // Adjust total count when filtering
posts: params.includeDetails ? filteredPosts : undefined
};
}
return {
postIds,
total,
posts: params.includeDetails ? posts : undefined
};
}
catch (error) {
return this.handleError(error, 'Failed to get posts by user', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Get posts by tribe and user with pagination
* @param params Query parameters
* @returns Paginated posts
*/
async getPostsByTribeAndUser(params) {
try {
const postMinter = this.getPostMinterContract();
const result = await postMinter.getPostsByTribeAndUser(params.tribeId, params.user, params.offset || 0, params.limit || 10);
const postIds = result.postIds.map((id) => Number(id));
const total = Number(result.total);
// If requested, fetch full post details for each post ID
let posts;
if ((params.includeDetails || params.postType) && postIds.length > 0) {
posts = await this.getPostDetailsByIds(postIds);
}
// Apply post type filtering if needed
if (params.postType && posts) {
const { filteredIds, filteredPosts } = await this.filterPostsByType(postIds, params.postType, posts);
return {
postIds: filteredIds,
total: params.postType ? filteredIds.length : total, // Adjust total count when filtering
posts: params.includeDetails ? filteredPosts : undefined
};
}
return {
postIds,
total,
posts: params.includeDetails ? posts : undefined
};
}
catch (error) {
return this.handleError(error, 'Failed to get posts by tribe and user', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Get feed for user with pagination
* @param params Query parameters
* @returns Paginated posts
*/
async getFeedForUser(params) {
const { user, offset = 0, limit = 10, postType } = params;
return this.getWithCache(`feed:user:${user}:${offset}:${limit}:${postType || 'all'}`, async () => {
try {
const postMinter = this.getPostMinterContract();
const [postIds, total] = await postMinter.getFeedForUser(user, offset, limit);
const mappedIds = postIds.map((id) => Number(id));
// Check if we need to filter by post type
if (postType !== undefined) {
const { filteredIds } = await this.filterPostsByType(mappedIds, postType);
// Get post details for the filtered IDs
const posts = await this.getPostDetailsByIds(filteredIds);
return {
postIds: filteredIds,
posts,
total: Number(total)
};
}
// No filtering needed, get all post details
const posts = await this.getPostDetailsByIds(mappedIds);
return {
postIds: mappedIds,
posts,
total: Number(total)
};
}
catch (error) {
return this.handleError(error, `Failed to get feed for user ${user}`, core_1.ErrorType.CONTRACT_ERROR);
}
}, {
blockBased: true, // Update on new blocks
maxAge: 60000 // Cache for 1 minute maximum
});
}
/**
* Get full post details for multiple post IDs
* @param postIds Array of post IDs
* @returns Array of post details
*/
async getPostDetailsByIds(postIds) {
// If there are no post IDs, return an empty array
if (postIds.length === 0) {
return [];
}
// For small batches, use individual cached calls
if (postIds.length <= 3) {
const postsPromises = postIds.map(id => this.getPost(id));
return Promise.all(postsPromises);
}
// For larger batches, use a single cache entry for the batch
return this.getWithCache(`post:batch:${postIds.sort((a, b) => a - b).join(',')}`, async () => {
try {
const postMinter = this.getPostMinterContract();
const posts = await postMinter.getPostBatch(postIds);
return posts.map((post) => ({
id: Number(post.id),
tribeId: Number(post.tribeId),
creator: post.creator,
metadata: post.metadata,
isGated: post.isGated,
collectibleContract: post.collectibleContract,
collectibleId: Number(post.collectibleId),
isEncrypted: post.isEncrypted,
accessSigner: post.accessSigner,
createdAt: Number(post.timestamp),
reportCount: Number(post.reportCount),
interactionCounts: {
likes: Number(post.interactionCounts[0]),
dislikes: Number(post.interactionCounts[1]),
shares: Number(post.interactionCounts[2]),
comments: Number(post.interactionCounts[3]),
saves: Number(post.interactionCounts[4])
}
}));
}
catch (error) {
return this.handleError(error, 'Failed to get post batch', core_1.ErrorType.CONTRACT_ERROR);
}
}, { blockBased: true });
}
/**
* Refresh feed for user to get the latest posts
* @param params Query parameters
* @returns Paginated posts with latest content
*/
async refreshFeed(params) {
try {
// Clear any caching that might be happening in the SDK or client
this.log('Refreshing feed for user', { user: params.user });
// Just call getFeedForUser with the same parameters
return this.getFeedForUser(params);
}
catch (error) {
return this.handleError(error, 'Failed to refresh feed', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Get feed with parsed metadata for UI display
* @param params Query parameters
* @returns Feed with parsed metadata
*/
async getFeedWithParsedMetadata(params) {
try {
// Get feed with included details
const paramsWithDetails = {
...params,
includeDetails: true
};
const feed = await this.getFeedForUser(paramsWithDetails);
if (!feed.posts || feed.posts.length === 0) {
return [];
}
// Parse metadata for each post
return feed.posts.map(post => {
try {
const parsedMetadata = JSON.parse(post.metadata);
return {
...post,
parsedMetadata
};
}
catch (error) {
this.log('Error parsing post metadata', { postId: post.id, error });
return {
...post,
parsedMetadata: { error: 'Invalid metadata format' }
};
}
});
}
catch (error) {
return this.handleError(error, 'Failed to get feed with parsed metadata', core_1.ErrorType.CONTRACT_ERROR);
}
}
/**
* Get a post with parsed metadata
* @param post Post details
* @returns Post with parsed metadata
*/
async getParsedPostDetails(post) {
return this.getWithCache(`parsed:post:${post.id}`, async () => {
try {
// Fetch the metadata if it's a URL
let parsedMetadata;
if (!post.metadata) {
parsedMetadata = { title: "", content: "", media: [] };
}
else if (typeof post.metadata === 'string' && post.metadata.startsWith('http')) {
// Use axios directly for API calls
const response = await axios_1.default.get(post.metadata);
parsedMetadata = response.data;
}
else {
try {
parsedMetadata = JSON.parse(post.metadata);
}
catch (e) {
parsedMetadata = { title: "", content: post.metadata, media: [] };
}
}
const parsedPost = {
...post,
parsedMetadata
};
return parsedPost;
}
catch (error) {
return this.handleError(error, `Failed to parse post details for post ${post.id}`, core_1.ErrorType.API_ERROR);
}
}, {
blockBased: false, // Not dependent on blockchain state
maxAge: 300000 // Cache parsed metadata for 5 minutes
});
}
/**
* Invalidate cache for a specific post
*/
invalidatePostCache(postId) {
this.invalidateCache(`post:${postId}`);
this.invalidateCache(`parsed:post:${postId}`);
// Also invalidate any batch caches that might contain this post
const batchKeyPattern = `post:batch:`;
this.invalidateCacheByPattern(batchKeyPattern);
}
/**
* Invalidate cache for posts in a tribe
*/
invalidateTribePostsCache(tribeId) {
const keyPattern = `posts:tribe:${tribeId}:`;
this.invalidateCacheByPattern(keyPattern);
}
/**
* Invalidate user feed cache
*/
invalidateUserFeedCache(userAddress) {
const keyPattern = `feed:user:${userAddress}:`;
this.invalidateCacheByPattern(keyPattern);
}
/**
* Set up a listener for post interaction events
* @param callback Callback function to handle interaction events
* @param postId Optional specific post ID to filter by
* @returns Function to call to remove the listener
*/
setupPostInteractionListener(callback, postId) {
try {
const postMinter = this.getPostMinterContract();
// Set up the filter
const filter = postId
? postMinter.filters.PostInteraction(BigInt(postId))
: postMinter.filters.PostInteraction();
// Define the event handler
const handleEvent = (eventPostId, eventUser, eventInteractionType, _event) => {
// Convert numeric interaction type back to enum
const interactionTypeValue = Number(eventInteractionType);
const interactionType = this.getInteractionTypeFromValue(interactionTypeValue);
// Call the callback with the parsed data
callback(Number(eventPostId), eventUser, interactionType);
// Invalidate post cache to refresh interaction counts
this.invalidatePostCache(Number(eventPostId));
};
// Set up the listener
postMinter.on(filter, handleEvent);
this.log(`Set up post interaction listener`, {
filter: postId ? `Post ID: ${postId}` : 'All posts'
});
// Return cleanup function
// eslint-disable-next-line @typescript-eslint/no-empty-function
return () => {
// Listener removal logic
postMinter.off(filter, handleEvent);
this.log(`Removed post interaction listener`, {
filter: postId ? `Post ID: ${postId}` : 'All posts'
});
};
}
catch (error) {
this.log(`Error setting up post interaction listener: ${error instanceof Error ? error.message : String(error)}`);
// Return a no-op cleanup function
return () => { };
}
}
/**
* Convert numeric interaction type to enum
* @param value Numeric interaction type from contract
* @returns InteractionType enum value
*/
getInteractionTypeFromValue(value) {
const interactionTypes = [
content_1.InteractionType.LIKE,
content_1.InteractionType.COMMENT,
content_1.InteractionType.SHARE,
content_1.InteractionType.BOOKMARK,
content_1.InteractionType.REPORT,
content_1.InteractionType.REPLY,
content_1.InteractionType.MENTION,
content_1.InteractionType.REPOST,
content_1.InteractionType.TIP
];
return value >= 0 && value < interactionTypes.length
? interactionTypes[value]
: content_1.InteractionType.LIKE; // Default to LIKE if value is out of range
}
/**
* Create a test post - ONLY FOR TESTING PURPOSES
* This bypasses tribe membership checks by using tribeId=0
* @param metadata Post metadata JSON string
* @returns Post ID of the created post
*/
async createTestPost(metadata) {
try {
const postMinter = this.getPostMinterContract(true);
const tx = await postMinter.createPost(0, // Use tribeId 0 to bypass tribe membership checks
metadata, false, // not gated
ethers_1.ethers.ZeroAddress, // no collectible
0 // no collectible id
);
const receipt = await tx.wait();
if (!receipt) {
throw new Error('Transaction receipt was null');
}
// Find the PostCreated event in the receipt
const event = receipt.logs.find((log) => (0, contracts_1.isPostCreatedEvent)(log));
if (!event || !(0, contracts_1.isPostCreatedEvent)(event)) {
throw new Error('Post creation event not found');
}
// Use type assertion because we've verified it's the right type of event
const postId = Number(event.args[0]);
this.log(`Created test post`, {
postId,
txHash: receipt.hash
});
return postId;
}
catch (error) {
return this.handleError(error, 'Failed to create test post', core_1.ErrorType.CONTRACT_ERROR);
}
}
}
exports.ContentModule = ContentModule;