STP
V1 Docs
Contract Reference
Factory Source

STPv1 Factory Source

// SPDX-License-Identifier: MIT
 
pragma solidity 0.8.17;
 
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "./SubscriptionTokenV1.sol";
import "./Shared.sol";
 
/**
 *
 * @title Fabric Subscription Token Factory Contract
 * @author Fabric Inc.
 *
 * @dev A factory which leverages Clones to deploy Fabric Subscription Token Contracts
 *
 */
contract SubscriptionTokenV1Factory is Ownable2Step {
    /// @dev The maximum fee that can be charged for a subscription contract
    uint16 private constant _MAX_FEE_BIPS = 1250;
 
    /// @dev The number of reward halvings
    uint8 private constant _DEFAULT_REWARD_HALVINGS = 6;
 
    /// @dev Guard to ensure the deploy fee is met
    modifier feeRequired() {
        require(msg.value >= _feeDeployMin, "Insufficient ETH to deploy");
        _;
    }
 
    /// @dev Emitted upon a successful contract deployment
    event Deployment(address indexed deployment, uint256 feeId);
 
    /// @dev Emitted when a new fee is created
    event FeeCreated(uint256 indexed id, address collector, uint16 bips);
 
    /// @dev Emitted when a fee is destroyed
    event FeeDestroyed(uint256 indexed id);
 
    /// @dev Emitted when the deployment fee changes
    event DeployFeeChange(uint256 amount);
 
    /// @dev Emitted when the deploy fees are collected by the owner
    event DeployFeeTransfer(address indexed recipient, uint256 amount);
 
    /// @dev The campaign contract implementation address
    address immutable _implementation;
 
    /// @dev Fee configuration for agreements and revshare
    struct FeeConfig {
        address collector;
        uint16 basisPoints;
    }
 
    /// @dev Configured fee ids and their config
    mapping(uint256 => FeeConfig) private _feeConfigs;
 
    /// @dev Fee to collect upon deployment
    uint256 private _feeDeployMin;
 
    /**
     * @param implementation the SubscriptionTokenV1 implementation address
     */
    constructor(address implementation) Ownable2Step() {
        _implementation = implementation;
        _feeDeployMin = 0;
    }
 
    /**
     * @notice Deploy a new Clone of a SubscriptionTokenV1 contract
     *
     * @param name the name of the collection
     * @param symbol the symbol of the collection
     * @param contractURI the metadata URI for the collection
     * @param tokenURI the metadata URI for the tokens
     * @param tokensPerSecond the number of base tokens required for a single second of time
     * @param minimumPurchaseSeconds the minimum number of seconds an account can purchase
     * @param rewardBps the basis points for reward allocations
     * @param erc20TokenAddr the address of the ERC20 token used for purchases, or the 0x0 for native
     * @param feeConfigId the fee configuration id to use for this deployment (if the id is invalid, the default fee is used)
     */
    function deploySubscription(
        string memory name,
        string memory symbol,
        string memory contractURI,
        string memory tokenURI,
        uint256 tokensPerSecond,
        uint256 minimumPurchaseSeconds,
        uint16 rewardBps,
        address erc20TokenAddr,
        uint256 feeConfigId
    ) public payable feeRequired returns (address) {
        // If an invalid fee id is provided, use the default fee (0)
        FeeConfig memory fees = _feeConfigs[feeConfigId];
        if (feeConfigId != 0 && fees.collector == address(0)) {
            fees = _feeConfigs[0];
        }
 
        address deployment = Clones.clone(_implementation);
        SubscriptionTokenV1(payable(deployment)).initialize(
            Shared.InitParams(
                name,
                symbol,
                contractURI,
                tokenURI,
                msg.sender,
                tokensPerSecond,
                minimumPurchaseSeconds,
                rewardBps,
                _DEFAULT_REWARD_HALVINGS,
                fees.basisPoints,
                fees.collector,
                erc20TokenAddr
            )
        );
        emit Deployment(deployment, feeConfigId);
 
        return deployment;
    }
 
    /**
     * @dev Owner Only: Transfer accumulated fees
     * @param recipient the address to transfer the fees to
     */
    function transferDeployFees(address recipient) external onlyOwner {
        uint256 amount = address(this).balance;
        require(amount > 0, "No fees to collect");
        emit DeployFeeTransfer(recipient, amount);
        (bool sent,) = payable(recipient).call{value: amount}("");
        require(sent, "Failed to transfer Ether");
    }
 
    /**
     * @notice Create a fee for future deployments using that fee id
     * @param id the id of the fee for future deployments
     * @param collector the address of the fee collector
     * @param bips the fee in basis points, allocated during withdraw
     */
    function createFee(uint256 id, address collector, uint16 bips) external onlyOwner {
        require(bips <= _MAX_FEE_BIPS, "Fee exceeds maximum");
        require(bips > 0, "Fee cannot be 0");
        require(collector != address(0), "Collector cannot be 0x0");
        require(_feeConfigs[id].collector == address(0), "Fee exists");
        _feeConfigs[id] = FeeConfig(collector, bips);
        emit FeeCreated(id, collector, bips);
    }
 
    /**
     * @notice Destroy a fee schedule
     * @param id the id of the fee to destroy
     */
    function destroyFee(uint256 id) external onlyOwner {
        require(_feeConfigs[id].collector != address(0), "Fee does not exists");
        emit FeeDestroyed(id);
        delete _feeConfigs[id];
    }
 
    /**
     * @notice Update the deploy fee (wei)
     * @param minFeeAmount the amount of wei required to deploy a campaign
     */
    function updateMinimumDeployFee(uint256 minFeeAmount) external onlyOwner {
        _feeDeployMin = minFeeAmount;
        emit DeployFeeChange(minFeeAmount);
    }
 
    /**
     * @notice Fetch the fee schedule for a given fee id
     * @return collector the address of the fee collector, or the 0 address if no fees are collected
     * @return bips the fee in basis points, allocated during withdraw
     * @return deployFeeWei the amount of wei required to deploy a campaign
     */
    function feeInfo(uint256 feeId) external view returns (address collector, uint16 bips, uint256 deployFeeWei) {
        FeeConfig memory fees = _feeConfigs[feeId];
        return (fees.collector, fees.basisPoints, _feeDeployMin);
    }
}