In this thread I describe a concept of Superfluid Tokens. “Very Liquid” Tokens, which you can “stake” in some protocols, while not moving them from your wallet. This idea allows building a Sybil-resistant voting system (and other software with similar requirements) that does not require users to lock their tokens to obtain voting power. The concept can be cost-efficiently implemented in any language and can be used to extend any existing token, both: fungible and non-fungible.
This idea is based on a hackernoon post I made back in the days. The “Superfluid” prefix is borrowed from physics, where it describes a state of matter when a liquid can flow with zero viscosity, therefore not losing any kinetic energy it has.
Problem definition
Staking locks liquidity.
Sometimes it is a good thing and is necessary - when staked tokens are used for something, while they are locked. For example, when you stake your tokens to a liquidity pool (like in ICPSwap or Sonic). When you do that, your stake is used by other people who can exchange tokens from one to another because of that. Another example - bank deposits. You deposit your money into the bank, which uses them to issue loans to other people.
But sometimes it is not necessary. In my opinion, staking as a mean to obtain voting power in DAOs (like NNS) is not necessary - the money are just laying around doing nothing.
Q: I do not agree, mate, those money create yield.
A: But this yield is synthetic, it is not a profit the protocol made because of your stake. It is just a freshly minted reward for you holding the token and participating in the protocol. Those tokens weren’t used to invest into something, or to issue a loan to someone, or to provide liquidity so other people could exchange. They are just sitting there doing nothing.
There is a category of solutions to this situation called “Liquid Staking” (like WaterNeuron). They work by issuing you a twin-token in exchange for your stake. This twin-token is luqiud - you can use it to do other things, like paying for stuff online. But they inherit the same problem - when you stake this twin-token one more time it is no longer liquid. Plus, this twin-token is physically a different asset - it’s price will inevitably differ from the price of the original one, because this is how exchanges work. And last but not least, I personally hate when you have 20 different incarnations of the same asset in your wallet.
Finding the root cause
The need for staking in DAOs is purely technical - by forcing users to lock their tokens in a provable way, the protocol becomes Sybil-resistant.
Because in a simplified scenario all we need from a user is to prove they hold some amount of tokens right now. Based on this number, we could calculate their share and add it to the proposal as voting power, when they cast a vote.
We could do that in a naive way - when a user casts a vote, call icrc1_balance_of
method of the ledger canister and use the result as voting power. But in this case, our system can be easily attacked by simply casting a vote, sending the tokens to another account and casting the vote again, repeating as many times as needed.
With staking we could check the balance of the stake and the timestamp when the tokens were locked. And if the stake was locked before creation of the poll, we could safely apply its voting power, because we know for sure that those are not freshly transferred tokens. So, we actually need staking, because it freezes tokens in time.
The solution
Can we implement something else that will give us timestamped balance infromation and therefore allow us to get rid of staking in votings? Of course we can! In analogy to transaction history canisters, we could implement a balance history canister, which would contain information about balance updates of each account over time and alongside the total supply history.
Q: Why would that work the same way as staking? This is just a balance history.
A: This is just boring (and at the same time exciting) logic. When I stake tokens with a traditional staking mechanism, it’s like I put my money in a time capsule. You can verify I still have them on me by checking the seal on the capsule, and once this seal is broken, I can no longer prove to you that I didn’t spend any of it. But a balance history canister contains all the time. It is like if you could watch me (and everyone else) from a satelite 24/7 - you would know for sure how much money do I have on me right now.
This canister would provide APIs to efficiently fetch account balance and the corresponding total supply at some particular point in time.
In practice
Let’s suppose we have a token with such a balance history canister attached. How do we use it for votings?
The simplest possible (and not very efficient) example would look like this. We have a canister that allows us to create a poll and then cast a vote. When we create a poll, the canister calls to the balance history canister and fetches the current total supply. When a user casts a vote, we use the same timestamp to fetch their balance from the balance history canister. This response we just interpret as user’s voting power and cast the vote.
Q: The voting happens in the context of some dedicated voting dapp. Does this mean that I have to hold my tokens on an account dedicated to that dapp in order to vote? How it is different from staking, if I can’t get my tokens out of one dapp anyway?
A: While this is true, it is not as bad as it might seem.
First of all, you need to have your tokens in the voting dapp only right before the voting is created. Once it is done, you can transfer it wherever you want and only return them back right before the next voting. They are completely liquid.
Second, wallets (and other dapps) could provide various APIs that would allow you to prove posession of the principal you’re using to manage the superfluid token. With this proof you can use the voting system while holding them in your wallet or other dapp.
So, this way, you stay liquid, while still proving you’re holding the token. This solution is completely different from traditional Liquid Staking solutions, simply because the Superfluid token stays the same after you “stake” it - it has the same value and same properties and capabilities as before staking. There is no price difference. And you can stake it in as many compatible protocols as you want, while still being able to spend it whenever you want.
There are other use-cases which can benefit from Superfluid tokens. For example dividends distribution - you can use a Superfluid asset, while still being able to receive dividends for holding it. The only addition here is that dividends distribution would have to probably happen at some specified moments in time, so there is a timestamp that can be used to fetch balance history canister.
Implementation tips
1 - Inter-canster calls optimisation
Dapps supporting superfluid tokens would especially benefit from user-orchestrated inter-canister calls. When casting a vote, users could fetch their own balance from the balance history canister via an update call and pass the certificate obtained from that call to the voting canister, which will verify it and save itself one inter-canister call.
2 - We don’t need full history
Unlike transaction history, there is no need in storing full balance history all the time. Votings rarely last longer than a month. So records older than two months are probably useless. This obviously depends on the particular use-case, but a sliding window pattern can be very helpful here.
3 - Making existing tokens superfluid is simple
All you need to do is to deploy the balance history canister and then make your Ledger
canister periodically dump balances into that canister. It should be possible to update this way almost any token canister without introducing any breaking change.
4 - This can be standartized
So we could all benefit from sharing the interface.
Outro
Thanks for reading this far, you’re awesome!
Share your feedback on this concept and take care!