Let's build Trustless Discord, a decentralized, unstoppable, and open messaging application.
create a new server
join an existing server
create a channel on an existing server
post a message to the created channel
get all messages posted to a channel
Write the Trustless Discord smart contract
It turns out that writing the Trustless Discord smart contract is very simple. Here is a basic contract to provide a decentralized social application.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Discord {
struct Server {
address creator;
string serverName;
}
struct Channel {
address creator;
string serverName;
string channelName;
}
struct Message {
address sender;
string message;
}
mapping(string => Server) public servers;
mapping(address => mapping(string => uint8)) public joinInfo;
mapping(string => Channel) public channels;
mapping(string => mapping(string => Message[])) public messages;
function createServer(string memory _sname) public {
require(keccak256(abi.encodePacked(servers[_sname].serverName)) == keccak256(abi.encodePacked("")), "The server name is existed already");
servers[_sname] = Server(
msg.sender,
_sname
);
}
function joinServer(string memory _sname) public {
require(keccak256(abi.encodePacked(_sname)) != keccak256(abi.encodePacked("")), "Server name must be not empty");
require(keccak256(abi.encodePacked(servers[_sname].serverName)) != keccak256(abi.encodePacked("")), "The server name is not existed yet");
require(joinInfo[msg.sender][_sname] == 0, "You have joined the server already");
joinInfo[msg.sender][_sname] = 1;
}
function createChannel(string memory _sname, string memory _cname) public {
require(keccak256(abi.encodePacked(_sname)) != keccak256(abi.encodePacked("")), "Server name must be not empty");
require(keccak256(abi.encodePacked(_cname)) != keccak256(abi.encodePacked("")), "Channel name must be not empty");
require(keccak256(abi.encodePacked(servers[_sname].serverName)) != keccak256(abi.encodePacked("")), "The server name is not existed yet");
channels[_cname] = Channel(
msg.sender,
servers[_sname].serverName,
_cname
);
}
function postMessage(string memory _sname, string memory _cname, string memory _message) public {
require(joinInfo[msg.sender][_sname] != 0, "You have not joined the server");
require(keccak256(abi.encodePacked(channels[_cname].channelName)) != keccak256(abi.encodePacked("")), "The channel is not existed yet");
Message memory newMsg = Message(
msg.sender,
_message
);
messages[_sname][_cname].push(newMsg);
}
function getMessages(string memory _sname, string memory _cname) public view returns (string memory, string memory, Message[] memory) {
return (_sname, _cname, messages[_sname][_cname]);
}
}
Clone the smart contract examples
We've prepared a few different examples for you to get started. The Messenger example is located at smart-contract-examples/contracts/Discord.sol.
Make sure the accounts in hardhat.config.ts have some $BVM.
Interact with the contracts
Once the contracts are deployed, you can interact with them. We've prepared a few hardhat tasks to make it easy for you to interact with the contracts.
# create a new Discord server
npx hardhat createServer --contract <your-contract-address> --sname server1
# join an existing server
npx hardhat joinServer --contract <your-contract-address> --sname server1
# create a new channel on an existing server
npx hardhat createChannel --contract <your-contract-address> --sname server1 --cname channel1
# post a message to a created channel
npx hardhat postMessage --contract <your-contract-address> --sname server1 --cname channel1 --message "The new Bitcoin city"
# get all messages from a channel
npx hardhat getMessages --contract <your-contract-address> --sname server1 --cname channel1