Trustless token management with Set Protocol

How to integrate token sets in your contracts

With Set Protocol you can create baskets of tokens that give users different levels of exposure to underlying assets (currently only ERC-20 tokens).

Set Protocol and their TokenSet functionality is the perfect example for making use of the new paradigm of Defi and composability. You can let someone else manage your tokens for you, but they cannot steal all your funds. Of course there is some level of trust involved that the manager generally acts in the best interest of every Set participant, but since he receives fees from participants and has no direct monetary gain, the risk is minimized.

Set Dino

So what is Set Protocol?

With the Set Protocol you can enable passive managements for ERC-20 tokens on Ethereum. A given set is managed by one or multiple managers that can employ different strategies based on the functionality added to this set. The system is designed in a modular way allowing you to choose which functionality in the form of modules to add to a set. In the future all of the governance inside Set Protocol will be fully decentralized.

Defi Pulse Set

On the website, you can see existing sets and choose to join them. For example the featured Defi Pulse Index currently gives you exposure to

Let's explore the relevant components of the underlying protocol...

Set Overview

1. The main contract: SetToken

The ERC20 Token contract that allows privileged modules to make modifications to its positions and invoke function calls from the SetToken.

You would create a new SetToken with the SetTokenCreator factory. You can call create and pass

  • a list of ERC-20 addresses (_components)
  • a list of quantities for each given token (_units)
  • a list of modules to be used (_modules)
  • the manager address (_manager)
  • the name of the set (_name)
  • the symbol of the set (_symbol)
function create(
    address[] memory _components,
    int256[] memory _units,
    address[] memory _modules,
    address _manager,
    string memory _name,
    string memory _symbol
) external returns (address)

A concept of virtual and real quantities exist for efficient updating. It's essentially just a multiplier that exists inside the SetToken. A real unit value is calculated as realUnit = positionUnit * positionMultiplier. This would allow updating the multiplier to change the real values of each component very efficiently.

2. Additional data contracts: Resources

Resources are contracts that provide data, functionality, or permissions that can be drawn upon from.

Those include most notably the controller, registry and price oracle.

A. System Controller

The controller is the master manager contract and can be used to

  • Add/Remove Modules
  • Add/Remove Factories
  • Add/Remove Resources
  • Remove SetTokens
  • Add/Edit/Change Fees
  • Edit Fee Recipient

The owner of the controller will eventually be a DAO, thus allowing decentralized governance of sets. Currently it's still a multi-sig contract.

B. Integration Registry

The IntegrationRegistry is used to manage external integrations, like support for the Aave system or an external DEX.

Governance can use this to:

  • Add/Edit/Remove Integrations

C. Price Oracle

The PriceOracle returns a price for a given token pair. The price is retrieved either directly from an oracle, calculated using common asset pairs, or retrieved from external data.

The governance is allowed to

  • Add/Edit/Remove Oracles and Adapters


3. Modules

Modules can be added to a SetToken and enable extra functionality. Th modules are optional additions to a SetToken and can be added or removed from a set by the manager.

Let's take a look at three common modules.

A. Basic Issuance

The Basic Issuance module allows users to mint (issue) and redeem a SetToken. The basic flow for this module is:

For each component inside the _setToken, do

  1. Retrieve ERC-20 address.
  2. Retrieve required quantity given the passed _quantity.
  3. Transfer quantity of given ERC-20 to the SetToken address from sender.

Lastly mint _quantity SetToken tokens to the passed _to address.

The respective redeem function works similarly the other way around and burns the SetTokens again.

function issue(
    ISetToken _setToken,
    uint256 _quantity,
    address _to
) external;
function redeem(
    ISetToken _setToken,
    uint256 _quantity,
    address _to
) external;

B. Trade

The Trade module adds powerful trading functionality to a Set. However you will have to write your own integration for whichever market you want to trade on or you can use the existing Kyber integration.

The trade function can be called to execute a trade for a given SetToken. You will essentially sell the given sendToken in the sendQuantity for the receiveToken. Obviously the token you want to sell must actually exist in the SetToken in sufficient quantity.

Only the token manager may call the trade function.

If you want to write your own DEX integration, have a look at the KyberExchangeAdapter.

function trade(
    ISetToken _setToken,
    string memory _exchangeName,
    address _sendToken,
    uint256 _sendQuantity,
    address _receiveToken,
    uint256 _minReceiveQuantity,
    bytes memory _data
)

C. Tokenset Governance

Tokens these days are commonly used for governance as well, just remember when we look at the COMP governance. This is exactly what you can do with the Governance Module. Intended governance protocols are Compound, Uniswap and Maker, but theoretically any system that complies to the same functions could be used here.

The functionality includes

  • delegate: Delegate voting power.
  • propose: Create a new proposal.
  • vote: Cast a vote on a proposal.

For more details on those functions and governance, take a look at the above mentioned COMP governance article.

Once again only the manager of the TokenSet may call any of those functions.

4. Rebalancing Sets

One interesting feature of sets is rebalancing. Usually with any assets you have the ratio of exposure you get from them can change dramatically over time given the price changes. Just imagine you have three tokens of projects you really like. Let's call them SuperCoolToken, SuperInterestingToken and SuperUsefulToken. You think all projects at the time of investing are valued at equally good prices, so you choose an even split of 33%.

Now you create a TokenSet with

  • 33% SuperCoolToken
  • 33% SuperInterestingToken
  • 33% SuperUsefulToken


Now after a few months, SuperCoolToken mooned and went 10x, while SuperInterestingToken stayed the same and SuperUsefulToken actually went down by 50%. Your ratio now would be

  • 87% SuperCoolToken
  • 9% SuperInterestingToken
  • 4% SuperUsefulToken

But you might still think all projects are still equally promising. Or you just want a more diversified exposure. In this case, wouldn't it be useful to have a mechanism automatically rebalance it to the original ratio?

It's explained in further details inside the Whitepaper. However, I'm curious as to what the current state for this is.

According to the Whitepaper the rebalancing works either via automated trading on DEX's or a system internal dutch auction. I can already see a rebalance overview site on https://www.tokensets.com/rebalance which is in fact listing auctions. However I could not find any smart contract code in the repository which contains the auction code. So if you know what's going on, please let me know.

There is however one module called SingleIndexModule which can be used for rebalancing functionality. It doesn't start auctions, but uses the defined DEX's to initiate a rebalancing.

There you have it, I find Set Protocol a fascinating idea and looking forward to where this develops in the future. Have you used Set Protocol before as a user? Or have you even interacted with the contracts directly? Let me know in the comments below.


Markus Waas

Solidity Developer

More great blog posts from Markus Waas

  • Zeppelin

    Openzeppelin Contracts v4 in Review

    Taking a look at the new Openzeppelin v4 Release

    The Openzeppelin v4 contracts are now available in Beta and most notably come with Solidity 0.8 support. For older compiler versions, you'll need to stick with the older contract versions. The beta tag means there still might be small breaking changes coming for the final v4 version, but you can...

  • Loan

    EIP-3156: Creating a standard for Flash Loans

    A new standard for flash loans unifying the interface + wrappers for existing ecosystems

    As we've discussed last week, flash loans are a commonly used pattern for hacks. But what exactly are they and how are they implemented in the contracts? As of right now each protocol has its own way of implementing flash loans. With EIP-3156 we will get a standardized interface. The standard was...

  • Zero

    Tornado.cash: A story of anonymity and zk-SNARKs

    What is Tornado.cash, how to use it and the future

    With the recent Yearn vault v1 hack from just a few days ago, we can see a new pattern of hacks emerging: Get anonymous ETH via tornado.cash . Use the ETH to pay for the hack transaction(s). Use a flash loan to decrease capital requirements. Create some imbalances given the large capital and...