ethereum / EIPs

The Ethereum Improvement Proposal repository
https://eips.ethereum.org/
Creative Commons Zero v1.0 Universal
12.72k stars 5.16k forks source link

ERC-1620: Money Streaming #1620

Closed PaulRBerg closed 2 years ago

PaulRBerg commented 5 years ago
eip: 1620
title: ERC-1620 Money Streaming
author: Paul Razvan Berg (@PaulRBerg) <paul@sablier.finance>
discussions-to: https://github.com/ethereum/EIPs/issues/1620
status: Final
type: Standards Track
category: ERC
created: 2018-11-24
requires: 20

This proposal has been accepted and merged as a draft standard. Please read up the thread below for discussions leading up to the final version.

Original Draft (Nov 24, 2018) ## Simple Summary Money streaming represents the idea of continuous payments over a finite period of time. Block numbers are used as a proxy of time to continuously update balances. ## Abstract The following describes a standard whereby time is measured using block numbers and streams are mappings in a master contract. 1. A provider sets up a money streaming contract. 2. A prospective payer can interact with the contract and start the stream right away by depositing the funds required for the chosen period. 3. The payee is able to withdraw money from the contract based on its ongoing solvency. That is: `payment rate * (current block height - starting block height)` 4. The stream terms (payment rate, length, metadata) can be updated at any time if both parties pledge their signatures. 5. The stream can be stopped at any point in time by any party without on-chain consensus. 6. If the stream period ended and it was not previously stopped by any party, the payee is entitled to withdraw all the deposited funds. ## Motivation This standardised interface aims to change the way we think about long-term financial commitments. Thanks to blockchains, payments need not be sent in chunks (e.g. monthly salaries), as there is much less overhead in paying-as-you-go. Money as a function of time would better align incentives in a host of scenarios. ### Use Cases This is just a preliminary list of use cases. There are other interesting ideas left to explore, such as time-dependent disincetivisation, but, for brevity, we have not included them here. - Salaries - Subscriptions - Consultancies - Rent - Parking - CDPs ### Crowdsales [RICOs](https://github.com/lukso-network/rico), or Reversible ICOs, were introduced at Devcon4 by @frozeman. The idea is to endow investors with more power and safety guarantees by allowing them to "reverse" the investment based on the evolution of the project. We previously discussed a similar concept called SICOs, or Streamable ICOs, in this research [thread](https://ethresear.ch/t/chronos-a-quirky-application-proposal-for-plasma/2928/14?u=paulrberg). Instead of investing a lump sum and giving the money away to the project developers, funds are held in a smart contract which allocates money based on the passage of time. Project developers can withdraw funds as the stream stays active, while investors have the power to get back a significant percentage of their initial commitment if the project halts. ## Specification ### Structs The structure of a `stream` should be as follows: - `stream` - `sender`: the `address` of the entity funding the stream - `recipient`: the `address` where the money is being delivered to - `tokenAddress`: the `address` of the ERC20 token used as payment asset - `timeframe`: as defined below - `rate`: as defined below - `balance`: the total funds left in the stream ```solidity struct Stream { address sender; address recipient; address tokenAddress; Timeframe timeframe; Rate rate; uint256 balance; } ``` - `timeframe` - `start`: the starting block number of the stream - `stop`: the stopping block number of the stream ```solidity struct Timeframe { uint256 start; uint256 stop; } ``` - `rate` - `payment`: how much money moves from `sender` to `recipient` - `interval`: how often `payment` moves from `sender` to `recipient` ```solidity struct Rate { uint256 payment; uint256 interval; } ``` --- ### Methods #### balanceOf Returns available funds for the given stream id and address. ```solidity function balanceOf(uint256 _streamId, address _addr) ``` #### getStream Returns the full stream data, if the id points to a valid stream. ```solidity function getStream(uint256 _streamId) returns (address sender, address recipient, address tokenAddress, uint256 balance, uint256 startBlock, uint256 stopBlock, uint256 payment, uint256 interval) ``` #### createStream Creates a new stream between `msg.sender` and `_recipient`. MUST allow senders to create multiple streams in parallel. SHOULD not accept Ether and only use ERC20-compatible tokens. **Triggers Event**: [CreateStream](#create-stream) ```solidity function createStream(address _sender, address _recipient, address _tokenAddress, uint256 _startBlock, uint256 _stopBlock, uint256 _payment, uint256 _interval) ``` #### withdrawFromStream Withdraws all or a fraction of the available funds. MUST allow only the recipient to perform this action. **Triggers Event**: [WithdrawFromStream](#withdraw-from-stream) ```solidity function withdrawFromStream(uint256 _streamId, uint256 _funds) ``` #### redeemStream Redeems the stream by distributing the funds to the sender and the recipient. SHOULD allow any party to redeem the stream. **Triggers Event**: [RedeemStream](#redeem-stream) ```solidity function redeemStream(uint256 _streamId) ``` #### confirmUpdate Signals one party's willingness to update the stream SHOULD allow any party to do this but MUST NOT be executed without consent from all involved parties. **Triggers Event**: [ConfirmUpdate](#confirm-update) **Triggers Event**: [ExecuteUpdate](#execute-update) when the last involved party calls this function ```solidity function confirmUpdate(uint256 _streamId, address _tokenAddress, uint256 _stopBlock, uint256 _payment, uint256 _interval) ``` #### revokeUpdate Revokes an update proposed by one of the involved parties. MUST allow any party to do this. **Triggers Event**: [RevokeUpdate](#revoke-update) ```solidity function revokeUpdate(uint256 _streamId, address _tokenAddress, uint256 _stopBlock, uint256 _payment, uint256 _interval) ``` --- ### Events #### CreateStream MUST be triggered when `create` is successfully called. ```solidity event CreateStream(uint256 indexed streamId, address indexed sender, address indexed recipient, address tokenAddress, uint256 startBlock, uint256 stopBlock, uint256 payment, uint256 interval) ``` #### WithdrawFromStream MUST be triggered when `withdraw` is successfully called. ```solidity event WithdrawFromStream(uint256 indexed streamId, address indexed recipient, uint256 amount) ``` #### RedeemStream MUST be triggered when `redeem` is successfully called. ```solidity event RedeemStream(uint256 indexed streamId, address indexed sender, address indexed recipient, uint256 senderAmount, uint256 recipientAmount) ``` #### ConfirmUpdate MUST be triggered when `confirmUpdate` is successfully called. ```solidity event ConfirmUpdate(uint256 indexed streamId, address indexed confirmer, address newTokenAddress, uint256 newStopBlock, uint256 newPayment, uint256 newInterval); ``` #### RevokeUpdate MUST be triggered when `revokeUpdate` is successfully called. ```solidity event RevokeUpdate(uint256 indexed streamId, address indexed revoker, address newTokenAddress, uint256 newStopBlock, uint256 newPayment, uint256 newInterval) ``` #### ExecuteUpdate MUST be triggered when an update is approved by all involved parties. ```solidity event ExecuteUpdate(uint256 indexed streamId, address indexed sender, address indexed recipient, address newTokenAddress, uint256 newStopBlock, uint256 newPayment, uint256 newInterval) ``` ## Rationale This specification was designed to serve as an entry point to the quirky concept of money as a function of time and it is definitely not set in stone. Several other designs, including payment channels and Plasma chains were also considered, but they were eventually deemed dense in assumptions unnecessary for an initial version. Block times are a reasonable, trustless proxy for time on the blockchain. Between 2016 and 2018, the Ethereum block time average value [hovered](https://etherscan.io/chart/blocktime) around 14 seconds, excluding the last two quarters of 2017. Mathematically speaking, it would be ideal to have a standard deviation as close to 0 as possible, but that is not how things work in the real world. This has huge implications on the feasibility of this ERC which we shall investigate below. ### GCD When setting up a stream, a payer and a payee may want to make the total streaming duration a multiple of the "greatest common denominator" (GCD) of the chain they operate on; that is, the average block time. This is not imperative in the smart contracts per se, but there needs to be an off-chain process to map streams to real-world time units in order to create a sound and fair payment mechanism. ### Block Times Because there is uncertainty regarding block times, streams may not be settled on the blockchain as initially planned. Let `$d` be the total streaming duration measured in seconds, `$t` the average block time before the stream started and `$t'` the actual average block time over `$d` after the stream started. We distinguish two undesirable scenarios: 1. `$t` < `$t'`: the payee will get their funds *later* than expected 2. `$t` > `$t'`: the payee will get their funds *sooner* than expected If the combined error delta is smaller than the payment rate (fifth parameter of the `create` method, measured in wei), there is no problem at all. Conversely, we stumble upon trust issues because real-world time frames do not correspond to the stream terms. For instance, if an employee is normally entitled to withdraw all the funds from the stream at the end of the month, but block times cause case 1 from above to occur, the employee is in a financial disadvantage because their continuous effort is not compensated as promised. Limiting the problem scope only to Ethereum, we propose two remedies: 1. Consensus on calling the `update` function to correct the stream terms. This might sound preposterous, but in most cases the stakes are low and stream participants are involved in long-term financial commitments. There is a high disincentive to refuse to cooperate. 2. Autonomously fix significant error deltas. In theory, we could achieve this using previous blocks' timestamps, "checkpointing" the stream once in a predefined number of blocks. This is still an area of active research because of potentially high overheads in gas costs. Nonetheless, it is important to note that this is still a major improvement on the traditional model where absolute trust is required. ### Sidechains It could be more efficient to implement this standard on independent sidechains like [POA Network](https://poa.network) or [xDai](https://medium.com/poa-network/poa-network-partners-with-makerdao-on-xdai-chain-the-first-ever-usd-stable-blockchain-65a078c41e6a) - thanks to their rather predictable nature. Admittedly, security is traded for scalability, but proper crypto-economic stakes could alleviate potential problems. Furthermore, it is intriguing to explore the prospect of stream-specific sidechains. ### Oracles The proposed specification uses block numbers to proxy time, but this need not be the only method. Albeit it would imply different trust assumptions, oracles could be used to provide a feed of timestamps. Coupled with the aforementioned idea of stream-specific sidechains, oracles could efficiently solve the problems outlined in [Block Times](#block-times). ### Multi-Hop Streams Future or upgraded versions of this standard may describe "multi-hop" streams. If: 1. There is a stream between A and B 2. There is another stream between B and C There could be a way to avoid running two different streams in parallel. That is, a fraction or all of the funds being streamed from A to B could be automatically wired to C. An interesting use case for this is taxes. Instead of manually moving money around, proactively calculating how much you owe and then transfer it, a stream could atomically perform those operations for you. ## Implementation - [Sablier Monorepo](https://github.com/SablierApp/sablier) ## Additional References - [Sablier Launch Thread](https://twitter.com/PaulRBerg/status/1134773451888238592) - [Chronos Protocol Ethresear.ch Plasma Proposal](https://ethresear.ch/t/chronos-a-quirky-application-proposal-for-plasma/2928?u=paulrberg) - [Flipper: Streaming Salaries @ CryptoLife Hackathon](https://devpost.com/software/flipper-3gvl4b) - [SICOs or Streamed ICOs](https://ethresear.ch/t/chronos-a-quirky-application-proposal-for-plasma/2928/14?u=paulrberg) - [RICOs or Reversible ICOs](https://twitter.com/feindura/status/1058057076306518017) - [Andreas Antonopoulos' Keynote on Bitcoin, Lightning and Money Streaming](https://www.youtube.com/watch?v=gF_ZQ_eijPs) ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
Second Draft (Sep 27, 2019) ## Simple Summary We define money streaming as continuous payments over a finite period of time. Block timestamps are used as an on-chain proxy for time. ## Abstract The following describes a standard whereby time is measured using block timestamps and streams are mappings in a master contract. 1. Developer sets up a money streaming contract. 2. A payer, henceforth referred to as sender, can create a stream by depositing funds in the contract and choosing a streaming duration. 3. A payee, henceforth referred to as recipient, can withdraw money from the stream based on its continuous solvency. That is, `payment * (current block timestamp - starting block timestamp)`. 4. The stream can be terminated by either the sender or the recipient. 5. If the stream ended and has not been terminated, the recipient is entitled to withdraw all the remaining balance. ## Motivation This standardised interface takes a stab at changing the way we think about trust in financial contracts. Thanks to blockchains, payments need not be sent in lumps (as with monthly salaries), as there is less overhead in pay-as-you-go. Money as a function of time would better align incentives in a host of scenarios. ### Use Cases This is just a preliminary list of use cases. There are other interesting ideas left to explore, such as time-dependent disincetivisation, but, for brevity, we have not included them here. - Salaries - Insurance - Subscriptions - Consultancies - Rent - Parking - Lending [Sablier](https://sablier.finance) is a champion project for this ERC. It is what we call the protocol for real-time finance. ## Specification ### Structs The structure of a stream should be as follows: - `Stream` - `deposit`: the amount of money to be streamed - `ratePerSecond`: the number of tokens allocated each second to the recipient - `remainingBalance`: the left in the stream - `startTime`: the unix timestamp for when the stream starts - `stopTime`: the unix timestamp for when the stream stops - `recipient`: the address towards which the money is streamed - `sender`: the address of the party funding the stream - `tokenAddress`: the ERC20 token to use as streaming currency - `isEntity`: indicates whether the stream exists or not ```solidity struct Stream { uint256 deposit; uint256 ratePerSecond; uint256 remainingBalance; uint256 startTime; uint256 stopTime; address recipient; address sender; address tokenAddress; bool isEntity; } ``` --- ### Methods #### getStream Returns the stream object with all its properties. ```solidity function getStream(uint256 streamId) view returns (address sender, address recipient, uint256 deposit, address token, uint256 startTime, uint256 stopTime, uint256 remainingBalance, uint256 ratePerSecond) ``` #### balanceOf Returns the real-time balance of the account with address `who`. ```solidity function balanceOf(uint256 streamId, address who) view returns (uint256 balance) ``` #### createStream Creates a new stream funded by `msg.sender` and paid towards `recipient`. MUST throw if `recipient` is the zero address, the contract itself or the caller themselves. MUST throw if `startTime` is before the current block timestamp. MUST throw if `stopTime` is before `startTime`. MUST throw if `deposit` is not a multiple of the time delta. MUST throw if the contract is not allowed to transfer enough tokens. **Triggers Event**: [CreateStream](#createstream) ```solidity function createStream(address recipient, address deposit, address tokenAddress, uint256 startTime, uint256 stopTime) returns (uint256 streamId) ``` #### withdrawFromStream Withdraws from the contract to the recipient's account. MUST throw if the caller is not the sender or the recipient. MUST throw if `amount` exceeds the available balance. **Triggers Event**: [WithdrawFromStream](#withdrawfromstream) ```solidity function withdrawFromStream(uint256 streamId, uint256 amount) returns (bool success) ``` #### cancelStream Cancels the stream and transfers the tokens back on a pro rata basis. MUST throw if the caller is not the sender or the recipient. **Triggers Event**: [CancelStream](#cancelstream) ```solidity function cancelStream(uint256 streamId) returns (bool success) ``` --- ### Events #### CreateStream MUST be triggered when `createStream` is successfully called. ```solidity event CreateStream(uint256 indexed streamId, address indexed sender, address indexed recipient, uint256 deposit, address tokenAddress, uint256 startTime, uint256 stopTime); ``` #### WithdrawFromStream MUST be triggered when `withdrawFromStream` is successfully called. ```solidity event WithdrawFromStream(uint256 indexed streamId, address indexed recipient, uint256 amount); ``` #### CancelStream MUST be triggered when `cancelStream` is successfully called. ```solidity event CancelStream(uint256 indexed streamId, address indexed sender, address indexed recipient, uint256 senderBalance, uint256 recipientBalance); ``` ## Rationale This specification is designed to serve as an entry point to the quirky concept of money as a function of time. Several other designs, including payment channels have been considered, but they were deemed dense in assumptions unnecessary for an initial draft. ### Timestamps Block timestamps are a reasonably secure proxy for time on the blockchain. Although miners handle them, there are game-theoretical incentives to not provide malicious timestamps. [Read this thread](https://ethereum.stackexchange.com/questions/413/can-a-contract-safely-rely-on-block-timestamp) on StackExchange for more details. The only dangerous scenario we can think of is when ERC-1620 derived implementations end up making up a significant share of the volume of money transferred on Ethereum. It is possible, although unlikely, that some stream recipients will have an incentive to coax miners in bumping the block timestamps for them to profit. But we posit the payoff (a few seconds or minutes times the payment rate) will not be high enough for this kind of attack to be worth it. ## Implementation - [Sablier](https://github.com/sablierhq/sablier/) ## Additional References - [Sablier Launch Thread](https://twitter.com/SablierHQ/status/1205533344886411264) - [Introducing Sablier: Continuous Salaries on Ethereum](https://medium.com/sablier/introducing-sablier-continuous-payments-on-ethereum-c2bf04446d31) - [Chronos Protocol Ethresear.ch Plasma Proposal](https://ethresear.ch/t/chronos-a-quirky-application-proposal-for-plasma/2928?u=paulrberg) - [Flipper: Streaming Salaries @ CryptoLife Hackathon](https://devpost.com/software/flipper-3gvl4b) - [SICOs or Streamed ICOs](https://ethresear.ch/t/chronos-a-quirky-application-proposal-for-plasma/2928/14?u=paulrberg) - [RICOs or Reversible ICOs](https://twitter.com/feindura/status/1058057076306518017) - [Andreas Antonopoulos' Keynote on Bitcoin, Lightning and Money Streaming](https://www.youtube.com/watch?v=gF_ZQ_eijPs) ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
frozeman commented 5 years ago

Thanks for this. Very interesting concept. Why only allowing ERC20 tokens and not ether?

PaulRBerg commented 5 years ago

Thank you! The feedback we got so far is that people would love to use this with stablecoins like DAI, TrueUSD etc. Indeed, in the case of ICOs, Ether is the main currency used but a lot of people have been implementing WETH lately. The rationale is to avoid writing duplicate logic and I said to make the ERC strict because of that. Nevertheless, on a second thought, it doesn't hurt to just make it a little bit looser and accept empty _tokenAddress params.

frozeman commented 5 years ago

In the RICO model this streaming of money is based on ETH.

In your idea the way to map streamer with the committed funds is through a streaming ID and an address, correct?

In the RICO idea you get a token back. The advantage is that the token can be transferred and so the commitment transferred and also only part of the commitment can be withdrawn rather than everything.
Also investing in a RICO van happens from any ether wallet and withdrawing your funds as well, by simply sending back the commit tokens. Though your idea works probably better for subscriptions etc. the disadvantage of the token is that you either have to have a locked balance if you already refunded (for the part if the tone. That is not refundable anymore), or two tokens: one refundable and one not. Either token can layer be swapped for whatever was bought in the RICO.

PaulRBerg commented 5 years ago

In your idea the way to map streamer with the committed funds is through a streaming ID and an address, correct?

Exactly, just an uint256 id incremented whenever a new stream is added. Each struct contains the addresses of both the payer and the payee.

In the RICO idea you get a token back.

I found this ERC and RICOs at least tangential, hence why I added a reference in the first place. Of course, the RICO proposal has more crowdsale-specific details insofar there is an extra set of instructions needed to be executed (i.e. receiving the ICO tokens), rather than just depositing the money and calculating balances based upon block heights.

I'd love to keep exploring refundable crowdsales because I think they're a massive improvement on the previous iterations - and it's one of the things which inspired me to continue with this ERC.

frozeman commented 5 years ago

Yes it’s a great idea. I have not yet planned to make an ERC for the RICO, we first will try it out with our project LUKSO and then see if it makes sense to standardize.

cupOJoseph commented 5 years ago

Seems pretty well thought out. Popular current time tracking programs systematically commit wage theft against the working class. I find it inevitable that we eventually get to paying people as the work for the exact amount of time they work. It's an idea a lot of folks have talked about, but it's nice to see the first technical spec for something that would enable this. Potentially take some thoughts from #1337 on subscriptions? Awesome job on this!

PaulRBerg commented 5 years ago

Hey @jschiarizzi interesting points, wasn't aware there's that much unfairness. I totally love #1337, what the @gitcoinco and @8x-protocol teams are doing and the cool thing is that the "subscriptions" EIP can be used in coalition with money streaming.

The current spec requires people to deposit funds beforehand, hence for longer periods it may not work for everyone due to liquidity problems. But fear not, because you can run a subscription layer and schedule to pay "streaming deposits" every month.

ipetrovic11 commented 5 years ago

@PaulRBerg Awesome to see this, you can take a look at Technical Paper that we wrote a while back making Streaming Money for salaries case: https://drive.google.com/file/d/1o34Cbm6tAPNHNvjxR9IuWVlQCmeshHNL/view

Our wallet has already implemented this.

Would be great to have a live chat and discuss how we can bring this further to life, you can contact me at: ivan@workchain.io

jtremback commented 5 years ago

We've implemented something like this proposal here: https://github.com/althea-mesh/aragon-node-list/blob/master/contracts/Althea.sol#L86

  function addBill() public payable {
    require(msg.value > perBlockFee, "Message value not enough");

    if (billMapping[msg.sender].lastUpdated == 0) {
      billMapping[msg.sender] = Bill(msg.value, perBlockFee, block.number);
      emit NewBill(msg.sender, vault);
    } else {
      billMapping[msg.sender].balance = billMapping[msg.sender].balance.add(msg.value);
      emit BillUpdated(msg.sender);
    }
  }

  function collectBills() external {
    uint transferValue = 0;
    for (uint i = 0; i < subnetSubscribers.length; i++) {
      transferValue = transferValue.add(
        processBills(userMapping[subnetSubscribers[i]].ethAddr)
      );
    }
    vault.deposit.value(transferValue)(ETH, transferValue);
  }

  function payMyBills() public {
    uint amount = processBills(msg.sender);
    vault.deposit.value(amount)(ETH, amount);
  }

  function withdrawFromBill() external {
    payMyBills();
    uint amount = billMapping[msg.sender].balance;
    require(amount > 0, "Amount to payout is no more than zero, aborting");
    billMapping[msg.sender].balance = 0;
    address(msg.sender).transfer(amount);
    emit BillUpdated(msg.sender);
  }

  function processBills(address _subscriber) internal returns(uint) {
    uint transferValue;
    Bill memory bill = billMapping[_subscriber];
    uint amountOwed = block.number.sub(bill.lastUpdated).mul(bill.perBlock);

    if (amountOwed <= bill.balance) {
      billMapping[_subscriber].balance= bill.balance.sub(amountOwed);
      transferValue = amountOwed;
    } else {
      transferValue = bill.balance;
      billMapping[_subscriber].balance = 0;
    }
    billMapping[_subscriber].lastUpdated = block.number;
    emit BillUpdated(_subscriber);
    return transferValue;
  }

Can you see any benefits that this proposal has over our system? Or is it just a standardization of this concept?

PaulRBerg commented 5 years ago

Hey @jtremback, good stuff. Imho, standardisation is a very powerful tool: there would've been no ICO craze without ERC20.

For one, accounting software working with Ethereum may expect the function names defined in this spec when integrating continuous payments, ergo they could have a hard time working with different functions like processBills.

Excluding coordination benefits, ERC1620 may not be helpful for specific apps where complex behaviour is not necessary (e.g. the ability to make an update to the streaming terms). If you know you'll not make significant updates in the future using something like ZeppelinOS, go without it. Otherwise, feel free to ping me on Twitter or email if you have any enquiries :)

PaulRBerg commented 5 years ago

Happy to announce that now there's a dApp built on top of ERC-1620! It's called Sablier and you can check it out at http://sablier.finance

For more information, check out this Twitter thread.

marsrobertson commented 5 years ago

1.

Why block height not now?

Because there is uncertainty regarding block times, streams may not be settled on the blockchain as initially planned. 

The rationale suggests that block times are uncertain, why not use human readable form?

2.

struct Rate {
  uint256 payment;
  uint256 interval;
}

Rename payment ➡️ amount. Calling thing a payment suggests it’s another struct, a payment can contain the amount, time, description, comment, receipt, timestamp. In this instance it is the amount only.

3.

Creating structs for 2 fields such as start and stop and calling it Timeframe? Needlessly complicated, start and stop (or beginning and end) are simple enough.

Similar comment regarding Rate, no need to create a Struct for two fields only, both amount (or amountPerInterval, not payment) and interval can live directly as a member of Stream.

4.

100% supporting first comment - https://github.com/ethereum/EIPs/issues/1620#issuecomment-441368518 - ETH is the native currency of the Ethereum, using tokens only seem weird. But yeah... Market research, stablecoins, can alwasy use WETH.

PaulRBerg commented 5 years ago

Hey @marsrobertson many thanks for your feedback.

  1. I found it easier to handle block numbers instead of timestamps. I guess it's also a tiny bit more gas efficient.
  2. Good idea, many people found payment a bit counter-intuitive.
  3. I don't disagree with you that it seems to be "needlessly complicated", but I guess I liked the brevity.
  4. By only accepting ERC20, I simplified the implementation. And yes, you can use WETH if you want to pay with Ether.
PaulRBerg commented 4 years ago

In preparation for migrating Sablier's to mainnet, I updated this draft to match the latest implementation.

Changelog:

With these changes, I seeked to minimise the complexity of the standard.

ERC-1620 is now in "Last Call" status.

wighawag commented 4 years ago

Hi, sorry for joining the discussion that late. I was not aware of it before. For transit pension plan (https://devpost.com/software/transit-pension-plan) we are planning to give out pension as a stream and would thus be interested in a standard for wallet to follow.

Throwing out quick comments right now but will hopefully find some time to get more specific later:

I think the standard as it stands is too specific:

Few notes regarding some of the stream's fields :

Regarding the amount available to withdraw, as mentioned above, I would make it a separate method

maybe amountAvailableToWithdraw(uint256 streamId, uint256 timestamp) that MUST throw if no stream exist with that id that MUST return the amount that can be given knowing how much time has passed, considering startTime and endTime (if any)

PaulRBerg commented 4 years ago

Hey @wighawag apologies for the delay, just woke up after a long journey getting to Devcon!

Generally, my take is that I wrote ERC-1620 particularly for streams funded by specific senders and paid to specific recipients. Are you familiar with @d10r's ERC-2100? I feel that's more aligned with your use case.

The standard should not prescribe that a end time is specified (for our pension, there is no pre-determined end-time)

Doesn't this mean you'll have to have other mechanism for whitelisting and discarding payees?

The standard should not prescribe that a specific deposit is backing it up. (for our pension, the pool is shared among participants)

This would mean different users can't choose their own payment rate.

But interestingly enough, we did consider this and think to normalise the payment rate/ second so we can create an sDai (Sablier DAI) token. Unfortunately it didn't fit our current business model, but it's something to keep in mind as a skunk work type of thing.

is that the remainingBalance available to withdraw (a dynamic value) ? or is that simple what is left of the deposit, regardless of time ?

The latter. Say the initial deposit is 3600 tokens and the payment rate is 1 token/ second. After 100 seconds, you can withdraw up to 100 tokens. Say you withdraw all of it. The remainingBalance is now 3600 - 100 = 3500.

Regarding the amount available to withdraw, as mentioned above, I would make it a separate method

Good point. The word "balance" is used to denote two rather different things. In balanceOf, it is the amount of money allocated to each party, but in the stream struct, it is the balance allocated to the stream itself.

amountAvailableToWithdraw sounds descriptive enough. I'll have a think about it and propose this as a change after we wrap our audit.

PaulRBerg commented 4 years ago

We just moved this to the "Final" state 🥳

There are some things that could've been better, some features that could've been added, but we like it as it is now. It's simple and it works in production in Sablier.

axic commented 4 years ago

This issue shouldn't be closed as it is the discussion URL of the EIP.

github-actions[bot] commented 2 years ago

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.

github-actions[bot] commented 2 years ago

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.

Arch0125 commented 1 year ago

Hey! I'm working on the implementation of EIP-1620 for payment streaming with the following upgrades

Here's link to my project docs : Salaried Docs GitHub Repo : Salaried3 Production on testnet (Polygon Mumbai) : Website

I would really like to contribute to this standard and improve it's functionality

PaulRBerg commented 1 year ago

Hey @Arch0125, I'm glad to see someone take up ERC-1620 and improve upon it. However, as per the latest discussions in the PR, this EIP ended up being marked as "Stagnant". A better place to talk about your project and your ideas about money streaming would be the Sablier Discord feel free to even DM me directly once you join the server. I would love to have a chat.

Arch0125 commented 1 year ago

Hey @Arch0125, I'm glad to see someone take up ERC-1620 and improve upon it. However, as per the latest discussions in the PR, this EIP ended up being marked as "Stagnant". A better place to talk about your project and your ideas about money streaming would be the Sablier Discord feel free to even DM me directly once you join the server. I would love to have a chat.

Hey I reached out to you in Sablier Discord, let's have a talk there about the improvement! My Discord ID is Archisman Das#9013. Also how to become the co-author if the EIP-1620 since I'm looking forward to contribute to the proposal

PaulRBerg commented 1 year ago

@Arch0125 you cannot become a co-author because, as I said above, this EIP is now marked as "Stagnant". It doesn't accept any further revisions.

Arch0125 commented 1 year ago

@paulrberg I had a talk with Moderators from Ethereum Cat Herders and they are open to move the status of this EIP from "Stagnant" after reviewing the improvements I made on the current standard. So, are you open to the proposal to make further changes on the EIP-1620 itself or should I create a separate EIP and work on it.

Pandapip1 commented 1 year ago

@Arch0125 you cannot become a co-author because, as I said above, this EIP is now marked as "Stagnant". It doesn't accept any further revisions.

This isn't necessarily true. Stagnant EIPs can be brought back to draft.

PaulRBerg commented 1 year ago

are you open to the proposal to make further changes on the EIP-1620 itself

Let me think about that, @Arch0125.

Stagnant EIPs can be brought back to draft.

I didn't know this, thanks for explaining @Pandapip1.

Arch0125 commented 1 year ago

are you open to the proposal to make further changes on the EIP-1620 itself

Let me think about that, @Arch0125.

Maybe I can create a new EIP having the new changes and give reference to 1620 when interfaces are used from the same.

PaulRBerg commented 1 year ago

@Arch0125 I'm open to reboot EIP-1620, but I am not able to review your proposed changes today. If you give me a few weeks, I will be able to.

Of course, if you're in a rush with this and you just can't afford to wait after me, please do go ahead with making a new EIP.

hellwolf commented 1 year ago

HI all @Arch0125 @paulrberg,

I am happy to see people continuing to explore the money streaming space. @Arch0125 have you looked into how Superfluid (https://github.com/superfluid-finance/protocol-monorepo/, docs.superfluid.finance/) has been approaching this the generalized money semantics including continuous money flows?

I am also preparing some EIPs for a more inclusive and unifying approach to these new money semantics. Would you be all be a discussion together some day?

P.S.: fwiw, my colleague at Superfluid also proposed https://github.com/ethereum/EIPs/issues/2100 a while ago.

Arch0125 commented 1 year ago

@hellwolf Hey! I went through SuperFluid's approach of money streaming, as a matter of fact I got introduced to Money stream by SuperFluid itself, however I'm still not a big fan of wrapping/unwrapping tokens, it would be overwhelming to a new defi user, so I'm trying to find out a more standard approach with ERC20s which I guess will drive a faster adoption towards money streaming

Arch0125 commented 1 year ago

@Arch0125 I'm open to reboot EIP-1620, but I am not able to review your proposed changes today. If you give me a few weeks, I will be able to.

Of course, if you're in a rush with this and you just can't afford to wait after me, please do go ahead with making a new EIP.

It's okay, let's take time and go through the changes and then we can decide upon whether to continue with this EIP or make a new one.

hellwolf commented 1 year ago

@hellwolf Hey! I went through SuperFluid's approach of money streaming, as a matter of fact I got introduced to Money stream by SuperFluid itself, however I'm still not a big fan of wrapping/unwrapping tokens, it would be overwhelming to a new defi user, so I'm trying to find out a more standard approach with ERC20s which I guess will drive a faster adoption towards money streaming

Fair enough.

I am very curious to understand though, what makes you feel unwrap and redeemStream/or any sort of claim is different?

rashtrakoff commented 1 year ago

I am very curious to understand though, what makes you feel unwrap and redeemStream/or any sort of claim is different?

I believe it's just the UX for new users. Telling them to wrap/unwrap is a pain point. However, automation can help and Superfluid has done a great job easing the whole process by providing batching abilities.

@Arch0125 , could you detail the issues which you think warrant a new EIP entirely or is it just wrap/unwrap that's the main issue?

Pandapip1 commented 1 year ago

Again, if an author approves, one can modify the existing EIP-1620.

Arch0125 commented 1 year ago

I am very curious to understand though, what makes you feel unwrap and redeemStream/or any sort of claim is different?

I believe it's just the UX for new users. Telling them to wrap/unwrap is a pain point. However, automation can help and Superfluid has done a great job easing the whole process by providing batching abilities.

@Arch0125 , could you detail the issues which you think warrant a new EIP entirely or is it just wrap/unwrap that's the main issue?

Hey @hellwolf @rashtrakoff One of the major issue due to which I switched to EIP-1620 for my project was due to fact that while sending an amount of token to a different address which can be done by either creating a new stream or transferring a whole amount from an existing stream. Here are the problems I faced while doing the same: With existing EIP-1620

With SuperFluid

chainblockHelper commented 1 year ago

Hello @Arch0125 @paulrberg @hellwolf

What are your opinions in using payment channels to perform the same thing?

I'm surprised it hasn't taken off since the concept was first introduced back in 2017!

I assume they can be adapted for ERC-20 tokens too!

Feb 2022 -> Uni-directional: https://itnext.io/uni-directional-payment-channels-in-solidity-1aab8cc7eda9

Newer -> Uni-directional: https://solidity-by-example.org/app/uni-directional-payment-channel/

Sometime in 2020: https://cryptomarketpool.com/how-to-create-a-payment-channel-on-ethereum/

No longer Maintained -> June 2017: https://medium.com/@asmiller1989/a-simple-ethereum-payment-channel-implementation-2d320d1fad93 by @alex-miller-0

PaulRBerg commented 1 year ago

@chainblockHelper I haven't read your materials, however, my take on using payments channels to implement money streaming is the following:

It makes for a much worse user and dev experience than just using smart contracts.

  1. Payment channels involve some off-chain software. An app developer integrating the streaming protocol must remember this, and integrate the off-chain component on top of the smart contracts.
  2. They add economic overhead. A user must wait some pre-defined period of time before they can claim their money, since their withdrawal request is subject to a slashing mechanism (to check for double-spends, etc.)
chainblockHelper commented 1 year ago

Paul,

Definitely appreciate your insight on this.

I was able to create a payment channel. From an intellectual standpoint it's a great way to learn more about ECDSA, r, s, v, and friends.

From a practicality standpoint, I totally agree with you. Just sending ETH to someone directly is a lot easier and cheaper!