Skip to Content

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); } }
Last updated on