Use this file to discover all available pages before exploring further.
Completed Code
Follow the completed code all the way through.
This section demonstrates how to attach License Terms to an IP Asset. By attaching terms, users can publicly mint License Tokens (the on-chain “license”) with those terms from the IP.
Now that we have created terms and have the associated licenseTermsId, we can attach them to an existing IP Asset.Let’s create a test file under test/2_AttachTerms.t.sol to see it work and verify the results:
Contract AddressesWe 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/2_AttachTerms.t.sol
// 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 { ILicenseRegistry } from "@storyprotocol/core/interfaces/registries/ILicenseRegistry.sol";import { IPILicenseTemplate } from "@storyprotocol/core/interfaces/modules/licensing/IPILicenseTemplate.sol";import { ILicensingModule } from "@storyprotocol/core/interfaces/modules/licensing/ILicensingModule.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/2_AttachTerms.t.solcontract AttachTermsTest is Test { address internal alice = address(0xa11ce); // For addresses, see https://docs.story.foundation/developers/deployed-smart-contracts // Protocol Core - IPAssetRegistry IIPAssetRegistry internal IP_ASSET_REGISTRY = IIPAssetRegistry(0x77319B4031e6eF1250907aa00018B8B1c67a244b); // Protocol Core - LicenseRegistry ILicenseRegistry internal LICENSE_REGISTRY = ILicenseRegistry(0x529a750E02d8E2f15649c13D69a465286a780e24); // 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; // 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); // Register random Commercial Remix terms so we can attach them later licenseTermsId = PIL_TEMPLATE.registerLicenseTerms( PILFlavors.commercialRemix({ mintingFee: 0, commercialRevShare: 10 * 10 ** 6, // 10% royaltyPolicy: ROYALTY_POLICY_LAP, currencyToken: MERC20 }) ); } /// @notice Attaches license terms to an IP Asset. /// @dev Only the owner of an IP Asset can attach license terms to it. /// So in this case, alice has to be the caller of the function because /// she owns the NFT associated with the IP Asset. function test_attachLicenseTerms() public { vm.prank(alice); LICENSING_MODULE.attachLicenseTerms(ipId, address(PIL_TEMPLATE), licenseTermsId); assertTrue(LICENSE_REGISTRY.hasIpAttachedLicenseTerms(ipId, address(PIL_TEMPLATE), licenseTermsId)); assertEq(LICENSE_REGISTRY.getAttachedLicenseTermsCount(ipId), 1); (address licenseTemplate, uint256 attachedLicenseTermsId) = LICENSE_REGISTRY.getAttachedLicenseTerms({ ipId: ipId, index: 0 }); assertEq(licenseTemplate, address(PIL_TEMPLATE)); assertEq(attachedLicenseTermsId, licenseTermsId); }}