Smart contract architecture diagram showing blockchain integration, contract methods, and data flow
Complete technical specification for the on-chain messaging contract with file attachment support.
Contract Name: EncryptedMessaging
Solidity Version: ^0.8.24
License: MIT
Deployment: Sonic Blaze Testnet
Address: 0x8189B2DC4B8b30765f3853D54e7a8250B3881473
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
contract EncryptedMessaging is Ownable, ReentrancyGuard, Pausable
Security Features:
struct Attachment {
string ipfsHash; // IPFS hash of the encrypted file
string encryptedFilename; // Encrypted original filename
uint256 fileSize; // File size in bytes
string mimeType; // MIME type (encrypted)
}
Field Descriptions:
ipfsHash
: Content-addressed hash for IPFS retrievalencryptedFilename
: Client-side encrypted original filenamefileSize
: Unencrypted file size for validation and UI displaymimeType
: File type for proper handling and displaystruct Message {
uint256 id; // Unique message identifier
address from; // Sender wallet address
address to; // Recipient wallet address
bytes encryptedContent; // Encrypted message content
bytes32 contentHash; // Hash for integrity verification
uint256 timestamp; // Block timestamp
bool isDeleted; // Soft delete flag
Attachment[] attachments; // Array of file attachments
bool hasAttachments; // Quick check for attachments
}
Field Descriptions:
id
: Auto-incremented unique identifier starting from 1from
/to
: Ethereum addresses for sender and recipientencryptedContent
: AES-256-CBC encrypted message bytescontentHash
: Keccak256 hash of original plaintext for verificationtimestamp
: Block timestamp for message orderingisDeleted
: Soft deletion (preserves history)attachments
: Dynamic array supporting multiple fileshasAttachments
: Gas-efficient attachment checkingsendMessage(address to, bytes calldata encryptedContent, bytes32 contentHash)
function sendMessage(
address to,
bytes calldata encryptedContent,
bytes32 contentHash
) external payable nonReentrant whenNotPaused
Parameters:
to
: Recipient wallet addressencryptedContent
: Client-side encrypted message bytescontentHash
: Keccak256 hash of original messageValidations:
Gas Usage: ~352,165 gas
sendMessageWithAttachments(address to, bytes calldata encryptedContent, bytes32 contentHash, Attachment[] memory attachments)
function sendMessageWithAttachments(
address to,
bytes calldata encryptedContent,
bytes32 contentHash,
Attachment[] memory attachments
) external payable nonReentrant whenNotPaused
Additional Validations:
Gas Usage: ~468,810 gas (with attachments)
getInboxMessages(address user, uint256 offset, uint256 limit)
function getInboxMessages(
address user,
uint256 offset,
uint256 limit
) external view returns (Message[] memory messages, uint256 total)
Returns:
messages
: Array of received messages (newest first)total
: Total number of messages in inboxAccess Control: Only the inbox owner can retrieve their messages
getSentMessages(address user, uint256 offset, uint256 limit)
function getSentMessages(
address user,
uint256 offset,
uint256 limit
) external view returns (Message[] memory messages, uint256 total)
Returns:
messages
: Array of sent messages (newest first)total
: Total number of sent messagesgetConversation(address otherUser, uint256 offset, uint256 limit)
function getConversation(
address otherUser,
uint256 offset,
uint256 limit
) external view returns (Message[] memory messages, uint256 total)
Returns:
messages
: All messages between caller and otherUsertotal
: Total conversation message countgetMessage(uint256 messageId)
function getMessage(uint256 messageId)
external view returns (Message memory message)
Returns: Complete message data including attachments
Access Control: Only sender or recipient can access message
deleteMessage(uint256 messageId)
function deleteMessage(uint256 messageId) external nonReentrant
Functionality:
isDeleted
flag to true (soft delete)Access Control: Message participants only
getContractStats()
function getContractStats() external view returns (
uint256 totalMessages,
uint256 contractBalance,
uint256 currentFee
)
Returns:
totalMessages
: Total messages sent through contractcontractBalance
: Current contract ETH balancecurrentFee
: Current message fee in weigetInboxCount(address user)
/ getSentCount(address user)
/ getConversationCount(address otherUser)
function getInboxCount(address user) external view returns (uint256 count)
function getSentCount(address user) external view returns (uint256 count)
function getConversationCount(address otherUser) external view returns (uint256 count)
Returns: Message counts for pagination and UI display
event MessageSent(
uint256 indexed messageId,
address indexed from,
address indexed to,
uint256 timestamp
);
Emitted: When any message is successfully sent
Indexed Fields: messageId, from, to (for efficient filtering)
event MessageDeleted(
uint256 indexed messageId,
address indexed deleter
);
Emitted: When a message is soft-deleted
Use Case: UI updates and audit trails
event EmergencyWithdraw(address indexed owner, uint256 amount);
Emitted: When contract owner withdraws accumulated fees
uint256 public constant MAX_MESSAGE_SIZE = 1024; // 1KB limit
uint256 public messageFee = 0; // Fee in wei (currently free)
Rationale:
uint256 private _messageIdCounter; // Auto-incrementing message IDs
mapping(uint256 => Message) private _messages; // ID → Message storage
mapping(address => uint256[]) private _userInbox; // User → Received message IDs
mapping(address => uint256[]) private _userSent; // User → Sent message IDs
mapping(address => mapping(address => uint256[])) private _conversations; // Conversation storage
// Only message participants can access
require(
message.from == msg.sender || message.to == msg.sender,
"EncryptedMessaging: Unauthorized access"
);
// Comprehensive parameter checking
require(to != address(0), "EncryptedMessaging: Invalid recipient address");
require(to != msg.sender, "EncryptedMessaging: Cannot send message to yourself");
require(encryptedContent.length > 0, "EncryptedMessaging: Empty message");
require(encryptedContent.length <= MAX_MESSAGE_SIZE, "EncryptedMessaging: Message too large");
require(contentHash != bytes32(0), "EncryptedMessaging: Invalid content hash");
// Attachment security checks
require(attachments.length <= 10, "EncryptedMessaging: Too many attachments");
for (uint256 i = 0; i < attachments.length; i++) {
require(bytes(attachments[i].ipfsHash).length > 0, "EncryptedMessaging: Invalid IPFS hash");
require(attachments[i].fileSize > 0, "EncryptedMessaging: Invalid file size");
}
// All state-changing functions use nonReentrant modifier
function sendMessage(...) external payable nonReentrant whenNotPaused
function deleteMessage(...) external nonReentrant
sendMessage (text only): ~352,165 gas
sendMessageWithAttachments: ~468,810 gas
getMessage: ~50,000 gas (view)
getInboxMessages: ~100,000+ gas (varies by count)
deleteMessage: ~45,000 gas
New Message: ~200,000 gas (storage allocation)
Attachment Metadata: ~116,000 gas per attachment
String Storage: ~20,000 gas per 32 bytes
Array Operations: ~40,000 gas per push operation
Unit Tests: 39 tests covering all functions
Integration Tests: End-to-end message flow
Stress Tests: 50-1000 message volume testing
Security Tests: Access control and validation
Performance Tests: Gas usage and optimization
✅ All functions working correctly
✅ Access control properly enforced
✅ Gas usage within expected ranges
✅ Attachment system fully functional
✅ Unicode text support verified
✅ Edge cases handled gracefully
Network: Sonic Blaze Testnet
Chain ID: 57054
RPC URL: https://rpc.blaze.soniclabs.com
Contract: 0x8189B2DC4B8b30765f3853D54e7a8250B3881473
Deployer: 0xfb96484930fcAea068974f6494693a8Bb1AE4ad5
Block: 56066029 (deployment)
function setMessageFee(uint256 _fee) external onlyOwner
function pause() external onlyOwner
function unpause() external onlyOwner
function emergencyWithdraw() external onlyOwner nonReentrant
Use Cases:
const tx = await contract.sendMessage(
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8", // recipient
"0x456e637279707465644d657373616765...", // encrypted content
"0x6d3eef4a603eedc4d3d52bdaf84d82f6244fed4618a876d539a219fad89c9ba6" // content hash
);
const attachment = {
ipfsHash: "QmXxx...",
encryptedFilename: "encrypted_filename",
fileSize: 1353702,
mimeType: "text/plain"
};
const tx = await contract.sendMessageWithAttachments(
recipient,
encryptedContent,
contentHash,
[attachment]
);
const [messages, total] = await contract.getInboxMessages(
userAddress,
0, // offset
10 // limit
);
The EncryptedMessaging smart contract provides a robust, secure foundation for decentralized messaging with comprehensive file attachment support. It has been thoroughly tested and is ready for production deployment.