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
| Getter | Returns | Description |
|---|---|---|
minDuration() | uint256 | Minimum duration per purchase, in seconds |
maxDuration() | uint256 | Maximum duration per purchase, in seconds |
maxAccumulatedDuration() | uint256 | Limit on remaining subscription time when extending an active subscription |
fundsRecipient() | address | Recipient of subscription payments |
expirations(tokenId) | uint256 | Subscription 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
minDurationandmaxDurationbounds. msg.valuemust equalgetSubscriptionCost(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.
| Function | Behavior |
|---|---|
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.