STP
Contract Reference
STP Ref

SubscriptionTokenV1

Git Source (opens in a new tab)

Inherits: ERC721Upgradeable, Ownable2StepUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable

Author: Fabric Inc.

An NFT contract which allows users to mint time and access token gated content while time remains.

The balanceOf function returns the number of seconds remaining in the subscription. Token gated systems leverage the balanceOf function to determine if a user has the token, and if no time remains, the balance is 0. NFT holders can mint additional time. The creator/owner of the contract can withdraw the funds at any point. There are additional functionalities for granting time, refunding accounts, fees, rewards, etc. This contract is designed to be used with Clones, but is not designed to be upgradeable. Added functionality will come with new versions.

State Variables

_MAX_REWARD_HALVINGS

The maximum number of reward halvings (limiting this prevents overflow)

uint256 private constant _MAX_REWARD_HALVINGS = 32;

_MAX_FEE_BIPS

Maximum protocol fee basis points (12.5%)

uint16 private constant _MAX_FEE_BIPS = 1250;

_MAX_BIPS

Maximum basis points (100%)

uint16 private constant _MAX_BIPS = 10000;

_contractURI

The metadata URI for the contract

string private _contractURI;

_tokenURI

The metadata URI for the tokens. Note: if it ends with /, then we append the tokenId

string private _tokenURI;

_tokensPerSecond

The cost of one second in denominated token (wei or other base unit)

uint256 private _tokensPerSecond;

_minPurchaseSeconds

Minimum number of seconds to purchase. Also, this is the number of seconds until the reward multiplier is halved.

uint256 private _minPurchaseSeconds;

_minimumPurchase

The minimum number of tokens accepted for a time purchase

uint256 private _minimumPurchase;

_token

The token contract address, or 0x0 for native tokens

IERC20 private _token;

_tokensIn

The total number of tokens transferred in (accounting)

uint256 private _tokensIn;

_tokensOut

The total number of tokens transferred out (accounting)

uint256 private _tokensOut;

_tokenCounter

The token counter for mint id generation and enforcing supply caps

uint256 private _tokenCounter;

_feeBalance

The total number of tokens allocated for the fee collector (accounting)

uint256 private _feeBalance;

_feeBps

The protocol fee basis points (10000 = 100%, max = _MAX_FEE_BIPS)

uint16 private _feeBps;

_feeCollector

The protocol fee collector address (for withdraws or sponsored transfers)

address private _feeCollector;

_erc20

Flag which determines if the contract is erc20 denominated

bool private _erc20;

_deployBlockTime

The block timestamp of the contract deployment (used for reward halvings)

uint256 private _deployBlockTime;

_totalRewardPoints

The reward pool size (used to calculate reward withdraws accurately)

uint256 private _totalRewardPoints;

_rewardPoolBalance

The reward pool balance (accounting)

uint256 private _rewardPoolBalance;

_rewardPoolTotal

The reward pool total (used to calculate reward withdraws accurately)

uint256 private _rewardPoolTotal;

_rewardPoolSlashed

The reward pool tokens slashed (used to calculate reward withdraws accurately)

uint256 private _rewardPoolSlashed;

_rewardBps

The basis points for reward allocations

uint16 private _rewardBps;

_numRewardHalvings

The number of reward halvings. This is used to calculate the reward multiplier for early supporters, if the creator chooses to reward them.

uint256 private _numRewardHalvings;

_supplyCap

The maximum number of tokens which can be minted (adjustable over time, but will not allow setting below current count)

uint256 private _supplyCap;

_transferRecipient

The address of the account which can receive transfers via sponsored calls

address private _transferRecipient;

_subscriptions

The subscription state for each account

mapping(address => Subscription) private _subscriptions;

_referralCodes

The collection of referral codes for referral rewards

mapping(uint256 => uint16) private _referralCodes;

Functions

validAmount

Guard to ensure the purchase amount is valid

modifier validAmount(uint256 amount);

constructor

Disable initializers on the logic contract

constructor();

receive

Fallback function to mint time for native token contracts

receive() external payable;

initialize

Initialize acts as the constructor, as this contract is intended to work with proxy contracts.

function initialize(Shared.InitParams memory params) public initializer;

Parameters

NameTypeDescription
paramsShared.InitParamsthe init params (See Common.InitParams)

mint

Mint or renew a subscription for sender

function mint(uint256 numTokens) external payable;

Parameters

NameTypeDescription
numTokensuint256the amount of ERC20 tokens or native tokens to transfer

mintWithReferral

Mint or renew a subscription for sender, with referral rewards for a referrer

function mintWithReferral(uint256 numTokens, uint256 referralCode, address referrer) external payable;

Parameters

NameTypeDescription
numTokensuint256the amount of ERC20 tokens or native tokens to transfer
referralCodeuint256the referral code to use
referreraddressthe referrer address and reward recipient

withdrawRewards

Withdraw available rewards. This is only possible if the subscription is active.

function withdrawRewards() external;

slashRewards

Slash the reward points for an expired subscription after a grace period which is 50% of the purchased time Any slashable points are burned, increasing the value of remaining points.

function slashRewards(address account) external;

Parameters

NameTypeDescription
accountaddressthe account of the subscription to slash

withdraw

Withdraw available funds as the owner

function withdraw() external;

withdrawAndTransferFees

Withdraw available funds and transfer fees as the owner

function withdrawAndTransferFees() external;

withdrawTo

Withdraw available funds as the owner to a specific account

function withdrawTo(address account) public onlyOwner;

Parameters

NameTypeDescription
accountaddressthe account to transfer funds to

refund

Refund one or more accounts remaining purchased time and revoke any granted time

This refunds accounts using creator balance, and can also transfer in to top up the fund. Any excess value is withdrawable.

function refund(uint256 numTokensIn, address[] memory accounts) external payable onlyOwner;

Parameters

NameTypeDescription
numTokensInuint256an optional amount of tokens to transfer in before refunding
accountsaddress[]the list of accounts to refund and revoke grants for

updateMetadata

Update the contract metadata

function updateMetadata(string memory contractUri, string memory tokenUri) external onlyOwner;

Parameters

NameTypeDescription
contractUristringthe collection metadata URI
tokenUristringthe token metadata URI

grantTime

Grant time to a list of accounts, so they can access content without paying

function grantTime(address[] memory accounts, uint256 secondsToAdd) external onlyOwner;

Parameters

NameTypeDescription
accountsaddress[]the list of accounts to grant time to
secondsToAdduint256the number of seconds to grant for each account

pause

Pause minting to allow for migrations or other actions

function pause() external onlyOwner;

unpause

Unpause to resume subscription minting

function unpause() external onlyOwner;

setSupplyCap

Update the maximum number of tokens (subscriptions)

function setSupplyCap(uint256 supplyCap) external onlyOwner;

Parameters

NameTypeDescription
supplyCapuint256the new supply cap (must be greater than token count or 0 for unlimited)

setTransferRecipient

Set a transfer recipient for automated/sponsored transfers

function setTransferRecipient(address recipient) external onlyOwner;

Parameters

NameTypeDescription
recipientaddressthe recipient address

mintFor

Mint or renew a subscription for a specific account. Intended for automated renewals.

function mintFor(address account, uint256 numTokens) public payable whenNotPaused validAmount(numTokens);

Parameters

NameTypeDescription
accountaddressthe account to mint or renew time for
numTokensuint256the amount of ERC20 tokens or native tokens to transfer

mintWithReferralFor

Mint or renew a subscription for a specific account, with referral details

function mintWithReferralFor(address account, uint256 numTokens, uint256 referralCode, address referrer)
    public
    payable
    whenNotPaused
    validAmount(numTokens);

Parameters

NameTypeDescription
accountaddressthe account to mint or renew time for
numTokensuint256the amount of ERC20 tokens or native tokens to transfer
referralCodeuint256the referral code to use for rewards
referreraddressthe referrer address and reward recipient

transferFees

Transfer any available fees to the fee collector

function transferFees() external;

transferAllBalances

Transfer all balances to the transfer recipient and fee collector (if applicable)

This is a way for EOAs to pay gas fees on behalf of the creator (automation, etc)

function transferAllBalances() external;

feeSchedule

Fetch the current fee schedule

function feeSchedule() external view returns (address feeCollector, uint16 feeBps);

Returns

NameTypeDescription
feeCollectoraddressthe feeCollector address
feeBpsuint16the fee basis points

feeBalance

Fetch the accumulated fee balance

function feeBalance() external view returns (uint256 balance);

Returns

NameTypeDescription
balanceuint256the accumulated fees which have not yet been transferred

updateFeeRecipient

Update the fee collector address. Can be set to 0x0 to disable fees permanently.

function updateFeeRecipient(address newCollector) external;

Parameters

NameTypeDescription
newCollectoraddressthe new fee collector address

createReferralCode

Create a referral code for giving rewards to referrers on mint

function createReferralCode(uint256 code, uint16 bps) external onlyOwner;

Parameters

NameTypeDescription
codeuint256the unique integer code for the referral
bpsuint16the reward basis points

deleteReferralCode

Delete a referral code

function deleteReferralCode(uint256 code) external onlyOwner;

Parameters

NameTypeDescription
codeuint256the unique integer code for the referral

referralCodeBps

Fetch the reward basis points for a given referral code

function referralCodeBps(uint256 code) external view returns (uint16 bps);

Parameters

NameTypeDescription
codeuint256the unique integer code for the referral

Returns

NameTypeDescription
bpsuint16the reward basis points

_purchaseTime

Add time to a given account (transfer happens before this is called)

function _purchaseTime(address account, uint256 amount) internal returns (uint256);

_fetchSubscription

Get or build a new subscription

function _fetchSubscription(address account) internal returns (Subscription memory);

_maybeMint

Mint the NFT if it does not exist. Used after grant/purchase state changes (check effects)

function _maybeMint(address account, uint256 tokenId) private;

_allocateFeesAndRewards

If fees or rewards are present, allocate a portion of the amount to the relevant pools

function _allocateFeesAndRewards(uint256 amount) private;

_allocateFees

Allocate tokens to the fee collector

function _allocateFees(uint256 amount) internal returns (uint256);

_allocateRewards

Allocate tokens to the reward pool

function _allocateRewards(uint256 amount) internal returns (uint256);

_transferIn

Transfer tokens into the contract, either native or ERC20

function _transferIn(address from, uint256 amount) internal nonReentrant returns (uint256);

_transferToCreator

Transfer tokens to the creator, after allocating protocol fees and rewards

function _transferToCreator(address to, uint256 amount) internal;

_transferOut

Transfer tokens out of the contract, either native or ERC20

function _transferOut(address to, uint256 amount) internal nonReentrant;

_transferFees

Transfer fees to the fee collector

function _transferFees() internal;

_transferAllBalances

Transfer all remaining balances to the creator and fee collector (if applicable)

function _transferAllBalances(address balanceRecipient) internal;

_grantTime

Grant time to a given account

function _grantTime(address account, uint256 numSeconds) internal;

_grantTimeRemaining

The amount of granted time remaining for a given subscription

function _grantTimeRemaining(Subscription memory sub) internal view returns (uint256);

_purchaseTimeRemaining

The amount of purchased time remaining for a given subscription

function _purchaseTimeRemaining(Subscription memory sub) internal view returns (uint256);

_refund

Refund the remaining time for the given accounts subscription, and clear grants

function _refund(address account) internal;

_referralAmount

Compute the reward amount for a given token amount and referral code

function _referralAmount(uint256 tokenAmount, uint256 referralCode) internal view returns (uint256);

_subscriptionExpiresAt

The timestamp when the subscription expires

function _subscriptionExpiresAt(Subscription memory sub) internal pure returns (uint256);

_rewardBalance

The reward balance for a given subscription

function _rewardBalance(Subscription memory sub) internal view returns (uint256);

_isActive

Determine if a subscription is active

function _isActive(Subscription memory sub) internal view returns (bool);

refundableTokenBalanceOfAll

Determine the total cost for refunding the given accounts

The value will change from block to block, so this is only an estimate

function refundableTokenBalanceOfAll(address[] memory accounts) public view returns (uint256 numTokens);

Parameters

NameTypeDescription
accountsaddress[]the list of accounts to refund

Returns

NameTypeDescription
numTokensuint256total number of tokens for refund

canRefund

Determines if a refund can be processed for the given accounts with the current balance

function canRefund(address[] memory accounts) public view returns (bool refundable);

Parameters

NameTypeDescription
accountsaddress[]the list of accounts to refund

Returns

NameTypeDescription
refundablebooltrue if the refund can be processed from the current balance

rewardMultiplier

The current reward multiplier used to calculate reward points on mint. This is halved every _minPurchaseSeconds and goes to 0 after N halvings.

function rewardMultiplier() public view returns (uint256 multiplier);

Returns

NameTypeDescription
multiplieruint256the current value

timeValue

The amount of time exchanged for the given number of tokens

function timeValue(uint256 numTokens) public view returns (uint256 numSeconds);

Parameters

NameTypeDescription
numTokensuint256the number of tokens to exchange for time

Returns

NameTypeDescription
numSecondsuint256the number of seconds purchased

creatorBalance

The creators withdrawable balance

function creatorBalance() public view returns (uint256 balance);

Returns

NameTypeDescription
balanceuint256the number of tokens available for withdraw

totalCreatorEarnings

The sum of all deposited tokens over time. Fees and refunds are not accounted for.

function totalCreatorEarnings() public view returns (uint256 total);

Returns

NameTypeDescription
totaluint256the total number of tokens deposited

subscriptionOf

Relevant subscription information for a given account

function subscriptionOf(address account)
    external
    view
    returns (uint256 tokenId, uint256 refundableAmount, uint256 rewardPoints, uint256 expiresAt);

Returns

NameTypeDescription
tokenIduint256the tokenId for the account
refundableAmountuint256the number of seconds which can be refunded
rewardPointsuint256the number of reward points earned
expiresAtuint256the timestamp when the subscription expires

rewardBps

The percentage (as basis points) of creator earnings which are rewarded to subscribers

function rewardBps() external view returns (uint16 bps);

Returns

NameTypeDescription
bpsuint16reward basis points

totalRewardPoints

The number of reward points allocated to all subscribers (used to calculate rewards)

function totalRewardPoints() external view returns (uint256 numPoints);

Returns

NameTypeDescription
numPointsuint256total number of reward points

rewardPoolBalance

The balance of the reward pool (for reward withdraws)

function rewardPoolBalance() external view returns (uint256 numTokens);

Returns

NameTypeDescription
numTokensuint256number of tokens in the reward pool

rewardBalanceOf

The number of tokens available to withdraw from the reward pool, for a given account

function rewardBalanceOf(address account) external view returns (uint256 numTokens);

Parameters

NameTypeDescription
accountaddressthe account to check

Returns

NameTypeDescription
numTokensuint256number of tokens available to withdraw

erc20Address

The ERC-20 address used for purchases, or 0x0 for native

function erc20Address() public view returns (address erc20);

Returns

NameTypeDescription
erc20addressaddress or 0x0 for native

refundableBalanceOf

The refundable time balance for a given account

function refundableBalanceOf(address account) public view returns (uint256 numSeconds);

Parameters

NameTypeDescription
accountaddressthe account to check

Returns

NameTypeDescription
numSecondsuint256the number of seconds which can be refunded

contractURI

The contract metadata URI for accessing collection metadata

function contractURI() public view returns (string memory uri);

Returns

NameTypeDescription
uristringthe collection URI

baseTokenURI

The base token URI for accessing token metadata

function baseTokenURI() public view returns (string memory uri);

Returns

NameTypeDescription
uristringthe base token URI

tps

The number of tokens required for a single second of time

function tps() external view returns (uint256 numTokens);

Returns

NameTypeDescription
numTokensuint256per second

minPurchaseSeconds

The minimum number of seconds required for a purchase

function minPurchaseSeconds() external view returns (uint256 numSeconds);

Returns

NameTypeDescription
numSecondsuint256minimum

supplyDetail

Fetch the current supply cap (0 for unlimited)

function supplyDetail() external view returns (uint256 count, uint256 cap);

Returns

NameTypeDescription
countuint256the current number
capuint256the max number of subscriptions

transferRecipient

Fetch the current transfer recipient address

function transferRecipient() external view returns (address recipient);

Returns

NameTypeDescription
recipientaddressthe address or 0x0 address for none

tokenURI

Fetch the metadata URI for a given token

If _tokenURI ends with a / then the tokenId is appended

function tokenURI(uint256 tokenId) public view override returns (string memory uri);

Parameters

NameTypeDescription
tokenIduint256the tokenId to fetch the metadata URI for

Returns

NameTypeDescription
uristringthe URI for the token

balanceOf

Override the default balanceOf behavior to account for time remaining

function balanceOf(address account) public view override returns (uint256 numSeconds);

Parameters

NameTypeDescription
accountaddressthe account to fetch the balance of

Returns

NameTypeDescription
numSecondsuint256the number of seconds remaining in the subscription

renounceOwnership

Renounce ownership of the contract, transferring all remaining funds to the creator and fee collector and pausing the contract to prevent further inflows.

function renounceOwnership() public override onlyOwner;

_beforeTokenTransfer

Transfers may occur if the destination does not have a subscription

function _beforeTokenTransfer(address from, address to, uint256, uint256) internal override;

reconcileERC20Balance

Reconcile the ERC20 balance of the contract with the internal state

The prevents lost funds if ERC20 tokens are transferred to the contract directly

function reconcileERC20Balance() external onlyOwner;

recoverERC20

Recover ERC20 tokens which were accidentally sent to the contract

function recoverERC20(address tokenAddress, address recipientAddress, uint256 tokenAmount) external onlyOwner;

Parameters

NameTypeDescription
tokenAddressaddressthe address of the token to recover
recipientAddressaddressthe address to send the tokens to
tokenAmountuint256the amount of tokens to send

recoverNativeTokens

Recover native tokens which bypassed receive. Only callable for erc20 denominated contracts.

function recoverNativeTokens(address recipient) external onlyOwner;

Parameters

NameTypeDescription
recipientaddressthe address to send the tokens to

reconcileNativeBalance

Reconcile native tokens which bypassed receive/mint. Only callable for native denominated contracts.

function reconcileNativeBalance() external onlyOwner;

Events

Withdraw

Emitted when the owner withdraws available funds

event Withdraw(address indexed account, uint256 tokensTransferred);

RewardWithdraw

Emitted when a subscriber withdraws their rewards

event RewardWithdraw(address indexed account, uint256 tokensTransferred);

RewardPointsSlashed

Emitted when a subscriber slashed the rewards of another subscriber

event RewardPointsSlashed(address indexed account, address indexed slasher, uint256 rewardPointsSlashed);

RewardsAllocated

Emitted when tokens are allocated to the reward pool

event RewardsAllocated(uint256 tokens);

Purchase

Emitted when time is purchased (new nft or renewed)

event Purchase(
    address indexed account,
    uint256 indexed tokenId,
    uint256 tokensTransferred,
    uint256 timePurchased,
    uint256 rewardPoints,
    uint256 expiresAt
);

Grant

Emitted when a subscriber is granted time by the creator

event Grant(address indexed account, uint256 indexed tokenId, uint256 secondsGranted, uint256 expiresAt);

Refund

Emitted when the creator refunds a subscribers remaining time

event Refund(address indexed account, uint256 indexed tokenId, uint256 tokensTransferred, uint256 timeReclaimed);

RefundTopUp

Emitted when the creator tops up the contract balance on refund

event RefundTopUp(uint256 tokensIn);

FeeTransfer

Emitted when the fees are transferred to the collector

event FeeTransfer(address indexed from, address indexed to, uint256 tokensTransferred);

FeeCollectorChange

Emitted when the fee collector is updated

event FeeCollectorChange(address indexed from, address indexed to);

FeeAllocated

Emitted when tokens are allocated to the fee pool

event FeeAllocated(uint256 tokens);

ReferralPayout

Emitted when a referral fee is paid out

event ReferralPayout(
    uint256 indexed tokenId, address indexed referrer, uint256 indexed referralId, uint256 rewardAmount
);

ReferralCreated

Emitted when a new referral code is created

event ReferralCreated(uint256 id, uint16 rewardBps);

ReferralDestroyed

Emitted when a referral code is deleted

event ReferralDestroyed(uint256 id);

SupplyCapChange

Emitted when the supply cap is updated

event SupplyCapChange(uint256 supplyCap);

TransferRecipientChange

Emitted when the transfer recipient is updated

event TransferRecipientChange(address indexed recipient);

Structs

Subscription

The subscription struct which holds the state of a subscription for an account

struct Subscription {
    uint256 tokenId;
    uint256 secondsPurchased;
    uint256 secondsGranted;
    uint256 grantOffset;
    uint256 purchaseOffset;
    uint256 rewardPoints;
    uint256 rewardsWithdrawn;
}