-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal for Streaming Token Standard #2100
Comments
Details I'm not yet so sure about: a) b) c) Should d) Should |
we have a video showing an example of this streams here: https://www.youtube.com/watch?v=TqVXTohUEpI. source code for the 2 raspbery pi:
|
There has been no activity on this issue for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. |
This issue was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment. |
Hey @d10r , is there any update on this? Any usage? Real life conclusions? |
Hi @radeksvarz, thx for your interest! |
Locking, as discussion threads should be migrated to FEM. |
Simple Summary
A standard interface for Streaming Tokens.
Abstract
The following standard extends the ERC20 Token Standard.
It adds an interface which allows tokens to be streamed between accounts.
Motivation
Atomic value transfers are not the ideal tool for economic transactions which are long lived.
Ethereum allows us to implement cheap continuous value transfers by leveraging block timestamps.
The interface proposed here offers an alternative to ERC-1620 for several reasons:
Specification
Preliminary definitions
A stream defines a directed relation between two accounts (sender and receiver).
A stream can be opened by the sender account and closed by the sender or the receiver account.
Once closed, a stream ceases to exist and can't be re-opened.
The stream balance is the amount of tokens which were transferred through a stream up until a given point in time.
The stream balance is defined by an implementation specific function which must be monotonically increasing in time.
now refers to the point in time defined by the block timestamp of the last known block.
Interface
openStream
flowrate is the target amount of tokens per second to be transferred. If set to a non-zero value, the average flowrate of a stream over its lifetime (TODO: clarify that this refers to any possible time range from stream opening until an arbitrary later point in time?) must never exceed this value.
If set to 0, the sender doesn't pose an upper limit.
maxAmount is the maximum amount of tokens which can be transferred through a stream overall. Once that amount is reached, the stream balance can't change anymore and the stream loses its utility.
(TODO: add a method
modifyStream
which allows to increase the limit?).If set to 0, the sender doesn't pose an upper limit.
Returns streamId which uniquely identifies the opened stream and emits an event StreamOpened.
Throws if a stream with the given parameters couldn't be opened.
canOpenStream
Implementations are free to impose constraints on which accounts can be stream senders or stream receivers, which combinations of sender/receiver are possible, and which values are accepted for flowrate and maxAmount.
This constant method allows to check beforehand if a stream with a given set of parameters could be opened, given the current state of the contract.
The return value of type CanOpenResult returns OK if such a stream could be opened, and an error code if not. The possible error codes are:
getStreamInfo
This constant method returns some metadata and the current balance of an open stream identified by the given streamId.
Throws if no such open stream exists.
On success, it returns multiple values:
TODO: split into getStreamMetadata and getStreamBalance instead?
closeStream
Closes the stream defined by streamId.
At least the sender and receiver account have permission to close a stream at any time.
Emits an event StreamClosed where transferredAmount is the amount of tokens transferred through the stream and outstandingAmount is the amount of tokens which would additionally have been transferred, had the sender had enough funds.
Emits an ERC20 compliant event Transfer with the additional field transferType set to TransferType.STREAM.
Rationale
This standard is specifically NOT about replicating the behaviour of payment channels on-chain.
It is designed for implementations which allow for continuous transfers which don't require any settle-/withdraw-type transactions.
Challenges
The fundamental difficulty for implementations is to deal with possible underflow situations - that is, streams not transferring at their designated capacity due to insufficient funds of the sender account. In order to not allow streams to issue additional tokens out of thin air in such underflow situations, calculating the stream balance always requires knowledge about the balance of the sender account - which in turn may depend on the balance of streams it's currently receiving, etc. This can lead to potentially infinite recursion depths.
While it's possible to correctly implement this, the complexity budget of a transaction - hard limited by the block gas limit - forces implementations to define constraints which prevent the contract from reaching states at which it can get stuck, e.g. due to recursions hitting the stack limit of the EVM.
There's several ways how this can be prevented, e.g. by allowing accounts to not be sender and receiver of streams at the same time, or by limiting the recursion depth.
In order to reason about the complexity of calculating the stream balance, it's useful to model the state of a Streaming Token as a directed graph where nodes represent accounts and edges represent open streams.
Genericity
The optimal choice of constraints for safe complexity limits depends on the intended applications. The chosen interface thus doesn't make any assumptions about those and is instead kept generic and as simple as possible.
Implementations are however free to extend the interface with additional methods, e.g. for batch operations.
Implicit State Updates
Block timestamps (BT) are the core building block which make this kind of Streaming Token possible in the first place.
They change with every new block and their correctness is guaranteed by the consensus protocol. More specifically, the block timestamps are guaranteed to be strictly increasing (see its definition in the yellow paper). The formal specification doesn't mention a limit to the maximum allowed drift from the current system time of a node, but implementations do impose such a limit (TODO: check and link).
Since the EVM allows to read the timestamp value of the last block (in Solidity, that is done with block.timestamp), the block timestamp can effectively be used like a state variable for the current time which is updated for free with every block.
Since we can rely on the time never going backwards and since this specification mandates the stream balance to be a monotonically increasing function in time, we can construct contracts with implicit balance updates which don't depend on settle/withdraw-type transactions.
Flowrate
Typical implementations will have a linear stream balance function. In this case, the actual flowrate will be constant and equal to the value of the flowrate parameter, as long as the sender has funds.
Other implementations may have non-linear stream balance functions or even functions with dynamic parameters (e.g. a streaming exchange driven by a price oracle).
If an implementation accepts a non-zero value for the flowrate parameter, it has to guarantee that the average flowrate never exceeds that value.
There is also types of implementations which require an unlimited flowrate (value of the flowrate parameter set to zero), e.g. a stream based splitter which causes incoming tokens (no matter if they are received through an atomic transfer or through a stream) to immediately being forwarded to a defined set of receiver accounts.
ERC20 compatibility
While all of the ERC20 interface can be adopted / extended by this standard, the implementations of the ERC20 methods needs to be considerably different.
Most importantly, the method
balanceOf
of a streamable token can't be a simple mapping lookup, as is usually the case. Instead, it needs to calculate the current balance based on a more complex set of state variables, which may also involve recursions.Some applications may assume that balance changes can take place only in the presence of a Transfer log event. This can result in an outdated account balance being shown.
The constant method balanceOf however always returns the correct balance. In order to always show the correct balance, applications monitoring the token balance of an account should call this method for every new block - as many (most?) are already doing anyway.
In order to avoid applications relying on Transfer events alone for token balance updates (e.g. Explorers) to permanently show wrong values, a Transfer event is emitted when streams are closed.
In order to allow ERC2100 aware applications to easily distinguish between Transfer events triggered by atomic transfers from those triggered by closed streams, an additional field transferType is set.
Overall, the interface makes sure that existing applications using the ERC20 interface can use ERC2100 tokens just like any other ERC20 token, without any guarantees specified by the ERC20 standard being violated.
The most likely cause for troubles may be applications using hardcoded gas limits, assuming all ERC20 implementations to look basically the same. That's why implementations of this standard should try to keep gas requirements as low as possible, especially for the ERC20 methods.
Implementations
Example implementations can be found here: https://github.com/lab10-coop/streaming-token-contracts
Copyright
Copyright and related rights waived via CC0.
The text was updated successfully, but these errors were encountered: