Skip to content

Commit

Permalink
Merge branch 'storyprotocol:main' into fix-name-mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
Spablob authored Dec 3, 2024
2 parents 4c9c71d + 28d1bf2 commit d25b29b
Show file tree
Hide file tree
Showing 13 changed files with 67 additions and 164 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ interface IGraphAwareRoyaltyPolicy is IRoyaltyPolicy {
/// @param ipId The ipId of the IP asset
/// @param ancestorIpId The ancestor ipId of the IP asset
/// @param token The token address to transfer
/// @param amount The amount of tokens to transfer
function transferToVault(address ipId, address ancestorIpId, address token, uint256 amount) external;
/// @return The amount of revenue tokens transferred
function transferToVault(address ipId, address ancestorIpId, address token) external returns (uint256);

/// @notice Returns the royalty percentage between an IP asset and a given ancestor
/// @param ipId The ipId to get the royalty for
Expand Down
18 changes: 3 additions & 15 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,6 @@ library Errors {
/// @notice Provided license template does not match the IP's current license template.
error LicenseRegistry__UnmatchedLicenseTemplate(address ipId, address licenseTemplate, address newLicenseTemplate);

/// @notice Provided license template and terms ID is a duplicate.
error LicenseRegistry__DuplicateLicense(address ipId, address licenseTemplate, uint256 licenseTermsId);

/// @notice Zero address provided for License Template.
error LicenseRegistry__ZeroLicenseTemplate();

Expand Down Expand Up @@ -283,6 +280,9 @@ library Errors {
/// @notice The IP has no attached the same license terms of Group IPA.
error LicenseRegistry__IpHasNoGroupLicenseTerms(address groupId, address licenseTemplate, uint256 licenseTermsId);

/// @notice The IP has already linked to the same parent IP.
error LicenseRegistry__DuplicateParentIp(address ipId, address parentIpId);

/// @notice When Set LicenseConfig the license template cannot be Zero address if royalty percentage is not Zero.
error LicensingModule__LicenseTemplateCannotBeZeroAddressToOverrideRoyaltyPercent();

Expand Down Expand Up @@ -653,15 +653,9 @@ library Errors {
/// @notice Zero claimable royalty.
error RoyaltyPolicyLAP__ZeroClaimableRoyalty();

/// @notice Amount exceeds the claimable royalty.
error RoyaltyPolicyLAP__ExceedsClaimableRoyalty();

/// @notice Above maximum percentage.
error RoyaltyPolicyLAP__AboveMaxPercent();

/// @notice Zero amount provided.
error RoyaltyPolicyLAP__ZeroAmount();

////////////////////////////////////////////////////////////////////////////
// Royalty Policy LRP //
////////////////////////////////////////////////////////////////////////////
Expand All @@ -684,15 +678,9 @@ library Errors {
/// @notice Zero claimable royalty.
error RoyaltyPolicyLRP__ZeroClaimableRoyalty();

/// @notice Claimer is not an ancestor of the IP.
error RoyaltyPolicyLRP__ExceedsClaimableRoyalty();

/// @notice Above maximum percentage.
error RoyaltyPolicyLRP__AboveMaxPercent();

/// @notice Zero amount provided.
error RoyaltyPolicyLRP__ZeroAmount();

////////////////////////////////////////////////////////////////////////////
// IP Royalty Vault //
////////////////////////////////////////////////////////////////////////////
Expand Down
27 changes: 15 additions & 12 deletions contracts/modules/royalty/policies/LAP/RoyaltyPolicyLAP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";

import { IRoyaltyModule } from "../../../../interfaces/modules/royalty/IRoyaltyModule.sol";
import { IGraphAwareRoyaltyPolicy } from "../../../../interfaces/modules/royalty/policies/IGraphAwareRoyaltyPolicy.sol";
Expand Down Expand Up @@ -124,12 +125,14 @@ contract RoyaltyPolicyLAP is
/// @param ipId The ipId of the IP asset
/// @param ancestorIpId The ancestor ipId of the IP asset
/// @param token The token address to transfer
/// @param amount The amount of tokens to transfer
function transferToVault(address ipId, address ancestorIpId, address token, uint256 amount) external whenNotPaused {
/// @return The amount of revenue tokens transferred
function transferToVault(
address ipId,
address ancestorIpId,
address token
) external whenNotPaused returns (uint256) {
RoyaltyPolicyLAPStorage storage $ = _getRoyaltyPolicyLAPStorage();

if (amount == 0) revert Errors.RoyaltyPolicyLAP__ZeroAmount();

uint32 ancestorPercent = $.ancestorPercentLAP[ipId][ancestorIpId];
if (ancestorPercent == 0) {
// on the first transfer to a vault from a specific descendant the royalty between the two is set
Expand All @@ -138,21 +141,21 @@ contract RoyaltyPolicyLAP is
$.ancestorPercentLAP[ipId][ancestorIpId] = ancestorPercent;
}

// check if the amount being claimed is within the claimable royalty amount
// calculate the amount to transfer
IRoyaltyModule royaltyModule = ROYALTY_MODULE;
uint256 totalRevenueTokens = royaltyModule.totalRevenueTokensReceived(ipId, token);
uint256 maxAmount = (totalRevenueTokens * ancestorPercent) / royaltyModule.maxPercent();
uint256 transferredAmount = $.transferredTokenLAP[ipId][ancestorIpId][token];
if (transferredAmount + amount > maxAmount) revert Errors.RoyaltyPolicyLAP__ExceedsClaimableRoyalty();
uint256 amountToTransfer = Math.min(maxAmount - transferredAmount, IERC20(token).balanceOf(address(this)));

// make the revenue token transfer
$.transferredTokenLAP[ipId][ancestorIpId][token] += amountToTransfer;
address ancestorIpRoyaltyVault = royaltyModule.ipRoyaltyVaults(ancestorIpId);
IIpRoyaltyVault(ancestorIpRoyaltyVault).updateVaultBalance(token, amountToTransfer);
IERC20(token).safeTransfer(ancestorIpRoyaltyVault, amountToTransfer);

$.transferredTokenLAP[ipId][ancestorIpId][token] += amount;

IIpRoyaltyVault(ancestorIpRoyaltyVault).updateVaultBalance(token, amount);
IERC20(token).safeTransfer(ancestorIpRoyaltyVault, amount);

emit RevenueTransferredToVault(ipId, ancestorIpId, token, amount);
emit RevenueTransferredToVault(ipId, ancestorIpId, token, amountToTransfer);
return amountToTransfer;
}

/// @notice Returns the amount of royalty tokens required to link a child to a given IP asset
Expand Down
27 changes: 15 additions & 12 deletions contracts/modules/royalty/policies/LRP/RoyaltyPolicyLRP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";

import { IRoyaltyModule } from "../../../../interfaces/modules/royalty/IRoyaltyModule.sol";
import { IGraphAwareRoyaltyPolicy } from "../../../../interfaces/modules/royalty/policies/IGraphAwareRoyaltyPolicy.sol";
Expand Down Expand Up @@ -151,12 +152,14 @@ contract RoyaltyPolicyLRP is
/// @param ipId The ipId of the IP asset
/// @param ancestorIpId The ancestor ipId of the IP asset
/// @param token The token address to transfer
/// @param amount The amount of tokens to transfer
function transferToVault(address ipId, address ancestorIpId, address token, uint256 amount) external whenNotPaused {
/// @return The amount of revenue tokens transferred
function transferToVault(
address ipId,
address ancestorIpId,
address token
) external whenNotPaused returns (uint256) {
RoyaltyPolicyLRPStorage storage $ = _getRoyaltyPolicyLRPStorage();

if (amount == 0) revert Errors.RoyaltyPolicyLRP__ZeroAmount();

uint32 ancestorPercent = $.ancestorPercentLRP[ipId][ancestorIpId];
if (ancestorPercent == 0) {
// on the first transfer to a vault from a specific descendant the royalty between the two is set
Expand All @@ -165,21 +168,21 @@ contract RoyaltyPolicyLRP is
$.ancestorPercentLRP[ipId][ancestorIpId] = ancestorPercent;
}

// check if the amount being claimed is within the claimable royalty amount
// calculate the amount to transfer
IRoyaltyModule royaltyModule = ROYALTY_MODULE;
uint256 totalRevenueTokens = royaltyModule.totalRevenueTokensReceived(ipId, token);
uint256 maxAmount = (totalRevenueTokens * ancestorPercent) / royaltyModule.maxPercent();
uint256 transferredAmount = $.transferredTokenLRP[ipId][ancestorIpId][token];
if (transferredAmount + amount > maxAmount) revert Errors.RoyaltyPolicyLRP__ExceedsClaimableRoyalty();
uint256 amountToTransfer = Math.min(maxAmount - transferredAmount, IERC20(token).balanceOf(address(this)));

// make the revenue token transfer
$.transferredTokenLRP[ipId][ancestorIpId][token] += amountToTransfer;
address ancestorIpRoyaltyVault = royaltyModule.ipRoyaltyVaults(ancestorIpId);
IIpRoyaltyVault(ancestorIpRoyaltyVault).updateVaultBalance(token, amountToTransfer);
IERC20(token).safeTransfer(ancestorIpRoyaltyVault, amountToTransfer);

$.transferredTokenLRP[ipId][ancestorIpId][token] += amount;

IIpRoyaltyVault(ancestorIpRoyaltyVault).updateVaultBalance(token, amount);
IERC20(token).safeTransfer(ancestorIpRoyaltyVault, amount);

emit RevenueTransferredToVault(ipId, ancestorIpId, token, amount);
emit RevenueTransferredToVault(ipId, ancestorIpId, token, amountToTransfer);
return amountToTransfer;
}

/// @notice Returns the amount of royalty tokens required to link a child to a given IP asset
Expand Down
8 changes: 4 additions & 4 deletions contracts/registries/LicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -249,12 +249,12 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
isUsingLicenseToken
);
$.childIps[parentIpIds[i]].add(childIpId);
// determine if duplicate license terms
// determine if duplicate parent IP
bool isNewParent = $.parentIps[childIpId].add(parentIpIds[i]);
bool isNewTerms = $.attachedLicenseTerms[childIpId].add(licenseTermsIds[i]);
if (!isNewParent && !isNewTerms) {
revert Errors.LicenseRegistry__DuplicateLicense(parentIpIds[i], licenseTemplate, licenseTermsIds[i]);
if (!isNewParent) {
revert Errors.LicenseRegistry__DuplicateParentIp(childIpId, parentIpIds[i]);
}
$.attachedLicenseTerms[childIpId].add(licenseTermsIds[i]);
// link child IP to parent IP with license terms
$.parentLicenseTerms[childIpId][parentIpIds[i]] = licenseTermsIds[i];
}
Expand Down
7 changes: 1 addition & 6 deletions test/foundry/integration/flows/grouping/Grouping.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,7 @@ contract Flows_Integration_Grouping is BaseIntegration {
ERC20[] memory tokens = new ERC20[](1);
tokens[0] = mockToken;

royaltyPolicyLAP.transferToVault(
ipAcct[3],
groupId,
address(mockToken),
(10 ether * 10_000_000) / royaltyModule.maxPercent()
);
royaltyPolicyLAP.transferToVault(ipAcct[3], groupId, address(mockToken));

vm.warp(block.timestamp + 7 days + 1);

Expand Down
27 changes: 8 additions & 19 deletions test/foundry/integration/flows/royalty/Royalty.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,7 @@ contract Flows_Integration_Disputes is BaseIntegration {
ipAcct[2] = registerIpAccount(address(mockNFT), 2, u.bob);

vm.expectRevert(
abi.encodeWithSelector(
Errors.LicenseRegistry__DuplicateLicense.selector,
ipAcct[1],
address(pilTemplate),
commRemixTermsId
)
abi.encodeWithSelector(Errors.LicenseRegistry__DuplicateParentIp.selector, ipAcct[2], ipAcct[1])
);
licensingModule.registerDerivativeWithLicenseTokens(ipAcct[2], licenseIds, "", 100e6);

Expand Down Expand Up @@ -187,18 +182,8 @@ contract Flows_Integration_Disputes is BaseIntegration {
uint256 earningsFromMintingFees = 4 * mintingFee;
assertEq(mockToken.balanceOf(vault), earningsFromMintingFees);

royaltyPolicyLAP.transferToVault(
ipAcct[2],
ipAcct[1],
address(mockToken),
(1 ether * 10_000_000) / royaltyModule.maxPercent()
);
royaltyPolicyLAP.transferToVault(
ipAcct[3],
ipAcct[1],
address(mockToken),
(1 ether * 20_000_000) / royaltyModule.maxPercent()
);
royaltyPolicyLAP.transferToVault(ipAcct[2], ipAcct[1], address(mockToken));
royaltyPolicyLAP.transferToVault(ipAcct[3], ipAcct[1], address(mockToken));

vm.warp(block.timestamp + 7 days + 1);

Expand All @@ -210,7 +195,11 @@ contract Flows_Integration_Disputes is BaseIntegration {

assertEq(
aliceBalanceAfter - aliceBalanceBefore,
earningsFromMintingFees + (1 ether * (10_000_000 + 20_000_000)) / royaltyModule.maxPercent()
earningsFromMintingFees +
(1 ether * 20_000_000) /
royaltyModule.maxPercent() + // 20% of the 1 ether payment made to IPAccount3
(mintingFee * 10_000_000) /
royaltyModule.maxPercent() // 10% of the 7 ether mintingFee IPAaccount2 received
);
}

Expand Down
2 changes: 1 addition & 1 deletion test/foundry/mocks/policy/MockRoyaltyPolicyLAP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ contract MockRoyaltyPolicyLAP is IGraphAwareRoyaltyPolicy {
function revenueTokenBalances(address ipId, address token) external view returns (uint256) {}
function snapshotsClaimed(address ipId, address token, uint256 snapshot) external view returns (bool) {}
function snapshotsClaimedCounter(address ipId, address token) external view returns (uint256) {}
function transferToVault(address ipId, address ancestorIpId, address token, uint256 amount) external {}
function transferToVault(address ipId, address ancestorIpId, address token) external returns (uint256) {}
function getPolicyRoyalty(address ipId, address ancestorIpId) external view returns (uint32) {}
function getAncestorPercent(address ipId, address ancestorIpId) external view returns (uint32) {}
function getTransferredTokens(address ipId, address ancestorIpId, address token) external view returns (uint256) {}
Expand Down
2 changes: 1 addition & 1 deletion test/foundry/modules/grouping/GroupingModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ contract GroupingModuleTest is BaseTest {
erc20.approve(address(royaltyModule), 1000);
royaltyModule.payRoyaltyOnBehalf(ipId3, ipOwner3, address(erc20), 1000);
vm.stopPrank();
royaltyPolicyLAP.transferToVault(ipId3, groupId, address(erc20), 100);
royaltyPolicyLAP.transferToVault(ipId3, groupId, address(erc20));
vm.warp(vm.getBlockTimestamp() + 7 days);

vm.expectEmit();
Expand Down
4 changes: 2 additions & 2 deletions test/foundry/modules/licensing/LicensingModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1819,8 +1819,8 @@ contract LicensingModuleTest is BaseTest {
vm.startPrank(ipOwner3);
erc20.approve(address(royaltyModule), 1000);
royaltyModule.payRoyaltyOnBehalf(ipId3, address(0), address(erc20), 1000);
royaltyPolicyLAP.transferToVault(ipId3, ipId2, address(erc20), 100);
royaltyPolicyLAP.transferToVault(ipId3, ipId1, address(erc20), 10);
royaltyPolicyLAP.transferToVault(ipId3, ipId2, address(erc20));
royaltyPolicyLAP.transferToVault(ipId3, ipId1, address(erc20));
vm.stopPrank();
assertEq(erc20.balanceOf(royaltyModule.ipRoyaltyVaults(ipId2)), 100);
assertEq(erc20.balanceOf(royaltyModule.ipRoyaltyVaults(ipId1)), 10);
Expand Down
42 changes: 2 additions & 40 deletions test/foundry/modules/royalty/LAP/RoyaltyPolicyLAP.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,6 @@ contract TestRoyaltyPolicyLAP is BaseTest {
assertEq(royaltyPolicyLAP.getPolicyRoyalty(address(80), address(30)), 20 * 10 ** 6);
}

function test_RoyaltyPolicyLAP_transferToVault_revert_ZeroAmount() public {
vm.expectRevert(Errors.RoyaltyPolicyLAP__ZeroAmount.selector);
royaltyPolicyLAP.transferToVault(address(80), address(10), address(USDC), 0);
}

function test_RoyaltyPolicyLAP_transferToVault_revert_ZeroClaimableRoyalty() public {
address[] memory parents = new address[](3);
address[] memory licenseRoyaltyPolicies = new address[](3);
Expand Down Expand Up @@ -214,42 +209,9 @@ contract TestRoyaltyPolicyLAP is BaseTest {

// first transfer to vault
vm.expectRevert(Errors.RoyaltyPolicyLAP__ZeroClaimableRoyalty.selector);
royaltyPolicyLAP.transferToVault(ipAccount1, address(2000), address(USDC), 100 * 10 ** 6);
royaltyPolicyLAP.transferToVault(ipAccount1, address(2000), address(USDC));
}

function test_RoyaltyPolicyLAP_transferToVault_revert_ExceedsClaimableRoyalty() public {
address[] memory parents = new address[](3);
address[] memory licenseRoyaltyPolicies = new address[](3);
uint32[] memory parentRoyalties = new uint32[](3);
parents[0] = address(10);
parents[1] = address(20);
parents[2] = address(30);
licenseRoyaltyPolicies[0] = address(royaltyPolicyLAP);
licenseRoyaltyPolicies[1] = address(royaltyPolicyLAP);
licenseRoyaltyPolicies[2] = address(royaltyPolicyLAP);
parentRoyalties[0] = uint32(10 * 10 ** 6);
parentRoyalties[1] = uint32(15 * 10 ** 6);
parentRoyalties[2] = uint32(20 * 10 ** 6);
ipGraph.addParentIp(ipAccount1, parents);

vm.startPrank(address(licensingModule));
royaltyModule.onLinkToParents(ipAccount1, parents, licenseRoyaltyPolicies, parentRoyalties, "", 100e6);

// make payment to ip 80
uint256 royaltyAmount = 100 * 10 ** 6;
address receiverIpId = ipAccount1;
address payerIpId = address(3);
vm.startPrank(payerIpId);
USDC.mint(payerIpId, royaltyAmount);
USDC.approve(address(royaltyModule), royaltyAmount);
royaltyModule.payRoyaltyOnBehalf(receiverIpId, payerIpId, address(USDC), royaltyAmount);
vm.stopPrank();

royaltyPolicyLAP.transferToVault(ipAccount1, address(10), address(USDC), 5 * 10 ** 6);

vm.expectRevert(Errors.RoyaltyPolicyLAP__ExceedsClaimableRoyalty.selector);
royaltyPolicyLAP.transferToVault(ipAccount1, address(10), address(USDC), 6 * 10 ** 6);
}
function test_RoyaltyPolicyLAP_transferToVault() public {
address[] memory parents = new address[](3);
address[] memory licenseRoyaltyPolicies = new address[](3);
Expand Down Expand Up @@ -288,7 +250,7 @@ contract TestRoyaltyPolicyLAP is BaseTest {
vm.expectEmit(true, true, true, true, address(royaltyPolicyLAP));
emit RevenueTransferredToVault(ipAccount1, address(10), address(USDC), 10 * 10 ** 6);

royaltyPolicyLAP.transferToVault(ipAccount1, address(10), address(USDC), 10 * 10 ** 6);
royaltyPolicyLAP.transferToVault(ipAccount1, address(10), address(USDC));

uint256 transferredAmountAfter = royaltyPolicyLAP.getTransferredTokens(ipAccount1, address(10), address(USDC));
uint256 usdcAncestorVaultBalanceAfter = USDC.balanceOf(ancestorIpRoyaltyVault);
Expand Down
Loading

0 comments on commit d25b29b

Please sign in to comment.