This is an experimental dapp that tries to re-create a messenger like experience on the Terra Blockchain. This is designed so that it is able to run fully on-chain without the need for any backend servers to index the transactions. Here are the list of features:
- Able to start a chat with anyone using their wallet address
- All chats are published in clear text and can be read by anyone
- Currently only supports chats between two people but the contract technically supports group chats
- Chats and messages are paginated with a hardcoded limit of 10 messages per query
- O(1) to write and O(N) to read chats and messages
- A "facebook messenger"-like UI is included in the
app
folder. Credits to: https://github.com/sejr/react-messenger for the base template.
Query to get the chats that a wallet address is involved in. The results are sorted by ascending user wallet address and returns a limit of 10 chats per call. Pagination is supported using the last_user
parameter.
GetChats {
user: String,
last_user: Option<String>,
}
Input Parameters
- user (String): Terra wallet address that you want to list the chats for
- last_user (Optional<String>): Value is used for pagination. The last address that was returned in a previous call so that the previous results can be excluded from the next search.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct GetChatsResponse {
pub chats: Vec<Chat>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Chat {
pub chat_id: u64,
pub user1: String,
pub user2: String,
}
Query to get a list of messages in a chat. The results are sorted by message_id
in descending order (newest messages first). There is a 10 results limit per query but pagination is supported using the last_message_id
parameter
GetMessages {
chat_id: u64,
last_message_id: Option<u64>,
}
Input Parameters:
- chat_id (u64) - The chat that the messages you are interested in belongs to
- last_message_id (Option<u64>) - The last message that was returned in the previous call. If this is provided, the query will skip all messages before this. Used to support pagination.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct GetMessagesResponse {
pub messages: Vec<Message>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Message {
pub from: String,
pub message_id: u64,
pub timestamp: Timestamp,
pub text: String,
}
Used to send a message to a wallet address. If no chat was created in the past between two wallets, this call will create a chat with a new chat_id
. If a chat already exists, it will use that for the new message.
SendMessage { text: String, to: String },
The following are the storages used in the contract. Every single message is stored in the contract. Deletion is not supported yet but it is possible to add.
State of the messenger contract. Keep tracks of a running count of the last chat and message IDs created so new ones can be created with unique IDs. Both IDs start from zero.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct State {
pub last_chat_id: u64,
pub last_message_id: u64,
}
Storage to keep track of which chat_id
belongs to which pair of users. Everytime a new chat is created, a pair of records will be added to the storage.
(user1, user2), chat_id
(user2, user1), chat_id
This trades storage for query efficiency so that we can quickly search for all chats that a user is involved in just by using the first value in the tuple key.
pub const CHATS: Map<(&Addr, &Addr), u64> = Map::new("chats");
Main storage to keep all messages sent in every chat.
pub struct Message {
pub timestamp: Timestamp,
pub from: String,
pub text: String,
}
pub const MESSAGES: Map<(U64Key, U64Key), Message> = Map::new("messages");
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
This project currently includes scripts to deploy to a running LocalTerra. Deployment address will be stored in scripts/localterra.config.json
. In order to redeploy with an updated code, you will need to delete the messengerCodeId
and messengerAddr
and rerun the deployment script
npm i
ts-node scripts/deploy.ts
Update the LocalTerra messenger contract address in app/src/data/networks.js
before running the UI.
There are some seed conversations that you can use to seed the contract storage. It currently assumes that you are using the test1
account (terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v
) as the main deployer and user of the dapp. Make sure your contract is already deployed to LocalTerra before running the seed scripts.
ts-node scripts/seed.ts
A simple UI is included that you can use to:
- Access chats
- Read messages
- Start chats
- Send messages
cd app
npm i
npm start
By default, it should run on http://localhost:3000.
- Encrypt messages using the public key of the recepient before posting the transaction.
- Instead of storing messages in a contract, we could use an indexer to index
SendMessage
transactions directly. - Allow the creation of group chats.
- Send tokens directly from dapp.