Building a Subscription Service in Solidity

Building a Subscription Service in Solidity

Table of contents

I see how much progress has been made in the Account Abstraction space, and I’d been thinking of practical use-cases for this, so I went ahead to build a Netflix-esque subscription service smart-contract. I strongly believe this SC will pave the way for something special.

This Smart Contract will be making use of the contract automation feature in Gelato.

Before I started building out the smart contract, I needed to outline what was needed:

  1. Subscription Capability: The ability for an address to seamlessly 'subscribe' to a service or product.

  2. Time Duration: A designated time period during which the 'subscription/payment' remains valid.

  3. Subscriber Management Pools:

    • TotalSubscribers Pool: To track the overall number of subscribers.

    • ActiveSubscribers Pool: Monitoring the currently active subscriptions.

    • InactiveSubscribers Pool: Managing subscriptions that are currently inactive.

  4. Withdrawal Mechanism: A method facilitating the withdrawal of funds to a specified address.

  5. Subscription Purchase Method: Enabling users to initiate the subscription process.

These were non-negotiable requirements to hit the ground running. While coding, i realized i needed a way to ‘refresh’ the ‘ActiveSubscribers’ pool, that’s where the expireSubscriptions method comes in — The expireSubscriptions expires subscriptions that are past their renewal date. Now i know you’e thinking that smart contract code has no way to run automatically, will this method be called manually, every time? Yes and No. That’s where Gelato comes in — more on this later in the article.


Allow me to walk you through the code.

The SubscriptionService smart contract in Solidity offers a straightforward solution for managing subscriptions any where solidity smart contract code can run.

Understanding the Solidity Code

Before we start building our blockchain subscription service, let's break down the key components of the SubscriptionService smart contract.

Creating the Smart Contract

The SubscriptionService contract begins with a constructor. The constructor initializes the contract with subscription price and duration. The constructor takes two parameters: _priceInEther and _duration. These parameters set the subscription price in Ether and the subscription duration in seconds.

constructor(uint256 _priceInEther, uint256 _duration) {
    owner = msg.sender;
    subscriptionPrice = _priceInEther * 1 ether; // Convert to wei
    subscriptionDuration = _duration;
}

This constructor sets the contract's owner, subscription price, and duration. The owner, typically the address that deployed the smart contract, has the privilege to manage the contract and withdraw collected funds.

Purchasing a Subscription

The purchaseSubscription function allows users to buy a subscription by sending the correct amount of Ether. It ensures that the sent Ether matches the subscription price and calculates the new expiry time.

function purchaseSubscription() public payable {
    require(msg.value == subscriptionPrice, string(abi.encodePacked("Incorrect payment amount. Payment should be ", subscriptionPrice, " wei")));
    uint256 newExpiry = block.timestamp + subscriptionDuration;
    // ...
}

This function is equipped to handle both new subscriptions and renewals. For new subscribers, it adds the user to the list of all subscribers and active subscribers. For renewals, it updates the subscription expiry time.

Subscriber Management

The isSubscriber, getSubscriptionExpiry, and getActiveSubscribers functions are essential for users to check their subscription status and obtain relevant information.

function isSubscriber(address user) public view returns (bool) {
    return block.timestamp <= subscriptionExpiry[user];
}

function getSubscriptionExpiry(address user) public view returns (uint256) {
    return subscriptionExpiry[user];
}

function getActiveSubscribers() public view returns (address[] memory) {
    return activeSubscribers;
}

These functions empower users to validate their subscription status and retrieve their subscription's expiry date. The getActiveSubscribers function provides a list of all current subscribers.

Managing Inactive Subscribers

In the SubscriptionService, you can identify inactive subscribers using the getInactiveSubscribers function. This function collects all subscribers who have let their subscriptions expire.

function getInactiveSubscribers() public view returns (address[] memory) {
    address[] memory result = new address[](allSubscribers.length - activeSubscribers.length);
    uint256 resultIndex = 0;
    // ...
}

The getInactiveSubscribers function helps in cleaning up the list of active subscribers and keeping track of those whose subscriptions have lapsed.

Financial Operations

The SubscriptionService also includes financial management features. The withdrawFunds function allows the contract owner to withdraw the collected subscription fees.

function withdrawFunds() public onlyOwner {
    payable(owner).transfer(address(this).balance);
}

This function ensures the secure withdrawal of funds, protecting the owner's earnings.

Expiring Subscriptions

To automatically expire subscriptions past their renewal date, the expireSubscriptions function is implemented. This function iterates through the list of active subscribers and updates their subscription status as needed.

function expireSubscriptions() public {
    for (uint256 i = 0; i < activeSubscribers.length; i++) {
        if (block.timestamp > subscriptionExpiry[activeSubscribers[i]]) {
            isSubscribed[activeSubscribers[i]] = false;
            subscriptionExpiry[activeSubscribers[i]] = 0;
        }
    }
}

This process keeps the list of active subscribers accurate and up-to-date.


Automation With Gelato

Now that your Smart Contract is deployed, the next step is to automate the execution of the expireSubscriptions method — This function iterates through the list of active subscribers and updates their subscription status as needed. We need to automate its execution for our dApp to manage subscriber status effectively. Here’s how it works:

  • Connect your wallet to Gelato

  • Create a task

  • Get the contract address of your Dapp, and paste it here 👇🏻

  • Select the function you want to automate. In this case ‘expireSubscriptions

  • Set when you want to execute it

  • Deposit funds for the transaction

  • Create your task!

Your smartcontract tw just got smarter! Now, whenever the conditions you set are met, the ‘expireSubscriptions’ code will be called, and the status of your subscribers will be updated if there are any changes.

Full code on GitHub: https://github.com/geniusyinka/nextjs-solidity-sub