Before you go, check out these stories!

0
Hackernoon logoSubscriptions on the Blockchain by@kaushik4real

Subscriptions on the Blockchain

Author profile picture

@kaushik4realKaushik Devireddy

Implementing a subscription model for a decentralized application (ethereum)

Backround Photo by Luca Bravo on Unsplash | Ethereum Logo | Tinder Gold Logo

Introduction
You may have heard that decentralized applications are going to be the future of the internet. In order for this decentralized ecosystem to thrive and be sustainable, we will need many developers to work on applications. These developers obviously want to make money; but since decentralized applications are so new, there’s a lot of confusion about how you can make money from them. I believe that a few traditional revenue models will work on the blockchain. So I’m creating a series where we implement them as an example for future applications. In this post, we learn when and how to implement a subscription model in Solidity.

Viability — “When”
Before we get into the code, let’s talk about when subscriptions can work on the blockchain. When your application is decentralized, anyone can see the contracts that comprise the application; this means they can copy and paste your code to replicate a big part of your product instantly. If the front-end is open-source, or hosted on decentralized storage, all of your application code can be cloned.

So what can we do to keep users on our app? The answer lies in one element that cannot be cloned: the network. If your application has network effects, users will tend towards the app with the better network, or larger user-base. Apps that exhibit network effects include social apps such as Tinder, Facebook, and Instagram. I like to think of this as being similar to the Nakamoto Consensus, where people choose the longest chain as the valid one.

Going back to subscriptions, the worth of a subscription in the example of Tinder, is somewhat related to the number of users on Tinder. If someone clones Tinder, it will be worthless because little to no users will be on the clone. No one will subscribe for Superlikes on the clone, because there is no one for them to use their Superlike on.

In summary, a subscription model may work with your dApp if it exhibits network effects and has an established user base. To be additionally resilient to clones, some subscription features themselves should also exhibit network effects.

Implementation — “How”
In a subscription model, users can pay to unlock access to special features for a certain amounts of time. Of course, in order for the users to pay a recurring fee to use the product, it has to be top notch. So for our example, the premium feature will be from one of the best subscription products: Tinder Gold.

To implement this model, we will create a way to track subscribers and make premium functions that can only be accessed by them. All of the code examples below can be found on Github.

We will start by making two structs: one for a user and their Tinder Cards and another for their subscription. We will assume the data (images, etc.) for each Tinder Card is stored on the IPFS, a peer-to-peer protocol for the web. To reference that data we can use a hash of the stored card.

User public userInfo;
struct User {        
address userAddr; // user's ethereum address
Subscription subInfo; // store their subscription
bytes current; // hash of currently displayed card
bytes previous; // hash of previous swiped Card
}

struct Subscription {
bool status; // store if the user is subscribed (T/F)
uint expiry; // when the user susbcription expires
}

Next, a constructor to create a user and set their subscription status to false. The expiry is set to the current time, we’ll see how this is used later on. For the Tinder Cards I used some arbitrary hash, it’s specific value isn’t that relevant for our purposes.

constructor() public     
{
userInfo = User({
userAddr:msg.sender, //The address of the user
subInfo:Subscription({status: false, expiry: block.timestamp}),
current:"Qm78fg903b9209rh20f03dla",
previous:""
});
}

Now we need a way for users to pay and subscribe. Let’s make a payable function that changes a user’s subscription status. To subscribe for one month it will cost 1 ETH. They can subscribe for any mutiple of one month by just calling the function the appropriate amount of times.

function subscribe() public payable     
{
require(msg.value == 1 ether); //or else function will throw
userInfo.subInfo.status = true;
userInfo.subInfo.expiry = block.timestamp + 30 days;
}

You may notice, that ether is being paid to the contract, not the app owner. There needs to be a way for the owner to receive their newfound riches. To do this, we can store the address of the owner and add the following line to the subscribe() function.

address public appOwner = 0xac4013A20D0FDb5908673CBCD4d400e3DC68726b;
function subscribe() public payable    //function from above
{
// ... omitted lines

appOwner.transfer(1 ether); //sending the money to the owner
}

For the final part of the subscription handlers, we need an internal way to check if a user is subscribed or not. This is a little more complicated than just checking if the status variable is true or false because when a subscription expires, the contract can’t automatically make the status false. To account for this, we need to also check if the current time is past the expiry timestamp we set when the user subscribed.

function checkSubscription() internal returns(bool status)    
{
if(userInfo.subInfo.status != true) {
// if the user is not subscribed
return false;
}
if (block.timestamp >= userInfo.subInfo.expiry) {
// if the subscription has expired
// update status to false
userInfo.subInfo.status = false;
return false;
} else {
return true;
}
}

Cool. Now that we have all this done, we can implement our premium function for our subscribers. Tinder Gold subscribers have a feature called Rewind, which lets you take back your last swipe. We can utilize our previous function checkSubscription() to ensure the user is a subscriber before allowing them to Rewind.

function rewind() public returns(bool success)    
{
if(checkSubscription() != true) {
return false;
} else {
userInfo.current = previous; //rewinding swipe
userInfo.previous = "";
return true;
}
}

Finally, we can create a public function to check if the user is subscribed without using gas. This is important so that the front-end of the application can avoid displaying features that the user doesn’t have access to (which would waste gas if incorrectly called). The keyword that makes this function gas-free is “constant”.

function peekSubscription() public constant returns(bool status)     {        
if(userInfo.subInfo.status != true ||
block.timestamp >= userInfo.subInfo.expiry) {
return false; //user is not subscribed
} else {
return true; //user is subscribed
}
}

The reason we have to make a new function instead of using the checkSubscription() function from before, is because checkSubscription() needs to make a state change if the subscription has expired. Therefore, that function can’t be made constant.

The End
Alright, that concludes our foray into the subscription model. Remember, this will only work if your app has network effects and a big userbase. Now go make racks with dApps!

The complete contract code is on Github. This was my first time writing Solidity code, so do not use the contracts without vetting them yourself.

Found a mistake? Have suggestions or feedback? Comment down below.

Read more about me at kaushikdevireddy.com

Tags

Join Hacker Noon

Create your free account to unlock your custom reading experience.