Weekly Update: Implementing ERC-900 for ETF underwriting

Tai Kersten
Proof of FinTech
7 min readJan 10, 2020

--

This week, we did some heavy research on Exchange Traded Products (ETP) with a focus on ETF’s in order to investigate possible extensions to our Structured contract set. Through this research we have garnered a better understanding of this popular product and have been experimenting with how platforms, institutions, and developers can utilize the draft protocol of ERC-900 to create an underwriting mechanism and create managed token funds from custom bundled sets of assets.

With the rise of DeFi derivative platforms such as Synthetix, we thought it would be interesting to see how we could create an end-to-end smart contract system that handles underwriting as staking rights, utilizes oracles to set pricing, and then offer the possibility of the issuance units for trading of the fund (of course this must occur after the ETF or ETP has been appropriately underwritten).

In short, an ETF is a packaging of various assets. It can consist of equities, derivatives, or even other ETF’s. Many ETF’s track various sectors such as indexes, specific industries, or even entire countries! It is an investing product whose volatility sits somewhere between an equity and a hedge fund.

Assets invested in ETF’s and other ETPs reached an all-time high of 4.8T at the end of August last year according to ETFGI. As an asset-class, ETF’s offer a commission-based revenue model for financial advisors as well as sometimes offering a more risk adverse diversified asset addition to a portfolio. An ETF is often issued with a group of underwriters who supply the underlying assets for the fund. Keep in mind this is not always the case.

However, for the sake of this test, we are going to build a decentralized underwriting contract where anyone holding a compatible token can become an ETF underwriter.

ETF’s on blockchains have the potential of offering many new opportunities such as sourcing clients/funds for existing products, creating streamlined approaches for companies to develop and launch new ETF’s, and can act as a more fundamentals-oriented service layer for reviews, price-discovery, data services, ratings, and more.

ETF markets consistently offer new entrants since creating the ETF structure itself requires little more than than basketing regulated securities, having a lot of capital, and having underwriting partners.

Implementing ERC Tokens into ETF’s

This post just contains snippets of the implementation. The full test contracts can be found here.

The ERC-900 draft specification can be found here. Since this is a draft EIP, we are going to be building an illustrative implementation of the spec. None of this code should be used in production without going many steps of iteration utilizing best practices. This specification outlines some of the basic functions and functionality of a Staking Token which can be used as a provenance and locking mechanism for value. In this case, the staking will be used as a mechanism for the underwriting of an ETP.

NOTE: Any code supplied here is more for illustrative purposes, we are planning on using some of these patterns down the line but initially, we are implementing most of this as an illustration and ideation of how the still-draft ERC 900 could be used.

interface Staking {event Staked(address indexed user, uint256 amount, uint256 total, bytes data);event Unstaked(address indexed user, uint256 amount, uint256 total, bytes data);function stake(uint256 amount, bytes data) public;function stakeFor(address user, uint256 amount, bytes data) public;function unstake(uint256 amount, bytes data) public;function totalStakedFor(address addr) public view returns (uint256);function totalStaked() public view returns (uint256);function token() public view returns (address);function supportsHistory() public pure returns (bool);// optionalfunction lastStakedFor(address addr) public view returns (uint256);function totalStakedForAt(address addr, uint256 blockNumber) public view returns (uint256);function totalStakedAt(uint256 blockNumber) public view returns (uint256);}

In this interface, we can see that there are some basic functions that we should expect in a contract called Staking . After some hemming and hawing, we have begun a draft implementation of this spec which looks something like this:

// ...uint256 lockup = 21 days; // Spin out into Variable & Constructor
uint256 __totalStaked;
struct Stake {
uint256 amount;
bytes data;
uint256 stakedOn;
uint256 canUnstakeAfter;
}
mapping(address => mapping(address => Stake)) stakes; // token_address => stakers => Stake
mapping(uint256 => address) tokens; // For example purposes this would be a numerical id
event Staked(address indexed user, uint256 amount, uint256 total, bytes data);
event Unstaked(address indexed user, uint256 amount, uint256 total, bytes data);
modifier MustBeAllowed(uint256 _token_idx, address _sender, address _deligate, uint256 _amount) {
require(IERC20(tokens[_token_idx]).allowance(_sender, _deligate) >= _amount, "This account lacks needed asset.");
_;
}
constructor (address[] memory _tokens) public {
for (uint i=0; i < _tokens.length; i++) {
tokens[i] = _tokens[i];
}
}
/*
* Internal Function for transferring tokens for staking.
*
* @remark Possibly spin this into the controller but for development, keep here
* for convenience
*/
function __transferToken(address _token_addr, address _sender, address _recipient, uint256 _amount) private {
// Allowance must be set
IERC20 token = IERC20(_token_addr);
token.transferFrom(_sender, _recipient, _amount);
}
/*
* Stakes a certain amount of tokens, this MUST transfer the given amount from the user.
*/
function stake(uint256 amount, bytes memory data, uint256 _token_idx) public MustBeAllowed(_token_idx, msg.sender, address(this), amount){
__transferToken(tokens[_token_idx], msg.sender, address(this), amount);
stakes[tokens[_token_idx]][msg.sender] = Stake(amount, data, block.timestamp, block.timestamp.add(lockup));
__totalStaked.add(amount);
emit Staked(msg.sender, amount, __totalStaked, data);
}
/*
* Stakes on behalf of another a certain amount of tokens, this MUST transfer the given amount from the caller.
*/
function stakeFor(address user, uint256 amount, bytes memory data, uint256 _token_idx) public MustBeAllowed(_token_idx, msg.sender, address(this), amount){
__transferToken(tokens[_token_idx], msg.sender, address(this), amount);
stakes[tokens[_token_idx]][user] = Stake(amount, data, block.timestamp, block.timestamp.add(lockup));
__totalStaked.add(amount);
emit Staked(user, amount, __totalStaked, data);
}
/*
* Unstakes a certain amount of tokens, this SHOULD return the given amount of
* tokens to the user, if unstaking is currently not possible the function MUST revert.
*/
function unstake(uint256 amount, bytes memory data, uint256 _token_idx) public {
require(block.timestamp > stakes[tokens[_token_idx]][msg.sender].canUnstakeAfter, "Cannot Unstake Yet.");
__transferToken(tokens[_token_idx], address(this), msg.sender, amount);
__totalStaked.sub(amount);
emit Unstaked(msg.sender, amount, __totalStaked, data);
}
/*
* Returns the current total of tokens staked for an address.
*/
function __totalStakedFor(address addr, uint256 token_idx) public view returns (uint256) {
return stakes[tokens[token_idx]][addr].amount;
}
// ...

This staking contract is then controlled by an ETFFactory contract with this interface:

// ...struct ETF {
string name;
address staking_addr;
address issuer;
uint256 price;
uint256 units;
address units_addr;
}
contract UnderwrittenETFController {
address owner;
mapping (address=>ETF) registry;
constructor() {
owner = msg.sender;
}
function __determineCurrentPrice(_asset) returns (uint256){
// ...
}
function rebalanceETF() {
// ...
}
function createETF(string memory _name, address[] memory _assets, uint256[] memory _initialDistribution) public {
uint256 price = __determineCurrentPrice(_assets, _initialDistribution);
address NewTimedStaker = new MultiTimedTokenStaking(_assets);ETF NewETF = ETF(_name, NewTimedStaker, msg.sender, );
registry[msg.sender] = NewETF;
}
}
// ...

To Do

Currently, as you can already see, this system is incomplete. As we move forward, we are looking to build and for help with a myriad of features and additions:

  • We need a good model for informing unit issuance with the current spread of the ETF, especially in times of low liquidity where spreads can become extremely large.
  • There are many quality-of-life and validation modifiers that need to be implemented.
  • The controller contract is currently in a very early state
  • Price feeds for decentralized pricing need to be implemented
  • Historical functions need to be implemented for reporting
  • Many many more things…

OrFeed & Pricing

OrFeed is an oracle aggregation system being developed by Proof that allows the inserting of of prices into smart contracts from a wide array of sources. For the issuance, pricing, and rebalancing of ETF assets, an oracle solution like OrFeed will be essential.

We envision a world where synthetics and derivatives and stable coins are packaged and unitized based on solid pricing data on both public and private blockchains with data, the assets themselves and their underlying infrastructure controlled by the masses.

Further Development

These contracts are just the beginning. With Token Staking, one could create multi-asset staking systems with Cosmos’s Peggy. Or, even run a plasma chain with a main-net staking system. As these protocols, platforms, and their liquidity continue to expand, the demand for stable, well executed, automated assets will also grow. That said, for traditional financial institutions, the demand for public-protocol blockchain assets is probably going to grow much more slowly.

On the institutional side, contracts such as these could be used on a system running an EVM compatible private-chain for consortium-based issuance and underwriting. As these expand, we are working to help nurture an environment of collaboration between both the open-source and private-chain protocols.

What This Means for Proof

With the approval of the team and community, we will be adding this system to our Structured product as well as investigating ways of taking this work and applying to other ERC-20 tokens. As we learn more about the hurdles and benefits of ERC staking, we will be looking at integrating this standard into the the future DAO’s and products that currently utilize smart contract systems. For now, the best thing we think we can do is make sure we integrate as much of these developments and fruits of our research into our own platforms.

--

--