This section demonstrates how to mint a License Token from an IP Asset. You can only mint a License Token from an IP Asset if the IP Asset has License Terms attached to it. A License Token is minted as an ERC-721.
There are two reasons you’d mint a License Token:
To hold the license and be able to use the underlying IP Asset as the license described (for ex. “Can use commercially as long as you provide proper attribution and share 5% of your revenue)
Use the license token to link another IP Asset as a derivative of it. Note though that, as you’ll see later, some SDK functions don’t require you to explicitly mint a license token first in order to register a derivative, and will actually handle it for you behind the scenes.
Let’s say that IP Asset (ipId = 0x01) has License Terms (licenseTermdId = 10) attached to it. We want to mint 2 License Tokens with those terms to a specific wallet address (0x02).
Paid Licenses
Be mindful that some IP Assets may have license terms attached that require the user minting the license to pay a mintingFee.
Let’s create a test file under test/3_LicenseToken.t.sol to see it work and verify the results:
Contract Addresses
We have filled in the addresses from the Story contracts for you. However you can also find the addresses for them here: Deployed Smart Contracts
test/3_LicenseToken.t.sol
Copy
// SPDX-License-Identifier: UNLICENSEDpragma solidity ^0.8.26;import { Test } from "forge-std/Test.sol";// for testing purposes onlyimport { MockIPGraph } from "@storyprotocol/test/mocks/MockIPGraph.sol";import { IIPAssetRegistry } from "@storyprotocol/core/interfaces/registries/IIPAssetRegistry.sol";import { IPILicenseTemplate } from "@storyprotocol/core/interfaces/modules/licensing/IPILicenseTemplate.sol";import { ILicensingModule } from "@storyprotocol/core/interfaces/modules/licensing/ILicensingModule.sol";import { ILicenseToken } from "@storyprotocol/core/interfaces/ILicenseToken.sol";import { RoyaltyPolicyLAP } from "@storyprotocol/core/modules/royalty/policies/LAP/RoyaltyPolicyLAP.sol";import { PILFlavors } from "@storyprotocol/core/lib/PILFlavors.sol";import { PILTerms } from "@storyprotocol/core/interfaces/modules/licensing/IPILicenseTemplate.sol";import { SimpleNFT } from "../src/mocks/SimpleNFT.sol";// Run this test:// forge test --fork-url https://aeneid.storyrpc.io/ --match-path test/3_LicenseToken.t.solcontract LicenseTokenTest is Test { address internal alice = address(0xa11ce); address internal bob = address(0xb0b); // For addresses, see https://docs.story.foundation/developers/deployed-smart-contracts // Protocol Core - IPAssetRegistry IIPAssetRegistry internal IP_ASSET_REGISTRY = IIPAssetRegistry(0x77319B4031e6eF1250907aa00018B8B1c67a244b); // Protocol Core - LicensingModule ILicensingModule internal LICENSING_MODULE = ILicensingModule(0x04fbd8a2e56dd85CFD5500A4A4DfA955B9f1dE6f); // Protocol Core - PILicenseTemplate IPILicenseTemplate internal PIL_TEMPLATE = IPILicenseTemplate(0x2E896b0b2Fdb7457499B56AAaA4AE55BCB4Cd316); // Protocol Core - RoyaltyPolicyLAP address internal ROYALTY_POLICY_LAP = 0xBe54FB168b3c982b7AaE60dB6CF75Bd8447b390E; // Protocol Core - LicenseToken ILicenseToken internal LICENSE_TOKEN = ILicenseToken(0xFe3838BFb30B34170F00030B52eA4893d8aAC6bC); // Revenue Token - MERC20 address internal MERC20 = 0xF2104833d386a2734a4eB3B8ad6FC6812F29E38E; SimpleNFT public SIMPLE_NFT; uint256 public tokenId; address public ipId; uint256 public licenseTermsId; function setUp() public { // this is only for testing purposes // due to our IPGraph precompile not being // deployed on the fork vm.etch(address(0x0101), address(new MockIPGraph()).code); SIMPLE_NFT = new SimpleNFT("Simple IP NFT", "SIM"); tokenId = SIMPLE_NFT.mint(alice); ipId = IP_ASSET_REGISTRY.register(block.chainid, address(SIMPLE_NFT), tokenId); licenseTermsId = PIL_TEMPLATE.registerLicenseTerms( PILFlavors.commercialRemix({ mintingFee: 0, commercialRevShare: 10 * 10 ** 6, // 10% royaltyPolicy: ROYALTY_POLICY_LAP, currencyToken: MERC20 }) ); vm.prank(alice); LICENSING_MODULE.attachLicenseTerms(ipId, address(PIL_TEMPLATE), licenseTermsId); } /// @notice Mints license tokens for an IP Asset. /// Anyone can mint a license token. function test_mintLicenseToken() public { uint256 startLicenseTokenId = LICENSING_MODULE.mintLicenseTokens({ licensorIpId: ipId, licenseTemplate: address(PIL_TEMPLATE), licenseTermsId: licenseTermsId, amount: 2, receiver: bob, royaltyContext: "", // for PIL, royaltyContext is empty string maxMintingFee: 0, maxRevenueShare: 0 }); assertEq(LICENSE_TOKEN.ownerOf(startLicenseTokenId), bob); assertEq(LICENSE_TOKEN.ownerOf(startLicenseTokenId + 1), bob); }}