From 6c2000bf3e448fb4b4f3a14ed7f4887c4eb14645 Mon Sep 17 00:00:00 2001 From: Raul Date: Fri, 11 Aug 2023 14:15:59 -0700 Subject: [PATCH 01/42] first commit for licensing --- .../modules/licensing/LicensingModule.sol | 52 +++++++++++++++++++ .../modules/timing/LibTimeConditional.sol | 14 +++++ 2 files changed, 66 insertions(+) create mode 100644 contracts/modules/licensing/LicensingModule.sol create mode 100644 contracts/modules/timing/LibTimeConditional.sol diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol new file mode 100644 index 00000000..be7bcb1a --- /dev/null +++ b/contracts/modules/licensing/LicensingModule.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.13; +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { LibTimeConditional } from "../timing/LibTimeConditional.sol"; + +contract LicensingModule { + + struct Token { + IERC721 collection; + uint256 tokenId; + } + + struct License { + bool active; + + Token licensedToken; // If this is not null, the license is non-transferable and the holder is this token. + + uint256 parentLicenseId; + address licenseHolder; // If transferrable, this is the ERC721 owner + bool tranferable; // If yes, assign an owner through ERC721 + bool transferableByParentOnly; // If yes, only the parent license can transfer the license + + bool exclusive; + bool canSublicense; // TODO: consider sublicensing limitations (i.e can only sublicense for media X, etc.) + bool commercial; + + // Contract defining the payment terms, royalties, milestones, etc. + // The interpreter does not necessarily enforce the terms. + address paymentTermsInterpreter; + bytes paymentTermsData; + + bytes32 mediaId; // ID of the media Type, e.q keccak256("MOVIE"), keccak256("YOUTUBE")... Should this be platform? + + // Intermediate steps before granting, such as approval by parent, KYC... Triggered in beforeTransfer. + address grantProcessor; + bytes grantData; + + // Terms for revoking the license, not temporal, for example Moral Rights, Can't be Evil, etc. Useful for disputes + address revocationTermsInterpreter; + bytes revocationTermsData; + address revoker; + + // Temporal terms and address that could renew the license + LibTimeConditional.TimeConfig timeConfig; + + string termsURI; + + } + + mapping(uint256 => License) private _licenses; + +} \ No newline at end of file diff --git a/contracts/modules/timing/LibTimeConditional.sol b/contracts/modules/timing/LibTimeConditional.sol new file mode 100644 index 00000000..02bbfaad --- /dev/null +++ b/contracts/modules/timing/LibTimeConditional.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.13; + +library LibTimeConditional { + + struct TimeConfig { + uint112 maxTTL; + uint112 minTTL; + bool renewable; + address renewer; + uint256 endTime; + } + +} \ No newline at end of file From f7900b19012fc13743d27058c79320a55728fc5c Mon Sep 17 00:00:00 2001 From: Raul Date: Fri, 11 Aug 2023 17:58:40 -0700 Subject: [PATCH 02/42] WIP --- contracts/FranchiseRegistry.sol | 31 ++- contracts/ip-assets/IPAssetRegistry.sol | 20 +- .../storage/IIPAssetDataManager.sol | 3 +- .../storage/IPAssetDataManager.sol | 8 +- .../modules/licensing/LicensingModule.sol | 209 +++++++++++++++--- 5 files changed, 236 insertions(+), 35 deletions(-) diff --git a/contracts/FranchiseRegistry.sol b/contracts/FranchiseRegistry.sol index 03a8bace..f94ba64b 100644 --- a/contracts/FranchiseRegistry.sol +++ b/contracts/FranchiseRegistry.sol @@ -11,6 +11,7 @@ import { IIPAssetRegistry } from "./ip-assets/IIPAssetRegistry.sol"; import { LibIPAssetId } from "./ip-assets/LibIPAssetId.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import { LicensingModule } from "./modules/licensing/LicensingModule.sol"; contract FranchiseRegistry is UUPSUpgradeable, @@ -41,6 +42,7 @@ contract FranchiseRegistry is /// Franchise id => IPAssetRegistry address mapping(uint256 => address) ipAssetRegistries; mapping(uint256 => string) tokenURIs; + LicensingModule licensingModule; } IPAssetRegistryFactory public immutable FACTORY; @@ -49,7 +51,6 @@ contract FranchiseRegistry is uint256 public constant PROTOCOL_ROOT_ID = 0; address public constant PROTOCOL_ROOT_ADDRESS = address(0); string private constant _VERSION = "0.1.0"; - constructor(address _factory) { if (_factory == address(0)) revert ZeroAddress(); @@ -63,6 +64,12 @@ contract FranchiseRegistry is __ERC721_init("Story Protocol", "SP"); } + function setLicensingModule(LicensingModule module) external{ + // TODO: set protocol role for this and check sender + if (address(module) == address(0)) revert ZeroAddress(); + _getFranchiseStorage().licensingModule = module; + } + function _getFranchiseStorage() private pure returns (FranchiseStorage storage $) { assembly { $.slot := _STORAGE_LOCATION @@ -85,6 +92,7 @@ contract FranchiseRegistry is $.ipAssetRegistries[nextId] = ipAssetRegistry; $.tokenURIs[nextId] = params.tokenURI; _safeMint(msg.sender, nextId); + // TODO: set licensing restrictions per franchise, maybe grant commercial root license to the franchise NFT emit FranchiseRegistered(msg.sender, nextId, ipAssetRegistry, params.name, params.symbol, params.tokenURI); @@ -98,6 +106,7 @@ contract FranchiseRegistry is return $.ipAssetRegistries[franchiseId]; } + /** * @notice checks if an address is a valid SP IPAssetRegistry. * @param ipAssetRegistry the address to check @@ -111,6 +120,26 @@ contract FranchiseRegistry is } } + function createIPAsset( + uint256 franchiseId, + IPAsset sb, + string calldata name, + string calldata _description, + string calldata mediaUrl + ) external returns (uint256 ipAssetID) { + FranchiseStorage storage $ = _getFranchiseStorage(); + if (msg.sender != $.ipAssetRegistries[franchiseId]) revert Unauthorized(); + ipAssetID = IIPAssetRegistry($.ipAssetRegistries[franchiseId]).createIPAsset(sb, name, _description, mediaUrl, msg.sender); + /// TODO: grant registry + /* + $.licensingModule.grantLicense( + + ); + */ + return ipAssetID; + } + + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { _requireMinted(tokenId); FranchiseStorage storage $ = _getFranchiseStorage(); diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index 7a8cf471..425a2819 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -9,6 +9,7 @@ import { GroupDAM } from "./data-access-modules/group/GroupDAM.sol"; import { IIPAssetEventEmitter } from "./events/IIPAssetEventEmitter.sol"; import { IIPAssetDataManager } from "./data-access-modules/storage/IIPAssetDataManager.sol"; import { IPAssetDataManager } from "./data-access-modules/storage/IPAssetDataManager.sol"; +import { FranchiseRegistry } from "../FranchiseRegistry.sol"; import { ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; @@ -32,16 +33,25 @@ contract IPAssetRegistry is } IIPAssetEventEmitter public immutable EVENT_EMITTER; + FranchiseRegistry public immutable FRANCHISE_REGISTRY; // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.ip-assets-registry.storage")) - 1))) bytes32 private constant _STORAGE_LOCATION = 0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378; string private constant _VERSION = "0.1.0"; - constructor(address _eventEmitter) { + constructor(address _eventEmitter, address _franchiseRegistry) { if (_eventEmitter == address(0)) revert ZeroAddress(); EVENT_EMITTER = IIPAssetEventEmitter(_eventEmitter); + if (_franchiseRegistry == address(0)) revert ZeroAddress(); + FRANCHISE_REGISTRY = FranchiseRegistry(_franchiseRegistry); _disableInitializers(); } + modifier onlyFranchiseRegistry() { + // TODO: extract to FranchiseRegistryControlled.sol + if (msg.sender != address(FRANCHISE_REGISTRY)) revert("Sender is not the franchise registry"); + _; + } + function initialize( uint256 _franchiseId, string calldata _name, @@ -74,15 +84,15 @@ contract IPAssetRegistry is IPAsset sb, string calldata name, string calldata _description, - string calldata mediaUrl - ) public virtual override(IIPAssetDataManager, IPAssetDataManager) returns (uint256) { - uint256 sbId = _createIPAsset(sb, name, _description, mediaUrl); + string calldata mediaUrl, + address to + ) public virtual onlyFranchiseRegistry override(IIPAssetDataManager, IPAssetDataManager) returns (uint256) { + uint256 sbId = _createIPAsset(sb, name, _description, mediaUrl, to); IPAssetRegistryStorage storage $ = _getIPAssetRegistryStorage(); EVENT_EMITTER.emitIPAssetCreation($.franchiseId, sbId); return sbId; } - function _mintBlock(address to, IPAsset sb) internal override returns (uint256) { uint256 nextId = currentIdFor(sb) + 1; if (nextId > LibIPAssetId._lastId(sb)) revert IdOverBounds(); diff --git a/contracts/ip-assets/data-access-modules/storage/IIPAssetDataManager.sol b/contracts/ip-assets/data-access-modules/storage/IIPAssetDataManager.sol index bbb4dca4..2e550d5a 100644 --- a/contracts/ip-assets/data-access-modules/storage/IIPAssetDataManager.sol +++ b/contracts/ip-assets/data-access-modules/storage/IIPAssetDataManager.sol @@ -26,7 +26,8 @@ interface IIPAssetDataManager { IPAsset sb, string calldata name, string calldata _description, - string calldata mediaUrl + string calldata mediaUrl, + address to ) external returns (uint256); } \ No newline at end of file diff --git a/contracts/ip-assets/data-access-modules/storage/IPAssetDataManager.sol b/contracts/ip-assets/data-access-modules/storage/IPAssetDataManager.sol index b4d8bdb2..783925e0 100644 --- a/contracts/ip-assets/data-access-modules/storage/IPAssetDataManager.sol +++ b/contracts/ip-assets/data-access-modules/storage/IPAssetDataManager.sol @@ -31,17 +31,19 @@ abstract contract IPAssetDataManager is Initializable, IIPAssetDataManager { IPAsset sb, string calldata name, string calldata _description, - string calldata mediaUrl + string calldata mediaUrl, + address to ) public virtual returns (uint256); function _createIPAsset( IPAsset sb, string calldata name, string calldata _description, - string calldata mediaUrl + string calldata mediaUrl, + address to ) internal returns (uint256) { if (sb == IPAsset.UNDEFINED) revert InvalidBlockType(); - uint256 sbId = _mintBlock(msg.sender, sb); + uint256 sbId = _mintBlock(to, sb); _writeIPAsset(sbId, name, _description, mediaUrl); return sbId; } diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index be7bcb1a..fa9f4fc7 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -1,52 +1,211 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.13; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { LibTimeConditional } from "../timing/LibTimeConditional.sol"; -contract LicensingModule { +contract LicensingModule is ERC721 { + + event LicenseGranted ( + uint256 indexed licenseId, + address holder, + address token, + uint256 tokenId, + uint256 parentLicenseId + ); struct Token { IERC721 collection; uint256 tokenId; } - struct License { - bool active; + struct GeneralTerms { + bool exclusive;// Ignored now, TODO + bool canSublicense; // TODO: consider sublicensing limitations (i.e can only sublicense for media X, etc.) + bool commercial; + } + + struct PaymentTerms { + address interpreter; + bytes data; + } + + struct GrantingTerms { + address grantProcessor; + bytes data; + } - Token licensedToken; // If this is not null, the license is non-transferable and the holder is this token. + struct OwnershipParams { + address holder; + Token token; + } + struct License { + bool active; uint256 parentLicenseId; - address licenseHolder; // If transferrable, this is the ERC721 owner - bool tranferable; // If yes, assign an owner through ERC721 - bool transferableByParentOnly; // If yes, only the parent license can transfer the license + bytes32 mediaId; // ID of the media Type, e.q keccak256("MOVIE"), keccak256("MOVIE"), keccak256("MERCHANDISE")... Should this be "platform"? + GeneralTerms generalTerms; - bool exclusive; - bool canSublicense; // TODO: consider sublicensing limitations (i.e can only sublicense for media X, etc.) - bool commercial; + // If licensed token is set, the license is bound to a token, so ownerOf must return 0, and the license is not transferrable + Token licensedToken; // Contract defining the payment terms, royalties, milestones, etc. // The interpreter does not necessarily enforce the terms. - address paymentTermsInterpreter; - bytes paymentTermsData; - - bytes32 mediaId; // ID of the media Type, e.q keccak256("MOVIE"), keccak256("YOUTUBE")... Should this be platform? + PaymentTerms paymentTerms; - // Intermediate steps before granting, such as approval by parent, KYC... Triggered in beforeTransfer. - address grantProcessor; - bytes grantData; - - // Terms for revoking the license, not temporal, for example Moral Rights, Can't be Evil, etc. Useful for disputes - address revocationTermsInterpreter; - bytes revocationTermsData; + // Intermediate steps before granting, such as approval by parent, KYC, revenue splits are set, time limits for transfer, security stuff... + // Triggered in beforeTransfer. + GrantingTerms grantingTerms; address revoker; // Temporal terms and address that could renew the license - LibTimeConditional.TimeConfig timeConfig; - - string termsURI; - + LibTimeConditional.TimeConfig durationTerms; + string licenseURI; } mapping(uint256 => License) private _licenses; + uint256 private _licenseCounter; + // TODO + mapping(address => bool) _bannedMarketPlaces; + // Each franchise can choose to restrict stuff, like allowed license templates, the holder of root commercial licenses can only be + // the franchise owner and external PFP owners, etc. + mapping(uint256 => bytes) _franchiseRestrictions; + + string public nonCommercialLicenseURI; + address immutable public FRANCHISE_REGISTRY; + + modifier onlyFranchiseRegistry() { + // TODO: extract to FranchiseRegistryControlled.sol + if (msg.sender != FRANCHISE_REGISTRY) revert("Sender is not the franchise registry"); + _; + } + + constructor(string memory name, string memory symbol, string memory _nonCommercialLicenseURI, address franchiseRegistry) ERC721(name, symbol) { + nonCommercialLicenseURI = _nonCommercialLicenseURI; + if (franchiseRegistry == address(0)) revert("Franchise registry cannot be zero address"); + FRANCHISE_REGISTRY = franchiseRegistry; + } + + function isLicenseActive(uint256 licenseId) public view virtual returns (bool) { + // TODO: limit to the tree depth + // TODO: check time limits + if (licenseId == 0) return false; + while (licenseId != 0) { + if (!_licenses[licenseId].active) return false; + licenseId = _licenses[licenseId].parentLicenseId; + } + return true; + } + + function grantLicense( + address licensor, + uint256 parentLicenseId, + bytes32 mediaId, + GeneralTerms memory generalTerms, + OwnershipParams calldata ownershipParams, + PaymentTerms memory paymentTerms, + GrantingTerms memory grantingTerms, + LibTimeConditional.TimeConfig memory durationTerms, + string memory licenseURI, + address revoker + ) public onlyFranchiseRegistry returns (uint256) { + string memory _licenseURI = licenseURI; + if (parentLicenseId == 0) { + if (!generalTerms.commercial) { + _licenseURI = nonCommercialLicenseURI; + } + } else { + if (ownerOf(parentLicenseId) != licensor) revert("Sender is not the owner of the parent license"); + License memory parentLicense = _licenses[parentLicenseId]; + if (!parentLicense.active) revert("Parent license is not active"); + if (!parentLicense.generalTerms.canSublicense) revert("Parent license cannot be sublicensed"); + if (parentLicense.generalTerms.commercial != generalTerms.commercial) revert("Commercial terms must be the same as parent license"); + if (!generalTerms.commercial) { + _licenseURI = parentLicense.licenseURI; + } + } + // TODO: check other terms + uint256 licenseId = _emitLicense( + parentLicenseId, + mediaId, + generalTerms, + ownershipParams, + paymentTerms, + grantingTerms, + durationTerms, + _licenseURI, + revoker + ); + // Not bound to a token, mint to holder + if (ownershipParams.holder != address(0)) { + _mint(ownershipParams.holder, licenseId); + } + emit LicenseGranted( + licenseId, + ownershipParams.holder, + address(ownershipParams.token.collection), + ownershipParams.token.tokenId, + parentLicenseId + ); + return licenseId; + } + + function _isUnsetToken(Token memory token) private pure returns (bool) { + return address(token.collection) == address(0); + } + + function ownerOf(uint256 tokenId) public view virtual override returns (address) { + // TODO: This should work with ERC6665 or similar, this is only for demo + Token memory token = _licenses[tokenId].licensedToken; + if (address(token.collection) != address(0)) { + return token.collection.ownerOf(token.tokenId); + } + return super.ownerOf(tokenId); + } + + function _emitLicense( + uint256 parentLicenseId, + bytes32 mediaId, + GeneralTerms memory generalTerms, + OwnershipParams memory ownershipParams, + PaymentTerms memory paymentTerms, + GrantingTerms memory grantingTerms, + LibTimeConditional.TimeConfig memory durationTerms, + string memory licenseURI, + address revoker + ) internal returns (uint256) { + if (ownershipParams.holder == address(0) && _isUnsetToken(ownershipParams.token)) revert("License must be bound to a token or a license holder"); + if (ownershipParams.holder != address(0) && !_isUnsetToken(ownershipParams.token)) revert("License cannot be bound to a token and a license holder at the same time"); + // TODO: validate all terms + _licenseCounter++; + _licenses[_licenseCounter] = License({ + active: true, + parentLicenseId: parentLicenseId, + mediaId: mediaId, + generalTerms: generalTerms, + licensedToken: ownershipParams.token, + paymentTerms: paymentTerms, + grantingTerms: grantingTerms, + revoker: revoker, + durationTerms: durationTerms, + licenseURI: licenseURI + }); + return _licenseCounter; + } + + function getLicense(uint256 licenseId) public view returns (License memory, address holder) { + return (_licenses[licenseId], ownerOf(licenseId)); + } + + + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override { + // TODO: check granting terms, banned marketplaces, etc. + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + } } \ No newline at end of file From 99972f87d8bd24e7c28c1ecc29dfe0504a67ffd9 Mon Sep 17 00:00:00 2001 From: Raul Date: Sat, 12 Aug 2023 00:20:35 -0700 Subject: [PATCH 03/42] grant license refactor --- contracts/FranchiseRegistry.sol | 78 +++++++++++++++++-- .../data-access-modules/group/GroupDAM.sol | 4 +- .../data-access-modules/group/IGroupDAM.sol | 2 +- .../modules/licensing/LicensingModule.sol | 72 +++++++++-------- script/foundry/deployment/Main.s.sol | 2 +- script/foundry/upgrades/DevUpgrades.s.sol | 2 +- test/foundry/IPAssetsRegistry.t.sol | 31 ++++---- test/foundry/IPAssetsRegistryFactory.t.sol | 10 ++- .../RelationshipModule.Relating.t.sol | 23 +++--- test/foundry/utils/BaseTest.sol | 2 +- 10 files changed, 150 insertions(+), 76 deletions(-) diff --git a/contracts/FranchiseRegistry.sol b/contracts/FranchiseRegistry.sol index f94ba64b..e7069f06 100644 --- a/contracts/FranchiseRegistry.sol +++ b/contracts/FranchiseRegistry.sol @@ -11,7 +11,9 @@ import { IIPAssetRegistry } from "./ip-assets/IIPAssetRegistry.sol"; import { LibIPAssetId } from "./ip-assets/LibIPAssetId.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { LicensingModule } from "./modules/licensing/LicensingModule.sol"; +import { LibTimeConditional } from "./modules/timing/LibTimeConditional.sol"; contract FranchiseRegistry is UUPSUpgradeable, @@ -128,18 +130,80 @@ contract FranchiseRegistry is string calldata mediaUrl ) external returns (uint256 ipAssetID) { FranchiseStorage storage $ = _getFranchiseStorage(); - if (msg.sender != $.ipAssetRegistries[franchiseId]) revert Unauthorized(); - ipAssetID = IIPAssetRegistry($.ipAssetRegistries[franchiseId]).createIPAsset(sb, name, _description, mediaUrl, msg.sender); - /// TODO: grant registry - /* + address ipAssetRegistry = $.ipAssetRegistries[franchiseId]; + ipAssetID = IIPAssetRegistry(ipAssetRegistry).createIPAsset(sb, name, _description, mediaUrl, msg.sender); + // For demo, every IPAsset has root comercial and non commercial $.licensingModule.grantLicense( - + msg.sender, + 0, // root license + keccak256(abi.encode(sb)), //Using IPAsset Type as media ID for demo, + LicensingModule.GeneralTerms({ + exclusive: false, + canSublicense: true, + commercial: false + }), + LicensingModule.OwnershipParams({ + holder: address(0), + token: LicensingModule.Token({ + collection: IERC721(ipAssetRegistry), + tokenId: ipAssetID + }) + }), + LicensingModule.PaymentTerms({ + interpreter: address(0), + data: "" + }), + LicensingModule.GrantingTerms({ + processor: address(0), + data: "" + }), + LibTimeConditional.TimeConfig({ + maxTTL: 0, + minTTL: 0, + renewable: false, + renewer: address(0), + endTime: 0 + }), + "", // License URI ignored for non commercial + address(0) // No revoker for demo + ); + $.licensingModule.grantLicense( + msg.sender, + 0, // root license + keccak256(abi.encode(sb)), //Using IPAsset Type as media ID for demo, + LicensingModule.GeneralTerms({ + exclusive: false, + canSublicense: true, + commercial: true + }), + LicensingModule.OwnershipParams({ + holder: address(0), + token: LicensingModule.Token({ + collection: IERC721(ipAssetRegistry), + tokenId: ipAssetID + }) + }), + LicensingModule.PaymentTerms({ + interpreter: address(0), + data: "" + }), + LicensingModule.GrantingTerms({ + processor: address(0), + data: "" + }), + LibTimeConditional.TimeConfig({ + maxTTL: 0, + minTTL: 0, + renewable: false, + renewer: address(0), + endTime: 0 + }), + "", // License URI ignored for non commercial + address(0) // No revoker for demo ); - */ return ipAssetID; } - function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { _requireMinted(tokenId); FranchiseStorage storage $ = _getFranchiseStorage(); diff --git a/contracts/ip-assets/data-access-modules/group/GroupDAM.sol b/contracts/ip-assets/data-access-modules/group/GroupDAM.sol index ae56f517..e79191ea 100644 --- a/contracts/ip-assets/data-access-modules/group/GroupDAM.sol +++ b/contracts/ip-assets/data-access-modules/group/GroupDAM.sol @@ -41,8 +41,8 @@ abstract contract GroupDAM is IGroupDAM, IPAssetDataManager { } } - function createGroup(string calldata name, string calldata _description, string calldata mediaUrl, IPAsset linkedType, uint256[] calldata linkedItems) external returns(uint256) { - uint256 id = createIPAsset(IPAsset.GROUP, name, _description, mediaUrl); + function createGroup(string calldata name, string calldata _description, string calldata mediaUrl, address to, IPAsset linkedType, uint256[] calldata linkedItems) external returns(uint256) { + uint256 id = createIPAsset(IPAsset.GROUP, name, _description, mediaUrl, to); GroupDAMStorage storage $ = _getGroupDAMStorage(); $.groupData[id].linkedType = linkedType; groupItems(id, linkedItems); diff --git a/contracts/ip-assets/data-access-modules/group/IGroupDAM.sol b/contracts/ip-assets/data-access-modules/group/IGroupDAM.sol index aea39a62..778fe421 100644 --- a/contracts/ip-assets/data-access-modules/group/IGroupDAM.sol +++ b/contracts/ip-assets/data-access-modules/group/IGroupDAM.sol @@ -6,7 +6,7 @@ import { IIPAssetDataManager } from "../storage/IIPAssetDataManager.sol"; interface IGroupDAM is IIPAssetDataManager { - function createGroup(string calldata name, string calldata _description, string calldata mediaUrl, IPAsset linkedType, uint256[] calldata linkedItems) external returns(uint256); + function createGroup(string calldata name, string calldata _description, string calldata mediaUrl, address to, IPAsset linkedType, uint256[] calldata linkedItems) external returns(uint256); function readGroup(uint256 id) external view returns (IPAssetData memory blockData, IPAsset linkedType, uint256[] memory linkedItems); function groupItems(uint256 id, uint256[] calldata linkedItems) external; diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index fa9f4fc7..19d82c08 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -31,7 +31,7 @@ contract LicensingModule is ERC721 { } struct GrantingTerms { - address grantProcessor; + address processor; bytes data; } @@ -110,47 +110,53 @@ contract LicensingModule is ERC721 { string memory licenseURI, address revoker ) public onlyFranchiseRegistry returns (uint256) { - string memory _licenseURI = licenseURI; - if (parentLicenseId == 0) { - if (!generalTerms.commercial) { - _licenseURI = nonCommercialLicenseURI; - } - } else { - if (ownerOf(parentLicenseId) != licensor) revert("Sender is not the owner of the parent license"); + if (parentLicenseId != 0) { License memory parentLicense = _licenses[parentLicenseId]; - if (!parentLicense.active) revert("Parent license is not active"); - if (!parentLicense.generalTerms.canSublicense) revert("Parent license cannot be sublicensed"); - if (parentLicense.generalTerms.commercial != generalTerms.commercial) revert("Commercial terms must be the same as parent license"); + _verifySublicense(parentLicenseId, licensor, parentLicense, generalTerms); + licenseURI = parentLicense.licenseURI; + } else { if (!generalTerms.commercial) { - _licenseURI = parentLicense.licenseURI; + licenseURI = nonCommercialLicenseURI; } } + // TODO: check other terms - uint256 licenseId = _emitLicense( - parentLicenseId, - mediaId, - generalTerms, - ownershipParams, - paymentTerms, - grantingTerms, - durationTerms, - _licenseURI, - revoker - ); - // Not bound to a token, mint to holder - if (ownershipParams.holder != address(0)) { - _mint(ownershipParams.holder, licenseId); + uint256 licenseId; + { + licenseId = _emitLicense( + parentLicenseId, + mediaId, + generalTerms, + ownershipParams, + paymentTerms, + grantingTerms, + durationTerms, + licenseURI, + revoker + ); + // Not bound to a token, mint to holder + if (ownershipParams.holder != address(0)) { + _mint(ownershipParams.holder, licenseId); + } + emit LicenseGranted( + licenseId, + ownershipParams.holder, + address(ownershipParams.token.collection), + ownershipParams.token.tokenId, + parentLicenseId + ); } - emit LicenseGranted( - licenseId, - ownershipParams.holder, - address(ownershipParams.token.collection), - ownershipParams.token.tokenId, - parentLicenseId - ); return licenseId; } + + function _verifySublicense(uint256 parentLicenseId, address licensor, License memory parentLicense, GeneralTerms memory generalTerms) private view { + if (ownerOf(parentLicenseId) != licensor) revert("Sender is not the owner of the parent license"); + if (!parentLicense.active) revert("Parent license is not active"); + if (!parentLicense.generalTerms.canSublicense) revert("Parent license cannot be sublicensed"); + if (parentLicense.generalTerms.commercial != generalTerms.commercial) revert("Commercial terms must be the same as parent license"); + } + function _isUnsetToken(Token memory token) private pure returns (bool) { return address(token.collection) == address(0); } diff --git a/script/foundry/deployment/Main.s.sol b/script/foundry/deployment/Main.s.sol index d56b3bc5..95c0ecb7 100644 --- a/script/foundry/deployment/Main.s.sol +++ b/script/foundry/deployment/Main.s.sol @@ -104,7 +104,7 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler, ProxyHelper { contractKey = "IPAssetRegistry-Template"; console.log(string.concat("Deploying ", contractKey, "...")); - newAddress = address(new IPAssetRegistry(commonIPAssetEventEmitter)); + newAddress = address(new IPAssetRegistry(commonIPAssetEventEmitter, franchiseRegistry)); _writeAddress(contractKey, newAddress); console.log(string.concat(contractKey, " deployed to:"), newAddress); diff --git a/script/foundry/upgrades/DevUpgrades.s.sol b/script/foundry/upgrades/DevUpgrades.s.sol index e209039b..fc81dac4 100644 --- a/script/foundry/upgrades/DevUpgrades.s.sol +++ b/script/foundry/upgrades/DevUpgrades.s.sol @@ -94,7 +94,7 @@ contract UpgradeIPAssetRegistry is Script, BroadcastManager, JsonDeploymentHandl contractKey = "IPAssetRegistry-Impl"; console.log(string.concat("Deploying ", contractKey, "...")); - address ipAssetRegistry = address(new IPAssetRegistry(eventEmitter)); + address ipAssetRegistry = address(new IPAssetRegistry(eventEmitter, franchiseRegistryProxy)); console.log(string.concat(contractKey, " deployed to:"), ipAssetRegistry); console.log(string.concat("Updating ", contractKey, " beacon...")); diff --git a/test/foundry/IPAssetsRegistry.t.sol b/test/foundry/IPAssetsRegistry.t.sol index 7d4b7144..889f77cc 100644 --- a/test/foundry/IPAssetsRegistry.t.sol +++ b/test/foundry/IPAssetsRegistry.t.sol @@ -26,6 +26,7 @@ contract IPAssetRegistryTest is Test { address owner = address(this); address mintee = address(1); address mintee2 = address(2); + address mockFranchiseRegistry = address(0x7474); uint256 private constant _ID_RANGE = 10**12; uint256 private constant _FIRST_ID_STORY = 1; @@ -38,7 +39,7 @@ contract IPAssetRegistryTest is Test { function setUp() public { factory = new IPAssetRegistryFactory(); address mockEventEmitter = address(new MockIPAssetEventEmitter()); - factory.upgradeFranchises(address(new IPAssetRegistry(mockEventEmitter))); + factory.upgradeFranchises(address(new IPAssetRegistry(mockEventEmitter, mockFranchiseRegistry))); ipAssetRegistry = IPAssetRegistry(factory.createFranchiseIPAssets(1, "name", "symbol", "description")); } @@ -56,12 +57,12 @@ contract IPAssetRegistryTest is Test { IPAsset sb = IPAsset(i); uint256 zero = LibIPAssetId._zeroId(sb); assertEq(ipAssetRegistry.currentIdFor(sb), zero, "starts with zero"); - vm.prank(mintee); - uint256 blockId1 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl"); + vm.prank(mockFranchiseRegistry); + uint256 blockId1 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl", mintee); assertEq(blockId1, zero + 1, "returned blockId is incremented by one"); assertEq(ipAssetRegistry.currentIdFor(sb), zero + 1, "mint increments currentIdFor by one"); - vm.prank(mintee); - uint256 blockId2 = ipAssetRegistry.createIPAsset(sb, "name2", "description2", "mediaUrl2"); + vm.prank(mockFranchiseRegistry); + uint256 blockId2 = ipAssetRegistry.createIPAsset(sb, "name2", "description2", "mediaUrl2", mintee); assertEq(blockId2, zero + 2, "returned blockId is incremented by one again"); assertEq(ipAssetRegistry.currentIdFor(sb), zero + 2, "2 mint increments currentIdFor by one again"); } @@ -75,26 +76,26 @@ contract IPAssetRegistryTest is Test { IPAsset sb = IPAsset(i); uint256 loopBalance = ipAssetRegistry.balanceOf(mintee); assertEq(loopBalance, (i - 1) * 2, "balance is zero for block type"); - vm.prank(mintee); - uint256 blockId1 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl"); + vm.prank(mockFranchiseRegistry); + uint256 blockId1 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl", mintee); assertEq(ipAssetRegistry.balanceOf(mintee), loopBalance + 1, "balance is incremented by one"); assertEq(ipAssetRegistry.ownerOf(blockId1), mintee); - vm.prank(mintee); - uint256 blockId2 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl"); + vm.prank(mockFranchiseRegistry); + uint256 blockId2 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl", mintee); assertEq(ipAssetRegistry.balanceOf(mintee), loopBalance + 2, "balance is incremented by one again"); assertEq(ipAssetRegistry.ownerOf(blockId2), mintee); } } function test_revertMintUnknownIPAsset() public { - vm.startPrank(mintee); + vm.startPrank(mockFranchiseRegistry); vm.expectRevert(InvalidBlockType.selector); - ipAssetRegistry.createIPAsset(IPAsset.UNDEFINED, "name", "description", "mediaUrl"); + ipAssetRegistry.createIPAsset(IPAsset.UNDEFINED, "name", "description", "mediaUrl", mintee); } function test_IPAssetCreationData() public { - vm.prank(mintee); - uint256 blockId = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "mediaUrl"); + vm.prank(mockFranchiseRegistry); + uint256 blockId = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "mediaUrl", mintee); IPAssetRegistry.IPAssetData memory data = ipAssetRegistry.readIPAsset(blockId); assertEq(uint8(data.blockType), uint8(IPAsset.STORY)); assertEq(data.name, "name"); @@ -111,8 +112,8 @@ contract IPAssetRegistryTest is Test { } function test_tokenUriReturnsMediaURL() public { - vm.prank(mintee); - uint256 blockId = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "https://mediaUrl.xyz"); + vm.prank(mockFranchiseRegistry); + uint256 blockId = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "https://mediaUrl.xyz", mintee); assertEq(ipAssetRegistry.tokenURI(blockId), "https://mediaUrl.xyz"); } diff --git a/test/foundry/IPAssetsRegistryFactory.t.sol b/test/foundry/IPAssetsRegistryFactory.t.sol index 038873ca..4f3dd03d 100644 --- a/test/foundry/IPAssetsRegistryFactory.t.sol +++ b/test/foundry/IPAssetsRegistryFactory.t.sol @@ -9,7 +9,7 @@ import "./mocks/MockIPAssetEventEmitter.sol"; contract IPAssetRegistryv2 is IPAssetRegistry { - constructor(address _eventEmitter) IPAssetRegistry(_eventEmitter) { + constructor(address _eventEmitter, address _franchiseRegistry) IPAssetRegistry(_eventEmitter, _franchiseRegistry) { } function version() virtual override external pure returns (string memory) { @@ -26,12 +26,14 @@ contract IPAssetRegistryFactoryTest is Test { address notOwner = address(0x123); IPAssetRegistryFactory public factory; address private _mockEventEmitter = address(0x123123); + address mockFranchiseRegistry = address(0x7474); function setUp() public { factory = new IPAssetRegistryFactory(); address eventEmitter = address(new MockIPAssetEventEmitter()); - address ipAssetRegistry = address(new IPAssetRegistry(eventEmitter)); + + address ipAssetRegistry = address(new IPAssetRegistry(eventEmitter, mockFranchiseRegistry)); factory.upgradeFranchises(ipAssetRegistry); } @@ -56,7 +58,7 @@ contract IPAssetRegistryFactoryTest is Test { } function test_UpgradeCollections() public { - IPAssetRegistryv2 newImplementation = new IPAssetRegistryv2(_mockEventEmitter); + IPAssetRegistryv2 newImplementation = new IPAssetRegistryv2(_mockEventEmitter, mockFranchiseRegistry); //vm.expectEmit(true, true, true, true); //emit CollectionsUpgraded(address(newImplementation), "2.0.0"); factory.upgradeFranchises(address(newImplementation)); @@ -65,7 +67,7 @@ contract IPAssetRegistryFactoryTest is Test { } function test_revertIfNotOwnerUpgrades() public { - IPAssetRegistryv2 newImplementation = new IPAssetRegistryv2(_mockEventEmitter); + IPAssetRegistryv2 newImplementation = new IPAssetRegistryv2(_mockEventEmitter, mockFranchiseRegistry); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); factory.upgradeFranchises(address(newImplementation)); diff --git a/test/foundry/relationships/RelationshipModule.Relating.t.sol b/test/foundry/relationships/RelationshipModule.Relating.t.sol index 82e2b1b1..d58ea11d 100644 --- a/test/foundry/relationships/RelationshipModule.Relating.t.sol +++ b/test/foundry/relationships/RelationshipModule.Relating.t.sol @@ -49,12 +49,13 @@ contract RelationshipModuleRelationshipTest is BaseTest { }); relationshipId = relationshipModule.setRelationshipConfig("RELATIONSHIP_ID", params); - vm.startPrank(ipAssetOwner); - - ipAssetIds[uint8(IPAsset.STORY)] = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "mediaUrl"); - ipAssetIds[uint8(IPAsset.CHARACTER)] = ipAssetRegistry.createIPAsset(IPAsset.CHARACTER, "name", "description", "mediaUrl"); - ipAssetIds[uint8(IPAsset.ART)] = ipAssetRegistry.createIPAsset(IPAsset.ART, "name", "description", "mediaUrl"); + vm.startPrank(address(franchiseRegistry)); + ipAssetIds[uint8(IPAsset.STORY)] = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "mediaUrl", ipAssetOwner); + ipAssetIds[uint8(IPAsset.CHARACTER)] = ipAssetRegistry.createIPAsset(IPAsset.CHARACTER, "name", "description", "mediaUrl", ipAssetOwner); + ipAssetIds[uint8(IPAsset.ART)] = ipAssetRegistry.createIPAsset(IPAsset.ART, "name", "description", "mediaUrl", ipAssetOwner); + vm.stopPrank(); + vm.startPrank(ipAssetOwner); externalAsset = new MockExternalAsset(); ipAssetIds[EXTERNAL_ASSET] = 333; externalAsset.mint(ipAssetOwner, 333); @@ -137,8 +138,8 @@ contract RelationshipModuleRelationshipTest is BaseTest { FranchiseRegistry.FranchiseCreationParams memory params = FranchiseRegistry.FranchiseCreationParams("name2", "symbol2", "description2", "tokenURI2"); (uint256 id, address otherIPAssets) = franchiseRegistry.registerFranchise(params); IPAssetRegistry otherIPAssetRegistry = IPAssetRegistry(otherIPAssets); - vm.prank(ipAssetOwner); - uint256 otherId = otherIPAssetRegistry.createIPAsset(IPAsset.CHARACTER, "name", "description", "mediaUrl"); + vm.prank(address(franchiseRegistry)); + uint256 otherId = otherIPAssetRegistry.createIPAsset(IPAsset.CHARACTER, "name", "description", "mediaUrl", ipAssetOwner); vm.expectRevert(IRelationshipModule.CannotRelateToOtherFranchise.selector); relationshipModule.relate( IRelationshipModule.RelationshipParams( @@ -149,8 +150,8 @@ contract RelationshipModuleRelationshipTest is BaseTest { } function test_revert_relateUnsupportedSource() public { - vm.prank(ipAssetOwner); - uint256 wrongId = ipAssetRegistry.createIPAsset(IPAsset.GROUP, "name", "description", "mediaUrl"); + vm.prank(address(franchiseRegistry)); + uint256 wrongId = ipAssetRegistry.createIPAsset(IPAsset.GROUP, "name", "description", "mediaUrl", ipAssetOwner); vm.expectRevert(IRelationshipModule.UnsupportedRelationshipSrc.selector); relationshipModule.relate( IRelationshipModule.RelationshipParams( @@ -161,8 +162,8 @@ contract RelationshipModuleRelationshipTest is BaseTest { } function test_revert_relateUnsupportedDestination() public { - vm.prank(ipAssetOwner); - uint256 wrongId = ipAssetRegistry.createIPAsset(IPAsset.GROUP, "name", "description", "mediaUrl"); + vm.prank(address(franchiseRegistry)); + uint256 wrongId = ipAssetRegistry.createIPAsset(IPAsset.GROUP, "name", "description", "mediaUrl", ipAssetOwner); vm.expectRevert(IRelationshipModule.UnsupportedRelationshipDst.selector); relationshipModule.relate( IRelationshipModule.RelationshipParams( diff --git a/test/foundry/utils/BaseTest.sol b/test/foundry/utils/BaseTest.sol index 8587211f..300b6f74 100644 --- a/test/foundry/utils/BaseTest.sol +++ b/test/foundry/utils/BaseTest.sol @@ -52,7 +52,7 @@ contract BaseTest is Test, ProxyHelper { ) ); address eventEmitter = address(new CommonIPAssetEventEmitter(address(franchiseRegistry))); - factory.upgradeFranchises(address(new IPAssetRegistry(eventEmitter))); + factory.upgradeFranchises(address(new IPAssetRegistry(eventEmitter, address(franchiseRegistry)))); vm.startPrank(franchiseOwner); FranchiseRegistry.FranchiseCreationParams memory params = FranchiseRegistry.FranchiseCreationParams("name", "symbol", "description", "tokenURI"); From 53c61587e62a1b489bef72b01300b6c082d935bc Mon Sep 17 00:00:00 2001 From: Raul Date: Sat, 12 Aug 2023 01:02:31 -0700 Subject: [PATCH 04/42] grant license --- contracts/FranchiseRegistry.sol | 99 ++++++++++--------- .../modules/licensing/LicensingModule.sol | 10 +- 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/contracts/FranchiseRegistry.sol b/contracts/FranchiseRegistry.sol index e7069f06..a9d8c06b 100644 --- a/contracts/FranchiseRegistry.sol +++ b/contracts/FranchiseRegistry.sol @@ -122,6 +122,7 @@ contract FranchiseRegistry is } } + // TODO create asset as derivative of another asset function createIPAsset( uint256 franchiseId, IPAsset sb, @@ -132,57 +133,58 @@ contract FranchiseRegistry is FranchiseStorage storage $ = _getFranchiseStorage(); address ipAssetRegistry = $.ipAssetRegistries[franchiseId]; ipAssetID = IIPAssetRegistry(ipAssetRegistry).createIPAsset(sb, name, _description, mediaUrl, msg.sender); - // For demo, every IPAsset has root comercial and non commercial - $.licensingModule.grantLicense( - msg.sender, - 0, // root license - keccak256(abi.encode(sb)), //Using IPAsset Type as media ID for demo, - LicensingModule.GeneralTerms({ - exclusive: false, - canSublicense: true, - commercial: false - }), - LicensingModule.OwnershipParams({ - holder: address(0), - token: LicensingModule.Token({ - collection: IERC721(ipAssetRegistry), - tokenId: ipAssetID - }) - }), - LicensingModule.PaymentTerms({ - interpreter: address(0), - data: "" - }), - LicensingModule.GrantingTerms({ - processor: address(0), - data: "" - }), - LibTimeConditional.TimeConfig({ - maxTTL: 0, - minTTL: 0, - renewable: false, - renewer: address(0), - endTime: 0 - }), - "", // License URI ignored for non commercial - address(0) // No revoker for demo - ); - $.licensingModule.grantLicense( + // For demo, every IPAsset has root comercial and non commercial licenses. Both can sublicense + LicensingModule.OwnershipParams memory ownershipParams = LicensingModule.OwnershipParams({ + holder: address(0), + token: LicensingModule.Token({ + collection: IERC721(ipAssetRegistry), + tokenId: ipAssetID + }) + }); + _demoGrantLicense(keccak256("FULL_COMMERCIAL_RIGHTS"), true, true, 0, ownershipParams, ""); + _demoGrantLicense(keccak256("NON_COMMERCIAL_PUBLIC_NFT_SHARE_ALIKE"), false, true, 0, ownershipParams, ""); + return ipAssetID; + } + + function createLicense( + uint256 franchiseId, + uint256 ipAssetId, + bool commercial, + bytes32 mediaId, + string memory licenseURI + ) external returns (uint256) { + FranchiseStorage storage $ = _getFranchiseStorage(); + address ipAssetRegistry = $.ipAssetRegistries[franchiseId]; + uint256 parentLicenseId = $.licensingModule.demoTokenToLicense(ipAssetRegistry, ipAssetId); + LicensingModule.OwnershipParams memory ownershipParams = LicensingModule.OwnershipParams({ + holder: msg.sender, + token: LicensingModule.Token({ + collection: IERC721(address(0)), + tokenId: 0 + }) + }); + return _demoGrantLicense(mediaId, commercial, false, parentLicenseId, ownershipParams, licenseURI); + } + + function _demoGrantLicense( + bytes32 mediaId, + bool commercial, + bool canSublicense, + uint256 parentLicenseId, + LicensingModule.OwnershipParams memory ownershipParamns, + string memory licenseUri + ) private returns (uint256) { + FranchiseStorage storage $ = _getFranchiseStorage(); + return $.licensingModule.grantLicense( msg.sender, - 0, // root license - keccak256(abi.encode(sb)), //Using IPAsset Type as media ID for demo, + parentLicenseId, + mediaId, LicensingModule.GeneralTerms({ exclusive: false, - canSublicense: true, - commercial: true - }), - LicensingModule.OwnershipParams({ - holder: address(0), - token: LicensingModule.Token({ - collection: IERC721(ipAssetRegistry), - tokenId: ipAssetID - }) + canSublicense: canSublicense, + commercial: commercial }), + ownershipParamns, LicensingModule.PaymentTerms({ interpreter: address(0), data: "" @@ -198,10 +200,9 @@ contract FranchiseRegistry is renewer: address(0), endTime: 0 }), - "", // License URI ignored for non commercial + licenseUri, // License URI ignored for non commercial address(0) // No revoker for demo ); - return ipAssetID; } function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index 19d82c08..86feb1b5 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -61,16 +61,20 @@ contract LicensingModule is ERC721 { // Temporal terms and address that could renew the license LibTimeConditional.TimeConfig durationTerms; string licenseURI; + // TODO: tokenbound license config (share alike, ledger authoritative...) } mapping(uint256 => License) private _licenses; uint256 private _licenseCounter; // TODO - mapping(address => bool) _bannedMarketPlaces; + mapping(address => bool) private _bannedMarketPlaces; // Each franchise can choose to restrict stuff, like allowed license templates, the holder of root commercial licenses can only be // the franchise owner and external PFP owners, etc. - mapping(uint256 => bytes) _franchiseRestrictions; + mapping(uint256 => bytes) private _franchiseRestrictions; + + // TODO: Remove this + mapping(address => mapping(uint256 => uint256)) public demoTokenToLicense; string public nonCommercialLicenseURI; address immutable public FRANCHISE_REGISTRY; @@ -146,6 +150,8 @@ contract LicensingModule is ERC721 { parentLicenseId ); } + // TODO: remove this, only for demo + demoTokenToLicense[address(ownershipParams.token.collection)][ownershipParams.token.tokenId] = licenseId; return licenseId; } From 53ab00d7cdaddaa909670bd516d7963ebac55ba6 Mon Sep 17 00:00:00 2001 From: Raul Date: Sat, 12 Aug 2023 12:12:50 -0700 Subject: [PATCH 05/42] license module upgradeable --- contracts/FranchiseRegistry.sol | 2 +- .../modules/licensing/LicensingModule.sol | 158 ++++++++++++------ 2 files changed, 104 insertions(+), 56 deletions(-) diff --git a/contracts/FranchiseRegistry.sol b/contracts/FranchiseRegistry.sol index a9d8c06b..fdccbbbb 100644 --- a/contracts/FranchiseRegistry.sol +++ b/contracts/FranchiseRegistry.sol @@ -155,7 +155,7 @@ contract FranchiseRegistry is ) external returns (uint256) { FranchiseStorage storage $ = _getFranchiseStorage(); address ipAssetRegistry = $.ipAssetRegistries[franchiseId]; - uint256 parentLicenseId = $.licensingModule.demoTokenToLicense(ipAssetRegistry, ipAssetId); + uint256 parentLicenseId = $.licensingModule.licenseIdForToken(ipAssetRegistry, ipAssetId); LicensingModule.OwnershipParams memory ownershipParams = LicensingModule.OwnershipParams({ holder: msg.sender, token: LicensingModule.Token({ diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index 86feb1b5..a1fe1f45 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.13; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import { LibTimeConditional } from "../timing/LibTimeConditional.sol"; -contract LicensingModule is ERC721 { +contract LicensingModule is ERC721Upgradeable { event LicenseGranted ( uint256 indexed licenseId, @@ -64,19 +64,22 @@ contract LicensingModule is ERC721 { // TODO: tokenbound license config (share alike, ledger authoritative...) } - mapping(uint256 => License) private _licenses; - uint256 private _licenseCounter; - - // TODO - mapping(address => bool) private _bannedMarketPlaces; - // Each franchise can choose to restrict stuff, like allowed license templates, the holder of root commercial licenses can only be - // the franchise owner and external PFP owners, etc. - mapping(uint256 => bytes) private _franchiseRestrictions; + struct LicenseModuleStorage { + mapping(uint256 => License) licenses; + uint256 licenseCounter; + // TODO + mapping(address => bool) bannedMarketPlaces; + // Each franchise can choose to restrict stuff, like allowed license templates, the holder of root commercial licenses can only be + // the franchise owner and external PFP owners, etc. + mapping(uint256 => bytes) franchiseRestrictions; + // TODO: Remove this + mapping(address => mapping(uint256 => uint256)) demoTokenToLicense; + string nonCommercialLicenseURI; + } - // TODO: Remove this - mapping(address => mapping(uint256 => uint256)) public demoTokenToLicense; + // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.license-module.storage")) - 1))) + bytes32 private constant _STORAGE_LOCATION = 0x778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd; - string public nonCommercialLicenseURI; address immutable public FRANCHISE_REGISTRY; modifier onlyFranchiseRegistry() { @@ -85,19 +88,32 @@ contract LicensingModule is ERC721 { _; } - constructor(string memory name, string memory symbol, string memory _nonCommercialLicenseURI, address franchiseRegistry) ERC721(name, symbol) { - nonCommercialLicenseURI = _nonCommercialLicenseURI; + constructor(address franchiseRegistry) { if (franchiseRegistry == address(0)) revert("Franchise registry cannot be zero address"); FRANCHISE_REGISTRY = franchiseRegistry; } + function initialize(string memory _nonCommercialLicenseURI) public initializer { + __ERC721_init("Story Protocol License", "SPL"); + _getLicenseModuleStorage().nonCommercialLicenseURI = _nonCommercialLicenseURI; + } + + function _getLicenseModuleStorage() private pure returns (LicenseModuleStorage storage $) { + assembly { + $.slot := _STORAGE_LOCATION + } + } + + function isLicenseActive(uint256 licenseId) public view virtual returns (bool) { // TODO: limit to the tree depth // TODO: check time limits if (licenseId == 0) return false; while (licenseId != 0) { - if (!_licenses[licenseId].active) return false; - licenseId = _licenses[licenseId].parentLicenseId; + LicenseModuleStorage storage $ = _getLicenseModuleStorage(); + License memory license = $.licenses[licenseId]; + if (!license.active) return false; + licenseId = license.parentLicenseId; } return true; } @@ -106,52 +122,80 @@ contract LicensingModule is ERC721 { address licensor, uint256 parentLicenseId, bytes32 mediaId, - GeneralTerms memory generalTerms, + GeneralTerms calldata generalTerms, OwnershipParams calldata ownershipParams, - PaymentTerms memory paymentTerms, - GrantingTerms memory grantingTerms, - LibTimeConditional.TimeConfig memory durationTerms, + PaymentTerms calldata paymentTerms, + GrantingTerms calldata grantingTerms, + LibTimeConditional.TimeConfig calldata durationTerms, string memory licenseURI, address revoker ) public onlyFranchiseRegistry returns (uint256) { + LicenseModuleStorage storage $ = _getLicenseModuleStorage(); if (parentLicenseId != 0) { - License memory parentLicense = _licenses[parentLicenseId]; + License memory parentLicense = $.licenses[parentLicenseId]; _verifySublicense(parentLicenseId, licensor, parentLicense, generalTerms); licenseURI = parentLicense.licenseURI; } else { if (!generalTerms.commercial) { - licenseURI = nonCommercialLicenseURI; + licenseURI = $.nonCommercialLicenseURI; } } // TODO: check other terms - uint256 licenseId; - { - licenseId = _emitLicense( - parentLicenseId, - mediaId, - generalTerms, - ownershipParams, - paymentTerms, - grantingTerms, - durationTerms, - licenseURI, - revoker - ); - // Not bound to a token, mint to holder - if (ownershipParams.holder != address(0)) { - _mint(ownershipParams.holder, licenseId); - } - emit LicenseGranted( - licenseId, - ownershipParams.holder, - address(ownershipParams.token.collection), - ownershipParams.token.tokenId, - parentLicenseId - ); - } + uint256 licenseId = _emitLicenseAndEvent( + parentLicenseId, + mediaId, + generalTerms, + ownershipParams, + paymentTerms, + grantingTerms, + durationTerms, + licenseURI, + revoker + ); + // TODO: remove this, only for demo - demoTokenToLicense[address(ownershipParams.token.collection)][ownershipParams.token.tokenId] = licenseId; + $.demoTokenToLicense[address(ownershipParams.token.collection)][ownershipParams.token.tokenId] = licenseId; + return licenseId; + } + + function _emitLicenseAndEvent( + uint256 parentLicenseId, + bytes32 mediaId, + GeneralTerms calldata generalTerms, + OwnershipParams calldata ownershipParams, + PaymentTerms calldata paymentTerms, + GrantingTerms calldata grantingTerms, + LibTimeConditional.TimeConfig calldata durationTerms, + string memory licenseURI, + address revoker + ) private returns (uint256 licenseId) { + + licenseId = _emitLicense( + parentLicenseId, + mediaId, + generalTerms, + ownershipParams, + paymentTerms, + grantingTerms, + durationTerms, + licenseURI, + revoker + ); + + // Not bound to a token, mint to holder + if (ownershipParams.holder != address(0)) { + _mint(ownershipParams.holder, licenseId); + } + + emit LicenseGranted( + licenseId, + ownershipParams.holder, + address(ownershipParams.token.collection), + ownershipParams.token.tokenId, + parentLicenseId + ); + return licenseId; } @@ -169,7 +213,7 @@ contract LicensingModule is ERC721 { function ownerOf(uint256 tokenId) public view virtual override returns (address) { // TODO: This should work with ERC6665 or similar, this is only for demo - Token memory token = _licenses[tokenId].licensedToken; + Token memory token = _getLicenseModuleStorage().licenses[tokenId].licensedToken; if (address(token.collection) != address(0)) { return token.collection.ownerOf(token.tokenId); } @@ -190,8 +234,9 @@ contract LicensingModule is ERC721 { if (ownershipParams.holder == address(0) && _isUnsetToken(ownershipParams.token)) revert("License must be bound to a token or a license holder"); if (ownershipParams.holder != address(0) && !_isUnsetToken(ownershipParams.token)) revert("License cannot be bound to a token and a license holder at the same time"); // TODO: validate all terms - _licenseCounter++; - _licenses[_licenseCounter] = License({ + LicenseModuleStorage storage $ = _getLicenseModuleStorage(); + uint256 currentCounter = $.licenseCounter++; + $.licenses[currentCounter] = License({ active: true, parentLicenseId: parentLicenseId, mediaId: mediaId, @@ -203,13 +248,16 @@ contract LicensingModule is ERC721 { durationTerms: durationTerms, licenseURI: licenseURI }); - return _licenseCounter; + return currentCounter; } - function getLicense(uint256 licenseId) public view returns (License memory, address holder) { - return (_licenses[licenseId], ownerOf(licenseId)); + function licenseIdForToken(address collection, uint256 tokenId) public view returns (uint256) { + return _getLicenseModuleStorage().demoTokenToLicense[collection][tokenId]; } + function getLicense(uint256 licenseId) public view returns (License memory, address holder) { + return (_getLicenseModuleStorage().licenses[licenseId], ownerOf(licenseId)); + } function _beforeTokenTransfer( address from, From 7437821f074639653bee8e9547f367730c7c2282 Mon Sep 17 00:00:00 2001 From: Raul Date: Sat, 12 Aug 2023 13:06:29 -0700 Subject: [PATCH 06/42] upgraded franchise to include license module --- .../5/run-1691868638.json | 61 +++++++ .../5/run-1691868669.json | 119 ++++++++++++++ .../5/run-1691868693.json | 119 ++++++++++++++ .../DevDeployments.s.sol/5/run-latest.json | 119 ++++++++++++++ .../DevUpgrades.s.sol/5/run-1691870521.json | 81 +++++++++ .../DevUpgrades.s.sol/5/run-1691870540.json | 155 ++++++++++++++++++ .../DevUpgrades.s.sol/5/run-1691870567.json | 155 ++++++++++++++++++ broadcast/DevUpgrades.s.sol/5/run-latest.json | 105 ++++++++---- contracts/FranchiseRegistry.sol | 2 +- .../modules/licensing/LicensingModule.sol | 2 +- deployment-5.json | 6 +- script/foundry/upgrades/DevDeployments.s.sol | 54 ++++++ script/foundry/upgrades/DevUpgrades.s.sol | 7 + 13 files changed, 947 insertions(+), 38 deletions(-) create mode 100644 broadcast/DevDeployments.s.sol/5/run-1691868638.json create mode 100644 broadcast/DevDeployments.s.sol/5/run-1691868669.json create mode 100644 broadcast/DevDeployments.s.sol/5/run-1691868693.json create mode 100644 broadcast/DevDeployments.s.sol/5/run-latest.json create mode 100644 broadcast/DevUpgrades.s.sol/5/run-1691870521.json create mode 100644 broadcast/DevUpgrades.s.sol/5/run-1691870540.json create mode 100644 broadcast/DevUpgrades.s.sol/5/run-1691870567.json create mode 100644 script/foundry/upgrades/DevDeployments.s.sol diff --git a/broadcast/DevDeployments.s.sol/5/run-1691868638.json b/broadcast/DevDeployments.s.sol/5/run-1691868638.json new file mode 100644 index 00000000..e0b650da --- /dev/null +++ b/broadcast/DevDeployments.s.sol/5/run-1691868638.json @@ -0,0 +1,61 @@ +{ + "transactions": [ + { + "hash": "0x70610487057d530e2da659732ebdb2bdba5c5cd4aeb52ed607a50a87877d7245", + "transactionType": "CREATE", + "contractName": "LicensingModule", + "contractAddress": "0x8602E35ee65074eF0D2701927289705f2F61773D", + "function": null, + "arguments": [ + "0xf157916d184598105FE4681eC93Be0f799599003" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x43e58c", + "value": "0x0", + "data": "0x60a06040523480156200001157600080fd5b5060405162003e2838038062003e288339810160408190526200003491620000b3565b6001600160a01b038116620000a15760405162461bcd60e51b815260206004820152602960248201527f4672616e63686973652072656769737472792063616e6e6f74206265207a65726044820152686f206164647265737360b81b606482015260840160405180910390fd5b6001600160a01b0316608052620000e5565b600060208284031215620000c657600080fd5b81516001600160a01b0381168114620000de57600080fd5b9392505050565b608051613d2062000108600039600081816102a101526113d30152613d206000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806394f99b40116100cd578063c87b56dd11610081578063e985e9c511610066578063e985e9c514610338578063f62d188814610381578063f782f6751461039457600080fd5b8063c87b56dd14610312578063e705c2b81461032557600080fd5b8063a22cb465116100b2578063a22cb465146102cb578063b2dd1212146102de578063b88d4fde146102ff57600080fd5b806394f99b401461029c57806395d89b41146102c357600080fd5b806323b872dd1161012457806342842e0e1161010957806342842e0e146102635780636352211e1461027657806370a082311461028957600080fd5b806323b872dd146101e05780632fb49eff146101f357600080fd5b806301ffc9a71461015657806306fdde031461017e578063081812fc14610193578063095ea7b3146101cb575b600080fd5b610169610164366004612ff7565b6103a7565b60405190151581526020015b60405180910390f35b61018661048c565b6040516101759190613082565b6101a66101a1366004613095565b61051e565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610175565b6101de6101d93660046130e0565b610552565b005b6101de6101ee36600461310c565b6106af565b6102556102013660046130e0565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfac160209081526040808320938352929052205490565b604051908152602001610175565b6101de61027136600461310c565b610736565b6101a6610284366004613095565b610751565b61025561029736600461314d565b610867565b6101a67f000000000000000000000000000000000000000000000000000000000000000081565b61018661091b565b6101de6102d936600461317a565b61092a565b6102f16102ec366004613095565b610939565b6040516101759291906131e4565b6101de61030d36600461349d565b610cdf565b610186610320366004613095565b610d6d565b610169610333366004613095565b610de0565b610169610346366004613509565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152606a6020908152604080832093909416825291909152205460ff1690565b6101de61038f366004613542565b6111a7565b6102556103a23660046135f0565b6113b9565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061043a57507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061048657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60606065805461049b906136e7565b80601f01602080910402602001604051908101604052809291908181526020018280546104c7906136e7565b80156105145780601f106104e957610100808354040283529160200191610514565b820191906000526020600020905b8154815290600101906020018083116104f757829003601f168201915b5050505050905090565b60006105298261192f565b5060009081526069602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b600061055d826119a3565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036106055760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216148061062e575061062e8133610346565b6106a05760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016105fc565b6106aa8383611a15565b505050565b6106b93382611ab5565b61072b5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016105fc565b6106aa838383611b75565b6106aa83838360405180602001604052806000815250610cdf565b60008181527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd602090815260408083208151808301909252600481015473ffffffffffffffffffffffffffffffffffffffff1680835260059091015492820192909252901561085e57805160208201516040517f6352211e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90921691636352211e916108169160040190815260200190565b602060405180830381865afa158015610833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108579190613734565b9392505050565b610857836119a3565b600073ffffffffffffffffffffffffffffffffffffffff82166108f25760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e6572000000000000000000000000000000000000000000000060648201526084016105fc565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526068602052604090205490565b60606066805461049b906136e7565b610935338383611e2f565b5050565b610941612ecd565b60008281527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd6020526040812061097784610751565b6040805161014081018252835460ff908116151582526001850154602080840191909152600286015483850152835160608181018652600388015480851615158352610100810485161515838501526201000090049093161515818601529183019190915282518084018452600486015473ffffffffffffffffffffffffffffffffffffffff90811682526005870154828401526080840191909152835180850190945260068601805490911684526007860180549394879460a08701949193929184019190610a46906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610a72906136e7565b8015610abf5780601f10610a9457610100808354040283529160200191610abf565b820191906000526020600020905b815481529060010190602001808311610aa257829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190610b0c906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610b38906136e7565b8015610b855780601f10610b5a57610100808354040283529160200191610b85565b820191906000526020600020905b815481529060010190602001808311610b6857829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e83018054919092019190610c52906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610c7e906136e7565b8015610ccb5780601f10610ca057610100808354040283529160200191610ccb565b820191906000526020600020905b815481529060010190602001808311610cae57829003601f168201915b505050505081525050915091509150915091565b610ce93383611ab5565b610d5b5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016105fc565b610d6784848484611f42565b50505050565b6060610d788261192f565b6000610d8f60408051602081019091526000815290565b90506000815111610daf5760405180602001604052806000815250610857565b80610db984611fcb565b604051602001610dca929190613751565b6040516020818303038152906040529392505050565b600081600003610df257506000919050565b811561119f5760008281527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd60208181526040808420815161014081018352815460ff90811615158252600183015482860152600283015482850152835160608082018652600385015480841615158352610100810484161515838901526201000090049092161515818601529082015282518084018452600483015473ffffffffffffffffffffffffffffffffffffffff90811682526005840154828701526080830191909152835180850190945260068301805490911684526007830180549697969295939460a087019490939084019190610eef906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610f1b906136e7565b8015610f685780601f10610f3d57610100808354040283529160200191610f68565b820191906000526020600020905b815481529060010190602001808311610f4b57829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190610fb5906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610fe1906136e7565b801561102e5780601f106110035761010080835404028352916020019161102e565b820191906000526020600020905b81548152906001019060200180831161101157829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e830180549190920191906110fb906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611127906136e7565b80156111745780601f1061114957610100808354040283529160200191611174565b820191906000526020600020905b81548152906001019060200180831161115757829003601f168201915b50505050508152505090508060000151611192575060009392505050565b602001519250610df29050565b506001919050565b600054610100900460ff16158080156111c75750600054600160ff909116105b806111e15750303b1580156111e1575060005460ff166001145b6112535760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016105fc565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156112b157600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6113256040518060400160405280601681526020017f53746f72792050726f746f636f6c204c6963656e7365000000000000000000008152506040518060400160405280600381526020017f53504c0000000000000000000000000000000000000000000000000000000000815250612089565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfac26113518385836137ce565b5080156106aa57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b60003373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146114655760405162461bcd60e51b8152602060048201526024808201527f53656e646572206973206e6f7420746865206672616e6368697365207265676960448201527f737472790000000000000000000000000000000000000000000000000000000060648201526084016105fc565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd8a156118175760008b815260208281526040808320815161014081018352815460ff90811615158252600183015482860152600283015482850152835160608181018652600385015480841615158352610100810484161515838901526201000090049092161515818601529082015282518084018452600483015473ffffffffffffffffffffffffffffffffffffffff90811682526005840154828701526080830191909152835180850190945260068301805490911684526007830180549295939460a08701949093908401919061155f906136e7565b80601f016020809104026020016040519081016040528092919081815260200182805461158b906136e7565b80156115d85780601f106115ad576101008083540402835291602001916115d8565b820191906000526020600020905b8154815290600101906020018083116115bb57829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190611625906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611651906136e7565b801561169e5780601f106116735761010080835404028352916020019161169e565b820191906000526020600020905b81548152906001019060200180831161168157829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e8301805491909201919061176b906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611797906136e7565b80156117e45780601f106117b9576101008083540402835291602001916117e4565b820191906000526020600020905b8154815290600101906020018083116117c757829003601f168201915b505050505081525050905061180b8c8e838d80360381019061180691906138e9565b612110565b610120015193506118bb565b61182760608a0160408b01613955565b6118bb5780600501805461183a906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611866906136e7565b80156118b35780601f10611888576101008083540402835291602001916118b3565b820191906000526020600020905b81548152906001019060200180831161189657829003601f168201915b505050505093505b60006118ce8c8c8c8c8c8c8c8c8c612306565b9050806004830160006118e760408d0160208e0161314d565b73ffffffffffffffffffffffffffffffffffffffff168152602080820192909252604090810160009081208d83013582529092529020559150509a9950505050505050505050565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff166119a05760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016105fc565b50565b60008181526067602052604081205473ffffffffffffffffffffffffffffffffffffffff16806104865760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016105fc565b600081815260696020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091558190611a6f826119a3565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600080611ac1836119a3565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611b2f575073ffffffffffffffffffffffffffffffffffffffff8082166000908152606a602090815260408083209388168352929052205460ff165b80611b6d57508373ffffffffffffffffffffffffffffffffffffffff16611b558461051e565b73ffffffffffffffffffffffffffffffffffffffff16145b949350505050565b8273ffffffffffffffffffffffffffffffffffffffff16611b95826119a3565b73ffffffffffffffffffffffffffffffffffffffff1614611c1e5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016105fc565b73ffffffffffffffffffffffffffffffffffffffff8216611ca65760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016105fc565b611cb38383836001612422565b8273ffffffffffffffffffffffffffffffffffffffff16611cd3826119a3565b73ffffffffffffffffffffffffffffffffffffffff1614611d5c5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016105fc565b600081815260696020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811690915573ffffffffffffffffffffffffffffffffffffffff8781168086526068855283862080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01905590871680865283862080546001019055868652606790945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611eaa5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016105fc565b73ffffffffffffffffffffffffffffffffffffffff8381166000818152606a602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611f4d848484611b75565b611f5984848484612427565b610d675760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016105fc565b60606000611fd883612600565b600101905060008167ffffffffffffffff811115611ff857611ff861339a565b6040519080825280601f01601f191660200182016040528015612022576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461202c57509392505050565b600054610100900460ff166121065760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105fc565b61093582826126e2565b8273ffffffffffffffffffffffffffffffffffffffff1661213085610751565b73ffffffffffffffffffffffffffffffffffffffff16146121b95760405162461bcd60e51b815260206004820152602d60248201527f53656e646572206973206e6f7420746865206f776e6572206f6620746865207060448201527f6172656e74206c6963656e73650000000000000000000000000000000000000060648201526084016105fc565b81516122075760405162461bcd60e51b815260206004820152601c60248201527f506172656e74206c6963656e7365206973206e6f74206163746976650000000060448201526064016105fc565b8160600151602001516122815760405162461bcd60e51b8152602060048201526024808201527f506172656e74206c6963656e73652063616e6e6f74206265207375626c69636560448201527f6e7365640000000000000000000000000000000000000000000000000000000060648201526084016105fc565b80604001511515826060015160400151151514610d675760405162461bcd60e51b815260206004820152603360248201527f436f6d6d65726369616c207465726d73206d757374206265207468652073616d60448201527f6520617320706172656e74206c6963656e73650000000000000000000000000060648201526084016105fc565b60006123538a8a61231c368c90038c018c6138e9565b61232b368c90038c018c613970565b6123348b613a56565b61233d8b613a56565b61234c368c90038c018c613a80565b8a8a612778565b90506000612364602089018961314d565b73ffffffffffffffffffffffffffffffffffffffff16146123955761239561238f602089018961314d565b82612ce8565b807f35805985e00e80103ddc5df8bca16fb35a75f7f0830d5d5448f443d689e9424a6123c460208a018a61314d565b6123d460408b0160208c0161314d565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152808b013590820152606081018d905260800160405180910390a29998505050505050505050565b610d67565b600073ffffffffffffffffffffffffffffffffffffffff84163b156125f5576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a029061249e903390899088908890600401613b0b565b6020604051808303816000875af19250505080156124f7575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526124f491810190613b54565b60015b6125aa573d808015612525576040519150601f19603f3d011682016040523d82523d6000602084013e61252a565b606091505b5080516000036125a25760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016105fc565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a0200000000000000000000000000000000000000000000000000000000149050611b6d565b506001949350505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310612649577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310612675576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061269357662386f26fc10000830492506010015b6305f5e10083106126ab576305f5e100830492506008015b61271083106126bf57612710830492506004015b606483106126d1576064830492506002015b600a83106104865760010192915050565b600054610100900460ff1661275f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105fc565b606561276b8382613b71565b5060666106aa8282613b71565b855160009073ffffffffffffffffffffffffffffffffffffffff161580156127b9575060208701515173ffffffffffffffffffffffffffffffffffffffff16155b1561282c5760405162461bcd60e51b815260206004820152603460248201527f4c6963656e7365206d75737420626520626f756e6420746f206120746f6b656e60448201527f206f722061206c6963656e736520686f6c64657200000000000000000000000060648201526084016105fc565b865173ffffffffffffffffffffffffffffffffffffffff161580159061286c575060208701515173ffffffffffffffffffffffffffffffffffffffff1615155b156129055760405162461bcd60e51b815260206004820152604860248201527f4c6963656e73652063616e6e6f7420626520626f756e6420746f206120746f6b60448201527f656e20616e642061206c6963656e736520686f6c64657220617420746865207360648201527f616d652074696d65000000000000000000000000000000000000000000000000608482015260a4016105fc565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabe80547f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd91600091908261295883613c8b565b9190505590506040518061014001604052806001151581526020018d81526020018c81526020018b81526020018a6020015181526020018981526020018881526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018781526020018681525082600001600083815260200190815260200160002060008201518160000160006101000a81548160ff021916908315150217905550602082015181600101556040820151816002015560608201518160030160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff02191690831515021790555060408201518160000160026101000a81548160ff021916908315150217905550505060808201518160040160008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155505060a08201518160060160008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001019081612b369190613b71565b50505060c082015180516008830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117815560208201516009840190612b9a9082613b71565b50505060e0820151600a8201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216179091556101008401518051600b850180546020840151604085015115157c0100000000000000000000000000000000000000000000000000000000027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff9283166e010000000000000000000000000000027fffffffff00000000000000000000000000000000000000000000000000000000909416929095169190911791909117929092169190911790556060810151600c850180549190941692169190911790915560800151600d820155610120820151600e820190612cd59082613b71565b50919d9c50505050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216612d4b5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016105fc565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612dbd5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016105fc565b612dcb600083836001612422565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612e3d5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016105fc565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260686020908152604080832080546001019055848352606790915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60408051610140810182526000808252602080830182905282840182905283516060808201865283825281830184905281860184905284015283518085019094528184528301529060808201908152602001612f526040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001606081525090565b8152602001612f8a6040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001606081525090565b8152600060208083018290526040805160a081018252838152918201839052818101839052606080830184905260808301939093528301529081015290565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146119a057600080fd5b60006020828403121561300957600080fd5b813561085781612fc9565b60005b8381101561302f578181015183820152602001613017565b50506000910152565b60008151808452613050816020860160208601613014565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006108576020830184613038565b6000602082840312156130a757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146119a057600080fd5b80356130db816130ae565b919050565b600080604083850312156130f357600080fd5b82356130fe816130ae565b946020939093013593505050565b60008060006060848603121561312157600080fd5b833561312c816130ae565b9250602084013561313c816130ae565b929592945050506040919091013590565b60006020828403121561315f57600080fd5b8135610857816130ae565b803580151581146130db57600080fd5b6000806040838503121561318d57600080fd5b8235613198816130ae565b91506131a66020840161316a565b90509250929050565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152611b6d6040850182613038565b604081526131f760408201845115159052565b60208301516060820152604083015160808201526000606084015161323960a08401828051151582526020808201511515908301526040908101511515910152565b50608084015161010061326e81850183805173ffffffffffffffffffffffffffffffffffffffff168252602090810151910152565b60a0860151915061022061014085015261328c6102608501836131af565b915060c08601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc080868503016101608701526132c984836131af565b935060e088015191506132f561018087018373ffffffffffffffffffffffffffffffffffffffff169052565b9187015180516dffffffffffffffffffffffffffff9081166101a08801526020820151166101c0870152604081015115156101e0870152606081015173ffffffffffffffffffffffffffffffffffffffff1661020087015260800151610220860152506101208601518483039091016102408501526133748282613038565b92505050610857602083018473ffffffffffffffffffffffffffffffffffffffff169052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156133ec576133ec61339a565b60405290565b600082601f83011261340357600080fd5b813567ffffffffffffffff8082111561341e5761341e61339a565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156134645761346461339a565b8160405283815286602085880101111561347d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080608085870312156134b357600080fd5b84356134be816130ae565b935060208501356134ce816130ae565b925060408501359150606085013567ffffffffffffffff8111156134f157600080fd5b6134fd878288016133f2565b91505092959194509250565b6000806040838503121561351c57600080fd5b8235613527816130ae565b91506020830135613537816130ae565b809150509250929050565b6000806020838503121561355557600080fd5b823567ffffffffffffffff8082111561356d57600080fd5b818501915085601f83011261358157600080fd5b81358181111561359057600080fd5b8660208285010111156135a257600080fd5b60209290920196919550909350505050565b6000606082840312156135c657600080fd5b50919050565b6000604082840312156135c657600080fd5b600060a082840312156135c657600080fd5b6000806000806000806000806000806102408b8d03121561361057600080fd5b6136198b6130d0565b995060208b0135985060408b013597506136368c60608d016135b4565b96506136458c60c08d016135b4565b95506101208b013567ffffffffffffffff8082111561366357600080fd5b61366f8e838f016135cc565b96506101408d013591508082111561368657600080fd5b6136928e838f016135cc565b95506136a28e6101608f016135de565b94506102008d01359150808211156136b957600080fd5b506136c68d828e016133f2565b9250506136d66102208c016130d0565b90509295989b9194979a5092959850565b600181811c908216806136fb57607f821691505b6020821081036135c6577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006020828403121561374657600080fd5b8151610857816130ae565b60008351613763818460208801613014565b835190830190613777818360208801613014565b01949350505050565b601f8211156106aa57600081815260208120601f850160051c810160208610156137a75750805b601f850160051c820191505b818110156137c6578281556001016137b3565b505050505050565b67ffffffffffffffff8311156137e6576137e661339a565b6137fa836137f483546136e7565b83613780565b6000601f84116001811461384c57600085156138165750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556138e2565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561389b578685013582556020948501946001909201910161387b565b50868210156138d6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6000606082840312156138fb57600080fd5b6040516060810181811067ffffffffffffffff8211171561391e5761391e61339a565b60405261392a8361316a565b81526139386020840161316a565b60208201526139496040840161316a565b60408201529392505050565b60006020828403121561396757600080fd5b6108578261316a565b6000818303606081121561398357600080fd5b61398b6133c9565b8335613996816130ae565b815260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0830112156139c857600080fd5b6139d06133c9565b915060208401356139e0816130ae565b82526040939093013560208083019190915283015250919050565b600060408284031215613a0d57600080fd5b613a156133c9565b90508135613a22816130ae565b8152602082013567ffffffffffffffff811115613a3e57600080fd5b613a4a848285016133f2565b60208301525092915050565b600061048636836139fb565b80356dffffffffffffffffffffffffffff811681146130db57600080fd5b600060a08284031215613a9257600080fd5b60405160a0810181811067ffffffffffffffff82111715613ab557613ab561339a565b604052613ac183613a62565b8152613acf60208401613a62565b6020820152613ae06040840161316a565b60408201526060830135613af3816130ae565b60608201526080928301359281019290925250919050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152613b4a6080830184613038565b9695505050505050565b600060208284031215613b6657600080fd5b815161085781612fc9565b815167ffffffffffffffff811115613b8b57613b8b61339a565b613b9f81613b9984546136e7565b84613780565b602080601f831160018114613bf25760008415613bbc5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556137c6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613c3f57888601518255948401946001909101908401613c20565b5085821015613c7b57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ce3577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b506001019056fea2646970667358221220f2a8449fa514209b5e1e6702694c3899389b46b19e276088a7b958f0a1db3c3e64736f6c63430008130033000000000000000000000000f157916d184598105fe4681ec93be0f799599003", + "nonce": "0x43", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionType": "CREATE", + "contractName": "ERC1967Proxy", + "contractAddress": "0xFbfa729428af206a084E18328Ec923587193525d", + "function": null, + "arguments": [ + "0x8602E35ee65074eF0D2701927289705f2F61773D", + "0xf62d18880000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003f68747470733a2f2f617277656176652e6e65742f794849624b6c4642673378754b537a6c4d5f6452454738593038756f642d67574b735769394f615046734d00" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x5824f", + "value": "0x0", + "data": "0x60806040526040516104ee3803806104ee833981016040819052610022916102de565b61002e82826000610035565b50506103fb565b61003e83610061565b60008251118061004b5750805b1561005c5761005a83836100a1565b505b505050565b61006a816100cd565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606100c683836040518060600160405280602781526020016104c760279139610180565b9392505050565b6001600160a01b0381163b61013f5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080856001600160a01b03168560405161019d91906103ac565b600060405180830381855af49150503d80600081146101d8576040519150601f19603f3d011682016040523d82523d6000602084013e6101dd565b606091505b5090925090506101ef868383876101f9565b9695505050505050565b60608315610268578251600003610261576001600160a01b0385163b6102615760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610136565b5081610272565b610272838361027a565b949350505050565b81511561028a5781518083602001fd5b8060405162461bcd60e51b815260040161013691906103c8565b634e487b7160e01b600052604160045260246000fd5b60005b838110156102d55781810151838201526020016102bd565b50506000910152565b600080604083850312156102f157600080fd5b82516001600160a01b038116811461030857600080fd5b60208401519092506001600160401b038082111561032557600080fd5b818501915085601f83011261033957600080fd5b81518181111561034b5761034b6102a4565b604051601f8201601f19908116603f01168101908382118183101715610373576103736102a4565b8160405282815288602084870101111561038c57600080fd5b61039d8360208301602088016102ba565b80955050505050509250929050565b600082516103be8184602087016102ba565b9190910192915050565b60208152600082518060208401526103e78160408501602087016102ba565b601f01601f19169190910160400192915050565b60be806104096000396000f3fe608060405236601057600e6013565b005b600e5b601f601b6021565b6065565b565b600060607f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b3660008037600080366000845af43d6000803e8080156083573d6000f35b3d6000fdfea2646970667358221220a678556ae280ddf13e459fe9ecd177633ae9dbc544c45d5b523d43ba9084c8dc64736f6c63430008130033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000008602e35ee65074ef0d2701927289705f2f61773d00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000084f62d18880000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003f68747470733a2f2f617277656176652e6e65742f794849624b6c4642673378754b537a6c4d5f6452454738593038756f642d67574b735769394f615046734d0000000000000000000000000000000000000000000000000000000000", + "nonce": "0x44", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [], + "libraries": [], + "pending": [ + "0x70610487057d530e2da659732ebdb2bdba5c5cd4aeb52ed607a50a87877d7245", + "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c" + ], + "path": "/Users/drmanhattan/Workspace/StoryProtocol/protocol-contracts/broadcast/DevDeployments.s.sol/5/run-latest.json", + "returns": {}, + "timestamp": 1691868638, + "chain": 5, + "multi": false, + "commit": "53ab00d" +} \ No newline at end of file diff --git a/broadcast/DevDeployments.s.sol/5/run-1691868669.json b/broadcast/DevDeployments.s.sol/5/run-1691868669.json new file mode 100644 index 00000000..19d2dd8f --- /dev/null +++ b/broadcast/DevDeployments.s.sol/5/run-1691868669.json @@ -0,0 +1,119 @@ +{ + "transactions": [ + { + "hash": "0x70610487057d530e2da659732ebdb2bdba5c5cd4aeb52ed607a50a87877d7245", + "transactionType": "CREATE", + "contractName": "LicensingModule", + "contractAddress": "0x8602E35ee65074eF0D2701927289705f2F61773D", + "function": null, + "arguments": [ + "0xf157916d184598105FE4681eC93Be0f799599003" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x43e58c", + "value": "0x0", + "data": "0x60a06040523480156200001157600080fd5b5060405162003e2838038062003e288339810160408190526200003491620000b3565b6001600160a01b038116620000a15760405162461bcd60e51b815260206004820152602960248201527f4672616e63686973652072656769737472792063616e6e6f74206265207a65726044820152686f206164647265737360b81b606482015260840160405180910390fd5b6001600160a01b0316608052620000e5565b600060208284031215620000c657600080fd5b81516001600160a01b0381168114620000de57600080fd5b9392505050565b608051613d2062000108600039600081816102a101526113d30152613d206000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806394f99b40116100cd578063c87b56dd11610081578063e985e9c511610066578063e985e9c514610338578063f62d188814610381578063f782f6751461039457600080fd5b8063c87b56dd14610312578063e705c2b81461032557600080fd5b8063a22cb465116100b2578063a22cb465146102cb578063b2dd1212146102de578063b88d4fde146102ff57600080fd5b806394f99b401461029c57806395d89b41146102c357600080fd5b806323b872dd1161012457806342842e0e1161010957806342842e0e146102635780636352211e1461027657806370a082311461028957600080fd5b806323b872dd146101e05780632fb49eff146101f357600080fd5b806301ffc9a71461015657806306fdde031461017e578063081812fc14610193578063095ea7b3146101cb575b600080fd5b610169610164366004612ff7565b6103a7565b60405190151581526020015b60405180910390f35b61018661048c565b6040516101759190613082565b6101a66101a1366004613095565b61051e565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610175565b6101de6101d93660046130e0565b610552565b005b6101de6101ee36600461310c565b6106af565b6102556102013660046130e0565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfac160209081526040808320938352929052205490565b604051908152602001610175565b6101de61027136600461310c565b610736565b6101a6610284366004613095565b610751565b61025561029736600461314d565b610867565b6101a67f000000000000000000000000000000000000000000000000000000000000000081565b61018661091b565b6101de6102d936600461317a565b61092a565b6102f16102ec366004613095565b610939565b6040516101759291906131e4565b6101de61030d36600461349d565b610cdf565b610186610320366004613095565b610d6d565b610169610333366004613095565b610de0565b610169610346366004613509565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152606a6020908152604080832093909416825291909152205460ff1690565b6101de61038f366004613542565b6111a7565b6102556103a23660046135f0565b6113b9565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061043a57507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061048657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60606065805461049b906136e7565b80601f01602080910402602001604051908101604052809291908181526020018280546104c7906136e7565b80156105145780601f106104e957610100808354040283529160200191610514565b820191906000526020600020905b8154815290600101906020018083116104f757829003601f168201915b5050505050905090565b60006105298261192f565b5060009081526069602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b600061055d826119a3565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036106055760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216148061062e575061062e8133610346565b6106a05760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016105fc565b6106aa8383611a15565b505050565b6106b93382611ab5565b61072b5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016105fc565b6106aa838383611b75565b6106aa83838360405180602001604052806000815250610cdf565b60008181527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd602090815260408083208151808301909252600481015473ffffffffffffffffffffffffffffffffffffffff1680835260059091015492820192909252901561085e57805160208201516040517f6352211e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90921691636352211e916108169160040190815260200190565b602060405180830381865afa158015610833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108579190613734565b9392505050565b610857836119a3565b600073ffffffffffffffffffffffffffffffffffffffff82166108f25760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e6572000000000000000000000000000000000000000000000060648201526084016105fc565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526068602052604090205490565b60606066805461049b906136e7565b610935338383611e2f565b5050565b610941612ecd565b60008281527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd6020526040812061097784610751565b6040805161014081018252835460ff908116151582526001850154602080840191909152600286015483850152835160608181018652600388015480851615158352610100810485161515838501526201000090049093161515818601529183019190915282518084018452600486015473ffffffffffffffffffffffffffffffffffffffff90811682526005870154828401526080840191909152835180850190945260068601805490911684526007860180549394879460a08701949193929184019190610a46906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610a72906136e7565b8015610abf5780601f10610a9457610100808354040283529160200191610abf565b820191906000526020600020905b815481529060010190602001808311610aa257829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190610b0c906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610b38906136e7565b8015610b855780601f10610b5a57610100808354040283529160200191610b85565b820191906000526020600020905b815481529060010190602001808311610b6857829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e83018054919092019190610c52906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610c7e906136e7565b8015610ccb5780601f10610ca057610100808354040283529160200191610ccb565b820191906000526020600020905b815481529060010190602001808311610cae57829003601f168201915b505050505081525050915091509150915091565b610ce93383611ab5565b610d5b5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016105fc565b610d6784848484611f42565b50505050565b6060610d788261192f565b6000610d8f60408051602081019091526000815290565b90506000815111610daf5760405180602001604052806000815250610857565b80610db984611fcb565b604051602001610dca929190613751565b6040516020818303038152906040529392505050565b600081600003610df257506000919050565b811561119f5760008281527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd60208181526040808420815161014081018352815460ff90811615158252600183015482860152600283015482850152835160608082018652600385015480841615158352610100810484161515838901526201000090049092161515818601529082015282518084018452600483015473ffffffffffffffffffffffffffffffffffffffff90811682526005840154828701526080830191909152835180850190945260068301805490911684526007830180549697969295939460a087019490939084019190610eef906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610f1b906136e7565b8015610f685780601f10610f3d57610100808354040283529160200191610f68565b820191906000526020600020905b815481529060010190602001808311610f4b57829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190610fb5906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610fe1906136e7565b801561102e5780601f106110035761010080835404028352916020019161102e565b820191906000526020600020905b81548152906001019060200180831161101157829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e830180549190920191906110fb906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611127906136e7565b80156111745780601f1061114957610100808354040283529160200191611174565b820191906000526020600020905b81548152906001019060200180831161115757829003601f168201915b50505050508152505090508060000151611192575060009392505050565b602001519250610df29050565b506001919050565b600054610100900460ff16158080156111c75750600054600160ff909116105b806111e15750303b1580156111e1575060005460ff166001145b6112535760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016105fc565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156112b157600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6113256040518060400160405280601681526020017f53746f72792050726f746f636f6c204c6963656e7365000000000000000000008152506040518060400160405280600381526020017f53504c0000000000000000000000000000000000000000000000000000000000815250612089565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfac26113518385836137ce565b5080156106aa57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b60003373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146114655760405162461bcd60e51b8152602060048201526024808201527f53656e646572206973206e6f7420746865206672616e6368697365207265676960448201527f737472790000000000000000000000000000000000000000000000000000000060648201526084016105fc565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd8a156118175760008b815260208281526040808320815161014081018352815460ff90811615158252600183015482860152600283015482850152835160608181018652600385015480841615158352610100810484161515838901526201000090049092161515818601529082015282518084018452600483015473ffffffffffffffffffffffffffffffffffffffff90811682526005840154828701526080830191909152835180850190945260068301805490911684526007830180549295939460a08701949093908401919061155f906136e7565b80601f016020809104026020016040519081016040528092919081815260200182805461158b906136e7565b80156115d85780601f106115ad576101008083540402835291602001916115d8565b820191906000526020600020905b8154815290600101906020018083116115bb57829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190611625906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611651906136e7565b801561169e5780601f106116735761010080835404028352916020019161169e565b820191906000526020600020905b81548152906001019060200180831161168157829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e8301805491909201919061176b906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611797906136e7565b80156117e45780601f106117b9576101008083540402835291602001916117e4565b820191906000526020600020905b8154815290600101906020018083116117c757829003601f168201915b505050505081525050905061180b8c8e838d80360381019061180691906138e9565b612110565b610120015193506118bb565b61182760608a0160408b01613955565b6118bb5780600501805461183a906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611866906136e7565b80156118b35780601f10611888576101008083540402835291602001916118b3565b820191906000526020600020905b81548152906001019060200180831161189657829003601f168201915b505050505093505b60006118ce8c8c8c8c8c8c8c8c8c612306565b9050806004830160006118e760408d0160208e0161314d565b73ffffffffffffffffffffffffffffffffffffffff168152602080820192909252604090810160009081208d83013582529092529020559150509a9950505050505050505050565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff166119a05760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016105fc565b50565b60008181526067602052604081205473ffffffffffffffffffffffffffffffffffffffff16806104865760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016105fc565b600081815260696020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091558190611a6f826119a3565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600080611ac1836119a3565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611b2f575073ffffffffffffffffffffffffffffffffffffffff8082166000908152606a602090815260408083209388168352929052205460ff165b80611b6d57508373ffffffffffffffffffffffffffffffffffffffff16611b558461051e565b73ffffffffffffffffffffffffffffffffffffffff16145b949350505050565b8273ffffffffffffffffffffffffffffffffffffffff16611b95826119a3565b73ffffffffffffffffffffffffffffffffffffffff1614611c1e5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016105fc565b73ffffffffffffffffffffffffffffffffffffffff8216611ca65760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016105fc565b611cb38383836001612422565b8273ffffffffffffffffffffffffffffffffffffffff16611cd3826119a3565b73ffffffffffffffffffffffffffffffffffffffff1614611d5c5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016105fc565b600081815260696020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811690915573ffffffffffffffffffffffffffffffffffffffff8781168086526068855283862080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01905590871680865283862080546001019055868652606790945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611eaa5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016105fc565b73ffffffffffffffffffffffffffffffffffffffff8381166000818152606a602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611f4d848484611b75565b611f5984848484612427565b610d675760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016105fc565b60606000611fd883612600565b600101905060008167ffffffffffffffff811115611ff857611ff861339a565b6040519080825280601f01601f191660200182016040528015612022576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461202c57509392505050565b600054610100900460ff166121065760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105fc565b61093582826126e2565b8273ffffffffffffffffffffffffffffffffffffffff1661213085610751565b73ffffffffffffffffffffffffffffffffffffffff16146121b95760405162461bcd60e51b815260206004820152602d60248201527f53656e646572206973206e6f7420746865206f776e6572206f6620746865207060448201527f6172656e74206c6963656e73650000000000000000000000000000000000000060648201526084016105fc565b81516122075760405162461bcd60e51b815260206004820152601c60248201527f506172656e74206c6963656e7365206973206e6f74206163746976650000000060448201526064016105fc565b8160600151602001516122815760405162461bcd60e51b8152602060048201526024808201527f506172656e74206c6963656e73652063616e6e6f74206265207375626c69636560448201527f6e7365640000000000000000000000000000000000000000000000000000000060648201526084016105fc565b80604001511515826060015160400151151514610d675760405162461bcd60e51b815260206004820152603360248201527f436f6d6d65726369616c207465726d73206d757374206265207468652073616d60448201527f6520617320706172656e74206c6963656e73650000000000000000000000000060648201526084016105fc565b60006123538a8a61231c368c90038c018c6138e9565b61232b368c90038c018c613970565b6123348b613a56565b61233d8b613a56565b61234c368c90038c018c613a80565b8a8a612778565b90506000612364602089018961314d565b73ffffffffffffffffffffffffffffffffffffffff16146123955761239561238f602089018961314d565b82612ce8565b807f35805985e00e80103ddc5df8bca16fb35a75f7f0830d5d5448f443d689e9424a6123c460208a018a61314d565b6123d460408b0160208c0161314d565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152808b013590820152606081018d905260800160405180910390a29998505050505050505050565b610d67565b600073ffffffffffffffffffffffffffffffffffffffff84163b156125f5576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a029061249e903390899088908890600401613b0b565b6020604051808303816000875af19250505080156124f7575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526124f491810190613b54565b60015b6125aa573d808015612525576040519150601f19603f3d011682016040523d82523d6000602084013e61252a565b606091505b5080516000036125a25760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016105fc565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a0200000000000000000000000000000000000000000000000000000000149050611b6d565b506001949350505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310612649577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310612675576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061269357662386f26fc10000830492506010015b6305f5e10083106126ab576305f5e100830492506008015b61271083106126bf57612710830492506004015b606483106126d1576064830492506002015b600a83106104865760010192915050565b600054610100900460ff1661275f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105fc565b606561276b8382613b71565b5060666106aa8282613b71565b855160009073ffffffffffffffffffffffffffffffffffffffff161580156127b9575060208701515173ffffffffffffffffffffffffffffffffffffffff16155b1561282c5760405162461bcd60e51b815260206004820152603460248201527f4c6963656e7365206d75737420626520626f756e6420746f206120746f6b656e60448201527f206f722061206c6963656e736520686f6c64657200000000000000000000000060648201526084016105fc565b865173ffffffffffffffffffffffffffffffffffffffff161580159061286c575060208701515173ffffffffffffffffffffffffffffffffffffffff1615155b156129055760405162461bcd60e51b815260206004820152604860248201527f4c6963656e73652063616e6e6f7420626520626f756e6420746f206120746f6b60448201527f656e20616e642061206c6963656e736520686f6c64657220617420746865207360648201527f616d652074696d65000000000000000000000000000000000000000000000000608482015260a4016105fc565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabe80547f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd91600091908261295883613c8b565b9190505590506040518061014001604052806001151581526020018d81526020018c81526020018b81526020018a6020015181526020018981526020018881526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018781526020018681525082600001600083815260200190815260200160002060008201518160000160006101000a81548160ff021916908315150217905550602082015181600101556040820151816002015560608201518160030160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff02191690831515021790555060408201518160000160026101000a81548160ff021916908315150217905550505060808201518160040160008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155505060a08201518160060160008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001019081612b369190613b71565b50505060c082015180516008830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117815560208201516009840190612b9a9082613b71565b50505060e0820151600a8201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216179091556101008401518051600b850180546020840151604085015115157c0100000000000000000000000000000000000000000000000000000000027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff9283166e010000000000000000000000000000027fffffffff00000000000000000000000000000000000000000000000000000000909416929095169190911791909117929092169190911790556060810151600c850180549190941692169190911790915560800151600d820155610120820151600e820190612cd59082613b71565b50919d9c50505050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216612d4b5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016105fc565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612dbd5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016105fc565b612dcb600083836001612422565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612e3d5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016105fc565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260686020908152604080832080546001019055848352606790915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60408051610140810182526000808252602080830182905282840182905283516060808201865283825281830184905281860184905284015283518085019094528184528301529060808201908152602001612f526040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001606081525090565b8152602001612f8a6040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001606081525090565b8152600060208083018290526040805160a081018252838152918201839052818101839052606080830184905260808301939093528301529081015290565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146119a057600080fd5b60006020828403121561300957600080fd5b813561085781612fc9565b60005b8381101561302f578181015183820152602001613017565b50506000910152565b60008151808452613050816020860160208601613014565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006108576020830184613038565b6000602082840312156130a757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146119a057600080fd5b80356130db816130ae565b919050565b600080604083850312156130f357600080fd5b82356130fe816130ae565b946020939093013593505050565b60008060006060848603121561312157600080fd5b833561312c816130ae565b9250602084013561313c816130ae565b929592945050506040919091013590565b60006020828403121561315f57600080fd5b8135610857816130ae565b803580151581146130db57600080fd5b6000806040838503121561318d57600080fd5b8235613198816130ae565b91506131a66020840161316a565b90509250929050565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152611b6d6040850182613038565b604081526131f760408201845115159052565b60208301516060820152604083015160808201526000606084015161323960a08401828051151582526020808201511515908301526040908101511515910152565b50608084015161010061326e81850183805173ffffffffffffffffffffffffffffffffffffffff168252602090810151910152565b60a0860151915061022061014085015261328c6102608501836131af565b915060c08601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc080868503016101608701526132c984836131af565b935060e088015191506132f561018087018373ffffffffffffffffffffffffffffffffffffffff169052565b9187015180516dffffffffffffffffffffffffffff9081166101a08801526020820151166101c0870152604081015115156101e0870152606081015173ffffffffffffffffffffffffffffffffffffffff1661020087015260800151610220860152506101208601518483039091016102408501526133748282613038565b92505050610857602083018473ffffffffffffffffffffffffffffffffffffffff169052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156133ec576133ec61339a565b60405290565b600082601f83011261340357600080fd5b813567ffffffffffffffff8082111561341e5761341e61339a565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156134645761346461339a565b8160405283815286602085880101111561347d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080608085870312156134b357600080fd5b84356134be816130ae565b935060208501356134ce816130ae565b925060408501359150606085013567ffffffffffffffff8111156134f157600080fd5b6134fd878288016133f2565b91505092959194509250565b6000806040838503121561351c57600080fd5b8235613527816130ae565b91506020830135613537816130ae565b809150509250929050565b6000806020838503121561355557600080fd5b823567ffffffffffffffff8082111561356d57600080fd5b818501915085601f83011261358157600080fd5b81358181111561359057600080fd5b8660208285010111156135a257600080fd5b60209290920196919550909350505050565b6000606082840312156135c657600080fd5b50919050565b6000604082840312156135c657600080fd5b600060a082840312156135c657600080fd5b6000806000806000806000806000806102408b8d03121561361057600080fd5b6136198b6130d0565b995060208b0135985060408b013597506136368c60608d016135b4565b96506136458c60c08d016135b4565b95506101208b013567ffffffffffffffff8082111561366357600080fd5b61366f8e838f016135cc565b96506101408d013591508082111561368657600080fd5b6136928e838f016135cc565b95506136a28e6101608f016135de565b94506102008d01359150808211156136b957600080fd5b506136c68d828e016133f2565b9250506136d66102208c016130d0565b90509295989b9194979a5092959850565b600181811c908216806136fb57607f821691505b6020821081036135c6577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006020828403121561374657600080fd5b8151610857816130ae565b60008351613763818460208801613014565b835190830190613777818360208801613014565b01949350505050565b601f8211156106aa57600081815260208120601f850160051c810160208610156137a75750805b601f850160051c820191505b818110156137c6578281556001016137b3565b505050505050565b67ffffffffffffffff8311156137e6576137e661339a565b6137fa836137f483546136e7565b83613780565b6000601f84116001811461384c57600085156138165750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556138e2565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561389b578685013582556020948501946001909201910161387b565b50868210156138d6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6000606082840312156138fb57600080fd5b6040516060810181811067ffffffffffffffff8211171561391e5761391e61339a565b60405261392a8361316a565b81526139386020840161316a565b60208201526139496040840161316a565b60408201529392505050565b60006020828403121561396757600080fd5b6108578261316a565b6000818303606081121561398357600080fd5b61398b6133c9565b8335613996816130ae565b815260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0830112156139c857600080fd5b6139d06133c9565b915060208401356139e0816130ae565b82526040939093013560208083019190915283015250919050565b600060408284031215613a0d57600080fd5b613a156133c9565b90508135613a22816130ae565b8152602082013567ffffffffffffffff811115613a3e57600080fd5b613a4a848285016133f2565b60208301525092915050565b600061048636836139fb565b80356dffffffffffffffffffffffffffff811681146130db57600080fd5b600060a08284031215613a9257600080fd5b60405160a0810181811067ffffffffffffffff82111715613ab557613ab561339a565b604052613ac183613a62565b8152613acf60208401613a62565b6020820152613ae06040840161316a565b60408201526060830135613af3816130ae565b60608201526080928301359281019290925250919050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152613b4a6080830184613038565b9695505050505050565b600060208284031215613b6657600080fd5b815161085781612fc9565b815167ffffffffffffffff811115613b8b57613b8b61339a565b613b9f81613b9984546136e7565b84613780565b602080601f831160018114613bf25760008415613bbc5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556137c6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613c3f57888601518255948401946001909101908401613c20565b5085821015613c7b57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ce3577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b506001019056fea2646970667358221220f2a8449fa514209b5e1e6702694c3899389b46b19e276088a7b958f0a1db3c3e64736f6c63430008130033000000000000000000000000f157916d184598105fe4681ec93be0f799599003", + "nonce": "0x43", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionType": "CREATE", + "contractName": "ERC1967Proxy", + "contractAddress": "0xFbfa729428af206a084E18328Ec923587193525d", + "function": null, + "arguments": [ + "0x8602E35ee65074eF0D2701927289705f2F61773D", + "0xf62d18880000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003f68747470733a2f2f617277656176652e6e65742f794849624b6c4642673378754b537a6c4d5f6452454738593038756f642d67574b735769394f615046734d00" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x5824f", + "value": "0x0", + "data": "0x60806040526040516104ee3803806104ee833981016040819052610022916102de565b61002e82826000610035565b50506103fb565b61003e83610061565b60008251118061004b5750805b1561005c5761005a83836100a1565b505b505050565b61006a816100cd565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606100c683836040518060600160405280602781526020016104c760279139610180565b9392505050565b6001600160a01b0381163b61013f5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080856001600160a01b03168560405161019d91906103ac565b600060405180830381855af49150503d80600081146101d8576040519150601f19603f3d011682016040523d82523d6000602084013e6101dd565b606091505b5090925090506101ef868383876101f9565b9695505050505050565b60608315610268578251600003610261576001600160a01b0385163b6102615760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610136565b5081610272565b610272838361027a565b949350505050565b81511561028a5781518083602001fd5b8060405162461bcd60e51b815260040161013691906103c8565b634e487b7160e01b600052604160045260246000fd5b60005b838110156102d55781810151838201526020016102bd565b50506000910152565b600080604083850312156102f157600080fd5b82516001600160a01b038116811461030857600080fd5b60208401519092506001600160401b038082111561032557600080fd5b818501915085601f83011261033957600080fd5b81518181111561034b5761034b6102a4565b604051601f8201601f19908116603f01168101908382118183101715610373576103736102a4565b8160405282815288602084870101111561038c57600080fd5b61039d8360208301602088016102ba565b80955050505050509250929050565b600082516103be8184602087016102ba565b9190910192915050565b60208152600082518060208401526103e78160408501602087016102ba565b601f01601f19169190910160400192915050565b60be806104096000396000f3fe608060405236601057600e6013565b005b600e5b601f601b6021565b6065565b565b600060607f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b3660008037600080366000845af43d6000803e8080156083573d6000f35b3d6000fdfea2646970667358221220a678556ae280ddf13e459fe9ecd177633ae9dbc544c45d5b523d43ba9084c8dc64736f6c63430008130033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000008602e35ee65074ef0d2701927289705f2f61773d00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000084f62d18880000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003f68747470733a2f2f617277656176652e6e65742f794849624b6c4642673378754b537a6c4d5f6452454738593038756f642d67574b735769394f615046734d0000000000000000000000000000000000000000000000000000000000", + "nonce": "0x44", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0x70610487057d530e2da659732ebdb2bdba5c5cd4aeb52ed607a50a87877d7245", + "transactionIndex": "0x0", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": null, + "cumulativeGasUsed": "0x343e52", + "gasUsed": "0x343e52", + "contractAddress": "0x8602E35ee65074eF0D2701927289705f2F61773D", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0e" + }, + { + "transactionHash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionIndex": "0x1", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": null, + "cumulativeGasUsed": "0x387b8d", + "gasUsed": "0x43d3b", + "contractAddress": "0xFbfa729428af206a084E18328Ec923587193525d", + "logs": [ + { + "address": "0xFbfa729428af206a084E18328Ec923587193525d", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000008602e35ee65074ef0d2701927289705f2f61773d" + ], + "data": "0x", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "transactionHash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionIndex": "0x1", + "logIndex": "0x0", + "removed": false + }, + { + "address": "0xFbfa729428af206a084E18328Ec923587193525d", + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "transactionHash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionIndex": "0x1", + "logIndex": "0x1", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000400000000000000000000042000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000001000000000000000000000000000000000000080000020100000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000000000000000000000000000000020000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0e" + } + ], + "libraries": [], + "pending": [], + "path": "/Users/drmanhattan/Workspace/StoryProtocol/protocol-contracts/broadcast/DevDeployments.s.sol/5/run-latest.json", + "returns": {}, + "timestamp": 1691868669, + "chain": 5, + "multi": false, + "commit": "53ab00d" +} \ No newline at end of file diff --git a/broadcast/DevDeployments.s.sol/5/run-1691868693.json b/broadcast/DevDeployments.s.sol/5/run-1691868693.json new file mode 100644 index 00000000..5ffde9c4 --- /dev/null +++ b/broadcast/DevDeployments.s.sol/5/run-1691868693.json @@ -0,0 +1,119 @@ +{ + "transactions": [ + { + "hash": "0x70610487057d530e2da659732ebdb2bdba5c5cd4aeb52ed607a50a87877d7245", + "transactionType": "CREATE", + "contractName": "LicensingModule", + "contractAddress": "0x8602E35ee65074eF0D2701927289705f2F61773D", + "function": null, + "arguments": [ + "0xf157916d184598105FE4681eC93Be0f799599003" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x43e58c", + "value": "0x0", + "data": "0x60a06040523480156200001157600080fd5b5060405162003e2838038062003e288339810160408190526200003491620000b3565b6001600160a01b038116620000a15760405162461bcd60e51b815260206004820152602960248201527f4672616e63686973652072656769737472792063616e6e6f74206265207a65726044820152686f206164647265737360b81b606482015260840160405180910390fd5b6001600160a01b0316608052620000e5565b600060208284031215620000c657600080fd5b81516001600160a01b0381168114620000de57600080fd5b9392505050565b608051613d2062000108600039600081816102a101526113d30152613d206000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806394f99b40116100cd578063c87b56dd11610081578063e985e9c511610066578063e985e9c514610338578063f62d188814610381578063f782f6751461039457600080fd5b8063c87b56dd14610312578063e705c2b81461032557600080fd5b8063a22cb465116100b2578063a22cb465146102cb578063b2dd1212146102de578063b88d4fde146102ff57600080fd5b806394f99b401461029c57806395d89b41146102c357600080fd5b806323b872dd1161012457806342842e0e1161010957806342842e0e146102635780636352211e1461027657806370a082311461028957600080fd5b806323b872dd146101e05780632fb49eff146101f357600080fd5b806301ffc9a71461015657806306fdde031461017e578063081812fc14610193578063095ea7b3146101cb575b600080fd5b610169610164366004612ff7565b6103a7565b60405190151581526020015b60405180910390f35b61018661048c565b6040516101759190613082565b6101a66101a1366004613095565b61051e565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610175565b6101de6101d93660046130e0565b610552565b005b6101de6101ee36600461310c565b6106af565b6102556102013660046130e0565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfac160209081526040808320938352929052205490565b604051908152602001610175565b6101de61027136600461310c565b610736565b6101a6610284366004613095565b610751565b61025561029736600461314d565b610867565b6101a67f000000000000000000000000000000000000000000000000000000000000000081565b61018661091b565b6101de6102d936600461317a565b61092a565b6102f16102ec366004613095565b610939565b6040516101759291906131e4565b6101de61030d36600461349d565b610cdf565b610186610320366004613095565b610d6d565b610169610333366004613095565b610de0565b610169610346366004613509565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152606a6020908152604080832093909416825291909152205460ff1690565b6101de61038f366004613542565b6111a7565b6102556103a23660046135f0565b6113b9565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061043a57507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061048657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60606065805461049b906136e7565b80601f01602080910402602001604051908101604052809291908181526020018280546104c7906136e7565b80156105145780601f106104e957610100808354040283529160200191610514565b820191906000526020600020905b8154815290600101906020018083116104f757829003601f168201915b5050505050905090565b60006105298261192f565b5060009081526069602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b600061055d826119a3565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036106055760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216148061062e575061062e8133610346565b6106a05760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016105fc565b6106aa8383611a15565b505050565b6106b93382611ab5565b61072b5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016105fc565b6106aa838383611b75565b6106aa83838360405180602001604052806000815250610cdf565b60008181527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd602090815260408083208151808301909252600481015473ffffffffffffffffffffffffffffffffffffffff1680835260059091015492820192909252901561085e57805160208201516040517f6352211e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90921691636352211e916108169160040190815260200190565b602060405180830381865afa158015610833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108579190613734565b9392505050565b610857836119a3565b600073ffffffffffffffffffffffffffffffffffffffff82166108f25760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e6572000000000000000000000000000000000000000000000060648201526084016105fc565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526068602052604090205490565b60606066805461049b906136e7565b610935338383611e2f565b5050565b610941612ecd565b60008281527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd6020526040812061097784610751565b6040805161014081018252835460ff908116151582526001850154602080840191909152600286015483850152835160608181018652600388015480851615158352610100810485161515838501526201000090049093161515818601529183019190915282518084018452600486015473ffffffffffffffffffffffffffffffffffffffff90811682526005870154828401526080840191909152835180850190945260068601805490911684526007860180549394879460a08701949193929184019190610a46906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610a72906136e7565b8015610abf5780601f10610a9457610100808354040283529160200191610abf565b820191906000526020600020905b815481529060010190602001808311610aa257829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190610b0c906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610b38906136e7565b8015610b855780601f10610b5a57610100808354040283529160200191610b85565b820191906000526020600020905b815481529060010190602001808311610b6857829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e83018054919092019190610c52906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610c7e906136e7565b8015610ccb5780601f10610ca057610100808354040283529160200191610ccb565b820191906000526020600020905b815481529060010190602001808311610cae57829003601f168201915b505050505081525050915091509150915091565b610ce93383611ab5565b610d5b5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016105fc565b610d6784848484611f42565b50505050565b6060610d788261192f565b6000610d8f60408051602081019091526000815290565b90506000815111610daf5760405180602001604052806000815250610857565b80610db984611fcb565b604051602001610dca929190613751565b6040516020818303038152906040529392505050565b600081600003610df257506000919050565b811561119f5760008281527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd60208181526040808420815161014081018352815460ff90811615158252600183015482860152600283015482850152835160608082018652600385015480841615158352610100810484161515838901526201000090049092161515818601529082015282518084018452600483015473ffffffffffffffffffffffffffffffffffffffff90811682526005840154828701526080830191909152835180850190945260068301805490911684526007830180549697969295939460a087019490939084019190610eef906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610f1b906136e7565b8015610f685780601f10610f3d57610100808354040283529160200191610f68565b820191906000526020600020905b815481529060010190602001808311610f4b57829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190610fb5906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610fe1906136e7565b801561102e5780601f106110035761010080835404028352916020019161102e565b820191906000526020600020905b81548152906001019060200180831161101157829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e830180549190920191906110fb906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611127906136e7565b80156111745780601f1061114957610100808354040283529160200191611174565b820191906000526020600020905b81548152906001019060200180831161115757829003601f168201915b50505050508152505090508060000151611192575060009392505050565b602001519250610df29050565b506001919050565b600054610100900460ff16158080156111c75750600054600160ff909116105b806111e15750303b1580156111e1575060005460ff166001145b6112535760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016105fc565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156112b157600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6113256040518060400160405280601681526020017f53746f72792050726f746f636f6c204c6963656e7365000000000000000000008152506040518060400160405280600381526020017f53504c0000000000000000000000000000000000000000000000000000000000815250612089565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfac26113518385836137ce565b5080156106aa57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b60003373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146114655760405162461bcd60e51b8152602060048201526024808201527f53656e646572206973206e6f7420746865206672616e6368697365207265676960448201527f737472790000000000000000000000000000000000000000000000000000000060648201526084016105fc565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd8a156118175760008b815260208281526040808320815161014081018352815460ff90811615158252600183015482860152600283015482850152835160608181018652600385015480841615158352610100810484161515838901526201000090049092161515818601529082015282518084018452600483015473ffffffffffffffffffffffffffffffffffffffff90811682526005840154828701526080830191909152835180850190945260068301805490911684526007830180549295939460a08701949093908401919061155f906136e7565b80601f016020809104026020016040519081016040528092919081815260200182805461158b906136e7565b80156115d85780601f106115ad576101008083540402835291602001916115d8565b820191906000526020600020905b8154815290600101906020018083116115bb57829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190611625906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611651906136e7565b801561169e5780601f106116735761010080835404028352916020019161169e565b820191906000526020600020905b81548152906001019060200180831161168157829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e8301805491909201919061176b906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611797906136e7565b80156117e45780601f106117b9576101008083540402835291602001916117e4565b820191906000526020600020905b8154815290600101906020018083116117c757829003601f168201915b505050505081525050905061180b8c8e838d80360381019061180691906138e9565b612110565b610120015193506118bb565b61182760608a0160408b01613955565b6118bb5780600501805461183a906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611866906136e7565b80156118b35780601f10611888576101008083540402835291602001916118b3565b820191906000526020600020905b81548152906001019060200180831161189657829003601f168201915b505050505093505b60006118ce8c8c8c8c8c8c8c8c8c612306565b9050806004830160006118e760408d0160208e0161314d565b73ffffffffffffffffffffffffffffffffffffffff168152602080820192909252604090810160009081208d83013582529092529020559150509a9950505050505050505050565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff166119a05760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016105fc565b50565b60008181526067602052604081205473ffffffffffffffffffffffffffffffffffffffff16806104865760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016105fc565b600081815260696020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091558190611a6f826119a3565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600080611ac1836119a3565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611b2f575073ffffffffffffffffffffffffffffffffffffffff8082166000908152606a602090815260408083209388168352929052205460ff165b80611b6d57508373ffffffffffffffffffffffffffffffffffffffff16611b558461051e565b73ffffffffffffffffffffffffffffffffffffffff16145b949350505050565b8273ffffffffffffffffffffffffffffffffffffffff16611b95826119a3565b73ffffffffffffffffffffffffffffffffffffffff1614611c1e5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016105fc565b73ffffffffffffffffffffffffffffffffffffffff8216611ca65760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016105fc565b611cb38383836001612422565b8273ffffffffffffffffffffffffffffffffffffffff16611cd3826119a3565b73ffffffffffffffffffffffffffffffffffffffff1614611d5c5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016105fc565b600081815260696020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811690915573ffffffffffffffffffffffffffffffffffffffff8781168086526068855283862080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01905590871680865283862080546001019055868652606790945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611eaa5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016105fc565b73ffffffffffffffffffffffffffffffffffffffff8381166000818152606a602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611f4d848484611b75565b611f5984848484612427565b610d675760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016105fc565b60606000611fd883612600565b600101905060008167ffffffffffffffff811115611ff857611ff861339a565b6040519080825280601f01601f191660200182016040528015612022576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461202c57509392505050565b600054610100900460ff166121065760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105fc565b61093582826126e2565b8273ffffffffffffffffffffffffffffffffffffffff1661213085610751565b73ffffffffffffffffffffffffffffffffffffffff16146121b95760405162461bcd60e51b815260206004820152602d60248201527f53656e646572206973206e6f7420746865206f776e6572206f6620746865207060448201527f6172656e74206c6963656e73650000000000000000000000000000000000000060648201526084016105fc565b81516122075760405162461bcd60e51b815260206004820152601c60248201527f506172656e74206c6963656e7365206973206e6f74206163746976650000000060448201526064016105fc565b8160600151602001516122815760405162461bcd60e51b8152602060048201526024808201527f506172656e74206c6963656e73652063616e6e6f74206265207375626c69636560448201527f6e7365640000000000000000000000000000000000000000000000000000000060648201526084016105fc565b80604001511515826060015160400151151514610d675760405162461bcd60e51b815260206004820152603360248201527f436f6d6d65726369616c207465726d73206d757374206265207468652073616d60448201527f6520617320706172656e74206c6963656e73650000000000000000000000000060648201526084016105fc565b60006123538a8a61231c368c90038c018c6138e9565b61232b368c90038c018c613970565b6123348b613a56565b61233d8b613a56565b61234c368c90038c018c613a80565b8a8a612778565b90506000612364602089018961314d565b73ffffffffffffffffffffffffffffffffffffffff16146123955761239561238f602089018961314d565b82612ce8565b807f35805985e00e80103ddc5df8bca16fb35a75f7f0830d5d5448f443d689e9424a6123c460208a018a61314d565b6123d460408b0160208c0161314d565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152808b013590820152606081018d905260800160405180910390a29998505050505050505050565b610d67565b600073ffffffffffffffffffffffffffffffffffffffff84163b156125f5576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a029061249e903390899088908890600401613b0b565b6020604051808303816000875af19250505080156124f7575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526124f491810190613b54565b60015b6125aa573d808015612525576040519150601f19603f3d011682016040523d82523d6000602084013e61252a565b606091505b5080516000036125a25760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016105fc565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a0200000000000000000000000000000000000000000000000000000000149050611b6d565b506001949350505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310612649577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310612675576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061269357662386f26fc10000830492506010015b6305f5e10083106126ab576305f5e100830492506008015b61271083106126bf57612710830492506004015b606483106126d1576064830492506002015b600a83106104865760010192915050565b600054610100900460ff1661275f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105fc565b606561276b8382613b71565b5060666106aa8282613b71565b855160009073ffffffffffffffffffffffffffffffffffffffff161580156127b9575060208701515173ffffffffffffffffffffffffffffffffffffffff16155b1561282c5760405162461bcd60e51b815260206004820152603460248201527f4c6963656e7365206d75737420626520626f756e6420746f206120746f6b656e60448201527f206f722061206c6963656e736520686f6c64657200000000000000000000000060648201526084016105fc565b865173ffffffffffffffffffffffffffffffffffffffff161580159061286c575060208701515173ffffffffffffffffffffffffffffffffffffffff1615155b156129055760405162461bcd60e51b815260206004820152604860248201527f4c6963656e73652063616e6e6f7420626520626f756e6420746f206120746f6b60448201527f656e20616e642061206c6963656e736520686f6c64657220617420746865207360648201527f616d652074696d65000000000000000000000000000000000000000000000000608482015260a4016105fc565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabe80547f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd91600091908261295883613c8b565b9190505590506040518061014001604052806001151581526020018d81526020018c81526020018b81526020018a6020015181526020018981526020018881526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018781526020018681525082600001600083815260200190815260200160002060008201518160000160006101000a81548160ff021916908315150217905550602082015181600101556040820151816002015560608201518160030160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff02191690831515021790555060408201518160000160026101000a81548160ff021916908315150217905550505060808201518160040160008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155505060a08201518160060160008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001019081612b369190613b71565b50505060c082015180516008830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117815560208201516009840190612b9a9082613b71565b50505060e0820151600a8201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216179091556101008401518051600b850180546020840151604085015115157c0100000000000000000000000000000000000000000000000000000000027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff9283166e010000000000000000000000000000027fffffffff00000000000000000000000000000000000000000000000000000000909416929095169190911791909117929092169190911790556060810151600c850180549190941692169190911790915560800151600d820155610120820151600e820190612cd59082613b71565b50919d9c50505050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216612d4b5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016105fc565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612dbd5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016105fc565b612dcb600083836001612422565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612e3d5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016105fc565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260686020908152604080832080546001019055848352606790915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60408051610140810182526000808252602080830182905282840182905283516060808201865283825281830184905281860184905284015283518085019094528184528301529060808201908152602001612f526040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001606081525090565b8152602001612f8a6040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001606081525090565b8152600060208083018290526040805160a081018252838152918201839052818101839052606080830184905260808301939093528301529081015290565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146119a057600080fd5b60006020828403121561300957600080fd5b813561085781612fc9565b60005b8381101561302f578181015183820152602001613017565b50506000910152565b60008151808452613050816020860160208601613014565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006108576020830184613038565b6000602082840312156130a757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146119a057600080fd5b80356130db816130ae565b919050565b600080604083850312156130f357600080fd5b82356130fe816130ae565b946020939093013593505050565b60008060006060848603121561312157600080fd5b833561312c816130ae565b9250602084013561313c816130ae565b929592945050506040919091013590565b60006020828403121561315f57600080fd5b8135610857816130ae565b803580151581146130db57600080fd5b6000806040838503121561318d57600080fd5b8235613198816130ae565b91506131a66020840161316a565b90509250929050565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152611b6d6040850182613038565b604081526131f760408201845115159052565b60208301516060820152604083015160808201526000606084015161323960a08401828051151582526020808201511515908301526040908101511515910152565b50608084015161010061326e81850183805173ffffffffffffffffffffffffffffffffffffffff168252602090810151910152565b60a0860151915061022061014085015261328c6102608501836131af565b915060c08601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc080868503016101608701526132c984836131af565b935060e088015191506132f561018087018373ffffffffffffffffffffffffffffffffffffffff169052565b9187015180516dffffffffffffffffffffffffffff9081166101a08801526020820151166101c0870152604081015115156101e0870152606081015173ffffffffffffffffffffffffffffffffffffffff1661020087015260800151610220860152506101208601518483039091016102408501526133748282613038565b92505050610857602083018473ffffffffffffffffffffffffffffffffffffffff169052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156133ec576133ec61339a565b60405290565b600082601f83011261340357600080fd5b813567ffffffffffffffff8082111561341e5761341e61339a565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156134645761346461339a565b8160405283815286602085880101111561347d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080608085870312156134b357600080fd5b84356134be816130ae565b935060208501356134ce816130ae565b925060408501359150606085013567ffffffffffffffff8111156134f157600080fd5b6134fd878288016133f2565b91505092959194509250565b6000806040838503121561351c57600080fd5b8235613527816130ae565b91506020830135613537816130ae565b809150509250929050565b6000806020838503121561355557600080fd5b823567ffffffffffffffff8082111561356d57600080fd5b818501915085601f83011261358157600080fd5b81358181111561359057600080fd5b8660208285010111156135a257600080fd5b60209290920196919550909350505050565b6000606082840312156135c657600080fd5b50919050565b6000604082840312156135c657600080fd5b600060a082840312156135c657600080fd5b6000806000806000806000806000806102408b8d03121561361057600080fd5b6136198b6130d0565b995060208b0135985060408b013597506136368c60608d016135b4565b96506136458c60c08d016135b4565b95506101208b013567ffffffffffffffff8082111561366357600080fd5b61366f8e838f016135cc565b96506101408d013591508082111561368657600080fd5b6136928e838f016135cc565b95506136a28e6101608f016135de565b94506102008d01359150808211156136b957600080fd5b506136c68d828e016133f2565b9250506136d66102208c016130d0565b90509295989b9194979a5092959850565b600181811c908216806136fb57607f821691505b6020821081036135c6577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006020828403121561374657600080fd5b8151610857816130ae565b60008351613763818460208801613014565b835190830190613777818360208801613014565b01949350505050565b601f8211156106aa57600081815260208120601f850160051c810160208610156137a75750805b601f850160051c820191505b818110156137c6578281556001016137b3565b505050505050565b67ffffffffffffffff8311156137e6576137e661339a565b6137fa836137f483546136e7565b83613780565b6000601f84116001811461384c57600085156138165750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556138e2565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561389b578685013582556020948501946001909201910161387b565b50868210156138d6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6000606082840312156138fb57600080fd5b6040516060810181811067ffffffffffffffff8211171561391e5761391e61339a565b60405261392a8361316a565b81526139386020840161316a565b60208201526139496040840161316a565b60408201529392505050565b60006020828403121561396757600080fd5b6108578261316a565b6000818303606081121561398357600080fd5b61398b6133c9565b8335613996816130ae565b815260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0830112156139c857600080fd5b6139d06133c9565b915060208401356139e0816130ae565b82526040939093013560208083019190915283015250919050565b600060408284031215613a0d57600080fd5b613a156133c9565b90508135613a22816130ae565b8152602082013567ffffffffffffffff811115613a3e57600080fd5b613a4a848285016133f2565b60208301525092915050565b600061048636836139fb565b80356dffffffffffffffffffffffffffff811681146130db57600080fd5b600060a08284031215613a9257600080fd5b60405160a0810181811067ffffffffffffffff82111715613ab557613ab561339a565b604052613ac183613a62565b8152613acf60208401613a62565b6020820152613ae06040840161316a565b60408201526060830135613af3816130ae565b60608201526080928301359281019290925250919050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152613b4a6080830184613038565b9695505050505050565b600060208284031215613b6657600080fd5b815161085781612fc9565b815167ffffffffffffffff811115613b8b57613b8b61339a565b613b9f81613b9984546136e7565b84613780565b602080601f831160018114613bf25760008415613bbc5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556137c6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613c3f57888601518255948401946001909101908401613c20565b5085821015613c7b57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ce3577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b506001019056fea2646970667358221220f2a8449fa514209b5e1e6702694c3899389b46b19e276088a7b958f0a1db3c3e64736f6c63430008130033000000000000000000000000f157916d184598105fe4681ec93be0f799599003", + "nonce": "0x43", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionType": "CREATE", + "contractName": "ERC1967Proxy", + "contractAddress": "0xFbfa729428af206a084E18328Ec923587193525d", + "function": null, + "arguments": [ + "0x8602E35ee65074eF0D2701927289705f2F61773D", + "0xf62d18880000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003f68747470733a2f2f617277656176652e6e65742f794849624b6c4642673378754b537a6c4d5f6452454738593038756f642d67574b735769394f615046734d00" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x5824f", + "value": "0x0", + "data": "0x60806040526040516104ee3803806104ee833981016040819052610022916102de565b61002e82826000610035565b50506103fb565b61003e83610061565b60008251118061004b5750805b1561005c5761005a83836100a1565b505b505050565b61006a816100cd565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606100c683836040518060600160405280602781526020016104c760279139610180565b9392505050565b6001600160a01b0381163b61013f5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080856001600160a01b03168560405161019d91906103ac565b600060405180830381855af49150503d80600081146101d8576040519150601f19603f3d011682016040523d82523d6000602084013e6101dd565b606091505b5090925090506101ef868383876101f9565b9695505050505050565b60608315610268578251600003610261576001600160a01b0385163b6102615760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610136565b5081610272565b610272838361027a565b949350505050565b81511561028a5781518083602001fd5b8060405162461bcd60e51b815260040161013691906103c8565b634e487b7160e01b600052604160045260246000fd5b60005b838110156102d55781810151838201526020016102bd565b50506000910152565b600080604083850312156102f157600080fd5b82516001600160a01b038116811461030857600080fd5b60208401519092506001600160401b038082111561032557600080fd5b818501915085601f83011261033957600080fd5b81518181111561034b5761034b6102a4565b604051601f8201601f19908116603f01168101908382118183101715610373576103736102a4565b8160405282815288602084870101111561038c57600080fd5b61039d8360208301602088016102ba565b80955050505050509250929050565b600082516103be8184602087016102ba565b9190910192915050565b60208152600082518060208401526103e78160408501602087016102ba565b601f01601f19169190910160400192915050565b60be806104096000396000f3fe608060405236601057600e6013565b005b600e5b601f601b6021565b6065565b565b600060607f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b3660008037600080366000845af43d6000803e8080156083573d6000f35b3d6000fdfea2646970667358221220a678556ae280ddf13e459fe9ecd177633ae9dbc544c45d5b523d43ba9084c8dc64736f6c63430008130033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000008602e35ee65074ef0d2701927289705f2f61773d00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000084f62d18880000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003f68747470733a2f2f617277656176652e6e65742f794849624b6c4642673378754b537a6c4d5f6452454738593038756f642d67574b735769394f615046734d0000000000000000000000000000000000000000000000000000000000", + "nonce": "0x44", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0x70610487057d530e2da659732ebdb2bdba5c5cd4aeb52ed607a50a87877d7245", + "transactionIndex": "0x0", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": null, + "cumulativeGasUsed": "0x343e52", + "gasUsed": "0x343e52", + "contractAddress": "0x8602E35ee65074eF0D2701927289705f2F61773D", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0e" + }, + { + "transactionHash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionIndex": "0x1", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": null, + "cumulativeGasUsed": "0x387b8d", + "gasUsed": "0x43d3b", + "contractAddress": "0xFbfa729428af206a084E18328Ec923587193525d", + "logs": [ + { + "address": "0xFbfa729428af206a084E18328Ec923587193525d", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000008602e35ee65074ef0d2701927289705f2f61773d" + ], + "data": "0x", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "transactionHash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionIndex": "0x1", + "logIndex": "0x0", + "removed": false + }, + { + "address": "0xFbfa729428af206a084E18328Ec923587193525d", + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "transactionHash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionIndex": "0x1", + "logIndex": "0x1", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000400000000000000000000042000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000001000000000000000000000000000000000000080000020100000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000000000000000000000000000000020000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0e" + } + ], + "libraries": [], + "pending": [], + "path": "/Users/drmanhattan/Workspace/StoryProtocol/protocol-contracts/broadcast/DevDeployments.s.sol/5/run-latest.json", + "returns": {}, + "timestamp": 1691868693, + "chain": 5, + "multi": false, + "commit": "53ab00d" +} \ No newline at end of file diff --git a/broadcast/DevDeployments.s.sol/5/run-latest.json b/broadcast/DevDeployments.s.sol/5/run-latest.json new file mode 100644 index 00000000..5ffde9c4 --- /dev/null +++ b/broadcast/DevDeployments.s.sol/5/run-latest.json @@ -0,0 +1,119 @@ +{ + "transactions": [ + { + "hash": "0x70610487057d530e2da659732ebdb2bdba5c5cd4aeb52ed607a50a87877d7245", + "transactionType": "CREATE", + "contractName": "LicensingModule", + "contractAddress": "0x8602E35ee65074eF0D2701927289705f2F61773D", + "function": null, + "arguments": [ + "0xf157916d184598105FE4681eC93Be0f799599003" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x43e58c", + "value": "0x0", + "data": "0x60a06040523480156200001157600080fd5b5060405162003e2838038062003e288339810160408190526200003491620000b3565b6001600160a01b038116620000a15760405162461bcd60e51b815260206004820152602960248201527f4672616e63686973652072656769737472792063616e6e6f74206265207a65726044820152686f206164647265737360b81b606482015260840160405180910390fd5b6001600160a01b0316608052620000e5565b600060208284031215620000c657600080fd5b81516001600160a01b0381168114620000de57600080fd5b9392505050565b608051613d2062000108600039600081816102a101526113d30152613d206000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806394f99b40116100cd578063c87b56dd11610081578063e985e9c511610066578063e985e9c514610338578063f62d188814610381578063f782f6751461039457600080fd5b8063c87b56dd14610312578063e705c2b81461032557600080fd5b8063a22cb465116100b2578063a22cb465146102cb578063b2dd1212146102de578063b88d4fde146102ff57600080fd5b806394f99b401461029c57806395d89b41146102c357600080fd5b806323b872dd1161012457806342842e0e1161010957806342842e0e146102635780636352211e1461027657806370a082311461028957600080fd5b806323b872dd146101e05780632fb49eff146101f357600080fd5b806301ffc9a71461015657806306fdde031461017e578063081812fc14610193578063095ea7b3146101cb575b600080fd5b610169610164366004612ff7565b6103a7565b60405190151581526020015b60405180910390f35b61018661048c565b6040516101759190613082565b6101a66101a1366004613095565b61051e565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610175565b6101de6101d93660046130e0565b610552565b005b6101de6101ee36600461310c565b6106af565b6102556102013660046130e0565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfac160209081526040808320938352929052205490565b604051908152602001610175565b6101de61027136600461310c565b610736565b6101a6610284366004613095565b610751565b61025561029736600461314d565b610867565b6101a67f000000000000000000000000000000000000000000000000000000000000000081565b61018661091b565b6101de6102d936600461317a565b61092a565b6102f16102ec366004613095565b610939565b6040516101759291906131e4565b6101de61030d36600461349d565b610cdf565b610186610320366004613095565b610d6d565b610169610333366004613095565b610de0565b610169610346366004613509565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152606a6020908152604080832093909416825291909152205460ff1690565b6101de61038f366004613542565b6111a7565b6102556103a23660046135f0565b6113b9565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061043a57507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061048657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60606065805461049b906136e7565b80601f01602080910402602001604051908101604052809291908181526020018280546104c7906136e7565b80156105145780601f106104e957610100808354040283529160200191610514565b820191906000526020600020905b8154815290600101906020018083116104f757829003601f168201915b5050505050905090565b60006105298261192f565b5060009081526069602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b600061055d826119a3565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036106055760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216148061062e575061062e8133610346565b6106a05760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016105fc565b6106aa8383611a15565b505050565b6106b93382611ab5565b61072b5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016105fc565b6106aa838383611b75565b6106aa83838360405180602001604052806000815250610cdf565b60008181527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd602090815260408083208151808301909252600481015473ffffffffffffffffffffffffffffffffffffffff1680835260059091015492820192909252901561085e57805160208201516040517f6352211e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90921691636352211e916108169160040190815260200190565b602060405180830381865afa158015610833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108579190613734565b9392505050565b610857836119a3565b600073ffffffffffffffffffffffffffffffffffffffff82166108f25760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e6572000000000000000000000000000000000000000000000060648201526084016105fc565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526068602052604090205490565b60606066805461049b906136e7565b610935338383611e2f565b5050565b610941612ecd565b60008281527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd6020526040812061097784610751565b6040805161014081018252835460ff908116151582526001850154602080840191909152600286015483850152835160608181018652600388015480851615158352610100810485161515838501526201000090049093161515818601529183019190915282518084018452600486015473ffffffffffffffffffffffffffffffffffffffff90811682526005870154828401526080840191909152835180850190945260068601805490911684526007860180549394879460a08701949193929184019190610a46906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610a72906136e7565b8015610abf5780601f10610a9457610100808354040283529160200191610abf565b820191906000526020600020905b815481529060010190602001808311610aa257829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190610b0c906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610b38906136e7565b8015610b855780601f10610b5a57610100808354040283529160200191610b85565b820191906000526020600020905b815481529060010190602001808311610b6857829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e83018054919092019190610c52906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610c7e906136e7565b8015610ccb5780601f10610ca057610100808354040283529160200191610ccb565b820191906000526020600020905b815481529060010190602001808311610cae57829003601f168201915b505050505081525050915091509150915091565b610ce93383611ab5565b610d5b5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016105fc565b610d6784848484611f42565b50505050565b6060610d788261192f565b6000610d8f60408051602081019091526000815290565b90506000815111610daf5760405180602001604052806000815250610857565b80610db984611fcb565b604051602001610dca929190613751565b6040516020818303038152906040529392505050565b600081600003610df257506000919050565b811561119f5760008281527f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd60208181526040808420815161014081018352815460ff90811615158252600183015482860152600283015482850152835160608082018652600385015480841615158352610100810484161515838901526201000090049092161515818601529082015282518084018452600483015473ffffffffffffffffffffffffffffffffffffffff90811682526005840154828701526080830191909152835180850190945260068301805490911684526007830180549697969295939460a087019490939084019190610eef906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610f1b906136e7565b8015610f685780601f10610f3d57610100808354040283529160200191610f68565b820191906000526020600020905b815481529060010190602001808311610f4b57829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190610fb5906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610fe1906136e7565b801561102e5780601f106110035761010080835404028352916020019161102e565b820191906000526020600020905b81548152906001019060200180831161101157829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e830180549190920191906110fb906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611127906136e7565b80156111745780601f1061114957610100808354040283529160200191611174565b820191906000526020600020905b81548152906001019060200180831161115757829003601f168201915b50505050508152505090508060000151611192575060009392505050565b602001519250610df29050565b506001919050565b600054610100900460ff16158080156111c75750600054600160ff909116105b806111e15750303b1580156111e1575060005460ff166001145b6112535760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016105fc565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156112b157600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6113256040518060400160405280601681526020017f53746f72792050726f746f636f6c204c6963656e7365000000000000000000008152506040518060400160405280600381526020017f53504c0000000000000000000000000000000000000000000000000000000000815250612089565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfac26113518385836137ce565b5080156106aa57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b60003373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146114655760405162461bcd60e51b8152602060048201526024808201527f53656e646572206973206e6f7420746865206672616e6368697365207265676960448201527f737472790000000000000000000000000000000000000000000000000000000060648201526084016105fc565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd8a156118175760008b815260208281526040808320815161014081018352815460ff90811615158252600183015482860152600283015482850152835160608181018652600385015480841615158352610100810484161515838901526201000090049092161515818601529082015282518084018452600483015473ffffffffffffffffffffffffffffffffffffffff90811682526005840154828701526080830191909152835180850190945260068301805490911684526007830180549295939460a08701949093908401919061155f906136e7565b80601f016020809104026020016040519081016040528092919081815260200182805461158b906136e7565b80156115d85780601f106115ad576101008083540402835291602001916115d8565b820191906000526020600020905b8154815290600101906020018083116115bb57829003601f168201915b5050509190925250505081526040805180820190915260088301805473ffffffffffffffffffffffffffffffffffffffff16825260098401805460209485019484019190611625906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611651906136e7565b801561169e5780601f106116735761010080835404028352916020019161169e565b820191906000526020600020905b81548152906001019060200180831161168157829003601f168201915b505050919092525050508152600a82015473ffffffffffffffffffffffffffffffffffffffff9081166020808401919091526040805160a081018252600b8601546dffffffffffffffffffffffffffff80821683526e010000000000000000000000000000820416938201939093527c010000000000000000000000000000000000000000000000000000000090920460ff16151582820152600c850154909216606080830191909152600d850154608083015291830152600e8301805491909201919061176b906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611797906136e7565b80156117e45780601f106117b9576101008083540402835291602001916117e4565b820191906000526020600020905b8154815290600101906020018083116117c757829003601f168201915b505050505081525050905061180b8c8e838d80360381019061180691906138e9565b612110565b610120015193506118bb565b61182760608a0160408b01613955565b6118bb5780600501805461183a906136e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611866906136e7565b80156118b35780601f10611888576101008083540402835291602001916118b3565b820191906000526020600020905b81548152906001019060200180831161189657829003601f168201915b505050505093505b60006118ce8c8c8c8c8c8c8c8c8c612306565b9050806004830160006118e760408d0160208e0161314d565b73ffffffffffffffffffffffffffffffffffffffff168152602080820192909252604090810160009081208d83013582529092529020559150509a9950505050505050505050565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff166119a05760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016105fc565b50565b60008181526067602052604081205473ffffffffffffffffffffffffffffffffffffffff16806104865760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016105fc565b600081815260696020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091558190611a6f826119a3565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600080611ac1836119a3565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611b2f575073ffffffffffffffffffffffffffffffffffffffff8082166000908152606a602090815260408083209388168352929052205460ff165b80611b6d57508373ffffffffffffffffffffffffffffffffffffffff16611b558461051e565b73ffffffffffffffffffffffffffffffffffffffff16145b949350505050565b8273ffffffffffffffffffffffffffffffffffffffff16611b95826119a3565b73ffffffffffffffffffffffffffffffffffffffff1614611c1e5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016105fc565b73ffffffffffffffffffffffffffffffffffffffff8216611ca65760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016105fc565b611cb38383836001612422565b8273ffffffffffffffffffffffffffffffffffffffff16611cd3826119a3565b73ffffffffffffffffffffffffffffffffffffffff1614611d5c5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016105fc565b600081815260696020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811690915573ffffffffffffffffffffffffffffffffffffffff8781168086526068855283862080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01905590871680865283862080546001019055868652606790945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611eaa5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016105fc565b73ffffffffffffffffffffffffffffffffffffffff8381166000818152606a602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611f4d848484611b75565b611f5984848484612427565b610d675760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016105fc565b60606000611fd883612600565b600101905060008167ffffffffffffffff811115611ff857611ff861339a565b6040519080825280601f01601f191660200182016040528015612022576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461202c57509392505050565b600054610100900460ff166121065760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105fc565b61093582826126e2565b8273ffffffffffffffffffffffffffffffffffffffff1661213085610751565b73ffffffffffffffffffffffffffffffffffffffff16146121b95760405162461bcd60e51b815260206004820152602d60248201527f53656e646572206973206e6f7420746865206f776e6572206f6620746865207060448201527f6172656e74206c6963656e73650000000000000000000000000000000000000060648201526084016105fc565b81516122075760405162461bcd60e51b815260206004820152601c60248201527f506172656e74206c6963656e7365206973206e6f74206163746976650000000060448201526064016105fc565b8160600151602001516122815760405162461bcd60e51b8152602060048201526024808201527f506172656e74206c6963656e73652063616e6e6f74206265207375626c69636560448201527f6e7365640000000000000000000000000000000000000000000000000000000060648201526084016105fc565b80604001511515826060015160400151151514610d675760405162461bcd60e51b815260206004820152603360248201527f436f6d6d65726369616c207465726d73206d757374206265207468652073616d60448201527f6520617320706172656e74206c6963656e73650000000000000000000000000060648201526084016105fc565b60006123538a8a61231c368c90038c018c6138e9565b61232b368c90038c018c613970565b6123348b613a56565b61233d8b613a56565b61234c368c90038c018c613a80565b8a8a612778565b90506000612364602089018961314d565b73ffffffffffffffffffffffffffffffffffffffff16146123955761239561238f602089018961314d565b82612ce8565b807f35805985e00e80103ddc5df8bca16fb35a75f7f0830d5d5448f443d689e9424a6123c460208a018a61314d565b6123d460408b0160208c0161314d565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152808b013590820152606081018d905260800160405180910390a29998505050505050505050565b610d67565b600073ffffffffffffffffffffffffffffffffffffffff84163b156125f5576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a029061249e903390899088908890600401613b0b565b6020604051808303816000875af19250505080156124f7575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526124f491810190613b54565b60015b6125aa573d808015612525576040519150601f19603f3d011682016040523d82523d6000602084013e61252a565b606091505b5080516000036125a25760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016105fc565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a0200000000000000000000000000000000000000000000000000000000149050611b6d565b506001949350505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310612649577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310612675576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061269357662386f26fc10000830492506010015b6305f5e10083106126ab576305f5e100830492506008015b61271083106126bf57612710830492506004015b606483106126d1576064830492506002015b600a83106104865760010192915050565b600054610100900460ff1661275f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105fc565b606561276b8382613b71565b5060666106aa8282613b71565b855160009073ffffffffffffffffffffffffffffffffffffffff161580156127b9575060208701515173ffffffffffffffffffffffffffffffffffffffff16155b1561282c5760405162461bcd60e51b815260206004820152603460248201527f4c6963656e7365206d75737420626520626f756e6420746f206120746f6b656e60448201527f206f722061206c6963656e736520686f6c64657200000000000000000000000060648201526084016105fc565b865173ffffffffffffffffffffffffffffffffffffffff161580159061286c575060208701515173ffffffffffffffffffffffffffffffffffffffff1615155b156129055760405162461bcd60e51b815260206004820152604860248201527f4c6963656e73652063616e6e6f7420626520626f756e6420746f206120746f6b60448201527f656e20616e642061206c6963656e736520686f6c64657220617420746865207360648201527f616d652074696d65000000000000000000000000000000000000000000000000608482015260a4016105fc565b7f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabe80547f778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd91600091908261295883613c8b565b9190505590506040518061014001604052806001151581526020018d81526020018c81526020018b81526020018a6020015181526020018981526020018881526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018781526020018681525082600001600083815260200190815260200160002060008201518160000160006101000a81548160ff021916908315150217905550602082015181600101556040820151816002015560608201518160030160008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548160ff02191690831515021790555060408201518160000160026101000a81548160ff021916908315150217905550505060808201518160040160008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155505060a08201518160060160008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001019081612b369190613b71565b50505060c082015180516008830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117815560208201516009840190612b9a9082613b71565b50505060e0820151600a8201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216179091556101008401518051600b850180546020840151604085015115157c0100000000000000000000000000000000000000000000000000000000027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff9283166e010000000000000000000000000000027fffffffff00000000000000000000000000000000000000000000000000000000909416929095169190911791909117929092169190911790556060810151600c850180549190941692169190911790915560800151600d820155610120820151600e820190612cd59082613b71565b50919d9c50505050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216612d4b5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016105fc565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612dbd5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016105fc565b612dcb600083836001612422565b60008181526067602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612e3d5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016105fc565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260686020908152604080832080546001019055848352606790915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60408051610140810182526000808252602080830182905282840182905283516060808201865283825281830184905281860184905284015283518085019094528184528301529060808201908152602001612f526040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001606081525090565b8152602001612f8a6040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001606081525090565b8152600060208083018290526040805160a081018252838152918201839052818101839052606080830184905260808301939093528301529081015290565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146119a057600080fd5b60006020828403121561300957600080fd5b813561085781612fc9565b60005b8381101561302f578181015183820152602001613017565b50506000910152565b60008151808452613050816020860160208601613014565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006108576020830184613038565b6000602082840312156130a757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146119a057600080fd5b80356130db816130ae565b919050565b600080604083850312156130f357600080fd5b82356130fe816130ae565b946020939093013593505050565b60008060006060848603121561312157600080fd5b833561312c816130ae565b9250602084013561313c816130ae565b929592945050506040919091013590565b60006020828403121561315f57600080fd5b8135610857816130ae565b803580151581146130db57600080fd5b6000806040838503121561318d57600080fd5b8235613198816130ae565b91506131a66020840161316a565b90509250929050565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152611b6d6040850182613038565b604081526131f760408201845115159052565b60208301516060820152604083015160808201526000606084015161323960a08401828051151582526020808201511515908301526040908101511515910152565b50608084015161010061326e81850183805173ffffffffffffffffffffffffffffffffffffffff168252602090810151910152565b60a0860151915061022061014085015261328c6102608501836131af565b915060c08601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc080868503016101608701526132c984836131af565b935060e088015191506132f561018087018373ffffffffffffffffffffffffffffffffffffffff169052565b9187015180516dffffffffffffffffffffffffffff9081166101a08801526020820151166101c0870152604081015115156101e0870152606081015173ffffffffffffffffffffffffffffffffffffffff1661020087015260800151610220860152506101208601518483039091016102408501526133748282613038565b92505050610857602083018473ffffffffffffffffffffffffffffffffffffffff169052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156133ec576133ec61339a565b60405290565b600082601f83011261340357600080fd5b813567ffffffffffffffff8082111561341e5761341e61339a565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156134645761346461339a565b8160405283815286602085880101111561347d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080608085870312156134b357600080fd5b84356134be816130ae565b935060208501356134ce816130ae565b925060408501359150606085013567ffffffffffffffff8111156134f157600080fd5b6134fd878288016133f2565b91505092959194509250565b6000806040838503121561351c57600080fd5b8235613527816130ae565b91506020830135613537816130ae565b809150509250929050565b6000806020838503121561355557600080fd5b823567ffffffffffffffff8082111561356d57600080fd5b818501915085601f83011261358157600080fd5b81358181111561359057600080fd5b8660208285010111156135a257600080fd5b60209290920196919550909350505050565b6000606082840312156135c657600080fd5b50919050565b6000604082840312156135c657600080fd5b600060a082840312156135c657600080fd5b6000806000806000806000806000806102408b8d03121561361057600080fd5b6136198b6130d0565b995060208b0135985060408b013597506136368c60608d016135b4565b96506136458c60c08d016135b4565b95506101208b013567ffffffffffffffff8082111561366357600080fd5b61366f8e838f016135cc565b96506101408d013591508082111561368657600080fd5b6136928e838f016135cc565b95506136a28e6101608f016135de565b94506102008d01359150808211156136b957600080fd5b506136c68d828e016133f2565b9250506136d66102208c016130d0565b90509295989b9194979a5092959850565b600181811c908216806136fb57607f821691505b6020821081036135c6577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006020828403121561374657600080fd5b8151610857816130ae565b60008351613763818460208801613014565b835190830190613777818360208801613014565b01949350505050565b601f8211156106aa57600081815260208120601f850160051c810160208610156137a75750805b601f850160051c820191505b818110156137c6578281556001016137b3565b505050505050565b67ffffffffffffffff8311156137e6576137e661339a565b6137fa836137f483546136e7565b83613780565b6000601f84116001811461384c57600085156138165750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556138e2565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561389b578685013582556020948501946001909201910161387b565b50868210156138d6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6000606082840312156138fb57600080fd5b6040516060810181811067ffffffffffffffff8211171561391e5761391e61339a565b60405261392a8361316a565b81526139386020840161316a565b60208201526139496040840161316a565b60408201529392505050565b60006020828403121561396757600080fd5b6108578261316a565b6000818303606081121561398357600080fd5b61398b6133c9565b8335613996816130ae565b815260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0830112156139c857600080fd5b6139d06133c9565b915060208401356139e0816130ae565b82526040939093013560208083019190915283015250919050565b600060408284031215613a0d57600080fd5b613a156133c9565b90508135613a22816130ae565b8152602082013567ffffffffffffffff811115613a3e57600080fd5b613a4a848285016133f2565b60208301525092915050565b600061048636836139fb565b80356dffffffffffffffffffffffffffff811681146130db57600080fd5b600060a08284031215613a9257600080fd5b60405160a0810181811067ffffffffffffffff82111715613ab557613ab561339a565b604052613ac183613a62565b8152613acf60208401613a62565b6020820152613ae06040840161316a565b60408201526060830135613af3816130ae565b60608201526080928301359281019290925250919050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152613b4a6080830184613038565b9695505050505050565b600060208284031215613b6657600080fd5b815161085781612fc9565b815167ffffffffffffffff811115613b8b57613b8b61339a565b613b9f81613b9984546136e7565b84613780565b602080601f831160018114613bf25760008415613bbc5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556137c6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613c3f57888601518255948401946001909101908401613c20565b5085821015613c7b57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ce3577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b506001019056fea2646970667358221220f2a8449fa514209b5e1e6702694c3899389b46b19e276088a7b958f0a1db3c3e64736f6c63430008130033000000000000000000000000f157916d184598105fe4681ec93be0f799599003", + "nonce": "0x43", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionType": "CREATE", + "contractName": "ERC1967Proxy", + "contractAddress": "0xFbfa729428af206a084E18328Ec923587193525d", + "function": null, + "arguments": [ + "0x8602E35ee65074eF0D2701927289705f2F61773D", + "0xf62d18880000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003f68747470733a2f2f617277656176652e6e65742f794849624b6c4642673378754b537a6c4d5f6452454738593038756f642d67574b735769394f615046734d00" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x5824f", + "value": "0x0", + "data": "0x60806040526040516104ee3803806104ee833981016040819052610022916102de565b61002e82826000610035565b50506103fb565b61003e83610061565b60008251118061004b5750805b1561005c5761005a83836100a1565b505b505050565b61006a816100cd565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606100c683836040518060600160405280602781526020016104c760279139610180565b9392505050565b6001600160a01b0381163b61013f5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080856001600160a01b03168560405161019d91906103ac565b600060405180830381855af49150503d80600081146101d8576040519150601f19603f3d011682016040523d82523d6000602084013e6101dd565b606091505b5090925090506101ef868383876101f9565b9695505050505050565b60608315610268578251600003610261576001600160a01b0385163b6102615760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610136565b5081610272565b610272838361027a565b949350505050565b81511561028a5781518083602001fd5b8060405162461bcd60e51b815260040161013691906103c8565b634e487b7160e01b600052604160045260246000fd5b60005b838110156102d55781810151838201526020016102bd565b50506000910152565b600080604083850312156102f157600080fd5b82516001600160a01b038116811461030857600080fd5b60208401519092506001600160401b038082111561032557600080fd5b818501915085601f83011261033957600080fd5b81518181111561034b5761034b6102a4565b604051601f8201601f19908116603f01168101908382118183101715610373576103736102a4565b8160405282815288602084870101111561038c57600080fd5b61039d8360208301602088016102ba565b80955050505050509250929050565b600082516103be8184602087016102ba565b9190910192915050565b60208152600082518060208401526103e78160408501602087016102ba565b601f01601f19169190910160400192915050565b60be806104096000396000f3fe608060405236601057600e6013565b005b600e5b601f601b6021565b6065565b565b600060607f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b3660008037600080366000845af43d6000803e8080156083573d6000f35b3d6000fdfea2646970667358221220a678556ae280ddf13e459fe9ecd177633ae9dbc544c45d5b523d43ba9084c8dc64736f6c63430008130033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000008602e35ee65074ef0d2701927289705f2f61773d00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000084f62d18880000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003f68747470733a2f2f617277656176652e6e65742f794849624b6c4642673378754b537a6c4d5f6452454738593038756f642d67574b735769394f615046734d0000000000000000000000000000000000000000000000000000000000", + "nonce": "0x44", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0x70610487057d530e2da659732ebdb2bdba5c5cd4aeb52ed607a50a87877d7245", + "transactionIndex": "0x0", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": null, + "cumulativeGasUsed": "0x343e52", + "gasUsed": "0x343e52", + "contractAddress": "0x8602E35ee65074eF0D2701927289705f2F61773D", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0e" + }, + { + "transactionHash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionIndex": "0x1", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": null, + "cumulativeGasUsed": "0x387b8d", + "gasUsed": "0x43d3b", + "contractAddress": "0xFbfa729428af206a084E18328Ec923587193525d", + "logs": [ + { + "address": "0xFbfa729428af206a084E18328Ec923587193525d", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000008602e35ee65074ef0d2701927289705f2f61773d" + ], + "data": "0x", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "transactionHash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionIndex": "0x1", + "logIndex": "0x0", + "removed": false + }, + { + "address": "0xFbfa729428af206a084E18328Ec923587193525d", + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x14ab3fe5347b4f095858a52eb28997ef8b6f3628a66f29cc63f8e42baf9260c0", + "blockNumber": "0x911055", + "transactionHash": "0x5c0ee04d75ba772c89575dadfb5e7032582c501f16e6f438aa1e2fdd6c4c284c", + "transactionIndex": "0x1", + "logIndex": "0x1", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000400000000000000000000042000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000001000000000000000000000000000000000000080000020100000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000000000000000000000000000000020000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0e" + } + ], + "libraries": [], + "pending": [], + "path": "/Users/drmanhattan/Workspace/StoryProtocol/protocol-contracts/broadcast/DevDeployments.s.sol/5/run-latest.json", + "returns": {}, + "timestamp": 1691868693, + "chain": 5, + "multi": false, + "commit": "53ab00d" +} \ No newline at end of file diff --git a/broadcast/DevUpgrades.s.sol/5/run-1691870521.json b/broadcast/DevUpgrades.s.sol/5/run-1691870521.json new file mode 100644 index 00000000..d3f7e3f6 --- /dev/null +++ b/broadcast/DevUpgrades.s.sol/5/run-1691870521.json @@ -0,0 +1,81 @@ +{ + "transactions": [ + { + "hash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", + "transactionType": "CREATE", + "contractName": "FranchiseRegistry", + "contractAddress": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", + "function": null, + "arguments": [ + "0x461DF243955aD5493cFa33434c2B7CfF2C583dc7" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x47a825", + "value": "0x0", + "data": "0x60c0604052306080523480156200001557600080fd5b50604051620041ed380380620041ed833981016040819052620000389162000140565b6001600160a01b038116620000605760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b03811660a052620000776200007e565b5062000172565b600054610100900460ff1615620000eb5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156200013e576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6000602082840312156200015357600080fd5b81516001600160a01b03811681146200016b57600080fd5b9392505050565b60805160a051614032620001bb6000396000818161036701526110b8015260008181610cc401528181610d5a0152818161128d01528181611323015261153601526140326000f3fe6080604052600436106101c25760003560e01c8063503fa317116100f7578063a22cb46511610095578063cc5b608411610064578063cc5b6084146105ab578063e985e9c5146105cb578063ef647e1014610621578063fc1dad811461063657600080fd5b8063a22cb4651461052b578063b88d4fde1461054b578063c4d66de81461056b578063c87b56dd1461058b57600080fd5b80636352211e116100d15780636352211e146104c157806370a08231146104e157806395d89b41146105015780639b0c6be41461051657600080fd5b8063503fa3171461044657806352d1902d1461046657806354fd4d501461047b57600080fd5b80632c5cefa3116101645780633b781c171161013e5780633b781c17146103a95780633da52093146103c957806342842e0e146104135780634f1ef2861461043357600080fd5b80632c5cefa3146102f35780632dd31000146103555780633659cfe61461038957600080fd5b8063095ea7b3116101a0578063095ea7b31461026357806319129e5a1461028557806323b872dd146102a5578063290cacf9146102c557600080fd5b806301ffc9a7146101c757806306fdde03146101fc578063081812fc1461021e575b600080fd5b3480156101d357600080fd5b506101e76101e2366004613316565b610680565b60405190151581526020015b60405180910390f35b34801561020857600080fd5b50610211610765565b6040516101f391906133a1565b34801561022a57600080fd5b5061023e6102393660046133b4565b6107f7565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101f3565b34801561026f57600080fd5b5061028361027e3660046133ef565b61082b565b005b34801561029157600080fd5b506102836102a036600461341b565b610988565b3480156102b157600080fd5b506102836102c0366004613438565b610b08565b3480156102d157600080fd5b506102e56102e036600461354a565b610b8f565b6040519081526020016101f3565b3480156102ff57600080fd5b5061023e61030e3660046133b4565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b34801561036157600080fd5b5061023e7f000000000000000000000000000000000000000000000000000000000000000081565b34801561039557600080fd5b506102836103a436600461341b565b610cad565b3480156103b557600080fd5b506102e56103c4366004613614565b610e7e565b3480156103d557600080fd5b506103e96103e43660046136ce565b61105e565b6040805192835273ffffffffffffffffffffffffffffffffffffffff9091166020830152016101f3565b34801561041f57600080fd5b5061028361042e366004613438565b61125b565b610283610441366004613729565b611276565b34801561045257600080fd5b506101e761046136600461341b565b611438565b34801561047257600080fd5b506102e561151c565b34801561048757600080fd5b5060408051808201909152600581527f302e312e300000000000000000000000000000000000000000000000000000006020820152610211565b3480156104cd57600080fd5b5061023e6104dc3660046133b4565b6115ee565b3480156104ed57600080fd5b506102e56104fc36600461341b565b611660565b34801561050d57600080fd5b50610211611714565b34801561052257600080fd5b5061023e600081565b34801561053757600080fd5b50610283610546366004613779565b611723565b34801561055757600080fd5b506102836105663660046137b2565b61172e565b34801561057757600080fd5b5061028361058636600461341b565b6117bc565b34801561059757600080fd5b506102116105a63660046133b4565b6119b2565b3480156105b757600080fd5b506102836105c636600461341b565b611a9e565b3480156105d757600080fd5b506101e76105e636600461381e565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260ce6020908152604080832093909416825291909152205460ff1690565b34801561062d57600080fd5b506102e5600081565b34801561064257600080fd5b507f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b65473ffffffffffffffffffffffffffffffffffffffff1661023e565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061071357507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061075f57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b606060c980546107749061384c565b80601f01602080910402602001604051908101604052809291908181526020018280546107a09061384c565b80156107ed5780601f106107c2576101008083540402835291602001916107ed565b820191906000526020600020905b8154815290600101906020018083116107d057829003601f168201915b5050505050905090565b600061080282611b51565b50600090815260cd602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b6000610836826115ee565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108de5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82161480610907575061090781336105e6565b6109795760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016108d5565b6109838383611bc2565b505050565b60006109948133611c62565b6109d3576040517f75000dc0000000000000000000000000000000000000000000000000000000008152600481018290523360248201526044016108d5565b610a1373ffffffffffffffffffffffffffffffffffffffff83167f7965db0b00000000000000000000000000000000000000000000000000000000611d2a565b610a79576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c00000000000000000000000000000000000060448201526064016108d5565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a2505050565b610b123382611d4d565b610b845760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016108d5565b610983838383611e0c565b6000807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f600088815260018201602052604080822054600384015491517f2fb49eff00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201819052602482018c9052949550911690632fb49eff90604401602060405180830381865afa158015610c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c65919061389f565b604080518082018252338152815180830190925260008083526020838101829052820192909252919250610c9f9088908a9085858b6120b9565b9a9950505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610d585760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c000000000000000000000000000000000000000060648201526084016108d5565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16610dcd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614610e565760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f7879000000000000000000000000000000000000000060648201526084016108d5565b610e5f81612296565b60408051600080825260208201909252610e7b91839190612300565b50565b60008881527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e9060205260408082205490517f99258ca10000000000000000000000000000000000000000000000000000000081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f9173ffffffffffffffffffffffffffffffffffffffff169081906399258ca190610f2f908d908d908d908d908d908d908d903390600401613901565b6020604051808303816000875af1158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f72919061389f565b925060006040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160405180604001604052808573ffffffffffffffffffffffffffffffffffffffff16815260200187815250815250905061100d7f781e9f3557d8768f1963c458e1b351a046f8e4f612fd4a2c28f5132ee0c0c87b600180600085604051806020016040528060008152506120b9565b5061104e7facb6e50a24ac7b7075ce347695829fdb25124f5d7485df10299c2e269a3a9f8360006001600085604051806020016040528060008152506120b9565b5050505098975050505050505050565b600080807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f905060008160000160008154611098906139a6565b91829055509050600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166386df779e836110e88980613a05565b6110f560208c018c613a05565b61110260408e018e613a05565b6040518863ffffffff1660e01b81526004016111249796959493929190613a6a565b6020604051808303816000875af1158015611143573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111679190613aac565b6000838152600185016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590506111c46060870187613a05565b60008481526002860160205260409020916111e0919083613b17565b506111eb33836124cb565b7f071096a6b974f736d72b386641c048d2b40ab6731f5e6da2a22c95b530e81bd73383836112198a80613a05565b61122660208d018d613a05565b61123360608f018f613a05565b60405161124899989796959493929190613c32565b60405180910390a1909590945092505050565b6109838383836040518060200160405280600081525061172e565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001630036113215760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c000000000000000000000000000000000000000060648201526084016108d5565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166113967f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161461141f5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f7879000000000000000000000000000000000000000060648201526084016108d5565b61142882612296565b61143482826001612300565b5050565b60008173ffffffffffffffffffffffffffffffffffffffff1663b9d0787c6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156114bf575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526114bc9181019061389f565b60015b6114cb57506000919050565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff908116921691909114919050565b60003073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146115c95760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c000000000000000060648201526084016108d5565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b600081815260cb602052604081205473ffffffffffffffffffffffffffffffffffffffff168061075f5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016108d5565b600073ffffffffffffffffffffffffffffffffffffffff82166116eb5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e6572000000000000000000000000000000000000000000000060648201526084016108d5565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260cc602052604090205490565b606060ca80546107749061384c565b6114343383836124e5565b6117383383611d4d565b6117aa5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016108d5565b6117b6848484846125f8565b50505050565b600054610100900460ff16158080156117dc5750600054600160ff909116105b806117f65750303b1580156117f6575060005460ff166001145b6118685760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108d5565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156118c657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6118ce612681565b6118d782612700565b61194b6040518060400160405280600e81526020017f53746f72792050726f746f636f6c0000000000000000000000000000000000008152506040518060400160405280600281526020017f535000000000000000000000000000000000000000000000000000000000000081525061299a565b801561143457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b60606119bd82611b51565b60008281527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e916020526040902080547f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f9190611a189061384c565b80601f0160208091040260200160405190810160405280929190818152602001828054611a449061384c565b8015611a915780601f10611a6657610100808354040283529160200191611a91565b820191906000526020600020905b815481529060010190602001808311611a7457829003601f168201915b5050505050915050919050565b73ffffffffffffffffffffffffffffffffffffffff8116611aeb576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e9280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff16610e7b5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016108d5565b600081815260cd6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091558190611c1c826115ee565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000807f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680546040517f91d148540000000000000000000000000000000000000000000000000000000081526004810187905273ffffffffffffffffffffffffffffffffffffffff86811660248301529293509116906391d1485490604401602060405180830381865afa158015611cfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d229190613caa565b949350505050565b6000611d3583612a21565b8015611d465750611d468383612a85565b9392505050565b600080611d59836115ee565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611dc7575073ffffffffffffffffffffffffffffffffffffffff808216600090815260ce602090815260408083209388168352929052205460ff165b80611d2257508373ffffffffffffffffffffffffffffffffffffffff16611ded846107f7565b73ffffffffffffffffffffffffffffffffffffffff1614949350505050565b8273ffffffffffffffffffffffffffffffffffffffff16611e2c826115ee565b73ffffffffffffffffffffffffffffffffffffffff1614611eb55760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016108d5565b73ffffffffffffffffffffffffffffffffffffffff8216611f3d5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016108d5565b8273ffffffffffffffffffffffffffffffffffffffff16611f5d826115ee565b73ffffffffffffffffffffffffffffffffffffffff1614611fe65760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016108d5565b600081815260cd6020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811690915573ffffffffffffffffffffffffffffffffffffffff87811680865260cc855283862080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559087168086528386208054600101905586865260cb90945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6000807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f90508060030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f782f67533878b60405180606001604052806000151581526020018c151581526020018d1515815250896040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001604051806020016040528060008152508152506040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001604051806020016040528060008152508152506040518060a0016040528060006dffffffffffffffffffffffffffff16815260200160006dffffffffffffffffffffffffffff168152602001600015158152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152508c60006040518b63ffffffff1660e01b81526004016122479a99989796959493929190613cfc565b6020604051808303816000875af1158015612266573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061228a919061389f565b98975050505050505050565b7f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e36122c18133611c62565b611434576040517f75000dc0000000000000000000000000000000000000000000000000000000008152600481018290523360248201526044016108d5565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff16156123335761098383612b54565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156123b8575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526123b59181019061389f565b60015b61242a5760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f74205555505300000000000000000000000000000000000060648201526084016108d5565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81146124bf5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c6555554944000000000000000000000000000000000000000000000060648201526084016108d5565b50610983838383612c44565b611434828260405180602001604052806000815250612c69565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036125605760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016108d5565b73ffffffffffffffffffffffffffffffffffffffff838116600081815260ce602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b612603848484611e0c565b61260f84848484612cf2565b6117b65760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b600054610100900460ff166126fe5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b565b600054610100900460ff16158080156127205750600054600160ff909116105b8061273a5750303b15801561273a575060005460ff166001145b6127ac5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108d5565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561280a57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b61284a73ffffffffffffffffffffffffffffffffffffffff83167f7965db0b00000000000000000000000000000000000000000000000000000000611d2a565b6128b0576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c00000000000000000000000000000000000060448201526064016108d5565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a250801561143457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020016119a6565b600054610100900460ff16612a175760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b6114348282612ecb565b6000612a4d827f01ffc9a700000000000000000000000000000000000000000000000000000000612a85565b801561075f5750612a7e827fffffffff00000000000000000000000000000000000000000000000000000000612a85565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015612b3d575060208210155b8015612b495750600081115b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81163b612bde5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016108d5565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612c4d83612f61565b600082511180612c5a5750805b15610983576117b68383612fae565b612c7383836130d3565b612c806000848484612cf2565b6109835760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b600073ffffffffffffffffffffffffffffffffffffffff84163b15612ec0576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612d69903390899088908890600401613e39565b6020604051808303816000875af1925050508015612dc2575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612dbf91810190613e82565b60015b612e75573d808015612df0576040519150601f19603f3d011682016040523d82523d6000602084013e612df5565b606091505b508051600003612e6d5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a0200000000000000000000000000000000000000000000000000000000149050611d22565b506001949350505050565b600054610100900460ff16612f485760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b60c9612f548382613e9f565b5060ca6109838282613e9f565b612f6a81612b54565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b606073ffffffffffffffffffffffffffffffffffffffff83163b61303a5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016108d5565b6000808473ffffffffffffffffffffffffffffffffffffffff16846040516130629190613fb9565b600060405180830381855af49150503d806000811461309d576040519150601f19603f3d011682016040523d82523d6000602084013e6130a2565b606091505b50915091506130ca8282604051806060016040528060278152602001613fd6602791396132aa565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff82166131365760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016108d5565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff16156131a85760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016108d5565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff161561321a5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016108d5565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260cc602090815260408083208054600101905584835260cb90915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b606083156132b9575081611d46565b611d4683838151156132ce5781518083602001fd5b8060405162461bcd60e51b81526004016108d591906133a1565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114610e7b57600080fd5b60006020828403121561332857600080fd5b8135611d46816132e8565b60005b8381101561334e578181015183820152602001613336565b50506000910152565b6000815180845261336f816020860160208601613333565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611d466020830184613357565b6000602082840312156133c657600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610e7b57600080fd5b6000806040838503121561340257600080fd5b823561340d816133cd565b946020939093013593505050565b60006020828403121561342d57600080fd5b8135611d46816133cd565b60008060006060848603121561344d57600080fd5b8335613458816133cd565b92506020840135613468816133cd565b929592945050506040919091013590565b8015158114610e7b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff808411156134d1576134d1613487565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561351757613517613487565b8160405280935085815286868601111561353057600080fd5b858560208301376000602087830101525050509392505050565b600080600080600060a0868803121561356257600080fd5b8535945060208601359350604086013561357b81613479565b925060608601359150608086013567ffffffffffffffff81111561359e57600080fd5b8601601f810188136135af57600080fd5b6135be888235602084016134b6565b9150509295509295909350565b60008083601f8401126135dd57600080fd5b50813567ffffffffffffffff8111156135f557600080fd5b60208301915083602082850101111561360d57600080fd5b9250929050565b60008060008060008060008060a0898b03121561363057600080fd5b8835975060208901356007811061364657600080fd5b9650604089013567ffffffffffffffff8082111561366357600080fd5b61366f8c838d016135cb565b909850965060608b013591508082111561368857600080fd5b6136948c838d016135cb565b909650945060808b01359150808211156136ad57600080fd5b506136ba8b828c016135cb565b999c989b5096995094979396929594505050565b6000602082840312156136e057600080fd5b813567ffffffffffffffff8111156136f757600080fd5b820160808185031215611d4657600080fd5b600082601f83011261371a57600080fd5b611d46838335602085016134b6565b6000806040838503121561373c57600080fd5b8235613747816133cd565b9150602083013567ffffffffffffffff81111561376357600080fd5b61376f85828601613709565b9150509250929050565b6000806040838503121561378c57600080fd5b8235613797816133cd565b915060208301356137a781613479565b809150509250929050565b600080600080608085870312156137c857600080fd5b84356137d3816133cd565b935060208501356137e3816133cd565b925060408501359150606085013567ffffffffffffffff81111561380657600080fd5b61381287828801613709565b91505092959194509250565b6000806040838503121561383157600080fd5b823561383c816133cd565b915060208301356137a7816133cd565b600181811c9082168061386057607f821691505b602082108103613899577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156138b157600080fd5b5051919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600060078a1061393a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b89825260a0602083015261395260a08301898b6138b8565b828103604084015261396581888a6138b8565b9050828103606084015261397a8186886138b8565b91505073ffffffffffffffffffffffffffffffffffffffff831660808301529998505050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036139fe577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613a3a57600080fd5b83018035915067ffffffffffffffff821115613a5557600080fd5b60200191503681900382131561360d57600080fd5b878152608060208201526000613a8460808301888a6138b8565b8281036040840152613a978187896138b8565b90508281036060840152610c9f8185876138b8565b600060208284031215613abe57600080fd5b8151611d46816133cd565b601f82111561098357600081815260208120601f850160051c81016020861015613af05750805b601f850160051c820191505b81811015613b0f57828155600101613afc565b505050505050565b67ffffffffffffffff831115613b2f57613b2f613487565b613b4383613b3d835461384c565b83613ac9565b6000601f841160018114613b955760008515613b5f5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355613c2b565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015613be45786850135825560209485019460019092019101613bc4565b5086821015613c1f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff808c1683528a6020840152808a1660408401525060c06060830152613c7260c08301888a6138b8565b8281036080840152613c858187896138b8565b905082810360a0840152613c9a8185876138b8565b9c9b505050505050505050505050565b600060208284031215613cbc57600080fd5b8151611d4681613479565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152611d226040850182613357565b73ffffffffffffffffffffffffffffffffffffffff8b8116825260208083018c905260408084018c90528a51151560608501528a820151151560808501528a0151151560a08401528851821660c084015288810151805190921660e08401520151610100820152600061024080610120840152613d7b81840189613cc7565b9050828103610140840152613d908188613cc7565b86516dffffffffffffffffffffffffffff908116610160860152602088015116610180850152604087015115156101a0850152606087015173ffffffffffffffffffffffffffffffffffffffff166101c085015260808701516101e08501529050828103610200840152613e048186613357565b915050613e2a61022083018473ffffffffffffffffffffffffffffffffffffffff169052565b9b9a5050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152613e786080830184613357565b9695505050505050565b600060208284031215613e9457600080fd5b8151611d46816132e8565b815167ffffffffffffffff811115613eb957613eb9613487565b613ecd81613ec7845461384c565b84613ac9565b602080601f831160018114613f205760008415613eea5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613b0f565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613f6d57888601518255948401946001909101908401613f4e565b5085821015613fa957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008251613fcb818460208701613333565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122069df0d4308aa6890cb1a63ed5fde72e5003558e570cb3043f0e387b70837636e64736f6c63430008130033000000000000000000000000461df243955ad5493cfa33434c2b7cff2c583dc7", + "nonce": "0x45", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", + "transactionType": "CALL", + "contractName": "ERC1967Proxy", + "contractAddress": "0xf157916d184598105FE4681eC93Be0f799599003", + "function": null, + "arguments": null, + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "to": "0xf157916d184598105fe4681ec93be0f799599003", + "gas": "0x10ad1", + "value": "0x0", + "data": "0x3659cfe600000000000000000000000088f3a4cc4eaa2433c44a7c502c7abbd2c76b03e2", + "nonce": "0x46", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x66ecb91002da900a2889cda1ec2af547945c55f6e2f175366566dc483fbb9d0f", + "transactionType": "CALL", + "contractName": "ERC1967Proxy", + "contractAddress": "0xf157916d184598105FE4681eC93Be0f799599003", + "function": null, + "arguments": null, + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "to": "0xf157916d184598105fe4681ec93be0f799599003", + "gas": "0x10757", + "value": "0x0", + "data": "0xcc5b6084000000000000000000000000fbfa729428af206a084e18328ec923587193525d", + "nonce": "0x47", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [], + "libraries": [], + "pending": [ + "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", + "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", + "0x66ecb91002da900a2889cda1ec2af547945c55f6e2f175366566dc483fbb9d0f" + ], + "path": "/Users/drmanhattan/Workspace/StoryProtocol/protocol-contracts/broadcast/DevUpgrades.s.sol/5/run-latest.json", + "returns": {}, + "timestamp": 1691870521, + "chain": 5, + "multi": false, + "commit": "53ab00d" +} \ No newline at end of file diff --git a/broadcast/DevUpgrades.s.sol/5/run-1691870540.json b/broadcast/DevUpgrades.s.sol/5/run-1691870540.json new file mode 100644 index 00000000..d52a899a --- /dev/null +++ b/broadcast/DevUpgrades.s.sol/5/run-1691870540.json @@ -0,0 +1,155 @@ +{ + "transactions": [ + { + "hash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", + "transactionType": "CREATE", + "contractName": "FranchiseRegistry", + "contractAddress": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", + "function": null, + "arguments": [ + "0x461DF243955aD5493cFa33434c2B7CfF2C583dc7" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x47a825", + "value": "0x0", + "data": "0x60c0604052306080523480156200001557600080fd5b50604051620041ed380380620041ed833981016040819052620000389162000140565b6001600160a01b038116620000605760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b03811660a052620000776200007e565b5062000172565b600054610100900460ff1615620000eb5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156200013e576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6000602082840312156200015357600080fd5b81516001600160a01b03811681146200016b57600080fd5b9392505050565b60805160a051614032620001bb6000396000818161036701526110b8015260008181610cc401528181610d5a0152818161128d01528181611323015261153601526140326000f3fe6080604052600436106101c25760003560e01c8063503fa317116100f7578063a22cb46511610095578063cc5b608411610064578063cc5b6084146105ab578063e985e9c5146105cb578063ef647e1014610621578063fc1dad811461063657600080fd5b8063a22cb4651461052b578063b88d4fde1461054b578063c4d66de81461056b578063c87b56dd1461058b57600080fd5b80636352211e116100d15780636352211e146104c157806370a08231146104e157806395d89b41146105015780639b0c6be41461051657600080fd5b8063503fa3171461044657806352d1902d1461046657806354fd4d501461047b57600080fd5b80632c5cefa3116101645780633b781c171161013e5780633b781c17146103a95780633da52093146103c957806342842e0e146104135780634f1ef2861461043357600080fd5b80632c5cefa3146102f35780632dd31000146103555780633659cfe61461038957600080fd5b8063095ea7b3116101a0578063095ea7b31461026357806319129e5a1461028557806323b872dd146102a5578063290cacf9146102c557600080fd5b806301ffc9a7146101c757806306fdde03146101fc578063081812fc1461021e575b600080fd5b3480156101d357600080fd5b506101e76101e2366004613316565b610680565b60405190151581526020015b60405180910390f35b34801561020857600080fd5b50610211610765565b6040516101f391906133a1565b34801561022a57600080fd5b5061023e6102393660046133b4565b6107f7565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101f3565b34801561026f57600080fd5b5061028361027e3660046133ef565b61082b565b005b34801561029157600080fd5b506102836102a036600461341b565b610988565b3480156102b157600080fd5b506102836102c0366004613438565b610b08565b3480156102d157600080fd5b506102e56102e036600461354a565b610b8f565b6040519081526020016101f3565b3480156102ff57600080fd5b5061023e61030e3660046133b4565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b34801561036157600080fd5b5061023e7f000000000000000000000000000000000000000000000000000000000000000081565b34801561039557600080fd5b506102836103a436600461341b565b610cad565b3480156103b557600080fd5b506102e56103c4366004613614565b610e7e565b3480156103d557600080fd5b506103e96103e43660046136ce565b61105e565b6040805192835273ffffffffffffffffffffffffffffffffffffffff9091166020830152016101f3565b34801561041f57600080fd5b5061028361042e366004613438565b61125b565b610283610441366004613729565b611276565b34801561045257600080fd5b506101e761046136600461341b565b611438565b34801561047257600080fd5b506102e561151c565b34801561048757600080fd5b5060408051808201909152600581527f302e312e300000000000000000000000000000000000000000000000000000006020820152610211565b3480156104cd57600080fd5b5061023e6104dc3660046133b4565b6115ee565b3480156104ed57600080fd5b506102e56104fc36600461341b565b611660565b34801561050d57600080fd5b50610211611714565b34801561052257600080fd5b5061023e600081565b34801561053757600080fd5b50610283610546366004613779565b611723565b34801561055757600080fd5b506102836105663660046137b2565b61172e565b34801561057757600080fd5b5061028361058636600461341b565b6117bc565b34801561059757600080fd5b506102116105a63660046133b4565b6119b2565b3480156105b757600080fd5b506102836105c636600461341b565b611a9e565b3480156105d757600080fd5b506101e76105e636600461381e565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260ce6020908152604080832093909416825291909152205460ff1690565b34801561062d57600080fd5b506102e5600081565b34801561064257600080fd5b507f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b65473ffffffffffffffffffffffffffffffffffffffff1661023e565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061071357507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061075f57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b606060c980546107749061384c565b80601f01602080910402602001604051908101604052809291908181526020018280546107a09061384c565b80156107ed5780601f106107c2576101008083540402835291602001916107ed565b820191906000526020600020905b8154815290600101906020018083116107d057829003601f168201915b5050505050905090565b600061080282611b51565b50600090815260cd602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b6000610836826115ee565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108de5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82161480610907575061090781336105e6565b6109795760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016108d5565b6109838383611bc2565b505050565b60006109948133611c62565b6109d3576040517f75000dc0000000000000000000000000000000000000000000000000000000008152600481018290523360248201526044016108d5565b610a1373ffffffffffffffffffffffffffffffffffffffff83167f7965db0b00000000000000000000000000000000000000000000000000000000611d2a565b610a79576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c00000000000000000000000000000000000060448201526064016108d5565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a2505050565b610b123382611d4d565b610b845760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016108d5565b610983838383611e0c565b6000807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f600088815260018201602052604080822054600384015491517f2fb49eff00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201819052602482018c9052949550911690632fb49eff90604401602060405180830381865afa158015610c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c65919061389f565b604080518082018252338152815180830190925260008083526020838101829052820192909252919250610c9f9088908a9085858b6120b9565b9a9950505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610d585760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c000000000000000000000000000000000000000060648201526084016108d5565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16610dcd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614610e565760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f7879000000000000000000000000000000000000000060648201526084016108d5565b610e5f81612296565b60408051600080825260208201909252610e7b91839190612300565b50565b60008881527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e9060205260408082205490517f99258ca10000000000000000000000000000000000000000000000000000000081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f9173ffffffffffffffffffffffffffffffffffffffff169081906399258ca190610f2f908d908d908d908d908d908d908d903390600401613901565b6020604051808303816000875af1158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f72919061389f565b925060006040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160405180604001604052808573ffffffffffffffffffffffffffffffffffffffff16815260200187815250815250905061100d7f781e9f3557d8768f1963c458e1b351a046f8e4f612fd4a2c28f5132ee0c0c87b600180600085604051806020016040528060008152506120b9565b5061104e7facb6e50a24ac7b7075ce347695829fdb25124f5d7485df10299c2e269a3a9f8360006001600085604051806020016040528060008152506120b9565b5050505098975050505050505050565b600080807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f905060008160000160008154611098906139a6565b91829055509050600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166386df779e836110e88980613a05565b6110f560208c018c613a05565b61110260408e018e613a05565b6040518863ffffffff1660e01b81526004016111249796959493929190613a6a565b6020604051808303816000875af1158015611143573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111679190613aac565b6000838152600185016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590506111c46060870187613a05565b60008481526002860160205260409020916111e0919083613b17565b506111eb33836124cb565b7f071096a6b974f736d72b386641c048d2b40ab6731f5e6da2a22c95b530e81bd73383836112198a80613a05565b61122660208d018d613a05565b61123360608f018f613a05565b60405161124899989796959493929190613c32565b60405180910390a1909590945092505050565b6109838383836040518060200160405280600081525061172e565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001630036113215760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c000000000000000000000000000000000000000060648201526084016108d5565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166113967f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161461141f5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f7879000000000000000000000000000000000000000060648201526084016108d5565b61142882612296565b61143482826001612300565b5050565b60008173ffffffffffffffffffffffffffffffffffffffff1663b9d0787c6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156114bf575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526114bc9181019061389f565b60015b6114cb57506000919050565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff908116921691909114919050565b60003073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146115c95760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c000000000000000060648201526084016108d5565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b600081815260cb602052604081205473ffffffffffffffffffffffffffffffffffffffff168061075f5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016108d5565b600073ffffffffffffffffffffffffffffffffffffffff82166116eb5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e6572000000000000000000000000000000000000000000000060648201526084016108d5565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260cc602052604090205490565b606060ca80546107749061384c565b6114343383836124e5565b6117383383611d4d565b6117aa5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016108d5565b6117b6848484846125f8565b50505050565b600054610100900460ff16158080156117dc5750600054600160ff909116105b806117f65750303b1580156117f6575060005460ff166001145b6118685760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108d5565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156118c657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6118ce612681565b6118d782612700565b61194b6040518060400160405280600e81526020017f53746f72792050726f746f636f6c0000000000000000000000000000000000008152506040518060400160405280600281526020017f535000000000000000000000000000000000000000000000000000000000000081525061299a565b801561143457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b60606119bd82611b51565b60008281527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e916020526040902080547f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f9190611a189061384c565b80601f0160208091040260200160405190810160405280929190818152602001828054611a449061384c565b8015611a915780601f10611a6657610100808354040283529160200191611a91565b820191906000526020600020905b815481529060010190602001808311611a7457829003601f168201915b5050505050915050919050565b73ffffffffffffffffffffffffffffffffffffffff8116611aeb576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e9280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff16610e7b5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016108d5565b600081815260cd6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091558190611c1c826115ee565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000807f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680546040517f91d148540000000000000000000000000000000000000000000000000000000081526004810187905273ffffffffffffffffffffffffffffffffffffffff86811660248301529293509116906391d1485490604401602060405180830381865afa158015611cfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d229190613caa565b949350505050565b6000611d3583612a21565b8015611d465750611d468383612a85565b9392505050565b600080611d59836115ee565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611dc7575073ffffffffffffffffffffffffffffffffffffffff808216600090815260ce602090815260408083209388168352929052205460ff165b80611d2257508373ffffffffffffffffffffffffffffffffffffffff16611ded846107f7565b73ffffffffffffffffffffffffffffffffffffffff1614949350505050565b8273ffffffffffffffffffffffffffffffffffffffff16611e2c826115ee565b73ffffffffffffffffffffffffffffffffffffffff1614611eb55760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016108d5565b73ffffffffffffffffffffffffffffffffffffffff8216611f3d5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016108d5565b8273ffffffffffffffffffffffffffffffffffffffff16611f5d826115ee565b73ffffffffffffffffffffffffffffffffffffffff1614611fe65760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016108d5565b600081815260cd6020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811690915573ffffffffffffffffffffffffffffffffffffffff87811680865260cc855283862080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559087168086528386208054600101905586865260cb90945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6000807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f90508060030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f782f67533878b60405180606001604052806000151581526020018c151581526020018d1515815250896040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001604051806020016040528060008152508152506040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001604051806020016040528060008152508152506040518060a0016040528060006dffffffffffffffffffffffffffff16815260200160006dffffffffffffffffffffffffffff168152602001600015158152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152508c60006040518b63ffffffff1660e01b81526004016122479a99989796959493929190613cfc565b6020604051808303816000875af1158015612266573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061228a919061389f565b98975050505050505050565b7f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e36122c18133611c62565b611434576040517f75000dc0000000000000000000000000000000000000000000000000000000008152600481018290523360248201526044016108d5565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff16156123335761098383612b54565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156123b8575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526123b59181019061389f565b60015b61242a5760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f74205555505300000000000000000000000000000000000060648201526084016108d5565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81146124bf5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c6555554944000000000000000000000000000000000000000000000060648201526084016108d5565b50610983838383612c44565b611434828260405180602001604052806000815250612c69565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036125605760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016108d5565b73ffffffffffffffffffffffffffffffffffffffff838116600081815260ce602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b612603848484611e0c565b61260f84848484612cf2565b6117b65760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b600054610100900460ff166126fe5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b565b600054610100900460ff16158080156127205750600054600160ff909116105b8061273a5750303b15801561273a575060005460ff166001145b6127ac5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108d5565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561280a57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b61284a73ffffffffffffffffffffffffffffffffffffffff83167f7965db0b00000000000000000000000000000000000000000000000000000000611d2a565b6128b0576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c00000000000000000000000000000000000060448201526064016108d5565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a250801561143457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020016119a6565b600054610100900460ff16612a175760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b6114348282612ecb565b6000612a4d827f01ffc9a700000000000000000000000000000000000000000000000000000000612a85565b801561075f5750612a7e827fffffffff00000000000000000000000000000000000000000000000000000000612a85565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015612b3d575060208210155b8015612b495750600081115b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81163b612bde5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016108d5565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612c4d83612f61565b600082511180612c5a5750805b15610983576117b68383612fae565b612c7383836130d3565b612c806000848484612cf2565b6109835760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b600073ffffffffffffffffffffffffffffffffffffffff84163b15612ec0576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612d69903390899088908890600401613e39565b6020604051808303816000875af1925050508015612dc2575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612dbf91810190613e82565b60015b612e75573d808015612df0576040519150601f19603f3d011682016040523d82523d6000602084013e612df5565b606091505b508051600003612e6d5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a0200000000000000000000000000000000000000000000000000000000149050611d22565b506001949350505050565b600054610100900460ff16612f485760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b60c9612f548382613e9f565b5060ca6109838282613e9f565b612f6a81612b54565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b606073ffffffffffffffffffffffffffffffffffffffff83163b61303a5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016108d5565b6000808473ffffffffffffffffffffffffffffffffffffffff16846040516130629190613fb9565b600060405180830381855af49150503d806000811461309d576040519150601f19603f3d011682016040523d82523d6000602084013e6130a2565b606091505b50915091506130ca8282604051806060016040528060278152602001613fd6602791396132aa565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff82166131365760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016108d5565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff16156131a85760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016108d5565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff161561321a5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016108d5565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260cc602090815260408083208054600101905584835260cb90915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b606083156132b9575081611d46565b611d4683838151156132ce5781518083602001fd5b8060405162461bcd60e51b81526004016108d591906133a1565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114610e7b57600080fd5b60006020828403121561332857600080fd5b8135611d46816132e8565b60005b8381101561334e578181015183820152602001613336565b50506000910152565b6000815180845261336f816020860160208601613333565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611d466020830184613357565b6000602082840312156133c657600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610e7b57600080fd5b6000806040838503121561340257600080fd5b823561340d816133cd565b946020939093013593505050565b60006020828403121561342d57600080fd5b8135611d46816133cd565b60008060006060848603121561344d57600080fd5b8335613458816133cd565b92506020840135613468816133cd565b929592945050506040919091013590565b8015158114610e7b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff808411156134d1576134d1613487565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561351757613517613487565b8160405280935085815286868601111561353057600080fd5b858560208301376000602087830101525050509392505050565b600080600080600060a0868803121561356257600080fd5b8535945060208601359350604086013561357b81613479565b925060608601359150608086013567ffffffffffffffff81111561359e57600080fd5b8601601f810188136135af57600080fd5b6135be888235602084016134b6565b9150509295509295909350565b60008083601f8401126135dd57600080fd5b50813567ffffffffffffffff8111156135f557600080fd5b60208301915083602082850101111561360d57600080fd5b9250929050565b60008060008060008060008060a0898b03121561363057600080fd5b8835975060208901356007811061364657600080fd5b9650604089013567ffffffffffffffff8082111561366357600080fd5b61366f8c838d016135cb565b909850965060608b013591508082111561368857600080fd5b6136948c838d016135cb565b909650945060808b01359150808211156136ad57600080fd5b506136ba8b828c016135cb565b999c989b5096995094979396929594505050565b6000602082840312156136e057600080fd5b813567ffffffffffffffff8111156136f757600080fd5b820160808185031215611d4657600080fd5b600082601f83011261371a57600080fd5b611d46838335602085016134b6565b6000806040838503121561373c57600080fd5b8235613747816133cd565b9150602083013567ffffffffffffffff81111561376357600080fd5b61376f85828601613709565b9150509250929050565b6000806040838503121561378c57600080fd5b8235613797816133cd565b915060208301356137a781613479565b809150509250929050565b600080600080608085870312156137c857600080fd5b84356137d3816133cd565b935060208501356137e3816133cd565b925060408501359150606085013567ffffffffffffffff81111561380657600080fd5b61381287828801613709565b91505092959194509250565b6000806040838503121561383157600080fd5b823561383c816133cd565b915060208301356137a7816133cd565b600181811c9082168061386057607f821691505b602082108103613899577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156138b157600080fd5b5051919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600060078a1061393a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b89825260a0602083015261395260a08301898b6138b8565b828103604084015261396581888a6138b8565b9050828103606084015261397a8186886138b8565b91505073ffffffffffffffffffffffffffffffffffffffff831660808301529998505050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036139fe577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613a3a57600080fd5b83018035915067ffffffffffffffff821115613a5557600080fd5b60200191503681900382131561360d57600080fd5b878152608060208201526000613a8460808301888a6138b8565b8281036040840152613a978187896138b8565b90508281036060840152610c9f8185876138b8565b600060208284031215613abe57600080fd5b8151611d46816133cd565b601f82111561098357600081815260208120601f850160051c81016020861015613af05750805b601f850160051c820191505b81811015613b0f57828155600101613afc565b505050505050565b67ffffffffffffffff831115613b2f57613b2f613487565b613b4383613b3d835461384c565b83613ac9565b6000601f841160018114613b955760008515613b5f5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355613c2b565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015613be45786850135825560209485019460019092019101613bc4565b5086821015613c1f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff808c1683528a6020840152808a1660408401525060c06060830152613c7260c08301888a6138b8565b8281036080840152613c858187896138b8565b905082810360a0840152613c9a8185876138b8565b9c9b505050505050505050505050565b600060208284031215613cbc57600080fd5b8151611d4681613479565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152611d226040850182613357565b73ffffffffffffffffffffffffffffffffffffffff8b8116825260208083018c905260408084018c90528a51151560608501528a820151151560808501528a0151151560a08401528851821660c084015288810151805190921660e08401520151610100820152600061024080610120840152613d7b81840189613cc7565b9050828103610140840152613d908188613cc7565b86516dffffffffffffffffffffffffffff908116610160860152602088015116610180850152604087015115156101a0850152606087015173ffffffffffffffffffffffffffffffffffffffff166101c085015260808701516101e08501529050828103610200840152613e048186613357565b915050613e2a61022083018473ffffffffffffffffffffffffffffffffffffffff169052565b9b9a5050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152613e786080830184613357565b9695505050505050565b600060208284031215613e9457600080fd5b8151611d46816132e8565b815167ffffffffffffffff811115613eb957613eb9613487565b613ecd81613ec7845461384c565b84613ac9565b602080601f831160018114613f205760008415613eea5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613b0f565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613f6d57888601518255948401946001909101908401613f4e565b5085821015613fa957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008251613fcb818460208701613333565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122069df0d4308aa6890cb1a63ed5fde72e5003558e570cb3043f0e387b70837636e64736f6c63430008130033000000000000000000000000461df243955ad5493cfa33434c2b7cff2c583dc7", + "nonce": "0x45", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", + "transactionType": "CALL", + "contractName": "ERC1967Proxy", + "contractAddress": "0xf157916d184598105FE4681eC93Be0f799599003", + "function": null, + "arguments": null, + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "to": "0xf157916d184598105fe4681ec93be0f799599003", + "gas": "0x10ad1", + "value": "0x0", + "data": "0x3659cfe600000000000000000000000088f3a4cc4eaa2433c44a7c502c7abbd2c76b03e2", + "nonce": "0x46", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x66ecb91002da900a2889cda1ec2af547945c55f6e2f175366566dc483fbb9d0f", + "transactionType": "CALL", + "contractName": "ERC1967Proxy", + "contractAddress": "0xf157916d184598105FE4681eC93Be0f799599003", + "function": null, + "arguments": null, + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "to": "0xf157916d184598105fe4681ec93be0f799599003", + "gas": "0x10757", + "value": "0x0", + "data": "0xcc5b6084000000000000000000000000fbfa729428af206a084e18328ec923587193525d", + "nonce": "0x47", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", + "transactionIndex": "0x1", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": null, + "cumulativeGasUsed": "0x37ace1", + "gasUsed": "0x372304", + "contractAddress": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", + "logs": [ + { + "address": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "transactionHash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", + "transactionIndex": "0x1", + "logIndex": "0x1", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000800000000000000000000000000000000000000000000000040000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0a" + }, + { + "transactionHash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", + "transactionIndex": "0x2", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": "0xf157916d184598105FE4681eC93Be0f799599003", + "cumulativeGasUsed": "0x386e0e", + "gasUsed": "0xc12d", + "contractAddress": null, + "logs": [ + { + "address": "0xf157916d184598105FE4681eC93Be0f799599003", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000088f3a4cc4eaa2433c44a7c502c7abbd2c76b03e2" + ], + "data": "0x", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "transactionHash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", + "transactionIndex": "0x2", + "logIndex": "0x2", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000002000000000000000000000000000400000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0a" + }, + { + "transactionHash": "0x66ecb91002da900a2889cda1ec2af547945c55f6e2f175366566dc483fbb9d0f", + "transactionIndex": "0x3", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": "0xf157916d184598105FE4681eC93Be0f799599003", + "cumulativeGasUsed": "0x392cb6", + "gasUsed": "0xbea8", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0a" + } + ], + "libraries": [], + "pending": [], + "path": "/Users/drmanhattan/Workspace/StoryProtocol/protocol-contracts/broadcast/DevUpgrades.s.sol/5/run-latest.json", + "returns": {}, + "timestamp": 1691870540, + "chain": 5, + "multi": false, + "commit": "53ab00d" +} \ No newline at end of file diff --git a/broadcast/DevUpgrades.s.sol/5/run-1691870567.json b/broadcast/DevUpgrades.s.sol/5/run-1691870567.json new file mode 100644 index 00000000..37eccb8e --- /dev/null +++ b/broadcast/DevUpgrades.s.sol/5/run-1691870567.json @@ -0,0 +1,155 @@ +{ + "transactions": [ + { + "hash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", + "transactionType": "CREATE", + "contractName": "FranchiseRegistry", + "contractAddress": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", + "function": null, + "arguments": [ + "0x461DF243955aD5493cFa33434c2B7CfF2C583dc7" + ], + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "gas": "0x47a825", + "value": "0x0", + "data": "0x60c0604052306080523480156200001557600080fd5b50604051620041ed380380620041ed833981016040819052620000389162000140565b6001600160a01b038116620000605760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b03811660a052620000776200007e565b5062000172565b600054610100900460ff1615620000eb5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156200013e576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6000602082840312156200015357600080fd5b81516001600160a01b03811681146200016b57600080fd5b9392505050565b60805160a051614032620001bb6000396000818161036701526110b8015260008181610cc401528181610d5a0152818161128d01528181611323015261153601526140326000f3fe6080604052600436106101c25760003560e01c8063503fa317116100f7578063a22cb46511610095578063cc5b608411610064578063cc5b6084146105ab578063e985e9c5146105cb578063ef647e1014610621578063fc1dad811461063657600080fd5b8063a22cb4651461052b578063b88d4fde1461054b578063c4d66de81461056b578063c87b56dd1461058b57600080fd5b80636352211e116100d15780636352211e146104c157806370a08231146104e157806395d89b41146105015780639b0c6be41461051657600080fd5b8063503fa3171461044657806352d1902d1461046657806354fd4d501461047b57600080fd5b80632c5cefa3116101645780633b781c171161013e5780633b781c17146103a95780633da52093146103c957806342842e0e146104135780634f1ef2861461043357600080fd5b80632c5cefa3146102f35780632dd31000146103555780633659cfe61461038957600080fd5b8063095ea7b3116101a0578063095ea7b31461026357806319129e5a1461028557806323b872dd146102a5578063290cacf9146102c557600080fd5b806301ffc9a7146101c757806306fdde03146101fc578063081812fc1461021e575b600080fd5b3480156101d357600080fd5b506101e76101e2366004613316565b610680565b60405190151581526020015b60405180910390f35b34801561020857600080fd5b50610211610765565b6040516101f391906133a1565b34801561022a57600080fd5b5061023e6102393660046133b4565b6107f7565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101f3565b34801561026f57600080fd5b5061028361027e3660046133ef565b61082b565b005b34801561029157600080fd5b506102836102a036600461341b565b610988565b3480156102b157600080fd5b506102836102c0366004613438565b610b08565b3480156102d157600080fd5b506102e56102e036600461354a565b610b8f565b6040519081526020016101f3565b3480156102ff57600080fd5b5061023e61030e3660046133b4565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b34801561036157600080fd5b5061023e7f000000000000000000000000000000000000000000000000000000000000000081565b34801561039557600080fd5b506102836103a436600461341b565b610cad565b3480156103b557600080fd5b506102e56103c4366004613614565b610e7e565b3480156103d557600080fd5b506103e96103e43660046136ce565b61105e565b6040805192835273ffffffffffffffffffffffffffffffffffffffff9091166020830152016101f3565b34801561041f57600080fd5b5061028361042e366004613438565b61125b565b610283610441366004613729565b611276565b34801561045257600080fd5b506101e761046136600461341b565b611438565b34801561047257600080fd5b506102e561151c565b34801561048757600080fd5b5060408051808201909152600581527f302e312e300000000000000000000000000000000000000000000000000000006020820152610211565b3480156104cd57600080fd5b5061023e6104dc3660046133b4565b6115ee565b3480156104ed57600080fd5b506102e56104fc36600461341b565b611660565b34801561050d57600080fd5b50610211611714565b34801561052257600080fd5b5061023e600081565b34801561053757600080fd5b50610283610546366004613779565b611723565b34801561055757600080fd5b506102836105663660046137b2565b61172e565b34801561057757600080fd5b5061028361058636600461341b565b6117bc565b34801561059757600080fd5b506102116105a63660046133b4565b6119b2565b3480156105b757600080fd5b506102836105c636600461341b565b611a9e565b3480156105d757600080fd5b506101e76105e636600461381e565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260ce6020908152604080832093909416825291909152205460ff1690565b34801561062d57600080fd5b506102e5600081565b34801561064257600080fd5b507f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b65473ffffffffffffffffffffffffffffffffffffffff1661023e565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061071357507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061075f57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b606060c980546107749061384c565b80601f01602080910402602001604051908101604052809291908181526020018280546107a09061384c565b80156107ed5780601f106107c2576101008083540402835291602001916107ed565b820191906000526020600020905b8154815290600101906020018083116107d057829003601f168201915b5050505050905090565b600061080282611b51565b50600090815260cd602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b6000610836826115ee565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108de5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82161480610907575061090781336105e6565b6109795760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016108d5565b6109838383611bc2565b505050565b60006109948133611c62565b6109d3576040517f75000dc0000000000000000000000000000000000000000000000000000000008152600481018290523360248201526044016108d5565b610a1373ffffffffffffffffffffffffffffffffffffffff83167f7965db0b00000000000000000000000000000000000000000000000000000000611d2a565b610a79576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c00000000000000000000000000000000000060448201526064016108d5565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a2505050565b610b123382611d4d565b610b845760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016108d5565b610983838383611e0c565b6000807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f600088815260018201602052604080822054600384015491517f2fb49eff00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201819052602482018c9052949550911690632fb49eff90604401602060405180830381865afa158015610c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c65919061389f565b604080518082018252338152815180830190925260008083526020838101829052820192909252919250610c9f9088908a9085858b6120b9565b9a9950505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610d585760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c000000000000000000000000000000000000000060648201526084016108d5565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16610dcd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614610e565760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f7879000000000000000000000000000000000000000060648201526084016108d5565b610e5f81612296565b60408051600080825260208201909252610e7b91839190612300565b50565b60008881527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e9060205260408082205490517f99258ca10000000000000000000000000000000000000000000000000000000081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f9173ffffffffffffffffffffffffffffffffffffffff169081906399258ca190610f2f908d908d908d908d908d908d908d903390600401613901565b6020604051808303816000875af1158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f72919061389f565b925060006040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160405180604001604052808573ffffffffffffffffffffffffffffffffffffffff16815260200187815250815250905061100d7f781e9f3557d8768f1963c458e1b351a046f8e4f612fd4a2c28f5132ee0c0c87b600180600085604051806020016040528060008152506120b9565b5061104e7facb6e50a24ac7b7075ce347695829fdb25124f5d7485df10299c2e269a3a9f8360006001600085604051806020016040528060008152506120b9565b5050505098975050505050505050565b600080807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f905060008160000160008154611098906139a6565b91829055509050600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166386df779e836110e88980613a05565b6110f560208c018c613a05565b61110260408e018e613a05565b6040518863ffffffff1660e01b81526004016111249796959493929190613a6a565b6020604051808303816000875af1158015611143573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111679190613aac565b6000838152600185016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590506111c46060870187613a05565b60008481526002860160205260409020916111e0919083613b17565b506111eb33836124cb565b7f071096a6b974f736d72b386641c048d2b40ab6731f5e6da2a22c95b530e81bd73383836112198a80613a05565b61122660208d018d613a05565b61123360608f018f613a05565b60405161124899989796959493929190613c32565b60405180910390a1909590945092505050565b6109838383836040518060200160405280600081525061172e565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001630036113215760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c000000000000000000000000000000000000000060648201526084016108d5565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166113967f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161461141f5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f7879000000000000000000000000000000000000000060648201526084016108d5565b61142882612296565b61143482826001612300565b5050565b60008173ffffffffffffffffffffffffffffffffffffffff1663b9d0787c6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156114bf575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526114bc9181019061389f565b60015b6114cb57506000919050565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff908116921691909114919050565b60003073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146115c95760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c000000000000000060648201526084016108d5565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b600081815260cb602052604081205473ffffffffffffffffffffffffffffffffffffffff168061075f5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016108d5565b600073ffffffffffffffffffffffffffffffffffffffff82166116eb5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e6572000000000000000000000000000000000000000000000060648201526084016108d5565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260cc602052604090205490565b606060ca80546107749061384c565b6114343383836124e5565b6117383383611d4d565b6117aa5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016108d5565b6117b6848484846125f8565b50505050565b600054610100900460ff16158080156117dc5750600054600160ff909116105b806117f65750303b1580156117f6575060005460ff166001145b6118685760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108d5565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156118c657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6118ce612681565b6118d782612700565b61194b6040518060400160405280600e81526020017f53746f72792050726f746f636f6c0000000000000000000000000000000000008152506040518060400160405280600281526020017f535000000000000000000000000000000000000000000000000000000000000081525061299a565b801561143457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b60606119bd82611b51565b60008281527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e916020526040902080547f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f9190611a189061384c565b80601f0160208091040260200160405190810160405280929190818152602001828054611a449061384c565b8015611a915780601f10611a6657610100808354040283529160200191611a91565b820191906000526020600020905b815481529060010190602001808311611a7457829003601f168201915b5050505050915050919050565b73ffffffffffffffffffffffffffffffffffffffff8116611aeb576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e9280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff16610e7b5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016108d5565b600081815260cd6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091558190611c1c826115ee565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000807f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680546040517f91d148540000000000000000000000000000000000000000000000000000000081526004810187905273ffffffffffffffffffffffffffffffffffffffff86811660248301529293509116906391d1485490604401602060405180830381865afa158015611cfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d229190613caa565b949350505050565b6000611d3583612a21565b8015611d465750611d468383612a85565b9392505050565b600080611d59836115ee565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611dc7575073ffffffffffffffffffffffffffffffffffffffff808216600090815260ce602090815260408083209388168352929052205460ff165b80611d2257508373ffffffffffffffffffffffffffffffffffffffff16611ded846107f7565b73ffffffffffffffffffffffffffffffffffffffff1614949350505050565b8273ffffffffffffffffffffffffffffffffffffffff16611e2c826115ee565b73ffffffffffffffffffffffffffffffffffffffff1614611eb55760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016108d5565b73ffffffffffffffffffffffffffffffffffffffff8216611f3d5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016108d5565b8273ffffffffffffffffffffffffffffffffffffffff16611f5d826115ee565b73ffffffffffffffffffffffffffffffffffffffff1614611fe65760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016108d5565b600081815260cd6020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811690915573ffffffffffffffffffffffffffffffffffffffff87811680865260cc855283862080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559087168086528386208054600101905586865260cb90945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6000807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f90508060030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f782f67533878b60405180606001604052806000151581526020018c151581526020018d1515815250896040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001604051806020016040528060008152508152506040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001604051806020016040528060008152508152506040518060a0016040528060006dffffffffffffffffffffffffffff16815260200160006dffffffffffffffffffffffffffff168152602001600015158152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152508c60006040518b63ffffffff1660e01b81526004016122479a99989796959493929190613cfc565b6020604051808303816000875af1158015612266573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061228a919061389f565b98975050505050505050565b7f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e36122c18133611c62565b611434576040517f75000dc0000000000000000000000000000000000000000000000000000000008152600481018290523360248201526044016108d5565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff16156123335761098383612b54565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156123b8575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526123b59181019061389f565b60015b61242a5760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f74205555505300000000000000000000000000000000000060648201526084016108d5565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81146124bf5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c6555554944000000000000000000000000000000000000000000000060648201526084016108d5565b50610983838383612c44565b611434828260405180602001604052806000815250612c69565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036125605760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016108d5565b73ffffffffffffffffffffffffffffffffffffffff838116600081815260ce602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b612603848484611e0c565b61260f84848484612cf2565b6117b65760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b600054610100900460ff166126fe5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b565b600054610100900460ff16158080156127205750600054600160ff909116105b8061273a5750303b15801561273a575060005460ff166001145b6127ac5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108d5565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561280a57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b61284a73ffffffffffffffffffffffffffffffffffffffff83167f7965db0b00000000000000000000000000000000000000000000000000000000611d2a565b6128b0576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c00000000000000000000000000000000000060448201526064016108d5565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a250801561143457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020016119a6565b600054610100900460ff16612a175760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b6114348282612ecb565b6000612a4d827f01ffc9a700000000000000000000000000000000000000000000000000000000612a85565b801561075f5750612a7e827fffffffff00000000000000000000000000000000000000000000000000000000612a85565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015612b3d575060208210155b8015612b495750600081115b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81163b612bde5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016108d5565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612c4d83612f61565b600082511180612c5a5750805b15610983576117b68383612fae565b612c7383836130d3565b612c806000848484612cf2565b6109835760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b600073ffffffffffffffffffffffffffffffffffffffff84163b15612ec0576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612d69903390899088908890600401613e39565b6020604051808303816000875af1925050508015612dc2575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612dbf91810190613e82565b60015b612e75573d808015612df0576040519150601f19603f3d011682016040523d82523d6000602084013e612df5565b606091505b508051600003612e6d5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a0200000000000000000000000000000000000000000000000000000000149050611d22565b506001949350505050565b600054610100900460ff16612f485760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b60c9612f548382613e9f565b5060ca6109838282613e9f565b612f6a81612b54565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b606073ffffffffffffffffffffffffffffffffffffffff83163b61303a5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016108d5565b6000808473ffffffffffffffffffffffffffffffffffffffff16846040516130629190613fb9565b600060405180830381855af49150503d806000811461309d576040519150601f19603f3d011682016040523d82523d6000602084013e6130a2565b606091505b50915091506130ca8282604051806060016040528060278152602001613fd6602791396132aa565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff82166131365760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016108d5565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff16156131a85760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016108d5565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff161561321a5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016108d5565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260cc602090815260408083208054600101905584835260cb90915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b606083156132b9575081611d46565b611d4683838151156132ce5781518083602001fd5b8060405162461bcd60e51b81526004016108d591906133a1565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114610e7b57600080fd5b60006020828403121561332857600080fd5b8135611d46816132e8565b60005b8381101561334e578181015183820152602001613336565b50506000910152565b6000815180845261336f816020860160208601613333565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611d466020830184613357565b6000602082840312156133c657600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610e7b57600080fd5b6000806040838503121561340257600080fd5b823561340d816133cd565b946020939093013593505050565b60006020828403121561342d57600080fd5b8135611d46816133cd565b60008060006060848603121561344d57600080fd5b8335613458816133cd565b92506020840135613468816133cd565b929592945050506040919091013590565b8015158114610e7b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff808411156134d1576134d1613487565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561351757613517613487565b8160405280935085815286868601111561353057600080fd5b858560208301376000602087830101525050509392505050565b600080600080600060a0868803121561356257600080fd5b8535945060208601359350604086013561357b81613479565b925060608601359150608086013567ffffffffffffffff81111561359e57600080fd5b8601601f810188136135af57600080fd5b6135be888235602084016134b6565b9150509295509295909350565b60008083601f8401126135dd57600080fd5b50813567ffffffffffffffff8111156135f557600080fd5b60208301915083602082850101111561360d57600080fd5b9250929050565b60008060008060008060008060a0898b03121561363057600080fd5b8835975060208901356007811061364657600080fd5b9650604089013567ffffffffffffffff8082111561366357600080fd5b61366f8c838d016135cb565b909850965060608b013591508082111561368857600080fd5b6136948c838d016135cb565b909650945060808b01359150808211156136ad57600080fd5b506136ba8b828c016135cb565b999c989b5096995094979396929594505050565b6000602082840312156136e057600080fd5b813567ffffffffffffffff8111156136f757600080fd5b820160808185031215611d4657600080fd5b600082601f83011261371a57600080fd5b611d46838335602085016134b6565b6000806040838503121561373c57600080fd5b8235613747816133cd565b9150602083013567ffffffffffffffff81111561376357600080fd5b61376f85828601613709565b9150509250929050565b6000806040838503121561378c57600080fd5b8235613797816133cd565b915060208301356137a781613479565b809150509250929050565b600080600080608085870312156137c857600080fd5b84356137d3816133cd565b935060208501356137e3816133cd565b925060408501359150606085013567ffffffffffffffff81111561380657600080fd5b61381287828801613709565b91505092959194509250565b6000806040838503121561383157600080fd5b823561383c816133cd565b915060208301356137a7816133cd565b600181811c9082168061386057607f821691505b602082108103613899577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156138b157600080fd5b5051919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600060078a1061393a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b89825260a0602083015261395260a08301898b6138b8565b828103604084015261396581888a6138b8565b9050828103606084015261397a8186886138b8565b91505073ffffffffffffffffffffffffffffffffffffffff831660808301529998505050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036139fe577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613a3a57600080fd5b83018035915067ffffffffffffffff821115613a5557600080fd5b60200191503681900382131561360d57600080fd5b878152608060208201526000613a8460808301888a6138b8565b8281036040840152613a978187896138b8565b90508281036060840152610c9f8185876138b8565b600060208284031215613abe57600080fd5b8151611d46816133cd565b601f82111561098357600081815260208120601f850160051c81016020861015613af05750805b601f850160051c820191505b81811015613b0f57828155600101613afc565b505050505050565b67ffffffffffffffff831115613b2f57613b2f613487565b613b4383613b3d835461384c565b83613ac9565b6000601f841160018114613b955760008515613b5f5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355613c2b565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015613be45786850135825560209485019460019092019101613bc4565b5086821015613c1f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff808c1683528a6020840152808a1660408401525060c06060830152613c7260c08301888a6138b8565b8281036080840152613c858187896138b8565b905082810360a0840152613c9a8185876138b8565b9c9b505050505050505050505050565b600060208284031215613cbc57600080fd5b8151611d4681613479565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152611d226040850182613357565b73ffffffffffffffffffffffffffffffffffffffff8b8116825260208083018c905260408084018c90528a51151560608501528a820151151560808501528a0151151560a08401528851821660c084015288810151805190921660e08401520151610100820152600061024080610120840152613d7b81840189613cc7565b9050828103610140840152613d908188613cc7565b86516dffffffffffffffffffffffffffff908116610160860152602088015116610180850152604087015115156101a0850152606087015173ffffffffffffffffffffffffffffffffffffffff166101c085015260808701516101e08501529050828103610200840152613e048186613357565b915050613e2a61022083018473ffffffffffffffffffffffffffffffffffffffff169052565b9b9a5050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152613e786080830184613357565b9695505050505050565b600060208284031215613e9457600080fd5b8151611d46816132e8565b815167ffffffffffffffff811115613eb957613eb9613487565b613ecd81613ec7845461384c565b84613ac9565b602080601f831160018114613f205760008415613eea5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613b0f565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613f6d57888601518255948401946001909101908401613f4e565b5085821015613fa957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008251613fcb818460208701613333565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122069df0d4308aa6890cb1a63ed5fde72e5003558e570cb3043f0e387b70837636e64736f6c63430008130033000000000000000000000000461df243955ad5493cfa33434c2b7cff2c583dc7", + "nonce": "0x45", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", + "transactionType": "CALL", + "contractName": "ERC1967Proxy", + "contractAddress": "0xf157916d184598105FE4681eC93Be0f799599003", + "function": null, + "arguments": null, + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "to": "0xf157916d184598105fe4681ec93be0f799599003", + "gas": "0x10ad1", + "value": "0x0", + "data": "0x3659cfe600000000000000000000000088f3a4cc4eaa2433c44a7c502c7abbd2c76b03e2", + "nonce": "0x46", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x66ecb91002da900a2889cda1ec2af547945c55f6e2f175366566dc483fbb9d0f", + "transactionType": "CALL", + "contractName": "ERC1967Proxy", + "contractAddress": "0xf157916d184598105FE4681eC93Be0f799599003", + "function": null, + "arguments": null, + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "to": "0xf157916d184598105fe4681ec93be0f799599003", + "gas": "0x10757", + "value": "0x0", + "data": "0xcc5b6084000000000000000000000000fbfa729428af206a084e18328ec923587193525d", + "nonce": "0x47", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", + "transactionIndex": "0x1", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": null, + "cumulativeGasUsed": "0x37ace1", + "gasUsed": "0x372304", + "contractAddress": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", + "logs": [ + { + "address": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "transactionHash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", + "transactionIndex": "0x1", + "logIndex": "0x1", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000800000000000000000000000000000000000000000000000040000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0a" + }, + { + "transactionHash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", + "transactionIndex": "0x2", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": "0xf157916d184598105FE4681eC93Be0f799599003", + "cumulativeGasUsed": "0x386e0e", + "gasUsed": "0xc12d", + "contractAddress": null, + "logs": [ + { + "address": "0xf157916d184598105FE4681eC93Be0f799599003", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000088f3a4cc4eaa2433c44a7c502c7abbd2c76b03e2" + ], + "data": "0x", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "transactionHash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", + "transactionIndex": "0x2", + "logIndex": "0x2", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000002000000000000000000000000000400000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0a" + }, + { + "transactionHash": "0x66ecb91002da900a2889cda1ec2af547945c55f6e2f175366566dc483fbb9d0f", + "transactionIndex": "0x3", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": "0xf157916d184598105FE4681eC93Be0f799599003", + "cumulativeGasUsed": "0x392cb6", + "gasUsed": "0xbea8", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0a" + } + ], + "libraries": [], + "pending": [], + "path": "/Users/drmanhattan/Workspace/StoryProtocol/protocol-contracts/broadcast/DevUpgrades.s.sol/5/run-latest.json", + "returns": {}, + "timestamp": 1691870567, + "chain": 5, + "multi": false, + "commit": "53ab00d" +} \ No newline at end of file diff --git a/broadcast/DevUpgrades.s.sol/5/run-latest.json b/broadcast/DevUpgrades.s.sol/5/run-latest.json index 7c03deb8..37eccb8e 100644 --- a/broadcast/DevUpgrades.s.sol/5/run-latest.json +++ b/broadcast/DevUpgrades.s.sol/5/run-latest.json @@ -1,10 +1,10 @@ { "transactions": [ { - "hash": "0xb3cf4327d9f0809ec44fe7f41a762d0931cb452d27f082cdd01ab2c4f93b7746", + "hash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", "transactionType": "CREATE", "contractName": "FranchiseRegistry", - "contractAddress": "0x08173A3D97aeAE39078a5714279c2233B270592e", + "contractAddress": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", "function": null, "arguments": [ "0x461DF243955aD5493cFa33434c2B7CfF2C583dc7" @@ -13,17 +13,17 @@ "transaction": { "type": "0x02", "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", - "gas": "0x3d2482", + "gas": "0x47a825", "value": "0x0", - "data": "0x60c0604052306080523480156200001557600080fd5b506040516200384f3803806200384f833981016040819052620000389162000140565b6001600160a01b038116620000605760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b03811660a052620000776200007e565b5062000172565b600054610100900460ff1615620000eb5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156200013e576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6000602082840312156200015357600080fd5b81516001600160a01b03811681146200016b57600080fd5b9392505050565b60805160a051613694620001bb600039600081816103180152610d39015260008181610b2501528181610bbb01528181610f0e01528181610fa401526111b701526136946000f3fe6080604052600436106101a15760003560e01c806352d1902d116100e1578063a22cb4651161008a578063c87b56dd11610064578063c87b56dd1461052a578063e985e9c51461054a578063ef647e10146105a0578063fc1dad81146105b557600080fd5b8063a22cb465146104ca578063b88d4fde146104ea578063c4d66de81461050a57600080fd5b806370a08231116100bb57806370a082311461048057806395d89b41146104a05780639b0c6be4146104b557600080fd5b806352d1902d146103f757806354fd4d501461041a5780636352211e1461046057600080fd5b80632c5cefa31161014e5780633da52093116101285780633da520931461035a57806342842e0e146103a45780634f1ef286146103c4578063503fa317146103d757600080fd5b80632c5cefa3146102a45780632dd31000146103065780633659cfe61461033a57600080fd5b8063095ea7b31161017f578063095ea7b31461024257806319129e5a1461026457806323b872dd1461028457600080fd5b806301ffc9a7146101a657806306fdde03146101db578063081812fc146101fd575b600080fd5b3480156101b257600080fd5b506101c66101c1366004612d07565b6105ff565b60405190151581526020015b60405180910390f35b3480156101e757600080fd5b506101f06106e4565b6040516101d29190612d92565b34801561020957600080fd5b5061021d610218366004612da5565b610776565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d2565b34801561024e57600080fd5b5061026261025d366004612de0565b6107aa565b005b34801561027057600080fd5b5061026261027f366004612e0c565b610907565b34801561029057600080fd5b5061026261029f366004612e29565b610a87565b3480156102b057600080fd5b5061021d6102bf366004612da5565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b34801561031257600080fd5b5061021d7f000000000000000000000000000000000000000000000000000000000000000081565b34801561034657600080fd5b50610262610355366004612e0c565b610b0e565b34801561036657600080fd5b5061037a610375366004612e6a565b610cdf565b6040805192835273ffffffffffffffffffffffffffffffffffffffff9091166020830152016101d2565b3480156103b057600080fd5b506102626103bf366004612e29565b610edc565b6102626103d2366004612f7f565b610ef7565b3480156103e357600080fd5b506101c66103f2366004612e0c565b6110b9565b34801561040357600080fd5b5061040c61119d565b6040519081526020016101d2565b34801561042657600080fd5b5060408051808201909152600581527f302e312e3000000000000000000000000000000000000000000000000000000060208201526101f0565b34801561046c57600080fd5b5061021d61047b366004612da5565b61126f565b34801561048c57600080fd5b5061040c61049b366004612e0c565b6112e1565b3480156104ac57600080fd5b506101f0611395565b3480156104c157600080fd5b5061021d600081565b3480156104d657600080fd5b506102626104e5366004612fdd565b6113a4565b3480156104f657600080fd5b50610262610505366004613016565b6113af565b34801561051657600080fd5b50610262610525366004612e0c565b61143d565b34801561053657600080fd5b506101f0610545366004612da5565b611633565b34801561055657600080fd5b506101c6610565366004613082565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260ce6020908152604080832093909416825291909152205460ff1690565b3480156105ac57600080fd5b5061040c600081565b3480156105c157600080fd5b507f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b65473ffffffffffffffffffffffffffffffffffffffff1661021d565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061069257507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b806106de57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b606060c980546106f3906130b0565b80601f016020809104026020016040519081016040528092919081815260200182805461071f906130b0565b801561076c5780601f106107415761010080835404028352916020019161076c565b820191906000526020600020905b81548152906001019060200180831161074f57829003601f168201915b5050505050905090565b60006107818261171f565b50600090815260cd602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60006107b58261126f565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361085d5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216148061088657506108868133610565565b6108f85760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c0000006064820152608401610854565b6109028383611790565b505050565b60006109138133611830565b610952576040517f75000dc000000000000000000000000000000000000000000000000000000000815260048101829052336024820152604401610854565b61099273ffffffffffffffffffffffffffffffffffffffff83167f7965db0b000000000000000000000000000000000000000000000000000000006118f8565b6109f8576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c0000000000000000000000000000000000006044820152606401610854565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a2505050565b610a91338261191b565b610b035760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f766564000000000000000000000000000000000000006064820152608401610854565b6109028383836119da565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610bb95760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610854565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16610c2e7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614610cb75760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610854565b610cc081611c87565b60408051600080825260208201909252610cdc91839190611cf1565b50565b600080807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f905060008160000160008154610d1990613103565b91829055509050600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166386df779e83610d698980613162565b610d7660208c018c613162565b610d8360408e018e613162565b6040518863ffffffff1660e01b8152600401610da59796959493929190613217565b6020604051808303816000875af1158015610dc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de89190613267565b6000838152600185016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790559050610e456060870187613162565b6000848152600286016020526040902091610e619190836132d2565b50610e6c3383611ebc565b7f071096a6b974f736d72b386641c048d2b40ab6731f5e6da2a22c95b530e81bd7338383610e9a8a80613162565b610ea760208d018d613162565b610eb460608f018f613162565b604051610ec9999897969594939291906133ed565b60405180910390a1909590945092505050565b610902838383604051806020016040528060008152506113af565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610fa25760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610854565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166110177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff16146110a05760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610854565b6110a982611c87565b6110b582826001611cf1565b5050565b60008173ffffffffffffffffffffffffffffffffffffffff1663b9d0787c6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611140575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261113d91810190613465565b60015b61114c57506000919050565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff908116921691909114919050565b60003073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461124a5760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c00000000000000006064820152608401610854565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b600081815260cb602052604081205473ffffffffffffffffffffffffffffffffffffffff16806106de5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606401610854565b600073ffffffffffffffffffffffffffffffffffffffff821661136c5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e657200000000000000000000000000000000000000000000006064820152608401610854565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260cc602052604090205490565b606060ca80546106f3906130b0565b6110b5338383611ed6565b6113b9338361191b565b61142b5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f766564000000000000000000000000000000000000006064820152608401610854565b61143784848484611fe9565b50505050565b600054610100900460ff161580801561145d5750600054600160ff909116105b806114775750303b158015611477575060005460ff166001145b6114e95760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610854565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561154757600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b61154f612072565b611558826120f1565b6115cc6040518060400160405280600e81526020017f53746f72792050726f746f636f6c0000000000000000000000000000000000008152506040518060400160405280600281526020017f535000000000000000000000000000000000000000000000000000000000000081525061238b565b80156110b557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b606061163e8261171f565b60008281527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e916020526040902080547f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f9190611699906130b0565b80601f01602080910402602001604051908101604052809291908181526020018280546116c5906130b0565b80156117125780601f106116e757610100808354040283529160200191611712565b820191906000526020600020905b8154815290600101906020018083116116f557829003601f168201915b5050505050915050919050565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff16610cdc5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606401610854565b600081815260cd6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811790915581906117ea8261126f565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000807f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680546040517f91d148540000000000000000000000000000000000000000000000000000000081526004810187905273ffffffffffffffffffffffffffffffffffffffff86811660248301529293509116906391d1485490604401602060405180830381865afa1580156118cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118f0919061347e565b949350505050565b600061190383612412565b801561191457506119148383612476565b9392505050565b6000806119278361126f565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611995575073ffffffffffffffffffffffffffffffffffffffff808216600090815260ce602090815260408083209388168352929052205460ff165b806118f057508373ffffffffffffffffffffffffffffffffffffffff166119bb84610776565b73ffffffffffffffffffffffffffffffffffffffff1614949350505050565b8273ffffffffffffffffffffffffffffffffffffffff166119fa8261126f565b73ffffffffffffffffffffffffffffffffffffffff1614611a835760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e65720000000000000000000000000000000000000000000000000000006064820152608401610854565b73ffffffffffffffffffffffffffffffffffffffff8216611b0b5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610854565b8273ffffffffffffffffffffffffffffffffffffffff16611b2b8261126f565b73ffffffffffffffffffffffffffffffffffffffff1614611bb45760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e65720000000000000000000000000000000000000000000000000000006064820152608401610854565b600081815260cd6020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811690915573ffffffffffffffffffffffffffffffffffffffff87811680865260cc855283862080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559087168086528386208054600101905586865260cb90945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b7f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e3611cb28133611830565b6110b5576040517f75000dc000000000000000000000000000000000000000000000000000000000815260048101829052336024820152604401610854565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615611d245761090283612545565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611da9575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252611da691810190613465565b60015b611e1b5760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f7420555550530000000000000000000000000000000000006064820152608401610854565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8114611eb05760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c655555494400000000000000000000000000000000000000000000006064820152608401610854565b50610902838383612635565b6110b582826040518060200160405280600081525061265a565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611f515760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610854565b73ffffffffffffffffffffffffffffffffffffffff838116600081815260ce602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611ff48484846119da565b612000848484846126e3565b6114375760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610854565b600054610100900460ff166120ef5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610854565b565b600054610100900460ff16158080156121115750600054600160ff909116105b8061212b5750303b15801561212b575060005460ff166001145b61219d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610854565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156121fb57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b61223b73ffffffffffffffffffffffffffffffffffffffff83167f7965db0b000000000000000000000000000000000000000000000000000000006118f8565b6122a1576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c0000000000000000000000000000000000006044820152606401610854565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a25080156110b557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001611627565b600054610100900460ff166124085760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610854565b6110b582826128bc565b600061243e827f01ffc9a700000000000000000000000000000000000000000000000000000000612476565b80156106de575061246f827fffffffff00000000000000000000000000000000000000000000000000000000612476565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d9150600051905082801561252e575060208210155b801561253a5750600081115b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81163b6125cf5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e7472616374000000000000000000000000000000000000006064820152608401610854565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b61263e83612952565b60008251118061264b5750805b1561090257611437838361299f565b6126648383612ac4565b61267160008484846126e3565b6109025760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610854565b600073ffffffffffffffffffffffffffffffffffffffff84163b156128b1576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a029061275a90339089908890889060040161349b565b6020604051808303816000875af19250505080156127b3575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526127b0918101906134e4565b60015b612866573d8080156127e1576040519150601f19603f3d011682016040523d82523d6000602084013e6127e6565b606091505b50805160000361285e5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610854565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a02000000000000000000000000000000000000000000000000000000001490506118f0565b506001949350505050565b600054610100900460ff166129395760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610854565b60c96129458382613501565b5060ca6109028282613501565b61295b81612545565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b606073ffffffffffffffffffffffffffffffffffffffff83163b612a2b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610854565b6000808473ffffffffffffffffffffffffffffffffffffffff1684604051612a53919061361b565b600060405180830381855af49150503d8060008114612a8e576040519150601f19603f3d011682016040523d82523d6000602084013e612a93565b606091505b5091509150612abb828260405180606001604052806027815260200161363860279139612c9b565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff8216612b275760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610854565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612b995760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610854565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612c0b5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610854565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260cc602090815260408083208054600101905584835260cb90915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60608315612caa575081611914565b6119148383815115612cbf5781518083602001fd5b8060405162461bcd60e51b81526004016108549190612d92565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114610cdc57600080fd5b600060208284031215612d1957600080fd5b813561191481612cd9565b60005b83811015612d3f578181015183820152602001612d27565b50506000910152565b60008151808452612d60816020860160208601612d24565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006119146020830184612d48565b600060208284031215612db757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610cdc57600080fd5b60008060408385031215612df357600080fd5b8235612dfe81612dbe565b946020939093013593505050565b600060208284031215612e1e57600080fd5b813561191481612dbe565b600080600060608486031215612e3e57600080fd5b8335612e4981612dbe565b92506020840135612e5981612dbe565b929592945050506040919091013590565b600060208284031215612e7c57600080fd5b813567ffffffffffffffff811115612e9357600080fd5b82016080818503121561191457600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112612ee557600080fd5b813567ffffffffffffffff80821115612f0057612f00612ea5565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715612f4657612f46612ea5565b81604052838152866020858801011115612f5f57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060408385031215612f9257600080fd5b8235612f9d81612dbe565b9150602083013567ffffffffffffffff811115612fb957600080fd5b612fc585828601612ed4565b9150509250929050565b8015158114610cdc57600080fd5b60008060408385031215612ff057600080fd5b8235612ffb81612dbe565b9150602083013561300b81612fcf565b809150509250929050565b6000806000806080858703121561302c57600080fd5b843561303781612dbe565b9350602085013561304781612dbe565b925060408501359150606085013567ffffffffffffffff81111561306a57600080fd5b61307687828801612ed4565b91505092959194509250565b6000806040838503121561309557600080fd5b82356130a081612dbe565b9150602083013561300b81612dbe565b600181811c908216806130c457607f821691505b6020821081036130fd577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361315b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261319757600080fd5b83018035915067ffffffffffffffff8211156131b257600080fd5b6020019150368190038213156131c757600080fd5b9250929050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b87815260806020820152600061323160808301888a6131ce565b82810360408401526132448187896131ce565b905082810360608401526132598185876131ce565b9a9950505050505050505050565b60006020828403121561327957600080fd5b815161191481612dbe565b601f82111561090257600081815260208120601f850160051c810160208610156132ab5750805b601f850160051c820191505b818110156132ca578281556001016132b7565b505050505050565b67ffffffffffffffff8311156132ea576132ea612ea5565b6132fe836132f883546130b0565b83613284565b6000601f841160018114613350576000851561331a5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556133e6565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561339f578685013582556020948501946001909201910161337f565b50868210156133da577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff808c1683528a6020840152808a1660408401525060c0606083015261342d60c08301888a6131ce565b82810360808401526134408187896131ce565b905082810360a08401526134558185876131ce565b9c9b505050505050505050505050565b60006020828403121561347757600080fd5b5051919050565b60006020828403121561349057600080fd5b815161191481612fcf565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250836040830152608060608301526134da6080830184612d48565b9695505050505050565b6000602082840312156134f657600080fd5b815161191481612cd9565b815167ffffffffffffffff81111561351b5761351b612ea5565b61352f8161352984546130b0565b84613284565b602080601f831160018114613582576000841561354c5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556132ca565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156135cf578886015182559484019460019091019084016135b0565b508582101561360b57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b6000825161362d818460208701612d24565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e799166ebc5a522412795994a9f93f82408c2d3707cb2ceabb5906d4f648638364736f6c63430008130033000000000000000000000000461df243955ad5493cfa33434c2b7cff2c583dc7", - "nonce": "0x41", + "data": "0x60c0604052306080523480156200001557600080fd5b50604051620041ed380380620041ed833981016040819052620000389162000140565b6001600160a01b038116620000605760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b03811660a052620000776200007e565b5062000172565b600054610100900460ff1615620000eb5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156200013e576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6000602082840312156200015357600080fd5b81516001600160a01b03811681146200016b57600080fd5b9392505050565b60805160a051614032620001bb6000396000818161036701526110b8015260008181610cc401528181610d5a0152818161128d01528181611323015261153601526140326000f3fe6080604052600436106101c25760003560e01c8063503fa317116100f7578063a22cb46511610095578063cc5b608411610064578063cc5b6084146105ab578063e985e9c5146105cb578063ef647e1014610621578063fc1dad811461063657600080fd5b8063a22cb4651461052b578063b88d4fde1461054b578063c4d66de81461056b578063c87b56dd1461058b57600080fd5b80636352211e116100d15780636352211e146104c157806370a08231146104e157806395d89b41146105015780639b0c6be41461051657600080fd5b8063503fa3171461044657806352d1902d1461046657806354fd4d501461047b57600080fd5b80632c5cefa3116101645780633b781c171161013e5780633b781c17146103a95780633da52093146103c957806342842e0e146104135780634f1ef2861461043357600080fd5b80632c5cefa3146102f35780632dd31000146103555780633659cfe61461038957600080fd5b8063095ea7b3116101a0578063095ea7b31461026357806319129e5a1461028557806323b872dd146102a5578063290cacf9146102c557600080fd5b806301ffc9a7146101c757806306fdde03146101fc578063081812fc1461021e575b600080fd5b3480156101d357600080fd5b506101e76101e2366004613316565b610680565b60405190151581526020015b60405180910390f35b34801561020857600080fd5b50610211610765565b6040516101f391906133a1565b34801561022a57600080fd5b5061023e6102393660046133b4565b6107f7565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101f3565b34801561026f57600080fd5b5061028361027e3660046133ef565b61082b565b005b34801561029157600080fd5b506102836102a036600461341b565b610988565b3480156102b157600080fd5b506102836102c0366004613438565b610b08565b3480156102d157600080fd5b506102e56102e036600461354a565b610b8f565b6040519081526020016101f3565b3480156102ff57600080fd5b5061023e61030e3660046133b4565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b34801561036157600080fd5b5061023e7f000000000000000000000000000000000000000000000000000000000000000081565b34801561039557600080fd5b506102836103a436600461341b565b610cad565b3480156103b557600080fd5b506102e56103c4366004613614565b610e7e565b3480156103d557600080fd5b506103e96103e43660046136ce565b61105e565b6040805192835273ffffffffffffffffffffffffffffffffffffffff9091166020830152016101f3565b34801561041f57600080fd5b5061028361042e366004613438565b61125b565b610283610441366004613729565b611276565b34801561045257600080fd5b506101e761046136600461341b565b611438565b34801561047257600080fd5b506102e561151c565b34801561048757600080fd5b5060408051808201909152600581527f302e312e300000000000000000000000000000000000000000000000000000006020820152610211565b3480156104cd57600080fd5b5061023e6104dc3660046133b4565b6115ee565b3480156104ed57600080fd5b506102e56104fc36600461341b565b611660565b34801561050d57600080fd5b50610211611714565b34801561052257600080fd5b5061023e600081565b34801561053757600080fd5b50610283610546366004613779565b611723565b34801561055757600080fd5b506102836105663660046137b2565b61172e565b34801561057757600080fd5b5061028361058636600461341b565b6117bc565b34801561059757600080fd5b506102116105a63660046133b4565b6119b2565b3480156105b757600080fd5b506102836105c636600461341b565b611a9e565b3480156105d757600080fd5b506101e76105e636600461381e565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260ce6020908152604080832093909416825291909152205460ff1690565b34801561062d57600080fd5b506102e5600081565b34801561064257600080fd5b507f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b65473ffffffffffffffffffffffffffffffffffffffff1661023e565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd00000000000000000000000000000000000000000000000000000000148061071357507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b8061075f57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b606060c980546107749061384c565b80601f01602080910402602001604051908101604052809291908181526020018280546107a09061384c565b80156107ed5780601f106107c2576101008083540402835291602001916107ed565b820191906000526020600020905b8154815290600101906020018083116107d057829003601f168201915b5050505050905090565b600061080282611b51565b50600090815260cd602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b6000610836826115ee565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108de5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82161480610907575061090781336105e6565b6109795760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016108d5565b6109838383611bc2565b505050565b60006109948133611c62565b6109d3576040517f75000dc0000000000000000000000000000000000000000000000000000000008152600481018290523360248201526044016108d5565b610a1373ffffffffffffffffffffffffffffffffffffffff83167f7965db0b00000000000000000000000000000000000000000000000000000000611d2a565b610a79576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c00000000000000000000000000000000000060448201526064016108d5565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a2505050565b610b123382611d4d565b610b845760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016108d5565b610983838383611e0c565b6000807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f600088815260018201602052604080822054600384015491517f2fb49eff00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201819052602482018c9052949550911690632fb49eff90604401602060405180830381865afa158015610c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c65919061389f565b604080518082018252338152815180830190925260008083526020838101829052820192909252919250610c9f9088908a9085858b6120b9565b9a9950505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610d585760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c000000000000000000000000000000000000000060648201526084016108d5565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16610dcd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614610e565760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f7879000000000000000000000000000000000000000060648201526084016108d5565b610e5f81612296565b60408051600080825260208201909252610e7b91839190612300565b50565b60008881527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e9060205260408082205490517f99258ca10000000000000000000000000000000000000000000000000000000081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f9173ffffffffffffffffffffffffffffffffffffffff169081906399258ca190610f2f908d908d908d908d908d908d908d903390600401613901565b6020604051808303816000875af1158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f72919061389f565b925060006040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160405180604001604052808573ffffffffffffffffffffffffffffffffffffffff16815260200187815250815250905061100d7f781e9f3557d8768f1963c458e1b351a046f8e4f612fd4a2c28f5132ee0c0c87b600180600085604051806020016040528060008152506120b9565b5061104e7facb6e50a24ac7b7075ce347695829fdb25124f5d7485df10299c2e269a3a9f8360006001600085604051806020016040528060008152506120b9565b5050505098975050505050505050565b600080807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f905060008160000160008154611098906139a6565b91829055509050600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166386df779e836110e88980613a05565b6110f560208c018c613a05565b61110260408e018e613a05565b6040518863ffffffff1660e01b81526004016111249796959493929190613a6a565b6020604051808303816000875af1158015611143573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111679190613aac565b6000838152600185016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590506111c46060870187613a05565b60008481526002860160205260409020916111e0919083613b17565b506111eb33836124cb565b7f071096a6b974f736d72b386641c048d2b40ab6731f5e6da2a22c95b530e81bd73383836112198a80613a05565b61122660208d018d613a05565b61123360608f018f613a05565b60405161124899989796959493929190613c32565b60405180910390a1909590945092505050565b6109838383836040518060200160405280600081525061172e565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001630036113215760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c000000000000000000000000000000000000000060648201526084016108d5565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166113967f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161461141f5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f7879000000000000000000000000000000000000000060648201526084016108d5565b61142882612296565b61143482826001612300565b5050565b60008173ffffffffffffffffffffffffffffffffffffffff1663b9d0787c6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156114bf575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526114bc9181019061389f565b60015b6114cb57506000919050565b60009081527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e90602052604090205473ffffffffffffffffffffffffffffffffffffffff908116921691909114919050565b60003073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146115c95760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c000000000000000060648201526084016108d5565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b600081815260cb602052604081205473ffffffffffffffffffffffffffffffffffffffff168061075f5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016108d5565b600073ffffffffffffffffffffffffffffffffffffffff82166116eb5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e6572000000000000000000000000000000000000000000000060648201526084016108d5565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260cc602052604090205490565b606060ca80546107749061384c565b6114343383836124e5565b6117383383611d4d565b6117aa5760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206f7220617070726f7665640000000000000000000000000000000000000060648201526084016108d5565b6117b6848484846125f8565b50505050565b600054610100900460ff16158080156117dc5750600054600160ff909116105b806117f65750303b1580156117f6575060005460ff166001145b6118685760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108d5565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156118c657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6118ce612681565b6118d782612700565b61194b6040518060400160405280600e81526020017f53746f72792050726f746f636f6c0000000000000000000000000000000000008152506040518060400160405280600281526020017f535000000000000000000000000000000000000000000000000000000000000081525061299a565b801561143457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b60606119bd82611b51565b60008281527f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e916020526040902080547f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f9190611a189061384c565b80601f0160208091040260200160405190810160405280929190818152602001828054611a449061384c565b8015611a915780601f10611a6657610100808354040283529160200191611a91565b820191906000526020600020905b815481529060010190602001808311611a7457829003601f168201915b5050505050915050919050565b73ffffffffffffffffffffffffffffffffffffffff8116611aeb576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e9280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff16610e7b5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e204944000000000000000060448201526064016108d5565b600081815260cd6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091558190611c1c826115ee565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000807f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680546040517f91d148540000000000000000000000000000000000000000000000000000000081526004810187905273ffffffffffffffffffffffffffffffffffffffff86811660248301529293509116906391d1485490604401602060405180830381865afa158015611cfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d229190613caa565b949350505050565b6000611d3583612a21565b8015611d465750611d468383612a85565b9392505050565b600080611d59836115ee565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480611dc7575073ffffffffffffffffffffffffffffffffffffffff808216600090815260ce602090815260408083209388168352929052205460ff165b80611d2257508373ffffffffffffffffffffffffffffffffffffffff16611ded846107f7565b73ffffffffffffffffffffffffffffffffffffffff1614949350505050565b8273ffffffffffffffffffffffffffffffffffffffff16611e2c826115ee565b73ffffffffffffffffffffffffffffffffffffffff1614611eb55760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016108d5565b73ffffffffffffffffffffffffffffffffffffffff8216611f3d5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016108d5565b8273ffffffffffffffffffffffffffffffffffffffff16611f5d826115ee565b73ffffffffffffffffffffffffffffffffffffffff1614611fe65760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e657200000000000000000000000000000000000000000000000000000060648201526084016108d5565b600081815260cd6020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811690915573ffffffffffffffffffffffffffffffffffffffff87811680865260cc855283862080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559087168086528386208054600101905586865260cb90945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6000807f5648324915b730d22cca7279385130ad43fd4829d795fb20e9ab398bfe537e8f90508060030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f782f67533878b60405180606001604052806000151581526020018c151581526020018d1515815250896040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001604051806020016040528060008152508152506040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001604051806020016040528060008152508152506040518060a0016040528060006dffffffffffffffffffffffffffff16815260200160006dffffffffffffffffffffffffffff168152602001600015158152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152508c60006040518b63ffffffff1660e01b81526004016122479a99989796959493929190613cfc565b6020604051808303816000875af1158015612266573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061228a919061389f565b98975050505050505050565b7f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e36122c18133611c62565b611434576040517f75000dc0000000000000000000000000000000000000000000000000000000008152600481018290523360248201526044016108d5565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff16156123335761098383612b54565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156123b8575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526123b59181019061389f565b60015b61242a5760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f74205555505300000000000000000000000000000000000060648201526084016108d5565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81146124bf5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c6555554944000000000000000000000000000000000000000000000060648201526084016108d5565b50610983838383612c44565b611434828260405180602001604052806000815250612c69565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036125605760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016108d5565b73ffffffffffffffffffffffffffffffffffffffff838116600081815260ce602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b612603848484611e0c565b61260f84848484612cf2565b6117b65760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b600054610100900460ff166126fe5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b565b600054610100900460ff16158080156127205750600054600160ff909116105b8061273a5750303b15801561273a575060005460ff166001145b6127ac5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016108d5565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561280a57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b61284a73ffffffffffffffffffffffffffffffffffffffff83167f7965db0b00000000000000000000000000000000000000000000000000000000611d2a565b6128b0576040517f1a1fdbb000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f49416363657373436f6e74726f6c00000000000000000000000000000000000060448201526064016108d5565b7f06c308ca3b780cede1217f5877d0c7fbf50796d93f836cb3b60e6457b0cf03b680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811782556040517fc8ebe3bc6cc2f03e640cefc0f0c872637b7d9492bd5e6318eaba1ab468da970990600090a250801561143457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020016119a6565b600054610100900460ff16612a175760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b6114348282612ecb565b6000612a4d827f01ffc9a700000000000000000000000000000000000000000000000000000000612a85565b801561075f5750612a7e827fffffffff00000000000000000000000000000000000000000000000000000000612a85565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015612b3d575060208210155b8015612b495750600081115b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81163b612bde5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016108d5565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612c4d83612f61565b600082511180612c5a5750805b15610983576117b68383612fae565b612c7383836130d3565b612c806000848484612cf2565b6109835760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b600073ffffffffffffffffffffffffffffffffffffffff84163b15612ec0576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612d69903390899088908890600401613e39565b6020604051808303816000875af1925050508015612dc2575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612dbf91810190613e82565b60015b612e75573d808015612df0576040519150601f19603f3d011682016040523d82523d6000602084013e612df5565b606091505b508051600003612e6d5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e746572000000000000000000000000000060648201526084016108d5565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a0200000000000000000000000000000000000000000000000000000000149050611d22565b506001949350505050565b600054610100900460ff16612f485760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016108d5565b60c9612f548382613e9f565b5060ca6109838282613e9f565b612f6a81612b54565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b606073ffffffffffffffffffffffffffffffffffffffff83163b61303a5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016108d5565b6000808473ffffffffffffffffffffffffffffffffffffffff16846040516130629190613fb9565b600060405180830381855af49150503d806000811461309d576040519150601f19603f3d011682016040523d82523d6000602084013e6130a2565b606091505b50915091506130ca8282604051806060016040528060278152602001613fd6602791396132aa565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff82166131365760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016108d5565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff16156131a85760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016108d5565b600081815260cb602052604090205473ffffffffffffffffffffffffffffffffffffffff161561321a5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016108d5565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260cc602090815260408083208054600101905584835260cb90915280822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b606083156132b9575081611d46565b611d4683838151156132ce5781518083602001fd5b8060405162461bcd60e51b81526004016108d591906133a1565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114610e7b57600080fd5b60006020828403121561332857600080fd5b8135611d46816132e8565b60005b8381101561334e578181015183820152602001613336565b50506000910152565b6000815180845261336f816020860160208601613333565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611d466020830184613357565b6000602082840312156133c657600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610e7b57600080fd5b6000806040838503121561340257600080fd5b823561340d816133cd565b946020939093013593505050565b60006020828403121561342d57600080fd5b8135611d46816133cd565b60008060006060848603121561344d57600080fd5b8335613458816133cd565b92506020840135613468816133cd565b929592945050506040919091013590565b8015158114610e7b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff808411156134d1576134d1613487565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561351757613517613487565b8160405280935085815286868601111561353057600080fd5b858560208301376000602087830101525050509392505050565b600080600080600060a0868803121561356257600080fd5b8535945060208601359350604086013561357b81613479565b925060608601359150608086013567ffffffffffffffff81111561359e57600080fd5b8601601f810188136135af57600080fd5b6135be888235602084016134b6565b9150509295509295909350565b60008083601f8401126135dd57600080fd5b50813567ffffffffffffffff8111156135f557600080fd5b60208301915083602082850101111561360d57600080fd5b9250929050565b60008060008060008060008060a0898b03121561363057600080fd5b8835975060208901356007811061364657600080fd5b9650604089013567ffffffffffffffff8082111561366357600080fd5b61366f8c838d016135cb565b909850965060608b013591508082111561368857600080fd5b6136948c838d016135cb565b909650945060808b01359150808211156136ad57600080fd5b506136ba8b828c016135cb565b999c989b5096995094979396929594505050565b6000602082840312156136e057600080fd5b813567ffffffffffffffff8111156136f757600080fd5b820160808185031215611d4657600080fd5b600082601f83011261371a57600080fd5b611d46838335602085016134b6565b6000806040838503121561373c57600080fd5b8235613747816133cd565b9150602083013567ffffffffffffffff81111561376357600080fd5b61376f85828601613709565b9150509250929050565b6000806040838503121561378c57600080fd5b8235613797816133cd565b915060208301356137a781613479565b809150509250929050565b600080600080608085870312156137c857600080fd5b84356137d3816133cd565b935060208501356137e3816133cd565b925060408501359150606085013567ffffffffffffffff81111561380657600080fd5b61381287828801613709565b91505092959194509250565b6000806040838503121561383157600080fd5b823561383c816133cd565b915060208301356137a7816133cd565b600181811c9082168061386057607f821691505b602082108103613899577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156138b157600080fd5b5051919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600060078a1061393a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b89825260a0602083015261395260a08301898b6138b8565b828103604084015261396581888a6138b8565b9050828103606084015261397a8186886138b8565b91505073ffffffffffffffffffffffffffffffffffffffff831660808301529998505050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036139fe577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613a3a57600080fd5b83018035915067ffffffffffffffff821115613a5557600080fd5b60200191503681900382131561360d57600080fd5b878152608060208201526000613a8460808301888a6138b8565b8281036040840152613a978187896138b8565b90508281036060840152610c9f8185876138b8565b600060208284031215613abe57600080fd5b8151611d46816133cd565b601f82111561098357600081815260208120601f850160051c81016020861015613af05750805b601f850160051c820191505b81811015613b0f57828155600101613afc565b505050505050565b67ffffffffffffffff831115613b2f57613b2f613487565b613b4383613b3d835461384c565b83613ac9565b6000601f841160018114613b955760008515613b5f5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355613c2b565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015613be45786850135825560209485019460019092019101613bc4565b5086821015613c1f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff808c1683528a6020840152808a1660408401525060c06060830152613c7260c08301888a6138b8565b8281036080840152613c858187896138b8565b905082810360a0840152613c9a8185876138b8565b9c9b505050505050505050505050565b600060208284031215613cbc57600080fd5b8151611d4681613479565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152611d226040850182613357565b73ffffffffffffffffffffffffffffffffffffffff8b8116825260208083018c905260408084018c90528a51151560608501528a820151151560808501528a0151151560a08401528851821660c084015288810151805190921660e08401520151610100820152600061024080610120840152613d7b81840189613cc7565b9050828103610140840152613d908188613cc7565b86516dffffffffffffffffffffffffffff908116610160860152602088015116610180850152604087015115156101a0850152606087015173ffffffffffffffffffffffffffffffffffffffff166101c085015260808701516101e08501529050828103610200840152613e048186613357565b915050613e2a61022083018473ffffffffffffffffffffffffffffffffffffffff169052565b9b9a5050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152613e786080830184613357565b9695505050505050565b600060208284031215613e9457600080fd5b8151611d46816132e8565b815167ffffffffffffffff811115613eb957613eb9613487565b613ecd81613ec7845461384c565b84613ac9565b602080601f831160018114613f205760008415613eea5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613b0f565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613f6d57888601518255948401946001909101908401613f4e565b5085821015613fa957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008251613fcb818460208701613333565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122069df0d4308aa6890cb1a63ed5fde72e5003558e570cb3043f0e387b70837636e64736f6c63430008130033000000000000000000000000461df243955ad5493cfa33434c2b7cff2c583dc7", + "nonce": "0x45", "accessList": [] }, "additionalContracts": [], "isFixedGasLimit": false }, { - "hash": "0x661d50a3a273c474006d397f6db676d7089f1ba6d8f4a200f82afa232b3dfbf0", + "hash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", "transactionType": "CALL", "contractName": "ERC1967Proxy", "contractAddress": "0xf157916d184598105FE4681eC93Be0f799599003", @@ -34,10 +34,31 @@ "type": "0x02", "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", "to": "0xf157916d184598105fe4681ec93be0f799599003", - "gas": "0x10ab4", + "gas": "0x10ad1", "value": "0x0", - "data": "0x3659cfe600000000000000000000000008173a3d97aeae39078a5714279c2233b270592e", - "nonce": "0x42", + "data": "0x3659cfe600000000000000000000000088f3a4cc4eaa2433c44a7c502c7abbd2c76b03e2", + "nonce": "0x46", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x66ecb91002da900a2889cda1ec2af547945c55f6e2f175366566dc483fbb9d0f", + "transactionType": "CALL", + "contractName": "ERC1967Proxy", + "contractAddress": "0xf157916d184598105FE4681eC93Be0f799599003", + "function": null, + "arguments": null, + "rpc": "https://eth-goerli.g.alchemy.com/v2/7WyRjkZW0XJmt7tb4OqO9YTp5z_iVl-o", + "transaction": { + "type": "0x02", + "from": "0xb6288e57bf7406b35ab4f70fd1135e907107e386", + "to": "0xf157916d184598105fe4681ec93be0f799599003", + "gas": "0x10757", + "value": "0x0", + "data": "0xcc5b6084000000000000000000000000fbfa729428af206a084e18328ec923587193525d", + "nonce": "0x47", "accessList": [] }, "additionalContracts": [], @@ -46,73 +67,89 @@ ], "receipts": [ { - "transactionHash": "0xb3cf4327d9f0809ec44fe7f41a762d0931cb452d27f082cdd01ab2c4f93b7746", + "transactionHash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", "transactionIndex": "0x1", - "blockHash": "0x7e9c593f36f60e2d7433dedc2c02e06ec391d84c48ff9761288cb5cc5fdd08d7", - "blockNumber": "0x90e96c", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", "to": null, - "cumulativeGasUsed": "0x2f95c9", - "gasUsed": "0x2f0bec", - "contractAddress": "0x08173A3D97aeAE39078a5714279c2233B270592e", + "cumulativeGasUsed": "0x37ace1", + "gasUsed": "0x372304", + "contractAddress": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", "logs": [ { - "address": "0x08173A3D97aeAE39078a5714279c2233B270592e", + "address": "0x88f3a4cc4Eaa2433c44A7c502C7AbBd2C76b03e2", "topics": [ "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" ], "data": "0x00000000000000000000000000000000000000000000000000000000000000ff", - "blockHash": "0x7e9c593f36f60e2d7433dedc2c02e06ec391d84c48ff9761288cb5cc5fdd08d7", - "blockNumber": "0x90e96c", - "transactionHash": "0xb3cf4327d9f0809ec44fe7f41a762d0931cb452d27f082cdd01ab2c4f93b7746", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "transactionHash": "0xbd67ab955f3c18b346f0972167637cf56a32bf420622707a6ee62ffe8d4f3a7a", "transactionIndex": "0x1", "logIndex": "0x1", "removed": false } ], "status": "0x1", - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000001000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000800000000000000000000000000000000000000000000000040000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "type": "0x2", - "effectiveGasPrice": "0xb2d05e09" + "effectiveGasPrice": "0xb2d05e0a" }, { - "transactionHash": "0x661d50a3a273c474006d397f6db676d7089f1ba6d8f4a200f82afa232b3dfbf0", + "transactionHash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", "transactionIndex": "0x2", - "blockHash": "0x7e9c593f36f60e2d7433dedc2c02e06ec391d84c48ff9761288cb5cc5fdd08d7", - "blockNumber": "0x90e96c", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", "to": "0xf157916d184598105FE4681eC93Be0f799599003", - "cumulativeGasUsed": "0x3056e0", - "gasUsed": "0xc117", + "cumulativeGasUsed": "0x386e0e", + "gasUsed": "0xc12d", "contractAddress": null, "logs": [ { "address": "0xf157916d184598105FE4681eC93Be0f799599003", "topics": [ "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", - "0x00000000000000000000000008173a3d97aeae39078a5714279c2233b270592e" + "0x00000000000000000000000088f3a4cc4eaa2433c44a7c502c7abbd2c76b03e2" ], "data": "0x", - "blockHash": "0x7e9c593f36f60e2d7433dedc2c02e06ec391d84c48ff9761288cb5cc5fdd08d7", - "blockNumber": "0x90e96c", - "transactionHash": "0x661d50a3a273c474006d397f6db676d7089f1ba6d8f4a200f82afa232b3dfbf0", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "transactionHash": "0x7bdeee174e4ec6fc6a65a26d422775df699cb5cab402bbdd9b8fa97d3f2d90a5", "transactionIndex": "0x2", "logIndex": "0x2", "removed": false } ], "status": "0x1", - "logsBloom": "0x00000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000080000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000002000000000000000000000000000400000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e0a" + }, + { + "transactionHash": "0x66ecb91002da900a2889cda1ec2af547945c55f6e2f175366566dc483fbb9d0f", + "transactionIndex": "0x3", + "blockHash": "0x38e37e9635db0cb7cce04196ad28258e85f6dcd9f2292a7ee26c10066e3bfc0a", + "blockNumber": "0x9110d2", + "from": "0xB6288e57bf7406B35ab4F70Fd1135E907107e386", + "to": "0xf157916d184598105FE4681eC93Be0f799599003", + "cumulativeGasUsed": "0x392cb6", + "gasUsed": "0xbea8", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "type": "0x2", - "effectiveGasPrice": "0xb2d05e09" + "effectiveGasPrice": "0xb2d05e0a" } ], "libraries": [], "pending": [], "path": "/Users/drmanhattan/Workspace/StoryProtocol/protocol-contracts/broadcast/DevUpgrades.s.sol/5/run-latest.json", "returns": {}, - "timestamp": 1691713712, + "timestamp": 1691870567, "chain": 5, "multi": false, - "commit": "6aaf21d" + "commit": "53ab00d" } \ No newline at end of file diff --git a/contracts/FranchiseRegistry.sol b/contracts/FranchiseRegistry.sol index fdccbbbb..a4941e76 100644 --- a/contracts/FranchiseRegistry.sol +++ b/contracts/FranchiseRegistry.sol @@ -67,7 +67,7 @@ contract FranchiseRegistry is } function setLicensingModule(LicensingModule module) external{ - // TODO: set protocol role for this and check sender + // TODO: set protocol role for this and check sender. if (address(module) == address(0)) revert ZeroAddress(); _getFranchiseStorage().licensingModule = module; } diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index a1fe1f45..3eb36b46 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -93,7 +93,7 @@ contract LicensingModule is ERC721Upgradeable { FRANCHISE_REGISTRY = franchiseRegistry; } - function initialize(string memory _nonCommercialLicenseURI) public initializer { + function initialize(string calldata _nonCommercialLicenseURI) public initializer { __ERC721_init("Story Protocol License", "SPL"); _getLicenseModuleStorage().nonCommercialLicenseURI = _nonCommercialLicenseURI; } diff --git a/deployment-5.json b/deployment-5.json index 5b651ce7..c2fee3f0 100644 --- a/deployment-5.json +++ b/deployment-5.json @@ -3,12 +3,14 @@ "AccessControlSingleton-Impl": "0xD933e6768a0C86ca11DDf802932FeBD50509Ba4D", "AccessControlSingleton-Proxy": "0x58eda9b5b0983f75fa887B598d7164E82791Ba21", "CommonIPAssetEventEmitter": "0xe2a7213762caDDb7438f21f82cEfbB4931167463", - "FranchiseRegistry-Impl": "0x08173A3D97aeAE39078a5714279c2233B270592e", + "FranchiseRegistry-Impl": "0x88f3a4cc4eaa2433c44a7c502c7abbd2c76b03e2", "FranchiseRegistry-Proxy": "0xf157916d184598105FE4681eC93Be0f799599003", "IPAssetRegistry-Template": "0xC60382Ea8cBea33ad9052E2FBC12d2F019C8947D", "IPAssetRegistryFactory": "0x461DF243955aD5493cFa33434c2B7CfF2C583dc7", "ProtocolRelationshipModule-Impl": "0x0c34223fc772aeC0514B5fbf5614b9ab85EdaAe1", - "ProtocolRelationshipModule-Proxy": "0xb146cB898E95953f353bf5A50Fb8f129eC0D5AAb" + "ProtocolRelationshipModule-Proxy": "0xb146cB898E95953f353bf5A50Fb8f129eC0D5AAb", + "LicenseModule-Impl": "0x8602E35ee65074eF0D2701927289705f2F61773D", + "LicenseModule-Proxy": "0xFbfa729428af206a084E18328Ec923587193525d" }, "relationship-processors": { "DstOwnerRelationshipProcessor": "0x2890a4263BA12c6d6241CfC5c8D762B828041b86", diff --git a/script/foundry/upgrades/DevDeployments.s.sol b/script/foundry/upgrades/DevDeployments.s.sol new file mode 100644 index 00000000..4b90c844 --- /dev/null +++ b/script/foundry/upgrades/DevDeployments.s.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +import "forge-std/Script.sol"; +import "script/foundry/utils/StringUtil.sol"; +import "script/foundry/utils/JsonDeploymentHandler.s.sol"; +import "script/foundry/utils/BroadcastManager.s.sol"; +import "test/foundry/utils/ProxyHelper.sol"; +import "contracts/modules/relationships/ProtocolRelationshipModule.sol"; +import "contracts/modules/relationships/RelationshipModuleBase.sol"; +import "contracts/FranchiseRegistry.sol"; +import "contracts/modules/licensing/LicensingModule.sol"; + +/** + * Use to upgrade contracts during development, in testnets. Expect things to break. + */ +contract DeployLicenseModule is Script, BroadcastManager, JsonDeploymentHandler, ProxyHelper { + + using StringUtil for uint256; + using stdJson for string; + + constructor() JsonDeploymentHandler("main") {} + + function run() public { + _readDeployment(); + _beginBroadcast(); + + address franchiseRegistryProxy = _readAddress(".main.FranchiseRegistry-Proxy"); + + + /// ACCESS CONTROL SINGLETON + string memory contractKey = "LicenseModule-Impl"; + + console.log(string.concat("Deploying ", contractKey, "...")); + address licenseModuleImpl = address(new LicensingModule(franchiseRegistryProxy)); + console.log(string.concat(contractKey, " deployed to:"), licenseModuleImpl); + + + contractKey = "LicenseModule-Proxy"; + string memory noncommercialLicenseURL = "https://arweave.net/yHIbKlFBg3xuKSzlM_dREG8Y08uod-gWKsWi9OaPFsM"; + + console.log(string.concat("Deploying ", contractKey, "...")); + address proxy = _deployUUPSProxy( + licenseModuleImpl, + abi.encodeWithSelector( + bytes4(keccak256(bytes("initialize(string)"))), noncommercialLicenseURL + ) + ); + + console.log(string.concat(contractKey, " deployed to:"), proxy); + + } + +} diff --git a/script/foundry/upgrades/DevUpgrades.s.sol b/script/foundry/upgrades/DevUpgrades.s.sol index fc81dac4..33e5de7a 100644 --- a/script/foundry/upgrades/DevUpgrades.s.sol +++ b/script/foundry/upgrades/DevUpgrades.s.sol @@ -7,6 +7,7 @@ import "script/foundry/utils/JsonDeploymentHandler.s.sol"; import "script/foundry/utils/BroadcastManager.s.sol"; import "contracts/modules/relationships/ProtocolRelationshipModule.sol"; import "contracts/modules/relationships/RelationshipModuleBase.sol"; +import "contracts/modules/licensing/LicensingModule.sol"; import "contracts/FranchiseRegistry.sol"; import "contracts/access-control/AccessControlSingleton.sol"; import "contracts/access-control/ProtocolRoles.sol"; @@ -64,6 +65,12 @@ contract UpgradeFranchiseRegistry is Script, BroadcastManager, JsonDeploymentHan address newFranchiseRegistry = address(new FranchiseRegistry(ipAssetRegistryFactory)); console.log("Upgrading FranchiseRegistry to ", newFranchiseRegistry); franchiseRegistry.upgradeTo(newFranchiseRegistry); + + console.log("Upgrading IPAssetRegistryFactory to ", newFranchiseRegistry); + + LicensingModule licenseModule = LicensingModule(_readAddress(".main.LicenseModule-Proxy")); + console.log("Setting license module"); + franchiseRegistry.setLicensingModule(licenseModule); } From 04c7568708f195030daf4f42cebd9f3d5d83d550 Mon Sep 17 00:00:00 2001 From: Raul Date: Sat, 12 Aug 2023 13:29:44 -0700 Subject: [PATCH 07/42] fix --- contracts/FranchiseRegistry.sol | 6 ++- .../modules/licensing/LicensingModule.sol | 12 ++++-- test/foundry/LicenseModule.t.sol | 43 +++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 test/foundry/LicenseModule.t.sol diff --git a/contracts/FranchiseRegistry.sol b/contracts/FranchiseRegistry.sol index a4941e76..58c85436 100644 --- a/contracts/FranchiseRegistry.sol +++ b/contracts/FranchiseRegistry.sol @@ -72,6 +72,10 @@ contract FranchiseRegistry is _getFranchiseStorage().licensingModule = module; } + function getLicenseingModule() external view returns (LicensingModule) { + return _getFranchiseStorage().licensingModule; + } + function _getFranchiseStorage() private pure returns (FranchiseStorage storage $) { assembly { $.slot := _STORAGE_LOCATION @@ -155,7 +159,7 @@ contract FranchiseRegistry is ) external returns (uint256) { FranchiseStorage storage $ = _getFranchiseStorage(); address ipAssetRegistry = $.ipAssetRegistries[franchiseId]; - uint256 parentLicenseId = $.licensingModule.licenseIdForToken(ipAssetRegistry, ipAssetId); + uint256 parentLicenseId = $.licensingModule.licenseIdForToken(ipAssetRegistry, ipAssetId, commercial); LicensingModule.OwnershipParams memory ownershipParams = LicensingModule.OwnershipParams({ holder: msg.sender, token: LicensingModule.Token({ diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index 3eb36b46..c3c785e7 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -73,7 +73,7 @@ contract LicensingModule is ERC721Upgradeable { // the franchise owner and external PFP owners, etc. mapping(uint256 => bytes) franchiseRestrictions; // TODO: Remove this - mapping(address => mapping(uint256 => uint256)) demoTokenToLicense; + mapping(address => mapping(uint256 => mapping(bool => uint256))) demoTokenToLicense; string nonCommercialLicenseURI; } @@ -104,6 +104,10 @@ contract LicensingModule is ERC721Upgradeable { } } + function getNonCommercialLicenseURI() public view returns (string memory) { + return _getLicenseModuleStorage().nonCommercialLicenseURI; + } + function isLicenseActive(uint256 licenseId) public view virtual returns (bool) { // TODO: limit to the tree depth @@ -155,7 +159,7 @@ contract LicensingModule is ERC721Upgradeable { ); // TODO: remove this, only for demo - $.demoTokenToLicense[address(ownershipParams.token.collection)][ownershipParams.token.tokenId] = licenseId; + $.demoTokenToLicense[address(ownershipParams.token.collection)][ownershipParams.token.tokenId][generalTerms.commercial] = licenseId; return licenseId; } @@ -251,8 +255,8 @@ contract LicensingModule is ERC721Upgradeable { return currentCounter; } - function licenseIdForToken(address collection, uint256 tokenId) public view returns (uint256) { - return _getLicenseModuleStorage().demoTokenToLicense[collection][tokenId]; + function licenseIdForToken(address collection, uint256 tokenId, bool commercial) public view returns (uint256) { + return _getLicenseModuleStorage().demoTokenToLicense[collection][tokenId][commercial]; } function getLicense(uint256 licenseId) public view returns (License memory, address holder) { diff --git a/test/foundry/LicenseModule.t.sol b/test/foundry/LicenseModule.t.sol new file mode 100644 index 00000000..1314e5f1 --- /dev/null +++ b/test/foundry/LicenseModule.t.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BUSDL-1.1 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import './utils/BaseTest.sol'; +import { LicensingModule } from "contracts/modules/licensing/LicensingModule.sol"; + +contract LicenseModuleTest is BaseTest { + + LicensingModule licensingModule; + string noncommercialLicenseURL; + address ipAssetHolder = address(0x23232); + + function setUp() virtual override public { + deployProcessors = false; + super.setUp(); + address licenseModuleImpl = address(new LicensingModule(address(franchiseRegistry))); + + noncommercialLicenseURL = "https://arweave.net/yHIbKlFBg3xuKSzlM_dREG8Y08uod-gWKsWi9OaPFsM"; + + address proxy = _deployUUPSProxy( + licenseModuleImpl, + abi.encodeWithSelector( + bytes4(keccak256(bytes("initialize(string)"))), noncommercialLicenseURL + ) + ); + licensingModule = LicensingModule(proxy); + franchiseRegistry.setLicensingModule(licensingModule); + } + + function test_setUp() public { + assertEq(address(franchiseRegistry.getLicenseingModule()), address(licensingModule)); + assertEq(licensingModule.getNonCommercialLicenseURI(), noncommercialLicenseURL); + } + + function test_happyPath() public { + vm.startPrank(ipAssetHolder); + uint256 ipAssetId = franchiseRegistry.createIPAsset(1, IPAsset.STORY, "name", "description", "tokenURI"); + franchiseRegistry.createLicense(1, ipAssetId, true, keccak256("MOVIE_ADAPTATION"), "https://cool-license-bro.pdf"); + vm.stopPrank(); + } + +} From 753d8515864291f4b60570446dc1ee2e8f683377 Mon Sep 17 00:00:00 2001 From: Raul Date: Sat, 12 Aug 2023 14:28:03 -0700 Subject: [PATCH 08/42] WIP --- .../modules/licensing/LicensingModule.sol | 18 ++++++++---- script/foundry/upgrades/DevDeployments.s.sol | 4 +-- script/foundry/upgrades/DevUpgrades.s.sol | 28 +++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index c3c785e7..566ffd20 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -3,14 +3,17 @@ pragma solidity ^0.8.13; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import { LibTimeConditional } from "../timing/LibTimeConditional.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import { AccessControlledUpgradeable } from "contracts/access-control/AccessControlledUpgradeable.sol"; +import { UPGRADER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; -contract LicensingModule is ERC721Upgradeable { +contract LicensingModule is UUPSUpgradeable, ERC721Upgradeable, AccessControlledUpgradeable { event LicenseGranted ( uint256 indexed licenseId, address holder, - address token, - uint256 tokenId, + address ipAssetRegistry, + uint256 ipAssetId, uint256 parentLicenseId ); @@ -93,7 +96,9 @@ contract LicensingModule is ERC721Upgradeable { FRANCHISE_REGISTRY = franchiseRegistry; } - function initialize(string calldata _nonCommercialLicenseURI) public initializer { + function initialize(string calldata _nonCommercialLicenseURI, address accessControl) public initializer { + __UUPSUpgradeable_init(); + __AccessControlledUpgradeable_init(accessControl); __ERC721_init("Story Protocol License", "SPL"); _getLicenseModuleStorage().nonCommercialLicenseURI = _nonCommercialLicenseURI; } @@ -108,7 +113,6 @@ contract LicensingModule is ERC721Upgradeable { return _getLicenseModuleStorage().nonCommercialLicenseURI; } - function isLicenseActive(uint256 licenseId) public view virtual returns (bool) { // TODO: limit to the tree depth // TODO: check time limits @@ -272,4 +276,8 @@ contract LicensingModule is ERC721Upgradeable { // TODO: check granting terms, banned marketplaces, etc. super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } + + function _authorizeUpgrade( + address newImplementation + ) internal virtual override onlyRole(UPGRADER_ROLE) {} } \ No newline at end of file diff --git a/script/foundry/upgrades/DevDeployments.s.sol b/script/foundry/upgrades/DevDeployments.s.sol index 4b90c844..14090e96 100644 --- a/script/foundry/upgrades/DevDeployments.s.sol +++ b/script/foundry/upgrades/DevDeployments.s.sol @@ -38,12 +38,12 @@ contract DeployLicenseModule is Script, BroadcastManager, JsonDeploymentHandler, contractKey = "LicenseModule-Proxy"; string memory noncommercialLicenseURL = "https://arweave.net/yHIbKlFBg3xuKSzlM_dREG8Y08uod-gWKsWi9OaPFsM"; - + address accessControl = _readAddress(".main.AccessControlSingleton-Proxy"); console.log(string.concat("Deploying ", contractKey, "...")); address proxy = _deployUUPSProxy( licenseModuleImpl, abi.encodeWithSelector( - bytes4(keccak256(bytes("initialize(string)"))), noncommercialLicenseURL + bytes4(keccak256(bytes("initialize(string,address)"))), noncommercialLicenseURL, accessControl ) ); diff --git a/script/foundry/upgrades/DevUpgrades.s.sol b/script/foundry/upgrades/DevUpgrades.s.sol index 33e5de7a..4a4807f3 100644 --- a/script/foundry/upgrades/DevUpgrades.s.sol +++ b/script/foundry/upgrades/DevUpgrades.s.sol @@ -13,6 +13,8 @@ import "contracts/access-control/AccessControlSingleton.sol"; import "contracts/access-control/ProtocolRoles.sol"; import "contracts/ip-assets/events/CommonIPAssetEventEmitter.sol"; import "contracts/ip-assets/IPAssetRegistry.sol"; +import "contracts/modules/licensing/LicensingModule.sol"; + /** * Use to upgrade contracts during development, in testnets. Expect things to break. @@ -111,3 +113,29 @@ contract UpgradeIPAssetRegistry is Script, BroadcastManager, JsonDeploymentHandl } } + +contract UpgradeLicenseModule is Script, BroadcastManager, JsonDeploymentHandler { + + using StringUtil for uint256; + using stdJson for string; + + constructor() JsonDeploymentHandler("main") {} + + function run() public { + _readDeployment(); + _beginBroadcast(); + + address franchiseRegistryProxy = _readAddress(".main.FranchiseRegistry-Proxy"); + + address licenseModuleImpl = address(new LicensingModule(franchiseRegistryProxy)); + + LicensingModule licenseModule = LicensingModule(_readAddress(".main.LicenseModule-Proxy")); + licenseModule.upgradeTo(licenseModuleImpl); + + console.log("Upgraded LicenseModule to ", licenseModuleImpl); + address accessControl = _readAddress(".main.AccessControlSingleton-Proxy"); + licenseModule.setAccessControl(accessControl); + + } + +} From 2caa838c93d653f10c84d066d5c68f1e648e04b3 Mon Sep 17 00:00:00 2001 From: Raul Date: Fri, 18 Aug 2023 01:04:06 +0200 Subject: [PATCH 09/42] IPAssetRegistry as RightsManager, and LicenseRegistry --- contracts/FranchiseRegistry.sol | 81 ----- contracts/ip-assets/IIPAssetRegistry.sol | 4 +- contracts/ip-assets/IPAssetRegistry.sol | 44 ++- contracts/modules/licensing/IERC5218.sol | 136 ++++++++ .../modules/licensing/LicenseRegistry.sol | 42 +++ .../modules/licensing/LicensingModule.sol | 283 ---------------- contracts/modules/licensing/RightsManager.sol | 304 ++++++++++++++++++ .../modules/licensing/licensingparams.txt | 0 script/foundry/upgrades/DevDeployments.s.sol | 54 ---- script/foundry/upgrades/DevUpgrades.s.sol | 33 -- test/foundry/LicenseModule.t.sol | 2 + 11 files changed, 519 insertions(+), 464 deletions(-) create mode 100644 contracts/modules/licensing/IERC5218.sol create mode 100644 contracts/modules/licensing/LicenseRegistry.sol delete mode 100644 contracts/modules/licensing/LicensingModule.sol create mode 100644 contracts/modules/licensing/RightsManager.sol create mode 100644 contracts/modules/licensing/licensingparams.txt delete mode 100644 script/foundry/upgrades/DevDeployments.s.sol diff --git a/contracts/FranchiseRegistry.sol b/contracts/FranchiseRegistry.sol index 58c85436..cf8ef3d7 100644 --- a/contracts/FranchiseRegistry.sol +++ b/contracts/FranchiseRegistry.sol @@ -12,7 +12,6 @@ import { LibIPAssetId } from "./ip-assets/LibIPAssetId.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import { LicensingModule } from "./modules/licensing/LicensingModule.sol"; import { LibTimeConditional } from "./modules/timing/LibTimeConditional.sol"; contract FranchiseRegistry is @@ -44,7 +43,6 @@ contract FranchiseRegistry is /// Franchise id => IPAssetRegistry address mapping(uint256 => address) ipAssetRegistries; mapping(uint256 => string) tokenURIs; - LicensingModule licensingModule; } IPAssetRegistryFactory public immutable FACTORY; @@ -66,16 +64,6 @@ contract FranchiseRegistry is __ERC721_init("Story Protocol", "SP"); } - function setLicensingModule(LicensingModule module) external{ - // TODO: set protocol role for this and check sender. - if (address(module) == address(0)) revert ZeroAddress(); - _getFranchiseStorage().licensingModule = module; - } - - function getLicenseingModule() external view returns (LicensingModule) { - return _getFranchiseStorage().licensingModule; - } - function _getFranchiseStorage() private pure returns (FranchiseStorage storage $) { assembly { $.slot := _STORAGE_LOCATION @@ -137,78 +125,9 @@ contract FranchiseRegistry is FranchiseStorage storage $ = _getFranchiseStorage(); address ipAssetRegistry = $.ipAssetRegistries[franchiseId]; ipAssetID = IIPAssetRegistry(ipAssetRegistry).createIPAsset(sb, name, _description, mediaUrl, msg.sender); - // For demo, every IPAsset has root comercial and non commercial licenses. Both can sublicense - LicensingModule.OwnershipParams memory ownershipParams = LicensingModule.OwnershipParams({ - holder: address(0), - token: LicensingModule.Token({ - collection: IERC721(ipAssetRegistry), - tokenId: ipAssetID - }) - }); - _demoGrantLicense(keccak256("FULL_COMMERCIAL_RIGHTS"), true, true, 0, ownershipParams, ""); - _demoGrantLicense(keccak256("NON_COMMERCIAL_PUBLIC_NFT_SHARE_ALIKE"), false, true, 0, ownershipParams, ""); return ipAssetID; } - function createLicense( - uint256 franchiseId, - uint256 ipAssetId, - bool commercial, - bytes32 mediaId, - string memory licenseURI - ) external returns (uint256) { - FranchiseStorage storage $ = _getFranchiseStorage(); - address ipAssetRegistry = $.ipAssetRegistries[franchiseId]; - uint256 parentLicenseId = $.licensingModule.licenseIdForToken(ipAssetRegistry, ipAssetId, commercial); - LicensingModule.OwnershipParams memory ownershipParams = LicensingModule.OwnershipParams({ - holder: msg.sender, - token: LicensingModule.Token({ - collection: IERC721(address(0)), - tokenId: 0 - }) - }); - return _demoGrantLicense(mediaId, commercial, false, parentLicenseId, ownershipParams, licenseURI); - } - - function _demoGrantLicense( - bytes32 mediaId, - bool commercial, - bool canSublicense, - uint256 parentLicenseId, - LicensingModule.OwnershipParams memory ownershipParamns, - string memory licenseUri - ) private returns (uint256) { - FranchiseStorage storage $ = _getFranchiseStorage(); - return $.licensingModule.grantLicense( - msg.sender, - parentLicenseId, - mediaId, - LicensingModule.GeneralTerms({ - exclusive: false, - canSublicense: canSublicense, - commercial: commercial - }), - ownershipParamns, - LicensingModule.PaymentTerms({ - interpreter: address(0), - data: "" - }), - LicensingModule.GrantingTerms({ - processor: address(0), - data: "" - }), - LibTimeConditional.TimeConfig({ - maxTTL: 0, - minTTL: 0, - renewable: false, - renewer: address(0), - endTime: 0 - }), - licenseUri, // License URI ignored for non commercial - address(0) // No revoker for demo - ); - } - function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { _requireMinted(tokenId); FranchiseStorage storage $ = _getFranchiseStorage(); diff --git a/contracts/ip-assets/IIPAssetRegistry.sol b/contracts/ip-assets/IIPAssetRegistry.sol index 85479e69..c2dc3839 100644 --- a/contracts/ip-assets/IIPAssetRegistry.sol +++ b/contracts/ip-assets/IIPAssetRegistry.sol @@ -5,12 +5,12 @@ import { IVersioned } from "../utils/IVersioned.sol"; import { IIPAssetDataManager } from './data-access-modules/storage/IIPAssetDataManager.sol'; import { IGroupDAM } from "./data-access-modules/group/IGroupDAM.sol"; import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; -import { IERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; +import { IERC5218 } from "../modules/licensing/IERC5218.sol"; interface IIPAssetRegistry is IVersioned, IERC165Upgradeable, - IERC721Upgradeable, + IERC5218, IIPAssetDataManager, IGroupDAM { diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index 425a2819..33929331 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -13,15 +13,15 @@ import { FranchiseRegistry } from "../FranchiseRegistry.sol"; import { ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import { RightsManager } from "../modules/licensing/RightsManager.sol"; contract IPAssetRegistry is IIPAssetRegistry, IPAssetDataManager, - ERC721Upgradeable, + RightsManager, MulticallUpgradeable, GroupDAM { - error IdOverBounds(); /// @custom:storage-location erc7201:story-protocol.ip-assets-registry.storage @@ -35,7 +35,8 @@ contract IPAssetRegistry is IIPAssetEventEmitter public immutable EVENT_EMITTER; FranchiseRegistry public immutable FRANCHISE_REGISTRY; // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.ip-assets-registry.storage")) - 1))) - bytes32 private constant _STORAGE_LOCATION = 0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378; + bytes32 private constant _STORAGE_LOCATION = + 0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378; string private constant _VERSION = "0.1.0"; constructor(address _eventEmitter, address _franchiseRegistry) { @@ -48,7 +49,8 @@ contract IPAssetRegistry is modifier onlyFranchiseRegistry() { // TODO: extract to FranchiseRegistryControlled.sol - if (msg.sender != address(FRANCHISE_REGISTRY)) revert("Sender is not the franchise registry"); + if (msg.sender != address(FRANCHISE_REGISTRY)) + revert("Sender is not the franchise registry"); _; } @@ -56,9 +58,10 @@ contract IPAssetRegistry is uint256 _franchiseId, string calldata _name, string calldata _symbol, - string calldata _description + string calldata _description, + string calldata _nonCommercialLicenseUri ) public initializer { - __ERC721_init(_name, _symbol); + __RightsManager_init(_nonCommercialLicenseUri, _name, _symbol); __Multicall_init(); if (_franchiseId == 0) revert ZeroAmount(); IPAssetRegistryStorage storage $ = _getIPAssetRegistryStorage(); @@ -86,14 +89,24 @@ contract IPAssetRegistry is string calldata _description, string calldata mediaUrl, address to - ) public virtual onlyFranchiseRegistry override(IIPAssetDataManager, IPAssetDataManager) returns (uint256) { + ) + public + virtual + override(IIPAssetDataManager, IPAssetDataManager) + onlyFranchiseRegistry + returns (uint256) + { uint256 sbId = _createIPAsset(sb, name, _description, mediaUrl, to); IPAssetRegistryStorage storage $ = _getIPAssetRegistryStorage(); EVENT_EMITTER.emitIPAssetCreation($.franchiseId, sbId); + // TODO: grant rights (root licenses) according to what the Franchise Owner sets in the LicensingModulegit return sbId; } - - function _mintBlock(address to, IPAsset sb) internal override returns (uint256) { + + function _mintBlock( + address to, + IPAsset sb + ) internal override returns (uint256) { uint256 nextId = currentIdFor(sb) + 1; if (nextId > LibIPAssetId._lastId(sb)) revert IdOverBounds(); IPAssetRegistryStorage storage $ = _getIPAssetRegistryStorage(); @@ -122,7 +135,9 @@ contract IPAssetRegistry is return $.franchiseId; } - function tokenURI(uint256 tokenId) public view override returns (string memory) { + function tokenURI( + uint256 tokenId + ) public view override returns (string memory) { return readIPAsset(tokenId).mediaUrl; } @@ -132,7 +147,7 @@ contract IPAssetRegistry is public view virtual - override(ERC721Upgradeable, IERC165Upgradeable) + override(RightsManager, IERC165Upgradeable) returns (bool) { return @@ -140,4 +155,11 @@ contract IPAssetRegistry is super.supportsInterface(interfaceId); } + function setNonCommercialLicenseURI(string calldata uri) + external + override + { + // TODO: add governance role to change this + } + } diff --git a/contracts/modules/licensing/IERC5218.sol b/contracts/modules/licensing/IERC5218.sol new file mode 100644 index 00000000..855416a8 --- /dev/null +++ b/contracts/modules/licensing/IERC5218.sol @@ -0,0 +1,136 @@ +pragma solidity ^0.8.0; + +import { IERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; + +/// @title ERC-5218: NFT Rights Management +interface IERC5218 is IERC721Upgradeable { + /// @dev This emits when a new license is created by any mechanism. + event CreateLicense( + uint256 _licenseId, + uint256 _tokenId, + uint256 _parentLicenseId, + address _licenseHolder, + string _uri, + address _revoker + ); + + /// @dev This emits when a license is revoked. Note that under some + /// license terms, the sublicenses may be `implicitly` revoked following the + /// revocation of some ancestral license. In that case, your smart contract + /// may only emit this event once for the ancestral license, and the revocation + /// of all its sublicenses can be implied without consuming additional gas. + event RevokeLicense(uint256 _licenseId); + + /// @dev This emits when the a license is transferred to a new holder. The + /// root license of an NFT should be transferred with the NFT in an ERC721 + /// `transfer` function call. + event TransferLicense(uint256 _licenseId, address _licenseHolder); + + /// @notice Check if a license is active. + /// @dev A non-existing or revoked license is inactive and this function must + /// return `false` upon it. Under some license terms, a license may become + /// inactive because some ancestral license has been revoked. In that case, + /// this function should return `false`. + /// @param _licenseId The identifier for the queried license + /// @return Whether the queried license is active + function isLicenseActive(uint256 _licenseId) external view returns (bool); + + /// @notice Retrieve the token identifier a license was issued upon. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The token identifier the queried license was issued upon + function getLicenseTokenId( + uint256 _licenseId + ) external view returns (uint256); + + /// @notice Retrieve the parent license identifier of a license. + /// @dev Throws unless the license is active. If a license doesn't have a + /// parent license, return a special identifier not referring to any license + /// (such as 0). + /// @param _licenseId The identifier for the queried license + /// @return The parent license identifier of the queried license + function getParentLicenseId( + uint256 _licenseId + ) external view returns (uint256); + + /// @notice Retrieve the holder of a license. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The holder address of the queried license + function getLicenseHolder( + uint256 _licenseId + ) external view returns (address); + + /// @notice Retrieve the URI of a license. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The URI of the queried license + function getLicenseURI( + uint256 _licenseId + ) external view returns (string memory); + + /// @notice Retrieve the revoker address of a license. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The revoker address of the queried license + function getLicenseRevoker( + uint256 _licenseId + ) external view returns (address); + + /// @notice Retrieve the root license identifier of an NFT. + /// @dev Throws unless the queried NFT exists. If the NFT doesn't have a root + /// license tethered to it, return a special identifier not referring to any + /// license (such as 0). + /// @param _tokenId The identifier for the queried NFT + /// @return The root license identifier of the queried NFT + function getLicenseIdByTokenId( + uint256 _tokenId, + bool _commercial // NOTE: MODIFIED ERC-5218 + ) external view returns (uint256); + + /// @notice Create a new license. + /// @dev Throws unless the NFT `_tokenId` exists. Throws unless the parent + /// license `_parentLicenseId` is active, or `_parentLicenseId` is a special + /// identifier not referring to any license (such as 0) and the NFT + /// `_tokenId` doesn't have a root license tethered to it. Throws unless the + /// message sender is eligible to create the license, i.e., either the + /// license to be created is a root license and `msg.sender` is the NFT owner, + /// or the license to be created is a sublicense and `msg.sender` is the holder + /// of the parent license. + /// @param _tokenId The identifier for the NFT the license is issued upon + /// @param _parentLicenseId The identifier for the parent license + /// @param _licenseHolder The address of the license holder + /// @param _uri The URI of the license terms + /// @param _revoker The revoker address + /// @return The identifier of the created license + function createLicense( + uint256 _tokenId, + uint256 _parentLicenseId, + address _licenseHolder, + string memory _uri, + address _revoker, + bool _commercial, // NOTE: MODIFIED ERC-5218 + bool _canSublicense // NOTE: MODIFIED ERC-5218 + ) external returns (uint256); + + /// @notice Revoke a license. + /// @dev Throws unless the license is active and the message sender is the + /// eligible revoker. This function should be used for revoking both root + /// licenses and sublicenses. Note that if a root license is revoked, the + /// NFT should be transferred back to its creator. + /// @param _licenseId The identifier for the queried license + function revokeLicense(uint256 _licenseId) external; + + /// @notice Transfer a sublicense. + /// @dev Throws unless the sublicense is active and `msg.sender` is the license + /// holder. Note that the root license of an NFT should be tethered to and + /// transferred with the NFT. Whenever an NFT is transferred by calling the + /// ERC721 `transfer` function, the holder of the root license should be + /// changed to the new NFT owner. + /// @param _licenseId The identifier for the queried license + /// @param _licenseHolder The new license holder + function transferSublicense( + uint256 _licenseId, + address _licenseHolder + ) external; +} diff --git a/contracts/modules/licensing/LicenseRegistry.sol b/contracts/modules/licensing/LicenseRegistry.sol new file mode 100644 index 00000000..78938833 --- /dev/null +++ b/contracts/modules/licensing/LicenseRegistry.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.13; +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol"; +import { IERC5218 } from "./IERC5218.sol"; + +contract LicenseRegistry is ERC721 { + + IERC5218 public immutable RIGHTS_MANAGER; + + constructor(address rightsManager, string memory name, string memory symbol) ERC721(name, symbol) { + if (rightsManager == address(0)) { + revert ZeroAddress(); + } + RIGHTS_MANAGER = IERC5218(rightsManager); + } + + + modifier onlyRightsManager() { + if (msg.sender != address(RIGHTS_MANAGER)) revert Unauthorized(); + _; + } + + function mint(address to, uint256 tokenId) public onlyRightsManager { + _mint(to, tokenId); + } + + + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override { + RIGHTS_MANAGER.transferSublicense(firstTokenId, to); + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + } + + + + +} \ No newline at end of file diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol deleted file mode 100644 index 566ffd20..00000000 --- a/contracts/modules/licensing/LicensingModule.sol +++ /dev/null @@ -1,283 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.13; -import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; -import { LibTimeConditional } from "../timing/LibTimeConditional.sol"; -import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { AccessControlledUpgradeable } from "contracts/access-control/AccessControlledUpgradeable.sol"; -import { UPGRADER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; - -contract LicensingModule is UUPSUpgradeable, ERC721Upgradeable, AccessControlledUpgradeable { - - event LicenseGranted ( - uint256 indexed licenseId, - address holder, - address ipAssetRegistry, - uint256 ipAssetId, - uint256 parentLicenseId - ); - - struct Token { - IERC721 collection; - uint256 tokenId; - } - - struct GeneralTerms { - bool exclusive;// Ignored now, TODO - bool canSublicense; // TODO: consider sublicensing limitations (i.e can only sublicense for media X, etc.) - bool commercial; - } - - struct PaymentTerms { - address interpreter; - bytes data; - } - - struct GrantingTerms { - address processor; - bytes data; - } - - struct OwnershipParams { - address holder; - Token token; - } - - struct License { - bool active; - uint256 parentLicenseId; - bytes32 mediaId; // ID of the media Type, e.q keccak256("MOVIE"), keccak256("MOVIE"), keccak256("MERCHANDISE")... Should this be "platform"? - GeneralTerms generalTerms; - - // If licensed token is set, the license is bound to a token, so ownerOf must return 0, and the license is not transferrable - Token licensedToken; - - // Contract defining the payment terms, royalties, milestones, etc. - // The interpreter does not necessarily enforce the terms. - PaymentTerms paymentTerms; - - // Intermediate steps before granting, such as approval by parent, KYC, revenue splits are set, time limits for transfer, security stuff... - // Triggered in beforeTransfer. - GrantingTerms grantingTerms; - address revoker; - - // Temporal terms and address that could renew the license - LibTimeConditional.TimeConfig durationTerms; - string licenseURI; - // TODO: tokenbound license config (share alike, ledger authoritative...) - } - - struct LicenseModuleStorage { - mapping(uint256 => License) licenses; - uint256 licenseCounter; - // TODO - mapping(address => bool) bannedMarketPlaces; - // Each franchise can choose to restrict stuff, like allowed license templates, the holder of root commercial licenses can only be - // the franchise owner and external PFP owners, etc. - mapping(uint256 => bytes) franchiseRestrictions; - // TODO: Remove this - mapping(address => mapping(uint256 => mapping(bool => uint256))) demoTokenToLicense; - string nonCommercialLicenseURI; - } - - // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.license-module.storage")) - 1))) - bytes32 private constant _STORAGE_LOCATION = 0x778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd; - - address immutable public FRANCHISE_REGISTRY; - - modifier onlyFranchiseRegistry() { - // TODO: extract to FranchiseRegistryControlled.sol - if (msg.sender != FRANCHISE_REGISTRY) revert("Sender is not the franchise registry"); - _; - } - - constructor(address franchiseRegistry) { - if (franchiseRegistry == address(0)) revert("Franchise registry cannot be zero address"); - FRANCHISE_REGISTRY = franchiseRegistry; - } - - function initialize(string calldata _nonCommercialLicenseURI, address accessControl) public initializer { - __UUPSUpgradeable_init(); - __AccessControlledUpgradeable_init(accessControl); - __ERC721_init("Story Protocol License", "SPL"); - _getLicenseModuleStorage().nonCommercialLicenseURI = _nonCommercialLicenseURI; - } - - function _getLicenseModuleStorage() private pure returns (LicenseModuleStorage storage $) { - assembly { - $.slot := _STORAGE_LOCATION - } - } - - function getNonCommercialLicenseURI() public view returns (string memory) { - return _getLicenseModuleStorage().nonCommercialLicenseURI; - } - - function isLicenseActive(uint256 licenseId) public view virtual returns (bool) { - // TODO: limit to the tree depth - // TODO: check time limits - if (licenseId == 0) return false; - while (licenseId != 0) { - LicenseModuleStorage storage $ = _getLicenseModuleStorage(); - License memory license = $.licenses[licenseId]; - if (!license.active) return false; - licenseId = license.parentLicenseId; - } - return true; - } - - function grantLicense( - address licensor, - uint256 parentLicenseId, - bytes32 mediaId, - GeneralTerms calldata generalTerms, - OwnershipParams calldata ownershipParams, - PaymentTerms calldata paymentTerms, - GrantingTerms calldata grantingTerms, - LibTimeConditional.TimeConfig calldata durationTerms, - string memory licenseURI, - address revoker - ) public onlyFranchiseRegistry returns (uint256) { - LicenseModuleStorage storage $ = _getLicenseModuleStorage(); - if (parentLicenseId != 0) { - License memory parentLicense = $.licenses[parentLicenseId]; - _verifySublicense(parentLicenseId, licensor, parentLicense, generalTerms); - licenseURI = parentLicense.licenseURI; - } else { - if (!generalTerms.commercial) { - licenseURI = $.nonCommercialLicenseURI; - } - } - - // TODO: check other terms - uint256 licenseId = _emitLicenseAndEvent( - parentLicenseId, - mediaId, - generalTerms, - ownershipParams, - paymentTerms, - grantingTerms, - durationTerms, - licenseURI, - revoker - ); - - // TODO: remove this, only for demo - $.demoTokenToLicense[address(ownershipParams.token.collection)][ownershipParams.token.tokenId][generalTerms.commercial] = licenseId; - return licenseId; - } - - function _emitLicenseAndEvent( - uint256 parentLicenseId, - bytes32 mediaId, - GeneralTerms calldata generalTerms, - OwnershipParams calldata ownershipParams, - PaymentTerms calldata paymentTerms, - GrantingTerms calldata grantingTerms, - LibTimeConditional.TimeConfig calldata durationTerms, - string memory licenseURI, - address revoker - ) private returns (uint256 licenseId) { - - licenseId = _emitLicense( - parentLicenseId, - mediaId, - generalTerms, - ownershipParams, - paymentTerms, - grantingTerms, - durationTerms, - licenseURI, - revoker - ); - - // Not bound to a token, mint to holder - if (ownershipParams.holder != address(0)) { - _mint(ownershipParams.holder, licenseId); - } - - emit LicenseGranted( - licenseId, - ownershipParams.holder, - address(ownershipParams.token.collection), - ownershipParams.token.tokenId, - parentLicenseId - ); - - return licenseId; - } - - - function _verifySublicense(uint256 parentLicenseId, address licensor, License memory parentLicense, GeneralTerms memory generalTerms) private view { - if (ownerOf(parentLicenseId) != licensor) revert("Sender is not the owner of the parent license"); - if (!parentLicense.active) revert("Parent license is not active"); - if (!parentLicense.generalTerms.canSublicense) revert("Parent license cannot be sublicensed"); - if (parentLicense.generalTerms.commercial != generalTerms.commercial) revert("Commercial terms must be the same as parent license"); - } - - function _isUnsetToken(Token memory token) private pure returns (bool) { - return address(token.collection) == address(0); - } - - function ownerOf(uint256 tokenId) public view virtual override returns (address) { - // TODO: This should work with ERC6665 or similar, this is only for demo - Token memory token = _getLicenseModuleStorage().licenses[tokenId].licensedToken; - if (address(token.collection) != address(0)) { - return token.collection.ownerOf(token.tokenId); - } - return super.ownerOf(tokenId); - } - - function _emitLicense( - uint256 parentLicenseId, - bytes32 mediaId, - GeneralTerms memory generalTerms, - OwnershipParams memory ownershipParams, - PaymentTerms memory paymentTerms, - GrantingTerms memory grantingTerms, - LibTimeConditional.TimeConfig memory durationTerms, - string memory licenseURI, - address revoker - ) internal returns (uint256) { - if (ownershipParams.holder == address(0) && _isUnsetToken(ownershipParams.token)) revert("License must be bound to a token or a license holder"); - if (ownershipParams.holder != address(0) && !_isUnsetToken(ownershipParams.token)) revert("License cannot be bound to a token and a license holder at the same time"); - // TODO: validate all terms - LicenseModuleStorage storage $ = _getLicenseModuleStorage(); - uint256 currentCounter = $.licenseCounter++; - $.licenses[currentCounter] = License({ - active: true, - parentLicenseId: parentLicenseId, - mediaId: mediaId, - generalTerms: generalTerms, - licensedToken: ownershipParams.token, - paymentTerms: paymentTerms, - grantingTerms: grantingTerms, - revoker: revoker, - durationTerms: durationTerms, - licenseURI: licenseURI - }); - return currentCounter; - } - - function licenseIdForToken(address collection, uint256 tokenId, bool commercial) public view returns (uint256) { - return _getLicenseModuleStorage().demoTokenToLicense[collection][tokenId][commercial]; - } - - function getLicense(uint256 licenseId) public view returns (License memory, address holder) { - return (_getLicenseModuleStorage().licenses[licenseId], ownerOf(licenseId)); - } - - function _beforeTokenTransfer( - address from, - address to, - uint256 firstTokenId, - uint256 batchSize - ) internal virtual override { - // TODO: check granting terms, banned marketplaces, etc. - super._beforeTokenTransfer(from, to, firstTokenId, batchSize); - } - - function _authorizeUpgrade( - address newImplementation - ) internal virtual override onlyRole(UPGRADER_ROLE) {} -} \ No newline at end of file diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol new file mode 100644 index 00000000..6687c036 --- /dev/null +++ b/contracts/modules/licensing/RightsManager.sol @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.13; +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import { LibTimeConditional } from "../timing/LibTimeConditional.sol"; +import { UPGRADER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; +import { IERC5218 } from "./IERC5218.sol"; +import { LicenseRegistry } from "./LicenseRegistry.sol"; +import { NonExistentID, Unauthorized, ZeroAddress } from "contracts/errors/General.sol"; +import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; + +abstract contract RightsManager is + ERC721Upgradeable, + IERC5218 +{ + error NotOwnerOfParentLicense(); + error InactiveLicense(); + error InactiveParentLicense(); + error CannotSublicense(); + error CommercialTermsMismatch(); + error SenderNotRevoker(); + error NotSublicense(); + error AlreadyHasRootLicense(); + + struct License { + bool active; + bool canSublicense; + bool commercial; + uint256 parentLicenseId; + uint256 tokenId; + address revoker; + string uri; // NOTE: should we merge this with IPAssetRegistry tokenURI for Licenses who are rights? + } + + struct LicenseModuleStorage { + mapping(uint256 => License) licenses; + // keccack256(commercial, tokenId) => licenseId + mapping(bytes32 => uint256) licenseForTokenId; + uint256 licenseCounter; + string nonCommercialLicenseURI; + } + + // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.license-module.storage")) - 1))) + bytes32 private constant _STORAGE_LOCATION = + 0x778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd; + uint256 private constant _UNSET_LICENSE_ID = 0; + LicenseRegistry public immutable LICENSE_REGISTRY; + + + constructor() { + LICENSE_REGISTRY = new LicenseRegistry(address(this), "Licenses", "SPLC"); + } + + function __RightsManager_init( + string calldata _nonCommercialLicenseURI, + string calldata name, + string calldata symbol + ) public initializer { + __ERC721_init(name, symbol); + _getLicenseModuleStorage() + .nonCommercialLicenseURI = _nonCommercialLicenseURI; + } + + function setNonCommercialLicenseURI( + string calldata _nonCommercialLicenseURI + ) external virtual; + + function _setNonCommercialLicenseURI( + string calldata _nonCommercialLicenseURI + ) internal { + _getLicenseModuleStorage() + .nonCommercialLicenseURI = _nonCommercialLicenseURI; + } + + function _getLicenseModuleStorage() + private + pure + returns (LicenseModuleStorage storage $) + { + assembly { + $.slot := _STORAGE_LOCATION + } + } + + function getNonCommercialLicenseURI() public view returns (string memory) { + return _getLicenseModuleStorage().nonCommercialLicenseURI; + } + + function isLicenseActive( + uint256 licenseId + ) public view virtual returns (bool) { + // TODO: limit to the tree depth + // TODO: check time limits + if (licenseId == 0) return false; + while (licenseId != 0) { + LicenseModuleStorage storage $ = _getLicenseModuleStorage(); + License memory license = $.licenses[licenseId]; + if (!license.active) return false; + licenseId = license.parentLicenseId; + } + return true; + } + + function _verifySublicense( + uint256 parentLicenseId, + address licensor, + bool commercial, + License memory parentLicense + ) private view { + if (ownerOf(parentLicenseId) != licensor) revert NotOwnerOfParentLicense(); + if (!parentLicense.active) revert InactiveParentLicense(); + if (!parentLicense.canSublicense) revert CannotSublicense(); + if (parentLicense.commercial != commercial) revert CommercialTermsMismatch(); + } + + function getLicense( + uint256 licenseId + ) public view returns (License memory, address holder) { + return ( + _getLicenseModuleStorage().licenses[licenseId], + getLicenseHolder(licenseId) + ); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override { + // TODO: check granting terms, banned marketplaces, etc. + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + } + + function getLicenseTokenId( + uint256 _licenseId + ) external view override returns (uint256) { + return _getLicenseModuleStorage().licenses[_licenseId].tokenId; + } + + function getParentLicenseId( + uint256 _licenseId + ) external view override returns (uint256) { + return _getLicenseModuleStorage().licenses[_licenseId].parentLicenseId; + } + + function getLicenseHolder( + uint256 _licenseId + ) public view override returns (address) { + License memory license = _getLicenseModuleStorage().licenses[ + _licenseId + ]; + if (license.parentLicenseId == _UNSET_LICENSE_ID) { + return ownerOf(_licenseId); + } else { + return LICENSE_REGISTRY.ownerOf(_licenseId); + } + } + + function getLicenseURI( + uint256 _licenseId + ) external view override returns (string memory) { + return _getLicenseModuleStorage().licenses[_licenseId].uri; + } + + function getLicenseRevoker( + uint256 _licenseId + ) external view override returns (address) { + return _getLicenseModuleStorage().licenses[_licenseId].revoker; + } + + function getLicenseIdByTokenId( + uint256 _tokenId, + bool _commercial + ) external view override returns (uint256) { + return + _getLicenseModuleStorage().licenseForTokenId[ + keccak256(abi.encode(_commercial, _tokenId)) + ]; + } + + function isRootLicense( + uint256 licenseId + ) public view returns (bool) { + return _getLicenseModuleStorage().licenses[licenseId].parentLicenseId == _UNSET_LICENSE_ID; + } + + function createLicense( + uint256 _tokenId, + uint256 _parentLicenseId, + address _licenseHolder, // NOTE: MODIFIED ERC-5218, we should ignore this + string memory _uri, + address _revoker, + bool _commercial, + bool _canSublicense + ) public override returns (uint256) { + if (_parentLicenseId == _UNSET_LICENSE_ID && msg.sender != address(this)) { + // Root licenses aka rights can only be minted by IPAssetRegistry + // TODO: check how to allow the Franchise NFT to have root commercial license + revert Unauthorized(); + } + if (!_commercial) { + _uri = getNonCommercialLicenseURI(); + } + return _createLicense( + _tokenId, + _parentLicenseId, + _licenseHolder, + _uri, + _revoker, + _commercial, + _canSublicense + ); + } + + function _createLicense( + uint256 tokenId, + uint256 parentLicenseId, + address licenseHolder, + string memory uri, + address revoker, + bool commercial, + bool canSublicense + ) internal returns (uint256) { + LicenseModuleStorage storage $ = _getLicenseModuleStorage(); + if (!_exists(tokenId)) { + revert NonExistentID(tokenId); + } + if ($.licenseForTokenId[ + keccak256(abi.encode(commercial, tokenId)) + ] != _UNSET_LICENSE_ID) { + revert AlreadyHasRootLicense(); + } + if (parentLicenseId != _UNSET_LICENSE_ID) { + License memory parentLicense = $.licenses[parentLicenseId]; + _verifySublicense(parentLicenseId, licenseHolder, commercial, parentLicense); + } + uint256 licenseId = ++$.licenseCounter; + $.licenses[licenseId] = License({ + active: true, + canSublicense: canSublicense, + commercial: commercial, + parentLicenseId: parentLicenseId, + tokenId: tokenId, + revoker: revoker, + uri: uri + }); + $.licenseForTokenId[ + keccak256(abi.encode(commercial, tokenId)) + ] = licenseId; + emit CreateLicense( + licenseId, + tokenId, + parentLicenseId, + licenseHolder, + uri, + revoker + ); + _updateLicenseHolder(licenseId, licenseHolder); + return licenseId; + } + + function revokeLicense(uint256 _licenseId) external override { + if (!_exists(_licenseId)) revert NonExistentID(_licenseId); + LicenseModuleStorage storage $ = _getLicenseModuleStorage(); + License storage license = $.licenses[_licenseId]; + if (msg.sender != license.revoker) revert SenderNotRevoker(); + license.active = false; + emit RevokeLicense(_licenseId); + } + + function _updateLicenseHolder( + uint256 licenseId, + address licenseHolder + ) internal virtual { + emit TransferLicense(licenseId, licenseHolder); + } + + function transferSublicense( + uint256 licenseId, + address licenseHolder + ) public virtual override(IERC5218) { + if (msg.sender != address(LICENSE_REGISTRY)) revert Unauthorized(); + if (!isLicenseActive(licenseId)) revert InactiveLicense(); + if (_getLicenseModuleStorage().licenses[licenseId].parentLicenseId == 0) + revert CannotSublicense(); + _updateLicenseHolder(licenseId, licenseHolder); + } + + function supportsInterface( + bytes4 interfaceId + ) + public + view + virtual + override(ERC721Upgradeable, IERC165Upgradeable) + returns (bool) + { + return + interfaceId == type(IERC5218).interfaceId || + super.supportsInterface(interfaceId); + } + +} \ No newline at end of file diff --git a/contracts/modules/licensing/licensingparams.txt b/contracts/modules/licensing/licensingparams.txt new file mode 100644 index 00000000..e69de29b diff --git a/script/foundry/upgrades/DevDeployments.s.sol b/script/foundry/upgrades/DevDeployments.s.sol deleted file mode 100644 index 14090e96..00000000 --- a/script/foundry/upgrades/DevDeployments.s.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import "forge-std/Script.sol"; -import "script/foundry/utils/StringUtil.sol"; -import "script/foundry/utils/JsonDeploymentHandler.s.sol"; -import "script/foundry/utils/BroadcastManager.s.sol"; -import "test/foundry/utils/ProxyHelper.sol"; -import "contracts/modules/relationships/ProtocolRelationshipModule.sol"; -import "contracts/modules/relationships/RelationshipModuleBase.sol"; -import "contracts/FranchiseRegistry.sol"; -import "contracts/modules/licensing/LicensingModule.sol"; - -/** - * Use to upgrade contracts during development, in testnets. Expect things to break. - */ -contract DeployLicenseModule is Script, BroadcastManager, JsonDeploymentHandler, ProxyHelper { - - using StringUtil for uint256; - using stdJson for string; - - constructor() JsonDeploymentHandler("main") {} - - function run() public { - _readDeployment(); - _beginBroadcast(); - - address franchiseRegistryProxy = _readAddress(".main.FranchiseRegistry-Proxy"); - - - /// ACCESS CONTROL SINGLETON - string memory contractKey = "LicenseModule-Impl"; - - console.log(string.concat("Deploying ", contractKey, "...")); - address licenseModuleImpl = address(new LicensingModule(franchiseRegistryProxy)); - console.log(string.concat(contractKey, " deployed to:"), licenseModuleImpl); - - - contractKey = "LicenseModule-Proxy"; - string memory noncommercialLicenseURL = "https://arweave.net/yHIbKlFBg3xuKSzlM_dREG8Y08uod-gWKsWi9OaPFsM"; - address accessControl = _readAddress(".main.AccessControlSingleton-Proxy"); - console.log(string.concat("Deploying ", contractKey, "...")); - address proxy = _deployUUPSProxy( - licenseModuleImpl, - abi.encodeWithSelector( - bytes4(keccak256(bytes("initialize(string,address)"))), noncommercialLicenseURL, accessControl - ) - ); - - console.log(string.concat(contractKey, " deployed to:"), proxy); - - } - -} diff --git a/script/foundry/upgrades/DevUpgrades.s.sol b/script/foundry/upgrades/DevUpgrades.s.sol index 4a4807f3..37802454 100644 --- a/script/foundry/upgrades/DevUpgrades.s.sol +++ b/script/foundry/upgrades/DevUpgrades.s.sol @@ -7,13 +7,11 @@ import "script/foundry/utils/JsonDeploymentHandler.s.sol"; import "script/foundry/utils/BroadcastManager.s.sol"; import "contracts/modules/relationships/ProtocolRelationshipModule.sol"; import "contracts/modules/relationships/RelationshipModuleBase.sol"; -import "contracts/modules/licensing/LicensingModule.sol"; import "contracts/FranchiseRegistry.sol"; import "contracts/access-control/AccessControlSingleton.sol"; import "contracts/access-control/ProtocolRoles.sol"; import "contracts/ip-assets/events/CommonIPAssetEventEmitter.sol"; import "contracts/ip-assets/IPAssetRegistry.sol"; -import "contracts/modules/licensing/LicensingModule.sol"; /** @@ -69,11 +67,6 @@ contract UpgradeFranchiseRegistry is Script, BroadcastManager, JsonDeploymentHan franchiseRegistry.upgradeTo(newFranchiseRegistry); console.log("Upgrading IPAssetRegistryFactory to ", newFranchiseRegistry); - - LicensingModule licenseModule = LicensingModule(_readAddress(".main.LicenseModule-Proxy")); - console.log("Setting license module"); - franchiseRegistry.setLicensingModule(licenseModule); - } } @@ -113,29 +106,3 @@ contract UpgradeIPAssetRegistry is Script, BroadcastManager, JsonDeploymentHandl } } - -contract UpgradeLicenseModule is Script, BroadcastManager, JsonDeploymentHandler { - - using StringUtil for uint256; - using stdJson for string; - - constructor() JsonDeploymentHandler("main") {} - - function run() public { - _readDeployment(); - _beginBroadcast(); - - address franchiseRegistryProxy = _readAddress(".main.FranchiseRegistry-Proxy"); - - address licenseModuleImpl = address(new LicensingModule(franchiseRegistryProxy)); - - LicensingModule licenseModule = LicensingModule(_readAddress(".main.LicenseModule-Proxy")); - licenseModule.upgradeTo(licenseModuleImpl); - - console.log("Upgraded LicenseModule to ", licenseModuleImpl); - address accessControl = _readAddress(".main.AccessControlSingleton-Proxy"); - licenseModule.setAccessControl(accessControl); - - } - -} diff --git a/test/foundry/LicenseModule.t.sol b/test/foundry/LicenseModule.t.sol index 1314e5f1..b4640d0c 100644 --- a/test/foundry/LicenseModule.t.sol +++ b/test/foundry/LicenseModule.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; import './utils/BaseTest.sol'; +/* import { LicensingModule } from "contracts/modules/licensing/LicensingModule.sol"; contract LicenseModuleTest is BaseTest { @@ -41,3 +42,4 @@ contract LicenseModuleTest is BaseTest { } } +*/ \ No newline at end of file From b3f46db7c20722b10b8087e520e70b60b705d8d2 Mon Sep 17 00:00:00 2001 From: Raul Date: Fri, 18 Aug 2023 13:42:47 +0200 Subject: [PATCH 10/42] licensing module --- contracts/access-control/ProtocolRoles.sol | 1 + .../modules/licensing/LicenseRegistry.sol | 5 -- .../modules/licensing/LicensingModule.sol | 83 +++++++++++++++++++ contracts/modules/licensing/RightsManager.sol | 57 ++++--------- .../modules/licensing/licensingparams.txt | 0 5 files changed, 101 insertions(+), 45 deletions(-) create mode 100644 contracts/modules/licensing/LicensingModule.sol delete mode 100644 contracts/modules/licensing/licensingparams.txt diff --git a/contracts/access-control/ProtocolRoles.sol b/contracts/access-control/ProtocolRoles.sol index 438a1706..fa517b20 100644 --- a/contracts/access-control/ProtocolRoles.sol +++ b/contracts/access-control/ProtocolRoles.sol @@ -5,3 +5,4 @@ pragma solidity ^0.8.13; bytes32 constant PROTOCOL_ADMIN_ROLE = bytes32(0); bytes32 constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); bytes32 constant RELATIONSHIP_MANAGER_ROLE = keccak256("RELATIONSHIP_MANAGER_ROLE"); +bytes32 constant LICENSING_MANAGER_ROLE = keccak256("LICENSING_MANAGER_ROLE"); diff --git a/contracts/modules/licensing/LicenseRegistry.sol b/contracts/modules/licensing/LicenseRegistry.sol index 78938833..0d6ebf53 100644 --- a/contracts/modules/licensing/LicenseRegistry.sol +++ b/contracts/modules/licensing/LicenseRegistry.sol @@ -15,7 +15,6 @@ contract LicenseRegistry is ERC721 { RIGHTS_MANAGER = IERC5218(rightsManager); } - modifier onlyRightsManager() { if (msg.sender != address(RIGHTS_MANAGER)) revert Unauthorized(); _; @@ -25,7 +24,6 @@ contract LicenseRegistry is ERC721 { _mint(to, tokenId); } - function _beforeTokenTransfer( address from, address to, @@ -36,7 +34,4 @@ contract LicenseRegistry is ERC721 { super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } - - - } \ No newline at end of file diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol new file mode 100644 index 00000000..2e21b34e --- /dev/null +++ b/contracts/modules/licensing/LicensingModule.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.13; +import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol"; +import { FranchiseRegistry } from "contracts/FranchiseRegistry.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import { AccessControlledUpgradeable } from "contracts/access-control/AccessControlledUpgradeable.sol"; +import { UPGRADER_ROLE, LICENSING_MANAGER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; + +contract LicensingModule is AccessControlledUpgradeable { + + struct IpAssetConfig { + bool canSublicense; + uint256 franchiseRootLicenseId; + // TODO: allowed license terms? processors? + // TODO: limit medium of sublicenses? something like LibIPAssetMask? + } + + struct FranchiseConfig { + IpAssetConfig commercialConfig; + IpAssetConfig nonCommercialConfig; + bool rootIpAssetHasCommercialRights; + } + + struct LicensingModuleStorage { + /// franchiseId => FranchiseConfig + mapping(uint256 => FranchiseConfig) franchiseConfigs; + string nonCommercialLicenseURI; + } + + event NonCommercialLicenseUriSet(string uri); + event FranchiseConfigSet(uint256 franchiseId, FranchiseConfig config); + + // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.licensing-module.storage")) - 1))) + bytes32 private constant _STORAGE_LOCATION = 0x80b4ea8c21e869c68acfd93c8ef2c0d867835b92e2fded15a1d74d7e7ff3312d; + + FranchiseRegistry public immutable FRANCHISE_REGISTRY; + + constructor(address franchiseRegistry) { + if (franchiseRegistry == address(0)) { + revert ZeroAddress(); + } + FRANCHISE_REGISTRY = FranchiseRegistry(franchiseRegistry); + _disableInitializers(); + } + + function initialize(address accessControl, string calldata nonCommercialLicenseUri) public initializer { + __AccessControlledUpgradeable_init(accessControl); + _getLicensingModuleStorage().nonCommercialLicenseURI = nonCommercialLicenseUri; + } + + function _getLicensingModuleStorage() internal pure returns (LicensingModuleStorage storage $) { + bytes32 position = _STORAGE_LOCATION; + assembly { + $.slot := position + } + } + + function getNonCommercialLicenseURI() public view returns (string memory) { + return _getLicensingModuleStorage().nonCommercialLicenseURI; + } + + function setNonCommercialLicenseURI(string calldata _nonCommercialLicenseURI) external onlyRole(LICENSING_MANAGER_ROLE) { + _getLicensingModuleStorage().nonCommercialLicenseURI = _nonCommercialLicenseURI; + emit NonCommercialLicenseUriSet(_nonCommercialLicenseURI); + } + + function configureLicense(uint256 franchiseId, FranchiseConfig memory config) external { + if (msg.sender != FRANCHISE_REGISTRY.ownerOf(franchiseId)) { + revert Unauthorized(); + } + LicensingModuleStorage storage $ = _getLicensingModuleStorage(); + $.franchiseConfigs[franchiseId] = config; + emit FranchiseConfigSet(franchiseId, config); + } + + function getFranchiseConfig(uint256 franchiseId) public view returns (FranchiseConfig memory) { + return _getLicensingModuleStorage().franchiseConfigs[franchiseId]; + } + + function _authorizeUpgrade( + address newImplementation + ) internal virtual override onlyRole(UPGRADER_ROLE) {} +} \ No newline at end of file diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 6687c036..2ac26382 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -32,17 +32,15 @@ abstract contract RightsManager is string uri; // NOTE: should we merge this with IPAssetRegistry tokenURI for Licenses who are rights? } - struct LicenseModuleStorage { + struct RightsManagerStorage { mapping(uint256 => License) licenses; // keccack256(commercial, tokenId) => licenseId mapping(bytes32 => uint256) licenseForTokenId; uint256 licenseCounter; - string nonCommercialLicenseURI; } - // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.license-module.storage")) - 1))) - bytes32 private constant _STORAGE_LOCATION = - 0x778e3a21329d920b45eecf00e356693a1888f1ae24d67d398ab1f17457bcfabd; + // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.rights-manager.storage")) - 1))) + bytes32 private constant _STORAGE_LOCATION = 0x315576c20e31e03ef3e70482445a4c33e45baf13beff28e79f2adf6d06cc0bee; uint256 private constant _UNSET_LICENSE_ID = 0; LicenseRegistry public immutable LICENSE_REGISTRY; @@ -52,40 +50,22 @@ abstract contract RightsManager is } function __RightsManager_init( - string calldata _nonCommercialLicenseURI, string calldata name, string calldata symbol ) public initializer { __ERC721_init(name, symbol); - _getLicenseModuleStorage() - .nonCommercialLicenseURI = _nonCommercialLicenseURI; } - function setNonCommercialLicenseURI( - string calldata _nonCommercialLicenseURI - ) external virtual; - - function _setNonCommercialLicenseURI( - string calldata _nonCommercialLicenseURI - ) internal { - _getLicenseModuleStorage() - .nonCommercialLicenseURI = _nonCommercialLicenseURI; - } - - function _getLicenseModuleStorage() + function _getRightsManagerStorage() private pure - returns (LicenseModuleStorage storage $) + returns (RightsManagerStorage storage $) { assembly { $.slot := _STORAGE_LOCATION } } - function getNonCommercialLicenseURI() public view returns (string memory) { - return _getLicenseModuleStorage().nonCommercialLicenseURI; - } - function isLicenseActive( uint256 licenseId ) public view virtual returns (bool) { @@ -93,7 +73,7 @@ abstract contract RightsManager is // TODO: check time limits if (licenseId == 0) return false; while (licenseId != 0) { - LicenseModuleStorage storage $ = _getLicenseModuleStorage(); + RightsManagerStorage storage $ = _getRightsManagerStorage(); License memory license = $.licenses[licenseId]; if (!license.active) return false; licenseId = license.parentLicenseId; @@ -117,7 +97,7 @@ abstract contract RightsManager is uint256 licenseId ) public view returns (License memory, address holder) { return ( - _getLicenseModuleStorage().licenses[licenseId], + _getRightsManagerStorage().licenses[licenseId], getLicenseHolder(licenseId) ); } @@ -135,19 +115,19 @@ abstract contract RightsManager is function getLicenseTokenId( uint256 _licenseId ) external view override returns (uint256) { - return _getLicenseModuleStorage().licenses[_licenseId].tokenId; + return _getRightsManagerStorage().licenses[_licenseId].tokenId; } function getParentLicenseId( uint256 _licenseId ) external view override returns (uint256) { - return _getLicenseModuleStorage().licenses[_licenseId].parentLicenseId; + return _getRightsManagerStorage().licenses[_licenseId].parentLicenseId; } function getLicenseHolder( uint256 _licenseId ) public view override returns (address) { - License memory license = _getLicenseModuleStorage().licenses[ + License memory license = _getRightsManagerStorage().licenses[ _licenseId ]; if (license.parentLicenseId == _UNSET_LICENSE_ID) { @@ -160,13 +140,13 @@ abstract contract RightsManager is function getLicenseURI( uint256 _licenseId ) external view override returns (string memory) { - return _getLicenseModuleStorage().licenses[_licenseId].uri; + return _getRightsManagerStorage().licenses[_licenseId].uri; } function getLicenseRevoker( uint256 _licenseId ) external view override returns (address) { - return _getLicenseModuleStorage().licenses[_licenseId].revoker; + return _getRightsManagerStorage().licenses[_licenseId].revoker; } function getLicenseIdByTokenId( @@ -174,7 +154,7 @@ abstract contract RightsManager is bool _commercial ) external view override returns (uint256) { return - _getLicenseModuleStorage().licenseForTokenId[ + _getRightsManagerStorage().licenseForTokenId[ keccak256(abi.encode(_commercial, _tokenId)) ]; } @@ -182,7 +162,7 @@ abstract contract RightsManager is function isRootLicense( uint256 licenseId ) public view returns (bool) { - return _getLicenseModuleStorage().licenses[licenseId].parentLicenseId == _UNSET_LICENSE_ID; + return _getRightsManagerStorage().licenses[licenseId].parentLicenseId == _UNSET_LICENSE_ID; } function createLicense( @@ -199,9 +179,6 @@ abstract contract RightsManager is // TODO: check how to allow the Franchise NFT to have root commercial license revert Unauthorized(); } - if (!_commercial) { - _uri = getNonCommercialLicenseURI(); - } return _createLicense( _tokenId, _parentLicenseId, @@ -222,7 +199,7 @@ abstract contract RightsManager is bool commercial, bool canSublicense ) internal returns (uint256) { - LicenseModuleStorage storage $ = _getLicenseModuleStorage(); + RightsManagerStorage storage $ = _getRightsManagerStorage(); if (!_exists(tokenId)) { revert NonExistentID(tokenId); } @@ -262,7 +239,7 @@ abstract contract RightsManager is function revokeLicense(uint256 _licenseId) external override { if (!_exists(_licenseId)) revert NonExistentID(_licenseId); - LicenseModuleStorage storage $ = _getLicenseModuleStorage(); + RightsManagerStorage storage $ = _getRightsManagerStorage(); License storage license = $.licenses[_licenseId]; if (msg.sender != license.revoker) revert SenderNotRevoker(); license.active = false; @@ -282,7 +259,7 @@ abstract contract RightsManager is ) public virtual override(IERC5218) { if (msg.sender != address(LICENSE_REGISTRY)) revert Unauthorized(); if (!isLicenseActive(licenseId)) revert InactiveLicense(); - if (_getLicenseModuleStorage().licenses[licenseId].parentLicenseId == 0) + if (_getRightsManagerStorage().licenses[licenseId].parentLicenseId == 0) revert CannotSublicense(); _updateLicenseHolder(licenseId, licenseHolder); } diff --git a/contracts/modules/licensing/licensingparams.txt b/contracts/modules/licensing/licensingparams.txt deleted file mode 100644 index e69de29b..00000000 From 60139cd0647c8e072bc20a18ca27a66c35b81f39 Mon Sep 17 00:00:00 2001 From: Raul Date: Fri, 18 Aug 2023 18:41:32 +0200 Subject: [PATCH 11/42] create initial rights --- contracts/FranchiseRegistry.sol | 13 --- contracts/ip-assets/IIPAssetRegistry.sol | 3 +- contracts/ip-assets/IPAssetRegistry.sol | 83 ++++++++++++------- .../modules/licensing/LicensingModule.sol | 7 +- contracts/modules/licensing/RightsManager.sol | 2 +- test/foundry/IPAssetsRegistry.t.sol | 18 ++-- .../RelationshipModule.Relating.t.sol | 12 +-- 7 files changed, 78 insertions(+), 60 deletions(-) diff --git a/contracts/FranchiseRegistry.sol b/contracts/FranchiseRegistry.sol index cf8ef3d7..756834aa 100644 --- a/contracts/FranchiseRegistry.sol +++ b/contracts/FranchiseRegistry.sol @@ -114,19 +114,6 @@ contract FranchiseRegistry is } } - // TODO create asset as derivative of another asset - function createIPAsset( - uint256 franchiseId, - IPAsset sb, - string calldata name, - string calldata _description, - string calldata mediaUrl - ) external returns (uint256 ipAssetID) { - FranchiseStorage storage $ = _getFranchiseStorage(); - address ipAssetRegistry = $.ipAssetRegistries[franchiseId]; - ipAssetID = IIPAssetRegistry(ipAssetRegistry).createIPAsset(sb, name, _description, mediaUrl, msg.sender); - return ipAssetID; - } function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { _requireMinted(tokenId); diff --git a/contracts/ip-assets/IIPAssetRegistry.sol b/contracts/ip-assets/IIPAssetRegistry.sol index 34159e51..0e514f70 100644 --- a/contracts/ip-assets/IIPAssetRegistry.sol +++ b/contracts/ip-assets/IIPAssetRegistry.sol @@ -20,7 +20,8 @@ interface IIPAssetRegistry is string calldata name, string calldata _description, string calldata mediaUrl, - address to + address to, + uint256 parentIpAssetId ) external returns (uint256); } diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index a3c6b663..ba8fc3bf 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -12,6 +12,7 @@ import { ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC7 import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; import { RightsManager } from "../modules/licensing/RightsManager.sol"; +import { LicensingModule } from "../modules/licensing/LicensingModule.sol"; contract IPAssetRegistry is IPAssetDataManager, @@ -29,35 +30,28 @@ contract IPAssetRegistry is } IIPAssetEventEmitter public immutable EVENT_EMITTER; - FranchiseRegistry public immutable FRANCHISE_REGISTRY; + LicensingModule public immutable LICENSING_MODULE; // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.ip-assets-registry.storage")) - 1))) bytes32 private constant _STORAGE_LOCATION = 0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378; string private constant _VERSION = "0.1.0"; - constructor(address _eventEmitter, address _franchiseRegistry) { + constructor(address _eventEmitter, address _licensingModule) { + // TODO: should Franchise owner be able to change this? if (_eventEmitter == address(0)) revert ZeroAddress(); EVENT_EMITTER = IIPAssetEventEmitter(_eventEmitter); - if (_franchiseRegistry == address(0)) revert ZeroAddress(); - FRANCHISE_REGISTRY = FranchiseRegistry(_franchiseRegistry); + if (_licensingModule == address(0)) revert ZeroAddress(); + LICENSING_MODULE = LicensingModule(_licensingModule); _disableInitializers(); } - modifier onlyFranchiseRegistry() { - // TODO: extract to FranchiseRegistryControlled.sol - if (msg.sender != address(FRANCHISE_REGISTRY)) - revert("Sender is not the franchise registry"); - _; - } - function initialize( uint256 _franchiseId, string calldata _name, string calldata _symbol, - string calldata _description, - string calldata _nonCommercialLicenseUri + string calldata _description ) public initializer { - __RightsManager_init(_nonCommercialLicenseUri, _name, _symbol); + __RightsManager_init(_name, _symbol); __Multicall_init(); if (_franchiseId == 0) revert ZeroAmount(); IPAssetRegistryStorage storage $ = _getIPAssetRegistryStorage(); @@ -80,25 +74,61 @@ contract IPAssetRegistry is } function createIPAsset( - IPAsset sb, + IPAsset ipAssetType, string calldata name, string calldata _description, string calldata mediaUrl, - address to + address to, + uint256 parentIpAssetId ) public - onlyFranchiseRegistry returns (uint256) { - if (sb == IPAsset.UNDEFINED) revert InvalidBlockType(); - uint256 sbId = _mintBlock(to, sb); - _writeIPAsset(sbId, name, _description, mediaUrl); + if (ipAssetType == IPAsset.UNDEFINED) revert InvalidBlockType(); + uint256 ipAssetId = _mintBlock(to, ipAssetType); + _writeIPAsset(ipAssetId, name, _description, mediaUrl); IPAssetRegistryStorage storage $ = _getIPAssetRegistryStorage(); - EVENT_EMITTER.emitIPAssetCreation($.franchiseId, sbId); - // TODO: grant rights (root licenses) according to what the Franchise Owner sets in the LicensingModulegit - return sbId; + EVENT_EMITTER.emitIPAssetCreation($.franchiseId, ipAssetId); + // Non commercial + LicensingModule.FranchiseConfig memory config = LICENSING_MODULE.getFranchiseConfig($.franchiseId); + _setNonCommercialRights(ipAssetId, parentIpAssetId, to, config.revoker, config.nonCommercialConfig); + + // If non derivative IpAsset, then franchise config may dictate commercial rights + // Derivative works do not have commercial rights unless a deal with the relevant licensor is made + if (config.rootIpAssetHasCommercialRights && parentIpAssetId == 0) { + // Commercial + _setCommercialRights(ipAssetId, 0, to, config.revoker, config.commercialLicenseUri, config.commercialConfig); + } + return ipAssetId; } + function _setNonCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, LicensingModule.IpAssetConfig memory config) private { + uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, false); + createLicense( + ipAssetId, + parentLicenseId, + holder, + LICENSING_MODULE.getNonCommercialLicenseURI(), + revoker, + false, + config.canSublicense + ); + } + + function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, LicensingModule.IpAssetConfig memory config) private { + uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, true); + createLicense( + ipAssetId, + parentLicenseId, + holder, + licenseUri, + revoker, + true, + config.canSublicense + ); + } + + function _mintBlock(address to, IPAsset sb) private returns (uint256) { uint256 nextId = currentIdFor(sb) + 1; if (nextId > LibIPAssetId._lastId(sb)) revert IdOverBounds(); @@ -148,11 +178,4 @@ contract IPAssetRegistry is super.supportsInterface(interfaceId); } - function setNonCommercialLicenseURI(string calldata uri) - external - override - { - // TODO: add governance role to change this - } - } diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index 2e21b34e..8fd7fba0 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -13,12 +13,15 @@ contract LicensingModule is AccessControlledUpgradeable { uint256 franchiseRootLicenseId; // TODO: allowed license terms? processors? // TODO: limit medium of sublicenses? something like LibIPAssetMask? + // TODO: limit who you can sublicense to? } struct FranchiseConfig { - IpAssetConfig commercialConfig; IpAssetConfig nonCommercialConfig; + IpAssetConfig commercialConfig; bool rootIpAssetHasCommercialRights; + address revoker; + string commercialLicenseUri; } struct LicensingModuleStorage { @@ -64,7 +67,7 @@ contract LicensingModule is AccessControlledUpgradeable { emit NonCommercialLicenseUriSet(_nonCommercialLicenseURI); } - function configureLicense(uint256 franchiseId, FranchiseConfig memory config) external { + function configureFranchiseLicensing(uint256 franchiseId, FranchiseConfig memory config) external { if (msg.sender != FRANCHISE_REGISTRY.ownerOf(franchiseId)) { revert Unauthorized(); } diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 2ac26382..c3797769 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -152,7 +152,7 @@ abstract contract RightsManager is function getLicenseIdByTokenId( uint256 _tokenId, bool _commercial - ) external view override returns (uint256) { + ) public view override returns (uint256) { return _getRightsManagerStorage().licenseForTokenId[ keccak256(abi.encode(_commercial, _tokenId)) diff --git a/test/foundry/IPAssetsRegistry.t.sol b/test/foundry/IPAssetsRegistry.t.sol index 889f77cc..c3bbc839 100644 --- a/test/foundry/IPAssetsRegistry.t.sol +++ b/test/foundry/IPAssetsRegistry.t.sol @@ -58,11 +58,11 @@ contract IPAssetRegistryTest is Test { uint256 zero = LibIPAssetId._zeroId(sb); assertEq(ipAssetRegistry.currentIdFor(sb), zero, "starts with zero"); vm.prank(mockFranchiseRegistry); - uint256 blockId1 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl", mintee); + uint256 blockId1 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl", mintee, 0); assertEq(blockId1, zero + 1, "returned blockId is incremented by one"); assertEq(ipAssetRegistry.currentIdFor(sb), zero + 1, "mint increments currentIdFor by one"); vm.prank(mockFranchiseRegistry); - uint256 blockId2 = ipAssetRegistry.createIPAsset(sb, "name2", "description2", "mediaUrl2", mintee); + uint256 blockId2 = ipAssetRegistry.createIPAsset(sb, "name2", "description2", "mediaUrl2", mintee, 0); assertEq(blockId2, zero + 2, "returned blockId is incremented by one again"); assertEq(ipAssetRegistry.currentIdFor(sb), zero + 2, "2 mint increments currentIdFor by one again"); } @@ -77,11 +77,11 @@ contract IPAssetRegistryTest is Test { uint256 loopBalance = ipAssetRegistry.balanceOf(mintee); assertEq(loopBalance, (i - 1) * 2, "balance is zero for block type"); vm.prank(mockFranchiseRegistry); - uint256 blockId1 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl", mintee); + uint256 blockId1 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl", mintee, 0); assertEq(ipAssetRegistry.balanceOf(mintee), loopBalance + 1, "balance is incremented by one"); assertEq(ipAssetRegistry.ownerOf(blockId1), mintee); vm.prank(mockFranchiseRegistry); - uint256 blockId2 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl", mintee); + uint256 blockId2 = ipAssetRegistry.createIPAsset(sb, "name", "description", "mediaUrl", mintee, 0); assertEq(ipAssetRegistry.balanceOf(mintee), loopBalance + 2, "balance is incremented by one again"); assertEq(ipAssetRegistry.ownerOf(blockId2), mintee); } @@ -90,12 +90,12 @@ contract IPAssetRegistryTest is Test { function test_revertMintUnknownIPAsset() public { vm.startPrank(mockFranchiseRegistry); vm.expectRevert(InvalidBlockType.selector); - ipAssetRegistry.createIPAsset(IPAsset.UNDEFINED, "name", "description", "mediaUrl", mintee); + ipAssetRegistry.createIPAsset(IPAsset.UNDEFINED, "name", "description", "mediaUrl", mintee, 0); } function test_IPAssetCreationData() public { vm.prank(mockFranchiseRegistry); - uint256 blockId = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "mediaUrl", mintee); + uint256 blockId = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "mediaUrl", mintee, 0); IPAssetRegistry.IPAssetData memory data = ipAssetRegistry.readIPAsset(blockId); assertEq(uint8(data.blockType), uint8(IPAsset.STORY)); assertEq(data.name, "name"); @@ -113,8 +113,12 @@ contract IPAssetRegistryTest is Test { function test_tokenUriReturnsMediaURL() public { vm.prank(mockFranchiseRegistry); - uint256 blockId = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "https://mediaUrl.xyz", mintee); + uint256 blockId = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "https://mediaUrl.xyz", mintee, 0); assertEq(ipAssetRegistry.tokenURI(blockId), "https://mediaUrl.xyz"); } +} + +contract DerivativeIPAssetRegistryTest is Test { + // TODO } \ No newline at end of file diff --git a/test/foundry/relationships/RelationshipModule.Relating.t.sol b/test/foundry/relationships/RelationshipModule.Relating.t.sol index d58ea11d..95da6263 100644 --- a/test/foundry/relationships/RelationshipModule.Relating.t.sol +++ b/test/foundry/relationships/RelationshipModule.Relating.t.sol @@ -50,9 +50,9 @@ contract RelationshipModuleRelationshipTest is BaseTest { relationshipId = relationshipModule.setRelationshipConfig("RELATIONSHIP_ID", params); vm.startPrank(address(franchiseRegistry)); - ipAssetIds[uint8(IPAsset.STORY)] = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "mediaUrl", ipAssetOwner); - ipAssetIds[uint8(IPAsset.CHARACTER)] = ipAssetRegistry.createIPAsset(IPAsset.CHARACTER, "name", "description", "mediaUrl", ipAssetOwner); - ipAssetIds[uint8(IPAsset.ART)] = ipAssetRegistry.createIPAsset(IPAsset.ART, "name", "description", "mediaUrl", ipAssetOwner); + ipAssetIds[uint8(IPAsset.STORY)] = ipAssetRegistry.createIPAsset(IPAsset.STORY, "name", "description", "mediaUrl", ipAssetOwner, 0); + ipAssetIds[uint8(IPAsset.CHARACTER)] = ipAssetRegistry.createIPAsset(IPAsset.CHARACTER, "name", "description", "mediaUrl", ipAssetOwner, 0); + ipAssetIds[uint8(IPAsset.ART)] = ipAssetRegistry.createIPAsset(IPAsset.ART, "name", "description", "mediaUrl", ipAssetOwner, 0); vm.stopPrank(); vm.startPrank(ipAssetOwner); @@ -139,7 +139,7 @@ contract RelationshipModuleRelationshipTest is BaseTest { (uint256 id, address otherIPAssets) = franchiseRegistry.registerFranchise(params); IPAssetRegistry otherIPAssetRegistry = IPAssetRegistry(otherIPAssets); vm.prank(address(franchiseRegistry)); - uint256 otherId = otherIPAssetRegistry.createIPAsset(IPAsset.CHARACTER, "name", "description", "mediaUrl", ipAssetOwner); + uint256 otherId = otherIPAssetRegistry.createIPAsset(IPAsset.CHARACTER, "name", "description", "mediaUrl", ipAssetOwner, 0); vm.expectRevert(IRelationshipModule.CannotRelateToOtherFranchise.selector); relationshipModule.relate( IRelationshipModule.RelationshipParams( @@ -151,7 +151,7 @@ contract RelationshipModuleRelationshipTest is BaseTest { function test_revert_relateUnsupportedSource() public { vm.prank(address(franchiseRegistry)); - uint256 wrongId = ipAssetRegistry.createIPAsset(IPAsset.GROUP, "name", "description", "mediaUrl", ipAssetOwner); + uint256 wrongId = ipAssetRegistry.createIPAsset(IPAsset.GROUP, "name", "description", "mediaUrl", ipAssetOwner, 0); vm.expectRevert(IRelationshipModule.UnsupportedRelationshipSrc.selector); relationshipModule.relate( IRelationshipModule.RelationshipParams( @@ -163,7 +163,7 @@ contract RelationshipModuleRelationshipTest is BaseTest { function test_revert_relateUnsupportedDestination() public { vm.prank(address(franchiseRegistry)); - uint256 wrongId = ipAssetRegistry.createIPAsset(IPAsset.GROUP, "name", "description", "mediaUrl", ipAssetOwner); + uint256 wrongId = ipAssetRegistry.createIPAsset(IPAsset.GROUP, "name", "description", "mediaUrl", ipAssetOwner, 0); vm.expectRevert(IRelationshipModule.UnsupportedRelationshipDst.selector); relationshipModule.relate( IRelationshipModule.RelationshipParams( From 57bb946c82a8a6a5f5c0c1f6940d61d896f70d16 Mon Sep 17 00:00:00 2001 From: Raul Date: Fri, 18 Aug 2023 19:25:51 +0200 Subject: [PATCH 12/42] parameter interpreters first commit --- contracts/errors/General.sol | 3 +- .../parameters/IParameterInterpreter.sol | 8 ++++++ .../MultipleParameterInterpreter.sol | 28 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 contracts/modules/licensing/parameters/IParameterInterpreter.sol create mode 100644 contracts/modules/licensing/parameters/MultipleParameterInterpreter.sol diff --git a/contracts/errors/General.sol b/contracts/errors/General.sol index e1205aad..1f96a120 100644 --- a/contracts/errors/General.sol +++ b/contracts/errors/General.sol @@ -6,4 +6,5 @@ error ZeroAddress(); error ZeroAmount(); error UnsupportedInterface(string name); error Unauthorized(); -error NonExistentID(uint256 id); \ No newline at end of file +error NonExistentID(uint256 id); +error EmptyArray(); \ No newline at end of file diff --git a/contracts/modules/licensing/parameters/IParameterInterpreter.sol b/contracts/modules/licensing/parameters/IParameterInterpreter.sol new file mode 100644 index 00000000..3661757e --- /dev/null +++ b/contracts/modules/licensing/parameters/IParameterInterpreter.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; + +interface IParameterInterpreter { + + function encodeParams() external returns(bytes memory); + +} \ No newline at end of file diff --git a/contracts/modules/licensing/parameters/MultipleParameterInterpreter.sol b/contracts/modules/licensing/parameters/MultipleParameterInterpreter.sol new file mode 100644 index 00000000..5eb6c3c1 --- /dev/null +++ b/contracts/modules/licensing/parameters/MultipleParameterInterpreter.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; + +import { IParameterInterpreter } from "./IParameterInterpreter.sol"; +import { EmptyArray } from "contracts/errors/General.sol"; + +contract MultipleParameterInterpreter is IParameterInterpreter { + + IParameterInterpreter[] public interpreters; + + constructor(IParameterInterpreter[] memory _interpreters) { + setInterpreters(_interpreters); + } + + function setInterpreters(IParameterInterpreter[] memory _interpreters) public { + if (_interpreters.length == 0) revert EmptyArray(); + interpreters = _interpreters; + } + + function encodeParams() external returns(bytes memory) { + bytes[] memory encodedParams = new bytes[](interpreters.length); + for (uint256 i = 0; i < interpreters.length; i++) { + encodedParams[i] = interpreters[i].encodeParams(); + } + return abi.encode(encodedParams); + } + +} \ No newline at end of file From 7d570496318ea2dcb400003b080924327d540323 Mon Sep 17 00:00:00 2001 From: Raul Date: Tue, 22 Aug 2023 18:54:30 +0200 Subject: [PATCH 13/42] terms processor --- contracts/errors/General.sol | 3 +- contracts/modules/licensing/IERC5218.sol | 14 +++++- contracts/modules/licensing/RightsManager.sol | 15 +++++-- .../parameters/IParameterInterpreter.sol | 8 ---- .../MultipleParameterInterpreter.sol | 28 ------------ .../licensing/terms/ITermsProcessor.sol | 31 +++++++++++++ .../licensing/terms/MultiTermsProcessor.sol | 45 +++++++++++++++++++ 7 files changed, 102 insertions(+), 42 deletions(-) delete mode 100644 contracts/modules/licensing/parameters/IParameterInterpreter.sol delete mode 100644 contracts/modules/licensing/parameters/MultipleParameterInterpreter.sol create mode 100644 contracts/modules/licensing/terms/ITermsProcessor.sol create mode 100644 contracts/modules/licensing/terms/MultiTermsProcessor.sol diff --git a/contracts/errors/General.sol b/contracts/errors/General.sol index 1f96a120..308b3631 100644 --- a/contracts/errors/General.sol +++ b/contracts/errors/General.sol @@ -7,4 +7,5 @@ error ZeroAmount(); error UnsupportedInterface(string name); error Unauthorized(); error NonExistentID(uint256 id); -error EmptyArray(); \ No newline at end of file +error EmptyArray(); +error LengthMismatch(); \ No newline at end of file diff --git a/contracts/modules/licensing/IERC5218.sol b/contracts/modules/licensing/IERC5218.sol index 855416a8..85eb230d 100644 --- a/contracts/modules/licensing/IERC5218.sol +++ b/contracts/modules/licensing/IERC5218.sol @@ -1,6 +1,7 @@ pragma solidity ^0.8.0; import { IERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; +import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; /// @title ERC-5218: NFT Rights Management interface IERC5218 is IERC721Upgradeable { @@ -26,6 +27,11 @@ interface IERC5218 is IERC721Upgradeable { /// `transfer` function call. event TransferLicense(uint256 _licenseId, address _licenseHolder); + struct TermsProcessorConfig { + ITermsProcessor config; + bytes data; + } + /// @notice Check if a license is active. /// @dev A non-existing or revoked license is inactive and this function must /// return `false` upon it. Under some license terms, a license may become @@ -102,15 +108,19 @@ interface IERC5218 is IERC721Upgradeable { /// @param _licenseHolder The address of the license holder /// @param _uri The URI of the license terms /// @param _revoker The revoker address + /// @param _commercial Whether the license granted is commercial or non commercial + /// @param _canSublicense Whether the license holder can sublicense the license + /// @param _terms The license terms /// @return The identifier of the created license function createLicense( uint256 _tokenId, uint256 _parentLicenseId, address _licenseHolder, string memory _uri, - address _revoker, + address _revoker, // NOTE: MODIFIED ERC-5218 bool _commercial, // NOTE: MODIFIED ERC-5218 - bool _canSublicense // NOTE: MODIFIED ERC-5218 + bool _canSublicense, // NOTE: MODIFIED ERC-5218 + TermsProcessorConfig memory _terms // NOTE: MODIFIED ERC-5218 ) external returns (uint256); /// @notice Revoke a license. diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index c3797769..2bfe07f4 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -93,6 +93,11 @@ abstract contract RightsManager is if (parentLicense.commercial != commercial) revert CommercialTermsMismatch(); } + function _addTerms(ProcessorConfig memory _terms) internal virtual { + if (IERC) + + } + function getLicense( uint256 licenseId ) public view returns (License memory, address holder) { @@ -102,6 +107,7 @@ abstract contract RightsManager is ); } + function _beforeTokenTransfer( address from, address to, @@ -172,7 +178,8 @@ abstract contract RightsManager is string memory _uri, address _revoker, bool _commercial, - bool _canSublicense + bool _canSublicense, + ProcessorConfig memory _terms ) public override returns (uint256) { if (_parentLicenseId == _UNSET_LICENSE_ID && msg.sender != address(this)) { // Root licenses aka rights can only be minted by IPAssetRegistry @@ -186,7 +193,8 @@ abstract contract RightsManager is _uri, _revoker, _commercial, - _canSublicense + _canSublicense, + _terms ); } @@ -197,7 +205,8 @@ abstract contract RightsManager is string memory uri, address revoker, bool commercial, - bool canSublicense + bool canSublicense, + ProcessorConfig memory _terms ) internal returns (uint256) { RightsManagerStorage storage $ = _getRightsManagerStorage(); if (!_exists(tokenId)) { diff --git a/contracts/modules/licensing/parameters/IParameterInterpreter.sol b/contracts/modules/licensing/parameters/IParameterInterpreter.sol deleted file mode 100644 index 3661757e..00000000 --- a/contracts/modules/licensing/parameters/IParameterInterpreter.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -interface IParameterInterpreter { - - function encodeParams() external returns(bytes memory); - -} \ No newline at end of file diff --git a/contracts/modules/licensing/parameters/MultipleParameterInterpreter.sol b/contracts/modules/licensing/parameters/MultipleParameterInterpreter.sol deleted file mode 100644 index 5eb6c3c1..00000000 --- a/contracts/modules/licensing/parameters/MultipleParameterInterpreter.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { IParameterInterpreter } from "./IParameterInterpreter.sol"; -import { EmptyArray } from "contracts/errors/General.sol"; - -contract MultipleParameterInterpreter is IParameterInterpreter { - - IParameterInterpreter[] public interpreters; - - constructor(IParameterInterpreter[] memory _interpreters) { - setInterpreters(_interpreters); - } - - function setInterpreters(IParameterInterpreter[] memory _interpreters) public { - if (_interpreters.length == 0) revert EmptyArray(); - interpreters = _interpreters; - } - - function encodeParams() external returns(bytes memory) { - bytes[] memory encodedParams = new bytes[](interpreters.length); - for (uint256 i = 0; i < interpreters.length; i++) { - encodedParams[i] = interpreters[i].encodeParams(); - } - return abi.encode(encodedParams); - } - -} \ No newline at end of file diff --git a/contracts/modules/licensing/terms/ITermsProcessor.sol b/contracts/modules/licensing/terms/ITermsProcessor.sol new file mode 100644 index 00000000..34ecff4a --- /dev/null +++ b/contracts/modules/licensing/terms/ITermsProcessor.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; + +/** + * @title ITermsProcessor + * @author Raul Martinez + * @notice Interface for licensing terms processors, which encode, decode and execute the terms set on an IERC5218 license parameters, + * in particular the TermsProcessorConfig struct for the terms parameter in createLicense() + */ +interface ITermsProcessor { + + /** + * @notice Encodes the terms to set on a license on creation. + * This should be called to construct the TermsProcessorConfig struct for the terms parameter in createLicense() + * @return The encoded terms. + */ + function encodeTerms() external returns(bytes memory); + /** + * @notice Decodes the terms to set on a license on creation. + * This should be called to before calling executeTerms() + * // TODO: should this be internal? + */ + function decodeTerms(bytes calldata data) external; + /** + * @notice Executes the terms set on a license on creation. + * This should be called after decodeTerms() + * @return Whether the terms were executed successfully. + */ + function executeTerms() external returns(bool); + +} \ No newline at end of file diff --git a/contracts/modules/licensing/terms/MultiTermsProcessor.sol b/contracts/modules/licensing/terms/MultiTermsProcessor.sol new file mode 100644 index 00000000..425bd324 --- /dev/null +++ b/contracts/modules/licensing/terms/MultiTermsProcessor.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; + +import { ITermsProcessor } from "./ITermsProcessor.sol"; +import { EmptyArray, LengthMismatch } from "contracts/errors/General.sol"; + +contract MultiTermsProcessor is ITermsProcessor { + ITermsProcessor[] public processors; + + constructor(ITermsProcessor[] memory _processors) { + setProcessors(_processors); + } + + function setProcessors(ITermsProcessor[] memory _processors) public { + if (_processors.length == 0) revert EmptyArray(); + processors = _processors; + } + + function encodeTerms() external returns (bytes memory) { + bytes[] memory encodedTerms = new bytes[](processors.length); + uint256 length = encodedTerms.length; + for (uint256 i = 0; i < length; i++) { + encodedTerms[i] = processors[i].encodeTerms(); + } + return abi.encode(encodedTerms); + } + + function decodeTerms(bytes calldata data) external { + bytes[] memory terms = abi.decode(data, (bytes[])); + uint256 length = terms.length; + if (length != processors.length) revert LengthMismatch(); + for (uint256 i = 0; i < length; i++) { + processors[i].decodeTerms(terms[i]); + } + } + + function executeTerms() external override returns (bool) { + uint256 length = processors.length; + bool result = true; + for (uint256 i = 0; i < length; i++) { + result = result && processors[i].executeTerms(); + } + return result; + } +} \ No newline at end of file From 991a3fe83aae813e36f238e28354d4122ebd534f Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 23 Aug 2023 00:38:34 +0200 Subject: [PATCH 14/42] link terms to licensing contracts --- contracts/ip-assets/IPAssetRegistry.sol | 14 ++++--- .../modules/licensing/LicensingModule.sol | 5 +++ contracts/modules/licensing/RightsManager.sol | 34 ++++++++++++----- .../licensing/terms/ITermsProcessor.sol | 10 +++-- .../licensing/terms/MultiTermsProcessor.sol | 37 ++++++++++++++++++- 5 files changed, 79 insertions(+), 21 deletions(-) diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index ba8fc3bf..f7d5eaad 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -91,18 +91,18 @@ contract IPAssetRegistry is EVENT_EMITTER.emitIPAssetCreation($.franchiseId, ipAssetId); // Non commercial LicensingModule.FranchiseConfig memory config = LICENSING_MODULE.getFranchiseConfig($.franchiseId); - _setNonCommercialRights(ipAssetId, parentIpAssetId, to, config.revoker, config.nonCommercialConfig); + _setNonCommercialRights(ipAssetId, parentIpAssetId, to, config.revoker, config.nonCommercialConfig, config.nonCommercialTerms); // If non derivative IpAsset, then franchise config may dictate commercial rights // Derivative works do not have commercial rights unless a deal with the relevant licensor is made if (config.rootIpAssetHasCommercialRights && parentIpAssetId == 0) { // Commercial - _setCommercialRights(ipAssetId, 0, to, config.revoker, config.commercialLicenseUri, config.commercialConfig); + _setCommercialRights(ipAssetId, 0, to, config.revoker, config.commercialLicenseUri, config.commercialConfig, config.commercialTerms); } return ipAssetId; } - function _setNonCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, LicensingModule.IpAssetConfig memory config) private { + function _setNonCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, LicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) private { uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, false); createLicense( ipAssetId, @@ -111,11 +111,12 @@ contract IPAssetRegistry is LICENSING_MODULE.getNonCommercialLicenseURI(), revoker, false, - config.canSublicense + config.canSublicense, + terms ); } - function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, LicensingModule.IpAssetConfig memory config) private { + function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, LicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) private { uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, true); createLicense( ipAssetId, @@ -124,7 +125,8 @@ contract IPAssetRegistry is licenseUri, revoker, true, - config.canSublicense + config.canSublicense, + terms ); } diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index 8fd7fba0..ee711342 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -5,6 +5,8 @@ import { FranchiseRegistry } from "contracts/FranchiseRegistry.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { AccessControlledUpgradeable } from "contracts/access-control/AccessControlledUpgradeable.sol"; import { UPGRADER_ROLE, LICENSING_MANAGER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; +import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; +import { IERC5218 } from "./IERC5218.sol"; contract LicensingModule is AccessControlledUpgradeable { @@ -18,10 +20,13 @@ contract LicensingModule is AccessControlledUpgradeable { struct FranchiseConfig { IpAssetConfig nonCommercialConfig; + IERC5218.TermsProcessorConfig nonCommercialTerms; IpAssetConfig commercialConfig; + IERC5218.TermsProcessorConfig commercialTerms; bool rootIpAssetHasCommercialRights; address revoker; string commercialLicenseUri; + } struct LicensingModuleStorage { diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 2bfe07f4..f11fb3ca 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -6,13 +6,17 @@ import { LibTimeConditional } from "../timing/LibTimeConditional.sol"; import { UPGRADER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; import { IERC5218 } from "./IERC5218.sol"; import { LicenseRegistry } from "./LicenseRegistry.sol"; -import { NonExistentID, Unauthorized, ZeroAddress } from "contracts/errors/General.sol"; +import { NonExistentID, Unauthorized, ZeroAddress, UnsupportedInterface } from "contracts/errors/General.sol"; import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; +import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; +import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; abstract contract RightsManager is ERC721Upgradeable, IERC5218 { + using ERC165CheckerUpgradeable for address; + error NotOwnerOfParentLicense(); error InactiveLicense(); error InactiveParentLicense(); @@ -30,6 +34,9 @@ abstract contract RightsManager is uint256 tokenId; address revoker; string uri; // NOTE: should we merge this with IPAssetRegistry tokenURI for Licenses who are rights? + ITermsProcessor termsProcessor; + bytes termsData; + } struct RightsManagerStorage { @@ -75,12 +82,13 @@ abstract contract RightsManager is while (licenseId != 0) { RightsManagerStorage storage $ = _getRightsManagerStorage(); License memory license = $.licenses[licenseId]; - if (!license.active) return false; + if (!(license.active && license.termsProcessor.tersmExecutedSuccessfully())) return false; licenseId = license.parentLicenseId; } return true; } + function _verifySublicense( uint256 parentLicenseId, address licensor, @@ -93,11 +101,6 @@ abstract contract RightsManager is if (parentLicense.commercial != commercial) revert CommercialTermsMismatch(); } - function _addTerms(ProcessorConfig memory _terms) internal virtual { - if (IERC) - - } - function getLicense( uint256 licenseId ) public view returns (License memory, address holder) { @@ -118,6 +121,14 @@ abstract contract RightsManager is super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } + function _verifyTerms(TermsProcessorConfig memory _terms) private view { + if (address(_terms.config) != address(0) && + !address(_terms.config).supportsInterface(type(ITermsProcessor).interfaceId)) { + revert UnsupportedInterface("ITermsProcessor"); + } + + } + function getLicenseTokenId( uint256 _licenseId ) external view override returns (uint256) { @@ -179,7 +190,7 @@ abstract contract RightsManager is address _revoker, bool _commercial, bool _canSublicense, - ProcessorConfig memory _terms + TermsProcessorConfig memory _terms ) public override returns (uint256) { if (_parentLicenseId == _UNSET_LICENSE_ID && msg.sender != address(this)) { // Root licenses aka rights can only be minted by IPAssetRegistry @@ -206,7 +217,7 @@ abstract contract RightsManager is address revoker, bool commercial, bool canSublicense, - ProcessorConfig memory _terms + TermsProcessorConfig memory _terms ) internal returns (uint256) { RightsManagerStorage storage $ = _getRightsManagerStorage(); if (!_exists(tokenId)) { @@ -221,6 +232,7 @@ abstract contract RightsManager is License memory parentLicense = $.licenses[parentLicenseId]; _verifySublicense(parentLicenseId, licenseHolder, commercial, parentLicense); } + _verifyTerms(_terms); uint256 licenseId = ++$.licenseCounter; $.licenses[licenseId] = License({ active: true, @@ -229,7 +241,9 @@ abstract contract RightsManager is parentLicenseId: parentLicenseId, tokenId: tokenId, revoker: revoker, - uri: uri + uri: uri, + termsProcessor: _terms.config, + termsData: _terms.data }); $.licenseForTokenId[ keccak256(abi.encode(commercial, tokenId)) diff --git a/contracts/modules/licensing/terms/ITermsProcessor.sol b/contracts/modules/licensing/terms/ITermsProcessor.sol index 34ecff4a..e3e92a55 100644 --- a/contracts/modules/licensing/terms/ITermsProcessor.sol +++ b/contracts/modules/licensing/terms/ITermsProcessor.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @title ITermsProcessor @@ -7,20 +8,20 @@ pragma solidity ^0.8.19; * @notice Interface for licensing terms processors, which encode, decode and execute the terms set on an IERC5218 license parameters, * in particular the TermsProcessorConfig struct for the terms parameter in createLicense() */ -interface ITermsProcessor { +interface ITermsProcessor is IERC165 { /** * @notice Encodes the terms to set on a license on creation. * This should be called to construct the TermsProcessorConfig struct for the terms parameter in createLicense() * @return The encoded terms. */ - function encodeTerms() external returns(bytes memory); + function encodeTerms() external view returns(bytes memory); /** * @notice Decodes the terms to set on a license on creation. * This should be called to before calling executeTerms() * // TODO: should this be internal? */ - function decodeTerms(bytes calldata data) external; + function decodeTerms(bytes calldata data) view external; /** * @notice Executes the terms set on a license on creation. * This should be called after decodeTerms() @@ -28,4 +29,7 @@ interface ITermsProcessor { */ function executeTerms() external returns(bool); + /// returns true if the terms have been executed successfully or they don't need to be executed, false otherwise + function tersmExecutedSuccessfully() external view returns(bool); + } \ No newline at end of file diff --git a/contracts/modules/licensing/terms/MultiTermsProcessor.sol b/contracts/modules/licensing/terms/MultiTermsProcessor.sol index 425bd324..2de6ba25 100644 --- a/contracts/modules/licensing/terms/MultiTermsProcessor.sol +++ b/contracts/modules/licensing/terms/MultiTermsProcessor.sol @@ -5,18 +5,27 @@ import { ITermsProcessor } from "./ITermsProcessor.sol"; import { EmptyArray, LengthMismatch } from "contracts/errors/General.sol"; contract MultiTermsProcessor is ITermsProcessor { + error TooManyTermsProcessors(); + + event ProcessorsSet(ITermsProcessor[] processors); + ITermsProcessor[] public processors; + uint256 public constant MAX_PROCESSORS = 100; + constructor(ITermsProcessor[] memory _processors) { setProcessors(_processors); } function setProcessors(ITermsProcessor[] memory _processors) public { if (_processors.length == 0) revert EmptyArray(); + if (_processors.length > MAX_PROCESSORS) + revert TooManyTermsProcessors(); processors = _processors; + emit ProcessorsSet(_processors); } - function encodeTerms() external returns (bytes memory) { + function encodeTerms() external view returns (bytes memory) { bytes[] memory encodedTerms = new bytes[](processors.length); uint256 length = encodedTerms.length; for (uint256 i = 0; i < length; i++) { @@ -25,7 +34,7 @@ contract MultiTermsProcessor is ITermsProcessor { return abi.encode(encodedTerms); } - function decodeTerms(bytes calldata data) external { + function decodeTerms(bytes calldata data) external view { bytes[] memory terms = abi.decode(data, (bytes[])); uint256 length = terms.length; if (length != processors.length) revert LengthMismatch(); @@ -42,4 +51,28 @@ contract MultiTermsProcessor is ITermsProcessor { } return result; } + + function supportsInterface( + bytes4 interfaceId + ) external view override returns (bool) { + bool supported = true; + if (interfaceId == type(ITermsProcessor).interfaceId) { + uint256 length = processors.length; + for (uint256 i = 0; i < length; i++) { + supported && processors[i].supportsInterface(interfaceId); + } + return supported; + } + return false; + } + + function tersmExecutedSuccessfully() external view override returns (bool) { + uint256 length = processors.length; + bool result = true; + for (uint256 i = 0; i < length; i++) { + result = result && processors[i].tersmExecutedSuccessfully(); + } + return result; + } + } \ No newline at end of file From 5576cb87207e300f7921c8812566db03939c5f3b Mon Sep 17 00:00:00 2001 From: Raul Date: Thu, 24 Aug 2023 17:16:42 +0200 Subject: [PATCH 15/42] wip --- .../licensing/terms/ITermsProcessor.sol | 14 +-------- .../licensing/terms/MultiTermsProcessor.sol | 29 +++++-------------- .../licensing/terms/TimingTermsProcessor.sol | 0 .../modules/timing/LibTimeConditional.sol | 6 ++-- 4 files changed, 13 insertions(+), 36 deletions(-) create mode 100644 contracts/modules/licensing/terms/TimingTermsProcessor.sol diff --git a/contracts/modules/licensing/terms/ITermsProcessor.sol b/contracts/modules/licensing/terms/ITermsProcessor.sol index e3e92a55..08886e2b 100644 --- a/contracts/modules/licensing/terms/ITermsProcessor.sol +++ b/contracts/modules/licensing/terms/ITermsProcessor.sol @@ -10,24 +10,12 @@ import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol */ interface ITermsProcessor is IERC165 { - /** - * @notice Encodes the terms to set on a license on creation. - * This should be called to construct the TermsProcessorConfig struct for the terms parameter in createLicense() - * @return The encoded terms. - */ - function encodeTerms() external view returns(bytes memory); - /** - * @notice Decodes the terms to set on a license on creation. - * This should be called to before calling executeTerms() - * // TODO: should this be internal? - */ - function decodeTerms(bytes calldata data) view external; /** * @notice Executes the terms set on a license on creation. * This should be called after decodeTerms() * @return Whether the terms were executed successfully. */ - function executeTerms() external returns(bool); + function executeTerms(bytes calldata data) external returns(bool); /// returns true if the terms have been executed successfully or they don't need to be executed, false otherwise function tersmExecutedSuccessfully() external view returns(bool); diff --git a/contracts/modules/licensing/terms/MultiTermsProcessor.sol b/contracts/modules/licensing/terms/MultiTermsProcessor.sol index 2de6ba25..a3dfc8c1 100644 --- a/contracts/modules/licensing/terms/MultiTermsProcessor.sol +++ b/contracts/modules/licensing/terms/MultiTermsProcessor.sol @@ -25,29 +25,16 @@ contract MultiTermsProcessor is ITermsProcessor { emit ProcessorsSet(_processors); } - function encodeTerms() external view returns (bytes memory) { - bytes[] memory encodedTerms = new bytes[](processors.length); - uint256 length = encodedTerms.length; - for (uint256 i = 0; i < length; i++) { - encodedTerms[i] = processors[i].encodeTerms(); - } - return abi.encode(encodedTerms); - } - - function decodeTerms(bytes calldata data) external view { - bytes[] memory terms = abi.decode(data, (bytes[])); - uint256 length = terms.length; - if (length != processors.length) revert LengthMismatch(); - for (uint256 i = 0; i < length; i++) { - processors[i].decodeTerms(terms[i]); - } - } - - function executeTerms() external override returns (bool) { + function executeTerms(bytes calldata data) external override returns (bool) { uint256 length = processors.length; + bytes[] memory encodedTerms = new bytes[](length); + encodedTerms = abi.decode(data, (bytes[])); bool result = true; - for (uint256 i = 0; i < length; i++) { - result = result && processors[i].executeTerms(); + for (uint256 i = 0; i < length;) { + result = result && processors[i].executeTerms(encodedTerms[i]); + unchecked { + i++; + } } return result; } diff --git a/contracts/modules/licensing/terms/TimingTermsProcessor.sol b/contracts/modules/licensing/terms/TimingTermsProcessor.sol new file mode 100644 index 00000000..e69de29b diff --git a/contracts/modules/timing/LibTimeConditional.sol b/contracts/modules/timing/LibTimeConditional.sol index 02bbfaad..77722777 100644 --- a/contracts/modules/timing/LibTimeConditional.sol +++ b/contracts/modules/timing/LibTimeConditional.sol @@ -4,11 +4,13 @@ pragma solidity ^0.8.13; library LibTimeConditional { struct TimeConfig { - uint112 maxTTL; - uint112 minTTL; bool renewable; address renewer; uint256 endTime; } + function isTimeExpired(TimeConfig memory self) internal view returns (bool) { + return self.endTime < block.timestamp; + } + } \ No newline at end of file From fa821b11a4f1e871429ee74a89e779d9e1b1a7a3 Mon Sep 17 00:00:00 2001 From: Raul Date: Thu, 24 Aug 2023 22:59:01 +0200 Subject: [PATCH 16/42] licenseholder fix and time relationship processors --- contracts/FranchiseRegistry.sol | 1 - contracts/modules/licensing/IERC5218.sol | 3 + .../modules/licensing/LicenseRegistry.sol | 4 + contracts/modules/licensing/RightsManager.sol | 222 +++++++++--------- .../licensing/terms/ITermsProcessor.sol | 6 +- .../licensing/terms/MultiTermsProcessor.sol | 24 +- .../licensing/terms/TimeTermsProcessor.sol | 29 +++ .../licensing/terms/TimingTermsProcessor.sol | 0 contracts/modules/timing/LibDuration.sol | 50 ++++ .../modules/timing/LibTimeConditional.sol | 16 -- 10 files changed, 221 insertions(+), 134 deletions(-) create mode 100644 contracts/modules/licensing/terms/TimeTermsProcessor.sol delete mode 100644 contracts/modules/licensing/terms/TimingTermsProcessor.sol create mode 100644 contracts/modules/timing/LibDuration.sol delete mode 100644 contracts/modules/timing/LibTimeConditional.sol diff --git a/contracts/FranchiseRegistry.sol b/contracts/FranchiseRegistry.sol index 756834aa..9d6075d6 100644 --- a/contracts/FranchiseRegistry.sol +++ b/contracts/FranchiseRegistry.sol @@ -12,7 +12,6 @@ import { LibIPAssetId } from "./ip-assets/LibIPAssetId.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import { LibTimeConditional } from "./modules/timing/LibTimeConditional.sol"; contract FranchiseRegistry is UUPSUpgradeable, diff --git a/contracts/modules/licensing/IERC5218.sol b/contracts/modules/licensing/IERC5218.sol index 85eb230d..87e3e64b 100644 --- a/contracts/modules/licensing/IERC5218.sol +++ b/contracts/modules/licensing/IERC5218.sol @@ -15,6 +15,9 @@ interface IERC5218 is IERC721Upgradeable { address _revoker ); + /// @dev emits when license terms are executed to activate a license + event ExecuteTerms(uint256 _licenseId, bytes _data); + /// @dev This emits when a license is revoked. Note that under some /// license terms, the sublicenses may be `implicitly` revoked following the /// revocation of some ancestral license. In that case, your smart contract diff --git a/contracts/modules/licensing/LicenseRegistry.sol b/contracts/modules/licensing/LicenseRegistry.sol index 0d6ebf53..0f9d5afc 100644 --- a/contracts/modules/licensing/LicenseRegistry.sol +++ b/contracts/modules/licensing/LicenseRegistry.sol @@ -24,6 +24,10 @@ contract LicenseRegistry is ERC721 { _mint(to, tokenId); } + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + function _beforeTokenTransfer( address from, address to, diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index f11fb3ca..6249ff24 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; -import { LibTimeConditional } from "../timing/LibTimeConditional.sol"; +import { LibDuration } from "../timing/LibDuration.sol"; import { UPGRADER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; import { IERC5218 } from "./IERC5218.sol"; import { LicenseRegistry } from "./LicenseRegistry.sol"; @@ -36,7 +36,6 @@ abstract contract RightsManager is string uri; // NOTE: should we merge this with IPAssetRegistry tokenURI for Licenses who are rights? ITermsProcessor termsProcessor; bytes termsData; - } struct RightsManagerStorage { @@ -73,6 +72,106 @@ abstract contract RightsManager is } } + function createLicense( + uint256 _tokenId, + uint256 _parentLicenseId, + address _licenseHolder, // NOTE: MODIFIED ERC-5218, we should ignore this + string memory _uri, + address _revoker, + bool _commercial, + bool _canSublicense, + TermsProcessorConfig memory _terms + ) public override returns (uint256) { + if (_parentLicenseId == _UNSET_LICENSE_ID && msg.sender != address(this)) { + // Root licenses aka rights can only be minted by IPAssetRegistry + // TODO: check how to allow the Franchise NFT to have root commercial license + revert Unauthorized(); + } + return _createLicense( + _tokenId, + _parentLicenseId, + _licenseHolder, + _uri, + _revoker, + _commercial, + _canSublicense, + _terms + ); + } + + function _createLicense( + uint256 tokenId, + uint256 parentLicenseId, + address licenseHolder, + string memory uri, + address revoker, + bool commercial, + bool canSublicense, + TermsProcessorConfig memory _terms + ) internal returns (uint256) { + RightsManagerStorage storage $ = _getRightsManagerStorage(); + if (!_exists(tokenId)) { + revert NonExistentID(tokenId); + } + if ($.licenseForTokenId[ + keccak256(abi.encode(commercial, tokenId)) + ] != _UNSET_LICENSE_ID) { + revert AlreadyHasRootLicense(); + } + if (parentLicenseId != _UNSET_LICENSE_ID) { + License memory parentLicense = $.licenses[parentLicenseId]; + _verifySublicense(parentLicenseId, licenseHolder, commercial, parentLicense); + } + _verifyTerms(_terms); + uint256 licenseId = ++$.licenseCounter; + $.licenses[licenseId] = License({ + active: true, + canSublicense: canSublicense, + commercial: commercial, + parentLicenseId: parentLicenseId, + tokenId: tokenId, + revoker: revoker, + uri: uri, + termsProcessor: _terms.config, + termsData: _terms.data + }); + $.licenseForTokenId[ + keccak256(abi.encode(commercial, tokenId)) + ] = licenseId; + emit CreateLicense( + licenseId, + tokenId, + parentLicenseId, + licenseHolder, + uri, + revoker + ); + emit TransferLicense(licenseId, licenseHolder); + return licenseId; + } + + function revokeLicense(uint256 _licenseId) external override { + if (!_exists(_licenseId)) revert NonExistentID(_licenseId); + RightsManagerStorage storage $ = _getRightsManagerStorage(); + License storage license = $.licenses[_licenseId]; + if (msg.sender != license.revoker) revert SenderNotRevoker(); + license.active = false; + emit RevokeLicense(_licenseId); + } + + function executeTerms(uint256 _licenseId) external { + if (msg.sender != address(LICENSE_REGISTRY)) revert Unauthorized(); + RightsManagerStorage storage $ = _getRightsManagerStorage(); + License storage license = $.licenses[_licenseId]; + if (license.termsProcessor != ITermsProcessor(address(0))) { + bytes memory newData = license.termsProcessor.executeTerms(license.termsData); + if (keccak256(license.termsData) != keccak256(newData)) { + license.termsData = newData; + } + } + emit ExecuteTerms(_licenseId, license.termsData); + } + function isLicenseActive( uint256 licenseId ) public view virtual returns (bool) { @@ -82,7 +181,12 @@ abstract contract RightsManager is while (licenseId != 0) { RightsManagerStorage storage $ = _getRightsManagerStorage(); License memory license = $.licenses[licenseId]; - if (!(license.active && license.termsProcessor.tersmExecutedSuccessfully())) return false; + if ( + !( + license.active && + license.termsProcessor.tersmExecutedSuccessfully(license.termsData) + ) + ) return false; licenseId = license.parentLicenseId; } return true; @@ -110,14 +214,13 @@ abstract contract RightsManager is ); } - function _beforeTokenTransfer( address from, address to, uint256 firstTokenId, uint256 batchSize ) internal virtual override { - // TODO: check granting terms, banned marketplaces, etc. + // TODO: trigger rights transfer check, check granting terms, banned marketplaces, etc. super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } @@ -126,7 +229,6 @@ abstract contract RightsManager is !address(_terms.config).supportsInterface(type(ITermsProcessor).interfaceId)) { revert UnsupportedInterface("ITermsProcessor"); } - } function getLicenseTokenId( @@ -144,13 +246,13 @@ abstract contract RightsManager is function getLicenseHolder( uint256 _licenseId ) public view override returns (address) { - License memory license = _getRightsManagerStorage().licenses[ - _licenseId - ]; - if (license.parentLicenseId == _UNSET_LICENSE_ID) { - return ownerOf(_licenseId); - } else { + if (LICENSE_REGISTRY.exists(_licenseId)) { return LICENSE_REGISTRY.ownerOf(_licenseId); + } else { + License storage license = _getRightsManagerStorage().licenses[ + _licenseId + ]; + return ownerOf(license.tokenId); } } @@ -182,99 +284,7 @@ abstract contract RightsManager is return _getRightsManagerStorage().licenses[licenseId].parentLicenseId == _UNSET_LICENSE_ID; } - function createLicense( - uint256 _tokenId, - uint256 _parentLicenseId, - address _licenseHolder, // NOTE: MODIFIED ERC-5218, we should ignore this - string memory _uri, - address _revoker, - bool _commercial, - bool _canSublicense, - TermsProcessorConfig memory _terms - ) public override returns (uint256) { - if (_parentLicenseId == _UNSET_LICENSE_ID && msg.sender != address(this)) { - // Root licenses aka rights can only be minted by IPAssetRegistry - // TODO: check how to allow the Franchise NFT to have root commercial license - revert Unauthorized(); - } - return _createLicense( - _tokenId, - _parentLicenseId, - _licenseHolder, - _uri, - _revoker, - _commercial, - _canSublicense, - _terms - ); - } - - function _createLicense( - uint256 tokenId, - uint256 parentLicenseId, - address licenseHolder, - string memory uri, - address revoker, - bool commercial, - bool canSublicense, - TermsProcessorConfig memory _terms - ) internal returns (uint256) { - RightsManagerStorage storage $ = _getRightsManagerStorage(); - if (!_exists(tokenId)) { - revert NonExistentID(tokenId); - } - if ($.licenseForTokenId[ - keccak256(abi.encode(commercial, tokenId)) - ] != _UNSET_LICENSE_ID) { - revert AlreadyHasRootLicense(); - } - if (parentLicenseId != _UNSET_LICENSE_ID) { - License memory parentLicense = $.licenses[parentLicenseId]; - _verifySublicense(parentLicenseId, licenseHolder, commercial, parentLicense); - } - _verifyTerms(_terms); - uint256 licenseId = ++$.licenseCounter; - $.licenses[licenseId] = License({ - active: true, - canSublicense: canSublicense, - commercial: commercial, - parentLicenseId: parentLicenseId, - tokenId: tokenId, - revoker: revoker, - uri: uri, - termsProcessor: _terms.config, - termsData: _terms.data - }); - $.licenseForTokenId[ - keccak256(abi.encode(commercial, tokenId)) - ] = licenseId; - emit CreateLicense( - licenseId, - tokenId, - parentLicenseId, - licenseHolder, - uri, - revoker - ); - _updateLicenseHolder(licenseId, licenseHolder); - return licenseId; - } - - function revokeLicense(uint256 _licenseId) external override { - if (!_exists(_licenseId)) revert NonExistentID(_licenseId); - RightsManagerStorage storage $ = _getRightsManagerStorage(); - License storage license = $.licenses[_licenseId]; - if (msg.sender != license.revoker) revert SenderNotRevoker(); - license.active = false; - emit RevokeLicense(_licenseId); - } - - function _updateLicenseHolder( - uint256 licenseId, - address licenseHolder - ) internal virtual { - emit TransferLicense(licenseId, licenseHolder); - } + function transferSublicense( uint256 licenseId, @@ -284,7 +294,7 @@ abstract contract RightsManager is if (!isLicenseActive(licenseId)) revert InactiveLicense(); if (_getRightsManagerStorage().licenses[licenseId].parentLicenseId == 0) revert CannotSublicense(); - _updateLicenseHolder(licenseId, licenseHolder); + emit TransferLicense(licenseId, licenseHolder); } function supportsInterface( diff --git a/contracts/modules/licensing/terms/ITermsProcessor.sol b/contracts/modules/licensing/terms/ITermsProcessor.sol index 08886e2b..44ca2599 100644 --- a/contracts/modules/licensing/terms/ITermsProcessor.sol +++ b/contracts/modules/licensing/terms/ITermsProcessor.sol @@ -13,11 +13,11 @@ interface ITermsProcessor is IERC165 { /** * @notice Executes the terms set on a license on creation. * This should be called after decodeTerms() - * @return Whether the terms were executed successfully. + * @return newData the new data to be set on the license */ - function executeTerms(bytes calldata data) external returns(bool); + function executeTerms(bytes calldata data) external returns(bytes memory newData); /// returns true if the terms have been executed successfully or they don't need to be executed, false otherwise - function tersmExecutedSuccessfully() external view returns(bool); + function tersmExecutedSuccessfully(bytes calldata data) external view returns(bool); } \ No newline at end of file diff --git a/contracts/modules/licensing/terms/MultiTermsProcessor.sol b/contracts/modules/licensing/terms/MultiTermsProcessor.sol index a3dfc8c1..28535f9a 100644 --- a/contracts/modules/licensing/terms/MultiTermsProcessor.sol +++ b/contracts/modules/licensing/terms/MultiTermsProcessor.sol @@ -25,18 +25,18 @@ contract MultiTermsProcessor is ITermsProcessor { emit ProcessorsSet(_processors); } - function executeTerms(bytes calldata data) external override returns (bool) { + function executeTerms(bytes calldata data) external override returns (bytes memory newData) { uint256 length = processors.length; bytes[] memory encodedTerms = new bytes[](length); encodedTerms = abi.decode(data, (bytes[])); - bool result = true; + bytes[] memory newEncodedTerms = new bytes[](length); for (uint256 i = 0; i < length;) { - result = result && processors[i].executeTerms(encodedTerms[i]); + newEncodedTerms[i] = processors[i].executeTerms(encodedTerms[i]); unchecked { i++; } } - return result; + return abi.encode(newEncodedTerms); } function supportsInterface( @@ -45,19 +45,27 @@ contract MultiTermsProcessor is ITermsProcessor { bool supported = true; if (interfaceId == type(ITermsProcessor).interfaceId) { uint256 length = processors.length; - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < length;) { supported && processors[i].supportsInterface(interfaceId); + unchecked { + i++; + } } return supported; } return false; } - function tersmExecutedSuccessfully() external view override returns (bool) { + function tersmExecutedSuccessfully(bytes calldata data) external view override returns (bool) { uint256 length = processors.length; + bytes[] memory encodedTerms = new bytes[](length); + encodedTerms = abi.decode(data, (bytes[])); bool result = true; - for (uint256 i = 0; i < length; i++) { - result = result && processors[i].tersmExecutedSuccessfully(); + for (uint256 i = 0; i < length;) { + result = result && processors[i].tersmExecutedSuccessfully(encodedTerms[i]); + unchecked { + i++; + } } return result; } diff --git a/contracts/modules/licensing/terms/TimeTermsProcessor.sol b/contracts/modules/licensing/terms/TimeTermsProcessor.sol new file mode 100644 index 00000000..a98645af --- /dev/null +++ b/contracts/modules/licensing/terms/TimeTermsProcessor.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; + +import { ITermsProcessor } from "./ITermsProcessor.sol"; +import { LibDuration } from "../../timing/LibDuration.sol"; + +contract TimeTermsProcessor is ITermsProcessor { + using LibDuration for LibDuration.TimeConfig; + + function executeTerms(bytes calldata data) external view override returns (bytes memory newData) { + // TODO: check caller is the rights manager + LibDuration.TimeConfig memory config = abi.decode(data, (LibDuration.TimeConfig)); + if (config.startTime == 0) { + config.startTime = uint64(block.timestamp); + } + return abi.encode(config); + } + + function tersmExecutedSuccessfully(bytes calldata data) external view override returns (bool) { + LibDuration.TimeConfig memory config = abi.decode(data, (LibDuration.TimeConfig)); + return config.isActive(); + } + + function supportsInterface( + bytes4 interfaceId + ) external pure override returns (bool) { + return interfaceId == type(ITermsProcessor).interfaceId; + } +} \ No newline at end of file diff --git a/contracts/modules/licensing/terms/TimingTermsProcessor.sol b/contracts/modules/licensing/terms/TimingTermsProcessor.sol deleted file mode 100644 index e69de29b..00000000 diff --git a/contracts/modules/timing/LibDuration.sol b/contracts/modules/timing/LibDuration.sol new file mode 100644 index 00000000..92a5b561 --- /dev/null +++ b/contracts/modules/timing/LibDuration.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.13; + +library LibDuration { + + error NotRenewable(); + error CallerNotRenewer(); + error ZeroTTL(); + error InvalidStartTime(); + + struct TimeConfig { + uint64 ttl; + uint64 startTime; + address renewer; + } + + function isActive(TimeConfig memory self) internal view returns (bool) { + return self.startTime >= block.timestamp && self.startTime + self.ttl < block.timestamp; + } + + function isRenewable(TimeConfig memory self) internal pure returns (bool) { + return self.renewer != address(0); + } + + function renew(TimeConfig memory self, uint64 ttl) view internal { + if (!isRenewable(self)) revert NotRenewable(); + if (msg.sender != self.renewer) revert CallerNotRenewer(); + if (ttl == 0) revert ZeroTTL(); + self.ttl = ttl; + self.startTime = uint64(block.timestamp); + } + + function createRunningTimeConfig(uint64 ttl, address renewer) internal view returns (TimeConfig memory) { + if (ttl == 0) revert ZeroTTL(); + return TimeConfig({ + ttl: ttl, + startTime: uint64(block.timestamp), + renewer: renewer + }); + } + + function createStoppedTimeConfig(uint64 ttl, address renewer) internal pure returns (TimeConfig memory) { + if (ttl == 0) revert ZeroTTL(); + return TimeConfig({ + ttl: ttl, + startTime: 0, + renewer: renewer + }); + } +} \ No newline at end of file diff --git a/contracts/modules/timing/LibTimeConditional.sol b/contracts/modules/timing/LibTimeConditional.sol deleted file mode 100644 index 77722777..00000000 --- a/contracts/modules/timing/LibTimeConditional.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.13; - -library LibTimeConditional { - - struct TimeConfig { - bool renewable; - address renewer; - uint256 endTime; - } - - function isTimeExpired(TimeConfig memory self) internal view returns (bool) { - return self.endTime < block.timestamp; - } - -} \ No newline at end of file From ed5511b3b49418021a042091d1b7a356c2446113 Mon Sep 17 00:00:00 2001 From: Raul Date: Fri, 25 Aug 2023 16:29:51 +0200 Subject: [PATCH 17/42] comment fix --- contracts/modules/licensing/IERC5218.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/modules/licensing/IERC5218.sol b/contracts/modules/licensing/IERC5218.sol index 87e3e64b..4ecc29f4 100644 --- a/contracts/modules/licensing/IERC5218.sol +++ b/contracts/modules/licensing/IERC5218.sol @@ -120,7 +120,7 @@ interface IERC5218 is IERC721Upgradeable { uint256 _parentLicenseId, address _licenseHolder, string memory _uri, - address _revoker, // NOTE: MODIFIED ERC-5218 + address _revoker, bool _commercial, // NOTE: MODIFIED ERC-5218 bool _canSublicense, // NOTE: MODIFIED ERC-5218 TermsProcessorConfig memory _terms // NOTE: MODIFIED ERC-5218 From ccb713c54b4db904e1bcbb0c15de125304fdd135 Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 28 Aug 2023 18:04:47 +0200 Subject: [PATCH 18/42] passing tests, fixing access --- contracts/ip-assets/IPAssetRegistry.sol | 24 ++++--- contracts/modules/licensing/IERC5218.sol | 3 +- .../modules/licensing/ILicensingModule.sol | 39 +++++++++++ .../modules/licensing/LicensingModule.sol | 23 +------ contracts/modules/licensing/RightsManager.sol | 16 ++--- test/foundry/IPAssetsRegistry.t.sol | 8 +-- test/foundry/mocks/MockLicensingModule.sol | 43 ++++++++++++ test/foundry/utils/BaseTest.sol | 67 +++++++++++++++++-- 8 files changed, 171 insertions(+), 52 deletions(-) create mode 100644 contracts/modules/licensing/ILicensingModule.sol create mode 100644 test/foundry/mocks/MockLicensingModule.sol diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index f7d5eaad..c8ec1568 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.13; -//import "forge-std/console.sol"; + import { IIPAssetRegistry } from "./IIPAssetRegistry.sol"; import { LibIPAssetId } from "./LibIPAssetId.sol"; import { Unauthorized, ZeroAmount, ZeroAddress } from "../errors/General.sol"; @@ -12,7 +12,7 @@ import { ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC7 import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; import { RightsManager } from "../modules/licensing/RightsManager.sol"; -import { LicensingModule } from "../modules/licensing/LicensingModule.sol"; +import { ILicensingModule } from "../modules/licensing/ILicensingModule.sol"; contract IPAssetRegistry is IPAssetDataManager, @@ -20,6 +20,7 @@ contract IPAssetRegistry is MulticallUpgradeable { error IdOverBounds(); + error LicensingNotConfigured(); /// @custom:storage-location erc7201:story-protocol.ip-assets-registry.storage struct IPAssetRegistryStorage { @@ -30,7 +31,7 @@ contract IPAssetRegistry is } IIPAssetEventEmitter public immutable EVENT_EMITTER; - LicensingModule public immutable LICENSING_MODULE; + ILicensingModule public immutable LICENSING_MODULE; // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.ip-assets-registry.storage")) - 1))) bytes32 private constant _STORAGE_LOCATION = 0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378; @@ -41,7 +42,7 @@ contract IPAssetRegistry is if (_eventEmitter == address(0)) revert ZeroAddress(); EVENT_EMITTER = IIPAssetEventEmitter(_eventEmitter); if (_licensingModule == address(0)) revert ZeroAddress(); - LICENSING_MODULE = LicensingModule(_licensingModule); + LICENSING_MODULE = ILicensingModule(_licensingModule); _disableInitializers(); } @@ -89,22 +90,25 @@ contract IPAssetRegistry is _writeIPAsset(ipAssetId, name, _description, mediaUrl); IPAssetRegistryStorage storage $ = _getIPAssetRegistryStorage(); EVENT_EMITTER.emitIPAssetCreation($.franchiseId, ipAssetId); + // Non commercial - LicensingModule.FranchiseConfig memory config = LICENSING_MODULE.getFranchiseConfig($.franchiseId); - _setNonCommercialRights(ipAssetId, parentIpAssetId, to, config.revoker, config.nonCommercialConfig, config.nonCommercialTerms); + ILicensingModule.FranchiseConfig memory config = LICENSING_MODULE.getFranchiseConfig($.franchiseId); + if (config.revoker == address(0)) revert LicensingNotConfigured(); + _setNonCommercialRights(ipAssetId, parentIpAssetId, to, config.revoker, config.nonCommercialConfig, config.nonCommercialTerms); // If non derivative IpAsset, then franchise config may dictate commercial rights // Derivative works do not have commercial rights unless a deal with the relevant licensor is made if (config.rootIpAssetHasCommercialRights && parentIpAssetId == 0) { // Commercial _setCommercialRights(ipAssetId, 0, to, config.revoker, config.commercialLicenseUri, config.commercialConfig, config.commercialTerms); } + return ipAssetId; } - function _setNonCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, LicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) private { + function _setNonCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) private { uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, false); - createLicense( + _createLicense( ipAssetId, parentLicenseId, holder, @@ -116,9 +120,9 @@ contract IPAssetRegistry is ); } - function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, LicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) private { + function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) private { uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, true); - createLicense( + _createLicense( ipAssetId, parentLicenseId, holder, diff --git a/contracts/modules/licensing/IERC5218.sol b/contracts/modules/licensing/IERC5218.sol index 4ecc29f4..b96c74ae 100644 --- a/contracts/modules/licensing/IERC5218.sol +++ b/contracts/modules/licensing/IERC5218.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import { IERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; @@ -31,7 +32,7 @@ interface IERC5218 is IERC721Upgradeable { event TransferLicense(uint256 _licenseId, address _licenseHolder); struct TermsProcessorConfig { - ITermsProcessor config; + ITermsProcessor processor; bytes data; } diff --git a/contracts/modules/licensing/ILicensingModule.sol b/contracts/modules/licensing/ILicensingModule.sol new file mode 100644 index 00000000..6ca0cfaa --- /dev/null +++ b/contracts/modules/licensing/ILicensingModule.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.13; +import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol"; +import { FranchiseRegistry } from "contracts/FranchiseRegistry.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import { AccessControlledUpgradeable } from "contracts/access-control/AccessControlledUpgradeable.sol"; +import { UPGRADER_ROLE, LICENSING_MANAGER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; +import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; +import { IERC5218 } from "./IERC5218.sol"; + +interface ILicensingModule { + + struct IpAssetConfig { + bool canSublicense; + uint256 franchiseRootLicenseId; + // TODO: allowed license terms? processors? + // TODO: limit medium of sublicenses? something like LibIPAssetMask? + // TODO: limit who you can sublicense to? + } + + struct FranchiseConfig { + IpAssetConfig nonCommercialConfig; + IERC5218.TermsProcessorConfig nonCommercialTerms; + IpAssetConfig commercialConfig; + IERC5218.TermsProcessorConfig commercialTerms; + bool rootIpAssetHasCommercialRights; + address revoker; + string commercialLicenseUri; + + } + + event FranchiseConfigSet(uint256 franchiseId, FranchiseConfig config); + + + function configureFranchiseLicensing(uint256 franchiseId, FranchiseConfig memory config) external; + function getFranchiseConfig(uint256 franchiseId) external view returns (FranchiseConfig memory); + function getNonCommercialLicenseURI() external view returns (string memory); + +} \ No newline at end of file diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index ee711342..98baf281 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -7,27 +7,9 @@ import { AccessControlledUpgradeable } from "contracts/access-control/AccessCont import { UPGRADER_ROLE, LICENSING_MANAGER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; import { IERC5218 } from "./IERC5218.sol"; +import { ILicensingModule } from "./ILicensingModule.sol"; -contract LicensingModule is AccessControlledUpgradeable { - - struct IpAssetConfig { - bool canSublicense; - uint256 franchiseRootLicenseId; - // TODO: allowed license terms? processors? - // TODO: limit medium of sublicenses? something like LibIPAssetMask? - // TODO: limit who you can sublicense to? - } - - struct FranchiseConfig { - IpAssetConfig nonCommercialConfig; - IERC5218.TermsProcessorConfig nonCommercialTerms; - IpAssetConfig commercialConfig; - IERC5218.TermsProcessorConfig commercialTerms; - bool rootIpAssetHasCommercialRights; - address revoker; - string commercialLicenseUri; - - } +contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { struct LicensingModuleStorage { /// franchiseId => FranchiseConfig @@ -36,7 +18,6 @@ contract LicensingModule is AccessControlledUpgradeable { } event NonCommercialLicenseUriSet(string uri); - event FranchiseConfigSet(uint256 franchiseId, FranchiseConfig config); // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.licensing-module.storage")) - 1))) bytes32 private constant _STORAGE_LOCATION = 0x80b4ea8c21e869c68acfd93c8ef2c0d867835b92e2fded15a1d74d7e7ff3312d; diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 6249ff24..e0d243fa 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -10,6 +10,8 @@ import { NonExistentID, Unauthorized, ZeroAddress, UnsupportedInterface } from " import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; +import "forge-std/console.sol"; + abstract contract RightsManager is ERC721Upgradeable, @@ -81,12 +83,8 @@ abstract contract RightsManager is bool _commercial, bool _canSublicense, TermsProcessorConfig memory _terms - ) public override returns (uint256) { - if (_parentLicenseId == _UNSET_LICENSE_ID && msg.sender != address(this)) { - // Root licenses aka rights can only be minted by IPAssetRegistry - // TODO: check how to allow the Franchise NFT to have root commercial license - revert Unauthorized(); - } + ) external override returns (uint256) { + // TODO: check if msg.sender is allowed to create license return _createLicense( _tokenId, _parentLicenseId, @@ -132,7 +130,7 @@ abstract contract RightsManager is tokenId: tokenId, revoker: revoker, uri: uri, - termsProcessor: _terms.config, + termsProcessor: _terms.processor, termsData: _terms.data }); $.licenseForTokenId[ @@ -225,8 +223,8 @@ abstract contract RightsManager is } function _verifyTerms(TermsProcessorConfig memory _terms) private view { - if (address(_terms.config) != address(0) && - !address(_terms.config).supportsInterface(type(ITermsProcessor).interfaceId)) { + if (address(_terms.processor) != address(0) && + !address(_terms.processor).supportsInterface(type(ITermsProcessor).interfaceId)) { revert UnsupportedInterface("ITermsProcessor"); } } diff --git a/test/foundry/IPAssetsRegistry.t.sol b/test/foundry/IPAssetsRegistry.t.sol index c3bbc839..3c34ecc2 100644 --- a/test/foundry/IPAssetsRegistry.t.sol +++ b/test/foundry/IPAssetsRegistry.t.sol @@ -8,6 +8,7 @@ import { LibIPAssetId } from "../../contracts/ip-assets/LibIPAssetId.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { IERC1967 } from "@openzeppelin/contracts/interfaces/IERC1967.sol"; import { MockIPAssetEventEmitter } from "./mocks/MockIPAssetEventEmitter.sol"; +import { MockLicensingModule } from "./mocks/MockLicensingModule.sol"; import "forge-std/Test.sol"; contract IPAssetRegistryTest is Test { @@ -27,6 +28,7 @@ contract IPAssetRegistryTest is Test { address mintee = address(1); address mintee2 = address(2); address mockFranchiseRegistry = address(0x7474); + address mockLicenseModule; uint256 private constant _ID_RANGE = 10**12; uint256 private constant _FIRST_ID_STORY = 1; @@ -39,7 +41,8 @@ contract IPAssetRegistryTest is Test { function setUp() public { factory = new IPAssetRegistryFactory(); address mockEventEmitter = address(new MockIPAssetEventEmitter()); - factory.upgradeFranchises(address(new IPAssetRegistry(mockEventEmitter, mockFranchiseRegistry))); + mockLicenseModule = address(new MockLicensingModule()); + factory.upgradeFranchises(address(new IPAssetRegistry(mockEventEmitter, mockLicenseModule))); ipAssetRegistry = IPAssetRegistry(factory.createFranchiseIPAssets(1, "name", "symbol", "description")); } @@ -119,6 +122,3 @@ contract IPAssetRegistryTest is Test { } -contract DerivativeIPAssetRegistryTest is Test { - // TODO -} \ No newline at end of file diff --git a/test/foundry/mocks/MockLicensingModule.sol b/test/foundry/mocks/MockLicensingModule.sol new file mode 100644 index 00000000..9b287608 --- /dev/null +++ b/test/foundry/mocks/MockLicensingModule.sol @@ -0,0 +1,43 @@ +import { ILicensingModule } from "contracts/modules/licensing/ILicensingModule.sol"; +import { IERC5218 } from "contracts/modules/licensing/IERC5218.sol"; +import { ITermsProcessor } from "contracts/modules/licensing/terms/ITermsProcessor.sol"; + +contract MockLicensingModule is ILicensingModule { + + function configureFranchiseLicensing(uint256 franchiseId, FranchiseConfig memory config) external override { + // No-op + } + + function getFranchiseConfig(uint256) external pure override returns (FranchiseConfig memory) { + return FranchiseConfig({ + nonCommercialConfig: IpAssetConfig({ + canSublicense: false, + franchiseRootLicenseId: 0 + }), + nonCommercialTerms: IERC5218.TermsProcessorConfig({ + processor: ITermsProcessor(address(0)), + data: "" + }), + commercialConfig: IpAssetConfig({ + canSublicense: false, + franchiseRootLicenseId: 0 + }), + commercialTerms: IERC5218.TermsProcessorConfig({ + processor: ITermsProcessor(address(0)), + data: "" + }), + rootIpAssetHasCommercialRights: false, + revoker: address(0x5656565), + commercialLicenseUri: "" + }); + } + + function getNonCommercialLicenseURI() + external + pure + override + returns (string memory) + { + return "mockmock"; + } +} \ No newline at end of file diff --git a/test/foundry/utils/BaseTest.sol b/test/foundry/utils/BaseTest.sol index 300b6f74..7b99de3a 100644 --- a/test/foundry/utils/BaseTest.sol +++ b/test/foundry/utils/BaseTest.sol @@ -15,6 +15,8 @@ import "contracts/errors/General.sol"; import "contracts/modules/relationships/processors/PermissionlessRelationshipProcessor.sol"; import "contracts/modules/relationships/RelationshipModuleBase.sol"; import "contracts/modules/relationships/ProtocolRelationshipModule.sol"; +import "contracts/modules/licensing/LicensingModule.sol"; +import "contracts/modules/licensing/terms/ITermsProcessor.sol"; contract BaseTest is Test, ProxyHelper { @@ -24,15 +26,21 @@ contract BaseTest is Test, ProxyHelper { RelationshipModuleBase public relationshipModule; AccessControlSingleton accessControl; PermissionlessRelationshipProcessor public relationshipProcessor; - - address admin = address(123); - address franchiseOwner = address(456); + LicensingModule public licensingModule; bool public deployProcessors = false; + address constant admin = address(123); + address constant franchiseOwner = address(456); + address constant revoker = address(789); + string constant NON_COMMERCIAL_LICENSE_URI = "https://noncommercial.license"; + string constant COMMERCIAL_LICENSE_URI = "https://commercial.license"; + constructor() {} function setUp() virtual public { factory = new IPAssetRegistryFactory(); + + // Create Access Control accessControl = AccessControlSingleton( _deployUUPSProxy( address(new AccessControlSingleton()), @@ -42,6 +50,7 @@ contract BaseTest is Test, ProxyHelper { ) ); + // Create Franchise Registry FranchiseRegistry impl = new FranchiseRegistry(address(factory)); franchiseRegistry = FranchiseRegistry( _deployUUPSProxy( @@ -51,14 +60,59 @@ contract BaseTest is Test, ProxyHelper { ) ) ); - address eventEmitter = address(new CommonIPAssetEventEmitter(address(franchiseRegistry))); - factory.upgradeFranchises(address(new IPAssetRegistry(eventEmitter, address(franchiseRegistry)))); + // Create Common Event Emitter + address eventEmitter = address(new CommonIPAssetEventEmitter(address(franchiseRegistry))); + console.log("Event Emitter", eventEmitter); + // Create Licensing Module + address licensingImplementation = address(new LicensingModule(address(franchiseRegistry))); + licensingModule = LicensingModule( + _deployUUPSProxy( + licensingImplementation, + abi.encodeWithSelector( + bytes4(keccak256(bytes("initialize(address,string)"))), + address(accessControl), NON_COMMERCIAL_LICENSE_URI + ) + ) + ); + + // upgrade factory to use new event emitter + factory.upgradeFranchises(address(new IPAssetRegistry(eventEmitter, address(licensingModule)))); vm.startPrank(franchiseOwner); + + // Register Franchise (will create IPAssetRegistry and associated LicenseRegistry) FranchiseRegistry.FranchiseCreationParams memory params = FranchiseRegistry.FranchiseCreationParams("name", "symbol", "description", "tokenURI"); - (uint256 id, address ipAssets) = franchiseRegistry.registerFranchise(params); + (uint256 franchiseId, address ipAssets) = franchiseRegistry.registerFranchise(params); ipAssetRegistry = IPAssetRegistry(ipAssets); + + // Configure Licensing for Franchise + LicensingModule.FranchiseConfig memory licenseConfig = ILicensingModule.FranchiseConfig({ + nonCommercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: true, + franchiseRootLicenseId: 0 + }), + nonCommercialTerms: IERC5218.TermsProcessorConfig({ + processor: ITermsProcessor(address(0)), + data: "" + }), + commercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: true, + franchiseRootLicenseId: 0 + }), + commercialTerms: IERC5218.TermsProcessorConfig({ + processor: ITermsProcessor(address(0)), + data: "" + }), + rootIpAssetHasCommercialRights: false, + revoker: revoker, + commercialLicenseUri: COMMERCIAL_LICENSE_URI + }); + licensingModule.configureFranchiseLicensing(franchiseId, licenseConfig); + + vm.stopPrank(); + + // Create Relationship Module relationshipModule = RelationshipModuleBase( _deployUUPSProxy( address(new RelationshipModuleHarness(address(franchiseRegistry))), @@ -67,7 +121,6 @@ contract BaseTest is Test, ProxyHelper { ) ) ); - if (deployProcessors) { relationshipProcessor = new PermissionlessRelationshipProcessor(address(relationshipModule)); } From f053ed8fa7a0cff7ec11536fe9fec7d349a799fb Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 28 Aug 2023 19:04:03 +0200 Subject: [PATCH 19/42] old test passing --- test/foundry/mocks/MockLicensingModule.sol | 71 +++++++++++-------- .../RelationshipModule.Relating.t.sol | 5 +- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/test/foundry/mocks/MockLicensingModule.sol b/test/foundry/mocks/MockLicensingModule.sol index 9b287608..777f088a 100644 --- a/test/foundry/mocks/MockLicensingModule.sol +++ b/test/foundry/mocks/MockLicensingModule.sol @@ -1,35 +1,50 @@ -import { ILicensingModule } from "contracts/modules/licensing/ILicensingModule.sol"; -import { IERC5218 } from "contracts/modules/licensing/IERC5218.sol"; -import { ITermsProcessor } from "contracts/modules/licensing/terms/ITermsProcessor.sol"; +import {ILicensingModule} from "contracts/modules/licensing/ILicensingModule.sol"; +import {IERC5218} from "contracts/modules/licensing/IERC5218.sol"; +import {ITermsProcessor} from "contracts/modules/licensing/terms/ITermsProcessor.sol"; -contract MockLicensingModule is ILicensingModule { +library LibMockFranchiseConfig { + function getMockFranchiseConfig() + internal + pure + returns (ILicensingModule.FranchiseConfig memory) + { + return + ILicensingModule.FranchiseConfig({ + nonCommercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: false, + franchiseRootLicenseId: 0 + }), + nonCommercialTerms: IERC5218.TermsProcessorConfig({ + processor: ITermsProcessor(address(0)), + data: "" + }), + commercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: false, + franchiseRootLicenseId: 0 + }), + commercialTerms: IERC5218.TermsProcessorConfig({ + processor: ITermsProcessor(address(0)), + data: "" + }), + rootIpAssetHasCommercialRights: false, + revoker: address(0x5656565), + commercialLicenseUri: "" + }); + } +} - function configureFranchiseLicensing(uint256 franchiseId, FranchiseConfig memory config) external override { +contract MockLicensingModule is ILicensingModule { + function configureFranchiseLicensing( + uint256 franchiseId, + FranchiseConfig memory config + ) external override { // No-op } - function getFranchiseConfig(uint256) external pure override returns (FranchiseConfig memory) { - return FranchiseConfig({ - nonCommercialConfig: IpAssetConfig({ - canSublicense: false, - franchiseRootLicenseId: 0 - }), - nonCommercialTerms: IERC5218.TermsProcessorConfig({ - processor: ITermsProcessor(address(0)), - data: "" - }), - commercialConfig: IpAssetConfig({ - canSublicense: false, - franchiseRootLicenseId: 0 - }), - commercialTerms: IERC5218.TermsProcessorConfig({ - processor: ITermsProcessor(address(0)), - data: "" - }), - rootIpAssetHasCommercialRights: false, - revoker: address(0x5656565), - commercialLicenseUri: "" - }); + function getFranchiseConfig( + uint256 + ) external pure override returns (FranchiseConfig memory) { + return LibMockFranchiseConfig.getMockFranchiseConfig(); } function getNonCommercialLicenseURI() @@ -40,4 +55,4 @@ contract MockLicensingModule is ILicensingModule { { return "mockmock"; } -} \ No newline at end of file +} diff --git a/test/foundry/relationships/RelationshipModule.Relating.t.sol b/test/foundry/relationships/RelationshipModule.Relating.t.sol index 95da6263..4ba00552 100644 --- a/test/foundry/relationships/RelationshipModule.Relating.t.sol +++ b/test/foundry/relationships/RelationshipModule.Relating.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; import '../utils/ProxyHelper.sol'; import '../utils/BaseTest.sol'; +import "../mocks/MockLicensingModule.sol"; import "contracts/IPAsset.sol"; import "contracts/errors/General.sol"; import "contracts/modules/relationships/processors/PermissionlessRelationshipProcessor.sol"; @@ -134,9 +135,11 @@ contract RelationshipModuleRelationshipTest is BaseTest { } function test_revert_relationshipsNotSameFranchise() public { - vm.prank(franchiseOwner); + vm.startPrank(franchiseOwner); FranchiseRegistry.FranchiseCreationParams memory params = FranchiseRegistry.FranchiseCreationParams("name2", "symbol2", "description2", "tokenURI2"); (uint256 id, address otherIPAssets) = franchiseRegistry.registerFranchise(params); + licensingModule.configureFranchiseLicensing(id, LibMockFranchiseConfig.getMockFranchiseConfig()); + vm.stopPrank(); IPAssetRegistry otherIPAssetRegistry = IPAssetRegistry(otherIPAssets); vm.prank(address(franchiseRegistry)); uint256 otherId = otherIPAssetRegistry.createIPAsset(IPAsset.CHARACTER, "name", "description", "mediaUrl", ipAssetOwner, 0); From 7dc3e1027cd062ce18c405d5c935c00f2e003679 Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 28 Aug 2023 21:18:45 +0200 Subject: [PATCH 20/42] WIP Licensing module --- contracts/ip-assets/IPAssetRegistry.sol | 3 +- .../modules/licensing/LicensingModule.sol | 21 +++++ contracts/modules/licensing/RightsManager.sol | 52 +++++++++--- script/foundry/deployment/Main.s.sol | 3 +- script/foundry/upgrades/DevUpgrades.s.sol | 3 +- test/foundry/IPAssetsRegistry.t.sol | 2 +- test/foundry/IPAssetsRegistryFactory.t.sol | 9 +- test/foundry/LicenseModule.t.sol | 45 ---------- test/foundry/licensing/LicensingModule.t.sol | 84 +++++++++++++++++++ test/foundry/mocks/MockLicensingModule.sol | 2 + test/foundry/mocks/MockTermsProcessor.sol | 31 +++++++ test/foundry/utils/BaseTest.sol | 2 +- 12 files changed, 191 insertions(+), 66 deletions(-) delete mode 100644 test/foundry/LicenseModule.t.sol create mode 100644 test/foundry/licensing/LicensingModule.t.sol create mode 100644 test/foundry/mocks/MockTermsProcessor.sol diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index c8ec1568..9f905af0 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -7,7 +7,6 @@ import { Unauthorized, ZeroAmount, ZeroAddress } from "../errors/General.sol"; import { IPAsset } from "contracts/IPAsset.sol"; import { IIPAssetEventEmitter } from "./events/IIPAssetEventEmitter.sol"; import { IPAssetDataManager } from "./storage/IPAssetDataManager.sol"; -import { FranchiseRegistry } from "../FranchiseRegistry.sol"; import { ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; @@ -37,7 +36,7 @@ contract IPAssetRegistry is 0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378; string private constant _VERSION = "0.1.0"; - constructor(address _eventEmitter, address _licensingModule) { + constructor(address _eventEmitter, address _licensingModule, address _franchiseRegistry) RightsManager(_franchiseRegistry) { // TODO: should Franchise owner be able to change this? if (_eventEmitter == address(0)) revert ZeroAddress(); EVENT_EMITTER = IIPAssetEventEmitter(_eventEmitter); diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index 98baf281..4d761e9b 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -19,6 +19,10 @@ contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { event NonCommercialLicenseUriSet(string uri); + error NonExistentFranchise(); + error RootLicenseNotActive(uint256 rootLicenseId); + error ZeroRevokerAddress(); + // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.licensing-module.storage")) - 1))) bytes32 private constant _STORAGE_LOCATION = 0x80b4ea8c21e869c68acfd93c8ef2c0d867835b92e2fded15a1d74d7e7ff3312d; @@ -57,11 +61,28 @@ contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { if (msg.sender != FRANCHISE_REGISTRY.ownerOf(franchiseId)) { revert Unauthorized(); } + _verifyRootLicense(franchiseId, config.nonCommercialConfig.franchiseRootLicenseId); + _verifyRootLicense(franchiseId, config.commercialConfig.franchiseRootLicenseId); + if (config.revoker == address(0)) { + revert ZeroRevokerAddress(); + } LicensingModuleStorage storage $ = _getLicensingModuleStorage(); $.franchiseConfigs[franchiseId] = config; emit FranchiseConfigSet(franchiseId, config); } + function _verifyRootLicense(uint256 franchiseId, uint256 rootLicenseId) internal view { + if (rootLicenseId != 0) { + IERC5218 rightsManager = IERC5218(FRANCHISE_REGISTRY.ipAssetRegistryForId(franchiseId)); + if (address(rightsManager) == address(0)) { + revert NonExistentFranchise(); + } + if (!rightsManager.isLicenseActive(rootLicenseId)) { + revert RootLicenseNotActive(rootLicenseId); + } + } + } + function getFranchiseConfig(uint256 franchiseId) public view returns (FranchiseConfig memory) { return _getLicensingModuleStorage().franchiseConfigs[franchiseId]; } diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index e0d243fa..b636d743 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -27,6 +27,7 @@ abstract contract RightsManager is error SenderNotRevoker(); error NotSublicense(); error AlreadyHasRootLicense(); + error ZeroRevokerAddress(); struct License { bool active; @@ -50,10 +51,16 @@ abstract contract RightsManager is // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.rights-manager.storage")) - 1))) bytes32 private constant _STORAGE_LOCATION = 0x315576c20e31e03ef3e70482445a4c33e45baf13beff28e79f2adf6d06cc0bee; uint256 private constant _UNSET_LICENSE_ID = 0; + uint256 public constant FRANCHISE_REGISTRY_OWNED_TOKEN_ID = type(uint256).max; LicenseRegistry public immutable LICENSE_REGISTRY; + IERC721 public immutable FRANCHISE_REGISTRY; - constructor() { + constructor(address _franchiseRegistry) { + if (_franchiseRegistry == address(0)) { + revert ZeroAddress(); + } + FRANCHISE_REGISTRY = IERC721(_franchiseRegistry); LICENSE_REGISTRY = new LicenseRegistry(address(this), "Licenses", "SPLC"); } @@ -97,6 +104,28 @@ abstract contract RightsManager is ); } + function createFranchiseRootLicense( + uint256 franchiseId, + address _licenseHolder, + string memory _uri, + address _revoker, + bool _commercial, + bool _canSublicense, + TermsProcessorConfig memory _terms + ) external returns (uint256) { + if (msg.sender != FRANCHISE_REGISTRY.ownerOf(franchiseId)) revert Unauthorized(); + return _createLicense( + FRANCHISE_REGISTRY_OWNED_TOKEN_ID, + _UNSET_LICENSE_ID, + _licenseHolder, + _uri, + _revoker, + _commercial, + _canSublicense, + _terms + ); + } + function _createLicense( uint256 tokenId, uint256 parentLicenseId, @@ -107,16 +136,19 @@ abstract contract RightsManager is bool canSublicense, TermsProcessorConfig memory _terms ) internal returns (uint256) { + // TODO: should revoker come from allowed revoker list? + if (revoker == address(0)) revert ZeroRevokerAddress(); RightsManagerStorage storage $ = _getRightsManagerStorage(); - if (!_exists(tokenId)) { - revert NonExistentID(tokenId); - } - if ($.licenseForTokenId[ - keccak256(abi.encode(commercial, tokenId)) - ] != _UNSET_LICENSE_ID) { - revert AlreadyHasRootLicense(); + if (tokenId != FRANCHISE_REGISTRY_OWNED_TOKEN_ID) { + if (!_exists(tokenId)) { + revert NonExistentID(tokenId); + } } - if (parentLicenseId != _UNSET_LICENSE_ID) { + if (parentLicenseId == _UNSET_LICENSE_ID) { + if ($.licenseForTokenId[keccak256(abi.encode(commercial, tokenId))] != _UNSET_LICENSE_ID) { + revert AlreadyHasRootLicense(); + } + } else { License memory parentLicense = $.licenses[parentLicenseId]; _verifySublicense(parentLicenseId, licenseHolder, commercial, parentLicense); } @@ -282,8 +314,6 @@ abstract contract RightsManager is return _getRightsManagerStorage().licenses[licenseId].parentLicenseId == _UNSET_LICENSE_ID; } - - function transferSublicense( uint256 licenseId, address licenseHolder diff --git a/script/foundry/deployment/Main.s.sol b/script/foundry/deployment/Main.s.sol index 95c0ecb7..b657fe75 100644 --- a/script/foundry/deployment/Main.s.sol +++ b/script/foundry/deployment/Main.s.sol @@ -29,6 +29,7 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler, ProxyHelper { /// @dev To use, run the following command (e.g. for Goerli): /// forge script script/Deploy.s.sol:Deploy --rpc-url $GOERLI_RPC_URL --broadcast --verify -vvvv + /** function run() public { _beginBroadcast(); string memory contractKey; @@ -143,5 +144,5 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler, ProxyHelper { _writeDeployment(); _endBroadcast(); } - + */ } diff --git a/script/foundry/upgrades/DevUpgrades.s.sol b/script/foundry/upgrades/DevUpgrades.s.sol index 37802454..52258049 100644 --- a/script/foundry/upgrades/DevUpgrades.s.sol +++ b/script/foundry/upgrades/DevUpgrades.s.sol @@ -70,7 +70,7 @@ contract UpgradeFranchiseRegistry is Script, BroadcastManager, JsonDeploymentHan } } - +/** contract UpgradeIPAssetRegistry is Script, BroadcastManager, JsonDeploymentHandler { using StringUtil for uint256; @@ -106,3 +106,4 @@ contract UpgradeIPAssetRegistry is Script, BroadcastManager, JsonDeploymentHandl } } +*/ \ No newline at end of file diff --git a/test/foundry/IPAssetsRegistry.t.sol b/test/foundry/IPAssetsRegistry.t.sol index 3c34ecc2..3fedbf89 100644 --- a/test/foundry/IPAssetsRegistry.t.sol +++ b/test/foundry/IPAssetsRegistry.t.sol @@ -42,7 +42,7 @@ contract IPAssetRegistryTest is Test { factory = new IPAssetRegistryFactory(); address mockEventEmitter = address(new MockIPAssetEventEmitter()); mockLicenseModule = address(new MockLicensingModule()); - factory.upgradeFranchises(address(new IPAssetRegistry(mockEventEmitter, mockLicenseModule))); + factory.upgradeFranchises(address(new IPAssetRegistry(mockEventEmitter, mockLicenseModule, mockFranchiseRegistry))); ipAssetRegistry = IPAssetRegistry(factory.createFranchiseIPAssets(1, "name", "symbol", "description")); } diff --git a/test/foundry/IPAssetsRegistryFactory.t.sol b/test/foundry/IPAssetsRegistryFactory.t.sol index 4f3dd03d..6aced690 100644 --- a/test/foundry/IPAssetsRegistryFactory.t.sol +++ b/test/foundry/IPAssetsRegistryFactory.t.sol @@ -9,7 +9,7 @@ import "./mocks/MockIPAssetEventEmitter.sol"; contract IPAssetRegistryv2 is IPAssetRegistry { - constructor(address _eventEmitter, address _franchiseRegistry) IPAssetRegistry(_eventEmitter, _franchiseRegistry) { + constructor(address _eventEmitter, address licensingModule, address _franchiseRegistry) IPAssetRegistry(_eventEmitter, licensingModule, _franchiseRegistry) { } function version() virtual override external pure returns (string memory) { @@ -27,13 +27,14 @@ contract IPAssetRegistryFactoryTest is Test { IPAssetRegistryFactory public factory; address private _mockEventEmitter = address(0x123123); address mockFranchiseRegistry = address(0x7474); + address mockLicenseModule = address(0x7222274); function setUp() public { factory = new IPAssetRegistryFactory(); address eventEmitter = address(new MockIPAssetEventEmitter()); - address ipAssetRegistry = address(new IPAssetRegistry(eventEmitter, mockFranchiseRegistry)); + address ipAssetRegistry = address(new IPAssetRegistry(eventEmitter, mockLicenseModule, mockFranchiseRegistry)); factory.upgradeFranchises(ipAssetRegistry); } @@ -58,7 +59,7 @@ contract IPAssetRegistryFactoryTest is Test { } function test_UpgradeCollections() public { - IPAssetRegistryv2 newImplementation = new IPAssetRegistryv2(_mockEventEmitter, mockFranchiseRegistry); + IPAssetRegistryv2 newImplementation = new IPAssetRegistryv2(_mockEventEmitter, mockLicenseModule, mockFranchiseRegistry); //vm.expectEmit(true, true, true, true); //emit CollectionsUpgraded(address(newImplementation), "2.0.0"); factory.upgradeFranchises(address(newImplementation)); @@ -67,7 +68,7 @@ contract IPAssetRegistryFactoryTest is Test { } function test_revertIfNotOwnerUpgrades() public { - IPAssetRegistryv2 newImplementation = new IPAssetRegistryv2(_mockEventEmitter, mockFranchiseRegistry); + IPAssetRegistryv2 newImplementation = new IPAssetRegistryv2(_mockEventEmitter, mockLicenseModule, mockFranchiseRegistry); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); factory.upgradeFranchises(address(newImplementation)); diff --git a/test/foundry/LicenseModule.t.sol b/test/foundry/LicenseModule.t.sol deleted file mode 100644 index b4640d0c..00000000 --- a/test/foundry/LicenseModule.t.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: BUSDL-1.1 -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; -import './utils/BaseTest.sol'; -/* -import { LicensingModule } from "contracts/modules/licensing/LicensingModule.sol"; - -contract LicenseModuleTest is BaseTest { - - LicensingModule licensingModule; - string noncommercialLicenseURL; - address ipAssetHolder = address(0x23232); - - function setUp() virtual override public { - deployProcessors = false; - super.setUp(); - address licenseModuleImpl = address(new LicensingModule(address(franchiseRegistry))); - - noncommercialLicenseURL = "https://arweave.net/yHIbKlFBg3xuKSzlM_dREG8Y08uod-gWKsWi9OaPFsM"; - - address proxy = _deployUUPSProxy( - licenseModuleImpl, - abi.encodeWithSelector( - bytes4(keccak256(bytes("initialize(string)"))), noncommercialLicenseURL - ) - ); - licensingModule = LicensingModule(proxy); - franchiseRegistry.setLicensingModule(licensingModule); - } - - function test_setUp() public { - assertEq(address(franchiseRegistry.getLicenseingModule()), address(licensingModule)); - assertEq(licensingModule.getNonCommercialLicenseURI(), noncommercialLicenseURL); - } - - function test_happyPath() public { - vm.startPrank(ipAssetHolder); - uint256 ipAssetId = franchiseRegistry.createIPAsset(1, IPAsset.STORY, "name", "description", "tokenURI"); - franchiseRegistry.createLicense(1, ipAssetId, true, keccak256("MOVIE_ADAPTATION"), "https://cool-license-bro.pdf"); - vm.stopPrank(); - } - -} -*/ \ No newline at end of file diff --git a/test/foundry/licensing/LicensingModule.t.sol b/test/foundry/licensing/LicensingModule.t.sol new file mode 100644 index 00000000..0885c7cf --- /dev/null +++ b/test/foundry/licensing/LicensingModule.t.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: BUSDL-1.1 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import '../utils/BaseTest.sol'; +import '../mocks/MockLicensingModule.sol'; +import '../mocks/MockTermsProcessor.sol'; +import "contracts/errors/General.sol"; + +contract LicensingModuleTest is BaseTest { + + ITermsProcessor public termsProcessor1; + ITermsProcessor public termsProcessor2; + + function setUp() virtual override public { + deployProcessors = false; + super.setUp(); + termsProcessor1 = new MockTermsProcessor(); + termsProcessor2 = new MockTermsProcessor(); + } + + function test_setUp() public { + assertEq(licensingModule.getNonCommercialLicenseURI(), NON_COMMERCIAL_LICENSE_URI); + } + + function test_revert_nonAuthorizedConfigSetter() public { + vm.expectRevert(Unauthorized.selector); + licensingModule.configureFranchiseLicensing(1, LibMockFranchiseConfig.getMockFranchiseConfig()); + } + + function test_revert_nonExistingFranchise() public { + vm.expectRevert("ERC721: invalid token ID"); + licensingModule.configureFranchiseLicensing(2, LibMockFranchiseConfig.getMockFranchiseConfig()); + } + + + function test_configFranchise() public { + vm.startPrank(franchiseOwner); + IERC5218.TermsProcessorConfig memory termsConfig = IERC5218.TermsProcessorConfig({ + processor: termsProcessor1, + data: abi.encode("root") + }); + uint256 rootLicenseId = ipAssetRegistry.createFranchiseRootLicense(1, franchiseOwner, "commercial_uri_root", revoker, true, true, termsConfig); + + ILicensingModule.FranchiseConfig memory config = ILicensingModule.FranchiseConfig({ + nonCommercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: true, + franchiseRootLicenseId: 0 + }), + nonCommercialTerms: IERC5218.TermsProcessorConfig({ + processor: termsProcessor1, + data: abi.encode("hi") + }), + commercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: false, + franchiseRootLicenseId: rootLicenseId + }), + commercialTerms: IERC5218.TermsProcessorConfig({ + processor: termsProcessor2, + data: abi.encode("bye") + }), + rootIpAssetHasCommercialRights: false, + revoker: address(0x5656565), + commercialLicenseUri: "uriuri" + }); + + licensingModule.configureFranchiseLicensing(1, config); + ILicensingModule.FranchiseConfig memory configResult = licensingModule.getFranchiseConfig(1); + assertEq(configResult.nonCommercialConfig.canSublicense, true); + assertEq(configResult.nonCommercialConfig.franchiseRootLicenseId, 0); + assertEq(address(configResult.nonCommercialTerms.processor), address(termsProcessor1)); + assertEq(configResult.nonCommercialTerms.data, abi.encode("hi")); + assertEq(configResult.commercialConfig.canSublicense, false); + assertEq(configResult.commercialConfig.franchiseRootLicenseId, 1); + assertEq(address(configResult.commercialTerms.processor), address(termsProcessor2)); + assertEq(configResult.commercialTerms.data, abi.encode("bye")); + assertEq(configResult.rootIpAssetHasCommercialRights, false); + assertEq(configResult.revoker, address(0x5656565)); + vm.stopPrank(); + } + + + +} diff --git a/test/foundry/mocks/MockLicensingModule.sol b/test/foundry/mocks/MockLicensingModule.sol index 777f088a..2ac8d9a6 100644 --- a/test/foundry/mocks/MockLicensingModule.sol +++ b/test/foundry/mocks/MockLicensingModule.sol @@ -1,3 +1,5 @@ +pragma solidity ^0.8.19; + import {ILicensingModule} from "contracts/modules/licensing/ILicensingModule.sol"; import {IERC5218} from "contracts/modules/licensing/IERC5218.sol"; import {ITermsProcessor} from "contracts/modules/licensing/terms/ITermsProcessor.sol"; diff --git a/test/foundry/mocks/MockTermsProcessor.sol b/test/foundry/mocks/MockTermsProcessor.sol new file mode 100644 index 00000000..98b8d3b0 --- /dev/null +++ b/test/foundry/mocks/MockTermsProcessor.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSDL-1.1 +pragma solidity ^0.8.13; + +import "contracts/modules/licensing/terms/ITermsProcessor.sol"; + +contract MockTermsProcessor is ITermsProcessor { + + bool success = true; + + function setSuccess(bool value) external { + success = value; + } + + function supportsInterface( + bytes4 + ) external pure override returns (bool) { + return true; + } + + function executeTerms( + bytes calldata data + ) external pure override returns (bytes memory newData) { + return data; + } + + function tersmExecutedSuccessfully( + bytes calldata + ) external view override returns (bool) { + return success; + } +} \ No newline at end of file diff --git a/test/foundry/utils/BaseTest.sol b/test/foundry/utils/BaseTest.sol index 7b99de3a..0a5702f5 100644 --- a/test/foundry/utils/BaseTest.sol +++ b/test/foundry/utils/BaseTest.sol @@ -77,7 +77,7 @@ contract BaseTest is Test, ProxyHelper { ); // upgrade factory to use new event emitter - factory.upgradeFranchises(address(new IPAssetRegistry(eventEmitter, address(licensingModule)))); + factory.upgradeFranchises(address(new IPAssetRegistry(eventEmitter, address(licensingModule), address(franchiseRegistry)))); vm.startPrank(franchiseOwner); // Register Franchise (will create IPAssetRegistry and associated LicenseRegistry) From d405dda12fd2b855b7617ac2f5ae0e24072204bc Mon Sep 17 00:00:00 2001 From: Raul Date: Tue, 29 Aug 2023 21:12:35 +0200 Subject: [PATCH 21/42] fix root license creation, config, license registry transfer --- contracts/ip-assets/IPAssetRegistry.sol | 6 ++- .../modules/licensing/LicenseRegistry.sol | 14 ++++-- contracts/modules/licensing/RightsManager.sol | 47 ++++++++++++------- .../licensing/terms/TimeTermsProcessor.sol | 8 ++-- test/foundry/licensing/LicensingModule.t.sol | 7 ++- test/foundry/mocks/MockTermsProcessor.sol | 6 ++- test/foundry/utils/BaseTest.sol | 4 +- 7 files changed, 62 insertions(+), 30 deletions(-) diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index 9f905af0..1b2c9291 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -115,7 +115,8 @@ contract IPAssetRegistry is revoker, false, config.canSublicense, - terms + terms, + false ); } @@ -129,7 +130,8 @@ contract IPAssetRegistry is revoker, true, config.canSublicense, - terms + terms, + false ); } diff --git a/contracts/modules/licensing/LicenseRegistry.sol b/contracts/modules/licensing/LicenseRegistry.sol index 0f9d5afc..e2508802 100644 --- a/contracts/modules/licensing/LicenseRegistry.sol +++ b/contracts/modules/licensing/LicenseRegistry.sol @@ -7,6 +7,8 @@ import { IERC5218 } from "./IERC5218.sol"; contract LicenseRegistry is ERC721 { IERC5218 public immutable RIGHTS_MANAGER; + + error LicenseCanBeRevokedNotBurned(); constructor(address rightsManager, string memory name, string memory symbol) ERC721(name, symbol) { if (rightsManager == address(0)) { @@ -20,11 +22,11 @@ contract LicenseRegistry is ERC721 { _; } - function mint(address to, uint256 tokenId) public onlyRightsManager { + function mint(address to, uint256 tokenId) external onlyRightsManager { _mint(to, tokenId); } - function exists(uint256 tokenId) public view returns (bool) { + function exists(uint256 tokenId) external view returns (bool) { return _exists(tokenId); } @@ -34,7 +36,13 @@ contract LicenseRegistry is ERC721 { uint256 firstTokenId, uint256 batchSize ) internal virtual override { - RIGHTS_MANAGER.transferSublicense(firstTokenId, to); + // Minting has already been checked by the RightsManager. + if (from != address(0)) { + RIGHTS_MANAGER.transferSublicense(firstTokenId, to); + } + if (to == address(0)) { + revert LicenseCanBeRevokedNotBurned(); + } super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index b636d743..3d7d3d9b 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -10,8 +10,6 @@ import { NonExistentID, Unauthorized, ZeroAddress, UnsupportedInterface } from " import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; -import "forge-std/console.sol"; - abstract contract RightsManager is ERC721Upgradeable, @@ -46,13 +44,13 @@ abstract contract RightsManager is // keccack256(commercial, tokenId) => licenseId mapping(bytes32 => uint256) licenseForTokenId; uint256 licenseCounter; + LicenseRegistry licenseRegistry; } // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.rights-manager.storage")) - 1))) bytes32 private constant _STORAGE_LOCATION = 0x315576c20e31e03ef3e70482445a4c33e45baf13beff28e79f2adf6d06cc0bee; uint256 private constant _UNSET_LICENSE_ID = 0; uint256 public constant FRANCHISE_REGISTRY_OWNED_TOKEN_ID = type(uint256).max; - LicenseRegistry public immutable LICENSE_REGISTRY; IERC721 public immutable FRANCHISE_REGISTRY; @@ -60,8 +58,7 @@ abstract contract RightsManager is if (_franchiseRegistry == address(0)) { revert ZeroAddress(); } - FRANCHISE_REGISTRY = IERC721(_franchiseRegistry); - LICENSE_REGISTRY = new LicenseRegistry(address(this), "Licenses", "SPLC"); + FRANCHISE_REGISTRY = IERC721(_franchiseRegistry); } function __RightsManager_init( @@ -69,6 +66,9 @@ abstract contract RightsManager is string calldata symbol ) public initializer { __ERC721_init(name, symbol); + // licenseRegistry should be immutable, but address(this) in the constructor is the interface address, not the proxy address + // TODO: Revisit this if/when we make IPAssetRegistries immutable + _getRightsManagerStorage().licenseRegistry = new LicenseRegistry(address(this), "Licenses", "SPLC"); } function _getRightsManagerStorage() @@ -100,7 +100,8 @@ abstract contract RightsManager is _revoker, _commercial, _canSublicense, - _terms + _terms, + true ); } @@ -122,7 +123,8 @@ abstract contract RightsManager is _revoker, _commercial, _canSublicense, - _terms + _terms, + true ); } @@ -134,7 +136,8 @@ abstract contract RightsManager is address revoker, bool commercial, bool canSublicense, - TermsProcessorConfig memory _terms + TermsProcessorConfig memory _terms, + bool inLicenseRegistry ) internal returns (uint256) { // TODO: should revoker come from allowed revoker list? if (revoker == address(0)) revert ZeroRevokerAddress(); @@ -168,6 +171,11 @@ abstract contract RightsManager is $.licenseForTokenId[ keccak256(abi.encode(commercial, tokenId)) ] = licenseId; + + if (inLicenseRegistry) { + $.licenseRegistry.mint(licenseHolder, licenseId); + } + emit TransferLicense(licenseId, licenseHolder); emit CreateLicense( licenseId, tokenId, @@ -176,7 +184,7 @@ abstract contract RightsManager is uri, revoker ); - emit TransferLicense(licenseId, licenseHolder); + return licenseId; } @@ -187,11 +195,12 @@ abstract contract RightsManager is if (msg.sender != license.revoker) revert SenderNotRevoker(); license.active = false; emit RevokeLicense(_licenseId); + // TODO: should we burn the license if it's from the LicenseRegistry? } function executeTerms(uint256 _licenseId) external { - if (msg.sender != address(LICENSE_REGISTRY)) revert Unauthorized(); RightsManagerStorage storage $ = _getRightsManagerStorage(); + if (msg.sender != address($.licenseRegistry)) revert Unauthorized(); License storage license = $.licenses[_licenseId]; if (license.termsProcessor != ITermsProcessor(address(0))) { bytes memory newData = license.termsProcessor.executeTerms(license.termsData); @@ -256,7 +265,7 @@ abstract contract RightsManager is function _verifyTerms(TermsProcessorConfig memory _terms) private view { if (address(_terms.processor) != address(0) && - !address(_terms.processor).supportsInterface(type(ITermsProcessor).interfaceId)) { + !_terms.processor.supportsInterface(type(ITermsProcessor).interfaceId)) { revert UnsupportedInterface("ITermsProcessor"); } } @@ -276,10 +285,11 @@ abstract contract RightsManager is function getLicenseHolder( uint256 _licenseId ) public view override returns (address) { - if (LICENSE_REGISTRY.exists(_licenseId)) { - return LICENSE_REGISTRY.ownerOf(_licenseId); + RightsManagerStorage storage $ = _getRightsManagerStorage(); + if ($.licenseRegistry.exists(_licenseId)) { + return $.licenseRegistry.ownerOf(_licenseId); } else { - License storage license = _getRightsManagerStorage().licenses[ + License storage license = $.licenses[ _licenseId ]; return ownerOf(license.tokenId); @@ -308,6 +318,10 @@ abstract contract RightsManager is ]; } + function getLicenseRegistry() external view returns (LicenseRegistry) { + return _getRightsManagerStorage().licenseRegistry; + } + function isRootLicense( uint256 licenseId ) public view returns (bool) { @@ -318,10 +332,9 @@ abstract contract RightsManager is uint256 licenseId, address licenseHolder ) public virtual override(IERC5218) { - if (msg.sender != address(LICENSE_REGISTRY)) revert Unauthorized(); + RightsManagerStorage storage $ = _getRightsManagerStorage(); + if (msg.sender != address($.licenseRegistry)) revert Unauthorized(); if (!isLicenseActive(licenseId)) revert InactiveLicense(); - if (_getRightsManagerStorage().licenses[licenseId].parentLicenseId == 0) - revert CannotSublicense(); emit TransferLicense(licenseId, licenseHolder); } diff --git a/contracts/modules/licensing/terms/TimeTermsProcessor.sol b/contracts/modules/licensing/terms/TimeTermsProcessor.sol index a98645af..e760f426 100644 --- a/contracts/modules/licensing/terms/TimeTermsProcessor.sol +++ b/contracts/modules/licensing/terms/TimeTermsProcessor.sol @@ -3,8 +3,10 @@ pragma solidity ^0.8.19; import { ITermsProcessor } from "./ITermsProcessor.sol"; import { LibDuration } from "../../timing/LibDuration.sol"; +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -contract TimeTermsProcessor is ITermsProcessor { +contract TimeTermsProcessor is ITermsProcessor, ERC165 { using LibDuration for LibDuration.TimeConfig; function executeTerms(bytes calldata data) external view override returns (bytes memory newData) { @@ -23,7 +25,7 @@ contract TimeTermsProcessor is ITermsProcessor { function supportsInterface( bytes4 interfaceId - ) external pure override returns (bool) { - return interfaceId == type(ITermsProcessor).interfaceId; + ) public view override(ERC165, IERC165) returns (bool) { + return interfaceId == type(ITermsProcessor).interfaceId || super.supportsInterface(interfaceId); } } \ No newline at end of file diff --git a/test/foundry/licensing/LicensingModule.t.sol b/test/foundry/licensing/LicensingModule.t.sol index 0885c7cf..1f116247 100644 --- a/test/foundry/licensing/LicensingModule.t.sol +++ b/test/foundry/licensing/LicensingModule.t.sol @@ -9,8 +9,8 @@ import "contracts/errors/General.sol"; contract LicensingModuleTest is BaseTest { - ITermsProcessor public termsProcessor1; - ITermsProcessor public termsProcessor2; + MockTermsProcessor public termsProcessor1; + MockTermsProcessor public termsProcessor2; function setUp() virtual override public { deployProcessors = false; @@ -40,7 +40,10 @@ contract LicensingModuleTest is BaseTest { processor: termsProcessor1, data: abi.encode("root") }); + uint256 rootLicenseId = ipAssetRegistry.createFranchiseRootLicense(1, franchiseOwner, "commercial_uri_root", revoker, true, true, termsConfig); + assertEq(licenseRegistry.ownerOf(rootLicenseId), franchiseOwner); + assertEq(rootLicenseId, 1); ILicensingModule.FranchiseConfig memory config = ILicensingModule.FranchiseConfig({ nonCommercialConfig: ILicensingModule.IpAssetConfig({ diff --git a/test/foundry/mocks/MockTermsProcessor.sol b/test/foundry/mocks/MockTermsProcessor.sol index 98b8d3b0..417c67d1 100644 --- a/test/foundry/mocks/MockTermsProcessor.sol +++ b/test/foundry/mocks/MockTermsProcessor.sol @@ -2,8 +2,10 @@ pragma solidity ^0.8.13; import "contracts/modules/licensing/terms/ITermsProcessor.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -contract MockTermsProcessor is ITermsProcessor { +contract MockTermsProcessor is ITermsProcessor, ERC165 { bool success = true; @@ -13,7 +15,7 @@ contract MockTermsProcessor is ITermsProcessor { function supportsInterface( bytes4 - ) external pure override returns (bool) { + ) public pure override(ERC165, IERC165) returns (bool) { return true; } diff --git a/test/foundry/utils/BaseTest.sol b/test/foundry/utils/BaseTest.sol index 0a5702f5..33e5726d 100644 --- a/test/foundry/utils/BaseTest.sol +++ b/test/foundry/utils/BaseTest.sol @@ -17,6 +17,7 @@ import "contracts/modules/relationships/RelationshipModuleBase.sol"; import "contracts/modules/relationships/ProtocolRelationshipModule.sol"; import "contracts/modules/licensing/LicensingModule.sol"; import "contracts/modules/licensing/terms/ITermsProcessor.sol"; +import "contracts/modules/licensing/LicenseRegistry.sol"; contract BaseTest is Test, ProxyHelper { @@ -27,6 +28,7 @@ contract BaseTest is Test, ProxyHelper { AccessControlSingleton accessControl; PermissionlessRelationshipProcessor public relationshipProcessor; LicensingModule public licensingModule; + LicenseRegistry public licenseRegistry; bool public deployProcessors = false; address constant admin = address(123); @@ -63,7 +65,6 @@ contract BaseTest is Test, ProxyHelper { // Create Common Event Emitter address eventEmitter = address(new CommonIPAssetEventEmitter(address(franchiseRegistry))); - console.log("Event Emitter", eventEmitter); // Create Licensing Module address licensingImplementation = address(new LicensingModule(address(franchiseRegistry))); licensingModule = LicensingModule( @@ -84,6 +85,7 @@ contract BaseTest is Test, ProxyHelper { FranchiseRegistry.FranchiseCreationParams memory params = FranchiseRegistry.FranchiseCreationParams("name", "symbol", "description", "tokenURI"); (uint256 franchiseId, address ipAssets) = franchiseRegistry.registerFranchise(params); ipAssetRegistry = IPAssetRegistry(ipAssets); + licenseRegistry = ipAssetRegistry.getLicenseRegistry(); // Configure Licensing for Franchise LicensingModule.FranchiseConfig memory licenseConfig = ILicensingModule.FranchiseConfig({ From b3df8a007865b9fc7f37388882a23acae70b0523 Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 30 Aug 2023 00:31:53 +0200 Subject: [PATCH 22/42] WIP: test_revert_rootLicenseNotActiveCommercial not working, isLicenseActive ok? --- .../modules/licensing/LicensingModule.sol | 5 ++ test/foundry/licensing/LicensingModule.t.sol | 78 +++++++++++++------ 2 files changed, 60 insertions(+), 23 deletions(-) diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index 4d761e9b..d8f7db15 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -8,6 +8,7 @@ import { UPGRADER_ROLE, LICENSING_MANAGER_ROLE } from "contracts/access-control/ import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; import { IERC5218 } from "./IERC5218.sol"; import { ILicensingModule } from "./ILicensingModule.sol"; +import "forge-std/console.sol"; contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { @@ -72,11 +73,15 @@ contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { } function _verifyRootLicense(uint256 franchiseId, uint256 rootLicenseId) internal view { + console.log("rootLicenseId", rootLicenseId); if (rootLicenseId != 0) { IERC5218 rightsManager = IERC5218(FRANCHISE_REGISTRY.ipAssetRegistryForId(franchiseId)); if (address(rightsManager) == address(0)) { + // FRANCHISE_REGISTRY.ownerOf(franchiseId) should take care of this, + // but leaving it in case IPAssetRegistration creation fails somewhow. revert NonExistentFranchise(); } + console.log("rightsManager.isLicenseActive(rootLicenseId)", rightsManager.isLicenseActive(rootLicenseId)); if (!rightsManager.isLicenseActive(rootLicenseId)) { revert RootLicenseNotActive(rootLicenseId); } diff --git a/test/foundry/licensing/LicensingModule.t.sol b/test/foundry/licensing/LicensingModule.t.sol index 1f116247..13d795f9 100644 --- a/test/foundry/licensing/LicensingModule.t.sol +++ b/test/foundry/licensing/LicensingModule.t.sol @@ -23,6 +23,35 @@ contract LicensingModuleTest is BaseTest { assertEq(licensingModule.getNonCommercialLicenseURI(), NON_COMMERCIAL_LICENSE_URI); } + function test_configFranchise() public { + vm.startPrank(franchiseOwner); + IERC5218.TermsProcessorConfig memory termsConfig = IERC5218.TermsProcessorConfig({ + processor: termsProcessor1, + data: abi.encode("root") + }); + + uint256 rootLicenseId = ipAssetRegistry.createFranchiseRootLicense(1, franchiseOwner, "commercial_uri_root", revoker, true, true, termsConfig); + assertEq(licenseRegistry.ownerOf(rootLicenseId), franchiseOwner); + assertEq(rootLicenseId, 1); + + ILicensingModule.FranchiseConfig memory config = _getLicensingConfig(); + config.commercialConfig.franchiseRootLicenseId = rootLicenseId; + + licensingModule.configureFranchiseLicensing(1, config); + ILicensingModule.FranchiseConfig memory configResult = licensingModule.getFranchiseConfig(1); + assertEq(configResult.nonCommercialConfig.canSublicense, true); + assertEq(configResult.nonCommercialConfig.franchiseRootLicenseId, 0); + assertEq(address(configResult.nonCommercialTerms.processor), address(termsProcessor1)); + assertEq(configResult.nonCommercialTerms.data, abi.encode("hi")); + assertEq(configResult.commercialConfig.canSublicense, false); + assertEq(configResult.commercialConfig.franchiseRootLicenseId, 1); + assertEq(address(configResult.commercialTerms.processor), address(termsProcessor2)); + assertEq(configResult.commercialTerms.data, abi.encode("bye")); + assertEq(configResult.rootIpAssetHasCommercialRights, false); + assertEq(configResult.revoker, address(0x5656565)); + vm.stopPrank(); + } + function test_revert_nonAuthorizedConfigSetter() public { vm.expectRevert(Unauthorized.selector); licensingModule.configureFranchiseLicensing(1, LibMockFranchiseConfig.getMockFranchiseConfig()); @@ -32,20 +61,40 @@ contract LicensingModuleTest is BaseTest { vm.expectRevert("ERC721: invalid token ID"); licensingModule.configureFranchiseLicensing(2, LibMockFranchiseConfig.getMockFranchiseConfig()); } - - function test_configFranchise() public { + function test_revert_zeroRevokerAddress() public { vm.startPrank(franchiseOwner); + ILicensingModule.FranchiseConfig memory config = LibMockFranchiseConfig.getMockFranchiseConfig(); + config.revoker = address(0); + vm.expectRevert(LicensingModule.ZeroRevokerAddress.selector); + licensingModule.configureFranchiseLicensing(1, config); + vm.stopPrank(); + } + + function test_revert_rootLicenseNotActiveCommercial() public { + IERC5218.TermsProcessorConfig memory termsConfig = IERC5218.TermsProcessorConfig({ processor: termsProcessor1, data: abi.encode("root") }); + + + vm.prank(franchiseOwner); uint256 rootLicenseId = ipAssetRegistry.createFranchiseRootLicense(1, franchiseOwner, "commercial_uri_root", revoker, true, true, termsConfig); - assertEq(licenseRegistry.ownerOf(rootLicenseId), franchiseOwner); - assertEq(rootLicenseId, 1); - ILicensingModule.FranchiseConfig memory config = ILicensingModule.FranchiseConfig({ + termsProcessor2.setSuccess(false); + + ILicensingModule.FranchiseConfig memory config = _getLicensingConfig(); + config.commercialConfig.franchiseRootLicenseId = rootLicenseId; + vm.startPrank(franchiseOwner); + vm.expectRevert(LicensingModule.RootLicenseNotActive.selector); + licensingModule.configureFranchiseLicensing(1, config); + vm.stopPrank(); + } + + function _getLicensingConfig() view private returns (ILicensingModule.FranchiseConfig memory) { + return ILicensingModule.FranchiseConfig({ nonCommercialConfig: ILicensingModule.IpAssetConfig({ canSublicense: true, franchiseRootLicenseId: 0 @@ -56,7 +105,7 @@ contract LicensingModuleTest is BaseTest { }), commercialConfig: ILicensingModule.IpAssetConfig({ canSublicense: false, - franchiseRootLicenseId: rootLicenseId + franchiseRootLicenseId: 0 }), commercialTerms: IERC5218.TermsProcessorConfig({ processor: termsProcessor2, @@ -66,22 +115,5 @@ contract LicensingModuleTest is BaseTest { revoker: address(0x5656565), commercialLicenseUri: "uriuri" }); - - licensingModule.configureFranchiseLicensing(1, config); - ILicensingModule.FranchiseConfig memory configResult = licensingModule.getFranchiseConfig(1); - assertEq(configResult.nonCommercialConfig.canSublicense, true); - assertEq(configResult.nonCommercialConfig.franchiseRootLicenseId, 0); - assertEq(address(configResult.nonCommercialTerms.processor), address(termsProcessor1)); - assertEq(configResult.nonCommercialTerms.data, abi.encode("hi")); - assertEq(configResult.commercialConfig.canSublicense, false); - assertEq(configResult.commercialConfig.franchiseRootLicenseId, 1); - assertEq(address(configResult.commercialTerms.processor), address(termsProcessor2)); - assertEq(configResult.commercialTerms.data, abi.encode("bye")); - assertEq(configResult.rootIpAssetHasCommercialRights, false); - assertEq(configResult.revoker, address(0x5656565)); - vm.stopPrank(); } - - - } From 126526f670e55ba4038b8d1c387a075e7c6fc2e8 Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 30 Aug 2023 02:39:33 +0200 Subject: [PATCH 23/42] wip --- contracts/modules/licensing/RightsManager.sol | 30 +++++---- test/foundry/licensing/LicensingModule.t.sol | 43 +++---------- test/foundry/licensing/RightsManager.t.sol | 64 +++++++++++++++++++ test/foundry/utils/BaseTest.sol | 54 +++++++++------- 4 files changed, 121 insertions(+), 70 deletions(-) create mode 100644 test/foundry/licensing/RightsManager.t.sol diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 3d7d3d9b..583da14e 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -10,6 +10,7 @@ import { NonExistentID, Unauthorized, ZeroAddress, UnsupportedInterface } from " import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; +import "forge-std/console.sol"; abstract contract RightsManager is ERC721Upgradeable, @@ -68,7 +69,11 @@ abstract contract RightsManager is __ERC721_init(name, symbol); // licenseRegistry should be immutable, but address(this) in the constructor is the interface address, not the proxy address // TODO: Revisit this if/when we make IPAssetRegistries immutable - _getRightsManagerStorage().licenseRegistry = new LicenseRegistry(address(this), "Licenses", "SPLC"); + _getRightsManagerStorage().licenseRegistry = new LicenseRegistry( + address(this), + string.concat("Licenses for ", name), + string.concat("sl", symbol) + ); } function _getRightsManagerStorage() @@ -175,7 +180,6 @@ abstract contract RightsManager is if (inLicenseRegistry) { $.licenseRegistry.mint(licenseHolder, licenseId); } - emit TransferLicense(licenseId, licenseHolder); emit CreateLicense( licenseId, tokenId, @@ -184,7 +188,7 @@ abstract contract RightsManager is uri, revoker ); - + emit TransferLicense(licenseId, licenseHolder); return licenseId; } @@ -215,22 +219,26 @@ abstract contract RightsManager is uint256 licenseId ) public view virtual returns (bool) { // TODO: limit to the tree depth - // TODO: check time limits + console.log("isLicenseActive(%s)"); if (licenseId == 0) return false; + RightsManagerStorage storage $ = _getRightsManagerStorage(); while (licenseId != 0) { - RightsManagerStorage storage $ = _getRightsManagerStorage(); + console.log("licenseId", licenseId); License memory license = $.licenses[licenseId]; - if ( - !( - license.active && - license.termsProcessor.tersmExecutedSuccessfully(license.termsData) - ) - ) return false; + if (!_isActiveAndTermsOk(license)) return false; licenseId = license.parentLicenseId; } return true; } + function _isActiveAndTermsOk(License memory license) view private returns (bool) { + //console.log("license.active", license.active); + //console.log("license.termsProcessor", address(license.termsProcessor)); + if (address(license.termsProcessor) == address(0)) return license.active; + //console.log("tersmExecutedSuccessfully", license.termsProcessor.tersmExecutedSuccessfully(license.termsData)); + return license.active && license.termsProcessor.tersmExecutedSuccessfully(license.termsData); + } + function _verifySublicense( uint256 parentLicenseId, diff --git a/test/foundry/licensing/LicensingModule.t.sol b/test/foundry/licensing/LicensingModule.t.sol index 13d795f9..9f854dcb 100644 --- a/test/foundry/licensing/LicensingModule.t.sol +++ b/test/foundry/licensing/LicensingModule.t.sol @@ -9,14 +9,9 @@ import "contracts/errors/General.sol"; contract LicensingModuleTest is BaseTest { - MockTermsProcessor public termsProcessor1; - MockTermsProcessor public termsProcessor2; - function setUp() virtual override public { deployProcessors = false; super.setUp(); - termsProcessor1 = new MockTermsProcessor(); - termsProcessor2 = new MockTermsProcessor(); } function test_setUp() public { @@ -26,7 +21,7 @@ contract LicensingModuleTest is BaseTest { function test_configFranchise() public { vm.startPrank(franchiseOwner); IERC5218.TermsProcessorConfig memory termsConfig = IERC5218.TermsProcessorConfig({ - processor: termsProcessor1, + processor: commercialTermsProcessor, data: abi.encode("root") }); @@ -35,17 +30,20 @@ contract LicensingModuleTest is BaseTest { assertEq(rootLicenseId, 1); ILicensingModule.FranchiseConfig memory config = _getLicensingConfig(); + config.revoker = address(0x5656565); config.commercialConfig.franchiseRootLicenseId = rootLicenseId; + config.commercialTerms.data = abi.encode("bye"); + config.nonCommercialTerms.data = abi.encode("hi"); licensingModule.configureFranchiseLicensing(1, config); ILicensingModule.FranchiseConfig memory configResult = licensingModule.getFranchiseConfig(1); assertEq(configResult.nonCommercialConfig.canSublicense, true); assertEq(configResult.nonCommercialConfig.franchiseRootLicenseId, 0); - assertEq(address(configResult.nonCommercialTerms.processor), address(termsProcessor1)); + assertEq(address(configResult.nonCommercialTerms.processor), address(nonCommercialTermsProcessor)); assertEq(configResult.nonCommercialTerms.data, abi.encode("hi")); assertEq(configResult.commercialConfig.canSublicense, false); assertEq(configResult.commercialConfig.franchiseRootLicenseId, 1); - assertEq(address(configResult.commercialTerms.processor), address(termsProcessor2)); + assertEq(address(configResult.commercialTerms.processor), address(commercialTermsProcessor)); assertEq(configResult.commercialTerms.data, abi.encode("bye")); assertEq(configResult.rootIpAssetHasCommercialRights, false); assertEq(configResult.revoker, address(0x5656565)); @@ -74,16 +72,14 @@ contract LicensingModuleTest is BaseTest { function test_revert_rootLicenseNotActiveCommercial() public { IERC5218.TermsProcessorConfig memory termsConfig = IERC5218.TermsProcessorConfig({ - processor: termsProcessor1, + processor: commercialTermsProcessor, data: abi.encode("root") }); - - vm.prank(franchiseOwner); uint256 rootLicenseId = ipAssetRegistry.createFranchiseRootLicense(1, franchiseOwner, "commercial_uri_root", revoker, true, true, termsConfig); - termsProcessor2.setSuccess(false); + commercialTermsProcessor.setSuccess(false); ILicensingModule.FranchiseConfig memory config = _getLicensingConfig(); config.commercialConfig.franchiseRootLicenseId = rootLicenseId; @@ -93,27 +89,4 @@ contract LicensingModuleTest is BaseTest { vm.stopPrank(); } - function _getLicensingConfig() view private returns (ILicensingModule.FranchiseConfig memory) { - return ILicensingModule.FranchiseConfig({ - nonCommercialConfig: ILicensingModule.IpAssetConfig({ - canSublicense: true, - franchiseRootLicenseId: 0 - }), - nonCommercialTerms: IERC5218.TermsProcessorConfig({ - processor: termsProcessor1, - data: abi.encode("hi") - }), - commercialConfig: ILicensingModule.IpAssetConfig({ - canSublicense: false, - franchiseRootLicenseId: 0 - }), - commercialTerms: IERC5218.TermsProcessorConfig({ - processor: termsProcessor2, - data: abi.encode("bye") - }), - rootIpAssetHasCommercialRights: false, - revoker: address(0x5656565), - commercialLicenseUri: "uriuri" - }); - } } diff --git a/test/foundry/licensing/RightsManager.t.sol b/test/foundry/licensing/RightsManager.t.sol new file mode 100644 index 00000000..a5c27b10 --- /dev/null +++ b/test/foundry/licensing/RightsManager.t.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: BUSDL-1.1 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import '../utils/BaseTest.sol'; +import '../mocks/MockLicensingModule.sol'; +import '../mocks/MockTermsProcessor.sol'; +import "contracts/errors/General.sol"; + +contract RightsManagerIPAssetRightsTest is BaseTest { + + address ipAssetCreator = address(0x999999); + + function setUp() virtual override public { + deployProcessors = false; + super.setUp(); + } + + function test_setUp() public { + assertEq(licenseRegistry.name(), "Licenses for FranchiseName"); + assertEq(licenseRegistry.symbol(), "slFRN"); + assertEq(address(licenseRegistry.RIGHTS_MANAGER()), address(ipAssetRegistry)); + } + + function test_Rights_NoCommercialRights_RootIPAsset() public { + + // TODO: test for events + vm.prank(ipAssetCreator); + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaurl", address(ipAssetCreator), 0); + uint256 ncrLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, false); + assertEq(ncrLicenseId, 1); + assertEq(ipAssetRegistry.getLicenseTokenId(ncrLicenseId), ipAssetId); + assertEq(ipAssetRegistry.getParentLicenseId(ncrLicenseId), 0); + assertTrue(ipAssetRegistry.isLicenseActive(ncrLicenseId)); + (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(ncrLicenseId); + assertEq(address(ipAssetCreator), owner, "license owner"); + assertEq(license.active, true, "license active"); + assertEq(license.canSublicense, true, "license canSublicense"); + assertEq(license.commercial, false, "license commercial"); + assertEq(license.parentLicenseId, 0, "license parentLicenseId"); + assertEq(license.tokenId, ipAssetId, "license tokenId"); + assertEq(license.revoker, revoker, "license revoker"); + assertEq(license.uri, NON_COMMERCIAL_LICENSE_URI, "license uri"); + assertEq(address(license.termsProcessor), address(nonCommercialTermsProcessor), "license termsProcessor"); + assertEq(license.termsData, abi.encode("nonCommercial"), "license termsData"); + uint256 crLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, true); + assertEq(crLicenseId, 0); + (RightsManager.License memory licenseCr, address ownerCr) = ipAssetRegistry.getLicense(ncrLicenseId); + assertEq(address(0), ownerCr, "commercial license owner"); + } + /* + function test_Rights_CommercialRights_RootIPAsset() public { + assertTrue(false); + } + function test_Rights_CommercialRights_NonRootIPAsset() public { + assertTrue(false); + } + function test_revert_licensingModuleNotConfigured() public { + assertTrue(false); + }*/ + + + +} diff --git a/test/foundry/utils/BaseTest.sol b/test/foundry/utils/BaseTest.sol index 33e5726d..2a1d730d 100644 --- a/test/foundry/utils/BaseTest.sol +++ b/test/foundry/utils/BaseTest.sol @@ -18,6 +18,7 @@ import "contracts/modules/relationships/ProtocolRelationshipModule.sol"; import "contracts/modules/licensing/LicensingModule.sol"; import "contracts/modules/licensing/terms/ITermsProcessor.sol"; import "contracts/modules/licensing/LicenseRegistry.sol"; +import '../mocks/MockTermsProcessor.sol'; contract BaseTest is Test, ProxyHelper { @@ -29,6 +30,8 @@ contract BaseTest is Test, ProxyHelper { PermissionlessRelationshipProcessor public relationshipProcessor; LicensingModule public licensingModule; LicenseRegistry public licenseRegistry; + MockTermsProcessor public nonCommercialTermsProcessor; + MockTermsProcessor public commercialTermsProcessor; bool public deployProcessors = false; address constant admin = address(123); @@ -82,36 +85,14 @@ contract BaseTest is Test, ProxyHelper { vm.startPrank(franchiseOwner); // Register Franchise (will create IPAssetRegistry and associated LicenseRegistry) - FranchiseRegistry.FranchiseCreationParams memory params = FranchiseRegistry.FranchiseCreationParams("name", "symbol", "description", "tokenURI"); + FranchiseRegistry.FranchiseCreationParams memory params = FranchiseRegistry.FranchiseCreationParams("FranchiseName", "FRN", "description", "tokenURI"); (uint256 franchiseId, address ipAssets) = franchiseRegistry.registerFranchise(params); ipAssetRegistry = IPAssetRegistry(ipAssets); licenseRegistry = ipAssetRegistry.getLicenseRegistry(); // Configure Licensing for Franchise - LicensingModule.FranchiseConfig memory licenseConfig = ILicensingModule.FranchiseConfig({ - nonCommercialConfig: ILicensingModule.IpAssetConfig({ - canSublicense: true, - franchiseRootLicenseId: 0 - }), - nonCommercialTerms: IERC5218.TermsProcessorConfig({ - processor: ITermsProcessor(address(0)), - data: "" - }), - commercialConfig: ILicensingModule.IpAssetConfig({ - canSublicense: true, - franchiseRootLicenseId: 0 - }), - commercialTerms: IERC5218.TermsProcessorConfig({ - processor: ITermsProcessor(address(0)), - data: "" - }), - rootIpAssetHasCommercialRights: false, - revoker: revoker, - commercialLicenseUri: COMMERCIAL_LICENSE_URI - }); - licensingModule.configureFranchiseLicensing(franchiseId, licenseConfig); + licensingModule.configureFranchiseLicensing(franchiseId, _getLicensingConfig()); - vm.stopPrank(); // Create Relationship Module @@ -127,4 +108,29 @@ contract BaseTest is Test, ProxyHelper { relationshipProcessor = new PermissionlessRelationshipProcessor(address(relationshipModule)); } } + + function _getLicensingConfig() view internal returns (ILicensingModule.FranchiseConfig memory) { + return ILicensingModule.FranchiseConfig({ + nonCommercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: true, + franchiseRootLicenseId: 0 + }), + nonCommercialTerms: IERC5218.TermsProcessorConfig({ + processor: nonCommercialTermsProcessor, + data: abi.encode("nonCommercial") + }), + commercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: false, + franchiseRootLicenseId: 0 + }), + commercialTerms: IERC5218.TermsProcessorConfig({ + processor: commercialTermsProcessor, + data: abi.encode("commercial") + }), + rootIpAssetHasCommercialRights: false, + revoker: revoker, + commercialLicenseUri: "uriuri" + }); + } + } \ No newline at end of file From b0b9e1ec52823a872bca87953a7034a6f4d3ae43 Mon Sep 17 00:00:00 2001 From: Raul Date: Thu, 31 Aug 2023 00:07:05 +0200 Subject: [PATCH 24/42] fix --- contracts/modules/licensing/IERC5218.sol | 3 +- .../modules/licensing/LicenseRegistry.sol | 5 +- contracts/modules/licensing/RightsManager.sol | 64 +++++- test/foundry/licensing/LicensingModule.t.sol | 7 +- test/foundry/licensing/RightsManager.t.sol | 202 +++++++++++++++++- test/foundry/mocks/MockERC721.sol | 12 ++ test/foundry/mocks/MockTermsProcessor.sol | 8 +- test/foundry/mocks/RightsManagerHarness.sol | 41 ++++ .../relationships/LibIPAssetMask.t.sol | 10 +- test/foundry/utils/BaseTest.sol | 2 + 10 files changed, 324 insertions(+), 30 deletions(-) create mode 100644 test/foundry/mocks/MockERC721.sol create mode 100644 test/foundry/mocks/RightsManagerHarness.sol diff --git a/contracts/modules/licensing/IERC5218.sol b/contracts/modules/licensing/IERC5218.sol index b96c74ae..641c552b 100644 --- a/contracts/modules/licensing/IERC5218.sol +++ b/contracts/modules/licensing/IERC5218.sol @@ -93,7 +93,7 @@ interface IERC5218 is IERC721Upgradeable { /// license (such as 0). /// @param _tokenId The identifier for the queried NFT /// @return The root license identifier of the queried NFT - function getLicenseIdByTokenId( + function getLicenseIdByTokenId( // NOTE: This will not work for sublicenses that have a tokenId associated. Rename to getRootLicenseIdByTokenId?? uint256 _tokenId, bool _commercial // NOTE: MODIFIED ERC-5218 ) external view returns (uint256); @@ -147,4 +147,5 @@ interface IERC5218 is IERC721Upgradeable { uint256 _licenseId, address _licenseHolder ) external; + } diff --git a/contracts/modules/licensing/LicenseRegistry.sol b/contracts/modules/licensing/LicenseRegistry.sol index e2508802..f98b99eb 100644 --- a/contracts/modules/licensing/LicenseRegistry.sol +++ b/contracts/modules/licensing/LicenseRegistry.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.13; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol"; import { IERC5218 } from "./IERC5218.sol"; +import "forge-std/console.sol"; contract LicenseRegistry is ERC721 { @@ -29,13 +30,14 @@ contract LicenseRegistry is ERC721 { function exists(uint256 tokenId) external view returns (bool) { return _exists(tokenId); } - + function _beforeTokenTransfer( address from, address to, uint256 firstTokenId, uint256 batchSize ) internal virtual override { + console.log("_beforeTokenTransfer LicenseRegistry"); // Minting has already been checked by the RightsManager. if (from != address(0)) { RIGHTS_MANAGER.transferSublicense(firstTokenId, to); @@ -45,5 +47,6 @@ contract LicenseRegistry is ERC721 { } super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } + } \ No newline at end of file diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 583da14e..3b6fec74 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -27,6 +27,7 @@ abstract contract RightsManager is error NotSublicense(); error AlreadyHasRootLicense(); error ZeroRevokerAddress(); + error NFTHasNoAssociatedLicense(); struct License { bool active; @@ -43,7 +44,7 @@ abstract contract RightsManager is struct RightsManagerStorage { mapping(uint256 => License) licenses; // keccack256(commercial, tokenId) => licenseId - mapping(bytes32 => uint256) licenseForTokenId; + mapping(bytes32 => uint256) rootLicensesForTokenId; uint256 licenseCounter; LicenseRegistry licenseRegistry; } @@ -56,6 +57,7 @@ abstract contract RightsManager is constructor(address _franchiseRegistry) { + console.log(_franchiseRegistry); if (_franchiseRegistry == address(0)) { revert ZeroAddress(); } @@ -147,20 +149,28 @@ abstract contract RightsManager is // TODO: should revoker come from allowed revoker list? if (revoker == address(0)) revert ZeroRevokerAddress(); RightsManagerStorage storage $ = _getRightsManagerStorage(); + // Only licenses minted to the FranchiseRegistry Owner as a root license should + // have tokenId = FRANCHISE_REGISTRY_OWNED_TOKEN_ID, otherwise the tokenId should be a minted NFT (IPAsset) + // Checks for the FranchiseRegistry Owner should be done in the calling function if (tokenId != FRANCHISE_REGISTRY_OWNED_TOKEN_ID) { if (!_exists(tokenId)) { revert NonExistentID(tokenId); } } + // If this is a root license, check that the tokenId doesn't already have a root license if (parentLicenseId == _UNSET_LICENSE_ID) { - if ($.licenseForTokenId[keccak256(abi.encode(commercial, tokenId))] != _UNSET_LICENSE_ID) { + if ($.rootLicensesForTokenId[keccak256(abi.encode(commercial, tokenId))] != _UNSET_LICENSE_ID) { revert AlreadyHasRootLicense(); } } else { + // If this is a sublicense, check that this is a valid sublicense License memory parentLicense = $.licenses[parentLicenseId]; _verifySublicense(parentLicenseId, licenseHolder, commercial, parentLicense); } + // Check that the terms are valid _verifyTerms(_terms); + + // Create the license and increment the licenseCounter uint256 licenseId = ++$.licenseCounter; $.licenses[licenseId] = License({ active: true, @@ -173,13 +183,19 @@ abstract contract RightsManager is termsProcessor: _terms.processor, termsData: _terms.data }); - $.licenseForTokenId[ - keccak256(abi.encode(commercial, tokenId)) - ] = licenseId; - + // Save tokenId => licenseId relationship IF this is a root license + if (parentLicenseId == _UNSET_LICENSE_ID) { + $.rootLicensesForTokenId[ + keccak256(abi.encode(commercial, tokenId)) + ] = licenseId; + } + // Mint the license in the LicenseRegistry if requested. Should not do this for IPAsset Rights, but + // the checks on inLicenseRegistry should be done in the calling function if (inLicenseRegistry) { $.licenseRegistry.mint(licenseHolder, licenseId); } + + // Emit events emit CreateLicense( licenseId, tokenId, @@ -200,6 +216,7 @@ abstract contract RightsManager is license.active = false; emit RevokeLicense(_licenseId); // TODO: should we burn the license if it's from the LicenseRegistry? + // TODO: delete the rootLicenseForTokenId mapping for licenseId if root license } function executeTerms(uint256 _licenseId) external { @@ -232,10 +249,10 @@ abstract contract RightsManager is } function _isActiveAndTermsOk(License memory license) view private returns (bool) { - //console.log("license.active", license.active); - //console.log("license.termsProcessor", address(license.termsProcessor)); + // console.log("license.active", license.active); + // console.log("license.termsProcessor", address(license.termsProcessor)); if (address(license.termsProcessor) == address(0)) return license.active; - //console.log("tersmExecutedSuccessfully", license.termsProcessor.tersmExecutedSuccessfully(license.termsData)); + // console.log("tersmExecutedSuccessfully", license.termsProcessor.tersmExecutedSuccessfully(license.termsData)); return license.active && license.termsProcessor.tersmExecutedSuccessfully(license.termsData); } @@ -267,9 +284,33 @@ abstract contract RightsManager is uint256 firstTokenId, uint256 batchSize ) internal virtual override { - // TODO: trigger rights transfer check, check granting terms, banned marketplaces, etc. + if (from != address(0)) { + for (uint256 i = firstTokenId; i < batchSize;) { + _verifyRightsTransfer(from, to, i); + unchecked { + i++; + } + } + } else { + console.log("minting", firstTokenId, batchSize); + } super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } + + function _verifyRightsTransfer( + address from, + address to, + uint256 tokenId + ) internal { + // TODO: trigger rights transfer check, check granting terms, banned marketplaces, etc. + RightsManagerStorage storage $ = _getRightsManagerStorage(); + // NOTE: We are assuming a revoked Non Commercial License impedes the transfer of the NFT. + // Should revoked commercial rights also impede the transfer? + uint256 licenseId = $.rootLicensesForTokenId[keccak256(abi.encode(false, tokenId))]; + if (licenseId != _UNSET_LICENSE_ID) revert NFTHasNoAssociatedLicense(); // This should not happen, if fired there is a bug somewhere + if (isLicenseActive(licenseId)) revert InactiveLicense(); // NOTE: Should we freeze invalid licenses? burn them? + emit TransferLicense(licenseId, to); + } function _verifyTerms(TermsProcessorConfig memory _terms) private view { if (address(_terms.processor) != address(0) && @@ -321,7 +362,7 @@ abstract contract RightsManager is bool _commercial ) public view override returns (uint256) { return - _getRightsManagerStorage().licenseForTokenId[ + _getRightsManagerStorage().rootLicensesForTokenId[ keccak256(abi.encode(_commercial, _tokenId)) ]; } @@ -340,6 +381,7 @@ abstract contract RightsManager is uint256 licenseId, address licenseHolder ) public virtual override(IERC5218) { + console.log("wtf", licenseId, licenseHolder); RightsManagerStorage storage $ = _getRightsManagerStorage(); if (msg.sender != address($.licenseRegistry)) revert Unauthorized(); if (!isLicenseActive(licenseId)) revert InactiveLicense(); diff --git a/test/foundry/licensing/LicensingModule.t.sol b/test/foundry/licensing/LicensingModule.t.sol index 9f854dcb..6d044e75 100644 --- a/test/foundry/licensing/LicensingModule.t.sol +++ b/test/foundry/licensing/LicensingModule.t.sol @@ -78,15 +78,16 @@ contract LicensingModuleTest is BaseTest { vm.prank(franchiseOwner); uint256 rootLicenseId = ipAssetRegistry.createFranchiseRootLicense(1, franchiseOwner, "commercial_uri_root", revoker, true, true, termsConfig); - + commercialTermsProcessor.setSuccess(false); - + ILicensingModule.FranchiseConfig memory config = _getLicensingConfig(); config.commercialConfig.franchiseRootLicenseId = rootLicenseId; vm.startPrank(franchiseOwner); - vm.expectRevert(LicensingModule.RootLicenseNotActive.selector); + vm.expectRevert(abi.encodeWithSignature("RootLicenseNotActive(uint256)", 1)); licensingModule.configureFranchiseLicensing(1, config); vm.stopPrank(); + } } diff --git a/test/foundry/licensing/RightsManager.t.sol b/test/foundry/licensing/RightsManager.t.sol index a5c27b10..547070b0 100644 --- a/test/foundry/licensing/RightsManager.t.sol +++ b/test/foundry/licensing/RightsManager.t.sol @@ -5,8 +5,205 @@ import "forge-std/Test.sol"; import '../utils/BaseTest.sol'; import '../mocks/MockLicensingModule.sol'; import '../mocks/MockTermsProcessor.sol'; -import "contracts/errors/General.sol"; +import '../mocks/RightsManagerHarness.sol'; +import "../mocks/MockERC721.sol"; +import "contracts/errors/General.sol"; + +contract RightsManagerTest is Test, ProxyHelper { + + MockERC721 mockFranchiseRegistry; + RightsManagerHarness rightsManager; + address constant mockEventEmitter = address(0x1234567); + address constant mockLicensingModule = address(0x23445); + address constant licenseHolder = address(0x8073465); + address constant revoker = address(0x123456722222); + + function setUp() public { + mockFranchiseRegistry = new MockERC721("mockFranchiseRegistry", "MFR"); + RightsManagerHarness impl = new RightsManagerHarness(mockEventEmitter, mockLicensingModule, address(mockFranchiseRegistry)); + rightsManager = RightsManagerHarness( + _deployUUPSProxy( + address(impl), + abi.encodeWithSelector( + bytes4( + keccak256( + bytes("initialize(uint256,string,string,string)") + ) + ), + 1, + "name", + "symbol", + "description" + ) + ) + ); + + } + + function test_setup() public { + assertEq(address(rightsManager.FRANCHISE_REGISTRY()), address(mockFranchiseRegistry)); + assertEq(rightsManager.name(), "name"); + assertEq(rightsManager.symbol(), "symbol"); + } + + function test_internal_create_license_rootLicense_notminting() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 0; + MockTermsProcessor termsProcessor = new MockTermsProcessor(); + IERC5218.TermsProcessorConfig memory terms = IERC5218.TermsProcessorConfig({ + processor: termsProcessor, + data: abi.encode("terms") + }); + // TODO test events + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + uint256 licenseId = _verifyLicense(tokenId, termsProcessor); + LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); + vm.expectRevert("ERC721: invalid token ID"); + licenseRegistry.ownerOf(licenseId); + + } + + function test_internal_create_license_rootLicense_minting() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 0; + MockTermsProcessor termsProcessor = new MockTermsProcessor(); + IERC5218.TermsProcessorConfig memory terms = IERC5218.TermsProcessorConfig({ + processor: termsProcessor, + data: abi.encode("terms") + }); + // TODO test events + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + true + ); + uint256 licenseId = _verifyLicense(tokenId, termsProcessor); + LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); + assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); + } + + function test_internal_create_license_nonRootLicense_notminting() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 1; + MockTermsProcessor termsProcessor = new MockTermsProcessor(); + IERC5218.TermsProcessorConfig memory terms = IERC5218.TermsProcessorConfig({ + processor: termsProcessor, + data: abi.encode("terms") + }); + // Mint root + rightsManager.createLicense_exposed( + tokenId, + 0, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + // Mint sublicense + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + uint256 licenseId = _verifyLicense(tokenId, termsProcessor); + LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); + vm.expectRevert("ERC721: invalid token ID"); + licenseRegistry.ownerOf(licenseId); + + } + /** + function test_internal_create_license_nonRootLicense_minting() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 1; + MockTermsProcessor termsProcessor = new MockTermsProcessor(); + IERC5218.TermsProcessorConfig memory terms = IERC5218.TermsProcessorConfig({ + processor: termsProcessor, + data: abi.encode("terms") + }); + // TODO test events + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + uint256 licenseId = _verifyLicense(tokenId, termsProcessor); + LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); + vm.expectRevert("ERC721: invalid token ID"); + licenseRegistry.ownerOf(licenseId); + + } + */ + + function test_revert_internal_createLicense_zeroRevoker() public {} + function test_revert_internal_createLicense_nonExistentId() public {} + function test_revert_internal_createLicense_alreadyHasRootLicense() public {} + function test_revert_internal_createLicense_notOwnerOfParentLicense() public {} + function test_revert_internal_createLicense_inactiveParentLicense() public {} + function test_revert_internal_createLicense_cannotSublicense() public {} + function test_revert_internal_createLicense_commercialTermsMismatch() public {} + function test_revert_internal_createLicense_nonCommercialTermsMismatch() public {} + function test_revert_internal_createLicense_termsProcessorUnsupportedInterface() public {} + + function _verifyLicense(uint256 tokenId, MockTermsProcessor termsProcessor) private returns(uint256) { + uint256 licenseId = rightsManager.getLicenseIdByTokenId(tokenId, true); + assertEq(licenseId, 1); + assertEq(rightsManager.getLicenseTokenId(licenseId), tokenId); + assertEq(rightsManager.getParentLicenseId(licenseId), 0); + assertTrue(rightsManager.isLicenseActive(licenseId)); + assertEq(rightsManager.getLicenseURI(licenseId), "licenseUri"); + (RightsManager.License memory license, address owner) = rightsManager.getLicense(licenseId); + assertEq(owner, licenseHolder, "internal method will not create ipasset, but we minted in RightsManagerHarness"); + assertEq(license.active, true, "license active"); + assertEq(license.canSublicense, true, "license canSublicense"); + assertEq(license.commercial, true, "license commercial"); + assertEq(license.parentLicenseId, 0, "license parentLicenseId"); + assertEq(license.tokenId, tokenId, "license tokenId"); + assertEq(license.revoker, revoker, "license revoker"); + assertEq(license.uri, "licenseUri", "license uri"); + assertEq(address(license.termsProcessor), address(termsProcessor), "license termsProcessor"); + assertEq(license.termsData, abi.encode("terms"), "license termsData"); + return licenseId; + } + +} + +/* contract RightsManagerIPAssetRightsTest is BaseTest { address ipAssetCreator = address(0x999999); @@ -57,8 +254,9 @@ contract RightsManagerIPAssetRightsTest is BaseTest { } function test_revert_licensingModuleNotConfigured() public { assertTrue(false); - }*/ + } } +*/ \ No newline at end of file diff --git a/test/foundry/mocks/MockERC721.sol b/test/foundry/mocks/MockERC721.sol new file mode 100644 index 00000000..47f4f4f0 --- /dev/null +++ b/test/foundry/mocks/MockERC721.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; + +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +contract MockERC721 is ERC721 { + constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {} + + function mint(address to, uint256 tokenId) external { + _mint(to, tokenId); + } +} \ No newline at end of file diff --git a/test/foundry/mocks/MockTermsProcessor.sol b/test/foundry/mocks/MockTermsProcessor.sol index 417c67d1..d012025f 100644 --- a/test/foundry/mocks/MockTermsProcessor.sol +++ b/test/foundry/mocks/MockTermsProcessor.sol @@ -4,13 +4,15 @@ pragma solidity ^0.8.13; import "contracts/modules/licensing/terms/ITermsProcessor.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "forge-std/console.sol"; contract MockTermsProcessor is ITermsProcessor, ERC165 { - bool success = true; + bool private _success = true; function setSuccess(bool value) external { - success = value; + _success = value; + console.log(_success); } function supportsInterface( @@ -28,6 +30,6 @@ contract MockTermsProcessor is ITermsProcessor, ERC165 { function tersmExecutedSuccessfully( bytes calldata ) external view override returns (bool) { - return success; + return _success; } } \ No newline at end of file diff --git a/test/foundry/mocks/RightsManagerHarness.sol b/test/foundry/mocks/RightsManagerHarness.sol new file mode 100644 index 00000000..b9008d44 --- /dev/null +++ b/test/foundry/mocks/RightsManagerHarness.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.13; + +import { IPAssetRegistry } from "contracts/ip-assets/IPAssetRegistry.sol"; + +contract RightsManagerHarness is IPAssetRegistry { + + constructor(address _eventEmitter, address _licensingModule, address _franchiseRegistry) + IPAssetRegistry(_eventEmitter, _licensingModule, _franchiseRegistry) { + + } + + function mint(address to, uint256 tokenId) external { + _mint(to, tokenId); + } + + function createLicense_exposed( + uint256 tokenId, + uint256 parentLicenseId, + address licenseHolder, + string memory uri, + address revoker, + bool commercial, + bool canSublicense, + TermsProcessorConfig memory _terms, + bool inLicenseRegistry + ) external returns(uint256 licenseId) { + return _createLicense( + tokenId, + parentLicenseId, + licenseHolder, + uri, + revoker, + commercial, + canSublicense, + _terms, + inLicenseRegistry + ); + } + +} \ No newline at end of file diff --git a/test/foundry/relationships/LibIPAssetMask.t.sol b/test/foundry/relationships/LibIPAssetMask.t.sol index d22367a5..83ac3c1e 100644 --- a/test/foundry/relationships/LibIPAssetMask.t.sol +++ b/test/foundry/relationships/LibIPAssetMask.t.sol @@ -9,6 +9,7 @@ import { IPAsset, EXTERNAL_ASSET } from "contracts/IPAsset.sol"; import { FranchiseRegistry } from "contracts/FranchiseRegistry.sol"; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { LibIPAssetId } from "contracts/ip-assets/LibIPAssetId.sol"; +import { MockERC721 } from "../mocks/MockERC721.sol"; contract LibIPAssetMaskHarness { @@ -29,15 +30,6 @@ contract LibIPAssetMaskHarness { } } -contract MockERC721 is ERC721 { - constructor(string memory name, string memory symbol) ERC721(name, symbol) {} - - function mint(address to, uint256 tokenId) external { - _mint(to, tokenId); - } -} - - contract LibIPAssetMaskHarnessTest is Test { LibIPAssetMaskHarness public checker; diff --git a/test/foundry/utils/BaseTest.sol b/test/foundry/utils/BaseTest.sol index 2a1d730d..4ef007eb 100644 --- a/test/foundry/utils/BaseTest.sol +++ b/test/foundry/utils/BaseTest.sol @@ -91,6 +91,8 @@ contract BaseTest is Test, ProxyHelper { licenseRegistry = ipAssetRegistry.getLicenseRegistry(); // Configure Licensing for Franchise + nonCommercialTermsProcessor = new MockTermsProcessor(); + commercialTermsProcessor = new MockTermsProcessor(); licensingModule.configureFranchiseLicensing(franchiseId, _getLicensingConfig()); vm.stopPrank(); From 627cd0b4ea037d156220efbc4f000a5f21c8fafa Mon Sep 17 00:00:00 2001 From: Raul Date: Thu, 31 Aug 2023 00:09:28 +0200 Subject: [PATCH 25/42] moar test --- test/foundry/licensing/RightsManager.t.sol | 24 +++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/test/foundry/licensing/RightsManager.t.sol b/test/foundry/licensing/RightsManager.t.sol index 547070b0..104b55a4 100644 --- a/test/foundry/licensing/RightsManager.t.sol +++ b/test/foundry/licensing/RightsManager.t.sol @@ -140,7 +140,7 @@ contract RightsManagerTest is Test, ProxyHelper { licenseRegistry.ownerOf(licenseId); } - /** + function test_internal_create_license_nonRootLicense_minting() public { uint256 tokenId = 1; rightsManager.mint(licenseHolder, tokenId); @@ -150,7 +150,19 @@ contract RightsManagerTest is Test, ProxyHelper { processor: termsProcessor, data: abi.encode("terms") }); - // TODO test events + // Mint root + rightsManager.createLicense_exposed( + tokenId, + 0, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + true + ); + // Mint sublicense rightsManager.createLicense_exposed( tokenId, parentLicenseId, @@ -160,15 +172,13 @@ contract RightsManagerTest is Test, ProxyHelper { true, true, terms, - false + true ); uint256 licenseId = _verifyLicense(tokenId, termsProcessor); LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); - vm.expectRevert("ERC721: invalid token ID"); - licenseRegistry.ownerOf(licenseId); - + assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); } - */ + function test_revert_internal_createLicense_zeroRevoker() public {} function test_revert_internal_createLicense_nonExistentId() public {} From 44a455444cd6869a1b5628abcadc129f82944f63 Mon Sep 17 00:00:00 2001 From: Raul Date: Thu, 31 Aug 2023 00:40:36 +0200 Subject: [PATCH 26/42] wip: prepare new tests --- .../licensing/RightsManager.IPAsset.t.sol | 69 ++++ test/foundry/licensing/RightsManager.t.sol | 358 +++++++++++++----- 2 files changed, 339 insertions(+), 88 deletions(-) create mode 100644 test/foundry/licensing/RightsManager.IPAsset.t.sol diff --git a/test/foundry/licensing/RightsManager.IPAsset.t.sol b/test/foundry/licensing/RightsManager.IPAsset.t.sol new file mode 100644 index 00000000..d115265a --- /dev/null +++ b/test/foundry/licensing/RightsManager.IPAsset.t.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: BUSDL-1.1 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import '../utils/BaseTest.sol'; +import '../mocks/MockLicensingModule.sol'; +import '../mocks/MockTermsProcessor.sol'; +import '../mocks/RightsManagerHarness.sol'; +import "../mocks/MockERC721.sol"; +import "contracts/errors/General.sol"; + + +/* +contract RightsManagerIPAssetRightsTest is BaseTest { + + address ipAssetCreator = address(0x999999); + + function setUp() virtual override public { + deployProcessors = false; + super.setUp(); + } + + function test_setUp() public { + assertEq(licenseRegistry.name(), "Licenses for FranchiseName"); + assertEq(licenseRegistry.symbol(), "slFRN"); + assertEq(address(licenseRegistry.RIGHTS_MANAGER()), address(ipAssetRegistry)); + } + + function test_Rights_NoCommercialRights_RootIPAsset() public { + + // TODO: test for events + vm.prank(ipAssetCreator); + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaurl", address(ipAssetCreator), 0); + uint256 ncrLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, false); + assertEq(ncrLicenseId, 1); + assertEq(ipAssetRegistry.getLicenseTokenId(ncrLicenseId), ipAssetId); + assertEq(ipAssetRegistry.getParentLicenseId(ncrLicenseId), 0); + assertTrue(ipAssetRegistry.isLicenseActive(ncrLicenseId)); + (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(ncrLicenseId); + assertEq(address(ipAssetCreator), owner, "license owner"); + assertEq(license.active, true, "license active"); + assertEq(license.canSublicense, true, "license canSublicense"); + assertEq(license.commercial, false, "license commercial"); + assertEq(license.parentLicenseId, 0, "license parentLicenseId"); + assertEq(license.tokenId, ipAssetId, "license tokenId"); + assertEq(license.revoker, revoker, "license revoker"); + assertEq(license.uri, NON_COMMERCIAL_LICENSE_URI, "license uri"); + assertEq(address(license.termsProcessor), address(nonCommercialTermsProcessor), "license termsProcessor"); + assertEq(license.termsData, abi.encode("nonCommercial"), "license termsData"); + uint256 crLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, true); + assertEq(crLicenseId, 0); + (RightsManager.License memory licenseCr, address ownerCr) = ipAssetRegistry.getLicense(ncrLicenseId); + assertEq(address(0), ownerCr, "commercial license owner"); + } + /* + function test_Rights_CommercialRights_RootIPAsset() public { + assertTrue(false); + } + function test_Rights_CommercialRights_NonRootIPAsset() public { + assertTrue(false); + } + function test_revert_licensingModuleNotConfigured() public { + assertTrue(false); + } + + + +} +*/ \ No newline at end of file diff --git a/test/foundry/licensing/RightsManager.t.sol b/test/foundry/licensing/RightsManager.t.sol index 104b55a4..d3ed8337 100644 --- a/test/foundry/licensing/RightsManager.t.sol +++ b/test/foundry/licensing/RightsManager.t.sol @@ -38,7 +38,6 @@ contract RightsManagerTest is Test, ProxyHelper { ) ) ); - } function test_setup() public { @@ -51,11 +50,7 @@ contract RightsManagerTest is Test, ProxyHelper { uint256 tokenId = 1; rightsManager.mint(licenseHolder, tokenId); uint256 parentLicenseId = 0; - MockTermsProcessor termsProcessor = new MockTermsProcessor(); - IERC5218.TermsProcessorConfig memory terms = IERC5218.TermsProcessorConfig({ - processor: termsProcessor, - data: abi.encode("terms") - }); + (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = _getTermsProcessorConfig(); // TODO test events rightsManager.createLicense_exposed( tokenId, @@ -79,11 +74,7 @@ contract RightsManagerTest is Test, ProxyHelper { uint256 tokenId = 1; rightsManager.mint(licenseHolder, tokenId); uint256 parentLicenseId = 0; - MockTermsProcessor termsProcessor = new MockTermsProcessor(); - IERC5218.TermsProcessorConfig memory terms = IERC5218.TermsProcessorConfig({ - processor: termsProcessor, - data: abi.encode("terms") - }); + (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = _getTermsProcessorConfig(); // TODO test events rightsManager.createLicense_exposed( tokenId, @@ -105,11 +96,7 @@ contract RightsManagerTest is Test, ProxyHelper { uint256 tokenId = 1; rightsManager.mint(licenseHolder, tokenId); uint256 parentLicenseId = 1; - MockTermsProcessor termsProcessor = new MockTermsProcessor(); - IERC5218.TermsProcessorConfig memory terms = IERC5218.TermsProcessorConfig({ - processor: termsProcessor, - data: abi.encode("terms") - }); + (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = _getTermsProcessorConfig(); // Mint root rightsManager.createLicense_exposed( tokenId, @@ -145,11 +132,7 @@ contract RightsManagerTest is Test, ProxyHelper { uint256 tokenId = 1; rightsManager.mint(licenseHolder, tokenId); uint256 parentLicenseId = 1; - MockTermsProcessor termsProcessor = new MockTermsProcessor(); - IERC5218.TermsProcessorConfig memory terms = IERC5218.TermsProcessorConfig({ - processor: termsProcessor, - data: abi.encode("terms") - }); + (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = _getTermsProcessorConfig(); // Mint root rightsManager.createLicense_exposed( tokenId, @@ -178,17 +161,267 @@ contract RightsManagerTest is Test, ProxyHelper { LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); } - - function test_revert_internal_createLicense_zeroRevoker() public {} - function test_revert_internal_createLicense_nonExistentId() public {} - function test_revert_internal_createLicense_alreadyHasRootLicense() public {} - function test_revert_internal_createLicense_notOwnerOfParentLicense() public {} - function test_revert_internal_createLicense_inactiveParentLicense() public {} - function test_revert_internal_createLicense_cannotSublicense() public {} - function test_revert_internal_createLicense_commercialTermsMismatch() public {} - function test_revert_internal_createLicense_nonCommercialTermsMismatch() public {} - function test_revert_internal_createLicense_termsProcessorUnsupportedInterface() public {} + function test_revert_internal_createLicense_zeroRevoker() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 0; + (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + vm.expectRevert(RightsManager.ZeroRevokerAddress.selector); + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + address(0), + true, + true, + terms, + false + ); + } + + function test_revert_internal_createLicense_nonExistentId() public { + uint256 tokenId = 1; + uint256 parentLicenseId = 0; + (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + vm.expectRevert(abi.encodeWithSignature("NonExistentID(uint256)", 1)); + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + } + + function test_revert_internal_createLicense_alreadyHasRootLicense() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 0; + (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + // Mint root + rightsManager.createLicense_exposed( + tokenId, + 0, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + // Mint root again + vm.expectRevert(RightsManager.AlreadyHasRootLicense.selector); + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + } + + function test_revert_internal_createLicense_notOwnerOfParentLicense() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 1; + (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + // Mint root + rightsManager.createLicense_exposed( + tokenId, + 0, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + // Mint sublicense + vm.expectRevert(RightsManager.NotOwnerOfParentLicense.selector); + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + address(0x123456), + "licenseUri", + revoker, + true, + true, + terms, + false + ); + } + + function test_revert_internal_createLicense_inactiveParentLicense() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 1; + (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + // Mint root + rightsManager.createLicense_exposed( + tokenId, + 0, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + + vm.prank(revoker); + rightsManager.revokeLicense(parentLicenseId); + + // Mint sublicense + vm.expectRevert(RightsManager.InactiveParentLicense.selector); + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + } + + function test_revert_internal_createLicense_cannotSublicense() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 1; + (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + // Mint root + rightsManager.createLicense_exposed( + tokenId, + 0, + licenseHolder, + "licenseUri", + revoker, + true, + false, + terms, + false + ); + // Mint sublicense + vm.expectRevert(RightsManager.CannotSublicense.selector); + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + } + + function test_revert_internal_createLicense_commercialTermsMismatch() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 1; + (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + // Mint root + rightsManager.createLicense_exposed( + tokenId, + 0, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + // Mint sublicense + vm.expectRevert(RightsManager.CommercialTermsMismatch.selector); + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + false, + true, + terms, + false + ); + } + + function test_revert_internal_createLicense_nonCommercialTermsMismatch() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + uint256 parentLicenseId = 1; + (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + // Mint root + rightsManager.createLicense_exposed( + tokenId, + 0, + licenseHolder, + "licenseUri", + revoker, + false, + true, + terms, + false + ); + // Mint sublicense + vm.expectRevert(RightsManager.CommercialTermsMismatch.selector); + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + ); + } + + function test_revert_internal_createLicense_termsProcessorUnsupportedInterface() public { + uint256 tokenId = 1; + rightsManager.mint(licenseHolder, tokenId); + + vm.expectRevert(abi.encodeWithSignature("UnsupportedInterface(string)", "ITermsProcessor")); + // Mint root + rightsManager.createLicense_exposed( + tokenId, + 0, + licenseHolder, + "licenseUri", + revoker, + false, + true, + IERC5218.TermsProcessorConfig({ + processor: ITermsProcessor(address(rightsManager)), + data: abi.encode("terms") + }), + false + ); + } + // TODO: could we inspect call to _createLicense with appropiate args? + function test_create_license() public {} + function test_revert_create_license_unauthorized() public {} + // This one we can just call the internal method + function test_create_root_license() public {} + function test_revert_create_root_license_unauthorized() public {} function _verifyLicense(uint256 tokenId, MockTermsProcessor termsProcessor) private returns(uint256) { uint256 licenseId = rightsManager.getLicenseIdByTokenId(tokenId, true); @@ -211,62 +444,11 @@ contract RightsManagerTest is Test, ProxyHelper { return licenseId; } -} - -/* -contract RightsManagerIPAssetRightsTest is BaseTest { - - address ipAssetCreator = address(0x999999); - - function setUp() virtual override public { - deployProcessors = false; - super.setUp(); - } - - function test_setUp() public { - assertEq(licenseRegistry.name(), "Licenses for FranchiseName"); - assertEq(licenseRegistry.symbol(), "slFRN"); - assertEq(address(licenseRegistry.RIGHTS_MANAGER()), address(ipAssetRegistry)); - } - - function test_Rights_NoCommercialRights_RootIPAsset() public { - - // TODO: test for events - vm.prank(ipAssetCreator); - uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaurl", address(ipAssetCreator), 0); - uint256 ncrLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, false); - assertEq(ncrLicenseId, 1); - assertEq(ipAssetRegistry.getLicenseTokenId(ncrLicenseId), ipAssetId); - assertEq(ipAssetRegistry.getParentLicenseId(ncrLicenseId), 0); - assertTrue(ipAssetRegistry.isLicenseActive(ncrLicenseId)); - (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(ncrLicenseId); - assertEq(address(ipAssetCreator), owner, "license owner"); - assertEq(license.active, true, "license active"); - assertEq(license.canSublicense, true, "license canSublicense"); - assertEq(license.commercial, false, "license commercial"); - assertEq(license.parentLicenseId, 0, "license parentLicenseId"); - assertEq(license.tokenId, ipAssetId, "license tokenId"); - assertEq(license.revoker, revoker, "license revoker"); - assertEq(license.uri, NON_COMMERCIAL_LICENSE_URI, "license uri"); - assertEq(address(license.termsProcessor), address(nonCommercialTermsProcessor), "license termsProcessor"); - assertEq(license.termsData, abi.encode("nonCommercial"), "license termsData"); - uint256 crLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, true); - assertEq(crLicenseId, 0); - (RightsManager.License memory licenseCr, address ownerCr) = ipAssetRegistry.getLicense(ncrLicenseId); - assertEq(address(0), ownerCr, "commercial license owner"); - } - /* - function test_Rights_CommercialRights_RootIPAsset() public { - assertTrue(false); - } - function test_Rights_CommercialRights_NonRootIPAsset() public { - assertTrue(false); - } - function test_revert_licensingModuleNotConfigured() public { - assertTrue(false); + function _getTermsProcessorConfig() private returns(IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor){ + termsProcessor = new MockTermsProcessor(); + terms = IERC5218.TermsProcessorConfig({ + processor: termsProcessor, + data: abi.encode("terms") + }); } - - - } -*/ \ No newline at end of file From 8d3b1676e73ed1758b7fb8898f46a3f34a5e1dc5 Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 4 Sep 2023 22:49:10 +0200 Subject: [PATCH 27/42] more tests and fixes --- contracts/ip-assets/IPAssetRegistry.sol | 4 +- .../modules/licensing/LicensingModule.sol | 2 - contracts/modules/licensing/RightsManager.sol | 50 ++- .../licensing/RightsManager.IPAsset.t.sol | 308 ++++++++++++++++-- ...ger.t.sol => RightsManager.Internal.t.sol} | 115 +++---- test/foundry/mocks/MockLicensingModule.sol | 9 + test/foundry/mocks/RightsManagerHarness.sol | 17 +- 7 files changed, 375 insertions(+), 130 deletions(-) rename test/foundry/licensing/{RightsManager.t.sol => RightsManager.Internal.t.sol} (79%) diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index 1b2c9291..a9731af6 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -105,7 +105,7 @@ contract IPAssetRegistry is return ipAssetId; } - function _setNonCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) private { + function _setNonCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) internal { uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, false); _createLicense( ipAssetId, @@ -120,7 +120,7 @@ contract IPAssetRegistry is ); } - function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) private { + function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) internal { uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, true); _createLicense( ipAssetId, diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index d8f7db15..848e6fbe 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -73,7 +73,6 @@ contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { } function _verifyRootLicense(uint256 franchiseId, uint256 rootLicenseId) internal view { - console.log("rootLicenseId", rootLicenseId); if (rootLicenseId != 0) { IERC5218 rightsManager = IERC5218(FRANCHISE_REGISTRY.ipAssetRegistryForId(franchiseId)); if (address(rightsManager) == address(0)) { @@ -81,7 +80,6 @@ contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { // but leaving it in case IPAssetRegistration creation fails somewhow. revert NonExistentFranchise(); } - console.log("rightsManager.isLicenseActive(rootLicenseId)", rightsManager.isLicenseActive(rootLicenseId)); if (!rightsManager.isLicenseActive(rootLicenseId)) { revert RootLicenseNotActive(rootLicenseId); } diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 3b6fec74..bd202577 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -28,6 +28,7 @@ abstract contract RightsManager is error AlreadyHasRootLicense(); error ZeroRevokerAddress(); error NFTHasNoAssociatedLicense(); + error UseCreateFranchiseRootLicenseInstead(); struct License { bool active; @@ -44,7 +45,7 @@ abstract contract RightsManager is struct RightsManagerStorage { mapping(uint256 => License) licenses; // keccack256(commercial, tokenId) => licenseId - mapping(bytes32 => uint256) rootLicensesForTokenId; + mapping(bytes32 => uint256) licensesForTokenId; uint256 licenseCounter; LicenseRegistry licenseRegistry; } @@ -89,16 +90,19 @@ abstract contract RightsManager is } function createLicense( - uint256 _tokenId, + uint256 _tokenId, // Question: should sublicenses be created with a tokenId or just a parentLicenseId? uint256 _parentLicenseId, - address _licenseHolder, // NOTE: MODIFIED ERC-5218, we should ignore this + address _licenseHolder, string memory _uri, address _revoker, bool _commercial, bool _canSublicense, TermsProcessorConfig memory _terms ) external override returns (uint256) { - // TODO: check if msg.sender is allowed to create license + if (_tokenId == FRANCHISE_REGISTRY_OWNED_TOKEN_ID || _parentLicenseId == _UNSET_LICENSE_ID) { + revert UseCreateFranchiseRootLicenseInstead(); + } + if (msg.sender != getLicenseHolder(_parentLicenseId)) revert Unauthorized(); return _createLicense( _tokenId, _parentLicenseId, @@ -157,15 +161,15 @@ abstract contract RightsManager is revert NonExistentID(tokenId); } } - // If this is a root license, check that the tokenId doesn't already have a root license - if (parentLicenseId == _UNSET_LICENSE_ID) { - if ($.rootLicensesForTokenId[keccak256(abi.encode(commercial, tokenId))] != _UNSET_LICENSE_ID) { + // If this is not a LicenseRegsitry license, check that the tokenId doesn't already have a root license + if (!inLicenseRegistry) { + if ($.licensesForTokenId[keccak256(abi.encode(commercial, tokenId))] != _UNSET_LICENSE_ID) { revert AlreadyHasRootLicense(); } } else { // If this is a sublicense, check that this is a valid sublicense License memory parentLicense = $.licenses[parentLicenseId]; - _verifySublicense(parentLicenseId, licenseHolder, commercial, parentLicense); + _verifySublicense(parentLicenseId, commercial, parentLicense); } // Check that the terms are valid _verifyTerms(_terms); @@ -183,16 +187,14 @@ abstract contract RightsManager is termsProcessor: _terms.processor, termsData: _terms.data }); - // Save tokenId => licenseId relationship IF this is a root license - if (parentLicenseId == _UNSET_LICENSE_ID) { - $.rootLicensesForTokenId[ - keccak256(abi.encode(commercial, tokenId)) - ] = licenseId; - } + // Mint the license in the LicenseRegistry if requested. Should not do this for IPAsset Rights, but // the checks on inLicenseRegistry should be done in the calling function if (inLicenseRegistry) { $.licenseRegistry.mint(licenseHolder, licenseId); + } else { + // Save tokenId => licenseId relationship IF this is a root license + $.licensesForTokenId[keccak256(abi.encode(commercial, tokenId))] = licenseId; } // Emit events @@ -236,7 +238,6 @@ abstract contract RightsManager is uint256 licenseId ) public view virtual returns (bool) { // TODO: limit to the tree depth - console.log("isLicenseActive(%s)"); if (licenseId == 0) return false; RightsManagerStorage storage $ = _getRightsManagerStorage(); while (licenseId != 0) { @@ -257,21 +258,13 @@ abstract contract RightsManager is } - function _verifySublicense( - uint256 parentLicenseId, - address licensor, - bool commercial, - License memory parentLicense - ) private view { - if (ownerOf(parentLicenseId) != licensor) revert NotOwnerOfParentLicense(); + function _verifySublicense(uint256 parentLicenseId, bool commercial, License memory parentLicense) private view { if (!parentLicense.active) revert InactiveParentLicense(); if (!parentLicense.canSublicense) revert CannotSublicense(); if (parentLicense.commercial != commercial) revert CommercialTermsMismatch(); } - function getLicense( - uint256 licenseId - ) public view returns (License memory, address holder) { + function getLicense(uint256 licenseId) public view returns (License memory, address holder) { return ( _getRightsManagerStorage().licenses[licenseId], getLicenseHolder(licenseId) @@ -291,8 +284,6 @@ abstract contract RightsManager is i++; } } - } else { - console.log("minting", firstTokenId, batchSize); } super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } @@ -306,7 +297,7 @@ abstract contract RightsManager is RightsManagerStorage storage $ = _getRightsManagerStorage(); // NOTE: We are assuming a revoked Non Commercial License impedes the transfer of the NFT. // Should revoked commercial rights also impede the transfer? - uint256 licenseId = $.rootLicensesForTokenId[keccak256(abi.encode(false, tokenId))]; + uint256 licenseId = $.licensesForTokenId[keccak256(abi.encode(false, tokenId))]; if (licenseId != _UNSET_LICENSE_ID) revert NFTHasNoAssociatedLicense(); // This should not happen, if fired there is a bug somewhere if (isLicenseActive(licenseId)) revert InactiveLicense(); // NOTE: Should we freeze invalid licenses? burn them? emit TransferLicense(licenseId, to); @@ -362,7 +353,7 @@ abstract contract RightsManager is bool _commercial ) public view override returns (uint256) { return - _getRightsManagerStorage().rootLicensesForTokenId[ + _getRightsManagerStorage().licensesForTokenId[ keccak256(abi.encode(_commercial, _tokenId)) ]; } @@ -381,7 +372,6 @@ abstract contract RightsManager is uint256 licenseId, address licenseHolder ) public virtual override(IERC5218) { - console.log("wtf", licenseId, licenseHolder); RightsManagerStorage storage $ = _getRightsManagerStorage(); if (msg.sender != address($.licenseRegistry)) revert Unauthorized(); if (!isLicenseActive(licenseId)) revert InactiveLicense(); diff --git a/test/foundry/licensing/RightsManager.IPAsset.t.sol b/test/foundry/licensing/RightsManager.IPAsset.t.sol index d115265a..10723e1d 100644 --- a/test/foundry/licensing/RightsManager.IPAsset.t.sol +++ b/test/foundry/licensing/RightsManager.IPAsset.t.sol @@ -7,13 +7,12 @@ import '../mocks/MockLicensingModule.sol'; import '../mocks/MockTermsProcessor.sol'; import '../mocks/RightsManagerHarness.sol'; import "../mocks/MockERC721.sol"; -import "contracts/errors/General.sol"; +import "contracts/errors/General.sol"; - -/* contract RightsManagerIPAssetRightsTest is BaseTest { address ipAssetCreator = address(0x999999); + address licenseHolder = address(0x888888); function setUp() virtual override public { deployProcessors = false; @@ -24,46 +23,287 @@ contract RightsManagerIPAssetRightsTest is BaseTest { assertEq(licenseRegistry.name(), "Licenses for FranchiseName"); assertEq(licenseRegistry.symbol(), "slFRN"); assertEq(address(licenseRegistry.RIGHTS_MANAGER()), address(ipAssetRegistry)); + + // Default licensing is root non-commercial with sublicense on, no commercial rights + ILicensingModule.FranchiseConfig memory configResult = licensingModule.getFranchiseConfig(1); + assertEq(configResult.nonCommercialConfig.canSublicense, true, "nonCommercialConfig.canSublicense"); + assertEq(configResult.nonCommercialConfig.franchiseRootLicenseId, 0, "nonCommercialConfig.franchiseRootLicenseId"); + assertEq(address(configResult.nonCommercialTerms.processor), address(nonCommercialTermsProcessor), "nonCommercialTerms.processor"); + assertEq(configResult.nonCommercialTerms.data, abi.encode("nonCommercial"), "nonCommercialTerms.data"); + assertEq(configResult.commercialConfig.canSublicense, false, "commercialConfig.canSublicense"); + assertEq(configResult.commercialConfig.franchiseRootLicenseId, 0, "commercialConfig.franchiseRootLicenseId"); + assertEq(address(configResult.commercialTerms.processor), address(commercialTermsProcessor), "commercialTerms.processor"); + assertEq(configResult.commercialTerms.data, abi.encode("commercial"), "commercialTerms.data"); + assertEq(configResult.rootIpAssetHasCommercialRights, false, "rootIpAssetHasCommercialRights"); + assertEq(configResult.revoker, revoker, "revoker"); + + } + + function test_create_ip_asset_root_noncommercial() public { + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", ipAssetCreator, 0); + bool commercial = false; + uint256 licenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, commercial); + assertEq(licenseId, 1); + assertEq(ipAssetRegistry.getLicenseTokenId(licenseId), ipAssetId); + (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(licenseId); + assertEq(owner, ipAssetCreator); + assertEq(license.active, true); + assertEq(license.canSublicense, true); + assertEq(license.commercial, commercial); + assertEq(license.parentLicenseId, 0); + assertEq(license.tokenId, ipAssetId); + assertEq(license.revoker, revoker); + assertEq(license.uri, "https://noncommercial.license"); + assertEq(address(license.termsProcessor), address(nonCommercialTermsProcessor)); + assertEq(license.termsData, abi.encode("nonCommercial")); + + commercial = true; + licenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, commercial); + assertEq(licenseId, 0); + vm.expectRevert("ERC721: invalid token ID"); + licenseRegistry.ownerOf(licenseId); + } + + function test_create_ip_asset_noncommercial_and_commercial() public { + _configFranchise(true, true, true); + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", ipAssetCreator, 0); + bool commercial = false; + uint256 licenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, commercial); + assertEq(licenseId, 1); + assertEq(ipAssetRegistry.getLicenseTokenId(licenseId), ipAssetId); + (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(licenseId); + assertEq(owner, ipAssetCreator); + assertEq(license.active, true); + assertEq(license.canSublicense, true); + assertEq(license.commercial, commercial); + assertEq(license.parentLicenseId, 0); + assertEq(license.tokenId, ipAssetId); + assertEq(license.revoker, revoker); + assertEq(license.uri, "https://noncommercial.license"); + assertEq(address(license.termsProcessor), address(nonCommercialTermsProcessor)); + assertEq(license.termsData, abi.encode("nonCommercial")); + vm.expectRevert("ERC721: invalid token ID"); + licenseRegistry.ownerOf(licenseId); + + commercial = true; + uint256 commercialLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, commercial); + assertEq(commercialLicenseId, 2); + assertEq(ipAssetRegistry.getLicenseTokenId(commercialLicenseId), ipAssetId); + (license, owner) = ipAssetRegistry.getLicense(commercialLicenseId); + assertEq(owner, ipAssetCreator); + assertEq(license.active, true); + assertEq(license.canSublicense, true); + assertEq(license.commercial, commercial); + assertEq(license.parentLicenseId, 0); + assertEq(license.tokenId, ipAssetId); + assertEq(license.revoker, revoker); + assertEq(license.uri, "https://commercial.license"); + assertEq(address(license.termsProcessor), address(commercialTermsProcessor)); + assertEq(license.termsData, abi.encode("commercial")); + + vm.expectRevert("ERC721: invalid token ID"); + licenseRegistry.ownerOf(commercialLicenseId); + + } + + function test_create_derivative_ip_asset_from_non_commercial() public { + uint256 rootIpAsset = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", ipAssetCreator, 0); + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name derv", "description deriv", "mediaUrl deriv", ipAssetCreator, rootIpAsset); + + bool commercial = false; + uint256 licenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, commercial); + + vm.expectRevert("ERC721: invalid token ID"); + licenseRegistry.ownerOf(licenseId); + assertEq(licenseId, 2); + assertEq(ipAssetRegistry.getLicenseTokenId(licenseId), ipAssetId); + (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(licenseId); + assertEq(owner, ipAssetCreator); + assertEq(license.active, true); + assertEq(license.canSublicense, true); + assertEq(license.commercial, commercial); + assertEq(license.parentLicenseId, 1); + assertEq(license.tokenId, ipAssetId); + assertEq(license.revoker, revoker); + assertEq(license.uri, "https://noncommercial.license"); + assertEq(address(license.termsProcessor), address(nonCommercialTermsProcessor)); + assertEq(license.termsData, abi.encode("nonCommercial")); + + commercial = true; + licenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, commercial); + assertEq(licenseId, 0); } - function test_Rights_NoCommercialRights_RootIPAsset() public { + function test_create_derivative_ip_asset_from_commercial() public { + _configFranchise(true, true, true); + uint256 rootIpAsset = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", ipAssetCreator, 0); + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name derv", "description deriv", "mediaUrl deriv", ipAssetCreator, rootIpAsset); + + bool commercial = false; + uint256 licenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, commercial); - // TODO: test for events + vm.expectRevert("ERC721: invalid token ID"); + licenseRegistry.ownerOf(licenseId); + + assertEq(licenseId, 3); + assertEq(ipAssetRegistry.getLicenseTokenId(licenseId), ipAssetId); + (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(licenseId); + assertEq(owner, ipAssetCreator); + assertEq(license.active, true); + assertEq(license.canSublicense, true); + assertEq(license.commercial, commercial); + assertEq(license.parentLicenseId, 1); + assertEq(license.tokenId, ipAssetId); + assertEq(license.revoker, revoker); + assertEq(license.uri, "https://noncommercial.license"); + assertEq(address(license.termsProcessor), address(nonCommercialTermsProcessor)); + assertEq(license.termsData, abi.encode("nonCommercial")); + + commercial = true; + licenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, commercial); + assertEq(licenseId, 0); + + } + + function test_create_license() public { + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", ipAssetCreator, 0); + uint256 parentLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, false); + bool commercial = false; vm.prank(ipAssetCreator); - uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaurl", address(ipAssetCreator), 0); - uint256 ncrLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, false); - assertEq(ncrLicenseId, 1); - assertEq(ipAssetRegistry.getLicenseTokenId(ncrLicenseId), ipAssetId); - assertEq(ipAssetRegistry.getParentLicenseId(ncrLicenseId), 0); - assertTrue(ipAssetRegistry.isLicenseActive(ncrLicenseId)); - (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(ncrLicenseId); - assertEq(address(ipAssetCreator), owner, "license owner"); - assertEq(license.active, true, "license active"); - assertEq(license.canSublicense, true, "license canSublicense"); - assertEq(license.commercial, false, "license commercial"); - assertEq(license.parentLicenseId, 0, "license parentLicenseId"); - assertEq(license.tokenId, ipAssetId, "license tokenId"); - assertEq(license.revoker, revoker, "license revoker"); - assertEq(license.uri, NON_COMMERCIAL_LICENSE_URI, "license uri"); - assertEq(address(license.termsProcessor), address(nonCommercialTermsProcessor), "license termsProcessor"); - assertEq(license.termsData, abi.encode("nonCommercial"), "license termsData"); - uint256 crLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, true); - assertEq(crLicenseId, 0); - (RightsManager.License memory licenseCr, address ownerCr) = ipAssetRegistry.getLicense(ncrLicenseId); - assertEq(address(0), ownerCr, "commercial license owner"); + uint256 licenseId = ipAssetRegistry.createLicense( + ipAssetId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + false, + false, + IERC5218.TermsProcessorConfig({ + processor: nonCommercialTermsProcessor, + data: abi.encode("terms") + }) + ); + (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(licenseId); + assertEq(owner, licenseHolder); + assertEq(license.active, true); + assertEq(license.canSublicense, false); + assertEq(license.commercial, commercial); + assertEq(license.parentLicenseId, parentLicenseId); + assertEq(license.tokenId, ipAssetId); + assertEq(license.revoker, revoker); + assertEq(license.uri, "licenseUri"); + assertEq(address(license.termsProcessor), address(nonCommercialTermsProcessor)); + assertEq(license.termsData, abi.encode("terms")); + assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); + } - /* - function test_Rights_CommercialRights_RootIPAsset() public { - assertTrue(false); + + function test_revert_create_license_unauthorized() public { + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", ipAssetCreator, 0); + uint256 parentLicenseId = 1; + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + vm.expectRevert(Unauthorized.selector); + ipAssetRegistry.createLicense( + ipAssetId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + false, + false, + terms + ); } - function test_Rights_CommercialRights_NonRootIPAsset() public { - assertTrue(false); + + function test_revert_create_license_franchise_owned_tokenId() public { + uint256 tokenId = ipAssetRegistry.FRANCHISE_REGISTRY_OWNED_TOKEN_ID(); + uint256 parentLicenseId = 1; + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + vm.startPrank(franchiseOwner); + vm.expectRevert(RightsManager.UseCreateFranchiseRootLicenseInstead.selector); + ipAssetRegistry.createLicense( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + false, + false, + terms + ); + vm.stopPrank(); } - function test_revert_licensingModuleNotConfigured() public { - assertTrue(false); + + function test_revert_create_license_unset_parent() public { + uint256 tokenId = 1; + uint256 parentLicenseId = 0; + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + vm.startPrank(franchiseOwner); + vm.expectRevert(RightsManager.UseCreateFranchiseRootLicenseInstead.selector); + ipAssetRegistry.createLicense( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + false, + false, + terms + ); + vm.stopPrank(); } + // This one we can just call the internal method + function test_create_root_license() public { + + } + function test_revert_create_root_license_unauthorized() public {} + function _verifyLicense(uint256 tokenId, MockTermsProcessor termsProcessor) private returns(uint256) { + uint256 licenseId = ipAssetRegistry.getLicenseIdByTokenId(tokenId, true); + assertEq(licenseId, 1); + assertEq(ipAssetRegistry.getLicenseTokenId(licenseId), tokenId); + assertEq(ipAssetRegistry.getParentLicenseId(licenseId), 0); + assertTrue(ipAssetRegistry.isLicenseActive(licenseId)); + assertEq(ipAssetRegistry.getLicenseURI(licenseId), "licenseUri"); + (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(licenseId); + assertEq(owner, licenseHolder, "internal method will not create ipasset, but we mockMinted in RightsManagerHarness"); + assertEq(license.active, true, "license active"); + assertEq(license.canSublicense, true, "license canSublicense"); + assertEq(license.commercial, true, "license commercial"); + assertEq(license.parentLicenseId, 0, "license parentLicenseId"); + assertEq(license.tokenId, tokenId, "license tokenId"); + assertEq(license.revoker, revoker, "license revoker"); + assertEq(license.uri, "licenseUri", "license uri"); + assertEq(address(license.termsProcessor), address(termsProcessor), "license termsProcessor"); + assertEq(license.termsData, abi.encode("terms"), "license termsData"); + return licenseId; + } + + function _configFranchise(bool sublicenseCommercial, bool sublicenseNonCommercial, bool rootIpAssetHasCommercialRights) private { + ILicensingModule.FranchiseConfig memory config = ILicensingModule.FranchiseConfig({ + nonCommercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: sublicenseNonCommercial, + franchiseRootLicenseId: 0 + }), + nonCommercialTerms: IERC5218.TermsProcessorConfig({ + processor: nonCommercialTermsProcessor, + data: abi.encode("nonCommercial") + }), + commercialConfig: ILicensingModule.IpAssetConfig({ + canSublicense: sublicenseCommercial, + franchiseRootLicenseId: 0 + }), + commercialTerms: IERC5218.TermsProcessorConfig({ + processor: commercialTermsProcessor, + data: abi.encode("commercial") + }), + rootIpAssetHasCommercialRights: rootIpAssetHasCommercialRights, + revoker: revoker, + commercialLicenseUri: "https://commercial.license" + }); + vm.prank(franchiseOwner); + licensingModule.configureFranchiseLicensing(1, config); + } } -*/ \ No newline at end of file diff --git a/test/foundry/licensing/RightsManager.t.sol b/test/foundry/licensing/RightsManager.Internal.t.sol similarity index 79% rename from test/foundry/licensing/RightsManager.t.sol rename to test/foundry/licensing/RightsManager.Internal.t.sol index d3ed8337..6806a0a3 100644 --- a/test/foundry/licensing/RightsManager.t.sol +++ b/test/foundry/licensing/RightsManager.Internal.t.sol @@ -10,7 +10,7 @@ import "../mocks/MockERC721.sol"; import "contracts/errors/General.sol"; -contract RightsManagerTest is Test, ProxyHelper { +contract RightsManagerInternalTest is Test, ProxyHelper { MockERC721 mockFranchiseRegistry; RightsManagerHarness rightsManager; @@ -46,11 +46,11 @@ contract RightsManagerTest is Test, ProxyHelper { assertEq(rightsManager.symbol(), "symbol"); } - function test_internal_create_license_rootLicense_notminting() public { + function test_internal_create_license_rootLicense_notmockMinting() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 0; - (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = _getTermsProcessorConfig(); + (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = LibMockFranchiseConfig.getTermsProcessorConfig(); // TODO test events rightsManager.createLicense_exposed( tokenId, @@ -70,11 +70,11 @@ contract RightsManagerTest is Test, ProxyHelper { } - function test_internal_create_license_rootLicense_minting() public { + function test_internal_create_license_rootLicense_mockMinting() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 0; - (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = _getTermsProcessorConfig(); + (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = LibMockFranchiseConfig.getTermsProcessorConfig(); // TODO test events rightsManager.createLicense_exposed( tokenId, @@ -92,12 +92,12 @@ contract RightsManagerTest is Test, ProxyHelper { assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); } - function test_internal_create_license_nonRootLicense_notminting() public { + function test_internal_create_license_nonRootLicense_notmockMinting() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 1; - (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = _getTermsProcessorConfig(); - // Mint root + (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = LibMockFranchiseConfig.getTermsProcessorConfig(); + // mockMint root rightsManager.createLicense_exposed( tokenId, 0, @@ -109,7 +109,7 @@ contract RightsManagerTest is Test, ProxyHelper { terms, false ); - // Mint sublicense + // mockMint sublicense rightsManager.createLicense_exposed( tokenId, parentLicenseId, @@ -128,12 +128,12 @@ contract RightsManagerTest is Test, ProxyHelper { } - function test_internal_create_license_nonRootLicense_minting() public { + function test_internal_create_license_nonRootLicense_mockMinting() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 1; - (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = _getTermsProcessorConfig(); - // Mint root + (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = LibMockFranchiseConfig.getTermsProcessorConfig(); + // mockMint root rightsManager.createLicense_exposed( tokenId, 0, @@ -145,7 +145,7 @@ contract RightsManagerTest is Test, ProxyHelper { terms, true ); - // Mint sublicense + // mockMint sublicense rightsManager.createLicense_exposed( tokenId, parentLicenseId, @@ -164,9 +164,9 @@ contract RightsManagerTest is Test, ProxyHelper { function test_revert_internal_createLicense_zeroRevoker() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 0; - (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); vm.expectRevert(RightsManager.ZeroRevokerAddress.selector); rightsManager.createLicense_exposed( tokenId, @@ -184,7 +184,7 @@ contract RightsManagerTest is Test, ProxyHelper { function test_revert_internal_createLicense_nonExistentId() public { uint256 tokenId = 1; uint256 parentLicenseId = 0; - (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); vm.expectRevert(abi.encodeWithSignature("NonExistentID(uint256)", 1)); rightsManager.createLicense_exposed( tokenId, @@ -201,10 +201,10 @@ contract RightsManagerTest is Test, ProxyHelper { function test_revert_internal_createLicense_alreadyHasRootLicense() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 0; - (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); - // Mint root + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + // mockMint root rightsManager.createLicense_exposed( tokenId, 0, @@ -216,7 +216,7 @@ contract RightsManagerTest is Test, ProxyHelper { terms, false ); - // Mint root again + // mockMint root again vm.expectRevert(RightsManager.AlreadyHasRootLicense.selector); rightsManager.createLicense_exposed( tokenId, @@ -233,10 +233,10 @@ contract RightsManagerTest is Test, ProxyHelper { function test_revert_internal_createLicense_notOwnerOfParentLicense() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 1; - (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); - // Mint root + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + // mockMint root rightsManager.createLicense_exposed( tokenId, 0, @@ -248,7 +248,7 @@ contract RightsManagerTest is Test, ProxyHelper { terms, false ); - // Mint sublicense + // mockMint sublicense vm.expectRevert(RightsManager.NotOwnerOfParentLicense.selector); rightsManager.createLicense_exposed( tokenId, @@ -265,10 +265,10 @@ contract RightsManagerTest is Test, ProxyHelper { function test_revert_internal_createLicense_inactiveParentLicense() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 1; - (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); - // Mint root + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + // mockMint root rightsManager.createLicense_exposed( tokenId, 0, @@ -284,7 +284,7 @@ contract RightsManagerTest is Test, ProxyHelper { vm.prank(revoker); rightsManager.revokeLicense(parentLicenseId); - // Mint sublicense + // mockMint sublicense vm.expectRevert(RightsManager.InactiveParentLicense.selector); rightsManager.createLicense_exposed( tokenId, @@ -301,10 +301,10 @@ contract RightsManagerTest is Test, ProxyHelper { function test_revert_internal_createLicense_cannotSublicense() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 1; - (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); - // Mint root + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + // mockMint root rightsManager.createLicense_exposed( tokenId, 0, @@ -316,7 +316,7 @@ contract RightsManagerTest is Test, ProxyHelper { terms, false ); - // Mint sublicense + // mockMint sublicense vm.expectRevert(RightsManager.CannotSublicense.selector); rightsManager.createLicense_exposed( tokenId, @@ -333,10 +333,10 @@ contract RightsManagerTest is Test, ProxyHelper { function test_revert_internal_createLicense_commercialTermsMismatch() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 1; - (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); - // Mint root + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + // mockMint root rightsManager.createLicense_exposed( tokenId, 0, @@ -348,7 +348,7 @@ contract RightsManagerTest is Test, ProxyHelper { terms, false ); - // Mint sublicense + // mockMint sublicense vm.expectRevert(RightsManager.CommercialTermsMismatch.selector); rightsManager.createLicense_exposed( tokenId, @@ -365,10 +365,10 @@ contract RightsManagerTest is Test, ProxyHelper { function test_revert_internal_createLicense_nonCommercialTermsMismatch() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); uint256 parentLicenseId = 1; - (IERC5218.TermsProcessorConfig memory terms,) = _getTermsProcessorConfig(); - // Mint root + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + // mockMint root rightsManager.createLicense_exposed( tokenId, 0, @@ -380,7 +380,7 @@ contract RightsManagerTest is Test, ProxyHelper { terms, false ); - // Mint sublicense + // mockMint sublicense vm.expectRevert(RightsManager.CommercialTermsMismatch.selector); rightsManager.createLicense_exposed( tokenId, @@ -397,10 +397,10 @@ contract RightsManagerTest is Test, ProxyHelper { function test_revert_internal_createLicense_termsProcessorUnsupportedInterface() public { uint256 tokenId = 1; - rightsManager.mint(licenseHolder, tokenId); + rightsManager.mockMint(licenseHolder, tokenId); vm.expectRevert(abi.encodeWithSignature("UnsupportedInterface(string)", "ITermsProcessor")); - // Mint root + // mockMint root rightsManager.createLicense_exposed( tokenId, 0, @@ -416,12 +416,13 @@ contract RightsManagerTest is Test, ProxyHelper { false ); } - // TODO: could we inspect call to _createLicense with appropiate args? - function test_create_license() public {} - function test_revert_create_license_unauthorized() public {} - // This one we can just call the internal method - function test_create_root_license() public {} - function test_revert_create_root_license_unauthorized() public {} + + function test_revert_unknown_license() public { + vm.expectRevert("ERC721: invalid token ID"); + rightsManager.getLicense(222); + vm.expectRevert("ERC721: invalid token ID"); + rightsManager.getLicense(0); + } function _verifyLicense(uint256 tokenId, MockTermsProcessor termsProcessor) private returns(uint256) { uint256 licenseId = rightsManager.getLicenseIdByTokenId(tokenId, true); @@ -431,7 +432,7 @@ contract RightsManagerTest is Test, ProxyHelper { assertTrue(rightsManager.isLicenseActive(licenseId)); assertEq(rightsManager.getLicenseURI(licenseId), "licenseUri"); (RightsManager.License memory license, address owner) = rightsManager.getLicense(licenseId); - assertEq(owner, licenseHolder, "internal method will not create ipasset, but we minted in RightsManagerHarness"); + assertEq(owner, licenseHolder, "internal method will not create ipasset, but we mockMinted in RightsManagerHarness"); assertEq(license.active, true, "license active"); assertEq(license.canSublicense, true, "license canSublicense"); assertEq(license.commercial, true, "license commercial"); @@ -443,12 +444,4 @@ contract RightsManagerTest is Test, ProxyHelper { assertEq(license.termsData, abi.encode("terms"), "license termsData"); return licenseId; } - - function _getTermsProcessorConfig() private returns(IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor){ - termsProcessor = new MockTermsProcessor(); - terms = IERC5218.TermsProcessorConfig({ - processor: termsProcessor, - data: abi.encode("terms") - }); - } } diff --git a/test/foundry/mocks/MockLicensingModule.sol b/test/foundry/mocks/MockLicensingModule.sol index 2ac8d9a6..05f7f332 100644 --- a/test/foundry/mocks/MockLicensingModule.sol +++ b/test/foundry/mocks/MockLicensingModule.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.19; import {ILicensingModule} from "contracts/modules/licensing/ILicensingModule.sol"; import {IERC5218} from "contracts/modules/licensing/IERC5218.sol"; import {ITermsProcessor} from "contracts/modules/licensing/terms/ITermsProcessor.sol"; +import {MockTermsProcessor} from "./MockTermsProcessor.sol"; library LibMockFranchiseConfig { function getMockFranchiseConfig() @@ -33,6 +34,14 @@ library LibMockFranchiseConfig { commercialLicenseUri: "" }); } + + function getTermsProcessorConfig() public returns(IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor){ + termsProcessor = new MockTermsProcessor(); + terms = IERC5218.TermsProcessorConfig({ + processor: termsProcessor, + data: abi.encode("terms") + }); + } } contract MockLicensingModule is ILicensingModule { diff --git a/test/foundry/mocks/RightsManagerHarness.sol b/test/foundry/mocks/RightsManagerHarness.sol index b9008d44..7112614f 100644 --- a/test/foundry/mocks/RightsManagerHarness.sol +++ b/test/foundry/mocks/RightsManagerHarness.sol @@ -2,6 +2,10 @@ pragma solidity ^0.8.13; import { IPAssetRegistry } from "contracts/ip-assets/IPAssetRegistry.sol"; +import { IPAsset } from "contracts/IPAsset.sol"; +import { ILicensingModule } from "contracts/modules/licensing/ILicensingModule.sol"; +import { IERC5218 } from "contracts/modules/licensing/IERC5218.sol"; +import { ITermsProcessor } from "contracts/modules/licensing/terms/ITermsProcessor.sol"; contract RightsManagerHarness is IPAssetRegistry { @@ -10,10 +14,21 @@ contract RightsManagerHarness is IPAssetRegistry { } - function mint(address to, uint256 tokenId) external { + function mockMint(address to, uint256 tokenId) external { _mint(to, tokenId); } + function mockMintWithRights(address to, uint256 tokenId, address revoker) external { + _mint(to, tokenId); + _setNonCommercialRights(tokenId, 0, to, revoker, ILicensingModule.IpAssetConfig({ + canSublicense: true, + franchiseRootLicenseId: 0 + }), IERC5218.TermsProcessorConfig({ + processor: ITermsProcessor(address(0)), + data: "" + })); + } + function createLicense_exposed( uint256 tokenId, uint256 parentLicenseId, From 731fcfe27a6535739022815801d7f152f8546b83 Mon Sep 17 00:00:00 2001 From: Raul Date: Tue, 5 Sep 2023 00:00:04 +0200 Subject: [PATCH 28/42] fix tests --- .../modules/licensing/LicenseRegistry.sol | 1 - contracts/modules/licensing/RightsManager.sol | 14 ++-- .../licensing/RightsManager.IPAsset.t.sol | 63 ++++++++++++++- .../licensing/RightsManager.Internal.t.sol | 76 +++++++++++-------- 4 files changed, 110 insertions(+), 44 deletions(-) diff --git a/contracts/modules/licensing/LicenseRegistry.sol b/contracts/modules/licensing/LicenseRegistry.sol index f98b99eb..5e2297d2 100644 --- a/contracts/modules/licensing/LicenseRegistry.sol +++ b/contracts/modules/licensing/LicenseRegistry.sol @@ -37,7 +37,6 @@ contract LicenseRegistry is ERC721 { uint256 firstTokenId, uint256 batchSize ) internal virtual override { - console.log("_beforeTokenTransfer LicenseRegistry"); // Minting has already been checked by the RightsManager. if (from != address(0)) { RIGHTS_MANAGER.transferSublicense(firstTokenId, to); diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index bd202577..dd5a6fa4 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -166,10 +166,13 @@ abstract contract RightsManager is if ($.licensesForTokenId[keccak256(abi.encode(commercial, tokenId))] != _UNSET_LICENSE_ID) { revert AlreadyHasRootLicense(); } - } else { + } else if(tokenId != FRANCHISE_REGISTRY_OWNED_TOKEN_ID && parentLicenseId != _UNSET_LICENSE_ID) { // If this is a sublicense, check that this is a valid sublicense License memory parentLicense = $.licenses[parentLicenseId]; - _verifySublicense(parentLicenseId, commercial, parentLicense); + if (!parentLicense.active) revert InactiveParentLicense(); + if (!parentLicense.canSublicense) revert CannotSublicense(); + if (parentLicense.commercial != commercial) revert CommercialTermsMismatch(); + if (getLicenseHolder(parentLicenseId) != licenseHolder) revert NotOwnerOfParentLicense(); } // Check that the terms are valid _verifyTerms(_terms); @@ -257,13 +260,6 @@ abstract contract RightsManager is return license.active && license.termsProcessor.tersmExecutedSuccessfully(license.termsData); } - - function _verifySublicense(uint256 parentLicenseId, bool commercial, License memory parentLicense) private view { - if (!parentLicense.active) revert InactiveParentLicense(); - if (!parentLicense.canSublicense) revert CannotSublicense(); - if (parentLicense.commercial != commercial) revert CommercialTermsMismatch(); - } - function getLicense(uint256 licenseId) public view returns (License memory, address holder) { return ( _getRightsManagerStorage().licenses[licenseId], diff --git a/test/foundry/licensing/RightsManager.IPAsset.t.sol b/test/foundry/licensing/RightsManager.IPAsset.t.sol index 10723e1d..204359f6 100644 --- a/test/foundry/licensing/RightsManager.IPAsset.t.sol +++ b/test/foundry/licensing/RightsManager.IPAsset.t.sol @@ -166,10 +166,10 @@ contract RightsManagerIPAssetRightsTest is BaseTest { } function test_create_license() public { - uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", ipAssetCreator, 0); + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", licenseHolder, 0); uint256 parentLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, false); bool commercial = false; - vm.prank(ipAssetCreator); + vm.prank(licenseHolder); uint256 licenseId = ipAssetRegistry.createLicense( ipAssetId, parentLicenseId, @@ -253,11 +253,66 @@ contract RightsManagerIPAssetRightsTest is BaseTest { vm.stopPrank(); } + function test_revert_create_license_terms_mismatch() public { + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", ipAssetCreator, 0); + uint256 parentLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, false); + bool commercial = true; + vm.expectRevert(RightsManager.CommercialTermsMismatch.selector); + vm.prank(ipAssetCreator); + ipAssetRegistry.createLicense( + ipAssetId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + commercial, + false, + IERC5218.TermsProcessorConfig({ + processor: nonCommercialTermsProcessor, + data: abi.encode("terms") + }) + ); + } + // This one we can just call the internal method function test_create_root_license() public { - + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + vm.prank(franchiseOwner); + uint256 licenseId = ipAssetRegistry.createFranchiseRootLicense( + 1, + franchiseOwner, + "licenseUri", + revoker, + true, + true, + terms + ); + (RightsManager.License memory license, address owner) = ipAssetRegistry.getLicense(licenseId); + assertEq(owner, franchiseOwner); + assertEq(license.active, true); + assertEq(license.canSublicense, true); + assertEq(license.commercial, true); + assertEq(license.parentLicenseId, 0); + assertEq(license.tokenId, ipAssetRegistry.FRANCHISE_REGISTRY_OWNED_TOKEN_ID()); + assertEq(license.revoker, revoker); + assertEq(license.uri, "licenseUri"); + assertEq(address(license.termsProcessor), address(terms.processor)); + assertEq(license.termsData, abi.encode("terms")); + } + + function test_revert_create_root_license_unauthorized() public { + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + vm.expectRevert(Unauthorized.selector); + ipAssetRegistry.createFranchiseRootLicense( + 1, + franchiseOwner, + "licenseUri", + revoker, + true, + true, + terms + ); } - function test_revert_create_root_license_unauthorized() public {} function _verifyLicense(uint256 tokenId, MockTermsProcessor termsProcessor) private returns(uint256) { uint256 licenseId = ipAssetRegistry.getLicenseIdByTokenId(tokenId, true); diff --git a/test/foundry/licensing/RightsManager.Internal.t.sol b/test/foundry/licensing/RightsManager.Internal.t.sol index 6806a0a3..d817a4f1 100644 --- a/test/foundry/licensing/RightsManager.Internal.t.sol +++ b/test/foundry/licensing/RightsManager.Internal.t.sol @@ -52,7 +52,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { uint256 parentLicenseId = 0; (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = LibMockFranchiseConfig.getTermsProcessorConfig(); // TODO test events - rightsManager.createLicense_exposed( + uint256 licenseId = rightsManager.createLicense_exposed( tokenId, parentLicenseId, licenseHolder, @@ -63,7 +63,8 @@ contract RightsManagerInternalTest is Test, ProxyHelper { terms, false ); - uint256 licenseId = _verifyLicense(tokenId, termsProcessor); + assertEq(licenseId, rightsManager.getLicenseIdByTokenId(tokenId, true)); + _verifyLicense(licenseId, parentLicenseId, tokenId, termsProcessor, true, true); LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); vm.expectRevert("ERC721: invalid token ID"); licenseRegistry.ownerOf(licenseId); @@ -76,7 +77,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { uint256 parentLicenseId = 0; (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = LibMockFranchiseConfig.getTermsProcessorConfig(); // TODO test events - rightsManager.createLicense_exposed( + uint256 licenseId = rightsManager.createLicense_exposed( tokenId, parentLicenseId, licenseHolder, @@ -85,20 +86,21 @@ contract RightsManagerInternalTest is Test, ProxyHelper { true, true, terms, - true + false ); - uint256 licenseId = _verifyLicense(tokenId, termsProcessor); + assertEq(licenseId, rightsManager.getLicenseIdByTokenId(tokenId, true), "wtf"); + _verifyLicense(licenseId, parentLicenseId, tokenId, termsProcessor, true, true); LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); - assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); + vm.expectRevert("ERC721: invalid token ID"); + licenseRegistry.ownerOf(licenseId); } function test_internal_create_license_nonRootLicense_notmockMinting() public { uint256 tokenId = 1; rightsManager.mockMint(licenseHolder, tokenId); - uint256 parentLicenseId = 1; (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = LibMockFranchiseConfig.getTermsProcessorConfig(); // mockMint root - rightsManager.createLicense_exposed( + uint256 parentLicenseId = rightsManager.createLicense_exposed( tokenId, 0, licenseHolder, @@ -109,9 +111,12 @@ contract RightsManagerInternalTest is Test, ProxyHelper { terms, false ); + // mockMint derivative ip + uint256 nextTokenId = tokenId + 1; + rightsManager.mockMint(licenseHolder, nextTokenId); // mockMint sublicense - rightsManager.createLicense_exposed( - tokenId, + uint256 licenseId = rightsManager.createLicense_exposed( + nextTokenId, parentLicenseId, licenseHolder, "licenseUri", @@ -121,7 +126,8 @@ contract RightsManagerInternalTest is Test, ProxyHelper { terms, false ); - uint256 licenseId = _verifyLicense(tokenId, termsProcessor); + assertEq(licenseId, rightsManager.getLicenseIdByTokenId(nextTokenId, true)); + _verifyLicense(licenseId, parentLicenseId, nextTokenId, termsProcessor, true, true); LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); vm.expectRevert("ERC721: invalid token ID"); licenseRegistry.ownerOf(licenseId); @@ -131,10 +137,9 @@ contract RightsManagerInternalTest is Test, ProxyHelper { function test_internal_create_license_nonRootLicense_mockMinting() public { uint256 tokenId = 1; rightsManager.mockMint(licenseHolder, tokenId); - uint256 parentLicenseId = 1; (IERC5218.TermsProcessorConfig memory terms, MockTermsProcessor termsProcessor) = LibMockFranchiseConfig.getTermsProcessorConfig(); // mockMint root - rightsManager.createLicense_exposed( + uint256 parentLicenseId = rightsManager.createLicense_exposed( tokenId, 0, licenseHolder, @@ -146,7 +151,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { true ); // mockMint sublicense - rightsManager.createLicense_exposed( + uint256 licenseId = rightsManager.createLicense_exposed( tokenId, parentLicenseId, licenseHolder, @@ -157,7 +162,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { terms, true ); - uint256 licenseId = _verifyLicense(tokenId, termsProcessor); + _verifyLicense(licenseId, parentLicenseId, tokenId, termsProcessor, true, true); LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); } @@ -259,7 +264,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { true, true, terms, - false + true ); } @@ -295,7 +300,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { true, true, terms, - false + true ); } @@ -327,7 +332,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { true, true, terms, - false + true ); } @@ -359,7 +364,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { false, true, terms, - false + true ); } @@ -391,8 +396,22 @@ contract RightsManagerInternalTest is Test, ProxyHelper { true, true, terms, - false + true ); + // Uncomment this if we ever allow commercial sublicenses attached to children tokenIds + /* + vm.expectRevert(RightsManager.CommercialTermsMismatch.selector); + rightsManager.createLicense_exposed( + tokenId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + true, + true, + terms, + false + );*/ } function test_revert_internal_createLicense_termsProcessorUnsupportedInterface() public { @@ -424,24 +443,21 @@ contract RightsManagerInternalTest is Test, ProxyHelper { rightsManager.getLicense(0); } - function _verifyLicense(uint256 tokenId, MockTermsProcessor termsProcessor) private returns(uint256) { - uint256 licenseId = rightsManager.getLicenseIdByTokenId(tokenId, true); - assertEq(licenseId, 1); - assertEq(rightsManager.getLicenseTokenId(licenseId), tokenId); - assertEq(rightsManager.getParentLicenseId(licenseId), 0); - assertTrue(rightsManager.isLicenseActive(licenseId)); + function _verifyLicense(uint256 licenseId, uint256 parentLicenseId, uint256 tokenId, MockTermsProcessor termsProcessor, bool canSublicense, bool commercial) private { + assertEq(rightsManager.getLicenseTokenId(licenseId), tokenId, "license tokenId"); + assertEq(rightsManager.getParentLicenseId(licenseId), parentLicenseId, "license parentLicenseId"); + assertTrue(rightsManager.isLicenseActive(licenseId), "license active"); assertEq(rightsManager.getLicenseURI(licenseId), "licenseUri"); (RightsManager.License memory license, address owner) = rightsManager.getLicense(licenseId); assertEq(owner, licenseHolder, "internal method will not create ipasset, but we mockMinted in RightsManagerHarness"); assertEq(license.active, true, "license active"); - assertEq(license.canSublicense, true, "license canSublicense"); - assertEq(license.commercial, true, "license commercial"); - assertEq(license.parentLicenseId, 0, "license parentLicenseId"); + assertEq(license.canSublicense, canSublicense, "license canSublicense"); + assertEq(license.commercial, commercial, "license commercial"); + assertEq(license.parentLicenseId, parentLicenseId, "license parentLicenseId"); assertEq(license.tokenId, tokenId, "license tokenId"); assertEq(license.revoker, revoker, "license revoker"); assertEq(license.uri, "licenseUri", "license uri"); assertEq(address(license.termsProcessor), address(termsProcessor), "license termsProcessor"); assertEq(license.termsData, abi.encode("terms"), "license termsData"); - return licenseId; } } From f98972cfd6b7403111529c8a1cb1502014006d43 Mon Sep 17 00:00:00 2001 From: Raul Date: Tue, 5 Sep 2023 00:27:36 +0200 Subject: [PATCH 29/42] test license registry --- .../modules/licensing/LicenseRegistry.sol | 6 -- .../modules/licensing/LicensingModule.sol | 2 +- contracts/modules/licensing/RightsManager.sol | 16 ++--- test/foundry/licensing/LicenseRegistry.t.sol | 68 +++++++++++++++++++ .../licensing/RightsManager.Internal.t.sol | 5 ++ test/foundry/mocks/MockTermsProcessor.sol | 3 +- 6 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 test/foundry/licensing/LicenseRegistry.t.sol diff --git a/contracts/modules/licensing/LicenseRegistry.sol b/contracts/modules/licensing/LicenseRegistry.sol index 5e2297d2..94adcdbe 100644 --- a/contracts/modules/licensing/LicenseRegistry.sol +++ b/contracts/modules/licensing/LicenseRegistry.sol @@ -3,14 +3,11 @@ pragma solidity ^0.8.13; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol"; import { IERC5218 } from "./IERC5218.sol"; -import "forge-std/console.sol"; contract LicenseRegistry is ERC721 { IERC5218 public immutable RIGHTS_MANAGER; - error LicenseCanBeRevokedNotBurned(); - constructor(address rightsManager, string memory name, string memory symbol) ERC721(name, symbol) { if (rightsManager == address(0)) { revert ZeroAddress(); @@ -41,9 +38,6 @@ contract LicenseRegistry is ERC721 { if (from != address(0)) { RIGHTS_MANAGER.transferSublicense(firstTokenId, to); } - if (to == address(0)) { - revert LicenseCanBeRevokedNotBurned(); - } super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index 848e6fbe..a363aa6d 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -8,7 +8,7 @@ import { UPGRADER_ROLE, LICENSING_MANAGER_ROLE } from "contracts/access-control/ import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; import { IERC5218 } from "./IERC5218.sol"; import { ILicensingModule } from "./ILicensingModule.sol"; -import "forge-std/console.sol"; + contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index dd5a6fa4..2b99a8ec 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -10,7 +10,7 @@ import { NonExistentID, Unauthorized, ZeroAddress, UnsupportedInterface } from " import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; -import "forge-std/console.sol"; + abstract contract RightsManager is ERC721Upgradeable, @@ -58,7 +58,6 @@ abstract contract RightsManager is constructor(address _franchiseRegistry) { - console.log(_franchiseRegistry); if (_franchiseRegistry == address(0)) { revert ZeroAddress(); } @@ -214,7 +213,7 @@ abstract contract RightsManager is } function revokeLicense(uint256 _licenseId) external override { - if (!_exists(_licenseId)) revert NonExistentID(_licenseId); + if (!isLicenseSet(_licenseId)) revert NonExistentID(_licenseId); RightsManagerStorage storage $ = _getRightsManagerStorage(); License storage license = $.licenses[_licenseId]; if (msg.sender != license.revoker) revert SenderNotRevoker(); @@ -244,7 +243,7 @@ abstract contract RightsManager is if (licenseId == 0) return false; RightsManagerStorage storage $ = _getRightsManagerStorage(); while (licenseId != 0) { - console.log("licenseId", licenseId); + License memory license = $.licenses[licenseId]; if (!_isActiveAndTermsOk(license)) return false; licenseId = license.parentLicenseId; @@ -253,10 +252,7 @@ abstract contract RightsManager is } function _isActiveAndTermsOk(License memory license) view private returns (bool) { - // console.log("license.active", license.active); - // console.log("license.termsProcessor", address(license.termsProcessor)); if (address(license.termsProcessor) == address(0)) return license.active; - // console.log("tersmExecutedSuccessfully", license.termsProcessor.tersmExecutedSuccessfully(license.termsData)); return license.active && license.termsProcessor.tersmExecutedSuccessfully(license.termsData); } @@ -361,7 +357,11 @@ abstract contract RightsManager is function isRootLicense( uint256 licenseId ) public view returns (bool) { - return _getRightsManagerStorage().licenses[licenseId].parentLicenseId == _UNSET_LICENSE_ID; + return _getRightsManagerStorage().licenses[licenseId].parentLicenseId == _UNSET_LICENSE_ID && isLicenseSet(licenseId); + } + + function isLicenseSet(uint256 licenseId) public view returns (bool) { + return _getRightsManagerStorage().licenses[licenseId].revoker != address(0); } function transferSublicense( diff --git a/test/foundry/licensing/LicenseRegistry.t.sol b/test/foundry/licensing/LicenseRegistry.t.sol new file mode 100644 index 00000000..81c070ee --- /dev/null +++ b/test/foundry/licensing/LicenseRegistry.t.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BUSDL-1.1 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import '../utils/BaseTest.sol'; +import '../mocks/MockLicensingModule.sol'; +import '../mocks/MockTermsProcessor.sol'; +import '../mocks/RightsManagerHarness.sol'; +import "../mocks/MockERC721.sol"; +import "contracts/errors/General.sol"; + +contract LicenseRegistryTest is BaseTest { + + address licenseHolder = address(0x888888); + address receiver = address(0x999999); + uint256 licenseId; + + function setUp() virtual override public { + deployProcessors = false; + super.setUp(); + vm.prank(licenseHolder); + uint256 ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", licenseHolder, 0); + uint256 parentLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, false); + (IERC5218.TermsProcessorConfig memory terms,) = LibMockFranchiseConfig.getTermsProcessorConfig(); + vm.prank(licenseHolder); + licenseId = ipAssetRegistry.createLicense( + ipAssetId, + parentLicenseId, + licenseHolder, + NON_COMMERCIAL_LICENSE_URI, + revoker, + false, + false, + terms + ); + + } + + function test_setUp() public { + assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); + assertEq(licenseRegistry.name(), "Licenses for FranchiseName"); + assertEq(licenseRegistry.symbol(), "slFRN"); + assertEq(address(licenseRegistry.RIGHTS_MANAGER()), address(ipAssetRegistry)); + assertEq(licenseRegistry.exists(licenseId), true); + } + + function test_revert_mint_non_rights_manager() public { + vm.expectRevert(Unauthorized.selector); + licenseRegistry.mint(licenseHolder, 1); + } + + function test_transfer() public { + assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); + vm.prank(licenseHolder); + licenseRegistry.transferFrom(licenseHolder, receiver, licenseId); + assertEq(licenseRegistry.ownerOf(licenseId), receiver); + } + + function test_revert_transfer_inactive_license() public { + vm.prank(revoker); + ipAssetRegistry.revokeLicense(licenseId); + + vm.expectRevert(RightsManager.InactiveLicense.selector); + vm.prank(licenseHolder); + licenseRegistry.transferFrom(licenseHolder, receiver, licenseId); + } + +} diff --git a/test/foundry/licensing/RightsManager.Internal.t.sol b/test/foundry/licensing/RightsManager.Internal.t.sol index d817a4f1..7cc1b129 100644 --- a/test/foundry/licensing/RightsManager.Internal.t.sol +++ b/test/foundry/licensing/RightsManager.Internal.t.sol @@ -46,6 +46,11 @@ contract RightsManagerInternalTest is Test, ProxyHelper { assertEq(rightsManager.symbol(), "symbol"); } + function test_revert_transfer_sublicense() public { + vm.expectRevert(Unauthorized.selector); + rightsManager.transferSublicense(1, address(0x123456)); + } + function test_internal_create_license_rootLicense_notmockMinting() public { uint256 tokenId = 1; rightsManager.mockMint(licenseHolder, tokenId); diff --git a/test/foundry/mocks/MockTermsProcessor.sol b/test/foundry/mocks/MockTermsProcessor.sol index d012025f..a4a9ac9e 100644 --- a/test/foundry/mocks/MockTermsProcessor.sol +++ b/test/foundry/mocks/MockTermsProcessor.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.13; import "contracts/modules/licensing/terms/ITermsProcessor.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import "forge-std/console.sol"; + contract MockTermsProcessor is ITermsProcessor, ERC165 { @@ -12,7 +12,6 @@ contract MockTermsProcessor is ITermsProcessor, ERC165 { function setSuccess(bool value) external { _success = value; - console.log(_success); } function supportsInterface( From 2a7a4334e08c144d38a215868a4a93cd52b865b6 Mon Sep 17 00:00:00 2001 From: Raul Date: Tue, 5 Sep 2023 00:31:00 +0200 Subject: [PATCH 30/42] todo: test terms --- .../terms/OwnerActivationTermsProcessor.sol | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 contracts/modules/licensing/terms/OwnerActivationTermsProcessor.sol diff --git a/contracts/modules/licensing/terms/OwnerActivationTermsProcessor.sol b/contracts/modules/licensing/terms/OwnerActivationTermsProcessor.sol new file mode 100644 index 00000000..2d5de7ee --- /dev/null +++ b/contracts/modules/licensing/terms/OwnerActivationTermsProcessor.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; + +import { ITermsProcessor } from "./ITermsProcessor.sol"; +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + +contract OwnerActivationTermsProcessor is ITermsProcessor, ERC165 { + + bool public activated; + + function executeTerms(bytes calldata data) external view override returns (bytes memory newData) { + return abi.encode(data); + } + + function tersmExecutedSuccessfully(bytes calldata data) external view override returns (bool) { + return activated; + } + + function supportsInterface( + bytes4 interfaceId + ) public view override(ERC165, IERC165) returns (bool) { + return interfaceId == type(ITermsProcessor).interfaceId || super.supportsInterface(interfaceId); + } +} \ No newline at end of file From ed114898c3cc00e50968a71ec42cded89b968ad0 Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 6 Sep 2023 00:41:23 +0200 Subject: [PATCH 31/42] timing terms --- contracts/modules/licensing/RightsManager.sol | 9 +- .../licensing/terms/BaseTermsProcessor.sol | 37 +++++ .../licensing/terms/MultiTermsProcessor.sol | 15 +- .../terms/OwnerActivationTermsProcessor.sol | 25 --- .../licensing/terms/TimeTermsProcessor.sol | 18 +-- contracts/modules/timing/LibDuration.sol | 4 +- .../licensing/terms/TimeTermsProcessor.t.sol | 152 ++++++++++++++++++ 7 files changed, 213 insertions(+), 47 deletions(-) create mode 100644 contracts/modules/licensing/terms/BaseTermsProcessor.sol delete mode 100644 contracts/modules/licensing/terms/OwnerActivationTermsProcessor.sol create mode 100644 test/foundry/licensing/terms/TimeTermsProcessor.t.sol diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 2b99a8ec..67568d0b 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -30,6 +30,8 @@ abstract contract RightsManager is error NFTHasNoAssociatedLicense(); error UseCreateFranchiseRootLicenseInstead(); + event TermsUpdated(uint256 indexed licenseId, address processor, bytes termsData); + struct License { bool active; bool canSublicense; @@ -225,12 +227,16 @@ abstract contract RightsManager is function executeTerms(uint256 _licenseId) external { RightsManagerStorage storage $ = _getRightsManagerStorage(); - if (msg.sender != address($.licenseRegistry)) revert Unauthorized(); + if (msg.sender != $.licenseRegistry.ownerOf(_licenseId)) revert Unauthorized(); License storage license = $.licenses[_licenseId]; if (license.termsProcessor != ITermsProcessor(address(0))) { bytes memory newData = license.termsProcessor.executeTerms(license.termsData); + console.logBytes32(keccak256(license.termsData)); + console.logBytes32(keccak256(newData)); if (keccak256(license.termsData) != keccak256(newData)) { license.termsData = newData; + console.log("newData"); + emit TermsUpdated(_licenseId, address(license.termsProcessor), newData); } } emit ExecuteTerms(_licenseId, license.termsData); @@ -243,7 +249,6 @@ abstract contract RightsManager is if (licenseId == 0) return false; RightsManagerStorage storage $ = _getRightsManagerStorage(); while (licenseId != 0) { - License memory license = $.licenses[licenseId]; if (!_isActiveAndTermsOk(license)) return false; licenseId = license.parentLicenseId; diff --git a/contracts/modules/licensing/terms/BaseTermsProcessor.sol b/contracts/modules/licensing/terms/BaseTermsProcessor.sol new file mode 100644 index 00000000..f1dee3f8 --- /dev/null +++ b/contracts/modules/licensing/terms/BaseTermsProcessor.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; + +import { ITermsProcessor } from "./ITermsProcessor.sol"; +import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol"; +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + +abstract contract BaseTermsProcessor is ITermsProcessor, ERC165 { + + address public immutable AUTHORIZED_EXECUTOR; + + constructor(address authorizedExecutor) { + if (authorizedExecutor == address(0)) { + revert ZeroAddress(); + } + AUTHORIZED_EXECUTOR = authorizedExecutor; + } + + modifier onlyAuthorizedExecutor() { + if(msg.sender != AUTHORIZED_EXECUTOR) revert Unauthorized(); + _; + } + + function executeTerms(bytes calldata data) onlyAuthorizedExecutor external returns(bytes memory newData) { + return _executeTerms(data); + } + + function _executeTerms(bytes calldata data) internal virtual returns (bytes memory newData); + + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC165, IERC165) returns (bool) { + return interfaceId == type(ITermsProcessor).interfaceId || super.supportsInterface(interfaceId); + } + +} \ No newline at end of file diff --git a/contracts/modules/licensing/terms/MultiTermsProcessor.sol b/contracts/modules/licensing/terms/MultiTermsProcessor.sol index 28535f9a..0a82f7b8 100644 --- a/contracts/modules/licensing/terms/MultiTermsProcessor.sol +++ b/contracts/modules/licensing/terms/MultiTermsProcessor.sol @@ -2,9 +2,10 @@ pragma solidity ^0.8.19; import { ITermsProcessor } from "./ITermsProcessor.sol"; +import { BaseTermsProcessor } from "./BaseTermsProcessor.sol"; import { EmptyArray, LengthMismatch } from "contracts/errors/General.sol"; -contract MultiTermsProcessor is ITermsProcessor { +contract MultiTermsProcessor is BaseTermsProcessor { error TooManyTermsProcessors(); event ProcessorsSet(ITermsProcessor[] processors); @@ -13,11 +14,11 @@ contract MultiTermsProcessor is ITermsProcessor { uint256 public constant MAX_PROCESSORS = 100; - constructor(ITermsProcessor[] memory _processors) { - setProcessors(_processors); + constructor(address authorizedExecutor, ITermsProcessor[] memory _processors) BaseTermsProcessor(authorizedExecutor) { + _setProcessors(_processors); } - function setProcessors(ITermsProcessor[] memory _processors) public { + function _setProcessors(ITermsProcessor[] memory _processors) private { if (_processors.length == 0) revert EmptyArray(); if (_processors.length > MAX_PROCESSORS) revert TooManyTermsProcessors(); @@ -25,7 +26,7 @@ contract MultiTermsProcessor is ITermsProcessor { emit ProcessorsSet(_processors); } - function executeTerms(bytes calldata data) external override returns (bytes memory newData) { + function _executeTerms(bytes calldata data) internal override returns (bytes memory newData) { uint256 length = processors.length; bytes[] memory encodedTerms = new bytes[](length); encodedTerms = abi.decode(data, (bytes[])); @@ -41,7 +42,7 @@ contract MultiTermsProcessor is ITermsProcessor { function supportsInterface( bytes4 interfaceId - ) external view override returns (bool) { + ) public view override returns (bool) { bool supported = true; if (interfaceId == type(ITermsProcessor).interfaceId) { uint256 length = processors.length; @@ -53,7 +54,7 @@ contract MultiTermsProcessor is ITermsProcessor { } return supported; } - return false; + return super.supportsInterface(interfaceId); } function tersmExecutedSuccessfully(bytes calldata data) external view override returns (bool) { diff --git a/contracts/modules/licensing/terms/OwnerActivationTermsProcessor.sol b/contracts/modules/licensing/terms/OwnerActivationTermsProcessor.sol deleted file mode 100644 index 2d5de7ee..00000000 --- a/contracts/modules/licensing/terms/OwnerActivationTermsProcessor.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { ITermsProcessor } from "./ITermsProcessor.sol"; -import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; - -contract OwnerActivationTermsProcessor is ITermsProcessor, ERC165 { - - bool public activated; - - function executeTerms(bytes calldata data) external view override returns (bytes memory newData) { - return abi.encode(data); - } - - function tersmExecutedSuccessfully(bytes calldata data) external view override returns (bool) { - return activated; - } - - function supportsInterface( - bytes4 interfaceId - ) public view override(ERC165, IERC165) returns (bool) { - return interfaceId == type(ITermsProcessor).interfaceId || super.supportsInterface(interfaceId); - } -} \ No newline at end of file diff --git a/contracts/modules/licensing/terms/TimeTermsProcessor.sol b/contracts/modules/licensing/terms/TimeTermsProcessor.sol index e760f426..1a0efef8 100644 --- a/contracts/modules/licensing/terms/TimeTermsProcessor.sol +++ b/contracts/modules/licensing/terms/TimeTermsProcessor.sol @@ -1,18 +1,17 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; -import { ITermsProcessor } from "./ITermsProcessor.sol"; +import { BaseTermsProcessor } from "./BaseTermsProcessor.sol"; import { LibDuration } from "../../timing/LibDuration.sol"; -import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -contract TimeTermsProcessor is ITermsProcessor, ERC165 { +contract TimeTermsProcessor is BaseTermsProcessor { using LibDuration for LibDuration.TimeConfig; - function executeTerms(bytes calldata data) external view override returns (bytes memory newData) { - // TODO: check caller is the rights manager + constructor(address authorizedExecutor) BaseTermsProcessor(authorizedExecutor) {} + + function _executeTerms(bytes calldata data) internal virtual override returns (bytes memory newData) { LibDuration.TimeConfig memory config = abi.decode(data, (LibDuration.TimeConfig)); - if (config.startTime == 0) { + if (config.startTime == LibDuration.START_TIME_NOT_SET) { config.startTime = uint64(block.timestamp); } return abi.encode(config); @@ -23,9 +22,4 @@ contract TimeTermsProcessor is ITermsProcessor, ERC165 { return config.isActive(); } - function supportsInterface( - bytes4 interfaceId - ) public view override(ERC165, IERC165) returns (bool) { - return interfaceId == type(ITermsProcessor).interfaceId || super.supportsInterface(interfaceId); - } } \ No newline at end of file diff --git a/contracts/modules/timing/LibDuration.sol b/contracts/modules/timing/LibDuration.sol index 92a5b561..dc3934d9 100644 --- a/contracts/modules/timing/LibDuration.sol +++ b/contracts/modules/timing/LibDuration.sol @@ -14,8 +14,10 @@ library LibDuration { address renewer; } + uint64 public constant START_TIME_NOT_SET = uint64(0); + function isActive(TimeConfig memory self) internal view returns (bool) { - return self.startTime >= block.timestamp && self.startTime + self.ttl < block.timestamp; + return self.startTime != START_TIME_NOT_SET && block.timestamp >= self.startTime && block.timestamp < self.startTime + self.ttl; } function isRenewable(TimeConfig memory self) internal pure returns (bool) { diff --git a/test/foundry/licensing/terms/TimeTermsProcessor.t.sol b/test/foundry/licensing/terms/TimeTermsProcessor.t.sol new file mode 100644 index 00000000..4b2533c5 --- /dev/null +++ b/test/foundry/licensing/terms/TimeTermsProcessor.t.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: BUSDL-1.1 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import 'test/foundry/utils/BaseTest.sol'; +import "contracts/errors/General.sol"; +import "contracts/modules/licensing/terms/TimeTermsProcessor.sol"; +import "contracts/modules/timing/LibDuration.sol"; + +contract LicenseRegistryTest is BaseTest { + + address licenseHolder = address(0x888888); + TimeTermsProcessor processor; + uint256 licenseId; + uint256 ipAssetId; + uint256 parentLicenseId; + + function setUp() virtual override public { + deployProcessors = false; + super.setUp(); + ipAssetId = ipAssetRegistry.createIPAsset(IPAsset(1), "name", "description", "mediaUrl", licenseHolder, 0); + parentLicenseId = ipAssetRegistry.getLicenseIdByTokenId(ipAssetId, false); + processor = new TimeTermsProcessor(address(ipAssetRegistry)); + } + + function test_revert_execute_terms_unauthorized() public { + bytes memory data = abi.encode(1); + vm.expectRevert(Unauthorized.selector); + processor.executeTerms(data); + } + + function test_execute_terms_start_on_license_creation() public { + uint64 ttl = 1000; + uint64 startTime = uint64(block.timestamp) + 100; + address renewer = address(0); + + LibDuration.TimeConfig memory config = LibDuration.TimeConfig( + ttl, + startTime, + renewer + ); + IERC5218.TermsProcessorConfig memory termsConfig = IERC5218.TermsProcessorConfig({ + processor: processor, + data: abi.encode(config) + }); + + assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config)), "terms should be inactive before start time"); + + vm.prank(licenseHolder); + licenseId = ipAssetRegistry.createLicense( + ipAssetId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + false, + false, + termsConfig + ); + vm.prank(licenseHolder); + ipAssetRegistry.executeTerms(licenseId); + assertFalse(ipAssetRegistry.isLicenseActive(licenseId), "execution is a noop if start time set"); + assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config)), "execution is a noop if start time set"); + vm.warp(startTime + 100); + assertTrue(ipAssetRegistry.isLicenseActive(licenseId), "license should be active after start time"); + assertTrue(processor.tersmExecutedSuccessfully(abi.encode(config)), "terms should be active after start time"); + vm.warp(startTime + ttl + 1); + assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config)), "terms should be inactive after ttl"); + assertFalse(ipAssetRegistry.isLicenseActive(licenseId), "license should be inactive after ttl"); + + } + + function test_terms_always_false_if_not_started() public { + uint64 ttl = 1000; + uint64 startTime = 0; // unset so it fills with block.timestamp in terms execution + address renewer = address(0); + + LibDuration.TimeConfig memory config = LibDuration.TimeConfig( + ttl, + startTime, + renewer + ); + IERC5218.TermsProcessorConfig memory termsConfig = IERC5218.TermsProcessorConfig({ + processor: processor, + data: abi.encode(config) + }); + + assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config))); + + vm.prank(licenseHolder); + licenseId = ipAssetRegistry.createLicense( + ipAssetId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + false, + false, + termsConfig + ); + assertFalse(ipAssetRegistry.isLicenseActive(licenseId)); + assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config))); + vm.warp(block.timestamp + 100); + assertFalse(ipAssetRegistry.isLicenseActive(licenseId)); + assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config))); + vm.warp(block.timestamp + ttl + 1); + assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config))); + assertFalse(ipAssetRegistry.isLicenseActive(licenseId)); + + } + + function test_execute_terms_start_license_countdown() public { + uint64 ttl = 1000; + uint64 startTime = 0; // unset so it fills with block.timestamp in terms execution + address renewer = address(0); + + LibDuration.TimeConfig memory config = LibDuration.TimeConfig( + ttl, + startTime, + renewer + ); + IERC5218.TermsProcessorConfig memory termsConfig = IERC5218.TermsProcessorConfig({ + processor: processor, + data: abi.encode(config) + }); + + assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config)), "terms should be inactive before start time"); + + vm.prank(licenseHolder); + licenseId = ipAssetRegistry.createLicense( + ipAssetId, + parentLicenseId, + licenseHolder, + "licenseUri", + revoker, + false, + false, + termsConfig + ); + assertFalse(ipAssetRegistry.isLicenseActive(licenseId), "terms not executed yet"); + vm.prank(licenseHolder); + ipAssetRegistry.executeTerms(licenseId); + assertTrue(ipAssetRegistry.isLicenseActive(licenseId), "license started after terms execution"); + vm.warp(block.timestamp + 100); + assertTrue(ipAssetRegistry.isLicenseActive(licenseId), "license should be active after start time"); + vm.warp(block.timestamp + ttl + 1); + assertFalse(ipAssetRegistry.isLicenseActive(licenseId), "license should be inactive after ttl"); + + } + + +} From d2b235a0de30fdb01ccc3bdbdfe0ff42fc9bdaa6 Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 6 Sep 2023 01:47:11 +0200 Subject: [PATCH 32/42] fix compilation --- contracts/modules/licensing/RightsManager.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 67568d0b..60852348 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -231,11 +231,8 @@ abstract contract RightsManager is License storage license = $.licenses[_licenseId]; if (license.termsProcessor != ITermsProcessor(address(0))) { bytes memory newData = license.termsProcessor.executeTerms(license.termsData); - console.logBytes32(keccak256(license.termsData)); - console.logBytes32(keccak256(newData)); if (keccak256(license.termsData) != keccak256(newData)) { license.termsData = newData; - console.log("newData"); emit TermsUpdated(_licenseId, address(license.termsProcessor), newData); } } From d7b643d8804de6952980ddd07c1e8fba2c477930 Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 6 Sep 2023 02:50:14 +0200 Subject: [PATCH 33/42] docs --- contracts/ip-assets/IPAssetRegistry.sol | 40 ++++++++++++- contracts/modules/licensing/IERC5218.sol | 3 + .../modules/licensing/ILicensingModule.sol | 6 +- .../modules/licensing/LicenseRegistry.sol | 12 +++- .../modules/licensing/LicensingModule.sol | 19 ++++++- contracts/modules/licensing/RightsManager.sol | 56 ++++++++++++++++++- 6 files changed, 127 insertions(+), 9 deletions(-) diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index a9731af6..bdba6fd7 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -73,6 +73,19 @@ contract IPAssetRegistry is return _VERSION; } + /** + * Creates a new IPAsset, and assigns licenses (rights) to it, according to the Franchise config in LicensingModule. + * A Non commercial license is always assigned, and if the IPAsset is a root IPAsset, a commercial license may also be assigned. + * @dev reverts if LicensingModule is not configured for the Franchise. + * Logs to IPAssetEventEmitter, common contract for all IPAsset registries. + * @param ipAssetType the type of IPAsset to create + * @param name IPAsset name + * @param _description short description of the IPAsset + * @param mediaUrl url to the IPAsset media and metadata + * @param to holder of the IPAsset (and thus the licenses) + * @param parentIpAssetId 0 if this is a root IPAsset, if it is a derivative, set the parent IPAsset id + * @return the created IPAsset id + */ function createIPAsset( IPAsset ipAssetType, string calldata name, @@ -105,6 +118,16 @@ contract IPAssetRegistry is return ipAssetId; } + /** + * Sets the non commercial rights for an IPAsset, with terms from the Franchise config in LicensingModule. + * If no parent asset id is provided, the root IPAsset id is used if it exists in the Franchise config. + * @param ipAssetId the IPAsset id + * @param parentIpAssetId in case this is a derivative IPAsset, set the parent IPAsset id, 0 otherwise + * @param holder of the IPAsset and licenses + * @param revoker of the license. Can't be zero or changed later + * @param config Franchise config + * @param terms for the license to be active + */ function _setNonCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) internal { uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, false); _createLicense( @@ -120,6 +143,16 @@ contract IPAssetRegistry is ); } + /** + * Sets the commercial rights for an IPAsset, with terms from the Franchise config in LicensingModule. + * If no parent asset id is provided, the root IPAsset id is used if it exists in the Franchise config. + * @param ipAssetId the IPAsset id + * @param parentIpAssetId in case this is a derivative IPAsset, set the parent IPAsset id, 0 otherwise + * @param holder of the IPAsset and licenses + * @param revoker of the license. Can't be zero or changed later + * @param config Franchise config + * @param terms for the license to be active + */ function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) internal { uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, true); _createLicense( @@ -135,7 +168,11 @@ contract IPAssetRegistry is ); } - + /** + * mints the IPAsset block, and assigns the next id to it. + * @param to holder + * @param sb ip asset type + */ function _mintBlock(address to, IPAsset sb) private returns (uint256) { uint256 nextId = currentIdFor(sb) + 1; if (nextId > LibIPAssetId._lastId(sb)) revert IdOverBounds(); @@ -168,6 +205,7 @@ contract IPAssetRegistry is function tokenURI( uint256 tokenId ) public view override returns (string memory) { + // TODO: should this reference the license too? return readIPAsset(tokenId).mediaUrl; } diff --git a/contracts/modules/licensing/IERC5218.sol b/contracts/modules/licensing/IERC5218.sol index 641c552b..d3ac7fbf 100644 --- a/contracts/modules/licensing/IERC5218.sol +++ b/contracts/modules/licensing/IERC5218.sol @@ -16,8 +16,11 @@ interface IERC5218 is IERC721Upgradeable { address _revoker ); + // NOTE: MODIFIED ERC-5218, this may be refactored into ILicenseTermsExecutor /// @dev emits when license terms are executed to activate a license event ExecuteTerms(uint256 _licenseId, bytes _data); + /// @dev This emits when the terms of a license are updated, after executing a processor + event TermsUpdated(uint256 indexed licenseId, address processor, bytes termsData); /// @dev This emits when a license is revoked. Note that under some /// license terms, the sublicenses may be `implicitly` revoked following the diff --git a/contracts/modules/licensing/ILicensingModule.sol b/contracts/modules/licensing/ILicensingModule.sol index 6ca0cfaa..7a05e69a 100644 --- a/contracts/modules/licensing/ILicensingModule.sol +++ b/contracts/modules/licensing/ILicensingModule.sol @@ -11,10 +11,10 @@ import { IERC5218 } from "./IERC5218.sol"; interface ILicensingModule { struct IpAssetConfig { - bool canSublicense; - uint256 franchiseRootLicenseId; + bool canSublicense; // If false, this IPAsset cannot be parentLicenseId of any other IPAsset + uint256 franchiseRootLicenseId; // If set, root IPAsset licenses will have this as their parentLicenseId // TODO: allowed license terms? processors? - // TODO: limit medium of sublicenses? something like LibIPAssetMask? + // TODO: limit medium of sublicenses? As in, you can only license prose to prose? something like LibIPAssetMask? // TODO: limit who you can sublicense to? } diff --git a/contracts/modules/licensing/LicenseRegistry.sol b/contracts/modules/licensing/LicenseRegistry.sol index 94adcdbe..c6f5d9a8 100644 --- a/contracts/modules/licensing/LicenseRegistry.sol +++ b/contracts/modules/licensing/LicenseRegistry.sol @@ -4,6 +4,11 @@ import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol"; import { IERC5218 } from "./IERC5218.sol"; +/** + * @title LicenseRegistry + * @author Raul Martinez + * @notice Simple NFT tracking the ownership of tradeable Licenses emitted by a RightsManager. + */ contract LicenseRegistry is ERC721 { IERC5218 public immutable RIGHTS_MANAGER; @@ -20,6 +25,11 @@ contract LicenseRegistry is ERC721 { _; } + /** + * @notice Mint a License to the given address. Only caller allowed is the RightsManager. + * @param to The address to mint the License to. + * @param tokenId The ID of the License to mint. + */ function mint(address to, uint256 tokenId) external onlyRightsManager { _mint(to, tokenId); } @@ -34,7 +44,7 @@ contract LicenseRegistry is ERC721 { uint256 firstTokenId, uint256 batchSize ) internal virtual override { - // Minting has already been checked by the RightsManager. + // Minting has already been checked by the RightsManager, but transfers need to pass some checks. if (from != address(0)) { RIGHTS_MANAGER.transferSublicense(firstTokenId, to); } diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index a363aa6d..4ed8fd3a 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -9,7 +9,13 @@ import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; import { IERC5218 } from "./IERC5218.sol"; import { ILicensingModule } from "./ILicensingModule.sol"; - +/** + * @title LicensingModule + * @author Raul Martinez + * @notice Contract for configuring and managing licensing for a Franchise. + * A licensing framework may be definbed through a FranchiseConfig, which is set by the Franchise owner. + * The non commercial license URI is set by a protocol admin key, since it will be common for all Story Protocol + */ contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { struct LicensingModuleStorage { @@ -53,11 +59,22 @@ contract LicensingModule is ILicensingModule, AccessControlledUpgradeable { return _getLicensingModuleStorage().nonCommercialLicenseURI; } + /** + * Set the URI for non-commercial licenses across Story Protocol. Setting this does NOT affect existing licenses, only new ones. + * @param _nonCommercialLicenseURI The URI to set for non-commercial licenses + */ function setNonCommercialLicenseURI(string calldata _nonCommercialLicenseURI) external onlyRole(LICENSING_MANAGER_ROLE) { _getLicensingModuleStorage().nonCommercialLicenseURI = _nonCommercialLicenseURI; emit NonCommercialLicenseUriSet(_nonCommercialLicenseURI); } + /** + * Set the FranchiseConfig for a Franchise, configuring its licensing framework. + * @dev if setting root licenses, they should be active. A revoker address must be set, and it will be + * common for all licenses in the Franchise. + * @param franchiseId The ID of the Franchise to set the config for + * @param config The FranchiseConfig to set + */ function configureFranchiseLicensing(uint256 franchiseId, FranchiseConfig memory config) external { if (msg.sender != FRANCHISE_REGISTRY.ownerOf(franchiseId)) { revert Unauthorized(); diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 60852348..433198de 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -11,7 +11,19 @@ import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/in import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; - +/** + * @title RightsManager + * @author Raul Martinez + * @notice IERC-5218 implementation. + Allows us to grant 2 type of licenses: + 1. Rights: Licenses tied to a tokenId (IPAsset id), in which the license holder is always the owner of the tokenId. Each tokenId can a commercial or non commercial license tied to it + 2. Tradeable Licenses): The license holder is the owner of the correspondent LicenseRegistry NFT. They are either: + 2.1 Franchise root license: LicenseRegistry enabled license minted by a Franchise owner to govern commercial or non commercial rights for all the IPAssetRegistries. + 2.2 Sublicense: a license coming from Rights or other Licenses, minted by the parent license owner. These would be the future "movie adaptation" type licenses that can be sold. + Creates a LicenseRegistry on initialization + Allows license holders to execute terms to activate the license to activate them. + Tracks active licenses along the license trees. + */ abstract contract RightsManager is ERC721Upgradeable, IERC5218 @@ -30,8 +42,6 @@ abstract contract RightsManager is error NFTHasNoAssociatedLicense(); error UseCreateFranchiseRootLicenseInstead(); - event TermsUpdated(uint256 indexed licenseId, address processor, bytes termsData); - struct License { bool active; bool canSublicense; @@ -90,6 +100,19 @@ abstract contract RightsManager is } } + /** + * Creates a tradeable sublicense. + * @dev Throws if trying to create a franchise level or root license. + * @param _tokenId The tokenId of the IPAsset to create the sublicense for. + * @param _parentLicenseId The parent license to create the sublicense from. + * @param _licenseHolder The address of the sublicense holder, will own the LicenseRegistry NFT. + * @param _uri License terms URI. + * @param _revoker address that can revoke the license. + * @param _commercial if the license is commercial or not. + * @param _canSublicense if the license can be parentLicense of another one + * @param _terms the on chain terms of the license, via executor and data + * @return licenseId + */ function createLicense( uint256 _tokenId, // Question: should sublicenses be created with a tokenId or just a parentLicenseId? uint256 _parentLicenseId, @@ -117,6 +140,18 @@ abstract contract RightsManager is ); } + /** + * Creates the root licenses that all other licenses of a Franchise may be based on. + * @dev Throws if caller not owner of the FranchiseRegistry NFt. + * @param franchiseId in the FranhiseRegistry + * @param _licenseHolder The address of the sublicense holder, will own the LicenseRegistry NFT. + * @param _uri License terms URI. + * @param _revoker address that can revoke the license. + * @param _commercial if the license is commercial or not. + * @param _canSublicense if the license can be parentLicense of another one + * @param _terms the on chain terms of the license, via executor and data + * @return licenseId + */ function createFranchiseRootLicense( uint256 franchiseId, address _licenseHolder, @@ -140,6 +175,7 @@ abstract contract RightsManager is ); } + function _createLicense( uint256 tokenId, uint256 parentLicenseId, @@ -214,6 +250,7 @@ abstract contract RightsManager is return licenseId; } + function revokeLicense(uint256 _licenseId) external override { if (!isLicenseSet(_licenseId)) revert NonExistentID(_licenseId); RightsManagerStorage storage $ = _getRightsManagerStorage(); @@ -225,6 +262,11 @@ abstract contract RightsManager is // TODO: delete the rootLicenseForTokenId mapping for licenseId if root license } + /** + * If set, runs the TermsExecutor with the terms data stored in the license. + * If the terms execution returns different data, the license is updated with the new data. + * @param _licenseId The identifier for the queried license + */ function executeTerms(uint256 _licenseId) external { RightsManagerStorage storage $ = _getRightsManagerStorage(); if (msg.sender != $.licenseRegistry.ownerOf(_licenseId)) revert Unauthorized(); @@ -239,6 +281,7 @@ abstract contract RightsManager is emit ExecuteTerms(_licenseId, license.termsData); } + /// returns true if the license is active (non revoked and terms returning true) and all its parent licenses are active, false otherwise function isLicenseActive( uint256 licenseId ) public view virtual returns (bool) { @@ -316,6 +359,7 @@ abstract contract RightsManager is return _getRightsManagerStorage().licenses[_licenseId].parentLicenseId; } + function getLicenseHolder( uint256 _licenseId ) public view override returns (address) { @@ -366,6 +410,12 @@ abstract contract RightsManager is return _getRightsManagerStorage().licenses[licenseId].revoker != address(0); } + /** + * Since the LicenseRegistry tracks sublicense ownership, this method can only be called by the LicenseRegistry. + * @dev Throws if the license is not active. Basically exists to not break ERC-5218. + * @param licenseId the license to transfer + * @param licenseHolder the new license holder + */ function transferSublicense( uint256 licenseId, address licenseHolder From 69b0611f059695dabc8f17f86faf2bfb2e2581da Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 6 Sep 2023 22:24:29 +0200 Subject: [PATCH 34/42] licenseRegistry decoupling --- contracts/ip-assets/IPAssetRegistry.sol | 2 +- .../ip-assets/IPAssetRegistryFactory.sol | 4 ++ .../modules/licensing/ILicenseRegistry.sol | 19 ++++++ .../modules/licensing/LicenseRegistry.sol | 26 ++++++-- contracts/modules/licensing/RightsManager.sol | 64 ++++++++----------- test/foundry/licensing/LicenseRegistry.t.sol | 2 +- .../licensing/RightsManager.IPAsset.t.sol | 2 +- .../licensing/RightsManager.Internal.t.sol | 10 +-- test/foundry/utils/BaseTest.sol | 4 +- 9 files changed, 81 insertions(+), 52 deletions(-) create mode 100644 contracts/modules/licensing/ILicenseRegistry.sol diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index bdba6fd7..c6d77e73 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -215,7 +215,7 @@ contract IPAssetRegistry is public view virtual - override + override(ERC721Upgradeable, IERC165Upgradeable) returns (bool) { return diff --git a/contracts/ip-assets/IPAssetRegistryFactory.sol b/contracts/ip-assets/IPAssetRegistryFactory.sol index 4ba08e3d..ae8536b7 100644 --- a/contracts/ip-assets/IPAssetRegistryFactory.sol +++ b/contracts/ip-assets/IPAssetRegistryFactory.sol @@ -7,6 +7,7 @@ import { IPAssetRegistry } from "./IPAssetRegistry.sol"; import { ZeroAddress } from "../errors/General.sol"; import { IVersioned } from "../utils/IVersioned.sol"; import { UnsupportedInterface } from "../errors/General.sol"; +import { LicenseRegistry } from "../modules/licensing/LicenseRegistry.sol"; import { RevertingIPAssetRegistry } from "contracts/utils/RevertingIPAssetRegistry.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; @@ -40,6 +41,9 @@ contract IPAssetRegistryFactory is Ownable { description ); address proxy = address(new BeaconProxy(address(BEACON), data)); + LicenseRegistry licenseRegistry = new LicenseRegistry(proxy, string.concat("Licenses for ", name), string.concat("sl", symbol)); + IPAssetRegistry(proxy).setLicenseRegistry(address(licenseRegistry)); + emit FranchiseCreated(proxy, name, symbol); return proxy; } diff --git a/contracts/modules/licensing/ILicenseRegistry.sol b/contracts/modules/licensing/ILicenseRegistry.sol new file mode 100644 index 00000000..b25403e3 --- /dev/null +++ b/contracts/modules/licensing/ILicenseRegistry.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.13; +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; + + +/** + * @title ILicenseRegistry + * @author Raul Martinez + * @notice Interface for NFT tracking the ownership of tradeable Licenses emitted by a RightsManager. + */ +interface ILicenseRegistry is IERC721 { + + function mint(address to, uint256 tokenId) external; + function exists(uint256 tokenId) external view returns (bool); + function name() external view returns (string memory); + function symbol() external view returns (string memory); + function getRightsManager() external view returns (address); + +} \ No newline at end of file diff --git a/contracts/modules/licensing/LicenseRegistry.sol b/contracts/modules/licensing/LicenseRegistry.sol index c6f5d9a8..4c071976 100644 --- a/contracts/modules/licensing/LicenseRegistry.sol +++ b/contracts/modules/licensing/LicenseRegistry.sol @@ -3,25 +3,26 @@ pragma solidity ^0.8.13; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol"; import { IERC5218 } from "./IERC5218.sol"; +import { ILicenseRegistry } from "./ILicenseRegistry.sol"; /** * @title LicenseRegistry * @author Raul Martinez * @notice Simple NFT tracking the ownership of tradeable Licenses emitted by a RightsManager. */ -contract LicenseRegistry is ERC721 { +contract LicenseRegistry is ILicenseRegistry, ERC721 { - IERC5218 public immutable RIGHTS_MANAGER; + IERC5218 private immutable _RIGHTS_MANAGER; - constructor(address rightsManager, string memory name, string memory symbol) ERC721(name, symbol) { - if (rightsManager == address(0)) { + constructor(address _rightsManager, string memory _name, string memory _symbol) ERC721(_name, _symbol) { + if (_rightsManager == address(0)) { revert ZeroAddress(); } - RIGHTS_MANAGER = IERC5218(rightsManager); + _RIGHTS_MANAGER = IERC5218(_rightsManager); } modifier onlyRightsManager() { - if (msg.sender != address(RIGHTS_MANAGER)) revert Unauthorized(); + if (msg.sender != address(_RIGHTS_MANAGER)) revert Unauthorized(); _; } @@ -46,10 +47,21 @@ contract LicenseRegistry is ERC721 { ) internal virtual override { // Minting has already been checked by the RightsManager, but transfers need to pass some checks. if (from != address(0)) { - RIGHTS_MANAGER.transferSublicense(firstTokenId, to); + _RIGHTS_MANAGER.transferSublicense(firstTokenId, to); } super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } + + function getRightsManager() external view override returns (address) { + return address(_RIGHTS_MANAGER); + } + + function name() public view override(ERC721, ILicenseRegistry) returns (string memory) { + return super.name(); + } + function symbol() public view override(ERC721, ILicenseRegistry) returns (string memory) { + return super.symbol(); + } } \ No newline at end of file diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 433198de..c6031e9a 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -5,11 +5,12 @@ import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC import { LibDuration } from "../timing/LibDuration.sol"; import { UPGRADER_ROLE } from "contracts/access-control/ProtocolRoles.sol"; import { IERC5218 } from "./IERC5218.sol"; -import { LicenseRegistry } from "./LicenseRegistry.sol"; +import { ILicenseRegistry } from "./ILicenseRegistry.sol"; import { NonExistentID, Unauthorized, ZeroAddress, UnsupportedInterface } from "contracts/errors/General.sol"; -import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; +import { ERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; +import "forge-std/console.sol"; /** * @title RightsManager @@ -41,6 +42,7 @@ abstract contract RightsManager is error ZeroRevokerAddress(); error NFTHasNoAssociatedLicense(); error UseCreateFranchiseRootLicenseInstead(); + error LicenseRegistryNotConfigured(); struct License { bool active; @@ -59,7 +61,7 @@ abstract contract RightsManager is // keccack256(commercial, tokenId) => licenseId mapping(bytes32 => uint256) licensesForTokenId; uint256 licenseCounter; - LicenseRegistry licenseRegistry; + ILicenseRegistry licenseRegistry; } // keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.rights-manager.storage")) - 1))) @@ -68,12 +70,12 @@ abstract contract RightsManager is uint256 public constant FRANCHISE_REGISTRY_OWNED_TOKEN_ID = type(uint256).max; IERC721 public immutable FRANCHISE_REGISTRY; - constructor(address _franchiseRegistry) { if (_franchiseRegistry == address(0)) { revert ZeroAddress(); } - FRANCHISE_REGISTRY = IERC721(_franchiseRegistry); + FRANCHISE_REGISTRY = IERC721(_franchiseRegistry); + } function __RightsManager_init( @@ -81,13 +83,14 @@ abstract contract RightsManager is string calldata symbol ) public initializer { __ERC721_init(name, symbol); - // licenseRegistry should be immutable, but address(this) in the constructor is the interface address, not the proxy address - // TODO: Revisit this if/when we make IPAssetRegistries immutable - _getRightsManagerStorage().licenseRegistry = new LicenseRegistry( - address(this), - string.concat("Licenses for ", name), - string.concat("sl", symbol) - ); + } + + + function setLicenseRegistry(address _licenseRegistry) external { + // NOTE: This assumes no need to change ILicenseRegistry implementation. + if (address(_getRightsManagerStorage().licenseRegistry) != address(0)) revert Unauthorized(); + if (_licenseRegistry == address(0)) revert ZeroAddress(); + _getRightsManagerStorage().licenseRegistry = ILicenseRegistry(_licenseRegistry); } function _getRightsManagerStorage() @@ -105,7 +108,7 @@ abstract contract RightsManager is * @dev Throws if trying to create a franchise level or root license. * @param _tokenId The tokenId of the IPAsset to create the sublicense for. * @param _parentLicenseId The parent license to create the sublicense from. - * @param _licenseHolder The address of the sublicense holder, will own the LicenseRegistry NFT. + * @param _licenseHolder The address of the sublicense holder, will own the ILicenseRegistry NFT. * @param _uri License terms URI. * @param _revoker address that can revoke the license. * @param _commercial if the license is commercial or not. @@ -144,7 +147,7 @@ abstract contract RightsManager is * Creates the root licenses that all other licenses of a Franchise may be based on. * @dev Throws if caller not owner of the FranchiseRegistry NFt. * @param franchiseId in the FranhiseRegistry - * @param _licenseHolder The address of the sublicense holder, will own the LicenseRegistry NFT. + * @param _licenseHolder The address of the sublicense holder, will own the ILicenseRegistry NFT. * @param _uri License terms URI. * @param _revoker address that can revoke the license. * @param _commercial if the license is commercial or not. @@ -203,13 +206,16 @@ abstract contract RightsManager is if ($.licensesForTokenId[keccak256(abi.encode(commercial, tokenId))] != _UNSET_LICENSE_ID) { revert AlreadyHasRootLicense(); } - } else if(tokenId != FRANCHISE_REGISTRY_OWNED_TOKEN_ID && parentLicenseId != _UNSET_LICENSE_ID) { - // If this is a sublicense, check that this is a valid sublicense - License memory parentLicense = $.licenses[parentLicenseId]; - if (!parentLicense.active) revert InactiveParentLicense(); - if (!parentLicense.canSublicense) revert CannotSublicense(); - if (parentLicense.commercial != commercial) revert CommercialTermsMismatch(); - if (getLicenseHolder(parentLicenseId) != licenseHolder) revert NotOwnerOfParentLicense(); + } else { + if($.licenseRegistry == ILicenseRegistry(address(0))) revert LicenseRegistryNotConfigured(); + if(tokenId != FRANCHISE_REGISTRY_OWNED_TOKEN_ID && parentLicenseId != _UNSET_LICENSE_ID) { + // If this is a sublicense, check that this is a valid sublicense + License memory parentLicense = $.licenses[parentLicenseId]; + if (!parentLicense.active) revert InactiveParentLicense(); + if (!parentLicense.canSublicense) revert CannotSublicense(); + if (parentLicense.commercial != commercial) revert CommercialTermsMismatch(); + if (getLicenseHolder(parentLicenseId) != licenseHolder) revert NotOwnerOfParentLicense(); + } } // Check that the terms are valid _verifyTerms(_terms); @@ -396,7 +402,7 @@ abstract contract RightsManager is ]; } - function getLicenseRegistry() external view returns (LicenseRegistry) { + function getLicenseRegistry() external view returns (ILicenseRegistry) { return _getRightsManagerStorage().licenseRegistry; } @@ -426,18 +432,4 @@ abstract contract RightsManager is emit TransferLicense(licenseId, licenseHolder); } - function supportsInterface( - bytes4 interfaceId - ) - public - view - virtual - override(ERC721Upgradeable, IERC165Upgradeable) - returns (bool) - { - return - interfaceId == type(IERC5218).interfaceId || - super.supportsInterface(interfaceId); - } - } \ No newline at end of file diff --git a/test/foundry/licensing/LicenseRegistry.t.sol b/test/foundry/licensing/LicenseRegistry.t.sol index 81c070ee..eda994eb 100644 --- a/test/foundry/licensing/LicenseRegistry.t.sol +++ b/test/foundry/licensing/LicenseRegistry.t.sol @@ -40,7 +40,7 @@ contract LicenseRegistryTest is BaseTest { assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); assertEq(licenseRegistry.name(), "Licenses for FranchiseName"); assertEq(licenseRegistry.symbol(), "slFRN"); - assertEq(address(licenseRegistry.RIGHTS_MANAGER()), address(ipAssetRegistry)); + assertEq(address(licenseRegistry.getRightsManager()), address(ipAssetRegistry)); assertEq(licenseRegistry.exists(licenseId), true); } diff --git a/test/foundry/licensing/RightsManager.IPAsset.t.sol b/test/foundry/licensing/RightsManager.IPAsset.t.sol index 204359f6..b554eca0 100644 --- a/test/foundry/licensing/RightsManager.IPAsset.t.sol +++ b/test/foundry/licensing/RightsManager.IPAsset.t.sol @@ -22,7 +22,7 @@ contract RightsManagerIPAssetRightsTest is BaseTest { function test_setUp() public { assertEq(licenseRegistry.name(), "Licenses for FranchiseName"); assertEq(licenseRegistry.symbol(), "slFRN"); - assertEq(address(licenseRegistry.RIGHTS_MANAGER()), address(ipAssetRegistry)); + assertEq(address(licenseRegistry.getRightsManager()), address(ipAssetRegistry)); // Default licensing is root non-commercial with sublicense on, no commercial rights ILicensingModule.FranchiseConfig memory configResult = licensingModule.getFranchiseConfig(1); diff --git a/test/foundry/licensing/RightsManager.Internal.t.sol b/test/foundry/licensing/RightsManager.Internal.t.sol index 9f8d6857..d78895e5 100644 --- a/test/foundry/licensing/RightsManager.Internal.t.sol +++ b/test/foundry/licensing/RightsManager.Internal.t.sol @@ -38,6 +38,8 @@ contract RightsManagerInternalTest is Test, ProxyHelper { ) ) ); + LicenseRegistry licenseRegistry = new LicenseRegistry(address(rightsManager), "name", "symbol"); + rightsManager.setLicenseRegistry(address(licenseRegistry)); } function test_setup() public { @@ -70,7 +72,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { ); assertEq(licenseId, rightsManager.getLicenseIdByTokenId(tokenId, true)); _verifyLicense(licenseId, parentLicenseId, tokenId, termsProcessor, true, true); - LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); + ILicenseRegistry licenseRegistry = ILicenseRegistry(rightsManager.getLicenseRegistry()); vm.expectRevert("ERC721: invalid token ID"); licenseRegistry.ownerOf(licenseId); @@ -95,7 +97,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { ); assertEq(licenseId, rightsManager.getLicenseIdByTokenId(tokenId, true), "wtf"); _verifyLicense(licenseId, parentLicenseId, tokenId, termsProcessor, true, true); - LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); + ILicenseRegistry licenseRegistry = ILicenseRegistry(rightsManager.getLicenseRegistry()); vm.expectRevert("ERC721: invalid token ID"); licenseRegistry.ownerOf(licenseId); } @@ -133,7 +135,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { ); assertEq(licenseId, rightsManager.getLicenseIdByTokenId(nextTokenId, true)); _verifyLicense(licenseId, parentLicenseId, nextTokenId, termsProcessor, true, true); - LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); + ILicenseRegistry licenseRegistry = ILicenseRegistry(rightsManager.getLicenseRegistry()); vm.expectRevert("ERC721: invalid token ID"); licenseRegistry.ownerOf(licenseId); @@ -168,7 +170,7 @@ contract RightsManagerInternalTest is Test, ProxyHelper { true ); _verifyLicense(licenseId, parentLicenseId, tokenId, termsProcessor, true, true); - LicenseRegistry licenseRegistry = LicenseRegistry(rightsManager.getLicenseRegistry()); + ILicenseRegistry licenseRegistry = ILicenseRegistry(rightsManager.getLicenseRegistry()); assertEq(licenseRegistry.ownerOf(licenseId), licenseHolder); } diff --git a/test/foundry/utils/BaseTest.sol b/test/foundry/utils/BaseTest.sol index 4ef007eb..17f805cb 100644 --- a/test/foundry/utils/BaseTest.sol +++ b/test/foundry/utils/BaseTest.sol @@ -29,7 +29,7 @@ contract BaseTest is Test, ProxyHelper { AccessControlSingleton accessControl; PermissionlessRelationshipProcessor public relationshipProcessor; LicensingModule public licensingModule; - LicenseRegistry public licenseRegistry; + ILicenseRegistry public licenseRegistry; MockTermsProcessor public nonCommercialTermsProcessor; MockTermsProcessor public commercialTermsProcessor; bool public deployProcessors = false; @@ -88,7 +88,7 @@ contract BaseTest is Test, ProxyHelper { FranchiseRegistry.FranchiseCreationParams memory params = FranchiseRegistry.FranchiseCreationParams("FranchiseName", "FRN", "description", "tokenURI"); (uint256 franchiseId, address ipAssets) = franchiseRegistry.registerFranchise(params); ipAssetRegistry = IPAssetRegistry(ipAssets); - licenseRegistry = ipAssetRegistry.getLicenseRegistry(); + licenseRegistry = ILicenseRegistry(ipAssetRegistry.getLicenseRegistry()); // Configure Licensing for Franchise nonCommercialTermsProcessor = new MockTermsProcessor(); From a1683adf9d048ac2486853c28c4c735fd90963be Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 6 Sep 2023 22:25:35 +0200 Subject: [PATCH 35/42] comment fix --- contracts/modules/licensing/RightsManager.sol | 1 - test/foundry/mocks/RightsManagerHarness.sol | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index c6031e9a..295623e7 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -21,7 +21,6 @@ import "forge-std/console.sol"; 2. Tradeable Licenses): The license holder is the owner of the correspondent LicenseRegistry NFT. They are either: 2.1 Franchise root license: LicenseRegistry enabled license minted by a Franchise owner to govern commercial or non commercial rights for all the IPAssetRegistries. 2.2 Sublicense: a license coming from Rights or other Licenses, minted by the parent license owner. These would be the future "movie adaptation" type licenses that can be sold. - Creates a LicenseRegistry on initialization Allows license holders to execute terms to activate the license to activate them. Tracks active licenses along the license trees. */ diff --git a/test/foundry/mocks/RightsManagerHarness.sol b/test/foundry/mocks/RightsManagerHarness.sol index 7112614f..2f1dd00a 100644 --- a/test/foundry/mocks/RightsManagerHarness.sol +++ b/test/foundry/mocks/RightsManagerHarness.sol @@ -7,11 +7,12 @@ import { ILicensingModule } from "contracts/modules/licensing/ILicensingModule.s import { IERC5218 } from "contracts/modules/licensing/IERC5218.sol"; import { ITermsProcessor } from "contracts/modules/licensing/terms/ITermsProcessor.sol"; + contract RightsManagerHarness is IPAssetRegistry { constructor(address _eventEmitter, address _licensingModule, address _franchiseRegistry) IPAssetRegistry(_eventEmitter, _licensingModule, _franchiseRegistry) { - + } function mockMint(address to, uint256 tokenId) external { From 7999353f45b736b1616066bd02c9feba50296d53 Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 6 Sep 2023 22:36:14 +0200 Subject: [PATCH 36/42] remove log --- contracts/modules/licensing/RightsManager.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index 295623e7..da7bc0dd 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -10,7 +10,6 @@ import { NonExistentID, Unauthorized, ZeroAddress, UnsupportedInterface } from " import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol"; import { ERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; import { ITermsProcessor } from "./terms/ITermsProcessor.sol"; -import "forge-std/console.sol"; /** * @title RightsManager From 575bc5c7609d78ee0bdc2e7fb747587aa36f6863 Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 11 Sep 2023 17:21:40 -0300 Subject: [PATCH 37/42] comment protocol roles --- contracts/access-control/ProtocolRoles.sol | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contracts/access-control/ProtocolRoles.sol b/contracts/access-control/ProtocolRoles.sol index fa517b20..35f781de 100644 --- a/contracts/access-control/ProtocolRoles.sol +++ b/contracts/access-control/ProtocolRoles.sol @@ -2,7 +2,13 @@ pragma solidity ^0.8.13; +// This roles are used by the AccessControlSingleton, accessed by AccessControlled contracts. + +// Default admin role as per OZ AccessControl system. All other roles stem from this. bytes32 constant PROTOCOL_ADMIN_ROLE = bytes32(0); +// Role that can upgrade UUPS contracts or Beacon Proxies bytes32 constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); +// Role that can perform admin tasks on the Protocol Relationship Module contract (e.g. adding new protocol-wide links) bytes32 constant RELATIONSHIP_MANAGER_ROLE = keccak256("RELATIONSHIP_MANAGER_ROLE"); -bytes32 constant LICENSING_MANAGER_ROLE = keccak256("LICENSING_MANAGER_ROLE"); +// Role that can perform admin tasks on the Licensing Module contracts (setNonCommercialLicenseURI) +bytes32 constant LICENSING_MANAGER_ROLE = keccak256("LICENSING_MANAGER_ROLE"); \ No newline at end of file From ef9e2e9a6ff78108e9ba86be70b653868fc4eddc Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 11 Sep 2023 17:38:32 -0300 Subject: [PATCH 38/42] 1 space inside brackets in imports --- contracts/ip-assets/IIPAssetRegistry.sol | 10 +++++----- test/foundry/mocks/MockLicensingModule.sol | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/ip-assets/IIPAssetRegistry.sol b/contracts/ip-assets/IIPAssetRegistry.sol index 0e514f70..37586f1c 100644 --- a/contracts/ip-assets/IIPAssetRegistry.sol +++ b/contracts/ip-assets/IIPAssetRegistry.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.13; -import {IVersioned} from "../utils/IVersioned.sol"; -import {IIPAssetDataManager} from "./storage/IIPAssetDataManager.sol"; -import {IERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; -import {IERC5218} from "../modules/licensing/IERC5218.sol"; -import {IPAsset} from "../IPAsset.sol"; +import { IVersioned } from "../utils/IVersioned.sol"; +import { IIPAssetDataManager } from "./storage/IIPAssetDataManager.sol"; +import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; +import { IERC5218 } from "../modules/licensing/IERC5218.sol"; +import { IPAsset } from "../IPAsset.sol"; interface IIPAssetRegistry is IVersioned, diff --git a/test/foundry/mocks/MockLicensingModule.sol b/test/foundry/mocks/MockLicensingModule.sol index 05f7f332..e2cdf8d3 100644 --- a/test/foundry/mocks/MockLicensingModule.sol +++ b/test/foundry/mocks/MockLicensingModule.sol @@ -1,9 +1,9 @@ pragma solidity ^0.8.19; -import {ILicensingModule} from "contracts/modules/licensing/ILicensingModule.sol"; -import {IERC5218} from "contracts/modules/licensing/IERC5218.sol"; -import {ITermsProcessor} from "contracts/modules/licensing/terms/ITermsProcessor.sol"; -import {MockTermsProcessor} from "./MockTermsProcessor.sol"; +import { ILicensingModule } from "contracts/modules/licensing/ILicensingModule.sol"; +import { IERC5218 } from "contracts/modules/licensing/IERC5218.sol"; +import { ITermsProcessor } from "contracts/modules/licensing/terms/ITermsProcessor.sol"; +import { MockTermsProcessor } from "./MockTermsProcessor.sol"; library LibMockFranchiseConfig { function getMockFranchiseConfig() From dc297c8a8adda1fa8f4cde7997626501cb869283 Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 11 Sep 2023 18:54:49 -0300 Subject: [PATCH 39/42] fix typo terms --- contracts/modules/licensing/RightsManager.sol | 2 +- .../licensing/terms/ITermsProcessor.sol | 2 +- .../licensing/terms/MultiTermsProcessor.sol | 4 ++-- .../licensing/terms/TimeTermsProcessor.sol | 2 +- .../licensing/terms/TimeTermsProcessor.t.sol | 18 +++++++++--------- test/foundry/mocks/MockTermsProcessor.sol | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/contracts/modules/licensing/RightsManager.sol b/contracts/modules/licensing/RightsManager.sol index da7bc0dd..299cdc9a 100644 --- a/contracts/modules/licensing/RightsManager.sol +++ b/contracts/modules/licensing/RightsManager.sol @@ -302,7 +302,7 @@ abstract contract RightsManager is function _isActiveAndTermsOk(License memory license) view private returns (bool) { if (address(license.termsProcessor) == address(0)) return license.active; - return license.active && license.termsProcessor.tersmExecutedSuccessfully(license.termsData); + return license.active && license.termsProcessor.termsExecutedSuccessfully(license.termsData); } function getLicense(uint256 licenseId) public view returns (License memory, address holder) { diff --git a/contracts/modules/licensing/terms/ITermsProcessor.sol b/contracts/modules/licensing/terms/ITermsProcessor.sol index 44ca2599..0ad4ab61 100644 --- a/contracts/modules/licensing/terms/ITermsProcessor.sol +++ b/contracts/modules/licensing/terms/ITermsProcessor.sol @@ -18,6 +18,6 @@ interface ITermsProcessor is IERC165 { function executeTerms(bytes calldata data) external returns(bytes memory newData); /// returns true if the terms have been executed successfully or they don't need to be executed, false otherwise - function tersmExecutedSuccessfully(bytes calldata data) external view returns(bool); + function termsExecutedSuccessfully(bytes calldata data) external view returns(bool); } \ No newline at end of file diff --git a/contracts/modules/licensing/terms/MultiTermsProcessor.sol b/contracts/modules/licensing/terms/MultiTermsProcessor.sol index 0a82f7b8..b25ad992 100644 --- a/contracts/modules/licensing/terms/MultiTermsProcessor.sol +++ b/contracts/modules/licensing/terms/MultiTermsProcessor.sol @@ -57,13 +57,13 @@ contract MultiTermsProcessor is BaseTermsProcessor { return super.supportsInterface(interfaceId); } - function tersmExecutedSuccessfully(bytes calldata data) external view override returns (bool) { + function termsExecutedSuccessfully(bytes calldata data) external view override returns (bool) { uint256 length = processors.length; bytes[] memory encodedTerms = new bytes[](length); encodedTerms = abi.decode(data, (bytes[])); bool result = true; for (uint256 i = 0; i < length;) { - result = result && processors[i].tersmExecutedSuccessfully(encodedTerms[i]); + result = result && processors[i].termsExecutedSuccessfully(encodedTerms[i]); unchecked { i++; } diff --git a/contracts/modules/licensing/terms/TimeTermsProcessor.sol b/contracts/modules/licensing/terms/TimeTermsProcessor.sol index 1a0efef8..a681cb45 100644 --- a/contracts/modules/licensing/terms/TimeTermsProcessor.sol +++ b/contracts/modules/licensing/terms/TimeTermsProcessor.sol @@ -17,7 +17,7 @@ contract TimeTermsProcessor is BaseTermsProcessor { return abi.encode(config); } - function tersmExecutedSuccessfully(bytes calldata data) external view override returns (bool) { + function termsExecutedSuccessfully(bytes calldata data) external view override returns (bool) { LibDuration.TimeConfig memory config = abi.decode(data, (LibDuration.TimeConfig)); return config.isActive(); } diff --git a/test/foundry/licensing/terms/TimeTermsProcessor.t.sol b/test/foundry/licensing/terms/TimeTermsProcessor.t.sol index 4b2533c5..4e659c6f 100644 --- a/test/foundry/licensing/terms/TimeTermsProcessor.t.sol +++ b/test/foundry/licensing/terms/TimeTermsProcessor.t.sol @@ -44,7 +44,7 @@ contract LicenseRegistryTest is BaseTest { data: abi.encode(config) }); - assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config)), "terms should be inactive before start time"); + assertFalse(processor.termsExecutedSuccessfully(abi.encode(config)), "terms should be inactive before start time"); vm.prank(licenseHolder); licenseId = ipAssetRegistry.createLicense( @@ -60,12 +60,12 @@ contract LicenseRegistryTest is BaseTest { vm.prank(licenseHolder); ipAssetRegistry.executeTerms(licenseId); assertFalse(ipAssetRegistry.isLicenseActive(licenseId), "execution is a noop if start time set"); - assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config)), "execution is a noop if start time set"); + assertFalse(processor.termsExecutedSuccessfully(abi.encode(config)), "execution is a noop if start time set"); vm.warp(startTime + 100); assertTrue(ipAssetRegistry.isLicenseActive(licenseId), "license should be active after start time"); - assertTrue(processor.tersmExecutedSuccessfully(abi.encode(config)), "terms should be active after start time"); + assertTrue(processor.termsExecutedSuccessfully(abi.encode(config)), "terms should be active after start time"); vm.warp(startTime + ttl + 1); - assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config)), "terms should be inactive after ttl"); + assertFalse(processor.termsExecutedSuccessfully(abi.encode(config)), "terms should be inactive after ttl"); assertFalse(ipAssetRegistry.isLicenseActive(licenseId), "license should be inactive after ttl"); } @@ -85,7 +85,7 @@ contract LicenseRegistryTest is BaseTest { data: abi.encode(config) }); - assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config))); + assertFalse(processor.termsExecutedSuccessfully(abi.encode(config))); vm.prank(licenseHolder); licenseId = ipAssetRegistry.createLicense( @@ -99,12 +99,12 @@ contract LicenseRegistryTest is BaseTest { termsConfig ); assertFalse(ipAssetRegistry.isLicenseActive(licenseId)); - assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config))); + assertFalse(processor.termsExecutedSuccessfully(abi.encode(config))); vm.warp(block.timestamp + 100); assertFalse(ipAssetRegistry.isLicenseActive(licenseId)); - assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config))); + assertFalse(processor.termsExecutedSuccessfully(abi.encode(config))); vm.warp(block.timestamp + ttl + 1); - assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config))); + assertFalse(processor.termsExecutedSuccessfully(abi.encode(config))); assertFalse(ipAssetRegistry.isLicenseActive(licenseId)); } @@ -124,7 +124,7 @@ contract LicenseRegistryTest is BaseTest { data: abi.encode(config) }); - assertFalse(processor.tersmExecutedSuccessfully(abi.encode(config)), "terms should be inactive before start time"); + assertFalse(processor.termsExecutedSuccessfully(abi.encode(config)), "terms should be inactive before start time"); vm.prank(licenseHolder); licenseId = ipAssetRegistry.createLicense( diff --git a/test/foundry/mocks/MockTermsProcessor.sol b/test/foundry/mocks/MockTermsProcessor.sol index a4a9ac9e..77ea7cbf 100644 --- a/test/foundry/mocks/MockTermsProcessor.sol +++ b/test/foundry/mocks/MockTermsProcessor.sol @@ -26,7 +26,7 @@ contract MockTermsProcessor is ITermsProcessor, ERC165 { return data; } - function tersmExecutedSuccessfully( + function termsExecutedSuccessfully( bytes calldata ) external view override returns (bool) { return _success; From cd1aca69ccfc300588a752da4ea2d95116365244 Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 11 Sep 2023 19:17:50 -0300 Subject: [PATCH 40/42] refactor magic number --- contracts/ip-assets/IPAssetRegistry.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/ip-assets/IPAssetRegistry.sol b/contracts/ip-assets/IPAssetRegistry.sol index c6d77e73..51330ce8 100644 --- a/contracts/ip-assets/IPAssetRegistry.sol +++ b/contracts/ip-assets/IPAssetRegistry.sol @@ -35,6 +35,7 @@ contract IPAssetRegistry is bytes32 private constant _STORAGE_LOCATION = 0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378; string private constant _VERSION = "0.1.0"; + uint256 private constant _ROOT_IP_ASSET = 0; constructor(address _eventEmitter, address _licensingModule, address _franchiseRegistry) RightsManager(_franchiseRegistry) { // TODO: should Franchise owner be able to change this? @@ -112,7 +113,7 @@ contract IPAssetRegistry is // Derivative works do not have commercial rights unless a deal with the relevant licensor is made if (config.rootIpAssetHasCommercialRights && parentIpAssetId == 0) { // Commercial - _setCommercialRights(ipAssetId, 0, to, config.revoker, config.commercialLicenseUri, config.commercialConfig, config.commercialTerms); + _setCommercialRights(ipAssetId, _ROOT_IP_ASSET, to, config.revoker, config.commercialLicenseUri, config.commercialConfig, config.commercialTerms); } return ipAssetId; @@ -154,7 +155,7 @@ contract IPAssetRegistry is * @param terms for the license to be active */ function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) internal { - uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, true); + uint256 parentLicenseId = parentIpAssetId == _ROOT_IP_ASSET ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, true); _createLicense( ipAssetId, parentLicenseId, From 41545629ed2b12b73ec10ba5573ca8ba5070bb37 Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 11 Sep 2023 20:03:43 -0300 Subject: [PATCH 41/42] comment time processor --- .../licensing/terms/BaseTermsProcessor.sol | 14 ++++++++++++++ .../licensing/terms/MultiTermsProcessor.sol | 19 ++++++++++++++++++- .../licensing/terms/TimeTermsProcessor.sol | 13 +++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/contracts/modules/licensing/terms/BaseTermsProcessor.sol b/contracts/modules/licensing/terms/BaseTermsProcessor.sol index f1dee3f8..5e2c655a 100644 --- a/contracts/modules/licensing/terms/BaseTermsProcessor.sol +++ b/contracts/modules/licensing/terms/BaseTermsProcessor.sol @@ -6,6 +6,17 @@ import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + +/** + * @title BaseTermsProcessor + * @notice Base contract for licensing terms processors, which encode, decode and execute the terms set on an IERC5218 license parameters, + * in particular the TermsProcessorConfig struct for the terms parameter in createLicense(). + * TermsProcessors need to be deployed once per AUTHORIZED_EXECUTOR, which is usually each Franchise IPAssetRegistry. + * @dev TermsProcessor are intended to be reused accross the protocol, so they should be generic enough to be used by different modules. + * Most will be stateless, and if a terms processor needs to update something license specific, + * it should return the updated encoded data in executeTerms() so it is stored back on the license. + * There could be cases where other methods or state is needed for more complicated flows. + */ abstract contract BaseTermsProcessor is ITermsProcessor, ERC165 { address public immutable AUTHORIZED_EXECUTOR; @@ -22,12 +33,15 @@ abstract contract BaseTermsProcessor is ITermsProcessor, ERC165 { _; } + /// @inheritdoc ITermsProcessor function executeTerms(bytes calldata data) onlyAuthorizedExecutor external returns(bytes memory newData) { return _executeTerms(data); } + /// method defining the actual execution of the terms, with no access control for caller, to be implemented by the child contract function _executeTerms(bytes calldata data) internal virtual returns (bytes memory newData); + function supportsInterface( bytes4 interfaceId ) public view virtual override(ERC165, IERC165) returns (bool) { diff --git a/contracts/modules/licensing/terms/MultiTermsProcessor.sol b/contracts/modules/licensing/terms/MultiTermsProcessor.sol index b25ad992..02eebf3a 100644 --- a/contracts/modules/licensing/terms/MultiTermsProcessor.sol +++ b/contracts/modules/licensing/terms/MultiTermsProcessor.sol @@ -5,6 +5,13 @@ import { ITermsProcessor } from "./ITermsProcessor.sol"; import { BaseTermsProcessor } from "./BaseTermsProcessor.sol"; import { EmptyArray, LengthMismatch } from "contracts/errors/General.sol"; +/** + * NOTE: this contract is not tested yet, do not use. + * @title MultiTermsProcessor + * @author Raul Martinez + * @notice Contract that allow to compose multiple terms processors into one, to allow for complex license arrangements. + * Either all processors are executed successfully, or none are. + */ contract MultiTermsProcessor is BaseTermsProcessor { error TooManyTermsProcessors(); @@ -12,12 +19,14 @@ contract MultiTermsProcessor is BaseTermsProcessor { ITermsProcessor[] public processors; - uint256 public constant MAX_PROCESSORS = 100; + /// arbitrary limit to avoid gas limit issues. If the processors are complex, gas DOS might be reached anyway. + uint256 public constant MAX_PROCESSORS = 50; constructor(address authorizedExecutor, ITermsProcessor[] memory _processors) BaseTermsProcessor(authorizedExecutor) { _setProcessors(_processors); } + /// Sets the processors to be executed in order. function _setProcessors(ITermsProcessor[] memory _processors) private { if (_processors.length == 0) revert EmptyArray(); if (_processors.length > MAX_PROCESSORS) @@ -26,6 +35,12 @@ contract MultiTermsProcessor is BaseTermsProcessor { emit ProcessorsSet(_processors); } + /** + * Decode the data into an array of bytes with length == processors length, and execute each processor in order. + * Encode the results into a new array of bytes and return it. + * @param data must be decodable into an array of bytes with length == processors length. + * @return newData the encoded bytes array with the results of each processor execution. + */ function _executeTerms(bytes calldata data) internal override returns (bytes memory newData) { uint256 length = processors.length; bytes[] memory encodedTerms = new bytes[](length); @@ -40,6 +55,7 @@ contract MultiTermsProcessor is BaseTermsProcessor { return abi.encode(newEncodedTerms); } + /// ERC165 interface support, but for ITermsProcessor it only returns true if only all processors support the interface. function supportsInterface( bytes4 interfaceId ) public view override returns (bool) { @@ -57,6 +73,7 @@ contract MultiTermsProcessor is BaseTermsProcessor { return super.supportsInterface(interfaceId); } + /// Checks if all the terms are executed, in order. If one fails, it returns false. function termsExecutedSuccessfully(bytes calldata data) external view override returns (bool) { uint256 length = processors.length; bytes[] memory encodedTerms = new bytes[](length); diff --git a/contracts/modules/licensing/terms/TimeTermsProcessor.sol b/contracts/modules/licensing/terms/TimeTermsProcessor.sol index a681cb45..8900b4f6 100644 --- a/contracts/modules/licensing/terms/TimeTermsProcessor.sol +++ b/contracts/modules/licensing/terms/TimeTermsProcessor.sol @@ -4,11 +4,23 @@ pragma solidity ^0.8.19; import { BaseTermsProcessor } from "./BaseTermsProcessor.sol"; import { LibDuration } from "../../timing/LibDuration.sol"; +/** + * @title TimeTermsProcessor + * @author Raul Martinez + * @notice Processor to set time limits to Licenses up to a Time To Live (TTL). It has 2 modes of operation: + * 1- Timer starts on a specific date set on License creation, and ends after a certain amount of time. + * To do this, set startTime to a timestemp when encoding the terms in createLicense(). + * 2- Timer starts on License execution, and ends after a certain amount of time. + * To do this, set startTime to LibDuration.START_TIME_NOT_SET (0) when encoding the terms in createLicense(). + * The processor will set the startTime to the block.timestamp when the terms are executed. + * Use case for this would be to indicate "this license is valid within 1 year after the first time it is used" + */ contract TimeTermsProcessor is BaseTermsProcessor { using LibDuration for LibDuration.TimeConfig; constructor(address authorizedExecutor) BaseTermsProcessor(authorizedExecutor) {} + /// If startTime is not set, set it to block.timestamp and return the new encoded data. If startTime is set, return the same data. function _executeTerms(bytes calldata data) internal virtual override returns (bytes memory newData) { LibDuration.TimeConfig memory config = abi.decode(data, (LibDuration.TimeConfig)); if (config.startTime == LibDuration.START_TIME_NOT_SET) { @@ -17,6 +29,7 @@ contract TimeTermsProcessor is BaseTermsProcessor { return abi.encode(config); } + /// returns true if the current block.timestamp is within the start and start + ttl, false otherwise function termsExecutedSuccessfully(bytes calldata data) external view override returns (bool) { LibDuration.TimeConfig memory config = abi.decode(data, (LibDuration.TimeConfig)); return config.isActive(); From f10a04f4dbf500ac5ef0dac3be818de636610c6a Mon Sep 17 00:00:00 2001 From: Leeren Chang Date: Mon, 11 Sep 2023 20:06:00 -0500 Subject: [PATCH 42/42] Fixes collect tests --- test/foundry/utils/BaseTest.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/foundry/utils/BaseTest.sol b/test/foundry/utils/BaseTest.sol index 4d34521a..0f0ed715 100644 --- a/test/foundry/utils/BaseTest.sol +++ b/test/foundry/utils/BaseTest.sol @@ -106,7 +106,8 @@ contract BaseTest is BaseTestUtils, ProxyHelper { // Register Franchise (will create IPAssetRegistry and associated LicenseRegistry) FranchiseRegistry.FranchiseCreationParams memory params = FranchiseRegistry.FranchiseCreationParams("FranchiseName", "FRN", "description", "tokenURI"); - (uint256 franchiseId, address ipAssets) = franchiseRegistry.registerFranchise(params); + address ipAssets; + (franchiseId, ipAssets) = franchiseRegistry.registerFranchise(params); ipAssetRegistry = IPAssetRegistry(ipAssets); licenseRegistry = ILicenseRegistry(ipAssetRegistry.getLicenseRegistry()); @@ -196,4 +197,4 @@ contract BaseTest is BaseTestUtils, ProxyHelper { vm.prank(ipAssetOwner); return ipAssetRegistry.createIPAsset(IPAsset(ipAssetType), "name", "description", "mediaUrl", ipAssetOwner, 0); } -} \ No newline at end of file +}