Skip to content

Commit

Permalink
Merge pull request #43 from tradersnow222/patch-2
Browse files Browse the repository at this point in the history
Update README.md
  • Loading branch information
galekseev authored Oct 9, 2024
2 parents 5e352c6 + 7e9bd7e commit 48d0c29
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 45 deletions.
135 changes: 90 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,105 @@
# Token Plugins

[![Build Status](https://github.com/1inch/token-plugins/workflows/CI/badge.svg)](https://github.com/1inch/token-plugins/actions)
[![Coverage Status](https://codecov.io/gh/1inch/token-plugins/branch/master/graph/badge.svg?token=Z3D5O3XUYV)](https://codecov.io/gh/1inch/token-plugins)
[![NPM Package](https://img.shields.io/npm/v/@1inch/token-plugins.svg)](https://www.npmjs.org/package/@1inch/token-plugins)

# 1inch Token Plugins: A Comprehensive Guide for Extending ERC20 Functionalities

[Overview](#overview)

[Primary Benefits](#primary-benefits)

[Implementation](#implementation)

[Generic Examples](#generic-examples)

[Deployed Examples](#deployed-examples)

[Helpful Links](#other-helpful-links)

## Overview
Token plugins are smart contracts that extend the capabilities of ERC20 tokens and wrappers by adding custom accounting features to the original token. Inspired by the plugin concept widely used in the web 2.0 world, these plugins enable users to dynamically increase the functionality of their tokens without the need to transfer tokens to a special smart contract.

This library introduces an extendable and secure system for ERC20-based tokens, inspired by the plugin concept widely used in the web 2.0 world. By subscribing an account to various plugins, users can dynamically enhance the functionality of their tokens.
The major benefit, and a key difference from existing solutions, is that these do not require token transfers to a special smart contract, as is commonly seen in farming or delegating protocols. Another beneficial point is that once an ERC20 plugin code is deployed, it can be reused by any tokens that support the 1inch plugin standard.

Technically plugins are a collection of smart contracts that track changes in ERC20 token balances and perform supplementary accounting tasks for those balances. They are particularly useful when you need to track, for example, token shares without actually transferring tokens to an accounting contract.
Support for plugins on the token side is similar to the implementation of classic ERC20 extensions (i.e., OpenZeppelin ERC20 extensions). The deployment and usage are permissionless from the perspective of a token contract owner, since the holder is the actor who decides which plugin to subscribe to.

## How does it work and what can I extend?
Technically, plugins are a collection of smart contracts that track changes in ERC20 token balances and perform supplementary accounting tasks for those balances. They are particularly useful when you need to track, for example, token shares without actually transferring tokens to a dedicated accounting contract.

Each time a balance of a connected account changes, the token contract notifies all plugins that the account has subscribed to. Each plugin implements the `_updateBalances` function, which is called on mint, burn and transfer, and includes change information like from and to addresses and amount. This way a plugin can perform necessary actions according to its logic to extend the base token functionality.
The token plugins standard is designed to be secure and to prevent asset loss, gas, and DoS attacks on transfers.

Keep in mind that the plugin's processing logic consumes additional gas, and operations that change an account balance will be more expensive in terms of gas. To reduce the gas impact the library implements a limit on the amount of gas that a plugin can spend and an account owner can choose which plugins to subscribe his account to.
***Note: ERC721 (NFT) and ERC1155 (Multi-token) support is coming soon!***

<img src='./src/img/scheme-n1.png' width='320' height='396'>
## Primary Benefits
- **100% permissionless from the token contract owner**: Open to all participants.
- **Risk-free participation**: Token plugins do not require any approval, deposit, or transfer of funds into an external contract for participation.
- **Multiple plugin connections**: Users can connect with multiple plugins, allowing for simultaneous involvement in multiple incentive programs or governance systems, etc. (subject to a predefined limit, set at deployment).
- **Simple to adopt**: Implementation is only 150 lines of code.
- **High security**: 1inch Token Plugins have gone through extensive [audits](https://github.com/1inch/1inch-audits/tree/master/Fusion%20mode%20and%20Token-plugins) by multiple top-tier companies.
- **Built-in reentrancy protection**: This feature ensures that the balances cannot be tampered with by manipulating plugin accounting.
- **Custom ERC20 representation**: A plugin can be represented by its own associated ERC20 (custom inheritance), enabling building complex and multi-layered accounting systems like 1inch Fusion.

Picture 1. Possible interactions
## Use-Cases
Here are some examples of how Token Plugins is currently being (or could be used) today:

## Implementation examples
- **st1INCH resolver delegation**
Through staking 1INCH, token holders receive Unicorn Power (UP), and can earn rewards from Resolvers in the Intent Swap system. In order to earn these rewards, the UP received from staking can be delegated (see contract) to a specific Resolver. The resolver is incentivized to have UP delegated to them, so they will reward delegators with some amount of funds. The delegation of st1INCH is done with a token plugin, so there is no need to transfer the tokens to another contract. ([see dst1inch contract](https://etherscan.io/token/0xAccfAc2339e16DC80c50d2fa81b5c2B049B4f947#code))

The examples of already implemented token-plugins
- **Weighted voting power**
VE governance models like veCRV require the user to lock tokens for a certain amount of time to earn voting rights. This signals to the protocol a long-term vested interest and greatly reduces the surface area for governance attacks. With Token Plugins, the VE token model can be replaced with logic that gives the wallet ramping voting power by simply holding the base governance token for long periods of time. When a wallet first holds the governance token, its voting power will be nearly zero, but over time (e.g. 2 years), it will increase until it reaches a set maximum.

- [FarmingPlugin](https://github.com/1inch/farming) - *The 1inch staking reward farming is based on this plugin.*
This plugin introduces farming without the need for asset transfer or locking on a farm. An account owner can join the farm by simply adding this plugin to their account. The plugin handles all necessary accounting for joined farmers and distributes rewards based on the actual farming token balance of the account during the farming period. This is particularly useful for non-transferable tokens, for example such as debt tokens in lending protocols.
- [DelegatingPlugin](https://github.com/1inch/delegating) - *The 1inch fusion mode resolver delegation is based on this plugin.*
This plugin allows an account balance to be delegated to another address. This can be beneficial for governance tokens if an account owner wants to delegate their voting power to another account without a physical token transfer. The owner can recall or redelegate their delegation at any time without locking their tokens in a governance contract.
- **LP-Token farming**
Some protocols incentivize LP token holders with additional rewards beyond swap fees through an additional yield contract that holds the LP tokens and distributes the rewards proportionally to the participating LPs. With token plugins, these extra rewards for LP holders can continue to be opt-in without the need to deposit those LP tokens into a secondary contract. ([See 1inch Fusion pods](https://etherscan.io/address/0x1A87c0F9CCA2f0926A155640e8958a8A6B0260bE#code))

## Security and limitations
- **Shadow staking**
If a protocol wanted to simply reward holders of their token, they could reward them similarly to the weighted voting power method, but instead of increasing voting power over time, the APR of holding the token can increase. Long-term holders will receive rewards and short-term holders/traders would not receive the same benefit.

The plugin system operates under the assumption that an account may subscribe to a malicious plugin. That is the reason why the following restrictions apply:
- **Borrow/lending rewards**
In traditional lending protocols, users must transfer assets and hold both lending and debt tokens in their wallets, limiting farming opportunities. With 1inch Token Plugins, users are able to maintain custody of their assets while a plugin tracks balances, distributing rewards seamlessly and securely without ever having to move the assets.

- If a plugin fails its execution and reverts, it won't impact the main flow. The failed plugin is bypassed, and execution continues.
- Each plugin has a gas limit set by the parent contract. If there is insufficient gas, it won't affect the main flow's execution.
- An account can have a limited number of plugins connected, set by host contract.
- Plugins are executed in the context specified in their contract (parent contract uses a call, not delegatecall).
- Plugins cannot alter the calling contract state
- Plugins cannot be reentered
## Limitations
- Any plugin's processing logic consumes additional gas, with external operations that change an account balance incurring higher costs. To mitigate this, the plugin extension sets a limit on the gas consumption per plugin and caps the maximum amount of gas that can be spent.
- **Plugin Quantity**: The contract deployer should establish a limit on the number of plugins managed under the plugin management contract.
- **Maximum gas usage**: The plugin management contract limits the amount of gas any plugin can use to avoid overspent and gas attacks. It is highly recommended not to change beyond the recommended amount of 140,000.
- **Only works with transferrable tokens**: By fundamental design, plugins are unable to integrate with tokens whose balances can update without transfers (such as rebase tokens).

## Integrating plugin support in your token implementation
## Implementation

To integrate plugins in a smart contract, a "mothership" or parent contract have to be used to manage all related pods. This includes adding, removing, and viewing pods, as well as connecting multiple pods. The parent contract calls the `updateBalance` function for each pod on every update of an account’s balance. The pod then executes its logic based on the updated balance information. And then an account has to connect plugin to the account to utilize its logic.
![ERC20Plugins](/src/img/PluginsDiagram.png)

1. Inherit token: `contract MyToken is ERC20Plugins { ... }`

Or wrap it: `contract MyWrapper is ERC20Wrapper, ERC20Plugins { ... }`

This will add support for the plugin infrastructure.

2. Wallets can plugin: `MyToken.addPlugin(plugin)`, where `plugin` is address of your and third-party deployed plugin.
3. Now every time wallet balance is change the plugin will know about it
Connecting a token contract with the 1inch Token Plugins is a straightforward process. If you’re creating a brand new token contract or migrating an existing one, you can simply inherit from the plugin-enabled ERC20 contract OR wrap an existing token and inherit plugin functionality within the wrapper (`contract MyWrapper is ERC20Wrapper, ERC20Plugins { ... }`). Subsequently, any plugin (deployed as a separate contract) can be connected to your plugin-enabled ERC20, enabling it to track balance updates of the underlying asset efficiently.

## How to create your own plugin
In other words, 1inch Token Plugins require inheritance from an independent, “plugin-enabled” ERC20 contract, which manages all related dependent plugin contracts. The plugin-enabled ERC20 contract is responsible for calling the `updateBalance` function with every change in an account’s balance.

All plugins will only track the balances of participating accounts. So all non-participants are represented as “0 addresses”. If an account is not participating in a plugin and receives a plugin-enabled token, the `From` and `To` amounts under `_updateBalances` will be represented as 0. Therefore, if a non-participant sends a plugin-enabled token to an existing participant, it will effectively “mint” the tracked balance. If a participant sends a plugin-enabled token to a non-participant, it will effectively “burn” the tracked balance.

![Token Transfers](/src/img/TokenTransferDiagram.png)

To create your own plugin it is necessary to inherit `Plugin` contract and implement its abstract function `_updateBalances`.
For security purposes, plugins are designed with several fail-safes, including a maximum number of usable plugins, custom gas limits, a reentrancy guard, and native isolation from the main contract state. The maximum plugins and gas limit can be initialized as state variables using `MAX_PLUGINS_PER_ACCOUNT` and `PLUGIN_CALL_GAS_LIMIT`, respectively. For reentrancy prevention, `ReentrancyGuardExt` is included from OpenZeppelin’s library. Finally, for native isolation from the token contract, a single method with only three arguments (`To`, `From`, and `Amount`) is used. This simple architecture results in a dynamic (and risk-free!) enhancement of any ERC20 contract’s capabilities.

1. Inherit plugin: `contract MyPlugin is Plugin { ... }`
2. Implement `_updateBalances` function to process wallet balance changes
## Integrating plugin support in your token implementation
To integrate plugins in a smart contract, a "mothership" or parent contract must be used to manage all related plugins. This includes adding, removing, and viewing plugins, as well as connecting multiple plugins. The parent contract calls the `updateBalance` function for each pod on every update of an account’s balance. The pod then executes its logic based on the updated balance information. An account must connect a plugin to utilize its logic.

- **Inherit token**: `contract MyToken is ERC20Plugins { ... }`
- **Or wrap it**: `contract MyWrapper is ERC20Wrapper, ERC20Plugins { ... }`

This will add support for the plugin infrastructure.

- **Wallets can plugin**: `MyToken.addPlugin(plugin)`, where `plugin` is the address of your or a third-party deployed plugin.
- Now every time a wallet balance changes, the plugin will know about it.

## How to create your own plugin
To create your own plugin, it is necessary to inherit the Plugin contract and implement its abstract function `_updateBalances`.

## Examples of token and plugin
- **Inherit plugin**: `contract MyPlugin is Plugin { ... }`
- **Implement _updateBalances** function to process wallet balance changes.

Below is the example of token implementing plugin support and a simple plugin that mints and burns its own token based on the parent’s token balance
## Generic Examples

Below is an example of an ERC20 token implementing plugin support and a simple plugin that mints and burns its own token based on the parent’s token balance.

```solidity
// Simple token contract with plugin support
contract HostTokenExample is ERC20Plugins {
**Simple plugin-enabled token contract**
```
contract NewToken is ERC20Plugins {
constructor(string memory name, string memory symbol, uint256 maxPluginsPerAccount, uint256 pluginCallGasLimit)
ERC20(name, symbol)
ERC20Plugins(maxPluginsPerAccount, pluginCallGasLimit)
Expand All @@ -77,9 +109,10 @@ contract HostTokenExample is ERC20Plugins {
_mint(account, amount);
}
}
// Simple plugin
contract PluginExample is ERC20, Plugin {
```
**Simple plugin contract**
```
contract MyPlugin is ERC20, Plugin {
constructor(string memory name, string memory symbol, IERC20Plugins token_)
ERC20(name, symbol)
Plugin(token_)
Expand All @@ -96,3 +129,15 @@ contract PluginExample is ERC20, Plugin {
}
}
```

## Deployed Examples
- [Plugin-enabled ERC20 contract](https://arbiscan.io/token/0x36a8747fc5F09cDE48e7b8Eb073Ae911b2cBa933#code)
- [Simple Plugin contract](https://arbiscan.io/address/0x7f75495bf9a3f20b253a68a34a152c5f5587a742#code)
- [1inch Fusion (Delegated Staked 1INCH) Plugin Contract](https://etherscan.io/address/0x806d9073136c8A4A3fD21E0e708a9e17C87129e8#code)
- [1inch Fusion Staking Farm](https://etherscan.io/address/0x1A87c0F9CCA2f0926A155640e8958a8A6B0260bE#code)

## Other Helpful Links
- [Plugin-enabled ERC20 Token contract (abstract)](https://github.com/1inch/token-plugins/blob/master/contracts/ERC20Plugins.sol)
- [Plugin contract (abstract)](https://github.com/1inch/token-plugins/blob/master/contracts/Plugin.sol)
- [Anton Bukov speech at ETHCC](https://youtu.be/Is-T5Q2E0A8?feature=shared)
- [Kirill Kuznetcov speech at Nethermind Summit, Istanbul](https://youtu.be/BwehZHhR8Z4?feature=shared)
Binary file added src/img/PluginsDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/img/TokenTransferDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/img/_updateBalances2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 48d0c29

Please sign in to comment.