What the heck is Event and Topic?
Tags: blockchain, web3, solidity, ethereum
Published: 2020-08-25 11:46:32

I am very new to blockchain development and I had a small [project](https://github.com/Eazybee/Ethexplorer) I needed to finish last week. Understanding these concepts was the biggest blocker I had as Web3js documentation wasn't helpful.

Writing a smart contract in Solidity uses a design pattern called [Publish-Subscribe(PubSub)](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern). In simple terms, when a particular event happens, it is broadcasted publicly across the blockchain. Anyone interested in the information can carefully listen for a particular event broadcast and use the information for whatever they need it for.

Was that confusing? Let me tell you a very short story.

> In a small village, whenever a new child is given birth into a Family, the town crier must announce this information across the whole village. The census organization records the new addition to the village. Other willing families in the village send their congratulations


**Blockchain**: The village
**SmartContract**: The Family,
**Event**: new child is given birth,
**Publisher**: The town crier
**Subscribers**: census organization, Other willing families

<a name="analogy"> </a>Now, lets read the story one more time
> In an ethereum blockchain, when an event is emitted in the smart contract, the event is stored in transaction logs and logs are stored on the blockchain. Anyone who is interested in the event can search for past events recorded or subscribe to new ones.

_This is also known as **EventDriven PubSub Design**_

Now that you understand what EventDriven PubSub Design is, let's move forward to how this is implemented in solidity and how it helps us understand events and topics.

Using the above <a href="#analogy">analogy</a>, a family smart contract will look like this... 

```solidity
contract Family {
    // ... other family logic

  event NewChild(person indexed father, person indexed mother, person child);

    function giveBirth() public {
       //...give Bith Logic
       emit NewChild(father, mother, child);
    };
}
```
A `NewChild` event is declared and emitted using the `emit` keyword
Now anybody that wants can listen for `NewChild` event can subscribe to the particular event across the blockchain.

### **But How?**
Using [web3](https://web3js.readthedocs.io/en/v1.2.11/index.html), this is a simple code snippet used to [subscribe to all event in the blockchain logs](https://web3js.readthedocs.io/en/v1.2.11/web3-eth-subscribe.html#subscribe-logs)

```javascript

const subscription = web3.eth.subscribe(
  'logs',
  {
   address: '0x123456..',
   from: 0,
  },
  (error, result) => {
    if (error) return;
    // do something with the data
    console.log(result);    
   }
);
```

The above code literally listens to all events in the blockchain and that's a lot, tons of data we probably don't need. What we want is to only subscribe to a specific event which is `NewChild`

Web3 got us covered, [`web3.eth.subscribe`'s second argument](https://web3js.readthedocs.io/en/v1.2.11/web3-eth-subscribe.html#subscribe-logs) is an object with the following keys: `fromBlock`, `address`, and `topics`. My focus will be on `topics` because I feel the other two are self-explanatory and I don't want to deviate too much from this article's subject.

### **So what the Hecks are Topics?**
Topics were my worst nightmare because I couldn't find a documentation/article to explain it to me in a way a beginner like me would understand.

#### **According to Web3 docs:**
- Topics - Array: An array of values which must each appear in the log entries. The order is important, if you want to leave topics out use null, e.g. [null, '0x00...']. You can also pass another array for each topic with options for that topic e.g. [null, ['option1', 'option2']]


#### **Translation:**
- Topics are 32-byte (256 bit) “words” that are used to describe what’s going on in an event.

- Topics are what helps us filter the events in the logs. With the right topics, we can subscribe only to a particular event instead of all events.

**_Events emitted are translated to topics._**


Example: Event `Transfer` emitted is equal to the `topics` below
```solidity 

contract SomeErc20Contract {
  // ...some smartcontract logic
  event Transfer(address indexed from, address indexed to, uint256 tokens);

  function transfer(address to, uint256 tokens) public returns (bool success) {
   // some subtraction and addition logic
   emit Transfer(
     '0x4dc8f417d4eb731d179a0f08b1feaf25216cefd0'
     '0xd2b2fb39b10cd50cab7aa8e834879069ab1a8d4',
     200
   );
  }
}
```
```javascript
const topics = [ 
  '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
  '0x0000000000000000000000004dc8f417d4eb731d179a0f08b1feaf25216cefd0',
  '0x0000000000000000000000000d2b2fb39b10cd50cab7aa8e834879069ab1a8d4'
]
```

Let me explain how `topics` are generated from `Event`.

<span>1. </span> **Event signature** (`topics[0]`): The first element in the `topics` array is the hash of the event signature. The event hash is generated by stripping off keywords and arguments from the event declaration and then hashed using keccak256(or sha3).

Example:

- <a name="ed"> </a>Event declaration
```solidity
 event Transfer(address indexed from, address indexed to, uint256 tokens);
```

- Strip off keywords and arguments
```solidity
 'Transfer(address,address,uint256)'
```

- Hash it using keccak256(or sha3)
```solidity
 keccak256('Transfer(address,address,uint256)');
```
and we get our first topic(hash of the event signature ) `0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef`
<br/>
<span>2. </span> **Other topics** (`topics[1 and above]`): are the `indexed` arguments of the event. In our <a href="#ed">event declaration</a>,  only the first two arguments are `indexed`.
 
Does that mean `0x4dc8f417d4eb731d179a0f08b1feaf25216cefd0` and `0xd2b2fb39b10cd50cab7aa8e834879069ab1a8d4` are topic[1] and topic[2] respectively? **No!**

Why? An ethereum address is 20 byte which is 44 characters long but a `topic` is 32 byte which is 62 characters long.
To convert a 20 byte string to 32 byte, I wrote a line javascript statement that pads the 44 characters to 62 characters appropriately

```javascript
  const my32byteString = `${web3.utils.padLeft('0x', 24)}${walletAddress.split('0x')[1]}`;
```

Hence, our topic[1] and topic[2] are 
`0x0000000000000000000000004dc8f417d4eb731d179a0f08b1feaf25216cefd0` and `0x0000000000000000000000000d2b2fb39b10cd50cab7aa8e834879069ab1a8d4` respectively.<br><br>

### One more Example
We have a smart contract for investment that emits `Invested` event whenever an investment has been made

```solidity
contract Business {
    // ... other business logic

  event Invested(address indexed who, address indexed into, unit256 amount);

  function makeInvestment(address into, uint256 amount) public  {
     //...some investment logic
     emit Invested(msg.sender, into, amount);
  };
}
```

<br>
<span>1. </span> Generate the first event signature(`topic[0]`) of `Invested` event

Solution:
```javascript
const eventSignature = web3.utils.sha3('Invested(address,address,uint256)');
```
<br>
<span>2. </span> Generate the `topics` of an `Invested` event made from/by `0x9d1a9d53bbccf2b2c1a4d3612dbe8b8b9bb4a558`

Solution:
```javascript
const fromAddress = '0x9d1a9d53bbccf2b2c1a4d3612dbe8b8b9bb4a558';
const eventSignature = web3.utils.sha3('Invested(address,address,uint256)');

const topics = [
  eventSignature,
  `${web3.utils.padLeft('0x', 24)}${fromAddress.split('0x')[1]}`,
];
```

<br>
<span>3. </span> Generate the `topics` of an `Invested` event made to `0xE701CD3329057AeA9D54300DdD05e41b8D74727A`

Solution:
```javascript
const toAddress = '0xE701CD3329057AeA9D54300DdD05e41b8D74727A';
const eventSignature = web3.utils.sha3('Invested(address,address,uint256)');

const topics = [
  eventSignature,
  null, // the order is important, this belongs to the first index argument and should be set to null if you want to leave it out
  `${web3.utils.padLeft('0x', 24)}${toAddress.split('0x')[1]}`,
];
```

<br>
<span>4. </span> Generate the `topics` of an `Invested` event made from/by `0x9d1a9d53bbccf2b2c1a4d3612dbe8b8b9bb4a558` to `0xE701CD3329057AeA9D54300DdD05e41b8D74727A`

Solution:
```javascript
const fromAddress = '0x9d1a9d53bbccf2b2c1a4d3612dbe8b8b9bb4a558';
const toAddress = '0xE701CD3329057AeA9D54300DdD05e41b8D74727A';
const eventSignature = web3.utils.sha3('Invested(address,address,uint256)');

const topics = [
  eventSignature,
  `${web3.utils.padLeft('0x', 24)}${fromAddress.split('0x'),
  `${web3.utils.padLeft('0x', 24)}${toAddress.split('0x')[1]}`,
];
```