Archive post
API update proposal for move from aiohivebot to aiow3
• pibara
Ideas for turning the aiohivebot HIVE-only API into a multi-chain API for aiow3, the multi-chain version od aiohivebot
As you may know, aiohivebot is my ongoing attempt at a robust fault tolerant all-node async python library for writing HIVE bots and HIVE L2 nodes. As I look into adding other chains to the library, the name aiohivebot is becoming a little bit too HIVE centric for comfort, and breaking API changes are needed to allow the everything to work intuitively and smoothly. I don't want any chain to be seen as inferior to any other chain when it comes to the API.
In this post I want to share my first attempt at the API changes I think are needed to turn aiohivebot into **aiow3**, a robust fault tolerant all-node async python library for writing Web 3.0 bots and Web 3.0 L2 nodes, first for HIVE, but in the future for other Web 3.0 chains ass well.
# aiow3
The aiow3 lib (renamed from aiohivebot) will be an async Python library for writing robust Web 3.0 bots and layer 2 Web 3.0 nodes.
The code is currently being ported over in order to adjust the API for the fact that HIVE will later no longer be the only supported chain. The aiow3 lib won't be API compatible with aiohivebot, but the API will be very similar and easy for users to port to.
The API change is still in progress, keep using aiohivebot for now.
## porting aiohivebot code to aiow3
An example, the following aiohivebot code:
```python
import asyncio
from aiohivebot import BaseBot
class MyBot(BaseBot):
"""Example of an aiohivebot python bot without real utility"""
async def vote_operation(self, body):
"""Handler for cote_operation type operations in the HIVE block stream"""
if "voter" in body and "author" in body and "permlink" in body:
content = await self.bridge.get_post(author=body["author"], permlink=body["permlink"])
if content and "is_paidout" in content and content["is_paidout"]:
print("Vote by", body["voter"], "on expired post detected: @" + \
body["author"] + "/" + body["permlink"] )
else:
print("Vote by", body["voter"], "on active post")
pncset = MyBot()
loop = asyncio.get_event_loop()
loop.run_until_complete(pncset.run())
print("Done")
```
In aiow3 will change to
```python
import asyncio
from aiow3 import event, Chain, BaseBot
class MyBot(BaseBot):
"""Example of an aiow3 python bot without real utility"""
@event(l1={Chain.HIVE: ["vote_operation"]})
async def process_vote(self, body):
"""Handler for vote_operation type operations in the HIVE block stream"""
if "voter" in body and "author" in body and "permlink" in body:
content = await self.chains[Chain.HIVE].bridge.get_post(author=body["author"], permlink=body["permlink"])
if content and "is_paidout" in content and content["is_paidout"]:
print("Vote by", body["voter"], "on expired post detected: @" + \
body["author"] + "/" + body["permlink"] )
else:
print("Vote by", body["voter"], "on active post")
pncset = MyBot()
loop = asyncio.get_event_loop()
loop.run_until_complete(pncset.run())
print("Done")
```
Let's walk through the changes
```python
from aiohivebot import BaseBot
```
changes to
```python
from aiow3 import event, Event, Chain, BaseBot
```
The initial line imports BaseBot from aiohivebot. It is obvious we need to import it from aiow3 now, but the second line migh not be that obvious.
The **event** is a decorator that changes the way we register our event handlers, while Chain is an enum that we will need for distinguishing between the chains.
```python
async def vote_operation(self, body):
...
```
This now becomes:
```
@event(l1={Chain.HIVE: ["vote_operation"]})
async def process_vote(self, body):
...
```
Note how the name of the method no longer needs to match the operation. Mapping to both the chain and the operation is now done in the preceding decorator.
Another note: as we can now register a single method to multiple chains, you can add **chain** to the method fingerprint if you need it.
```
@event(l1={Chain.HIVE: ["vote_operation"], Chain.BLURT: ["vote_operation"]})
async def process_vote(self, body, chain):
...
if chain == Chain.BLURT:
# This is just an example, not sure if we want to support Blurt right now.
...
else:
...
```
Argument erasure will continue to work like it did in aiohivebot, so chain is fully optional.
Finaly when calling the API we need to specify which chain now too.
The code
```python
content = await self.bridge.get_post(author=body["author"], permlink=body["permlink"])
```
now becomes
```
content = await self.chains[Chain.HIVE].bridge.get_post(author=body["author"], permlink=body["permlink"])
```
This was an example where there was no constructor, but the aiohivebot BaseBot constructor did have optional constructor arguments and these were partialy chain specific. Given that we will be allowing symultanious use of multiple chains, we need to update this. The aiohivebot vesion of the Basebot has the following constructor arguments:
* start\_block
* eat\_exceptions
* enable\_layer2
* use\_irreversible
* use\_virtual
* maintain\_order
Let's walk throug all of them.
### start\_block
This constructor argument took an integer before. We change this to a dict:
```python
super().__init__(start_block={Chain.HIVE: 79600000})
```
### eat\_exceptions
We are keeping this one global for all chains
### enable\_layer2
Previously this one was string based
```python
super().__init__(enable_layer2=["engine"])
```
This one will no longer be needed, but instead will be infered from decorators just as the activated chains are infered from decorators.
Now instead of:
```
async def engine_market_buy(self, required_auths, required_posting_auths, body, blockno, timestamp):
```
we need to write:
```python
from aiow3 import L2Chain
...
@event(l2={L2Chain.HIVE_ENGINE: ["market_buy"]})
async def engine_market_buy(self, required_auths, required_posting_auths, body, blockno, timestamp):
...
```
### use\_irreversible
This is one again where we use the dict appoach:
```python
super().__init__(use_irreversable={Chain.HIVE: True})
```
### use\_virtual
This is a tough one. virtual operations are probably HIVE specific, and other chains likely have their own non universal switches that would be usefull in the constructor, So we are going to phase this one out in exchange for a chain specific config dict.
```python
super().__init__(chain_conf={Chain.HIVE: {"use_virtual": True}})
```
### maintain\_order
And one last time we use a dict aproach
```python
super().__init__(maintain_order={Chain.HIVE: True})
```