Skip to content

Commit 32f0857

Browse files
committed
badge WIP
1 parent 6acb0db commit 32f0857

16 files changed

+1776
-27
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.26;
3+
4+
/// @title Minimal Soulbound NFT Interface
5+
/// @notice Minimal interface for soulbinding EIP-721 NFTs
6+
interface IERC5192 {
7+
/// @notice Emitted when the locking status is changed to locked.
8+
/// @dev If a token is minted and the status is locked, this event should be emitted.
9+
/// @param tokenId The identifier for a token.
10+
event Locked(uint256 tokenId);
11+
12+
/// @notice Emitted when the locking status is changed to unlocked.
13+
/// @dev If a token is minted and the status is unlocked, this event should be emitted.
14+
/// @param tokenId The identifier for a token.
15+
event Unlocked(uint256 tokenId);
16+
17+
/// @notice Returns the locking status of an Soulbound Token
18+
/// @dev SBTs assigned to zero address are considered invalid, and queries
19+
/// about them do throw.
20+
/// @param tokenId The identifier for an SBT.
21+
function locked(uint256 tokenId) external view returns (bool);
22+
}
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.26;
3+
4+
import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
5+
6+
/// @title Organization NFT Interface
7+
/// @notice Each organization token represents a Story ecosystem project.
8+
/// The root organization token represents Story.
9+
/// Each organization token register as a IP on Story and is a derivative of the root organization IP.
10+
interface IOrgNFT is IERC721Metadata {
11+
////////////////////////////////////////////////////////////////////////////
12+
// Errors //
13+
////////////////////////////////////////////////////////////////////////////
14+
/// @notice Caller is not the StoryNFTFactory contract.
15+
/// @param caller The address of the caller.
16+
/// @param storyNftFactory The address of the `StoryNFTFactory` contract.
17+
error OrgNFT__CallerNotStoryNFTFactory(address caller, address storyNftFactory);
18+
19+
/// @notice Root organization NFT has already been minted.
20+
error OrgNFT__RootOrgNftAlreadyMinted();
21+
22+
/// @notice Root organization NFT has not been minted yet (`mintRootOrgNft` has not been called).
23+
error OrgNFT__RootOrgNftNotMinted();
24+
25+
/// @notice Zero address provided as a param to OrgNFT functions.
26+
error OrgNFT__ZeroAddressParam();
27+
28+
////////////////////////////////////////////////////////////////////////////
29+
// Events //
30+
////////////////////////////////////////////////////////////////////////////
31+
/// @notice Emitted when a organization token minted.
32+
/// @param recipient The address of the recipient of the organization token.
33+
/// @param orgNft The address of the organization NFT.
34+
/// @param tokenId The ID of the minted organization token.
35+
/// @param orgIpId The ID of the organization IP.
36+
event OrgNFTMinted(address recipient, address orgNft, uint256 tokenId, address orgIpId);
37+
38+
////////////////////////////////////////////////////////////////////////////
39+
// Functions //
40+
////////////////////////////////////////////////////////////////////////////
41+
/// @notice Mints the root organization token and register it as an IP.
42+
/// @param recipient The address of the recipient of the root organization token.
43+
/// @param tokenURI The URI of the root organization token.
44+
/// @return rootOrgTokenId The ID of the root organization token.
45+
/// @return rootOrgIpId The ID of the root organization IP.
46+
function mintRootOrgNft(
47+
address recipient,
48+
string memory tokenURI
49+
) external returns (uint256 rootOrgTokenId, address rootOrgIpId);
50+
51+
/// @notice Mints a organization token, register it as an IP,
52+
/// and makes the IP as a derivative of the root organization IP.
53+
/// @param recipient The address of the recipient of the minted organization token.
54+
/// @param tokenURI The URI of the minted organization token.
55+
/// @return orgTokenId The ID of the minted organization token.
56+
/// @return orgIpId The ID of the organization IP.
57+
function mintOrgNft(
58+
address recipient,
59+
string memory tokenURI
60+
) external returns (uint256 orgTokenId, address orgIpId);
61+
62+
/// @notice Returns the ID of the root organization IP.
63+
function getRootOrgIpId() external view returns (address);
64+
65+
/// @notice Returns the total supply of OrgNFT.
66+
function totalSupply() external view returns (uint256);
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.26;
3+
4+
import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
5+
6+
import { IERC5192 } from "./IERC5192.sol";
7+
8+
/// @title Story Badge NFT Interface
9+
/// @notice A Story Badge NFT is a soulbound NFT that has an unified token URI for all tokens.
10+
interface IStoryBadgeNFT is IERC5192, IERC721Metadata {
11+
////////////////////////////////////////////////////////////////////////////
12+
// Errors //
13+
////////////////////////////////////////////////////////////////////////////
14+
/// @notice Invalid whitelist signature.
15+
error StoryBadgeNFT__InvalidSignature();
16+
17+
/// @notice The provided whitelist signature is already used.
18+
error StoryBadgeNFT__SignatureAlreadyUsed();
19+
20+
/// @notice Badges are soulbound, cannot be transferred.
21+
error StoryBadgeNFT__TransferLocked();
22+
23+
/// @notice Zero address provided as a param to StoryBadgeNFT functions.
24+
error StoryBadgeNFT__ZeroAddressParam();
25+
26+
////////////////////////////////////////////////////////////////////////////
27+
// Structs //
28+
////////////////////////////////////////////////////////////////////////////
29+
/// @notice Struct for custom data for initializing the StoryBadgeNFT contract.
30+
/// @param tokenURI The token URI for all the badges (follows OpenSea metadata standard).
31+
/// @param signer The signer of the whitelist signatures.
32+
struct CustomInitParams {
33+
string tokenURI;
34+
address signer;
35+
}
36+
37+
////////////////////////////////////////////////////////////////////////////
38+
// Events //
39+
////////////////////////////////////////////////////////////////////////////
40+
/// @notice Emitted when a badge NFT is minted.
41+
/// @param recipient The address of the recipient of the badge NFT.
42+
/// @param tokenId The token ID of the minted badge NFT.
43+
/// @param ipId The ID of the badge NFT IP.
44+
event StoryBadgeNFTMinted(address recipient, uint256 tokenId, address ipId);
45+
46+
/// @notice Emitted when the signer is updated.
47+
/// @param signer The new signer address.
48+
event StoryBadgeNFTSignerUpdated(address signer);
49+
50+
////////////////////////////////////////////////////////////////////////////
51+
// Functions //
52+
////////////////////////////////////////////////////////////////////////////
53+
/// @notice Mints a badge for the given recipient, registers it as an IP,
54+
/// and makes it a derivative of the organization IP.
55+
/// @param recipient The address of the recipient of the badge NFT.
56+
/// @param signature The signature from the whitelist signer. This signautre is genreated by having the whitelist
57+
/// signer sign the caller's address (msg.sender) for this `mint` function.
58+
/// @return tokenId The token ID of the minted badge NFT.
59+
/// @return ipId The ID of the badge NFT IP.
60+
function mint(address recipient, bytes calldata signature) external returns (uint256 tokenId, address ipId);
61+
62+
/// @notice Updates the whitelist signer.
63+
/// @param signer_ The new whitelist signer address.
64+
function setSigner(address signer_) external;
65+
66+
/// @notice Updates the unified token URI for all badges.
67+
/// @param tokenURI_ The new token URI.
68+
function setTokenURI(string memory tokenURI_) external;
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.26;
3+
4+
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
5+
6+
/// @title IStoryNFT
7+
/// @notice Interface for StoryNFT contracts.
8+
interface IStoryNFT is IERC721 {
9+
////////////////////////////////////////////////////////////////////////////
10+
// Errors //
11+
////////////////////////////////////////////////////////////////////////////
12+
/// @notice Zero address provided as a param to BaseStoryNFT constructor.
13+
error BaseStoryNFT__ZeroAddressParam();
14+
15+
////////////////////////////////////////////////////////////////////////////
16+
// Structs //
17+
////////////////////////////////////////////////////////////////////////////
18+
/// @notice Struct for initializing StoryNFT contracts.
19+
/// @param owner The address of the owner of this collection.
20+
/// @param name The name of the collection.
21+
/// @param symbol The symbol of the collection.
22+
/// @param contractURI The contract URI of the collection (follows OpenSea contract-level metadata standard).
23+
/// @param baseURI The base URI of the collection (see {ERC721URIStorage-tokenURI} for how it is used).
24+
/// @param customInitData Custom data to initialize the StoryNFT.
25+
struct StoryNftInitParams {
26+
address owner;
27+
string name;
28+
string symbol;
29+
string contractURI;
30+
string baseURI;
31+
bytes customInitData;
32+
}
33+
34+
////////////////////////////////////////////////////////////////////////////
35+
// Functions //
36+
////////////////////////////////////////////////////////////////////////////
37+
/// @notice Initializes the StoryNFT.
38+
/// @param orgTokenId_ The token ID of the organization NFT.
39+
/// @param orgIpId_ The ID of the organization IP.
40+
/// @param initParams The initialization parameters for StoryNFT {see {StoryNftInitParams}}.
41+
function initialize(uint256 orgTokenId_, address orgIpId_, StoryNftInitParams calldata initParams) external;
42+
43+
/// @notice Returns the current total supply of the collection.
44+
function totalSupply() external view returns (uint256);
45+
46+
/// @notice Returns the contract URI of the collection (follows OpenSea contract-level metadata standard).
47+
function contractURI() external view returns (string memory);
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.26;
3+
4+
import { IStoryNFT } from "./IStoryNFT.sol";
5+
6+
/// @title Story NFT Factory Interface
7+
/// @notice Story NFT Factory is the entrypoint for creating new Story NFT collections.
8+
interface IStoryNFTFactory {
9+
////////////////////////////////////////////////////////////////////////////
10+
// Errors //
11+
////////////////////////////////////////////////////////////////////////////
12+
/// @notice Invalid signature provided to StoryNFTFactory functions.
13+
/// @param signature The signature that is invalid.
14+
error StoryNFTFactory__InvalidSignature(bytes signature);
15+
16+
/// @notice NftTemplate is not whitelisted to be used as a StoryNFT.
17+
/// @param nftTemplate The NFT template that is not whitelisted.
18+
error StoryNFTFactory__NftTemplateNotWhitelisted(address nftTemplate);
19+
20+
/// @notice Organization is already deployed by the StoryNFTFactory.
21+
/// @param orgName The name of the organization that is already deployed.
22+
/// @param deployedStoryNft The address of the already deployed StoryNFT for the organization.
23+
error StoryNFTFactory__OrgAlreadyDeployed(string orgName, address deployedStoryNft);
24+
25+
/// @notice Organization is not found in the StoryNFTFactory.
26+
/// @param orgName The name of the organization that is not found.
27+
error StoryNFTFactory__OrgNotFoundByOrgName(string orgName);
28+
29+
/// @notice Organization is not found in the StoryNFTFactory.
30+
/// @param orgTokenId The token ID of the organization that is not found.
31+
error StoryNFTFactory__OrgNotFoundByOrgTokenId(uint256 orgTokenId);
32+
33+
/// @notice Organization is not found in the StoryNFTFactory.
34+
/// @param orgIpId The ID of the organization IP that is not found.
35+
error StoryNFTFactory__OrgNotFoundByOrgIpId(address orgIpId);
36+
37+
/// @notice Signature is already used to deploy a StoryNFT.
38+
/// @param signature The signature that is already used.
39+
error StoryNFTFactory__SignatureAlreadyUsed(bytes signature);
40+
41+
/// @notice BaseStoryNFT is not supported by the StoryNFTFactory.
42+
/// @param tokenContract The address of the token contract that does not implement IStoryNFT.
43+
error StoryNFTFactory__UnsupportedIStoryNFT(address tokenContract);
44+
45+
/// @notice Zero address provided as a param to StoryNFTFactory functions.
46+
error StoryNFTFactory__ZeroAddressParam();
47+
48+
////////////////////////////////////////////////////////////////////////////
49+
// Events //
50+
////////////////////////////////////////////////////////////////////////////
51+
/// @notice Emitted when the default StoryNFT template is updated.
52+
/// @param defaultStoryNftTemplate The new default StoryNFT template.
53+
event StoryNFTFactoryDefaultStoryNftTemplateUpdated(address defaultStoryNftTemplate);
54+
55+
/// @notice Emitted when a new orgnization NFT is minted and a new StoryNFT associated with it is deployed.
56+
/// @param orgName The name of the organization.
57+
/// @param orgNft The address of the organization NFT.
58+
/// @param orgTokenId The token ID of the organization NFT.
59+
/// @param orgIpId The ID of the organization IP.
60+
/// @param storyNft The address of the deployed StoryNFT.
61+
event StoryNftDeployed(string orgName, address orgNft, uint256 orgTokenId, address orgIpId, address storyNft);
62+
63+
/// @notice Emitted when the signer of the StoryNFTFactory is updated.
64+
/// @param signer The new signer of the StoryNFTFactory.
65+
event StoryNFTFactorySignerUpdated(address signer);
66+
67+
/// @notice Emitted when a new Story NFT template is whitelisted.
68+
/// @param nftTemplate The new Story NFT template that is whitelisted to be used in StoryNFTFactory.
69+
event StoryNFTFactoryNftTemplateWhitelisted(address nftTemplate);
70+
71+
////////////////////////////////////////////////////////////////////////////
72+
// Functions //
73+
////////////////////////////////////////////////////////////////////////////
74+
/// @notice Mints a new organization NFT and deploys (creates a clone of) `storyNftTemplate` as the StoryNFT
75+
/// associated with the new organization NFT.
76+
/// @param storyNftTemplate The address of a whitelisted StoryNFT template to be cloned.
77+
/// @param orgNftRecipient The address of the recipient of the organization NFT.
78+
/// @param orgName The name of the organization.
79+
/// @param orgTokenURI The token URI of the organization NFT.
80+
/// @param signature The signature from the StoryNFTFactory's whitelist signer. This signautre is genreated by
81+
/// having the whitelist signer sign the caller's address (msg.sender) for this `deployStoryNft` function.
82+
/// @param storyNftInitParams The initialization parameters for StoryNFT {see {IStoryNFT-StoryNftInitParams}}.
83+
/// @return orgNft The address of the organization NFT.
84+
/// @return orgTokenId The token ID of the organization NFT.
85+
/// @return orgIpId The ID of the organization IP.
86+
/// @return storyNft The address of the dployed StoryNFT
87+
function deployStoryNft(
88+
address storyNftTemplate,
89+
address orgNftRecipient,
90+
string calldata orgName,
91+
string calldata orgTokenURI,
92+
bytes calldata signature,
93+
IStoryNFT.StoryNftInitParams calldata storyNftInitParams
94+
) external returns (address orgNft, uint256 orgTokenId, address orgIpId, address storyNft);
95+
96+
/// @notice Mints a new organization NFT and deploys (creates a clone of) `storyNftTemplate` as the StoryNFT
97+
/// associated with the new organization NFT.
98+
/// @dev Enforced to be only callable by the protocol admin in governance.
99+
/// @param storyNftTemplate The address of a whitelisted StoryNFT template to be cloned.
100+
/// @param orgNftRecipient The address of the recipient of the organization NFT.
101+
/// @param orgName The name of the organization.
102+
/// @param orgTokenURI The token URI of the organization NFT.
103+
/// @param storyNftInitParams The initialization parameters for StoryNFT {see {IStoryNFT-StoryNftInitParams}}.
104+
/// @param isRootOrg Whether the organization is the root organization.
105+
/// @return orgNft The address of the organization NFT.
106+
/// @return orgTokenId The token ID of the organization NFT.
107+
/// @return orgIpId The ID of the organization IP.
108+
/// @return storyNft The address of the dployed StoryNFT
109+
function deployStoryNftByAdmin(
110+
address storyNftTemplate,
111+
address orgNftRecipient,
112+
string calldata orgName,
113+
string calldata orgTokenURI,
114+
IStoryNFT.StoryNftInitParams calldata storyNftInitParams,
115+
bool isRootOrg
116+
) external returns (address orgNft, uint256 orgTokenId, address orgIpId, address storyNft);
117+
118+
/// @notice Sets the default StoryNFT template of the StoryNFTFactory.
119+
/// @param defaultStoryNftTemplate The new default StoryNFT template.
120+
function setDefaultStoryNftTemplate(address defaultStoryNftTemplate) external;
121+
122+
/// @notice Sets the signer of the StoryNFTFactory.
123+
/// @param signer The new signer of the StoryNFTFactory.
124+
function setSigner(address signer) external;
125+
126+
/// @notice Whitelists a new StoryNFT template.
127+
/// @param storyNftTemplate The new StoryNFT template to be whitelisted.
128+
function whitelistNftTemplate(address storyNftTemplate) external;
129+
130+
/// @notice Returns the default StoryNFT template address.
131+
function getDefaultStoryNftTemplate() external view returns (address);
132+
133+
/// @notice Returns the address of the StoryNFT for a given organization name.
134+
/// @param orgName The name of the organization.
135+
function getStoryNftAddressByOrgName(string calldata orgName) external view returns (address);
136+
137+
/// @notice Returns the address of the StoryNFT for a given organization token ID.
138+
/// @param orgTokenId The token ID of the organization.
139+
function getStoryNftAddressByOrgTokenId(uint256 orgTokenId) external view returns (address);
140+
141+
/// @notice Returns the address of the StoryNFT for a given organization IP ID.
142+
/// @param orgIpId The ID of the organization IP.
143+
function getStoryNftAddressByOrgIpId(address orgIpId) external view returns (address);
144+
}

0 commit comments

Comments
 (0)