Plugin Development
Introduction
Algebra introduces plugins, custom smart contracts connected to a pool, allowing developers to inject custom logic into pool lifecycle events (e.g., swaps, liquidity provision). Plugins enable advanced features like dynamic fees, limit orders, TWAP oracles, and more.
In this guide we are going to walk through the process of creating and testing Algebra Plugin. As an example, let's take a plugin that determines the commission in a pool
Which questions we will answer:
How to start writing a plugin
How the pool interacts with a plugin
What are the flags
How to earn fees with a plugin
Setup
Algebra provides a repository template with required dependencies, configs and examples of source code and tests.
Clone this repository to setup a project:
Basic things
Let's create our plugin DynamicFeePlugin.sol by starting with this code:
Above code snippet does the following:
Imports Plugins library and BasePlugin abstract contract
Sets
defaultPluginConfig
with before_swap, before_position_modify, dynamic_fee flagsInitialises BasePlugin's constructor by providing pool's and plugin factory's addresses
Setting such flags means that we would like a pool to call beforeSwap, beforeModifyPosition hooks and that our plugin is going to manipulate a pool's fees. We chose these particular hooks because we would like to demonstrate how to alter fees on swap and burn interactions.
Pool's and plugin factory's addresses are going to be passed when the plugin is deployed by plugin factory.
Now we can proceed to adding HOOOKS!!!
Firstly, we should add beforeInitialize hook handler. This one is going to be called when the pool is initialized. In that handler we want to set plugin config (flags) in the pool. Primarily, this is done to limit hooks which are called on the plugin to save gas.
Then, the same way we are doing with all other hooks that we would not like to process:
Implied, that these hook would not be called at all. But if for some reason the config is set wrong in a pool. We should set config in a pool to our defaultPluginConfig, disabling unwanted hooks.
Custom Logic
Authorization
Finally, we have implemented everything needed to proceed to our custom logic. Although, if we try to compile the code, we will get an error:
This is because we have to implement authorization mechanism in our plugin. Authorization is necessary since it determines who is able to collect plugin fees inside BasePlugin. Additionally we are going to need authorization for persmissioned actions with our plugin. Algebra suggests such implementation:
Such implementation will not allow anyone except plugin factory to call authorised functions. Thus as a plugin developer you should add needed functionality to a plugin factory to be able to call these functions later. Or you can implement authorization the other way, for instance like that:
The above one requires a caller to be the owner of plugin factory (it should adhere to Ownable ). After all, it's up to you how to implement authorization mechanism in plugin, but it has to be secure.
Custom variables and setters
Proceeding to a custom logic, as a reminder, we would like to develop a plugin which manipulates a pool's fee. Let's introduce some variables inside our plugin:
Above, we added overrideFee
and pluginFee
which are going to be used later. overrideFee
is going to define LP's fee. pluginFee
is going to define fee that belongs to a plugin. A total fee of a swap would be a sum of overrideFee
and pluginFee
. You should also consider that plugin will probably not receive the exact fee amount defined by pluginFee
. Because in reality communityFee
(protocol fee) is going to be deducted from calculated plugin fee amount. As well as from LP's fee.
Since we now have some variables, we should add setters to them in case we would want to change their values:
The code above is self-explainatory with a little addition in the form of our previously implemented _authorize() function.
Hooks
Now we are ready to define a logic of handling beforeSwap and beforeModifyPosition. Let's start with beforeSwap:
beforeSwap hook has to return: it's selector, override fee, plugin fee. For illustration purposes, we implemented the hook in a way that:
if someone swaps in a
zeroToOne
direction, plugin sets LP fee tooverrideFee / 2
if someone swaps in a
oneToZero
direction, plugin sets LP fee tooverrideFee
If you would not like to manipulate LP fee at all in your plugin, it just has to return 0 as overrideFee.
Further, since we want to charge plugin fee also on burn operations, we have to implement beforeModifyPosition hook:
Here as well the hook returns it's selector and plugin fee. beforeModifyPosition is called on both mint and burn operations, but plugin fee is charged only on burn.
Plugin Fees
BasePlugin provides basic functionality for plugin fees, that could be overridden optionally.
Firstly, it is possible to override handlePluginFee
:
This function in permitted only to a pool and is called every time when plugin fees are transferred to a plugin. Implementation of this handler inside BasePlugin does not do anything with plugin fees, so they are just accrued on its balance.
Secondly, there is an option to override collectPluginFee
:
Considering how handlePluginFee
works by default, BasePlugin provides a functionality to collect plugin fee which is essentially a transfer of any token from a plugin to a provided recipient.
Plugin Factory
Plugin Factories play essential role in Algebra Integral architecture. Since Algebra sticks to a approach with dedicated plugins for each pool, plugin factories deploy plugins for new or existing pools. Depending on a chosen authorisation mechanism in a plugin, plugin factory might be also a managment point of its plugins.
In order to be able to deploy our DynamicFeePlugin, lets create DynamicFeePluginFactory starting with this code:
Breaking down the code above:
Import BasePluginFactory which simplifies the development process
Define
defaultOverrideFee
anddefaultPluginFee
which represent values with which new plugins are going to be deployedInitialize
entryPoint
variable with its value in the constructorInitialises BasePluginFactory's constructor by providing AlgebraCustomPoolEntryPoint address
As we mentioned earlier, plugin factory is not only a main point of deploying custom pools with plugins but also a managment point. Thats why we also have to implement some authorization to it, for example using Ownable by OpenZeppelin:
Now our plugin factory has authorization mechanism and we can proceed to implementing differrent managment functions. Let's start with the ones required by BasePluginFactory:
We implemented:
setTickSpacing
- sets a tick spacing in a provided poolsetPlugin
- sets a plugin in a provided poolsetPluginConfig
- sets plugin config in a provided poolsetFee
- sets fee in a provided pool. DYNAMIC_FEE flag must beFalse
in a particular pool in order to be able to call setFee on this pool.
pool
parameter has to be a pool which is authorized to that factory, thus it can be only the pool which is deployed by the that plugin factory.
As you can see, all the functions above are protected by onlyOwner
modifier.
Now, we can add some custom logic to our plugin factory. In our case we might want change the default parameters (overrideFee
and pluginFee
) used to deploy new plugins. Therefore, let's add a setter:
For simplicity let it be the only one function which sets both parameters. This one is protected by onlyOwner
as well.
Next Steps
Well Done! You have developed your very first Algebra Plugin! The next step is to thoroughly test it. Check out Plugin Testing section for help.
Last updated