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

Remove Expiry from License Token #123

Merged
merged 1 commit into from
Apr 18, 2024
Merged
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
20 changes: 1 addition & 19 deletions contracts/LicenseToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag
licensorIpId: licensorIpId,
licenseTemplate: licenseTemplate,
licenseTermsId: licenseTermsId,
transferable: ILicenseTemplate(licenseTemplate).isLicenseTransferable(licenseTermsId),
mintedAt: block.timestamp,
expiresAt: ILicenseTemplate(licenseTemplate).getExpireTime(licenseTermsId, block.timestamp)
transferable: ILicenseTemplate(licenseTemplate).isLicenseTransferable(licenseTermsId)
});

LicenseTokenStorage storage $ = _getLicenseTokenStorage();
Expand All @@ -136,7 +134,6 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag

/// @notice Validates License Tokens for registering a derivative IP.
/// @dev This function checks if the License Tokens are valid for the derivative IP registration process.
/// for example, whether token is expired.
/// The function will be called by LicensingModule when registering a derivative IP with license tokens.
/// @param childIpId The ID of the derivative IP.
/// @param childIpOwner The address of the owner of the derivative IP.
Expand All @@ -161,9 +158,6 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag

for (uint256 i = 0; i < tokenIds.length; i++) {
LicenseTokenMetadata memory ltm = $.licenseTokenMetadatas[tokenIds[i]];
if (_isExpiredNow(tokenIds[i])) {
revert Errors.LicenseToken__LicenseTokenExpired(tokenIds[i], ltm.expiresAt, block.timestamp);
}
if (ownerOf(tokenIds[i]) != childIpOwner) {
revert Errors.LicenseToken__NotLicenseTokenOwner(tokenIds[i], childIpOwner, ownerOf(tokenIds[i]));
}
Expand Down Expand Up @@ -213,13 +207,6 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag
return _getLicenseTokenStorage().licenseTokenMetadatas[tokenId].licenseTemplate;
}

/// @notice Gets the expiration time of a License Token.
/// @param tokenId The ID of the License Token.
/// @return The expiration time of the License Token.
function getExpirationTime(uint256 tokenId) external view returns (uint256) {
return _getLicenseTokenStorage().licenseTokenMetadatas[tokenId].expiresAt;
}

/// @notice Returns the canonical protocol-wide DisputeModule
/// @return The DisputeModule instance
function disputeModule() external view returns (IDisputeModule) {
Expand Down Expand Up @@ -319,11 +306,6 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag
return super._update(to, tokenId, auth);
}

function _isExpiredNow(uint256 tokenId) internal view returns (bool) {
uint256 expireTime = _getLicenseTokenStorage().licenseTokenMetadatas[tokenId].expiresAt;
return expireTime != 0 && expireTime < block.timestamp;
}

////////////////////////////////////////////////////////////////////////////
// Upgrades related //
////////////////////////////////////////////////////////////////////////////
Expand Down
12 changes: 1 addition & 11 deletions contracts/interfaces/ILicenseToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ILicensingModule } from "./modules/licensing/ILicensingModule.sol";
/// @title ILicenseToken
/// @notice Interface for the License Token (ERC721) NFT collection that manages License Tokens representing
/// License Terms.
/// Each License Token may represent a set of License Terms and could have an expiration time.
/// Each License Token may represent a set of License Terms.
/// License Tokens are ERC721 NFTs that can be minted, transferred (if allowed), and burned.
/// Derivative IP owners can burn License Tokens to register their IP as a derivative of the licensor IP for which
/// the License Token was minted.
Expand All @@ -22,15 +22,11 @@ interface ILicenseToken is IERC721Metadata, IERC721Enumerable {
/// @param licenseTemplate The address of the License Template associated with the License Token.
/// @param licenseTermsId The ID of the License Terms associated with the License Token.
/// @param transferable Whether the License Token is transferable, determined by the License Terms.
/// @param mintedAt The timestamp at which the License Token was minted.
/// @param expiresAt The timestamp at which the License Token expires.
struct LicenseTokenMetadata {
address licensorIpId;
address licenseTemplate;
uint256 licenseTermsId;
bool transferable;
uint256 mintedAt;
uint256 expiresAt;
}

/// @notice Emitted when a License Token is minted.
Expand Down Expand Up @@ -85,11 +81,6 @@ interface ILicenseToken is IERC721Metadata, IERC721Enumerable {
/// @return True if the License Token has been revoked, false otherwise.
function isLicenseTokenRevoked(uint256 tokenId) external view returns (bool);

/// @notice Gets the expiration time of a License Token.
/// @param tokenId The ID of the License Token.
/// @return The expiration time of the License Token.
function getExpirationTime(uint256 tokenId) external view returns (uint256);

/// @notice Retrieves the metadata associated with a License Token.
/// @param tokenId The ID of the License Token.
/// @return A `LicenseTokenMetadata` struct containing the metadata of the specified License Token.
Expand All @@ -105,7 +96,6 @@ interface ILicenseToken is IERC721Metadata, IERC721Enumerable {

/// @notice Validates License Tokens for registering a derivative IP.
/// @dev This function checks if the License Tokens are valid for the derivative IP registration process.
/// for example, whether token is expired.
/// The function will be called by LicensingModule when registering a derivative IP with license tokens.
/// @param childIpId The ID of the derivative IP.
/// @param childIpOwner The address of the owner of the derivative IP.
Expand Down
3 changes: 0 additions & 3 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,6 @@ library Errors {
/// @notice License token is not transferable.
error LicenseToken__NotTransferable();

/// @notice License token is expired.
error LicenseToken__LicenseTokenExpired(uint256 tokenId, uint256 expiredAt, uint256 currentTimestamp);

/// @notice License token is not owned by the caller.
error LicenseToken__NotLicenseTokenOwner(uint256 tokenId, address iPowner, address tokenOwner);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ contract LicensingIntegrationTest is BaseIntegration {
assertEq(licenseToken.getLicenseTermsId(lcTokenId), 1);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipAcct[1]);
assertEq(licenseToken.getExpirationTime(lcTokenId), 0);
assertEq(licenseToken.totalMintedTokens(), 1);

// register derivative with license tokens
Expand Down Expand Up @@ -177,7 +176,6 @@ contract LicensingIntegrationTest is BaseIntegration {
assertEq(licenseToken.getLicenseTermsId(lcTokenId), 2);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipAcct[1]);
assertEq(licenseToken.getExpirationTime(lcTokenId), 0);
assertEq(licenseToken.totalMintedTokens(), 2);
assertEq(erc20.balanceOf(u.dan), 900);

Expand Down
77 changes: 0 additions & 77 deletions test/foundry/modules/licensing/LicensingModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(lcTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1);
assertEq(licenseToken.getExpirationTime(lcTokenId), block.timestamp + 10 days);
assertEq(licenseToken.totalMintedTokens(), 1);
assertEq(licenseToken.totalSupply(), 1);
assertEq(licenseToken.balanceOf(ipOwner2), 1);
Expand Down Expand Up @@ -336,7 +335,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(lcTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1);
assertEq(licenseToken.getExpirationTime(lcTokenId), 0);
assertEq(licenseToken.tokenOfOwnerByIndex(receiver, 0), lcTokenId);
assertEq(licenseToken.totalMintedTokens(), 1);
assertEq(licenseToken.totalSupply(), 1);
Expand All @@ -363,7 +361,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(firstTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(firstTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(firstTokenId), ipId1);
assertEq(licenseToken.getExpirationTime(firstTokenId), 0);
assertEq(licenseToken.tokenOfOwnerByIndex(receiver, 0), firstTokenId);

uint256 secondTokenId = firstTokenId + 1;
Expand All @@ -372,7 +369,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(secondTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(secondTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(secondTokenId), ipId1);
assertEq(licenseToken.getExpirationTime(secondTokenId), 0);
assertEq(licenseToken.tokenOfOwnerByIndex(receiver, 1), secondTokenId);
assertEq(licenseToken.totalMintedTokens(), 2);
assertEq(licenseToken.totalSupply(), 2);
Expand Down Expand Up @@ -408,7 +404,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(tokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(tokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(tokenId), ipId1);
assertEq(licenseToken.getExpirationTime(tokenId), 0);
assertEq(licenseToken.tokenOfOwnerByIndex(receiver, i), tokenId);
assertEq(licenseToken.totalMintedTokens(), i + 1);
assertEq(licenseToken.totalSupply(), i + 1);
Expand Down Expand Up @@ -455,7 +450,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(lcTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1);
assertEq(licenseToken.getExpirationTime(lcTokenId), block.timestamp + 10 days);
assertEq(licenseToken.totalMintedTokens(), 1);
assertEq(licenseToken.totalSupply(), 1);
assertEq(licenseToken.balanceOf(ipOwner2), 1);
Expand Down Expand Up @@ -562,7 +556,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(lcTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1);
assertEq(licenseToken.getExpirationTime(lcTokenId), 0);
assertEq(licenseToken.tokenOfOwnerByIndex(receiver, 0), lcTokenId);
assertEq(licenseToken.totalMintedTokens(), 1);
assertEq(licenseToken.totalSupply(), 1);
Expand All @@ -586,7 +579,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(lcTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1);
assertEq(licenseToken.getExpirationTime(lcTokenId), 0);
assertEq(licenseToken.totalMintedTokens(), 1);
assertEq(licenseToken.totalSupply(), 1);
assertEq(licenseToken.balanceOf(ipOwner2), 1);
Expand Down Expand Up @@ -670,13 +662,11 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(lcTokenId1), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId1), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId1), ipId1);
assertEq(licenseToken.getExpirationTime(lcTokenId1), 0);

assertEq(licenseToken.ownerOf(lcTokenId2), ipOwner3);
assertEq(licenseToken.getLicenseTermsId(lcTokenId2), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId2), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId2), ipId2);
assertEq(licenseToken.getExpirationTime(lcTokenId2), 0);

assertEq(licenseToken.totalMintedTokens(), 2);
assertEq(licenseToken.totalSupply(), 2);
Expand Down Expand Up @@ -741,69 +731,6 @@ contract LicensingModuleTest is BaseTest {
licensingModule.registerDerivativeWithLicenseTokens(ipId1, licenseTokens, "");
}

function test_LicensingModule_registerDerivativeWithLicenseTokens_revert_ExpiredLicenseToken() public {
vm.prank(u.admin);
royaltyModule.whitelistRoyaltyToken(address(0x123), true);
PILTerms memory terms = PILTerms({
transferable: true,
royaltyPolicy: address(royaltyPolicyLAP),
mintingFee: 0,
expiration: 10 days,
commercialUse: true,
commercialAttribution: true,
commercializerChecker: address(0),
commercializerCheckerData: "",
commercialRevShare: 0,
commercialRevCelling: 0,
derivativesAllowed: true,
derivativesAttribution: true,
derivativesApproval: false,
derivativesReciprocal: true,
derivativeRevCelling: 0,
currency: address(0x123),
uri: ""
});

uint256 termsId = pilTemplate.registerLicenseTerms(terms);
vm.prank(ipOwner1);
licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), termsId);

uint256 lcTokenId = licensingModule.mintLicenseTokens({
licensorIpId: ipId1,
licenseTemplate: address(pilTemplate),
licenseTermsId: termsId,
amount: 1,
receiver: ipOwner2,
royaltyContext: ""
});

uint256 lcTokenExpiredTime = licenseToken.getExpirationTime(lcTokenId);
assertEq(licenseToken.ownerOf(lcTokenId), ipOwner2);
assertEq(licenseToken.getLicenseTermsId(lcTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1);
assertEq(lcTokenExpiredTime, block.timestamp + 10 days);
assertEq(licenseToken.totalMintedTokens(), 1);
assertEq(licenseToken.totalSupply(), 1);
assertEq(licenseToken.balanceOf(ipOwner2), 1);

vm.warp(11 days);

uint256[] memory licenseTokens = new uint256[](1);
licenseTokens[0] = lcTokenId;

vm.expectRevert(
abi.encodeWithSelector(
Errors.LicenseToken__LicenseTokenExpired.selector,
lcTokenId,
lcTokenExpiredTime,
block.timestamp
)
);
vm.prank(ipOwner2);
licensingModule.registerDerivativeWithLicenseTokens(ipId2, licenseTokens, "");
}

function test_LicensingModule_registerDerivativeWithLicenseTokens_revert_ParentExpired() public {
uint256 termsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());
PILTerms memory expiredTerms = PILFlavors.nonCommercialSocialRemixing();
Expand Down Expand Up @@ -837,13 +764,11 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(lcTokenId1), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId1), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId1), ipId1);
assertEq(licenseToken.getExpirationTime(lcTokenId1), 0);

assertEq(licenseToken.ownerOf(lcTokenId2), ipOwner3);
assertEq(licenseToken.getLicenseTermsId(lcTokenId2), expiredTermsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId2), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId2), ipId2);
assertEq(licenseToken.getExpirationTime(lcTokenId2), block.timestamp + 10 days);

assertEq(licenseToken.totalMintedTokens(), 2);
assertEq(licenseToken.totalSupply(), 2);
Expand Down Expand Up @@ -1081,7 +1006,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(lcTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1);
assertEq(licenseToken.getExpirationTime(lcTokenId), 0);
assertEq(licenseToken.totalMintedTokens(), 1);
assertEq(licenseToken.totalSupply(), 1);
assertEq(licenseToken.balanceOf(ipOwner2), 1);
Expand Down Expand Up @@ -1177,7 +1101,6 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseToken.getLicenseTermsId(lcTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1);
assertEq(licenseToken.getExpirationTime(lcTokenId), 0);
assertEq(licenseToken.totalMintedTokens(), 1);
assertEq(licenseToken.totalSupply(), 1);
assertEq(licenseToken.balanceOf(ipOwner2), 1);
Expand Down
Loading