@@ -13,9 +13,17 @@ import { IAccessController } from "@storyprotocol/core/interfaces/access/IAccess
13
13
// solhint-disable-next-line max-line-length
14
14
import { IPILicenseTemplate, PILTerms } from "@storyprotocol/core/interfaces/modules/licensing/IPILicenseTemplate.sol " ;
15
15
import { ILicensingModule } from "@storyprotocol/core/interfaces/modules/licensing/ILicensingModule.sol " ;
16
+ import { ILicenseTemplate } from "@storyprotocol/core/interfaces/modules/licensing/ILicenseTemplate.sol " ;
17
+ import { ILicenseRegistry } from "@storyprotocol/core/interfaces/registries/ILicenseRegistry.sol " ;
18
+ import { ILicensingHook } from "@storyprotocol/core/interfaces/modules/licensing/ILicensingHook.sol " ;
19
+ import { Licensing } from "@storyprotocol/core/lib/Licensing.sol " ;
20
+ import { IRoyaltyModule } from "@storyprotocol/core/interfaces/modules/royalty/IRoyaltyModule.sol " ;
21
+
16
22
import { ICoreMetadataModule } from "@storyprotocol/core/interfaces/modules/metadata/ICoreMetadataModule.sol " ;
17
23
import { IIPAssetRegistry } from "@storyprotocol/core/interfaces/registries/IIPAssetRegistry.sol " ;
18
24
import { AccessPermission } from "@storyprotocol/core/lib/AccessPermission.sol " ;
25
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
26
+ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol " ;
19
27
20
28
import { IStoryProtocolGateway } from "./interfaces/IStoryProtocolGateway.sol " ;
21
29
import { ISPGNFT } from "./interfaces/ISPGNFT.sol " ;
@@ -24,6 +32,7 @@ import { SPGNFTLib } from "./lib/SPGNFTLib.sol";
24
32
25
33
contract StoryProtocolGateway is IStoryProtocolGateway , AccessManagedUpgradeable , UUPSUpgradeable {
26
34
using ERC165Checker for address ;
35
+ using SafeERC20 for IERC20 ;
27
36
28
37
/// @dev Storage structure for the SPG
29
38
/// @param nftContractBeacon The address of the NFT contract beacon.
@@ -44,6 +53,12 @@ contract StoryProtocolGateway is IStoryProtocolGateway, AccessManagedUpgradeable
44
53
/// @notice The address of the Licensing Module.
45
54
ILicensingModule public immutable LICENSING_MODULE;
46
55
56
+ /// @notice The address of the License Registry.
57
+ ILicenseRegistry public immutable LICENSE_REGISTRY;
58
+
59
+ /// @notice The address of the Royalty Module.
60
+ IRoyaltyModule public immutable ROYALTY_MODULE;
61
+
47
62
/// @notice The address of the Core Metadata Module.
48
63
ICoreMetadataModule public immutable CORE_METADATA_MODULE;
49
64
@@ -65,6 +80,8 @@ contract StoryProtocolGateway is IStoryProtocolGateway, AccessManagedUpgradeable
65
80
address accessController ,
66
81
address ipAssetRegistry ,
67
82
address licensingModule ,
83
+ address licenseRegistry ,
84
+ address royaltyModule ,
68
85
address coreMetadataModule ,
69
86
address pilTemplate ,
70
87
address licenseToken
@@ -73,13 +90,17 @@ contract StoryProtocolGateway is IStoryProtocolGateway, AccessManagedUpgradeable
73
90
accessController == address (0 ) ||
74
91
ipAssetRegistry == address (0 ) ||
75
92
licensingModule == address (0 ) ||
93
+ licenseRegistry == address (0 ) ||
94
+ royaltyModule == address (0 ) ||
76
95
coreMetadataModule == address (0 ) ||
77
96
licenseToken == address (0 )
78
97
) revert Errors.SPG__ZeroAddressParam ();
79
98
80
99
ACCESS_CONTROLLER = IAccessController (accessController);
81
100
IP_ASSET_REGISTRY = IIPAssetRegistry (ipAssetRegistry);
82
101
LICENSING_MODULE = ILicensingModule (licensingModule);
102
+ LICENSE_REGISTRY = ILicenseRegistry (licenseRegistry);
103
+ ROYALTY_MODULE = IRoyaltyModule (royaltyModule);
83
104
CORE_METADATA_MODULE = ICoreMetadataModule (coreMetadataModule);
84
105
PIL_TEMPLATE = IPILicenseTemplate (pilTemplate);
85
106
LICENSE_TOKEN = ILicenseToken (licenseToken);
@@ -254,6 +275,14 @@ contract StoryProtocolGateway is IStoryProtocolGateway, AccessManagedUpgradeable
254
275
ipId = IP_ASSET_REGISTRY.register (block .chainid , nftContract, tokenId);
255
276
_setMetadata (ipMetadata, ipId);
256
277
278
+ _collectMintFeesAndSetApproval (
279
+ msg .sender ,
280
+ ipId,
281
+ derivData.parentIpIds,
282
+ derivData.licenseTemplate,
283
+ derivData.licenseTermsIds
284
+ );
285
+
257
286
LICENSING_MODULE.registerDerivative ({
258
287
childIpId: ipId,
259
288
parentIpIds: derivData.parentIpIds,
@@ -289,6 +318,15 @@ contract StoryProtocolGateway is IStoryProtocolGateway, AccessManagedUpgradeable
289
318
address (LICENSING_MODULE),
290
319
ILicensingModule.registerDerivative.selector
291
320
);
321
+
322
+ _collectMintFeesAndSetApproval (
323
+ msg .sender ,
324
+ ipId,
325
+ derivData.parentIpIds,
326
+ derivData.licenseTemplate,
327
+ derivData.licenseTermsIds
328
+ );
329
+
292
330
LICENSING_MODULE.registerDerivative ({
293
331
childIpId: ipId,
294
332
parentIpIds: derivData.parentIpIds,
@@ -441,6 +479,109 @@ contract StoryProtocolGateway is IStoryProtocolGateway, AccessManagedUpgradeable
441
479
_setMetadata (ipMetadata, ipId);
442
480
}
443
481
482
+ /// @dev Collect mint fees for all parent IPs from the payer and set approval for Royalty Module to spend mint fees.
483
+ /// @param payerAddress The address of the payer for the license mint fees.
484
+ /// @param childIpId The ID of the derivative IP.
485
+ /// @param parentIpIds The IDs of all the parent IPs.
486
+ /// @param licenseTemplate The address of the license template.
487
+ /// @param licenseTermsIds The IDs of the license terms for each corresponding parent IP.
488
+ function _collectMintFeesAndSetApproval (
489
+ address payerAddress ,
490
+ address childIpId ,
491
+ address [] calldata parentIpIds ,
492
+ address licenseTemplate ,
493
+ uint256 [] calldata licenseTermsIds
494
+ ) private {
495
+ // Get currency token and royalty policy, assumes all parent IPs have the same currency token.
496
+ ILicenseTemplate lct = ILicenseTemplate (licenseTemplate);
497
+ (address royaltyPolicy , , , address mintFeeCurrencyToken ) = lct.getRoyaltyPolicy (licenseTermsIds[0 ]);
498
+
499
+ if (royaltyPolicy != address (0 )) {
500
+ // Get total mint fee for all parent IPs
501
+ uint256 totalMintFee = _aggregateMintFees (parentIpIds, childIpId, licenseTemplate, licenseTermsIds);
502
+
503
+ if (totalMintFee != 0 ) {
504
+ // Transfer mint fee from payer to this contract
505
+ IERC20 (mintFeeCurrencyToken).safeTransferFrom (payerAddress, address (this ), totalMintFee);
506
+
507
+ // Approve Royalty Policy to spend mint fee
508
+ IERC20 (mintFeeCurrencyToken).forceApprove (royaltyPolicy, totalMintFee);
509
+ }
510
+ }
511
+ }
512
+
513
+ /// @dev Aggregate license mint fees for all parent IPs.
514
+ /// @param parentIpIds The IDs of all the parent IPs.
515
+ /// @param childIpId The ID of the derivative IP.
516
+ /// @param licenseTemplate The address of the license template.
517
+ /// @param licenseTermsIds The IDs of the license terms for each corresponding parent IP.
518
+ /// @return totalMintFee The sum of license mint fees across all parent IPs.
519
+ function _aggregateMintFees (
520
+ address [] calldata parentIpIds ,
521
+ address childIpId ,
522
+ address licenseTemplate ,
523
+ uint256 [] calldata licenseTermsIds
524
+ ) private returns (uint256 totalMintFee ) {
525
+ totalMintFee = 0 ;
526
+
527
+ for (uint256 i = 0 ; i < parentIpIds.length ; i++ ) {
528
+ totalMintFee += _getMintFeeForSingleParent (
529
+ childIpId,
530
+ parentIpIds[i],
531
+ licenseTemplate,
532
+ licenseTermsIds[i],
533
+ 1
534
+ );
535
+ }
536
+ }
537
+
538
+ /// @dev Fetch the license token mint fee from the licensing hook or license terms for the given parent IP.
539
+ /// @param childIpId The ID of the derivative IP.
540
+ /// @param parentIpId The ID of the parent IP.
541
+ /// @param licenseTemplate The address of the license template.
542
+ /// @param licenseTermsId The ID of the license terms for the parent IP.
543
+ /// @param amount The amount of licenses to mint.
544
+ function _getMintFeeForSingleParent (
545
+ address childIpId ,
546
+ address parentIpId ,
547
+ address licenseTemplate ,
548
+ uint256 licenseTermsId ,
549
+ uint256 amount
550
+ ) private returns (uint256 ) {
551
+ ILicenseTemplate lct = ILicenseTemplate (licenseTemplate);
552
+
553
+ // Get mint fee set by license terms
554
+ (address royaltyPolicy , , uint256 mintFeeSetByLicenseTerms , ) = lct.getRoyaltyPolicy (licenseTermsId);
555
+
556
+ // If no royalty policy, return 0
557
+ if (royaltyPolicy == address (0 )) return 0 ;
558
+
559
+ uint256 mintFeeSetByHook = 0 ;
560
+
561
+ Licensing.LicensingConfig memory licensingConfig = LICENSE_REGISTRY.getLicensingConfig (
562
+ parentIpId,
563
+ licenseTemplate,
564
+ licenseTermsId
565
+ );
566
+
567
+ // Get mint fee from licensing hook
568
+ if (licensingConfig.licensingHook != address (0 )) {
569
+ mintFeeSetByHook = ILicensingHook (licensingConfig.licensingHook).beforeRegisterDerivative (
570
+ address (this ),
571
+ childIpId,
572
+ parentIpId,
573
+ licenseTemplate,
574
+ licenseTermsId,
575
+ licensingConfig.hookData
576
+ );
577
+ }
578
+
579
+ if (! licensingConfig.isSet) return mintFeeSetByLicenseTerms * amount;
580
+ if (licensingConfig.licensingHook == address (0 )) return licensingConfig.mintingFee * amount;
581
+
582
+ return mintFeeSetByHook;
583
+ }
584
+
444
585
//
445
586
// Upgrade
446
587
//
0 commit comments