ethereum / EIPs

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

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

Closed owocki closed 2 years ago

owocki commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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)?

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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:

From the providers's side:

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

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.

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 commented 6 years ago

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 commented 6 years ago

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:

Some things I thought might be worth considering:

pipermerriam commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

@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 commented 6 years ago

@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 commented 6 years ago

@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 commented 6 years ago

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 commented 6 years ago

@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 commented 6 years ago

@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 commented 6 years ago

@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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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.

leafcutterant commented 6 years ago

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

Through WETH, ETH already is (can be) a token. I'm not familiar with the WETH contract, but maybe the conversion can be automated by the subscription contract(s).

Side note: I think subscriptions can benefit greatly from a stablecoin like Dai. Most people probably won't want to commit to a subscription or get payments in a currency which they don't know the future value of.

gtaschuk commented 6 years ago

My team took a look into ethereum recurring subscriptions at a Consensys Academy hackathon. The method we opted for was a pre-paid subscription with ethereum held in escrow.

The service provider can at any point withdraw from escrow the (rate of subscription)*(time since the subscription started).

The customer can at any point, add money to escrow (to buy them more time) or cancel their subscription and withdraw any unused funds from escrow.

In this way, subscriptions can be cancelled at any time, and are paid at a continuous rate (effectively allowed pro-rated monthly subscriptions.)

We also took it a bit further and allowed users to "lock in" a lower rate by paying ahead and commiting to a longer subscription. There's still a lot of work to make it a fully functioning dApp, but what we liked about the "pay as you go" and "lock-in" options is that service providers can incentivize loyalty for their users by parameterizing the agreement for "lock-in" (ameliorating the escrow necessity), and you could potentially support multiple subscriptions with one escrow.

Repo with contracts Slides

owocki commented 6 years ago

My team took a look into ethereum recurring subscriptions at a Consensys Academy hackathon. The method we opted for was a pre-paid subscription with ethereum held in escrow.

While I respect your prior art in the space (and commend you for taking a stab).. My view is that pre-paying subscriptions is not an optimal UX for an end user. By placing the money for a 12 month subscription in escrow, the user effectively loses the means to use that capital for other things. Problem gets worse when you start talking about 24 month, 36 month, potentially even longer subscriptions.

Side note: I think subscriptions can benefit greatly from a stablecoin like Dai. Most people probably won't want to commit to a subscription or get payments in a currency which they don't know the future value of.

Strongly agree.

evbots commented 6 years ago

This was an insightful thread to read through. Thanks @owocki for opening this issue. As I understand it, with 0x contracts the user initiates an allowance on their token balance, then subsequently creates or fills an order leveraging that allowance. The allowance doesn't have to be unlimited, but it is more gas efficient.

As @gtaschuk mentioned an alternative to allowances is to use an escrow contract. Very cool what he's done and I'm starting to look over that code. I think the ideal solution for the simple subscription scenario wouldn't require moving funds into a separate contract. I'm not sure what the mechanics would look like exactly, but the main benefit I see of a UX similar to 0x is that the user wouldn't have to first transfer Ether (or wrapped ether WETH) back into their account in order to spend it elsewhere.

dpyro commented 6 years ago

Lot of exciting ideas here! I think a common subscription interface would be incredibly powerful for the Ethereum ecosystem for both users and providers and would spurn further innovation--much like ERC-20 has already done. Here are some thoughts:

The subscription period should be widely variable and not focused around a 30 day timeframe. The minimum subscription period is likely 1 block. Add in a micropayments channel and you can charge users in up to ~15second intervals. Think of Amazon Web Services, for example, where you can elect to subscribe by the minute.

The provider should deploy a subscription contract. They have the greatest need to tailor payment schedules according to the services. The user could optionally use a contract to manage their subscriptions. For example, they could prioritize certain services over others.

A zero payment should be valid. One use case is a notification subscription.

Payments may be flexible. A subscriber should be able to propose both a payment and a time period. This should be submitted through a payment proposal verification contract that may or may not be part of the rest of the subscription contract which enables third party hooks. This contract should either approve or reject the payment in its entirety. Only the final, accepted payment should be recorded in the blockchain. This is similar to how 0x relay providers work. Each may provide its own fee schedule and can either accept or reject an off-chain into its orderbook based on the fee parameters proposed by the order maker.

Payment should support at least ERC #20 and ERC #721 but perhaps any proposed transaction. The most generic method I can think of is to allow the subscriber to propose the payment as a list of at least 0 arbitrary transactions, all of which must be invoked together. As an example, the subscriber could propose to transfer a certain amount of a certain token in one transaction signed by their key and increment the subscription expiration date associated to them in the next transaction which will be signed by the provider in the proposed link. (I believe payment channels get the procedure of this correct.) The acceptable transactions would almost certainly be highly constrained to whitelisted contracts and methods to prevent arbitrary contract execution and exploits.

Depending on the service provided, its very likely there will be no way to verify a provider rendered the subscribed services without using an oracle. What is verifiable is the existence of a payment to that provider. For this reason, a prepay model of crediting is the pragmatic choice.

I'm fairly new to this but I do think we should try to think beyond what we are used to and instead try what is both pragmatic and possible.

owocki commented 6 years ago

From the reddit thread:

Wonder if this might dovetail in some way with the recent discussion of requiring "rental" for blockchain space.

Article in coindesk about the same

owocki commented 6 years ago

someone just mentioned to me that maybe somehow ethereum alarm clock could be built upon the standard. not saying i think its the right path. just putting that thought on the table

https://github.com/ethereum-alarm-clock/ethereum-alarm-clock

pipermerriam commented 6 years ago

@owocki I think you are correct with respect to the concept of recurring schedules (cron) which was always a planned feature.

kosecki123 commented 6 years ago

@owocki this definitely can be built using ethereum alarm clock. The way eac works is more or less what @alexvandesande proposed, signed cheques. An example implementation for DelayedPayment is quite close to something that can handle recurring payments.

Signed cheques approach using ETH makes user to deposit their funds on a contract that's going to used to serve subscriptions (subscription wallet). For e.g given annual subscription to deposit 12x payments upfront. One way to improve that is to wrap the ETH using WETH approach, and follow the allow/transferFrom functionality.

Alternatively users could fill the subscription wallet before subscription payment date, but that effectively makes this solution just an overhead, users can send the payment directly to provider instead.

nieldlr commented 6 years ago

Hey everyone,

I decided to start scratching around some experimental code to get a better idea in my head on how a standard might play out based mainly on @pipermerriam's ideas & what a few others have shared here: https://github.com/nieldlr/subscription-experiments

In my view, standardization should usually follow from actual use (bottom-up), or at least in close collaboration with defining a standard before hand (top-down). The reason why I mention this, is that after some experimentation I still believe that having the provider collect payments from each subscriber is not preferable.

My code above is not 100% optimized yet. I'm not the best solidity dev as I mentioned. If there's anyone that can upgrade these to make the flow smoother please do. Check out the tests + code for running simulations.

Even using batch payments, a token transfer usually is around 40,000 to 100,000 in gas costs. So for scaling purposes if you take a conservative approach a single collecting payment, 40,000, then you can feasibly have about 25 subscribers before the cost reaches 1mil in gas costs. As I mentioned, I already have 30 accounts funding me on StakeTree. So these gas costs reaches very unfeasible ranges very quickly.

In terms of scaling the protocol to account for these issues, it's very hard for me as a product dev to take these risks right now. Thus, I'd be happy to help with forming a standard that involves a payment per subscriptions setup, but it's not something I'd be comfortable in using at StakeTree right now.

This brings me back to standardization that follows use. I'd be curious from other projects who have implemented similar subscription-type systems how things have scaled for them and what optimizations they have considered. I'm struggling to see how subscriptions can scale if they don't funnel in to many:1 type of contract. Ie many subscribers pooling funds into a contract where the provider can withdraw from.

With that all said. I'd like to see more devs run simulations with my code or their own code (let's get the code out there and see what happens!). I'm not confident I have the best approach right now and might be getting these specs horribly wrong. I also really don't want to provide stop energy here. I want to keep seeing more ideas and possible ways to make this scale.

Thanks for reading.

d10r commented 6 years ago

Very valid use case, however I don't get why we stick to a model of recurring payments.
We can finally hack money. Why not implement such use cases with continuous transfers?

@aantonop already talked about Streaming Money in 2016. He imagines it implemented with payment channels (lots of micro txs). It is however possible to implement this on-chain, getting rid of several drawbacks (mainly: no need to keep a hot wallet and/or preliminary deposit).

We are working on a native implementation (Parity fork) with the goals of allowing continuous transfers for both the base coin and also for tokens (providing builtin primitives for significantly reduced tx costs).
While our modifications break the protocol (we deploy it to a new chain), it is possible to implement continuous transfer for Ethereum tokens - I did an ERC-20 based Solidity implementation a while ago as PoC.
More details on this approach can be found here.
If somebody wants to know more (e.g. see the PoC Solidity code - I don't yet feel comfortable publishing it bcs of the fear of copy cats burning the idea in a scam ICO), feel free to approach me, e.g. on gitter.

kermankohli commented 6 years ago

@nieldlr I've been following this thread for a while and think I've figured out a way to solve the gas cost problem you've been facing/talking about. Essentially I'm creating a protocol to facilitate recurring payments and would love to chat more about it with you. How can I reach out to you to discuss this more in detail?

owocki commented 6 years ago

@kermankohli awesome! would you be able to type up a summary (either here or on a medium post) about your solution!?

owocki commented 6 years ago

hey everyone on this thread,

i am having a call with several of you next week in order to discuss turning this ERC into an actual EIP pull request. in the spirit of keeping the leadership here distributed across the community, you are all invited to join! if you would like to join, please reach out at kevin@gitcoin.co or on the gitcoin slack

kermankohli commented 6 years ago

@owocki @nieldlr @alexvandesande @pipermerriam @gtaschuk @ptrwtts @dpyro @ScottStevenson @niran

Firstly, I'd like to thank all of you for your ideas/discussion here. I've spent a lot of time thinking about what all of you have said, and as a result been able to come up with this solution.

Without further ado, here's a potential solution for implementing recurring payments on Ethereum: https://github.com/8x-protocol/whitepaper

I've already written a bit of code at: https://github.com/8x-protocol/8x-protocol in case you'd like to check out a very barebones implementation (or would like to help out!)

Excited to hear what you guys think or suggestions for any major improvements which you think could be made!

ps: @pipermerriam I've mentioned Ethereum Alarm Clock and you in the references :)

owocki commented 6 years ago

@kermankohli looks like you have some good ideas on this. if youre interested in making some of this an open standard, want to join our call this week? email me: kevin at gitcoin dot co

kermankohli commented 6 years ago

Done. Look forward to hearing back from you :)

On Mon, 30 Apr 2018 at 11:03 PM Kevin Owocki < Kevin Owocki ( Kevin Owocki notifications@github.com ) > wrote:

@kermankohli ( https://github.com/kermankohli ) looks like you have some good ideas on this. if youre interested in making some of this an open standard, want to join our call this week? email me: kevin at gitcoin dot co

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub ( https://github.com/ethereum/EIPs/issues/948#issuecomment-385391881 ) , or mute the thread ( https://github.com/notifications/unsubscribe-auth/AB9LcYiHbfZO42OJcs2s8_ehsvwH-hAfks5ttwuygaJpZM4S6OSD ).

nieldlr commented 6 years ago

Heya @kermankohli,

spent some time today to read through the whitepaper. Thanks for all the effort on that! Some really interesting & cool ideas there. Here's some unstructured thoughts on it (sorry in advance for the randomness of it):

1) I like the idea of outsourcing the payments to "processors/executors". This could really help with not putting the burden on either the subscriber or provider (except for a consistent small fee of course).

2) A 1% fee feels plausible in as much as it is should always be higher than the gas cost. I wonder if this could be pushed lower? Not sure of the economics here. But the main reason is mainly that it'd be great if a subscription service on Ethereum could deliver the cheapest subscription service possible (because no massive middlemen companies taking cuts). If we can't compete with existing services, then it might be tricky to make this viable alternative for dapps. (Another related thought: perhaps there's a minimum/maximum fee?)

3) Who pays the fee by the way? From my rough understanding, the fee is paid by the subscriber? But I guess you can easily flip this around/split in whichever way you want?

4) Not 100% sure if an additional token is needed or not. I always try and push for not introducing additional tokens unless they add more value than the additional layer it adds. There's additional complexity & additional risk in adding another token + the economics of it. However, I'm totally open for it to be there if needed. I still need to think on this for a little bit to get my head around it. Is this a token use case that's used by other projects by any chance?

Also, just curious, totally might be me still mending from the flu, I don't quite understand why if a payment is claimed early, they get less percentage of the fee? Don't we want people to compete for payments to get in as early possible to ensure that payments will succeed?

5) I recall that CryptoKitties had a bounty (paid for by the users), that allowed anyone to call a function to birth the kittens when the right amount of time had passed. I wonder if this could also be a similar idea here? Each payment that needs to be triggered has a small bounty for the executor.

6) This additional complexity in a system/protocol like this at the same time could be a solution but also adds an additional layer of maintenance. I'm totally okay with that, it just makes me a bit nervous sometimes. So I'm always trying to balance the additional complexity versus what is exactly needed.

7) There's a lot to think through here. But I'm feeling some thoughts take seed in my head. I need to marinade on this for a bit more to get the whole gist (and possibly share more ideas here). I think I keep coming back to a point I made in an earlier post, in that, how can we find a solution that derives from actual use. But I've always had that bias, sometimes to the extreme. However, that shouldn't stop us from finding the right solution. As a dapp developer, the question I ask is, how can I implement subscriptions in a secure, trustless, stable way that is easy to use and costs are low? If I depend on an additional protocol (and other parties to execute payments), it feels more risky, but it could very well be a risk worth taking for the benefit it provides. A protocol like this could be so fundamental to the Ethereum ecosystem, it might be very well maintained & stable.

Thanks again for this. I think this has got some really neat ideas. Excited to think through this a bit more. My brain has a been a bit mushy from the flu the past few days, so hopefully it clears so I can think about this a bit more solidly.

kermankohli commented 6 years ago

@owocki Just got the invite and turns out the event is 12am for me today (Sydney, Australia). Is there any way we could pull back the meeting 3 hours or reschedule it?

kermankohli commented 6 years ago

@nieldlr Firstly, thanks so much for spending the time to go through it and letting me know your thoughts on it! There's some great points you've mentioned and want to address each of them so here goes:

Point 2 + 3: I initially had the same thoughts as you when I came up with this (gave me a bit of anxiety too haha), although it's worth keeping into account existing methods of payment. Stripe + Paypal charge 2.9% + 30c fee for transactions made on their platform. Since the execution of payments isn't that critical you can safely use 1 gWei to interact with the smart contract (about 30 minutes to execute). Assuming you take worst case calculations, it costs 30c for 450,000 gas at the price of 1 gWei. Of course you wouldn't to charge that much but it kinda shows how leeway you have before it becomes sort-of infeasible. However you do need to take into account the fact that Ethereum's price is at $500, and if it ever goes to $1500 your worst case is reduced to ~ 115,000 gas. Might need to pre-purchase gas or something to solve that problem.

Point 4: In addition to using the token as a fee for using the network, it'll also be used for governance. Processors who execute a lot of transactions will want a say in the way the protocol operates as they have a significant investment in it. There's also a lot of time and money involved to create good interfaces, SDKs, global fiat settling layers etc. A token sale would help to build that out a lot quicker.

Point 6 + 7: Right now Ethereum is focused on getting sharding + POS up and running so not sure how long it'll take for this to get the proper support it needs. In regards to complexity, if the system can be designed in a way that allows dApps and businesses to accept recurring crypto payments while hiding the complexity away for them - I think that's something to be proud of :)

Hope you get well soon though!

androolloyd commented 6 years ago

@kermankohli Would love to connect, what you're describing is a lot like what we're doing for Groundhog(https://Groundhog.network).

If you're able to make the call would love to connect afterwards.