Skip to Content
Smart ContractsOverview

Smart Contracts

ChainSocial is built on MUD, a framework for on-chain applications. This section explains the architecture for developers who want to extend the protocol or integrate at the contract level.

Why MUD?

MUD provides:

  • Structured Storage — Tables with schemas, not arbitrary mappings
  • Modular Systems — Logic separated from data
  • Upgradability — Add new systems without migrating data
  • Access Control — Fine-grained permissions per table/system

If you just want to build apps, use the SDK or API. Contract-level knowledge is only needed for extending the protocol.

Architecture Overview

┌─────────────────────────────────────────────────────────────────┐ │ World Contract │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Store (Tables) │ │ │ │ Profile, Post, Content, Follow, Reaction, Block, │ │ │ │ DirectMessage, Delegation, Notification, Plugin... │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Systems (Logic) │ │ │ │ ProfileSystem, PostCore, PostInlineSystem, │ │ │ │ PostBatchSystem, PostFeeSystem, FollowSystem, │ │ │ │ BlockSystem, ContentSystem, ReactionSystem, │ │ │ │ DelegationSystem, DirectMessageSystem... │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Access Control │ │ │ │ Namespace permissions, system-level access │ │ │ └──────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘

Key Concepts

World — The single entry point. All calls go through the World, which routes to the appropriate system.

Systems — Stateless contracts with business logic. They read/write to tables and emit events.

Tables — On-chain storage with defined schemas. Tables belong to namespaces.

Namespace — A permission boundary. ChainSocial uses the chainsocial namespace.

Calling Systems

All ChainSocial systems are namespaced. Function calls look like:

IWorld(worldAddress).chainsocial__createPost(contentId, visibility);

The double underscore (__) separates namespace from function name.

From TypeScript with viem:

import { encodeFunctionData } from 'viem' import { worldAbi } from '@chainsocial/client' const data = encodeFunctionData({ abi: worldAbi, functionName: 'chainsocial__createPost', args: [contentId, visibility] }) await walletClient.sendTransaction({ to: worldAddress, data })

System Categories

Tables Reference

Key tables in the chainsocial namespace:

TablePurpose
ProfileUser profiles with username, timestamps, status
ProfileFieldKey-value profile metadata
PostPosts with content, visibility, timestamps
PostStatsLike counts, reply counts
ContentContent storage (inline, IPFS, URL)
FollowFollow relationships
BlockBlock relationships
ReactionPost reactions (likes)
DirectMessageDM content and metadata
ConversationConversation summaries
DelegationPermission grants
NotificationUser notifications
PluginPlugin registry
PluginInstallUser plugin installations

Extending the Protocol

MUD’s architecture means you can add new functionality without modifying existing contracts.

Adding a New System

  1. Create a new Solidity contract inheriting from System
  2. Deploy it to the World
  3. Grant it access to required tables
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { System } from "@latticexyz/world/src/System.sol"; import { Post, PostData } from "../codegen/index.sol"; contract TippingSystem is System { function tipPost(bytes32 postId) external payable { PostData memory post = Post.get(postId); require(post.author != address(0), "Post not found"); // Send tip to author (bool success,) = post.author.call{value: msg.value}(""); require(success, "Transfer failed"); } }

Adding a New Table

  1. Define the table schema in mud.config.ts
  2. Run code generation
  3. Create systems that read/write to the table
// mud.config.ts export default mudConfig({ tables: { Tip: { keySchema: { postId: "bytes32", tipper: "address", }, valueSchema: { amount: "uint256", timestamp: "uint256", }, }, }, });

Cross-System Calls

Systems can call each other through the World interface to preserve msg.sender:

import { IWorld } from "../codegen/world/IWorld.sol"; contract MySystem is System { function createPostAndFollow(bytes32 contentId, address userToFollow) external { // Create post IWorld(_world()).chainsocial__createPost(contentId, 0); // Follow user (preserves msg.sender) IWorld(_world()).chainsocial__follow(userToFollow); } }

Events and Indexing

ChainSocial uses MUD’s Store events for indexing. State changes are captured through MUD’s table write events rather than custom per-action events.

MUD automatically emits events when table records are created, updated, or deleted. The indexer listens for these Store events to build the queryable database.

Some systems emit custom events for specific operations:

  • ContentSystem emits ContentCreated
  • DirectMessageSystem emits MessageSent
  • DelegationSystem emits DelegationRegistered
  • VersionSystem emits ProtocolVersionUpdated

Most other state changes (profiles, posts, follows, reactions, blocks) are indexed through MUD’s Store events rather than custom events.

Security Considerations

  • Access Control — Check permissions in systems before state changes
  • Reentrancy — Use checks-effects-interactions pattern
  • Input Validation — Validate all inputs (addresses, content lengths)
  • Delegation — Always check isDelegateAuthorized for on-behalf-of actions

Deployment

ChainSocial uses MUD’s deployment tooling:

cd packages/contracts pnpm mud deploy --rpc <RPC_URL>

After deployment, archive build artifacts immediately. The via-IR optimizer produces non-deterministic bytecode, making verification impossible if artifacts are overwritten.

Contract Addresses

NetworkChain IDWorld Address
Base Mainnet84530x7405fCbEc24C00278b7e821Ace222f5CFfa6c6eA

Next Steps