Skip to content
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

Recurring Subscription Models are a Good Thing and should be viable on Ethereum (Merit + Architecture ERC) #948

Closed
owocki opened this issue Mar 25, 2018 · 98 comments
Labels

Comments

@owocki
Copy link
Contributor

owocki commented Mar 25, 2018

I am opening this ERC as a means of discussing (a) the merits and (b) the viability of creating a standard way of managing recurring payments on the blockchain, both (1) for tokens, and (2) for Eehereum.

Merit

Monthly subscriptions are a key monetization channel for legacy web, and arguably they are the most healthy monetization channel for businesses on the legacy web (especially when compared to ad/surveillance) based models. They are arguably more healthy than a token based economic system (depending upon the vesting model of the ICO).

For these reasons, I think it's worth talking about the viability of creating a standard way to do 'subscriptions' on Ethereum. I'm envisioning an

From UX standpoint, it would be pretty nice if you could manage all your ethereum-based SAAS subscriptions from one service like an on-chain keychain, if privacy was respected.

Viability

Opt in every month model

This is already viable if a service were to send an email (or other notification) to a user to every month sign a transaction.

But it creates a lot of churn in the subscriber base because the steps of

  1. sending the email.
  2. receiving the email
  3. opening the email
  4. signing a tx
  5. broadcasting the tx

is a lot of friction for the user.

It is also suboptimal from a cash flow perspective from the business, because the trickle of exchange of a value to the user and revenue for the business requires each party to reaffirm their relationship every month.

In a world in which there are 100s of Ethereum based dapps, it would simply be untenable from an attention standpoint for a consumer to manage all of their subscriptions in this world.

Opt out model

For the above reasons, I think it's optimal for Ethereum to support opt out subscription models. I am defining an opt out subscription model as

  1. User consents to having price worth of ETH (or tokens) withdrawn every time_period by service_address.
  2. The user may remove consent at any time.
  3. The owner of service_address may remove price worth of ETH/tokens every time_period. If those tokens are available and the users consent is active and its been at least time_period since last withdrawal, then the tx will be successful. If not it will throw().

Case Studies

Take the case study of Adobe Creative Cloud. Prior to 2013, you had to pay $1000 for creative suite, and it was a massive barrier to entry. Now you just pay $40 per month, and you can learn the software and continue to pay if you use it. And Adobe can manage their cash flow over time.

Or the case of Amazon Prime. For $80 per year, one can simply (1) not have to pay shipping for their goods (2) receive a ton of benefits related to their content. And now amazon can do revenue forecasts more accurately because they're managing a consistent voume of cash flow.

virtuous-cycle-of-the-saas-business-model-e1497454519597

Technical Viability

Right now, it is not technically viable to do opt out recurring subscriptions on the blockchain. The best workaround would be to present a user with an approve(x) where x = price * n, where price is the monthly price of the service and n is a number of months, and then call transfer(x/n) every month or so.

Until (and if) ETH becomes a token, it would not be viable to do this at all with ETH.

Proposal.

I am not at a point yet with this idea where I feel comfortable presenting an interface. A discussion on (a) merit should precede the discussion on viability and proposal design.

My only strongly held beliefs for the 'proposal' stage of this ERC at this point is

  1. that subscription payments are a core piece of infrastructure for the Ethereum ecossytem and thereby should not be subject to the rent-seeking nature of any tokenized product (other than gas payments setup already active in the Ethereum protocol)
  2. The system should be architected such that a subscription product can be managed in a completely trustless way. (i.e. no trusted intermediary in between the two parties).

👋
@owocki and the @gitcoinco team.

@djosey
Copy link

djosey commented Mar 25, 2018

I like this. SAAS subscriptions aren't usually just a binary thing where they're just either active or not -- you might pick a service level or price point or number of users or whatever options to configure the sub & maybe that's done in the service's traditional web application. But it seems like if you're basically signing a contract, the various terms beyond whether the sub is active/inactive as well as pricing details etc should be somehow linked to this abstraction for the purpose of recording what the user signs for. So, to break it down, it seems to me like you could have terms parameters(which could be adjusted potentially with implications on payment), payment calculation, and a signature working together on this. Although for an MVP, would probably be good to just focus on as though the terms are just binary -- user is subscribed, user is unsubscribed.

@jrmoreau
Copy link

Do you feel you'd want to make sure the user signed a transaction associated with the subscription every time a payment was due? This is basically like a double opt-in, which is better for the consumer. If they don't approve/sign the transaction requesting, the service or product does not go to them. Out-out by default?

@eshon
Copy link

eshon commented Mar 25, 2018

Maybe this was already in the plan but yeah it'd be nice ux to have a subscription registry service for all your subscriptions (with updates aggregated in 1 sweep / notification / single monthly or annual transaction by the user then auto-paid to each service). Might be more of a dapp tho like http://scroogeup.com or a budgeting app or advanced wallet but maybe it could be an ERC.

@ptrwtts
Copy link

ptrwtts commented Mar 25, 2018

For the case of tokens, couldn't you do something similar to 0x, where you give an unlimited allowance to a smart contract which has clearly bounded functionality (to only allow withdrawals of amounts you approve, on a certain interval)?

  • Service deploys a smart contract that can withdraw tokens from users (or even better, it's a shared contract for all subscriptions). The contract could be audited to see that it will only withdraw amounts that the user approves
  • The user "approves" the contract for an unlimited allowance
  • The user calls the createSubscription() function, allowing price tokens to be withdrawn from them every time_period by service_address, until they cancel
  • Every month, the service_address calls withdrawSubscription(), which uses transferFrom() to collect the tokens that have been authorized

This would allow the user to authorize an ongoing subscription with one-time setup, and no need to escrow funds. Would this satisfy your requirements?

Doing this with ETH is probably harder, as there's nothing like the "approve" function that I'm aware of.

@niran
Copy link

niran commented Mar 25, 2018

Recurring payments are very important. One unaddressed issue in this model is volatility. For users to grant price-based withdrawals to a contract, the oracle needs to be able to disable withdrawals during periods of high volatility.

@ScottStevenson
Copy link

Love this idea. An important discussion to have is whether this should be baked into Ethereum or whether this should be it's own project built on top of Ethereum.

Personally I believe it's so critical to the development of the dapp ecosystem that a core standard or new functionality that enables this sort of subscription with a consistent method that the user can understand, is warranted.

One difference to note in the model discussed here and the typical model is that the credit card model uses credit - you don't need to have money sitting on your credit card for payments to go through - and many people (if not most) do manually pay their credit cards. So you could envision a model where multiple apps request charges and then a user can approve them at all at once at the end of each month. Apps could decide themselves how long to allow the user to go without approving.

But maybe that's pointlessly carrying over the legacy mechanics.

@owocki
Copy link
Contributor Author

owocki commented Mar 26, 2018

So, to break it down, it seems to me like you could have terms parameters(which could be adjusted potentially with implications on payment), payment calculation, and a signature working together on this.

This is a neat idea.. I hadnt even though of encapsulating the TOS as a smart contract and linking it but it makes total sense!

Although for an MVP, would probably be good to just focus on as though the terms are just binary -- user is subscribed, user is unsubscribed.

Agree

Do you feel you'd want to make sure the user signed a transaction associated with the subscription every time a payment was due?

I don't feel like it's better for the consumer in all cases. See the 'opt in ever month' vs 'opt out' models above

Maybe this was already in the plan but yeah it'd be nice ux to have a subscription registry service for all your subscriptions (with updates aggregated in 1 sweep / notification / single monthly or annual transaction by the user then auto-paid to each service).

I agree!

Might be more of a dapp tho like http://scroogeup.com or a budgeting app or advanced wallet but maybe it could be an ERC.

The reason I ERC'd it is that I think this is a core enough piece of infrastructure that it shouldnt be written by a rent seeking ICO or token based model. It should be a core piece of the infrastructure.

For the case of tokens, couldn't you do something similar to 0x,

This is interesting.. Thanks I didnt know about 0x doing this!

The user "approves" the contract for an unlimited allowance

It scares me to have the contract approved for an unlimited allowance. I get that you can mitigate it by code reviewing the smart contract that it's called for the unlimited allowance, but I still think the trust model is funny.

The other thing that

Doing this with ETH is probably harder, as there's nothing like the "approve" function that I'm aware of.

I have heard rumours of ETH being moved to being an ERC20 token, but am unsure of the status there. Does anyone know?

For users to grant price-based withdrawals to a contract, the oracle needs to be able to disable withdrawals during periods of high volatility.

Could you articulate what you mean by 'price based withdrawals'? Do you mean $20 worth of token X every month?

I had not envisioned this protocol being priced based, if thats what you meant. Only use case I had envisioned being in scope was "X tokens per TIME_PERIOD", aka "10 tokens per mont"
.

An important discussion to have is whether this should be baked into Ethereum or whether this should be it's own project built on top of Ethereum.

Yep! My only two core beliefs so far are that I dont want to see a rent seeking token powering this (so thats a vote in favor of being built into Ethereum) and that the system shoudl be trustless (which I think could go eitehr way.)

Personally I believe it's so critical to the development of the dapp ecosystem that a core standard or new functionality that enables this sort of subscription with a consistent method that the user can understand, is warranted.

yes! it fundamentally aligns incentives between user and dapp.

you could envision a model where multiple apps request charges and then a user can approve them at all at once at the end of each month. Apps could decide themselves how long to allow the user to go without approving.

Interesting, I had not though of this. Will noodle on it.

@marclijour
Copy link

I like the idea and the premises. Industry does have come to like steady cash flow streams vs one-time payments. The second benefit is that such feature will enable businesses processes common to e-commerce and sales. I would argue it is best practice to enforce some ground rules in the lowest protocol where that makes sense vs the dapp layer.

One situation to be avoided is an intermediary could make payment decision on behalf of a customer. The person who is paying should be the one committing the payment directly.

@ptrwtts
Copy link

ptrwtts commented Mar 26, 2018

It scares me to have the contract approved for an unlimited allowance

It's true, but unlimited allowances make for a much better UX. That's why with the new #777 standard, unlimited is the only sort of allowance supported (via authorizeOperator). In reality, it's not unlimited, because there are very strict rules coded into the contract about when tokens can be moved. It works best with a shared contract (like 0x), that you only need to audit / approve once, rather than everyone deploying their own.

@MicahZoltu
Copy link
Contributor

Consider: Pre-paid vs post-paid. Pre-paid puts trust in the service provider, post-paid puts trust in the subscriber. Post-paid allows for things like pro-rated subscriptions on cancellation and some other UX benefits, but subscribers generally can't be trusted compared to service providers (one to many relationships tend to by much more Sybil attackable by the many vs the one).

@owocki
Copy link
Contributor Author

owocki commented Mar 26, 2018

Consider: Pre-paid vs post-paid

Hi! It's not clear to me what you mean by 'pre paid' vs 'post paid'. Are you using a different verbage vs opt in/opt out verbage that I used in the OP issue desc?

@pipermerriam
Copy link
Member

pipermerriam commented Mar 26, 2018

Here are a few thoughts:

I think that price should be out-of-scope for this standard. This feels a lot like scope creep to me and doesn't need to be part of the core API. Ideally whatever protocol rules are decided allow for this type of behavior.

From the subscriber's side:

  • I want strong guarantees on when I can cancel my subscription.
  • I will normally want payments to happen automatically without any action on my part.
  • In some cases it might be valuable to require an approval process.
  • For dynamically priced subscriptions I want to be able to set limits (require authorization if subscription is more than X).

From the providers's side:

  • I need the ability to charge a fixed fee per subscription time unit (netflix, pandora, etc).
  • I need the ability to charge a dynamic fee per subscription time unit (aws, twilio, etc).
  • I need to be able to create reasonably accurate forecasts for upcoming subscriptions: Programatic checks that subscription accounts have available balance and that subscription is active.

And to think a bit about protocol:

Building on the token ERCs seems valuable here since they already setup primatives for transferring and approving. What's missing is the concept of time based approvals. I think that we can get very close to hitting all of the use cases above with the following API

Subscription API

I think we need the following minimal API

  • triggerPayment() returns bool : Triggers payment of the subscription. This spec does not specify the behavior of this function, leaving it up to the implementer.
  • cancel() returns bool: Immediately cancels the subscription. Implementations should ensure that any unpaid subscription payments are paid to the provider as part of cancellation to ensure providers are able to let subscriptions fees fill up for arbitrary lengths of time, allowing them to reduce overhead from transaction costs.

These probably have Payment and Cancelled events that would get fired.

An ERC20 token based subscription contract.

With the above, we can now think about what a subscription paid in ERC20 tokens might look like. A minimal implementation would require the following fields.

  • address token: defines the token contract which payments are paid from.
  • address provider: the address of the provider
  • uint256 time_unit: the number of seconds per time unit.
  • uint256 tokens_per_time_unit: the number of tokens that can be paid towards the subscription per time_unit.
  • uint256 last_payment_at: the timestamp when the last payment was made.

The triggerPayment method would call token.transfer(provider, (now - last_payment_at) * tokens_per_time_unit / time_unit)`.

Closing Thoughts

Given the wide set of use cases for subscriptions and the wide array of different business requirements, I think this specification will be most useful if it sticks to trying to define an interface, and leaves the exact implementation up to the provider. A provider would either provide their own implementation of a subscription contract, requiring the user to fund the contract once it was created for them, or they might delegate to a 3rd party service which offers pre-built subscription contracts that fit their business requirements.

@niran
Copy link

niran commented Mar 27, 2018

I think most tokens are too volatile for people to be comfortable denominating subscriptions in, (e.g. all crypto subscriptions would've been cancelled last fall) but let's find out

@abunsen
Copy link

abunsen commented Mar 28, 2018

Hi! It's not clear to me what you mean by 'pre paid' vs 'post paid'. Are you using a different verbage vs opt in/opt out verbage that I used in the OP issue desc?

I'm not speaking on his behalf, but my read on it was that pre-paid means paying upfront for service and post-paid at the end of the period. Like how you have to pay your rent up front, but you pay your Netflix subscription at the end of the month. It's independent of opt-in/opt-out.

Until (and if) ETH becomes a token, it would not be viable to do this at all with ETH.

Excuse my ignorance, but why is this not viable to do with ETH?


I love this proposal & have been thinking about it for a few months. It could really put the power in the hands of the consumer / customer - right now if I want to cancel my Netflix, I have to log in to my Netflix account or worse (e.g. get on the phone 😲 ). Rinse and repeat for any other poor choices I've made with my credit card info (e.g. my Knit-Wise.com subscription or my XtremeNitroShred.com subscription).

The way I've been thinking about this is as follows (keep in mind, I'm an ETH hobbyist, not a professional solidity dev):

  1. Subscriber creates a single "subscriptions contract" that will manage their subscriptions
  2. Subscriber funds that contract
  3. The subscriptions contract manages subscriptions by having something like an array of "approved subscription" structs (name, web domain, ETH address, amount, interval)
  4. External "vendor contracts" can make a request to become a vendor in the "approved subscription" array
  5. Vendor contracts that are in the "approved subscription" array can make calls to withdraw based on the previously agreed upon amount and interval
  6. A user can cancel a subscription at any time by removing the vendor from the array
  7. Subscriber should be able to destroy entire subscriptions contract and have remaining ETH sent back to their address

The assumptions this is based on:

  • Priced in ETH, not USD (so no need for Oracles)
  • The subscriptions are based on paying up-front like the pricing presented on https://quiknode.io/

Some things I thought might be worth considering:

  • How can I verify that I'm actually getting a request from Netflix? This might be as simple as having netflix.com host a netflix.com/subscriptions.eth file with their approved vendor contract addresses
  • How could we update the code? Well since each user is deploying one of these contracts on an as needed basis, we could have versions & make improvements to the standard.
  • How private should this be? I think to start, not that private. We can iterate on standard (see line above) & add in these features as people build a desire to maintain privacy.

@pipermerriam
Copy link
Member

pipermerriam commented Mar 28, 2018

I think most tokens are too volatile for people to be comfortable denominating subscriptions in

one particular token comes to mind which may prove to be an illustrative counter-example. Even if most are not suitable for this, it only takes a few that are stable and fungible to make it worthwhile. (my 2 cents)

@nieldlr
Copy link

nieldlr commented Mar 28, 2018

Heya everyone,

dig this discussion a lot @owocki! Thanks for kicking this off.

I'm the creator of StakeTree a crypto equivalent of Patreon. It's still early days, but I've got some pilot projects running to test market interest and there seems to be quite a neat demand for this. (I'm even funding myself through this!). I've thought a bit on this and tried some contract variants on how to make some kind of pseudo-subscription system. I'm a new solidity dev so there's still lots that I might miss here.

To @owocki's point, on the merit of a subscription standard, I'm fully onboard with this. Having previously worked in SaaS, it's a massive industry and having subscriptions could be hugely beneficial for crypto projects.

My naive implementation to get this working was to have a contract where funders can pool money into the contract and the beneficiary can then withdraw 10% from the pool every week. Funders can refund whatever is left of their funds at any time. For example, if they put in 1 eth, and two withdrawals have occurred, they can then refund themselves 0.81 eth if they want to or continue funding the beneficiary.

See code here.

There's an additional tokenization aspect I added which is unrelated to this discussion (it allows you to reward funders for their contribution).

This implementation is sub-optimal in my view because it requires a big capital upfront cost for the subscription. 1 eth only generates 0.1 eth of "payment", then diminishes every week by 10%. Although this could be useful for a Patreon-style subscription where the exact funds aren't the issue, but rather that funds are there. In cases where an exact payment amount is needed, this doesn't work.

I then tried to do a cleaner version where you could commit to X amount of ether over Y weeks. I called it X-Over-Y subscriptions. So let's say I want to use Netflix for 12 weeks, I can pay upfront and then the beneficiary can withdraw the allocated funds each week. However, I ran into gas cost issues very quickly due to iteration. In most cases, this was my concern technically with subscriptions on smart contracts. I was working on these before CryptoKitties arrived and then it was just barely plausible for gas costs, but soon, the network congested and it became incredibly unfeasible. (To reiterate, I'm not the best solidity dev here, so I might have missed something).

I might be reading these wrong (please do correct me!), but it seems like @pipermerriam, @ptrwtts & @abunsen's possible implementations would require the provider/beneficiary (the project/person receiving the funds) to send a transaction for each subscriber. This kind of scalability might be solved in future, but at the moment, this has been a big blocker for me because of inherent costs and potential uncertainty regarding fluctuating gas price. This is also a tricky UX issue. If the provider runs a withdraw action on a front-end and triggers multiple transactions, what's the easiest way to have that run without someone sitting there signing each transaction?

The other kind of scalability that iterates within a contract, let's say, all there's some kind of aggregation where all subscribers gather in one contract for the provider/beneficiary, could lead to gas limit issues. I solved this by using some math to calculate the totals for funders on the fly and only store how many withdrawals occurred and at what withdrawal they started their funding. But this might lead to the 10% issue, where all funders would have the pay the same amount. (I might look into this again in the future).

At the risk of not letting this get too technical & sorry for being a bit late to this discussion. I'm in agreement that a common interface would be really amazing here, but the reason I go a bit deeper here is so that I can urge proposers to please consider the usability & scalability of a subscriptions standard as well. When proposals are put forth, can we also discuss scenarios of how it would work for 1 subscriber, 10 subscribers, 500 subscribers, 1000 subscribers etc. If it doesn't scale, any subscription can be trivially DOSd. Never mind the possibility of a sybil attack added on top of this.

I'm hugely excited for this. I'd love to hear more thoughts here. And again sorry if I misunderstood some of the suggested implementations (y'all are all probably much smarter than me!). If either we create a scalable subscriptions standard on the smart contract level or we look at the core ethereum level, this will be huge. Thanks again @owocki! Let's do this!

@Josephrp
Copy link

I'm not sure I'm fully convinced by the rational for the EIP. In fiat country subscription services are in fact highly regulated via amongst other things something called a "clearing house". It might be the case that a service provider eg "ClearETH" could be the intermediary between users and their subscription providers and solve much of this while not reducing a user's control.

@charlieknoll
Copy link

Regarding @pipermerriam's spec, it should be required that the cancel function should trigger unpaid payment. This way payments can be accumulated and the provider only needs to call the triggerPayment function when they need access to their accumulated Dai. This will help with scalability such that the provider can time their payments to times of low network usage and gas prices.

@pipermerriam
Copy link
Member

Regarding @pipermerriam's spec, it should be required that the cancel function should trigger unpaid payment.

I've updated my post to include this. Note that my spec makes no requirements on what the underlying implementation does in terms of functionality, so this was added as a "should" to ensure that implementers are aware of this use case.

@pipermerriam
Copy link
Member

@nieldlr I think I understand your concerns, however, from the complexity/security/optimization/efficiency trade-off perspective, I think that a contract-per-subscription is the right choice.

Providers can still batch their withdrawals which should have a noticeable effect on reducing transaction overhead. In theory calls to the triggerPayment should be able to be optimized to be well within a 100k gas budget which puts it close enough to the fully optimized threshold that to achieve any more significant gains will require protocol level scalability.

@ptrwtts
Copy link

ptrwtts commented Mar 29, 2018

@pipermerriam wouldn't a single shared contract be better than a contract per subscription? this way, you can have confidence around what it will do, without having to audit every single subscription you do. i'm guessing it would also be cheaper to setup, if you used a createSubscription() function instead of deploying a contract every time.

it could either be one universal contract, or at least one contract per service (similar to tokens).

@pipermerriam
Copy link
Member

@ptrwtts My preference for a contract-per-subscription model is fueled by the same reason that ENS uses stand-alone deed contracts. Since these contracts hold user funds, by keeping them in separate contracts you reduce the attack surface area by eliminating an entire class of attack since user's funds are not intermingled.

@cryppadotta
Copy link

I've implemented pre-paid "subscriptions" in dotta-license by setting an expiration time on an ERC721 token.

At checkout, users select the length of time they want to pre-pay:

screenshot 2018-04-02 09 44 20

When the token is issued, it sets an expiration according the number of "cycles" the user paid for. Users can renew at any time.

My client-side app verifies that the token has not expired. (You could do this with a desktop app, mobile, or SaaS service.) Because the verification is client-side you can provide any sort of "grace" period you want, such as not disabling until it's been unpaid for a certain amount of time.

Also, because there is a client-side app, I'm using that app for reminders when the subscription is near-due.

From a sellers perspective, credit-card based subscriptions are be beneficial in that users are default-pay (for example, if they forget or just do nothing, you still retain the subscriber). Obviously, we don't have that in Ethereum today.

For pricing, there is code that will sync a list of products and their prices. I update prices periodically to be reasonable within conversion rates.

However, the downside(?) to this (optional) process is that a subscription's price isn't "fixed" (or grandfathered) to a particular price in ETH. I tell my customers that it they want a fixed rate, then they ought to pre-pay for a longer time period. There are interesting incentive dynamics here.

The code is all open-source/MIT, including the contracts, commandline management tools, and React UI widget.

@tjayrush
Copy link

tjayrush commented Apr 3, 2018

@cryppadotta

However, the downside(?) to this (optional) process is that a subscription's price isn't "fixed" (or grandfathered) to a particular price in ETH

I actually see this as a benefit. If you build in a short waiting period during which time your client could either quit your service (because the price you set is too high) or make a counter-offer (because he/she believes the exchange rate is not fair), then you actually have a model that might work. As it is, I don't like that only you can change the rate.

@pipermerriam
Copy link
Member

@tjayrush / @cryppadotta

I believe the schemes your both mentioning can be accomplished under the spec I proposed here. Please correct me if I'm wrong.

@nieldlr
Copy link

nieldlr commented Apr 4, 2018

@nieldlr I think I understand your concerns, however, from the complexity/security/optimization/efficiency trade-off perspective, I think that a contract-per-subscription is the right choice.

Providers can still batch their withdrawals which should have a noticeable effect on reducing transaction overhead. In theory calls to the triggerPayment should be able to be optimized to be well within a 100k gas budget which puts it close enough to the fully optimized threshold that to achieve any more significant gains will require protocol level scalability.

Heya @pipermerriam, could you explain a bit more on how this works? I'm not quite familiar with batching and how this exactly works here. Eager to learn if this could solve our challenge.

Again, from a purely practical point of view, having multiple tx costs to the provider is not scalable. My focus is primarily on people building up support from their communities/fans/supporters and this type of funding can be much smaller. Here's the income distribution on Patreon for example:
image

My withdraw function is currently sitting about ~50k gas cost & back during the network spike in December/January I ended up paying $0.50 per withdrawal. https://etherscan.io/tx/0xe6e5534baee4a6d91c2d288dfb803199d0e1dcb8c3798162dc2a4bb11935a8df

Back then I had about 20 funders, which means that if I ran a ~100k gas cost withdrawal for all of them, it would've cost me $1*20 = $20. This is unfortunately not a reasonable cost for a provider & I would call that a failure of Ethereum to be able to handle subscriptions and rather prefer using centralized providers like Patreon. I'm taking the extreme case here in terms of network congestion, but this is by far not the most extreme in terms of how many funders one could get for a project/app etc. It's just not a user experience risk that I'm comfortable with taking for an app/service

I'm fully aware that we might need to make some tradeoffs here somewhere, but my hunch is that perhaps there's a different way to solve this. Eager to hear if batching (or any other solution) might solve this.

Thanks for exploring this with me (and everyone else here!). I'm passionate about this, because I believe this opens up so many opportunities.

@owocki
Copy link
Contributor Author

owocki commented Apr 4, 2018

I wonder if the optimal interface for 1:many subscriptions (neil's point above) is different than the optimal interface for1:1 subscription contract (piper's interface).

there is certainly a tradeoff from an attack surface perspective, as it's a nice best practice from a security perspective to be able to keep funds for different subscriptions in seperate contracts to make each contract less of a honeypot

@pipermerriam
Copy link
Member

Heya @pipermerriam, could you explain a bit more on how this works? I'm not quite familiar with batching and how this exactly works here. Eager to learn if this could solve our challenge.

Each user would have their own subscription contract. The naive approach would be to send 1 transaction for each subscriber, calling triggerPayment on each subscription contract.

To batch these, you would do this with another contract layer. Here's a psuedo-solidity implementation.

contract SubscriptionInterface {
  function triggerPayment() returns (bool);
}
contract BatchTriggerPayment {
  function triggerBatchPayments(address[] subscriptions) returns (bool success) {
    for (uint i=0; i<subscriptions.length; i++) {
        success = SubscriptionInterface(subscriptions[i]).triggerPayment();
        if (!success) {
          revert();
        }
    }
  }
}

This should save you 21000 gas of overhead for each subscription, reducing the gas footprint to the overhead of a single transaction + the cost of triggering a payment for each subscription.

It's still an O(n) cost, but it's reduced by an O(n) factor due to the savings on transaction overhead.

Remember that true scalability is something that will happen at the protocol level, after which most of this gas accounting and optimization should matter way less.

@alexvandesande
Copy link

Can't this be done by signing some time-locked erc20 cheques somehow? Ideally the user should sign a message saying "after block X, transfer N tokens to Bob". They could do it 12 times for all months and renew the subscription next month. The scope and how to do time lock cheques is what should be is debate in this ERC.

@owocki
Copy link
Contributor Author

owocki commented Jul 17, 2018

@PaulRBerg good to meet you. wanna join our weekly call (thursday mornings) and nerd out about this?

@PaulRBerg
Copy link
Contributor

Sure thing! Emailed you now.

@Josephrp
Copy link

Josephrp commented Jul 17, 2018 via email

@owocki
Copy link
Contributor Author

owocki commented Jul 17, 2018

yes. contact @androolloyd for the weekly call.

we are all hanging out at gitcoin.co/slack in the #proj-subscriptions channel too

@PaulRBerg
Copy link
Contributor

Just published the research I and @mmilton41 have been doing on Chronos, a protocol for continuous, recurring payments. Although it's tangent with the ideas expressed in this chat, the focus is instead on infinitesimally small payment intervals (minutes, hours, rather than months). We mentioned ERC948 and the projects working on it in our draft white paper.

Still many things left to cover (we're using Plasma), but aiming to start coding soon to test our assumptions. Feel free to join the discussion here:

https://ethresear.ch/t/chronos-a-quirky-application-proposal-for-plasma/2928

@leonprou
Copy link

leonprou commented Sep 26, 2018

I've had an idea that the subscription model can be used for crowdfunding. In some cases it's a better fit than the ICO.

For example, I want to create a service (a Dapp). I can sell subscriptions for the service to fund the development. Maybe even investors would buy subscriptions to the future service as an investment, to sell them later to customers for a higher price.

Looking at this use case subscriptions are like NFT's.

@androolloyd
Copy link

@leonprou you're going to really enjoy an upcoming post from Groundhog, we've been working on this model for a few months now.

@jtakalai
Copy link

jtakalai commented Dec 18, 2018

I see this is already a fairly old thread. Has this idea been already discussed: Build (an ERC20) token that allows for "streaming transfer", that is, a stream of value at a set rate. It would perhaps be similar to time-locked cheques, only the time-lock is checked on every transfer (normal or "streaming" kind) so that tokens that "don't belong to you anymore" can't be transferred.

interface IERC20Streamable {
    function stream(address to, uint256 tokensPerSecond) external returns (bool);
}

Benefits:

  • a recurring payment could be implemented on top of it as a smart contract, no need for layer 2 solutions
  • it could be as precise as desired: pay per second; or if block.timestamp is considered bad, pay per block
  • it wouldn't need recurring transactions; only initiating the subscription and withdrawing revenues would incur gas costs
  • no need to lock up tokens up-front; they would be debited over time automatically (until account runs dry)
  • unopinionated and basic: recurring, volume, etc. subscriptions could be implemented on top of it

It would be straightforward to check if the account still can pay for its subscription(s). What happens after the account dries up would be outside the scope of the specification, but at least the vendors would have simple ways to follow if they're still getting paid. If the particular ERC20+streaming would be tied to a service with limited number of different types of subscriptions, the "end timestamp" of currently active subscriptions could be calculated and emitted upon each transfer. This would make it even simpler for vendors to follow if they're getting paid: instead of polling, they could listen to update events by the subscription ID.

Withdrawing could even be done so that existing approval functions are augmented to show tokens belonging to a vendor as approval from the ERC20 account, and vendor could then simply transferFrom their earnings so far any time they please. This would mean the minimal addition to the token API would be the one function stream that sets the volume of the flow of tokens. The flow could then be cut by calling stream(to, 0). Likely other functions for querying the transfer streams would also be useful to wallets that want to support this kind of model, though.

Implementation scope and challenges:

  • transfer, transferFrom, balanceOf need to be re-implemented to account for the streaming transfers, basically to set limits to what the account or anyone approved can transfer out. Perhaps simply re-implementing balanceOf would suffice.
  • each streaming transfer should only receive its correct portion when the account runs out of credit, so that there won't be a race to withdraw tokens by different vendors (in the subscription case). The seconds * tokensPerSecond tokens that were (conceptually) transferred before the account ran out of credit belong to their respective to addresses. In case the payer later tops up the account, then how tokens should be allocated from the intervening "defaulted" time, is up for discussion. Straightforward solution would be to pretend there was not "default" to begin with, and continue calculating the balances as before, but of course in practice the vendors may already have cut the service. Perhaps that could be left outside the specification.

If this idea sounds appealing to anyone, I could knock together a simple PoC implementation.

If this already has been discussed and/or implemented, I'd love to hear about it (please link to URL). If the functionality has been implemented already in another form, that's interesting too. I skimmed through the 8xprotocol white-paper, and it proposed an interesting layer 2 solution. I'm just wondering here if a simple (it's relative...) smart contract could also do.

EDIT: Chronos has an implementation of "token streams"

EDIT: @d10r mentioned their project Artis that implements token streams called Streems

@d10r
Copy link

d10r commented Feb 7, 2019

@jtakalai yes, I've done a PoC a while ago, the code can be found here.
This was a first, unsafe implementation of the idea. I've since implemented a safe one (so I claim, yet to be proven) with the limitations described here. It will soon be published, together with a demo use case. We're also working on a Rust implementation for pwasm (which I've heard is going to somehow be merged with ewasm).

PS: Vitalik also seems to have discovered the topic: https://twitter.com/VitalikButerin/status/1093091291066294272?s=19

@d10r
Copy link

d10r commented Jun 5, 2019

Follow up to my previous comment: Proposal for Streaming Token Standard with example implementations linked in Implementations section.

@github-actions
Copy link

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 github-actions bot added the stale label Dec 18, 2021
@github-actions
Copy link

github-actions bot commented Jan 1, 2022

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.

@github-actions github-actions bot closed this as completed Jan 1, 2022
@Josephrp
Copy link

Josephrp commented Jan 8, 2022 via email

@folego
Copy link

folego commented Nov 4, 2023

I tried to find the status of this proposal on the ETH page https://eips.ethereum.org/all and I couldn't. So kept googling and I found this. Sad to see that is closed and no more in discussion. I don't understand how such good idea can't be on the queue to be implemented. Any chances to reopen this?

@specialOne-coder
Copy link

Hi eveyone, Largely inspired by this idea, I create Subs , a protocol for making recurring onchain payments that can have different uses cases such as subscriptions, DCA, recurring donations or others.

Website : https://subsprotocol.com/
Doc : https://subspro.gitbook.io/subs/introduction/overview
Whitepaper : https://subsprotocol.com/assets/Subs_Protocol_Whitepaper-a15e8bea.pdf

Please, take a look, am open to discuss if needed.
Best regards

@nathantr
Copy link
Contributor

nathantr commented Nov 4, 2023

Hi eveyone, Largely inspired by this idea, I create Subs , a protocol for making recurring onchain payments that can have different uses cases such as subscriptions, DCA, recurring donations or others.

Website : https://subsprotocol.com/ Doc : https://subspro.gitbook.io/subs/introduction/overview Whitepaper : https://subsprotocol.com/assets/Subs_Protocol_Whitepaper-a15e8bea.pdf

Please, take a look, am open to discuss if needed. Best regards

@specialOne-coder This is absolutely incredible what you have created. Tried to reach out to you on Twitter but unsure if there is a better way to contact you?

@livingrockrises
Copy link

For the case of tokens, couldn't you do something similar to 0x, where you give an unlimited allowance to a smart contract which has clearly bounded functionality (to only allow withdrawals of amounts you approve, on a certain interval)?

  • Service deploys a smart contract that can withdraw tokens from users (or even better, it's a shared contract for all subscriptions). The contract could be audited to see that it will only withdraw amounts that the user approves
  • The user "approves" the contract for an unlimited allowance
  • The user calls the createSubscription() function, allowing price tokens to be withdrawn from them every time_period by service_address, until they cancel
  • Every month, the service_address calls withdrawSubscription(), which uses transferFrom() to collect the tokens that have been authorized

This would allow the user to authorize an ongoing subscription with one-time setup, and no need to escrow funds. Would this satisfy your requirements?

Doing this with ETH is probably harder, as there's nothing like the "approve" function that I'm aware of.

I think for eth or erc20 any subscription payments, this can be done with smart contract wallets for consumers and having special contracts aka SubscriptionModules enabled where service provider can pull payments

@folego
Copy link

folego commented Nov 4, 2023

For the case of tokens, couldn't you do something similar to 0x, where you give an unlimited allowance to a smart contract which has clearly bounded functionality (to only allow withdrawals of amounts you approve, on a certain interval)?

  • Service deploys a smart contract that can withdraw tokens from users (or even better, it's a shared contract for all subscriptions). The contract could be audited to see that it will only withdraw amounts that the user approves
  • The user "approves" the contract for an unlimited allowance
  • The user calls the createSubscription() function, allowing price tokens to be withdrawn from them every time_period by service_address, until they cancel
  • Every month, the service_address calls withdrawSubscription(), which uses transferFrom() to collect the tokens that have been authorized

This would allow the user to authorize an ongoing subscription with one-time setup, and no need to escrow funds. Would this satisfy your requirements?
Doing this with ETH is probably harder, as there's nothing like the "approve" function that I'm aware of.

I think for eth or erc20 any subscription payments, this can be done with smart contract wallets for consumers and having special contracts aka SubscriptionModules enabled where service provider can pull payments

But in this case the user should create a new smart wallet right?

@specialOne-coder
Copy link

For the case of tokens, couldn't you do something similar to 0x, where you give an unlimited allowance to a smart contract which has clearly bounded functionality (to only allow withdrawals of amounts you approve, on a certain interval)?

  • Service deploys a smart contract that can withdraw tokens from users (or even better, it's a shared contract for all subscriptions). The contract could be audited to see that it will only withdraw amounts that the user approves
  • The user "approves" the contract for an unlimited allowance
  • The user calls the createSubscription() function, allowing price tokens to be withdrawn from them every time_period by service_address, until they cancel
  • Every month, the service_address calls withdrawSubscription(), which uses transferFrom() to collect the tokens that have been authorized

This would allow the user to authorize an ongoing subscription with one-time setup, and no need to escrow funds. Would this satisfy your requirements?
Doing this with ETH is probably harder, as there's nothing like the "approve" function that I'm aware of.

I think for eth or erc20 any subscription payments, this can be done with smart contract wallets for consumers and having special contracts aka SubscriptionModules enabled where service provider can pull payments

For eth yes, but for ERC-20, smart wallets is not needed, just approval can do the job. And I don't think anyone is keen on paying for subscriptions with eth, unless it's a specific use case, like paying off a debt every month.

@filmakarov
Copy link
Contributor

For eth yes, but for ERC-20, smart wallets is not needed, just approval can do the job.

Unlimited approvals to a third-party contract can't be good practice tbh.
Making this via Modular SCW with a subscription module would be much safer

@daochild
Copy link

Hi eveyone, Largely inspired by this idea, I create Subs , a protocol for making recurring onchain payments that can have different uses cases such as subscriptions, DCA, recurring donations or others.
Website : https://subsprotocol.com/ Doc : https://subspro.gitbook.io/subs/introduction/overview Whitepaper : https://subsprotocol.com/assets/Subs_Protocol_Whitepaper-a15e8bea.pdf
Please, take a look, am open to discuss if needed. Best regards

@specialOne-coder This is absolutely incredible what you have created. Tried to reach out to you on Twitter but unsure if there is a better way to contact you?

Could you briefly describe pros/cons, fees, networks support, etc.?

@specialOne-coder
Copy link

Hey, let's disuss it on

Hi eveyone, Largely inspired by this idea, I create Subs , a protocol for making recurring onchain payments that can have different uses cases such as subscriptions, DCA, recurring donations or others.
Website : https://subsprotocol.com/ Doc : https://subspro.gitbook.io/subs/introduction/overview Whitepaper : https://subsprotocol.com/assets/Subs_Protocol_Whitepaper-a15e8bea.pdf
Please, take a look, am open to discuss if needed. Best regards

@specialOne-coder This is absolutely incredible what you have created. Tried to reach out to you on Twitter but unsure if there is a better way to contact you?

Could you briefly describe pros/cons, fees, networks support, etc.?

Hey let's discuss it. Here is my telegram : @FolkoK

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests