Skip to main content

API Reference

This page describes the subscription-specific API in the current ERC721S implementation. Inherited ERC721 and ownership functions are not exhaustively listed here.

State Variables​

GetterReturnsDescription
minDuration()uint256Minimum duration per purchase, in seconds
maxDuration()uint256Maximum duration per purchase, in seconds
maxAccumulatedDuration()uint256Limit on remaining subscription time when extending an active subscription
fundsRecipient()addressRecipient of subscription payments
expirations(tokenId)uint256Subscription expiration as a Unix timestamp
tokenConfigs(token)(uint256 pricePerSecond, bool enabled)Price and availability for a payment token; address(0) represents native ETH

User Functions​

subscribe: Native ETH​

function subscribe(address subscriptionOwner, uint256 durationInSeconds)
public payable returns (uint256 tokenId, uint256 expiration)

Creates or extends a subscription for subscriptionOwner. The payer may be a different account.

  • Native payments must be enabled, and the duration must fall within the inclusive minDuration and maxDuration bounds.
  • msg.value must equal getSubscriptionCost(address(0), durationInSeconds) exactly.
  • A new or expired subscription expires at block.timestamp + durationInSeconds.
  • An active subscription extends from its existing expiration. The resulting remaining time must be strictly less than maxAccumulatedDuration; equality reverts in the current implementation.
  • A token is minted to the subscription owner if needed. Renewing an expired subscription reuses its existing token.
  • Funds are forwarded immediately to fundsRecipient, unless that recipient is the subscription contract itself.

subscribe: ERC20 Authorization​

function subscribe(
address subscriptionOwner,
uint256 durationInSeconds,
address token,
EIP3009Auth calldata auth
) external returns (uint256 tokenId, uint256 expiration)

Uses an enabled ERC20 token implementing EIP-3009 receiveWithAuthorization. The token must not be address(0).

struct EIP3009Auth {
address from;
uint256 value;
uint256 validAfter;
uint256 validBefore;
bytes32 nonce;
uint8 v;
bytes32 r;
bytes32 s;
}

auth.from is the payer. auth.value must equal the quoted cost in the token's smallest unit. Sign a ReceiveWithAuthorization message whose to is the subscription contract, not fundsRecipient. A TransferWithAuthorization signature uses a different type hash and is not interchangeable. The payment token enforces authorization validity and nonce reuse. See EIP-3009.

The subscription rules match the native overload. After receiving the payment, the contract forwards it to fundsRecipient unless the recipient is the contract itself.

View Functions​

getSubscriptionCost​

function getSubscriptionCost(address token, uint256 durationInSeconds)
public view returns (uint256)

Returns durationInSeconds * tokenConfigs[token].pricePerSecond. Reverts if the token is disabled or the duration is outside the configured bounds. It does not check a particular account's accumulated subscription time.

deriveTokenId​

function deriveTokenId(address account) public pure returns (uint256)

Returns uint256(uint160(account)).

hasActiveSubscription​

function hasActiveSubscription(address account) public view returns (bool)

Checks that the account has a subscription token and that it has not expired. Use a nonzero account address; the inherited balanceOf check rejects the zero address.

isSubscriptionActive​

function isSubscriptionActive(uint256 tokenId) public view returns (bool)

Returns whether expirations[tokenId] > block.timestamp. A subscription is inactive at its exact expiration timestamp.

Owner Functions​

All functions below require the contract owner.

FunctionBehavior
setTokenConfig(address token, uint256 pricePerSecond, bool enabled)Sets the per-token price and enables or disables payments. Use address(0) for ETH.
setDurationBounds(uint256 newMinDuration, uint256 newMaxDuration)Requires nonzero bounds, minimum ≤ maximum, and maximum ≤ accumulated limit.
setMaxAccumulatedDuration(uint256 newMaxAccumulatedDuration)Requires the new limit to be at least maxDuration.
setFundsRecipient(address newFundsRecipient)Sets a nonzero recipient address.
withdraw(address token)Sends the contract's entire balance of the specified token to fundsRecipient; use address(0) for ETH. Reverts if the recipient is the contract itself.

Changing pricing or duration configuration does not rewrite existing expiration timestamps.

Events​

event SubscriptionStarted(address indexed account, uint256 indexed tokenId, uint256 startTime, uint256 expiration);
event SubscriptionExtended(address indexed account, uint256 indexed tokenId, uint256 expiration);
event TokenConfigUpdated(address indexed token, uint256 pricePerSecond, bool enabled);
event FundsRecipientUpdated(address newFundsRecipient);
event PaymentReceived(address indexed account, address indexed payer, address indexed token, uint256 amount);
event DurationBoundsUpdated(uint256 newMinDuration, uint256 newMaxDuration);
event MaxAccumulatedDurationUpdated(uint256 newMaxAccumulatedDuration);

Errors​

error InvalidAddress(string parameterName, address account);
error InvalidPayment(uint256 required, uint256 received);
error InvalidDuration(uint256 duration);
error TokenNotEnabled(address token);
error TokenNonTransferable();
error NativeTransferFailed(address recipient, uint256 amount);

Inherited contracts and payment tokens can also revert with their own errors.

Implementation Notes​

  • Both subscription overloads use ReentrancyGuard.
  • Ownership transfers use Ownable2Step, requiring the proposed owner to accept the transfer.
  • Transfers and burns are rejected by the token update override; only minting is allowed.
  • Subscriptions require an explicit purchase or renewal transaction. They do not automatically charge recurring payments.