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

Open
owocki opened this Issue Mar 25, 2018 · 83 comments

Comments

Projects
None yet
@owocki

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

This comment has been minimized.

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

This comment has been minimized.

jrmoreau commented Mar 25, 2018

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

ScottStevenson commented Mar 26, 2018

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

This comment has been minimized.

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

This comment has been minimized.

marclijour commented Mar 26, 2018

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

This comment has been minimized.

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

This comment has been minimized.

Contributor

MicahZoltu commented Mar 26, 2018

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Josephrp commented Mar 28, 2018

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

This comment has been minimized.

charlieknoll commented Mar 28, 2018

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

This comment has been minimized.

Member

pipermerriam commented Mar 29, 2018

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

This comment has been minimized.

Member

pipermerriam commented Mar 29, 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.

@ptrwtts

This comment has been minimized.

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

This comment has been minimized.

Member

pipermerriam commented Mar 29, 2018

@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

This comment has been minimized.

cryppadotta commented Apr 2, 2018

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

This comment has been minimized.

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

This comment has been minimized.

Member

pipermerriam commented Apr 3, 2018

@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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Member

pipermerriam commented Apr 4, 2018

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

This comment has been minimized.

Contributor

alexvandesande commented Apr 6, 2018

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.

@kosecki123

This comment has been minimized.

kosecki123 commented May 31, 2018

Date math - the pipermeriam library looks good but it only deals with date units as points in time. It does not deal with time intervals or date maths. E.g. when we are on month 12 and we want to increment the nextPaymentTime by a month, it can’t tell us how to do this. I’m reluctant to implement a date/time math library so if anyone has any alternatives suggestions I’d love to know.

@johngriffin I came accros this library https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary from @bokkypoobah

@owocki

This comment has been minimized.

owocki commented Jun 1, 2018

hey everyone.. ( i mean this in the least shill-ey way possible ) the working group creating this standard is really starting to pick up steam...

if you want to join... there is a slakc channel here => https://gitcoin.co/slack -- msg me and ill invite you to it
and here are our notes https://docs.google.com/document/d/1P1fhGAJS_IYubUUEHEmPpg2ltjMZy7PR5Ot8xuJRbS8/edit#

@keymethod

This comment has been minimized.

keymethod commented Jun 7, 2018

What are the advantage of a new token over designing a wallet that would send recurring payments at set periods? I'm thinking that this proposed token is advantageous for accounting and payment management of the service provider, how about the user?

@sb777

This comment has been minimized.

sb777 commented Jun 7, 2018

Awesome work @johngriffin and @Davidkobro!

If we allow payeeAddress to be set by the subscriber when they createSubscription() we probably need the contract to contain a whitelist of valid payeeAddresses.

Do you mean, set up before the call to createSubscription, to compare against, or afterwards to keep track of which address was set?

Perhaps there other constraints on valid subscriptions that the payee might want to implement when deploying the contract - e.g. only certain tokens / amounts are valid.

I think so. My instinct is to leave the implementation of this outside of the spec, and just revert().

What is the purpose of isToken in the signature for createSubscription() ? Surely all payments have to be in tokens rather than ether as you can't delegate authority for a contract to move ether from your wallet in future.

Sorry, this was not clear enough. Let me expand a bit on the thinking behind this.

The model we're proposing would allow ETH as well by essentially creating an "escrow" wallet contract for all your subscriptions, where the wallet contract itself holds the funds/tokens, which would enable it to send out both ether and tokens.

The subscriber contract needs a whitelist of addresses which can call the subscriber-side management functions. At minimum, this can be the owner/contract deployer. In Groundhog, we are allowing user to authorize multiple devices/wallets (hw wallet, browser extension, Groundhog mobile wallet, other mobile wallet). Other contracts could even be whitelisted depending on what functionality people want to introduce. But in general our thinking is leave that up to the implementer.

processPayment() will only process one payment for one subscription at a time. If there are multiple billing periods for which payments haven’t been processed on a given subscription, then a transaction will have to be made for each of them. Of course it is possible to make a more efficient way of collecting these but doesn’t need to be part of the spec.

This is an interesting point. There was some discussion of whether "outstanding due payments" should be all sent at once or pulled one at a time. I'm inclined to think that processPayment should behave like a call to a credit card or a bank account - always have a value parameter and pull only that value, success/fail -- but -- if there are further outstanding payment amounts, multiple/subsequent calls can be made until there is no more available/due.

One other thought - Since one of the use cases we are trying to enable is the iTunes/API model, where you can approve up to X amount, and make subsequent calls throughout the period as that balance is spent - I wonder if we should add a mode flag on subscriptions to indicate this subscription type, so that balances don't roll over by period.

For example, if I'm authorizing an iTunes style subscription for up to $20/month, I don't want unspent amount to roll over into the next period as available balance. Maybe an unspentRollover flag?

The reason this is needed is, if it's just a garden variety $20/month SaSS subscription, if for whatever reason the merchant calls the subscriber wallet for a $10 payment, it should then be able to call another subsequent $10 payment in order to bill the full due amount. The only other way to do it would be to not specify a billing amount in the call, and just have the contract send the entire amount that's due when processSubscription is called. Then to enable the iTunes model, a flag could be set for discretePayments in which case a value is required for processSubscription.

So if we want to enable both "whole amount" billing and "up to billing" for the two identified use cases, it seems that we need either unspentRollover or discretePayments as a flag when creating the subscription. (there may be better names for these variables.)

And to @nathantr's point about subscriptions vs payments, I wonder if the function should actually be called processPayment rather than processSubscription.

@sb777

This comment has been minimized.

sb777 commented Jun 7, 2018

Oh, and just to introduce myself to the people on the thread I haven't met yet, I'm Scott, one of the co-founders of The Groundhog Network, along with @androolloyd. We're building Groundhog to supercharge Ethereum e-commerce.

Introducing Groundhog: https://medium.com/groundhog-network/introducing-groundhog-a1099f98ae84

@johngriffin

This comment has been minimized.

johngriffin commented Jun 9, 2018

Hey, so following the last call and chat on slack I just wanted to put forward what I propose to be the most important topics that we need to agree on. There are lots of great ideas flying around but we need to decide what should and shouldn't be part of this standard in order that we can agree on the technical spec.

Why does this need to be an ERC standard?
Anyone can create and implement a smart contract that has this functionality, what is the advantage in defining a standard interface? Answering this question should help us to answer all the other questions about what should / shouldn’t be included in the standard.

I suggest the reason for this standard is interoperability - we want wallets to understand that you’re about to sign a recurring payment contract so that they can present you with a UI that summarises the agreement you’re about to enter into. As your wallet now knows you’ve entered into a subscription contract it can also provide appropriate UI for managing and cancelling your subscriptions in future.

Should the standard deal with ETH escrow or just ERC-20 token payments?

  • Option 1 - payments must be made with ERC-20 tokens. The tokens remain in the subscriber’s wallet until a valid payment is requested by the merchant. The contract then transfers tokens directly from the subscribers wallet to the merchant.

  • Option 2 - (copied from @sb777 's post above) - The model we're proposing would allow ETH as well by essentially creating an "escrow" wallet contract for all your subscriptions, where the wallet contract itself holds the funds/tokens, which would enable it to send out both ether and tokens. The subscriber contract needs a whitelist of addresses which can call the subscriber-side management functions. At minimum, this can be the owner/contract deployer. In Groundhog, we are allowing user to authorize multiple devices/wallets (hw wallet, browser extension, Groundhog mobile wallet, other mobile wallet). Other contracts could even be whitelisted depending on what functionality people want to introduce. But in general our thinking is leave that up to the implementer.

Rules around payments and billing periods
As @nathantr pointed out above it’s important to define the terms “billing” and “payment”. So we have billing periods and then payments that can be “pulled” within those billing periods. When the subscription is created there is a maximum amount that can be pulled per billing period.

  • Do we want to support multiple payments being pulled per billing period, upto the maximum
    allowance? Or merchant can only pull one payment per billing period.

  • Do we want to support pulling payments for past billing periods that have already elapsed, like a roll-over? See unspentRollover flag as mentioned by @sb777.

@nathantr

This comment has been minimized.

nathantr commented Jun 10, 2018

Some great points @johngriffin. I can't say I fully follow the options @sb777 is proposing for ETH vs ERC-20, but having a single wallet that holds all your subscriptions definitely sounds appealing. Half of the problem of subscriptions is simply keeping track and being in control of all of them.

Re: payments and billing periods. I think having a notion of billing period is necessary (this is the periodType in the proposed spec I presume), and then pulling multiple payments per billing period would allow you to service a broader range of use cases. E.g., usage. If I as the merchant charge 1 USD per GB of Storage, then I am allowed to charge you up to, say, $10 for 10 GB of Storage. At which point you would need either raise the "cap" or start a higher cap subscription relationship with me.

The unspentRollover question I would be less in favor simply because of the complexity it creates. It really, at least to me, seems to be a flag that favors more advanced usage based billing use cases, and not sure if in a standard we want to start tracking some notion of unspent "credits". It would be great if this is something the implementer could do, e.g. they could check for the unspent amount and then use that to alter the "limit" of the subscription in the subsequent period, without needing the standard to do so explicitly.

I'd be an advocate for keeping it simple, and saying you can pull any amount of payments you want in a certain period up to an authorized limit. But it's a use it or lose it type deal, and anything unused does not get rolled over into the next period.

@owocki

This comment has been minimized.

owocki commented Jun 14, 2018

Update: The group will be using this repo to collaborate on the EIP: https://github.com/EthereumOpenSubscriptions/standard

@Bergqvisten

This comment has been minimized.

Bergqvisten commented Jun 19, 2018

Hello everyone,

Thanks for the interesting read, I very much agree that there should be a standard for this type of service. What do you think of adding a timeSubscribed variable to keep track of the time a customer has been subscribed? A lot of subscription services appreciate loyal customers and often reward long time subscribers. The reason block.timestamp - startTime isn't enough for keeping track of this is because of the pauseSubscription() function, which will create empty time slots that shouldn't be counted.

@nathantr

This comment has been minimized.

nathantr commented Jun 20, 2018

@Bergqvisten welcome! I think while pause resume does tend to throw off the notion of how long you have been an active subscriber, it doesn't affect the fact you may have signed up, for example, for a 12 month subscription. As long as you know when the pause and resume happened you could then calculate the impact on the "active" subscription length.

Typically with pause/resume you might build in a parameter to automatically extend the subscription when you resume, if you know it. that might be nice @sb777

@Bergqvisten

This comment has been minimized.

Bergqvisten commented Jun 20, 2018

Thanks @nathantr! You're right, that would totally work for a subscription plan with a specified length. I think the implementation I had in mind was a little bit different, hence the requirement for a total time subscribed.

I read about the problem of gas cost earlier in the thread. Is this something that has been solved to an acceptable degree yet?

@kosecki123

This comment has been minimized.

kosecki123 commented Jun 21, 2018

As an extension to @pipermerriam proposal, I think having definitions for functions to return the status of the subscription would be beneficial

  • isFunded() returns bool: returns true if there is currently enough funds (ETH or tokens, depending on implementation) available for provider to collect
  • availableFunds() returns uint256: returns the amount of ETH or tokens available for the provider

Major benefit would be an ability for subscription provider to monitor the current status of the subscription without using gas (for e.g by executing triggerPayment()). For the subsribee perspective would allow scenarios where 3rd party protocol can monitor and fill the subscription accounts for e.g keeping the funds to pay for next 1 or 2 months

@nathantr

This comment has been minimized.

nathantr commented Jun 21, 2018

@owocki

This comment has been minimized.

owocki commented Jul 12, 2018

Update from the working group: We are drafting the EIP at EthereumOpenSubscriptions/standard#8 . All are welcome to contribute. Goal is to have this submitted by early August

@PaulRBerg

This comment has been minimized.

PaulRBerg commented Jul 17, 2018

Hi everyone,

I'm doing some research on state channels and fully decentralised, recurring payments, so it was pretty cool to read through all the ideas brainstormed here! Definitely a great initiative @owocki.

Static, time-based subscriptions totally make sense, but I want to challenge the underlying assumptions in https://github.com/EthereumOpenSubscriptions regarding dynamic pricing. Why would a company like AWS want to use ERC948 instead of asking the user to pay per unit? That is, when they consume 1MB on S3, they are charged $0.1 (arbitrarily set price). Thanks to Raiden, Liquidity & co, this will be possible in the future once the tech consolidates.

I acknowledge the fact that on state channels one has a balance they cannot go over (although there are proposals to remedy this), while on ERC948 they could be continuously charged from an address, allowing them to top up. This is handy for long-term subscriptions.

However, given low mental transaction costs, I posit that the advantages of pay-per-unit outweigh the disadvantages of billing at the end of the month.

@owocki

This comment has been minimized.

owocki commented Jul 17, 2018

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

@PaulRBerg

This comment has been minimized.

PaulRBerg commented Jul 17, 2018

Sure thing! Emailed you now.

@Josephrp

This comment has been minimized.

Josephrp commented Jul 17, 2018

@owocki

This comment has been minimized.

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

This comment has been minimized.

PaulRBerg commented Aug 16, 2018

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

This comment has been minimized.

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

This comment has been minimized.

androolloyd commented Sep 27, 2018

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment