Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spg): add group IPA feature #40

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ MAINNET_RPC_URL = https://eth-mainnet.g.alchemy.com/v2/1234123412341234
# TODO: Remove private key in favor of forge cast wallet
MAINNET_PRIVATEKEY= 12341234123412341234123412341234

# SEPOLIA
SEPOLIA_RPC_URL = https://eth-mainnet.g.alchemy.com/v2/1234123412341234
# TESTNET
TESTNET_RPC_URL = https://eth-mainnet.g.alchemy.com/v2/1234123412341234
# TODO: Remove private key in favor of forge cast wallet

# ETHSCAN
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
# CHANGELOG

## v1.1.0
- Migrate periphery contracts from protocol core repo (#1)
- Revamped SPG with NFT collection and mint token logic. (#5, #6)
- Added support for batch transactions via `multicall` (#38)
- Added functionality for registering IP with metadata and supporting metadata for SPG NFT. (#8, #20, #37)
- Addressed ownership transfer issues in deployment script. (#18, #39)
- Fixed issues with derivative registration, including minting fees for commercial licenses, license token flow, and making register and attach PIL terms idempotent. (#23, #25, #30)
- Added SPG & SPG NFT upgrade scripts (#10)
- Added IP Graph, Solady's ERC6551 integration, and core protocol package bumps. (#30)
- Enhance CI/CD, repo, and misc.(#2, #3, #11, #32)

**Full Changelog**: [v1.1.0](https://github.com/storyprotocol/protocol-periphery-v1/commits/v1.1.0)

## v1.0.0-beta-rc1

This is the first release of the Story Protocol Gateway

- Adds the SPG, a convenient wrapper around the core contracts for registration
- Includes NFT minting management tooling for registering and minting in one-shot

252 changes: 203 additions & 49 deletions contracts/StoryProtocolGateway.sol

Large diffs are not rendered by default.

110 changes: 83 additions & 27 deletions contracts/interfaces/IStoryProtocolGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ interface IStoryProtocolGateway {
address owner
) external returns (address nftContract);

/// @notice Mint an NFT from a collection and register it with metadata as an IP.
/// @notice Mint an NFT from a SPGNFT collection and register it with metadata as an IP.
/// @dev Caller must have the minter role for the provided SPG NFT.
/// @param nftContract The address of the NFT collection.
/// @param spgNftContract The address of the SPGNFT collection.
/// @param recipient The address of the recipient of the minted NFT.
/// @param ipMetadata OPTIONAL. The desired metadata for the newly minted NFT and registered IP.
/// @return ipId The ID of the registered IP.
/// @return tokenId The ID of the minted NFT.
/// @return tokenId The ID of the newly minted NFT.
function mintAndRegisterIp(
address nftContract,
address spgNftContract,
address recipient,
IPMetadata calldata ipMetadata
) external returns (address ipId, uint256 tokenId);
Expand All @@ -78,7 +78,7 @@ interface IStoryProtocolGateway {
/// @param tokenId The ID of the NFT.
/// @param ipMetadata OPTIONAL. The desired metadata for the newly registered IP.
/// @param sigMetadata OPTIONAL. Signature data for setAll (metadata) for the IP via the Core Metadata Module.
/// @return ipId The ID of the registered IP.
/// @return ipId The ID of the newly registered IP.
function registerIp(
address nftContract,
uint256 tokenId,
Expand All @@ -89,21 +89,22 @@ interface IStoryProtocolGateway {
/// @notice Register Programmable IP License Terms (if unregistered) and attach it to IP.
/// @param ipId The ID of the IP.
/// @param terms The PIL terms to be registered.
/// @return licenseTermsId The ID of the registered PIL terms.
/// @return licenseTermsId The ID of the newly registered PIL terms.
function registerPILTermsAndAttach(address ipId, PILTerms calldata terms) external returns (uint256 licenseTermsId);

/// @notice Mint an NFT from a collection, register it with metadata as an IP, register Programmable IP License
/// @notice Mint an NFT from a SPGNFT collection, register it with metadata as an IP,
/// register Programmable IPLicense
/// Terms (if unregistered), and attach it to the registered IP.
/// @dev Caller must have the minter role for the provided SPG NFT.
/// @param nftContract The address of the NFT collection.
/// @param spgNftContract The address of the SPGNFT collection.
/// @param recipient The address of the recipient of the minted NFT.
/// @param ipMetadata OPTIONAL. The desired metadata for the newly minted NFT and registered IP.
/// @param terms The PIL terms to be registered.
/// @return ipId The ID of the registered IP.
/// @return tokenId The ID of the minted NFT.
/// @return licenseTermsId The ID of the registered PIL terms.
/// @return ipId The ID of the newly registered IP.
/// @return tokenId The ID of the newly minted NFT.
/// @return licenseTermsId The ID of the newly registered PIL terms.
function mintAndRegisterIpAndAttachPILTerms(
address nftContract,
address spgNftContract,
address recipient,
IPMetadata calldata ipMetadata,
PILTerms calldata terms
Expand All @@ -118,8 +119,8 @@ interface IStoryProtocolGateway {
/// @param terms The PIL terms to be registered.
/// @param sigMetadata OPTIONAL. Signature data for setAll (metadata) for the IP via the Core Metadata Module.
/// @param sigAttach Signature data for attachLicenseTerms to the IP via the Licensing Module.
/// @return ipId The ID of the registered IP.
/// @return licenseTermsId The ID of the registered PIL terms.
/// @return ipId The ID of the newly registered IP.
/// @return licenseTermsId The ID of the newly registered PIL terms.
function registerIpAndAttachPILTerms(
address nftContract,
uint256 tokenId,
Expand All @@ -129,29 +130,29 @@ interface IStoryProtocolGateway {
SignatureData calldata sigAttach
) external returns (address ipId, uint256 licenseTermsId);

/// @notice Mint an NFT from a collection and register it as a derivative IP without license tokens.
/// @notice Mint an NFT from a SPGNFT collection and register it as a derivative IP without license tokens.
/// @dev Caller must have the minter role for the provided SPG NFT.
/// @param nftContract The address of the NFT collection.
/// @param spgNftContract The address of the SPGNFT collection.
/// @param derivData The derivative data to be used for registerDerivative.
/// @param ipMetadata OPTIONAL. The desired metadata for the newly minted NFT and registered IP.
/// @param recipient The address to receive the minted NFT.
/// @return ipId The ID of the registered IP.
/// @return tokenId The ID of the minted NFT.
/// @return ipId The ID of the newly registered IP.
/// @return tokenId The ID of the newly minted NFT.
function mintAndRegisterIpAndMakeDerivative(
address nftContract,
address spgNftContract,
MakeDerivative calldata derivData,
IPMetadata calldata ipMetadata,
address recipient
) external returns (address ipId, uint256 tokenId);

/// @notice Register the given NFT as a derivative IP with metadata without using license tokens.
/// @notice Register the given NFT as a derivative IP with metadata without license tokens.
/// @param nftContract The address of the NFT collection.
/// @param tokenId The ID of the NFT.
/// @param derivData The derivative data to be used for registerDerivative.
/// @param ipMetadata OPTIONAL. The desired metadata for the newly registered IP.
/// @param sigMetadata OPTIONAL. Signature data for setAll (metadata) for the IP via the Core Metadata Module.
/// @param sigRegister Signature data for registerDerivative for the IP via the Licensing Module.
/// @return ipId The ID of the registered IP.
/// @return ipId The ID of the newly registered IP.
function registerIpAndMakeDerivative(
address nftContract,
uint256 tokenId,
Expand All @@ -161,17 +162,17 @@ interface IStoryProtocolGateway {
SignatureData calldata sigRegister
) external returns (address ipId);

/// @notice Mint an NFT from a collection and register it as a derivative IP using license tokens.
/// @notice Mint an NFT from a SPGNFT collection and register it as a derivative IP using license tokens.
/// @dev Caller must have the minter role for the provided SPG NFT.
/// @param nftContract The address of the NFT collection.
/// @param spgNftContract The address of the SPGNFT collection.
/// @param licenseTokenIds The IDs of the license tokens to be burned for linking the IP to parent IPs.
/// @param royaltyContext The context for royalty module, should be empty for Royalty Policy LAP.
/// @param ipMetadata OPTIONAL. The desired metadata for the newly minted NFT and registered IP.
/// @param recipient The address to receive the minted NFT.
/// @return ipId The ID of the registered IP.
/// @return tokenId The ID of the minted NFT.
/// @return ipId The ID of the newly registered IP.
/// @return tokenId The ID of the newly minted NFT.
function mintAndRegisterIpAndMakeDerivativeWithLicenseTokens(
address nftContract,
address spgNftContract,
uint256[] calldata licenseTokenIds,
bytes calldata royaltyContext,
IPMetadata calldata ipMetadata,
Expand All @@ -186,7 +187,7 @@ interface IStoryProtocolGateway {
/// @param ipMetadata OPTIONAL. The desired metadata for the newly registered IP.
/// @param sigMetadata OPTIONAL. Signature data for setAll (metadata) for the IP via the Core Metadata Module.
/// @param sigRegister Signature data for registerDerivativeWithLicenseTokens for the IP via the Licensing Module.
/// @return ipId The ID of the registered IP.
/// @return ipId The ID of the newly registered IP.
function registerIpAndMakeDerivativeWithLicenseTokens(
address nftContract,
uint256 tokenId,
Expand All @@ -196,4 +197,59 @@ interface IStoryProtocolGateway {
SignatureData calldata sigMetadata,
SignatureData calldata sigRegister
) external returns (address ipId);

/// @notice Mint an NFT from a SPGNFT collection, register it with metadata as an IP,
/// attach Programmable IP License Terms to the registered IP, and add it to a group IP.
/// @dev Caller must have the minter role for the provided SPG NFT.
/// @param spgNftContract The address of the SPGNFT collection.
/// @param groupId The ID of the group IP to add the newly registered IP.
/// @param recipient The address of the recipient of the minted NFT.
/// @param licenseTermsId The ID of the registered PIL terms that will be attached to the newly registered IP.
/// @param ipMetadata OPTIONAL. The desired metadata for the newly minted NFT and registered IP.
/// @param sigAddToGroup Signature data for addIp to the group IP via the Grouping Module.
/// @return ipId The ID of the newly registered IP.
/// @return tokenId The ID of the newly minted NFT.
function mintAndRegisterIpAndAttachPILTermsAndAddToGroup(
address spgNftContract,
address groupId,
address recipient,
uint256 licenseTermsId,
IPMetadata calldata ipMetadata,
SignatureData calldata sigAddToGroup
) external returns (address ipId, uint256 tokenId);

/// @notice Register an NFT as IP with metadata, attach Programmable IP License Terms to the registered IP,
/// and add it to a group IP.
/// @param nftContract The address of the NFT collection.
/// @param tokenId The ID of the NFT.
/// @param groupId The ID of the group IP to add the newly registered IP.
/// @param licenseTermsId The ID of the registered PIL terms that will be attached to the newly registered IP.
/// @param ipMetadata OPTIONAL. The desired metadata for the newly registered IP.
/// @param sigMetadataAndAttach Signature data for setAll (metadata) and attachLicenseTerms to the IP
/// via the Core Metadata Module and Licensing Module.
/// @param sigAddToGroup Signature data for addIp to the group IP via the Grouping Module.
/// @return ipId The ID of the newly registered IP.
function registerIpAndAttachPILTermsAndAddToGroup(
address nftContract,
uint256 tokenId,
address groupId,
uint256 licenseTermsId,
IPMetadata calldata ipMetadata,
SignatureData calldata sigMetadataAndAttach,
SignatureData calldata sigAddToGroup
) external returns (address ipId);

/// @notice Register a group IP with a group reward pool, register Programmable IP License Terms,
/// attach it to the group IP, and add individual IPs to the group IP.
/// @dev ipIds must be have the same PIL terms as the group IP.
/// @param groupPool The address of the group reward pool.
/// @param ipIds The IDs of the IPs to add to the newly registered group IP.
/// @param groupIpTerms The PIL terms to be registered and attached to the newly registered group IP.
/// @return groupId The ID of the newly registered group IP.
/// @return groupLicenseTermsId The ID of the newly registered PIL terms.
function registerGroupAndAttachPILTermsAndAddIps(
address groupPool,
address[] calldata ipIds,
PILTerms calldata groupIpTerms
) external returns (address groupId, uint256 groupLicenseTermsId);
}
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fs_permissions = [{ access = 'read', path = './' }, { access = 'read-write', pat
# Comment out for local development (testing requires 0xSplit forks — will add Mock soon)
# pin fork by using --fork-block-number 19042069 to reduce wait time
mainnet = "https://rpc.ankr.com/eth"
sepolia = "${SEPOLIA_RPC_URL}"
testnet = "${TESTNET_RPC_URL}"


# See more config options https://github.com/foundry-rs/foundry/tree/master/config
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@story-protocol/protocol-periphery",
"version": "v1.0.0-rc1",
"version": "v1.1.0",
"description": "Story Protocol periphery smart contracts",
"main": "",
"directories": {
Expand Down
8 changes: 4 additions & 4 deletions script/Main.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ pragma solidity ^0.8.23;

import { console2 } from "forge-std/console2.sol";
import { Script } from "forge-std/Script.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { ICreate3Deployer } from "@create3-deployer/contracts/Create3Deployer.sol";

import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import { IPAssetRegistry } from "@storyprotocol/core/registries/IPAssetRegistry.sol";

import { StoryProtocolGateway } from "../contracts/StoryProtocolGateway.sol";
import { SPGNFT } from "../contracts/SPGNFT.sol";
Expand Down Expand Up @@ -61,7 +59,9 @@ contract Main is Script, StoryProtocolCoreAddressManager, BroadcastManager, Json
royaltyModuleAddr,
coreMetadataModuleAddr,
pilTemplateAddr,
licenseTokenAddr
licenseTokenAddr,
groupingModuleAddr,
groupNFTAddr
)
);
spg = StoryProtocolGateway(
Expand Down Expand Up @@ -103,7 +103,7 @@ contract Main is Script, StoryProtocolCoreAddressManager, BroadcastManager, Json
console2.log(string.concat(contractKey, " deployed to:"), newAddress);
}

function _getSalt(string memory name) private view returns (bytes32 salt) {
function _getSalt(string memory name) private pure returns (bytes32 salt) {
salt = keccak256(abi.encode(name, create3SaltSeed));
}
}
4 changes: 3 additions & 1 deletion script/UpgradeSPG.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ contract UpgradeSPG is
royaltyModuleAddr,
coreMetadataModuleAddr,
pilTemplateAddr,
licenseTokenAddr
licenseTokenAddr,
groupingModuleAddr,
groupNFTAddr
)
);
console2.log("New SPG Implementation", newSpgImpl);
Expand Down
6 changes: 3 additions & 3 deletions script/utils/BroadcastManager.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ contract BroadcastManager is Script {
multisig = vm.envAddress("MAINNET_MULTISIG_ADDRESS");
vm.startBroadcast(deployerPrivateKey);
} else if (block.chainid == 1513 || block.chainid == 11155111) {
deployerPrivateKey = vm.envUint("SEPOLIA_PRIVATEKEY");
deployer = vm.envAddress("SEPOLIA_DEPLOYER_ADDRESS");
multisig = vm.envAddress("SEPOLIA_MULTISIG_ADDRESS");
deployerPrivateKey = vm.envUint("TESTNET_PRIVATEKEY");
deployer = vm.envAddress("TESTNET_DEPLOYER_ADDRESS");
multisig = vm.envAddress("TESTNET_MULTISIG_ADDRESS");
vm.startBroadcast(deployerPrivateKey);
} else if (block.chainid == 31337) {
multisig = address(0x456);
Expand Down
4 changes: 4 additions & 0 deletions script/utils/StoryProtocolCoreAddressManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ contract StoryProtocolCoreAddressManager is Script {
address internal accessControllerAddr;
address internal pilTemplateAddr;
address internal licenseTokenAddr;
address internal groupingModuleAddr;
address internal groupNFTAddr;

function _readStoryProtocolCoreAddresses() internal {
string memory root = vm.projectRoot();
Expand All @@ -39,5 +41,7 @@ contract StoryProtocolCoreAddressManager is Script {
accessControllerAddr = json.readAddress(".main.AccessController");
pilTemplateAddr = json.readAddress(".main.PILicenseTemplate");
licenseTokenAddr = json.readAddress(".main.LicenseToken");
groupingModuleAddr = json.readAddress(".main.GroupingModule");
groupNFTAddr = json.readAddress(".main.GroupNFT");
}
}
2 changes: 1 addition & 1 deletion script/utils/upgrades/ERC7201Helper.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ contract ERC7201HelperScript is Script {
string constant NAMESPACE = "story-protocol-periphery";
string constant CONTRACT_NAME = "SPG";

function run() external {
function run() external pure {
bytes memory erc7201Key = abi.encodePacked(NAMESPACE, ".", CONTRACT_NAME);
bytes32 hash = keccak256(abi.encode(uint256(keccak256(erc7201Key)) - 1)) & ~bytes32(uint256(0xff));

Expand Down
Loading
Loading