Skip to content

Commit

Permalink
Merge pull request #15 from blocto/feature/refactor-token-factory
Browse files Browse the repository at this point in the history
refactor: token factory
  • Loading branch information
scottphc authored Jan 24, 2025
2 parents 92be490 + 90db2e9 commit 5384c9a
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 183 deletions.
2 changes: 0 additions & 2 deletions script/DeployTokenFactory.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ contract Deploy is Script {
address private _STORY_DERIVATIVE_WORKFLOWS_ADDRESS = 0xa8815CEB96857FFb8f5F8ce920b1Ae6D70254C7B;
address private _SPOTLIGHT_TOKEN_FACTORY_OWNER = 0x582d6944a8EA7e4ACD385D18DC95CF5915510289;

address private constant WRAPPER_IP = 0xe8CabF9d1FFB6CE23cF0a86641849543ec7BD7d5;
address private constant PIPERX_V2_ROUTER = 0x8812d810EA7CC4e1c3FB45cef19D6a7ECBf2D85D;
address private constant PIPERX_V2_FACTORY = 0x700722D24f9256Be288f56449E8AB1D27C4a70ca;

Expand Down Expand Up @@ -77,7 +76,6 @@ contract Deploy is Script {
address(tokenIpCollection), // tokenIpCollection_
address(spotlightTokenImpl), // tokenImplementation_
address(bondingCurve), // bondingCurve_
WRAPPER_IP,
_STORY_DERIVATIVE_WORKFLOWS_ADDRESS, // storyDerivativeWorkflows_
PIPERX_V2_ROUTER, // piperxV2Router_
PIPERX_V2_FACTORY, // piperxV2Factory_
Expand Down
59 changes: 19 additions & 40 deletions src/spotlight-token-factory/ISpotlightTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,41 +68,38 @@ interface ISpotlightTokenFactory {
* @param tokenIpCollection_ The address of the token IP collection contract.
* @param tokenImplementation_ The address of the token implementation contract.
* @param bondingCurve_ The address of the bonding curve contract.
* @param baseToken_ The address of the base token.
* @param storyDerivativeWorkflows_ The address of the story derivative workflows contract.
* @param piperXRouter_ The address of the PiperX router contract.
* @param piperXFactory_ The address of the PiperX factory contract.
* @param protocolRewards_ The address of the protocol rewards contract.
* @param rewardsVault_ The address of the reward vault contract.
*/
function initialize(
address owner_,
uint256 creationFee_,
address tokenIpCollection_,
address tokenImplementation_,
address bondingCurve_,
address baseToken_,
address storyDerivativeWorkflows_,
address piperXRouter_,
address piperXFactory_,
address protocolRewards_
address rewardsVault_
) external;

/**
* @dev Returns the address of the token factory owner.
* @dev Returns the fee amount required to create a token (in native token)
*/
function owner() external view returns (address);
function createTokenFee() external view returns (uint256);

/**
* @dev Returns the address of the token IP collection contract
* @notice The token IP collection contract must implement ISpotlightTokenIPCollection
* @dev Sets the fee amount required to create a token (in native token)
*/
function tokenIpCollection() external view returns (address);
function setCreateTokenFee(uint256 newFee) external;

/**
* @dev Sets the address of the token IP collection contract
* @dev Returns the address of the token IP collection contract
* @notice The token IP collection contract must implement ISpotlightTokenIPCollection
*/
function setTokenIpCollection(address newTokenIpCollection) external;
function tokenIpCollection() external view returns (address);

/**
* @dev Returns the address of the token implementation contract
Expand All @@ -115,44 +112,34 @@ interface ISpotlightTokenFactory {
function setTokenImplementation(address newTokenImplementation) external;

/**
* @dev Returns the fee amount required to create a token (in native token)
* @dev Returns the address of the bonding curve contract
*/
function createTokenFee() external view returns (uint256);
function bondingCurve() external view returns (address);

/**
* @dev Sets the fee amount required to create a token (in native token)
* @dev Sets the address of the bonding curve contract
*/
function setCreateTokenFee(uint256 newFee) external;
function setBondingCurve(address newBondingCurve) external;

/**
* @dev Returns the address of the story derivative workflows contract
*/
function storyDerivativeWorkflows() external view returns (address);

/**
* @dev Sets the address of the story derivative workflows contract
*/
function setStoryDerivativeWorkflows(address newStoryDerivativeWorkflows) external;

/**
* @dev Returns the address of the base token
*/
function baseToken() external view returns (address);

/**
* @dev Sets the address of the base token
* @dev Returns the address of the PiperX router contract
*/
function setBaseToken(address newBaseToken) external;
function piperXRouter() external view returns (address);

/**
* @dev Returns the address of the bonding curve contract
* @dev Returns the address of the PiperX factory contract
*/
function bondingCurve() external view returns (address);
function piperXFactory() external view returns (address);

/**
* @dev Sets the address of the bonding curve contract
* @dev Returns the address of the rewards vault contract
*/
function setBindingCurve(address newBondingCurve) external;
function rewardsVault() external view returns (address);

/**
* @dev Computes the address of a token created by the specified token creator.
Expand All @@ -170,7 +157,6 @@ interface ISpotlightTokenFactory {
* @param ipMetadata Metadata for the intellectual property. See {IStoryDerivativeWorkflows-registerIpAndMakeDerivative}.
* @param sigMetadata Signature data for token creation. See {IStoryDerivativeWorkflows-registerIpAndMakeDerivative}.
* @param sigRegister Signature data for IP registration. See {IStoryDerivativeWorkflows-registerIpAndMakeDerivative}.
* @param specificAddress The address of the specific address to be used for the token creation.
*
* @return tokenAddress The address of the newly created token.
* @return ipId The ID of the newly registered intellectual property.
Expand All @@ -181,16 +167,9 @@ interface ISpotlightTokenFactory {
StoryWorkflowStructs.MakeDerivative calldata derivData,
StoryWorkflowStructs.IPMetadata calldata ipMetadata,
StoryWorkflowStructs.SignatureData calldata sigMetadata,
StoryWorkflowStructs.SignatureData calldata sigRegister,
address specificAddress
StoryWorkflowStructs.SignatureData calldata sigRegister
) external payable returns (address tokenAddress, address ipId);

/**
* @dev Returns the quote for initial buying tokens
* @param tokensOut The number of tokens to be bought
*/
function getInitialBuyTokenQuote(uint256 tokensOut) external view returns (uint256);

/**
* @dev Returns the number of tokens created by a token creator
*/
Expand Down
122 changes: 47 additions & 75 deletions src/spotlight-token-factory/SpotlightTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity ^0.8.13;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {IMinimalIPAccount} from "../spotlight-protocol-rewards/IMinimalIPAccount.sol";
import {ISpotlightTokenFactory} from "./ISpotlightTokenFactory.sol";
import {ISpotlightToken} from "../spotlight-token/ISpotlightToken.sol";
import {ISpotlightTokenIPCollection} from "../spotlight-token-collection/ISpotlightTokenIPCollection.sol";
Expand All @@ -13,22 +15,17 @@ import {SpotlightTokenFactoryStorage} from "./SpotlightTokenFactoryStorage.sol";
import {ISpotlightBondingCurve} from "../spotlight-bonding-curve/ISpotlightBondingCurve.sol";
import {MarketType} from "../spotlight-token/ISpotlightToken.sol";

contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenFactory {
contract SpotlightTokenFactory is OwnableUpgradeable, SpotlightTokenFactoryStorage, ISpotlightTokenFactory {
modifier needInitialized() {
_checkIsInitialized();
_;
}

modifier onlyOwner() {
_checkIsOwner();
_;
}

/**
* @dev See {ISpotlightTokenFactory-isInitialized}.
*/
function isInitialized() public view returns (bool) {
return _isInitialized;
return _getInitializedVersion() == 1;
}

/**
Expand All @@ -40,54 +37,47 @@ contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenF
address tokenIpCollection_,
address tokenImplementation_,
address bondingCurve_,
address baseToken_,
address storyDerivativeWorkflows_,
address piperXRouter_,
address piperXFactory_,
address protocolRewards_
) external {
if (isInitialized()) {
revert("SpotlightTokenFactory: Already initialized");
}
_owner = owner_;
address rewardsVault_
) external initializer {
__Ownable_init(owner_);
_creationFee = creationFee_;
_tokenIpCollection = tokenIpCollection_;
_tokenImplementation = tokenImplementation_;
_bondingCurve = bondingCurve_;
_baseToken = baseToken_;
_storyDerivativeWorkflows = storyDerivativeWorkflows_;

_isInitialized = true;
_piperXRouter = piperXRouter_;
_piperXFactory = piperXFactory_;
_protocolRewards = protocolRewards_;
_rewardsVault = rewardsVault_;
}

/**
* @dev See {ISpotlightTokenFactory-owner}.
* @dev See {ISpotlightTokenFactory-creationFee}.
*/
function owner() public view needInitialized returns (address) {
return _owner;
function createTokenFee() public view returns (uint256) {
return _creationFee;
}

/**
* @dev See {ISpotlightTokenFactory-tokenIpCollection}.
* @dev See {ISpotlightTokenFactory-setCreateTokenFee}.
*/
function tokenIpCollection() public view needInitialized returns (address) {
return _tokenIpCollection;
function setCreateTokenFee(uint256 newFee) external needInitialized onlyOwner {
_creationFee = newFee;
}

/**
* @dev See {ISpotlightTokenFactory-setTokenIpCollection}.
* @dev See {ISpotlightTokenFactory-tokenIpCollection}.
*/
function setTokenIpCollection(address newTokenIpCollection) external needInitialized onlyOwner {
_tokenIpCollection = newTokenIpCollection;
function tokenIpCollection() public view returns (address) {
return _tokenIpCollection;
}

/**
* @dev See {ISpotlightTokenFactory-tokenImplementation}.
*/
function tokenImplementation() public view needInitialized returns (address) {
function tokenImplementation() public view returns (address) {
return _tokenImplementation;
}

Expand All @@ -99,65 +89,51 @@ contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenF
}

/**
* @dev See {ISpotlightTokenFactory-creationFee}.
* @dev See {ISpotlightTokenFactory-bondingCurve}.
*/
function createTokenFee() public view needInitialized returns (uint256) {
return _creationFee;
function bondingCurve() public view returns (address) {
return _bondingCurve;
}

/**
* @dev See {ISpotlightTokenFactory-setCreateTokenFee}.
* @dev See {ISpotlightTokenFactory-setBondingCurve}.
*/
function setCreateTokenFee(uint256 newFee) external needInitialized onlyOwner {
_creationFee = newFee;
function setBondingCurve(address newBondingCurve) external needInitialized onlyOwner {
_bondingCurve = newBondingCurve;
}

/**
* @dev See {ISpotlightTokenFactory-storyDerivativeWorkflows}.
*/
function storyDerivativeWorkflows() public view needInitialized returns (address) {
function storyDerivativeWorkflows() public view returns (address) {
return _storyDerivativeWorkflows;
}

/**
* @dev See {ISpotlightTokenFactory-setStoryDerivativeWorkflows}.
* @dev See {ISpotlightTokenFactory-piperXRouter}.
*/
function setStoryDerivativeWorkflows(address newStoryDerivativeWorkflows) external needInitialized onlyOwner {
_storyDerivativeWorkflows = newStoryDerivativeWorkflows;
function piperXRouter() public view returns (address) {
return _piperXRouter;
}

/**
* @dev See {ISpotlightTokenFactory-baseToken}.
* @dev See {ISpotlightTokenFactory-piperXFactory}.
*/
function baseToken() public view needInitialized returns (address) {
return _baseToken;
function piperXFactory() public view returns (address) {
return _piperXFactory;
}

/**
* @dev See {ISpotlightTokenFactory-setBaseToken}.
* @dev See {ISpotlightTokenFactory-rewardsVault}.
*/
function setBaseToken(address newBaseToken) external needInitialized onlyOwner {
_baseToken = newBaseToken;
}

/**
* @dev See {ISpotlightTokenFactory-bondingCurve}.
*/
function bondingCurve() public view needInitialized returns (address) {
return _bondingCurve;
}

/**
* @dev See {ISpotlightTokenFactory-setBondingCurve}.
*/
function setBindingCurve(address newBondingCurve) external needInitialized onlyOwner {
_bondingCurve = newBondingCurve;
function rewardsVault() public view returns (address) {
return _rewardsVault;
}

/**
* @dev See {ISpotlightTokenFactory-calculateTokenAddress}.
*/
function calculateTokenAddress(address tokenCreator) external view needInitialized returns (address) {
function calculateTokenAddress(address tokenCreator) external view returns (address) {
bytes32 salt = _salt(tokenCreator);
return Clones.predictDeterministicAddress(_tokenImplementation, salt, address(this));
}
Expand All @@ -171,10 +147,12 @@ contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenF
StoryWorkflowStructs.MakeDerivative calldata derivData,
StoryWorkflowStructs.IPMetadata calldata ipMetadata,
StoryWorkflowStructs.SignatureData calldata sigMetadata,
StoryWorkflowStructs.SignatureData calldata sigRegister,
address specificAddress
StoryWorkflowStructs.SignatureData calldata sigRegister
) external payable needInitialized returns (address tokenAddress, address ipId) {
tokenAddress = _deploySpotlightToken(tokenCreationData, msg.sender, specificAddress);
require(derivData.parentIpIds.length > 0, "SpotlightTokenFactory: Parent IP ID is required");
address parentIPAccount = derivData.parentIpIds[0];
require(_checkIsValidIPAccount(parentIPAccount), "SpotlightTokenFactory: Parent IP Account is not valid");
tokenAddress = _deploySpotlightToken(tokenCreationData, msg.sender, parentIPAccount);

ISpotlightTokenIPCollection(_tokenIpCollection).mint(msg.sender, tokenCreationData.tokenIpNFTId);

Expand All @@ -200,17 +178,10 @@ contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenF
);
}

/**
* @dev See {ISpotlightTokenFactory-getInitialBuyTokenQuote}.
*/
function getInitialBuyTokenQuote(uint256 tokensOut) external view needInitialized returns (uint256) {
return ISpotlightBondingCurve(_bondingCurve).getTargetTokenBuyQuote(0, tokensOut);
}

/**
* @dev See {ISpotlightTokenFactory-numberOfTokensCreated}.
*/
function numberOfTokensCreated(address tokenCreator) public view needInitialized returns (uint256) {
function numberOfTokensCreated(address tokenCreator) public view returns (uint256) {
return _numbersOfTokensCreated[tokenCreator];
}

Expand All @@ -231,10 +202,6 @@ contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenF
}
}

function _checkIsOwner() internal view {
require(msg.sender == _owner, "SpotlightTokenFactory: Not owner");
}

function _salt(address account) internal view returns (bytes32) {
return keccak256(abi.encodePacked(account, numberOfTokensCreated(account)));
}
Expand All @@ -251,7 +218,7 @@ contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenF
_bondingCurve,
address(this),
ipAccount,
_protocolRewards,
_rewardsVault,
_piperXRouter,
_piperXFactory
);
Expand Down Expand Up @@ -287,4 +254,9 @@ contract SpotlightTokenFactory is SpotlightTokenFactoryStorage, ISpotlightTokenF
(bool success,) = tokenCreator.call{value: excess}("");
require(success, "SpotlightTokenFactory: Failed to return excess fee");
}

function _checkIsValidIPAccount(address ipAccount) internal view returns (bool) {
(, address tokenContract,) = IMinimalIPAccount(ipAccount).token();
return tokenContract != address(0);
}
}
Loading

0 comments on commit 5384c9a

Please sign in to comment.