EIPs/EIPS/eip-1337.md
lightclient 3194278525
Switch validator to eipv (#2860)
* switch to eipv

* fix

* fix

* 1153 remove trailing whitespace

* remove file name checks

* 615 remo whitespace before comma

* 884 remove extra single-quotes

* 1337 remove whitespace before comma

* 1057 remove extra spaces after comma

* 2470 update created date to Y/M/D format

* 1078 update required eips to be in ascending order

* 2477 update required eips to be in ascending order

* 1271 remove extra whitespace

* 2767 required eipupdated to be in ascending order

* 2525 update created date to Y/M/D format

* 2458 remove trailing whitespace

* 1884 remove trailing whitespace

* 712 authors should be on a single line

* 601 remove extra whitespace

* 1485 remove unneeded parentheses

* 634 remove trailing whitespace

* 2657 update discussions-to to correct spelling

* 2009 remove trailing whitespace

* 998 required eips updated to be in ascending order

* 1186 remove trailing whitespace

* 1470 remove extra whitespace

* 1895 update created date to Y/M/D format

* 2747 remove extra whitespace

* 1613 remove leading whitespace

* 1571 can'have both handle and email in author field

* 1191 remove trailing whitespace

* 1973 remove trailing whitespace

* 196 don't wrap title field

* 1679 required eips must be in ascending order

* 1620 author can't have both handle and email

* 197 don't line wrap title field

* 2378 remove extra newline

* 1355 author can't have both handle and email

* 698 update created date to Y/M/D format

* 2193 required eips must be in ascending order

* 214 remove extra info after author email

* use v0.0.3 of eipv

* 1 remove malformed field

* bump eipv to v0.0.4

* cache eipv build

* 1485 remove extra author info

* 2771 removing extra whitespaces
2020-08-10 11:18:25 -05:00

9.8 KiB

eip title author discussions-to type status category created requires
1337 Subscriptions on the blockchain Kevin Owocki <kevin@gitcoin.co>, Andrew Redden <andrew@blockcrushr.com>, Scott Burke <scott@blockcrushr.com>, Kevin Seagraves <k.s.seagraves@gmail.com>, Luka Kacil <luka.kacil@gmail.com>, Štefan Šimec <stefan.simec@gmail.com>, Piotr Kosiński (@kosecki123), ankit raj <tradeninja7@gmail.com>, John Griffin <john@atchai.com>, Nathan Creswell <nathantr@gmail.com> https://ethereum-magicians.org/t/eip-1337-subscriptions-on-the-blockchain/4422 Standards Track Draft ERC 2018-08-01 20, 165

Simple Summary

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

For a user:
  • you don't have to read a complex whitepaper to use a dapps utility (as opposed to utility tokens)
  • you don't have to understand the founder's vesting schedules
  • you can cancel anytime
For a Service Provider:
  • since you know your subscriber numbers, churn numbers, conversion rate, you get consistent cash flow, and accurate projections
  • you get to focus on making your customers happy
  • enables you to remove speculators from your ecosystem

For these reasons, we think it's imperative to create a standard way to do 'subscriptions' on Ethereum.

Abstract

To enable replay-able transactions users sign a concatenated bytes hash that is composed of the input data needed to execute the transaction. This data is stored off chain by the recipient of the payment and is transmitted to the customers smart contract for execution alongside a provided signature.

Motivation

Recurring payments are the bedrock of SaSS and countless other businesses, a robust specification for defining this interaction will enable a broad spectrum of revenue generation and business models.

Specification

Enum Contract

EIP-1337 Contracts should be compiled with a contract that references all the enumerations that are required for operation

/// @title Enum - Collection of enums
/// Original concept from Richard Meissner - <richard@gnosis.pm> Gnosis safe contracts
contract Enum {
    enum Operation {
        Call,
        DelegateCall,
        Create,
        ERC20, 
        ERC20Approve
    }
    enum SubscriptionStatus {
        ACTIVE,
        PAUSED,
        CANCELLED,
        EXPIRED
    }
    
    enum Period {
        INIT,
        DAY,
        WEEK,
        MONTH
    }
}

EIP-165

EIP-1337 compliant contracts support EIP-165 announcing what interfaces they support

interface ERC165 {
  /**
   * @notice Query if a contract implements an interface
   * @param interfaceID The interface identifier, as specified in ERC-165
   * @dev Interface identification is specified in ERC-165. This function
   * uses less than 30,000 gas.
   * @return `true` if the contract implements `interfaceID` and
   * `interfaceID` is not 0xffffffff, `false` otherwise
   **/
  function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

Public View Functions

isValidSubscription

/** @dev Checks if the subscription is valid.
  * @param bytes subscriptionHash is the identifier of the customer's subscription with its relevant details.
  * @return success is the result of whether the subscription is valid or not.
  **/

function isValidSubscription(
            uint256 subscriptionHash
        ) 
        public 
        view 
        returns (
            bool success
        )
getSubscriptionStatus

/** @dev returns the value of the subscription
  * @param bytes subscriptionHash is the identifier of the customer's subscription with its relevant details.
  * @return status is the enumerated status of the current subscription, 0 expired, 1 active, 2 paused, 3 cancelled
  **/
function getSubscriptionStatus(
        uint256 subscriptionHash
    )
    public 
    view 
    returns (
        uint256 status, 
        uint256 nextWithdraw
    )
getSubscriptionHash
/** @dev returns the hash of cocatenated inputs to the address of the contract holding the logic.,
  * the owner would sign this hash and then provide it to the party for execution at a later date,
  * this could be viewed like a cheque, with the exception that unless you specifically
  * capture the hash on chain a valid signature will be executable at a later date, capturing the hash lets you modify the status to cancel or expire it.
  * @param address recipient the address of the person who is getting the funds.
  * @param uint256 value the value of the transaction
  * @param bytes data the data the user is agreeing to
  * @param uint256 txGas the cost of executing one of these transactions in gas(probably safe to pad this)
  * @param uint256 dataGas the cost of executing the data portion of the transaction(delegate calls etc)
  * @param uint 256 gasPrice the agreed upon gas cost of Execution of this subscription(cost incurment is up to implementation, ie, sender or receiver)
  * @param address gasToken address of the token in which gas will be compensated by, address(0) is ETH, only works in the case of an enscrow implementation)
  * @param bytes meta dynamic bytes array with 4 slots, 2 required, 2 optional // address refundAddress / uint256 period / uint256 offChainID / uint256 expiration (uinx timestamp)
  * @return bytes32, return the hash input arguments concatenated to the address of the contract that holds the logic.
  **/
function getSubscriptionHash(
        address recipient,
        uint256 value,
        bytes data,
        Enum.Operation operation,
        uint256 txGas,
        uint256 dataGas,
        uint256 gasPrice,
        address gasToken,
        bytes meta
    )
    public
    view
    returns (
        bytes32 subscriptionHash
    )
getModifyStatusHash
/** @dev returns the hash of concatenated inputs that the owners user would sign with their public keys
  * @param address recipient the address of the person who is getting the funds.
  * @param uint256 value the value of the transaction
  * @return bytes32 returns the hash of concatenated inputs with the address of the contract holding the subscription hash
  **/
function getModifyStatusHash(
        bytes32 subscriptionHash
        Enum.SubscriptionStatus status
    )
    public
    view
    returns (
        bytes32 modifyStatusHash
    )

Public Functions

modifyStatus

/** @dev modifys the current subscription status
  * @param uint256 subscriptionHash is the identifier of the customer's subscription with its relevant details.
  * @param Enum.SubscriptionStatus status the new status of the subscription
  * @param bytes signatures of the requested method being called
  * @return success is the result of the subscription being paused
  **/
function modifyStatus(
        uint256 subscriptionHash, 
        Enum.SubscriptionStatus status, 
        bytes signatures
    ) 
    public 
    returns (
        bool success
    )
executeSubscription

/** @dev returns the hash of cocatenated inputs to the address of the contract holding the logic.,
  * the owner would sign this hash and then provide it to the party for execution at a later date,
  * this could be viewed like a cheque, with the exception that unless you specifically
  * capture the hash on chain a valid signature will be executable at a later date, capturing the hash lets you modify the status to cancel or expire it.
  * @param address recipient the address of the person who is getting the funds.
  * @param uint256 value the value of the transaction
  * @param bytes data the data the user is agreeing to
  * @param uint256 txGas the cost of executing one of these transactions in gas(probably safe to pad this)
  * @param uint256 dataGas the cost of executing the data portion of the transaction(delegate calls etc)
  * @param uint 256 gasPrice the agreed upon gas cost of Execution of this subscription(cost incurment is up to implementation, ie, sender or receiver)
  * @param address gasToken address of the token in which gas will be compensated by, address(0) is ETH, only works in the case of an enscrow implementation)
  * @param bytes meta dynamic bytes array with 4 slots, 2 required, 2 optional // address refundAddress / uint256 period / uint256 offChainID / uint256 expiration (uinx timestamp)
  * @param bytes signatures signatures concatenated that have signed the inputs as proof of valid execution
  * @return bool success something to note that a failed execution will still pay the issuer of the transaction for their gas costs.
  **/
function executeSubscription(
        address to,
        uint256 value,
        bytes data,
        Enum.Operation operation,
        uint256 txGas,
        uint256 dataGas,
        uint256 gasPrice,
        address gasToken,
        bytes meta,
        bytes signatures
    )
    public 
    returns (
        bool success
    )

Rationale

Merchants who accept credit-cards do so by storing a token that is retrieved from a third party processor(stripe, paypal, etc), this token is used to grant access to pull payment from the cx's credit card provider and move funds to the merchant account. Having users sign input data acts in a similliar fashion and enables that merchant to store the signature of the concatenated bytes hash and input data used to generate the hash and pass them off to the contract holding the subscription logic, thus enabling a workflow that is similliar to what exists in the present day legacy web.

Backwards Compatibility

N/A

Test Cases

TBD

Implementation

TBD

Copyright and related rights waived via CC0.