Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EIP: Notification Interface #7017

Closed
wants to merge 13 commits into from
281 changes: 281 additions & 0 deletions EIPS/eip-7017.md
@@ -0,0 +1,281 @@
---
eip: 7017
title: Notification Interface
description: A notifications interface for a more engaging blockchain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your description doesn't really add any additional information over the title. You should try to expand on the ideas introduced on the title.

Perhaps something like:

Suggested change
description: A notifications interface for a more engaging blockchain
description: An event signature and JSON schema for broadcast and unicast notifications.

author: Oliver Stehr (@Oli-art)
discussions-to: https://ethereum-magicians.org/t/eip-7017-notifications-interface-for-a-more-engaging-blockchain/11091
status: Draft
type: Standards Track
category: ERC
created: 2022-11-10
---

## Abstract

The following standard allows addresses / smart contracts to send notifications one-to-one and one-to-many.

It achieves it in a trustless and decentralized way and without requiring any on-chain interaction by the receivers.

It requires a front-end implementation where the notifications are listened to, filtered and showed to the end user (address owner).
Comment on lines +16 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be combined into a single paragraph.

Suggested change
The following standard allows addresses / smart contracts to send notifications one-to-one and one-to-many.
It achieves it in a trustless and decentralized way and without requiring any on-chain interaction by the receivers.
It requires a front-end implementation where the notifications are listened to, filtered and showed to the end user (address owner).
The following standard allows addresses / smart contracts to send notifications one-to-one and one-to-many. It achieves it in a trustless and decentralized way and without requiring any on-chain interaction by the receivers. It requires a front-end implementation where the notifications are listened to, filtered and showed to the end user (address owner).


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see a bit more about how this proposal works, technically, in the abstract.

Some usecases include but are not limited to:

- DAO governance voting anouncement
- DEX informing an address about a certain price limit being reached, for example a stop-loss or a margin-call.
- An NFT marketplace informing an NFT owner about an offer being made to one of it’s NFTs.
- A metaverse informing about an important event.
- Warning to an address about an ENS domain expiration date approaching.
Comment on lines +22 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These use cases should be moved to the Motivation section.


## Motivation

With the adoption of web3 applications, an increasing necessity arises to be informed about certain events happening on-chain.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this wording instead:

Suggested change
With the adoption of web3 applications, an increasing necessity arises to be informed about certain events happening on-chain.
As the adoption of web3 applications grows, so too does the need to be informed about certain events happening on-chain.


Users are used to being informed, whether it be about news or updates of their favorite applications. As we are in a time of instant data feeds on social media, instant messaging apps and notifications of events that users care about, notifications are a feature in practically every application that exists in web2. They mostly come to email inboxes, SMSs, inside the applications, or in the notification inbox in the operating system.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Users are used to being informed, whether it be about news or updates of their favorite applications. As we are in a time of instant data feeds on social media, instant messaging apps and notifications of events that users care about, notifications are a feature in practically every application that exists in web2. They mostly come to email inboxes, SMSs, inside the applications, or in the notification inbox in the operating system.
Users are accustomed to being informed, whether it be about news or updates of their favorite applications. As we are in a time of instant data feeds on social media, instant messaging apps and notifications of events that users care about, notifications are a feature in practically every application that exists in web2. They mostly come to email inboxes, SMSs, inside the applications, or in the notification inbox in the operating system.


If they would be taken away, the engagement on these web2 applications would sink. This is not different with web3 applications: users cannot be left in the dark about what is going on in them. Not only that, for some applications, all that matters is the participation of users on certain events, like governance on a DAO.

In web2, most of the user account’s are linked to an email address that is required at sign-up. This makes it easy to send notifications to specific users and requires no further complexity as an infrastructure exists to deliver a message to the user using their email address.

In web3, on the other hand, there's mostly is only one inbox to send notifications to: addresses.
Every smart contract can define its own event’s to which one can listen to, but for each of them, a change has to be done in the frontend to listen to that specific contract and event structure. This poses a problem of coordination between smart contracts and web3 applications that can notify users.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Every smart contract can define its own event’s to which one can listen to, but for each of them, a change has to be done in the frontend to listen to that specific contract and event structure. This poses a problem of coordination between smart contracts and web3 applications that can notify users.
Every smart contract can define its own events to which one can listen to, but for each of them, a change has to be done in the frontend to listen to that specific contract and event structure. This poses a problem of coordination between smart contracts and web3 applications that can notify users.


This EIP aims at proposing a decentralized approach to send and receive notifications from and to ethereum addresses, including smart contracts, in a standarized way, facilitating front-end intrgrations. These could be:

- Wallets
- Mobile phone dapps
- Email services
- Discord / Telegram Bots

All of these could be notifying about on-chain notifications to the user.

## Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

An approach that is both simple, decentralized and easy to implement is to use a notifications smart contract standard that is **event-based** to be able to emit notifications to one address or broadcast them to anyone that wants to listen. Off-chain **whitelists** would record all addresses a user wants to allow receiving messages from to avoid spam. This is useful for direct messages from one address to another. Off-chain **subscription lists** would indicate which addresses they want to listen for general broadcasts. This is useful for receiving updates on a project.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph introduces no requirements or recommendations for implementation. It should be split and moved to motivation and rationale as appropriate.


As a user **SHALL NOT** record its whitelist and subscription list on-chain, the receiver will not do any transaction.

Every compliant contract **MUST** implement the `INotification` interface:

```solidity
pragma solidity ^0.8.7;
interface IERC7017 {
/// @notice Send a direct message to an address.
/// @dev `from` must be equal to either the smart contract address
/// or msg.sender. `to` must not be the zero address.
event DirectMsg (address indexed from, address indexed to, string data);

/// @notice Broadcast a message to a general public.
/// @dev `from` parameter must be equal to either the smart contract address
/// or msg.sender.
event BroadcastMsg (address indexed from, string data);

/**
* @dev Send a message to an address from the address executing the function
* @param to address to send a notification to
* @param data to send: (json format, URI recomended):
* {subject, body, image_url (optional),
* transaction_request (optional)}
*/
function walletDM (address to, string memory data) external;

/**
* @dev Send a message to an address from the smart contract
* @param to address to send a notification to
* @param data to send: (json format, URI recomended):
* {subject, body, image_url (optional),
* transaction_request (optional)}
*/
function contractDM (address to, string memory data) external;

/**
* @dev Send a general notification from the address executing the function
* @param data to broadcast: (json format, URI recomended):
* {subject, body, attention_level (1, 2 or 3), image_url (optional),
* transaction_request (optional)}
*/
function walletBroadcast (string memory data) external;

/**
* @dev Send a general notification from the address executing the function
* @param data to broadcast: (json format, URI recomended):
* {subject, body, attention_level (1, 2 or 3), image_url (optional),
* transaction_request (optional)}
*/
function contractBroadcast (string memory data) external;
}
```

The event's are the message couriers and the functions call the events:

Every `walletBroadcast` and `contractBroadcast` function **MUST** implement the `BroadcastMsg` event with `from` set as `msg.sender` and `address(this)` respectively.

Also, every `walletDM` and `contractDM` function **MUST** implement the `DirectMsg` event with `from` set as `msg.sender` and `address(this)` respectively.

Here is a table to better represent this:

| | DirectMessage | Broadcast |
| -------- | ------------------------------------------------- | ------------------------------------------------ |
| Wallet | emit DirectMsg(msg.sender, to, message); | emit BroadcastMsg(msg.sender, message); |
| Contract | emit DirectMsg(address(this), to, message); | emit BroadcastMsg(address(this), message); |

### Data Schema

The `data` field **REQUIRED** in the message events **MUST** have a JSON schema that can be written directly as a string, or passed as a URI containing the JSON. It is **RECOMENDED** to use a URI, as it is a cheaper way to send the data.

It has two different specifications, one for broadcast messages and another one for direct messages. Only small differences exist between them. This is the base specifications for both:

```json
{
"subject": "The subject of the message",
"body": "The body of the message",
"image_uri": "An image URI",
"transaction_request": "A transaction request"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like you are missing indentations here. would recommend trying to match lines 151–154, and also clarifying how indentation/spacing should be handled in the JSONs.

}
```

`image_url` and `transaction_request` are **OPTIONAL**.
The `transaction_request` **MUST** be in the [ERC-681](./eip-681.md) format.

The differences between the schemas of Broadcast Messgaes and Direct Messages are the following:

- Direct message data **MUST** be encrypted. The encryption method is to be discussed.
- Broadcast message's `subject` **SHOULD** have a maximum size of 30 characters.
- Broadcast message's `body` **SHOULD** have a maximum size of 205 characters.

Here is an example for a broadcast message:

```json
{
"subject": "This is an ERC-7017 message",
"body": "This is a message being broadcasted to show you how a contract implementing IERC-7017 can notify its users about an important event. Attached is an image of a unicorn and a request for you to burn 1 eth.",
"image_uri": "https://i.pinimg.com/originals/fc/a3/ee/fca3ee19c83bae8e558bcac23d150001.jpg",
"transaction_request": "ethereum:0x0000000000000000000000000000000000000000?value=1e18"
}
```


## Rationale

### Why these four functions?

Given that both smart contracts and wallets should be able to send notifications, two different types of functions must exist, as the sender is defined differently.
Then, as there are two different methods of sending notifications, a targeted one or a general one, also two types of functions must exist.
In total, if we want to have all combinations, we must have these four functions, one for evey combination of sender and method.

### Initial Idea

The initial idea was to have one smart contract which would be called to send messages to addresses. Such messages would be stored in inboxes linked to every address.

This was an obvious mistake, as pointed out by the community, as on-chain storage is not neccesary if the messages are not read from within the EVM. All that is needed is for off-chain reading of events. This also eliminated the need for only one smart contract to manage all the messages and now every smart contract could implement the interface with the events.

### Encryption of Direct Messages

Direct messages are meant to be read by the receiver only, so they must be encrypted.

Details as to what encryption should be used in direct messages is a topic to be adressed in a sister EIP to standarize encryption methods.

### Maximum Subject and Body Size

Note that only broadcasted messages have a maximum subject and body size. This is to facilitate notifications UX and UI, as this would be the main use of broadcasting messages: notifying users about stuff.

As tho why 30 characters for the subject and 205 for the body, these are sizes that are long enough for informing a user about an event, but also fit inisde a standard desktop and mobile notification. Here's an example for your intuition:

> **System Maintenance Today**
> Attention: Scheduled maintenance
> will be conducted today at 10 PM
> UTC. Please expect temporary service
> interruptions. We apologize for any
> inconvenience caused and appreciate
> your understanding.
> Your Team.

The enforcing on these limits is done on the apps that notify the users. Messages surpassing the limits can be managed as the apps desire, but all messages that are within the limits should be shown to the user.

### Transaction Requests Inside a Message

Transaction requests can come in a compact string format when using the ERC-681 format. They can be shown to the user as a button inside of the message. This button would ask for the user to sign a transaction that the message sender is requesting. This further improves the user experience in web3. In case the notification is received in an application that is not a wallet, a QR code can be generated based on the transaction request. Many wallets today come with a QR code scanner that can read transaction requests.

### What about Push Protocol's solution?

Push Protocol could be thought as a solution to all the problems described and is something that is already in use. But there are many points that make it's use problematic. This problems are:

- Despite what they say, their protocol is not really decentralized, as described in their whitepaper. For example, to send a notification, you send a JSON to their API, which they can then pass over to the users that are listening. This is done off-chain and has no way to be verified. There has to be trust in the server running the protocol.
- As described in “subscribing-to-channel” section of their whitepaper, their protocol requires subscribers to do a transaction in order to subscribe or unsubscribe. The lists are stored on-chain, which is something that isn’t necessary, as users could choose who to sisten from by doing a filter in the frontend.
- To ease this problem, they decided to incentivize the subscribers by paying them for subscribing. This is a cost that goes to the entities emitting the messages, as they are required to stake DAI on behalf of the protocol. This is yet another cost to consider.
- Sending and receiving notifications is not only costly, but involves a process that is unnecessarily tied to their servers. The processes involved are uneasy to implement. Sender’s can’t simply send a message from their smart contract at low cost, but they have to go to a DeFi protocol, create an account, stake DAI, develop a way to deliver the message to the protocol, etc. All of this with poor documentation and transparency.
- As described in the governance section of their whitepaper, the protocol is not run by users. All decisions will be made by “the Company”. There is no actual governance mechanism in place.

Some of this problems could be solved, but most of them are in the very architecture of their solution. Because of this, a new solution must be implemented to overcome them.

## Reference Implementation

```solidity
pragma solidity ^0.8.7;
contract ERC7017 is IERC7017 {
/**
* @dev Send a notification to an address from the address executing the function
* @param to address to send a notification to
* @param data to send: (json format, URI recomended):
* {subject, body, image_url (optional),
* transaction_request (optional)}
*/
function walletDM (address to, string memory data) external virtual override {
emit DirectMsg(msg.sender, to, data);
}

/**
* @dev Send a notification to an address from the smart contract
* @param to address to send a notification to
* @param data to send: (json format, URI recomended):
* {subject, body, image_url (optional),
* transaction_request (optional)}
*/
function contractDM (address to, string memory data) external virtual override {
emit DirectMsg(address(this), to, data);
}

/**
* @dev Send a general notification from the address executing the function
* @param data to broadcast: (json format, URI recomended):
* {subject, body, attention_level (1, 2 or 3), image_url (optional),
* transaction_request (optional)}
*/
function walletBroadcast (string memory data) external virtual override {
emit BroadcastMsg (msg.sender, data);
}

/**
* @dev Send a general notification from the address executing the function
* @param data to broadcast: (json format, URI recomended):
* {subject, body, attention_level (1, 2 or 3), image_url (optional),
* transaction_request (optional)}
*/
function contractBroadcast (string memory data) external virtual override {
emit BroadcastMsg (address(this), data);
}
}
```

## Security Considerations

### Impersonation

There are security considerations for the proposal regarding threats.

The first is related to how the clients will interpret and filter the events. There can be malicious contracts that implement the notification interface but whose functions emit events where the sender is neither the message sender nor the contract. They could try to impersonate another address by changing the sender.

Here is an example of a malitious implementation of a BroadcastMsg event impersonating Vitalik Buterin:

```solidity
emit BroadcastMsg (0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045, message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This event doesn't match one from the interface. Is that intentional?

```

This is why the message sender in the log's topic must allways be verified against the transaction "from" or transaction "to", depending on the notification type (wallet or contract respectively).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after further discussion: this "advice" is now obsolete. it is up to the client-side application to listen only to contracts which make "sensible" choices as far as the meanings of from and to. in particular, there are various patterns which it might make sense for a contract to use, depending on the circumstance. it doesn't make sense to be prescriptive here.


Another way to impersonate a sender would be to find a vulnerability in a contract and execute the contractBroadcast() or contractDM() functions to send messages that do not represent the entity behind the contract.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).