Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 101 additions & 2 deletions src/StoryFactory.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IMCV2_Bond} from "./interfaces/IMCV2_Bond.sol";
import {IMCV2_Bond, TokenParams, BondParams} from "./interfaces/IMCV2_Bond.sol";
import {IERC20} from "./interfaces/IERC20.sol";

/// @title StoryFactory — PlotLink storyline and plot management
Expand Down Expand Up @@ -45,22 +45,121 @@ contract StoryFactory {

event Donation(uint256 indexed storylineId, address indexed donor, uint256 amount);

// -----------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------

uint16 public constant MINT_ROYALTY = 500; // 5% (basis points, base 10000)
uint16 public constant BURN_ROYALTY = 500; // 5%

// -----------------------------------------------------------------------
// State
// -----------------------------------------------------------------------

IMCV2_Bond public immutable BOND;
IERC20 public immutable PLOT_TOKEN;

/// @notice Bonding curve step arrays (same for every storyline, set at deploy)
uint128[] public stepRanges;
uint128[] public stepPrices;
uint128 public immutable MAX_SUPPLY;

mapping(uint256 => Storyline) public storylines;
uint256 public storylineCount;

// -----------------------------------------------------------------------
// Constructor
// -----------------------------------------------------------------------

constructor(address _bond, address _plotToken) {
constructor(
address _bond,
address _plotToken,
uint128 _maxSupply,
uint128[] memory _stepRanges,
uint128[] memory _stepPrices
) {
require(_stepRanges.length == _stepPrices.length, "Step arrays length mismatch");
require(_stepRanges.length > 0, "Empty step arrays");

BOND = IMCV2_Bond(_bond);
PLOT_TOKEN = IERC20(_plotToken);
MAX_SUPPLY = _maxSupply;
stepRanges = _stepRanges;
stepPrices = _stepPrices;
}

// -----------------------------------------------------------------------
// createStoryline
// -----------------------------------------------------------------------

/// @notice Create a new storyline with a genesis plot on a Mint Club V2 bonding curve
/// @param title Human-readable title of the storyline
/// @param openingCID IPFS CID of the genesis plot content
/// @param openingHash keccak256 hash of the genesis plot content
/// @param hasDeadline Whether the 72h deadline mechanism is enabled
/// @return storylineId The ID of the newly created storyline
function createStoryline(string calldata title, string calldata openingCID, bytes32 openingHash, bool hasDeadline)
external
returns (uint256 storylineId)
{
require(bytes(title).length > 0, "Empty title");
require(bytes(openingCID).length >= 46 && bytes(openingCID).length <= 100, "Invalid CID");

storylineId = ++storylineCount;

// 1. Create token on Mint Club V2 bonding curve
// Factory becomes initial creator; we transfer to writer below
TokenParams memory tp =
TokenParams({name: title, symbol: string(abi.encodePacked("PLOT-", _uint2str(storylineId)))});

BondParams memory bp = BondParams({
mintRoyalty: MINT_ROYALTY,
burnRoyalty: BURN_ROYALTY,
reserveToken: address(PLOT_TOKEN),
maxSupply: MAX_SUPPLY,
stepRanges: stepRanges,
stepPrices: stepPrices
});

address tokenAddress = BOND.createToken(tp, bp);

// 2. Transfer creator role to writer (royalties go directly to them)
BOND.updateBondCreator(tokenAddress, msg.sender);

// 3. Store storyline
storylines[storylineId] = Storyline({
writer: msg.sender,
token: tokenAddress,
plotCount: 1,
lastPlotTime: block.timestamp,
hasDeadline: hasDeadline,
sunset: false
});

// 4. Emit events
emit StorylineCreated(storylineId, msg.sender, tokenAddress, title, hasDeadline, openingCID, openingHash);
emit PlotChained(storylineId, 0, msg.sender, openingCID, openingHash);
}

// -----------------------------------------------------------------------
// Internal helpers
// -----------------------------------------------------------------------

/// @dev Convert uint to decimal string (for token symbol generation)
function _uint2str(uint256 value) internal pure returns (string memory) {
if (value == 0) return "0";
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}