Skip to content

An extension of the ERC20 token standard that allows for a new type of approval. This contract enables an owner to approve a spender to spend a fixed amount of tokens on their behalf, with a custom recurrence interval and for a specified duration of time. This allows for the creation of subscriptions using ERC20 tokens.

License

Notifications You must be signed in to change notification settings

0xJord4n/ERC20Subscription

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ERC20Subscription

test GitHub forks GitHub stars

ERC20 extension for subscriptions

An extension of the ERC20 token standard that allows for a new type of approval. This contract enables an owner to approve a spender to spend a fixed amount of tokens on their behalf, with a custom recurrence interval and for a specified duration of time. This allows for the creation of subscriptions using ERC20 tokens.

Installation

Forge:

forge install 0xJord4n/ERC20Subscription

Manually:

Take the content of the file ERC20Subscription.sol in src directory and import it in your project

NPM:

Soon

Interface

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IERC20Subscription {
    function permitForSubscription(
        address owner,
        address spender,
        uint256 value,
        uint32 recurrenceInterval,
        uint48 approveUntil,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (bool);

    function approveForSubscription(
        address spender,
        uint256 value,
        uint32 recurrenceInterval,
        uint48 approveUntil
    ) external returns (bool);

    function allowanceForSubscription(
        address owner,
        address spender,
        uint32 recurrenceInterval,
        uint48 approvedUntil
    ) external view returns (uint256);

    function increaseAllowanceForSubscription(
        address spender,
        uint256 addedAmount,
        uint32 recurrenceInterval,
        uint48 approvedUntil
    ) external returns (bool);

    function decreaseAllowanceForSubscription(
        address spender,
        uint256 removedAmount,
        uint32 recurrenceInterval,
        uint48 approvedUntil
    ) external returns (bool);

    function transferFromForSubscription(
        address from,
        address to,
        uint256 amount,
        uint32 recurrenceInterval,
        uint48 approvedUntil
    ) external returns (bool);

    function nonces(address owner) external view returns (uint256);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

Usage example

Token contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract TestToken is ERC20, ERC20Subscription {
    constructor() ERC20("Test Token", "TEST") ERC20Subscription("Test Token") {}
}

Use case (Generated by Chat GPT)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./ERC20Subscription.sol";

contract SubscriptionContract is Ownable {
    struct UserSubscription {
        uint256 amount;
        uint48[] lastPayments;
    }

    mapping(address => UserSubscription) private subscriptions;
    ERC20Subscription private token;

    event SubscriptionTokensTransferred(address indexed user, uint256 amount);
    event SubscriptionCancelled(address indexed user);

    constructor(address _tokenAddress) {
        token = ERC20Subscription(_tokenAddress);
    }

    function subscribe(uint256 amount) external {
        require(amount > 0, "Invalid amount");
        UserSubscription storage userSubscription = subscriptions[msg.sender];
        require(userSubscription.amount == 0, "Already subscribed");

        userSubscription.amount = amount;

        emit SubscriptionTokensTransferred(msg.sender, amount);
    }

    function cancelSubscription() external {
        UserSubscription storage userSubscription = subscriptions[msg.sender];
        require(userSubscription.amount > 0, "No subscription found");

        // Transfer remaining tokens back to the user
        require(token.transfer(msg.sender, userSubscription.amount), "Token transfer failed");

        userSubscription.amount = 0;

        emit SubscriptionCancelled(msg.sender);
    }

    function transferSubscriptionTokens(address[] calldata users, uint256[] calldata amounts) external onlyOwner {
        require(users.length == amounts.length, "Invalid input length");

        for (uint256 i = 0; i < users.length; i++) {
            address user = users[i];
            uint256 amount = amounts[i];
            UserSubscription storage userSubscription = subscriptions[user];
            require(userSubscription.amount >= amount, "Insufficient subscription amount");

            // Transfer tokens from the contract to the user
            require(token.transfer(user, amount), "Token transfer failed");

            // Add the current timestamp as the latest payment
            userSubscription.lastPayments.push(uint48(block.timestamp));

            emit SubscriptionTokensTransferred(user, amount);
        }
    }

    function getUserSubscription(address user) external view returns (uint256 amount, uint48[] memory lastPayments) {
        UserSubscription storage userSubscription = subscriptions[user];
        return (userSubscription.amount, userSubscription.lastPayments);
    }
}

Release History

  • 1.0.0
    • First release

Reporting Issues and Gas Optimization

We welcome and encourage anyone to actively participate in the improvement of this project. If you come across any issues, bugs, or areas for optimization, please don't hesitate to report them. By reporting issues, you contribute to the overall stability and quality of the project. Additionally, if you have suggestions or ideas on how to optimize gas usage and make the contract more efficient, we would be thrilled to hear them. While the contract has not been audited, we value community feedback and are open to collaboration to enhance its security and reliability. Your contributions, whether it's reporting issues or proposing gas optimizations, are greatly appreciated and will help us create a better product for everyone involved.

Contributing

  1. Fork it (https://github.com/0xJord4n/ERC20Subscription/fork)
  2. Create your feature branch (git checkout -b feature/fooBar)
  3. Commit your changes (git commit -am 'Add some fooBar')
  4. Push to the branch (git push origin feature/fooBar)
  5. Create a new Pull Request

Ressources used to make the project

  • Openzeppelin ERC20 Approval
  • Openzeppelin ERC20 Permit

Meta

0xJordan – @0xjord4n_[email protected] Distributed under the MIT license. See LICENSE for more information.

Donate

  • EVM Chains: 0x1814b7a2a132a816ff5bd8573b1c2bf5995d2fda
  • BTC: bc1q6wjvnldvrcaq6aafgacjy70vh32a4rc2f3mdgj
  • LTC: MCufZV9LUczQT7Fnctzh58iGPkXrQMjcK7
  • XMR: 457Yxyaguty51nD4pDAo2zTGy3a2V22gW4BsTkUUCUUa2Efng8xxKRFNMupiVu8CBGGKAcDCT7rQvhrHP5n8EJSRCjFYwa9
  • BCH: qqhpf7cepn23wwu6yxh7nn33u9dslhnkdc7m48y3u8
  • Cosmos: cosmos1jy8exr0m6j7twu3d28hd8j3j6m3fzkhgml42ng
  • SUI: 0xc0e53341b57c67c3fa1d23a10b4f28c3449e7d6c8930495b5d66236b5944c75f
  • Solana: C3VmQpDvQ8zibqQ9AByJidfsmwE1NmKFJ75x3josUFPx
  • Aptos: 0x6a9d57a6308beffa370a6efb544b54dc5d157db5252d12c299e872cfee2a8f72

About

An extension of the ERC20 token standard that allows for a new type of approval. This contract enables an owner to approve a spender to spend a fixed amount of tokens on their behalf, with a custom recurrence interval and for a specified duration of time. This allows for the creation of subscriptions using ERC20 tokens.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published