Skip to content

Commit

Permalink
fix(story-nft): multiple token could be minted by same caller
Browse files Browse the repository at this point in the history
  • Loading branch information
sebsadface committed Feb 11, 2025
1 parent 8bb6d18 commit b431f66
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 0 deletions.
4 changes: 4 additions & 0 deletions contracts/interfaces/story-nft/IStoryBadgeNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ interface IStoryBadgeNFT is IStoryNFT, IERC721Metadata, IERC5192 {
/// @notice Zero address provided as a param to StoryBadgeNFT functions.
error StoryBadgeNFT__ZeroAddressParam();

/// @notice The recipient already has a badge.
/// @param recipient The address of the recipient.
error StoryBadgeNFT__RecipientAlreadyHasBadge(address recipient);

////////////////////////////////////////////////////////////////////////////
// Structs //
////////////////////////////////////////////////////////////////////////////
Expand Down
3 changes: 3 additions & 0 deletions contracts/story-nft/OrgStoryNFTFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ contract OrgStoryNFTFactory is IOrgStoryNFTFactory, AccessManagedUpgradeable, UU
if ($.deployedOrgStoryNftsByOrgName[orgName] != address(0))
revert OrgStoryNFTFactory__OrgAlreadyDeployed(orgName, $.deployedOrgStoryNftsByOrgName[orgName]);

if (ORG_NFT.balanceOf(orgNftRecipient) > 0)
revert OrgStoryNFTFactory__OrgAlreadyDeployed(orgName, $.deployedOrgStoryNftsByOrgName[orgName]);

// Mint the organization NFT and register it as an IP
if (isRootOrg) {
(orgTokenId, orgIpId) = ORG_NFT.mintRootOrgNft(orgNftRecipient, orgIpMetadata);
Expand Down
3 changes: 3 additions & 0 deletions contracts/story-nft/StoryBadgeNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ contract StoryBadgeNFT is IStoryBadgeNFT, BaseOrgStoryNFT, CachableNFT, ERC721Ho
// The given signature must not have been used
if ($.usedSignatures[signature]) revert StoryBadgeNFT__SignatureAlreadyUsed();

// The recipient must not already have a badge
if (balanceOf(recipient) > 0) revert StoryBadgeNFT__RecipientAlreadyHasBadge(recipient);

// Mark the signature as used
$.usedSignatures[signature] = true;

Expand Down
38 changes: 38 additions & 0 deletions test/story-nft/OrgStoryNFTFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -369,4 +369,42 @@ contract OrgStoryNFTFactoryTest is BaseTest {
storyNftInitParams: storyNftInitParams
});
}

function test_StoryNFTFactory_revert_deployStoryNftByAdmin_OrgAlreadyDeployed() public {
bytes memory signature = _signAddress(orgStoryNftFactorySignerSk, u.carl);

vm.startPrank(u.carl);
(, uint256 orgTokenId, address orgIpId, address storyNft) = orgStoryNftFactory.deployOrgStoryNft({
orgStoryNftTemplate: address(defaultOrgStoryNftTemplate),
orgNftRecipient: u.carl,
orgName: orgName,
orgIpMetadata: ipMetadataDefault,
signature: signature,
storyNftInitParams: storyNftInitParams
});
vm.stopPrank();

// Change signer
vm.prank(u.admin);
orgStoryNftFactory.setSigner(u.dan);

// Try to deploy with same org name but different signer
signature = _signAddress(sk.dan, u.carl);
vm.prank(u.carl);
vm.expectRevert(
abi.encodeWithSelector(
IOrgStoryNFTFactory.OrgStoryNFTFactory__OrgAlreadyDeployed.selector,
orgName,
address(storyNft)
)
);
orgStoryNftFactory.deployOrgStoryNft({
orgStoryNftTemplate: address(defaultOrgStoryNftTemplate),
orgNftRecipient: u.carl,
orgName: orgName,
orgIpMetadata: ipMetadataDefault,
signature: signature,
storyNftInitParams: storyNftInitParams
});
}
}
21 changes: 21 additions & 0 deletions test/story-nft/StoryBadgeNFT.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -321,4 +321,25 @@ contract StoryBadgeNFTTest is BaseTest {
rootOrgStoryNft.safeTransferFrom(u.carl, u.bob, tokenId);
vm.stopPrank();
}

function test_StoryBadgeNFT_revert_mint_RecipientAlreadyHasBadge() public {
bytes memory signature = _signAddress(rootOrgStoryNftSignerSk, u.carl);

vm.startPrank(u.carl);
rootOrgStoryNft.mint(u.carl, signature);
vm.stopPrank();

// Change signer
vm.prank(u.admin);
rootOrgStoryNft.setSigner(u.dan);

// Try to mint again with new signer
signature = _signAddress(sk.dan, u.carl);

vm.prank(u.carl);
vm.expectRevert(
abi.encodeWithSelector(IStoryBadgeNFT.StoryBadgeNFT__RecipientAlreadyHasBadge.selector, u.carl)
);
rootOrgStoryNft.mint(u.carl, signature);
}
}

0 comments on commit b431f66

Please sign in to comment.