# Introducing Relation ONE IM

<figure><img src="https://1159189017-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqxkigVyC8hqt4WsqD2KD%2Fuploads%2Fgit-blob-e0d9e95208b8f327294a92453411316b4839de51%2Fim.png?alt=media" alt=""><figcaption><p>Fig. 1-1 IM Architecture</p></figcaption></figure>

Relation ONE IM is a decentralized private messaging protocol.

* It implements [Relation Protocol](https://docs.relationlabs.ai/protocol/), manages identity via [Relation Name Service](https://docs.relationlabs.ai/protocol/open-standard-api/overview/name-service), and manages contacts and groups with [Relationship](https://docs.relationlabs.ai/protocol/open-standard-api/overview/relationship).
* It uses [Lit Protocol](https://litprotocol.com/) to encrypt and decrypt messages.
* It uses [Arweave](https://www.arweave.org/) as its decentralized storage solution.

Relation ONE IM realizes true decentralization and privacy.

It consists of four parts: (see Fig. 1-1）

* Client: This is a front-end application for users to encrypt and decrypt messages and to send such messages via Proxy. It uses the standard interface provided by Relation Protocol to manage contacts and groups.
* Proxy: This is a simple back-end program that accepts messages from "Client" only. It stores the messages to Arweave and pushes them to their receivers.
* Decentralized Storage(Arweave): To store encrypted messages permanently.
* Contracts: A set of contracts (Relation Name Service, Follow, DAO) conforming to Relation Protocol to store user information, Follow relationships, and information on DAOs.

## 1. The main procedure for sending messages

> See Fig. 1-1

1. A sender connects to "Client" using a wallet.
2. The "Client" uses [Lit Protocol](https://developer.litprotocol.com/) to encrypt the message.
3. "Client" sends the encrypted message to a "Proxy".
4. The "Proxy" stores the message permanently.
5. The "Proxy" sends the message to the receiver's "Client" via Websocket.
6. The receiver decrypts the message on the "Client" using Lit Protocol.

## 2. Identity

Each pair of Ethereum public and private keys constitutes an identity. Users can use wallets like MetaMask to connect to "Client".

An ethereum address is like an ID for a user. When messaging a user, the message body specifies the receiver's address, and the "Proxy" will push the message to the "Client" logged in using the target address.

The private key signs the message body to ensure the message is sent by that address and cannot be modified by anybody.

The identity is also used to login to [Lit Protocol](https://litprotocol.com/) to encrypt or decrypt messages in the "Client".

The identity needs to register a SBT Token in \[Relation Name Service]\(#2.1. Relation Name Service) as a prerequisite for private messaging.

### 2.1. Relation Name Service

Relation Name Service contract conforms to the [Name Service](https://docs.relationlabs.ai/protocol/contract-open-standard/identity/name-service) standard published by Relation Protocol.

Before a user can use private messaging, a SBT Token shall be acquired by the user via registering with `Relation Name Service` contract.

In a chat, we can see other users' information ,such as Name and Avatar, from `Relation Name Service`.

Relation Name Service contract provides the following interfaces:

* register: `register(address owner, string calldata name, bool reverseRecord) external returns (uint)`
* set an avatar: `setProfileURI(string memory profileURI) external`
* Query a user name: `nameOf(address addr) external view returns (string memory)`
* Query an avatar: `profileURI(address addr) external view returns (string memory)`

<figure><img src="https://1159189017-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqxkigVyC8hqt4WsqD2KD%2Fuploads%2Fgit-blob-9af4633b00e29e2da43c3c9869b63ded22f4df4d%2Fname-service.png?alt=media" alt=""><figcaption><p>Fig. 1-2 Relation Name Service</p></figcaption></figure>

## 3. Client

"Client" is a front-end application. It can be a Web application, Chrome plugin, desktop or mobile application. In Relation ONE, it is a [Chrome plugin](https://chrome.google.com/webstore/detail/relation-one/bmabahhenimmnfijaiccmonalfhpcndh).

When users are logged in their "Client"s, the "Client" will connect to Proxy via WebSocket. When someone sends messages to said users, the Proxy will push the messages to "Client", and the "Client" can decrypt the messages using Lit Protocol.

Before sending messages, the "Client" composites Messages according to the \[Messages structure]\(#7.1. Message structure) , encrypts the Messages using Lit. Finally, the encrypted Messages are sent to Proxy.

Data privacy is secured this way because messages are encrypted and decrypted on the "Client" side.

The "Client" also manages \[contacts]\(#5.1. Manage contacts) and \[groups]\(#5.2. Manage groups) by calling respective contracts.

## 4. Proxy

Relation ONE Proxy is a back-end application which provides a set of REST API (to receive messages from "Client" and to modify states) and a WebSocket interface (to push messages to "Client".)

"Proxy" will store the messages sent from "Client" to Arweave. Because the messages are stored in a decentralized manner, "Client" does not rely on any single "Proxy". Even if one "Proxy" fails, "Client" can change to another "Proxy" with the message data intact.

### 4.1. Receive/Send Messages

Client sends encrypted messages to Proxy via REST API. The Proxy will store the messages to Arweave according to the Message Persistence strategies and push the messages to the Client logged in by the receiver.

<figure><img src="https://1159189017-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqxkigVyC8hqt4WsqD2KD%2Fuploads%2Fgit-blob-d9c5426ed4543f2a6cf4bb32e11d4a966af9276f%2Fsend-message.png?alt=media" alt=""><figcaption><p>Fig. 1-3 Send Message</p></figcaption></figure>

### 4.2. State Storage

Data concerning states, such as unread messages, muted group messages, sticky messages, are stored by a "Proxy" in its own database. These data are not shared among different Proxies when a Client switches between them.

### 4.3. Message Persistence

\[Proxy]\(#4. Proxy) uploads \[Messages]\(#7.2. Messages) containing one or multiple \[Message(s)]\(#7.3. Payload). When uploading to Arweave, each Transaction has a tag pointing to the Hash of the previous Transaction.

There are three strategies for Message Persistence:

1. Pack and upload the messages to Arweave every 12 hours.
2. If there is no message within 12 hours, then the pack will not be conducted, and the count for "12 hours" restarts.
3. When the messages exceeded 100MB in size, they will be packed and uploaded to Arweave even if the scheduled task is not yet triggered.

### 4.4. The REST interface

A "Proxy" should provide the following interfaces:

| Description          | Http Method | interface                                      | Request Body                     | Response Body                       |
| -------------------- | ----------- | ---------------------------------------------- | -------------------------------- | ----------------------------------- |
| Send messages        | POST        | /message/send                                  | [Message](#7.1.-message-jie-gou) |                                     |
| Query messages       | GET         | /message/list?limit=${limit}\&offset=${offset} |                                  | \[[Message](#7.1.-message-jie-gou)] |
| Mark as read         | POST        | /message/read?messageId=${messageId}           |                                  |                                     |
| Unread message count | GET         | /message/unreadCount                           |                                  | Long                                |
| Chat lists           | GET         | /chats/list?limit=${limit}\&offset=${offset}   |                                  | \[[Chat](#7.4.-chat-jie-gou)]       |

## 5. Manage relationships

### 5.1. Manage contacts

Users manage contacts using [Follow contract](https://docs.relationlabs.ai/protocol/open-standard-api/overview/relationship#follow) conforming to Relation Protocol.

Each \[identity]\(#2. Identity) creates a Follow contract using `FollowRegister` (see Fig. 1-4). When Alice `follow` Bob, the former needs to call the `follow()` method of Bob's `Follow contract` .

<figure><img src="https://1159189017-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqxkigVyC8hqt4WsqD2KD%2Fuploads%2Fgit-blob-5b9e1b25833ebd90d9d4ae0044917cd138e2dfd3%2Ffollow.png?alt=media" alt=""><figcaption><p>Fig. 1-4 Follow</p></figcaption></figure>

* Friend list: List all Follow contracts with `FollowRegister` and list each user's follow information.
* Follow: call the `follow()` method from the other person's contract.
* Unfollow: call the `unfollow()` method from the other person's contract.

### 5.2. Manage groups

Group chats are managed using [DAO contracts](https://docs.relationlabs.ai/protocol/open-standard-api/overview/relationship#dao). The addresses listed in the contract which hold Semantic SBTs are considered group members.

"Client" manages groups by calling [standard DAO interface](https://docs.relationlabs.ai/protocol/open-standard-api/overview/relationship#dao-1).

Example: A creator creates a DAO contract via `DaoRegister`. The `isFreeJoin()` method is used to set whether people are free to join the group. With the `join()` method, one can join a group chat. With the `remove(address addr)` method, one can leave a group chat. (see Fig. 1-5）

<figure><img src="https://1159189017-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqxkigVyC8hqt4WsqD2KD%2Fuploads%2Fgit-blob-a702ec0db2f190923e0dd918637e9bbba5bd6911%2Fdao.png?alt=media" alt=""><figcaption><p>Fig. 1-5 DAO</p></figcaption></figure>

* Create a group chat: The Client calls the `deployDaoContract(address owner,string memory name) external returns (uint256)` method of the `DaoRegister` contract to create a new group chat.(See Fig. 1-5).
* Join a group chat: If the `isFreeJoin()` is set to `True`, then one can use the `join()` method to freely join a group. It is set to `False`, then the administrator needs to add members via the method `addMember(address[] memory addr) external`.
* Leave a group chat : One can call the method `remove(address addr)` to leave a group chat. Or, the administrator can call the method `remove(address addr)` to remove a member.
* Set a group announcement: The administrator of a group sends a special \[message]\(#7.1. Message structure) in the group to realize this, with the `kind` set to `3`.
* Group name: Query a group's name via the method `name()` .
* Group avatar: Query the group avatar using the method `daoURI()`.
* Group members: The addresses listed in the contract holding its SBT Tokens are group members.

## 6. Chat

### 6.1. P2P chat

Users can chat with their \[contacts]\(#5.1. Manage contacts) or send messages to Ethereum addresses not owned by their contacts.

A Client（A）encrypts `payload` of the message using Lit, with [accessCondition](https://developer.litprotocol.com/accesscontrol/evm/basicexamples/#a-specific-wallet-address) as the receiver's Ethereum address. The `receiver` should also be the receiver's Ethereum address. Then, the procedure composites "Messages" according to the \[Message Structure]\(#7.1. Message structure) and sends it to Proxy. The Proxy will push the messages to the Client (B) logged in with the receiver's address.

### 6.2. Group chat

When sending group messages, a user A logs in a Client（A）and encrypts the message's `payload` with Lit, with the [accessCondition](https://developer.litprotocol.com/accesscontrol/evm/basicexamples/#must-posess-any-token-in-an-erc721-collection-nft-collection) set to be visible to group members.) The `receiver` should be set to the group chat's contract address. Then, the procedure composites "Messages" according to the \[Message Structure]\(#7.1. Message structure) and sends it to Proxy. The Proxy will then push the messages to each Client of the users in the group, such as Client（C）、Client（D）、Client（E）...

### 6.3. Encrypt/Decrypt

\[Client]\(#3. Client) implements [Lit Protocol SDK](https://developer.litprotocol.com/sdk/explanation/encryption/). Each message is encrypted with the Client on the sender's side and decrypted with the Client on the receiver's side.

<figure><img src="https://1159189017-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqxkigVyC8hqt4WsqD2KD%2Fuploads%2Fgit-blob-400b49432a50e95df0f081fa67c54e2ba90988f4%2Fencrypt-decrypt.png?alt=media" alt=""><figcaption><p>Fig. 1-6 Encrypt and Decrypt messages</p></figcaption></figure>

As with Fig. 1-6, `Alice` wants to send an encrypted message to `Bob`: `Hi`.

1. Alice's "Client" will use the Lit SDK to encrypt `Hi` locally. Subsequently, it generates the encrypted message `encryptedString` and key `symmetricKey`:

```js
const messageToEncrypt = "Hi";
// 1. Encryption
const { encryptedString, symmetricKey } = await LitJsSdk.encryptString(messageToEncrypt);
```

2. Alice's "Client" sets `accessControlConditions` to be visible to Bob's address, and uploads `accessControlConditions` and key `symmetricKey` to Lit's node.

```js
const accessControlConditions = [
    {
      "contractAddress": "",
      "standardContractType": "",
      "chain": "ethereum",
      "method": "",
      "parameters": [
        ":userAddress",
      ],
      "returnValueTest": {
        "comparator": "=",
        //Bob's wallet address
        "value": "0x50e2dac5e78B5905CB09495547452cEE64426db2"
      }
    }
];
// 2. Saving the Encrypted Content to the Lit Nodes
const encryptedSymmetricKey = await litNodeClient.saveEncryptionKey({
  accessControlConditions,
  symmetricKey,
  authSig,
  chain,
});
```

3. Alice's "Client" composites `Message` and send it to "Proxy".

```js
const message = {
  ...
  "sender": "Alice's wallet address",
  "receiver": "Bob's wallet address",
  "payload": "${encryptedString}",
  "encryptedSymmetricKey": "${encryptedSymmetricKey}",
  ...
}
```

4. The Proxy forwards the message to Bob's "Client".
5. After Bob's "Client" received `Message` pushed by the "Proxy", it acquires the key `_symmetricKey` after it passed the verification conducted by the Lit node:

```js
// 5. Decrypt it
// <String> toDecrypt
const toDecrypt = LitJsSdk.uint8arrayToString(encryptedSymmetricKey, 'base16');

// <Uint8Array(32)> _symmetricKey
const _symmetricKey = await litNodeClient.getEncryptionKey({
    accessControlConditions,
    toDecrypt,
    chain,
    authSig
})
```

6. Bob decrypts the message locally:

```js
// <String> decryptedString
let decryptedString;

try{
    // gets the original text: Hi
    decryptedString = await LitJsSdk.decryptString(
        encryptedString,
        _symmetricKey
    );
}catch(e){
    console.log(e);
}
```

## 7. Define a message

### 7.1. Message structure

```json
{
  "id": "${messageId}",
  "encryptedBy": "Lit",
  "sender": "${senderWalletAddress}",
  "receiver": "${receiverWalletAddress/receiverDaoContractAddress}",
  "timestamp": ${the message creates timestamp in milliseconds},
  "kind": ${kind},
  "tags": [
    ["quoteId", "${messageId of the quoted message}"],
    ["mentionedUsers", "${The wallet address of the mentioned user}"]
  ],
  "type": "${TEXT/CARD/NFT/IMAGE/EMOJI/BATCH_TRANSFER/ANNOUNCEMENT}",
  "payload": "${message encrypted by Lit}",
  "encryptedSymmetricKey": "${a hex string that LIT will use to decrypt payload as long as you satisfy the conditions}",
  "delegationMessage": "Authorize  ${address} to delegate signature, Creation timestamp: ${}, Expiry timestamp: ${}",
  "delegationSig": "${The signature generated by signing against ${delegationMessage} using private key}",
  "sig": "${The signature generated by signing against ${messageId} using private key}"
}
```

* How to generate `messageId` : Composite a json string with the following format and calculate its Keccak256:

```json
[
  <encryptedBy>,
  <sender>,
  <receiver>,
  <timestamp>,
  <kind>,
  <tags>,
  <payload>,
  <delegationMessage>,
  <delegationSig>
]
```

* `kind`
  * 0: p2p message。
  * 1: group message。
  * 2: dao message。
  * 3: announcement。
  * 4: hide message。
* Session Key：

A pair of public and private keys are generated on the Client side as the Session Key. The Session Key is used to composite `delegationMessage`. Then, the user's private key will be used to sign against `delegationMessage`, resulting in `delegationSig`.

When users send a message, they sign against the message using the private key of the Session Key pair, and attach the `delegationMessage` and `delegationSig`. With this delegationChain, it can be verified that the message is indeed signed by the said user.

* How to generate `sig`:

Sign `messageId` using the private key of Session Key, and you will have a `sig`.

### 7.2. Messages

"Messages" is an array with one or multiple "Message" with the following format:

```
Messages = [Message,Message,...]
```

### 7.3. Payload

* Text message

```json5
{
  //  ...
  "type": "TEXT",
  "payload": "Hi"
  //  ...
}
```

* CARD message

```json5
{
  //  ...
  "type": "CARD",
  "payload": "type:href;action:shareFavorite;p1(Text):https%3A%2F%2Fspace.id%2Fvoyage,p2(Text):,p3(Text):SPACE ID,p4(Text):https%3A%2F%2Fspace.id%2Ffavicon.ico"
  //type:${href};action:${shareFavorite};p1(Text):${URL},p2(Text):,p3(Text):${title},p4(Text):${icon}
  //  ...
}
```

* NFT

```json5
{
  //  ...
  "type": "NFT",
  "payload": "type:image;action:nftImage;p1(Text):https%3A%2F%2F3fypb-gqaaa-aaaag-aaedq-cai.ic1.io%2Fnft%2Fpolygon%2F0x4b5c095380e8e5f016a70a28af570c3bca93b811%2F25908.jpeg,p2(Text):polygon"
  //type:image;action:nftImage;p1(Text):${imageURL},p2(Text):${chainName}
  //  ...
}
```

* Local IMAGE

```json5
{
  //  ...
  "type": "IMAGE",
  "payload": "type:image;action:localImage;p1(Text):https%3A%2F%2F3fypb-gqaaa-aaaag-aaedq-cai.ic1.io%2Fim%2Fimage%2Fnymb5-kqaaa-aaaaj-4thiq-cai%2F2f3537d1a2474f2fb92cb5f7de2ed70a.png,p2(Text):im/image/nymb5-kqaaa-aaaaj-4thiq-cai/2f3537d1a2474f2fb92cb5f7de2ed70a.png"
  //type:image;action:localImage;p1(Text):${imageURL},p2(Text):${s3ObjectKey}
  //  ...
}
```

* EMOJI

```json5
{
  //  ...
  "type": "EMOJI",
  "payload": "type:image;action:customEmoji;p1(Text):https%3A%2F%2F3fypb-gqaaa-aaaag-aaedq-cai.ic1.io%2Fim%2Femoji%2Flov6e-qqaaa-aaaaj-xykxq-cai%2Fac1dc2bf6bad48aeb1a502c0a28ee942.jpg,p2(Text):im/emoji/lov6e-qqaaa-aaaaj-xykxq-cai/ac1dc2bf6bad48aeb1a502c0a28ee942.jpg"
  //type:image;action:customEmoji;p1(Text):${imageURL},p2(Text):${s3ObjectKey}
  //  ...
}
```

* Batch transfer

```json5
{
  //  ...
  "type": "BATCH_TRANSFER",
  "payload": "0xd84ed2b4deacbad8a568747fea45f3fc7f2d204595101f99f43006712f3ff290::USDT::20.0000::0x969f85053b44d7eCE8108094420Aba45149b7A3a::qzoo5-ryaaa-aaaaj-xs6ma-cai"
  //payload: 0xbaf915e778b044dbf62974d17bd2ab8875bccce8bd02c40dfc241d2893755851::USDT::5.0000::aukee-3yaaa-aaaah-qc3ya-cai::4xtgo-viaaa-aaaaj-aavyq-cai,ms5ob-iqaaa-aaaaj-aa2za-cai,kppoo-ozrgz-rtkn3-cgqyq-cai,meohx-szymu-ytezr-ugiza-cai,oazqd-ndeg4-ytqyj-zga2q-cai
  //payload: 0x71de31f73f34fc9af20f7ce3b35ec4e56bf25af476f99cb376779761c2fe91fa::USDT::1.0000::aukee-3yaaa-aaaah-qc3ya-cai::7zshb-ryaaa-aaaaj-aag3a-cai
  //  ...
}
```

* ANNOUNCEMENT

```json5
{
  //  ...
  "type": "TEXT",
  "payload": "It's an announcement"
  //  ...
}
```

### 7.4. The structure of a Chat

```json
{
  "id": "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5",
  "name": "test-group",
  "avatar": "",
  "channelType": "GROUP",
  "unreadMessageCount": 0,
  "type": "TEXT",
  "payload": "the message payload",
  "lastPostAt": "1665219499000"
}
```

## 8. Message storage

The application uses Arweave to store encrypted messages permanently to ensure reliability and decentralization.

A series of \[Message]\(#7.1. Message structure) is grouped into \[Messages]\(#7.2. Messages) by \[Proxy]\(#4. Proxy) and uploaded to Arweave. Each Transaction has a tag pointing to the Hash of the previous Transaction, so it is easy to find all history messages with the latest Hash.

```
Messages = [Message, Message, Message]
```

<figure><img src="https://1159189017-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqxkigVyC8hqt4WsqD2KD%2Fuploads%2Fgit-blob-e9350d41965885bb30dd514b472502d895814a12%2Fmessage-data-storage.png?alt=media" alt=""><figcaption><p>Fig. 1-7 Message storage</p></figcaption></figure>

## 9. Contract

| Name                  | Docs                                                                                                    | Mumbai                                     | Goerli                                     | zkSync Era Testnet                         |
| --------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ |
| Relation Name Service | [name-service docs](https://docs.relationlabs.ai/protocol/contract-open-standard/identity/name-service) | 0x04C23dDfE813d8D208bc8120b73F8EF30f423850 | 0xe4AA1f1E5be0A4E7F80CAC26C2Db1611C3E70c41 | -                                          |
| FollowRegister        | [follow docs](https://docs.relationlabs.ai/protocol/contract-open-standard/relationship/follow)         | 0xA0cECa90d7A2033f5162dc73391cdB72272456E7 | -                                          | 0x734dE4f1b3a1c0619ECB80e3a676a6cFabe72Adc |
| DaoRegister           | [DAO docs](https://docs.relationlabs.ai/protocol/contract-open-standard/relationship/dao)               | 0x153C3eF051C7665c9A9c1a718bF12bC8EE6b5115 | -                                          | 0x7229Dc0117E8484D61137B7304b02b163beC912c |
