Skip to content

Commit

Permalink
example: added gated content example imp
Browse files Browse the repository at this point in the history
  • Loading branch information
geolffreym committed Sep 11, 2024
1 parent 7ffabc8 commit 31bc502
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 33 deletions.
8 changes: 6 additions & 2 deletions contracts/interfaces/IPolicy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ interface IPolicy {
/// @notice Returns the string identifier associated with the policy.
function name() external pure returns (string memory);

/// @notice Returns the business/strategy model implemented by the policy.
/// @return A detailed description of the subscription policy as bytes.
function description() external pure override returns (bytes memory);

/// @notice Retrieves the access terms for a specific account and content ID.
/// @param account The address of the account for which access terms are being retrieved.
/// @param contentId The ID of the content associated with the access terms.
/// @return The access terms as a `bytes` array, which can contain any necessary data
/// @return The access terms as a `bytes` array, which can contain any necessary data
/// for validating on-chain or off-chain access. eg: PILTerms https://docs.story.foundation/docs/pil-terms
function terms(
address account,
Expand All @@ -30,7 +34,7 @@ interface IPolicy {
/// @dev This method is expected to be called only by RM contract and its used to establish
/// any logic related to access, validations, etc...
/// @param deal The deal object containing the terms agreed upon between the content holder and the account.
/// @param data Additional data required for processing the deal.
/// @param data Additional data required for processing the deal.
/// @return bool A boolean indicating whether the deal was successfully executed (`true`) or not (`false`).
/// @return string A message providing context for the execution result.
function exec(
Expand Down
72 changes: 72 additions & 0 deletions examples/GatedPolicy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "contracts/base/BasePolicy.sol";
import "contracts/interfaces/IPolicy.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; // For NFT gating

/// @title GatedContentPolicy
/// @notice Implements a content access policy where users must meet specific criteria to access the gated content.
contract GatedContentPolicy is BasePolicy, IPolicy {
address public nftToken; // Address of the NFT token used for gating access.
mapping(address => mapping(uint256 => bool)) public accessList; // Tracks access by contentId.

/// @notice Constructor for the GatedContentPolicy contract.
/// @param rmAddress Address of the Rights Manager (RM) contract.
/// @param ownershipAddress Address of the Ownership contract.
/// @param nftTokenAddress Address of the NFT token contract used for gating.
constructor(
address rmAddress,
address ownershipAddress,
address nftTokenAddress
) BasePolicy(rmAddress, ownershipAddress) {
nftToken = nftTokenAddress;
}

/// @notice Register a user on the whitelist for specific content.
/// @param user The address of the user to be whitelisted.
/// @param contentId The ID of the content.
function addToWhitelist(address user, uint256 contentId) external onlyOwner {
accessList[user][contentId] = true;
}

/// @notice Check whether a user meets the access criteria for a specific content.
/// @param account The address of the account to check.
/// @param contentId The ID of the content to check.
/// @return bool Returns true if the user has access to the content.
function comply(
address account,
uint256 contentId
) external view override returns (bool) {
bool ownsNFT = IERC721(nftToken).balanceOf(account) > 0;
bool isWhitelisted = accessList[account][contentId];

return ownsNFT || isWhitelisted;
}

/// @notice Execute the logic of access validation.
/// @param deal The deal object containing the terms agreed upon between the content holder and the user.
/// @param data Additional data needed for processing the deal.
/// @return bool A boolean indicating whether the execution was successful.
/// @return string A message providing context for the execution result.
function exec(
T.Deal calldata deal,
bytes calldata data
) external onlyRM returns (bool, string memory) {
if (comply(deal.account, deal.contentId)) {
return (true, "Access granted");
} else {
return (false, "Access denied");
}
}

/// @notice Returns a detailed description of the gated content policy.
function description() external pure override returns (bytes memory) {
return abi.encodePacked(
"The GatedContentPolicy restricts access to content based on user criteria such as owning a specific NFT, ",
"being whitelisted by the content holder, or paying an access fee. Users must fulfill at least one of these criteria ",
"to access the gated content.";
)
}

}
82 changes: 60 additions & 22 deletions examples/RentalPolicy.sol
Original file line number Diff line number Diff line change
@@ -1,30 +1,66 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "contracts/base/BasePolicy.sol";
import "contracts/interfaces/IPolicy.sol";
import "contracts/libraries/Types.sol";
import "contracts/libraries/TreasuryHelper.sol";

/// @title RentalPolicy
/// @notice This contract implements the IPolicy interface to manage content rental terms.
/// It allows for registering content with rental durations and prices and handles the rental process.
contract RentalPolicy is BasePolicy, IPolicy {
using TreasuryHelper for address;

/// @dev Structure to hold rental details for content.
struct Content {
uint256 rentalDuration;
uint256 price;
uint256 rentalDuration; // Duration in seconds for which content is rented.
uint256 price; // Price to rent the content.
}

// Mapping to store content data by content ID.
mapping(uint256 => Content) public contents;

// Mapping to track rental expiration timestamps for each account and content.
mapping(address => mapping(uint256 => uint256)) private rentals;

/// @notice Constructor for the RentalPolicy contract.
/// @param rmAddress Address of the Rights Manager (RM) contract.
/// @param ownershipAddress Address of the Ownership contract.
constructor(
address rmAddress,
address ownershipAddress
) BasePolicy(rmAddress, ownershipAddress) {}

/// @notice Returns the name of the policy.
/// @return The name of the policy, "RentalPolicy".
function name() external pure override returns (string memory) {
return "RentalPolicy";
}

/// @notice Returns the business/strategy model implemented by the policy.
/// @return A detailed description of the policy's rental model.
function description() external pure override returns (bytes memory) {
return
abi.encodePacked(
"The RentalPolicy implements a content rental strategy where users pay a fixed fee to access digital content "
"for a limited period. The strategy is focused on temporary access, allowing content holders to monetize their assets "
"through short-term rentals without transferring full ownership. Key aspects of this policy include: \n\n"
"1) Flexible rental duration: Each content can have a customized rental period defined by the content holder. \n"
"2) Pay-per-use model: Users pay a one-time fee per rental, providing a cost-effective way to access content without a long-term commitment.\n "
"3) Automated rental management: Once the rental fee is paid, the content becomes accessible to the user for the specified duration,\n "
"after which access is automatically revoked.\n "
"4) Secure revenue distribution: The rental fee is transferred directly to the content holder through the TreasuryHelper, ensuring secure and \n"
"timely payments. This policy provides a straightforward and transparent way for content owners to generate revenue from their digital assets \n"
"while giving users temporary access to premium content."
);
}

/// @notice Registers content with rental terms including duration and price.
/// @dev Only callable for registered content IDs.
/// @param contentId The ID of the content to register.
/// @param rentalDuration Duration (in seconds) for which the content will be rented.
/// @param price The price to rent the content.
function registerContent(
uint256 contentId,
uint256 rentalDuration,
Expand All @@ -33,64 +69,66 @@ contract RentalPolicy is BasePolicy, IPolicy {
contents[contentId] = Content(rentalDuration, price);
}

/// @dev Internal function to register the rental of content for a specific account.
/// @param account The address of the account renting the content.
/// @param contentId The ID of the content being rented.
/// @param expire The expiration time (in seconds) for the rental.
function _registerRent(
address account,
uint256 contentId,
uint256 expire
) private {
// setup renting condition..
rentals[account][contentId] = block.timestamp + expire;
}

/// @notice Exec the deal between the content holder and the account based on the policy's rules.
/// @dev This method is expected to be called only by RM contract and its used to establish
/// any logic related to access, validations, etc...
/// @param deal The deal object containing the terms agreed upon between the content holder and the account.
/// @param data Additional data required for processing the deal.
/// @return bool A boolean indicating whether the deal was successfully executed (`true`) or not (`false`).
/// @return string A message providing context for the execution result.
/// @notice Executes the deal between the content holder and the account based on the policy's rules.
/// @dev This function is expected to be called only by the Rights Manager (RM) contract.
/// It handles any logic related to access and validation of the rental terms.
/// @param deal The deal object containing the agreed terms between the content holder and the account.
/// @param data Additional data required for processing the deal, e.g., content ID.
/// @return bool Indicates whether the deal was successfully executed.
/// @return string Provides a message describing the result of the execution.
function exec(
T.Deal calldata deal,
bytes calldata data
) external onlyRM returns (bool, string memory) {
uint256 contentId = abi.decode(data, (uint256));
Content memory content = contents[contentId];

if (contentId == 0) return (false, "Invalid content id");
if (contentId == 0) return (false, "Invalid content ID");
if (getHolder(contentId) != deal.holder)
return (false, "Invalid content id holder");
return (false, "Invalid content ID holder");
if (deal.total < content.price)
return (false, "Insufficient funds for rental");

// The rigths manager send funds to policy before call this method
// then the logic of distribution could be here...
// example transfering all the revenues to content holder..
// Transfer the funds to the content holder.
deal.holder.transfer(deal.available, deal.currency);
// Register the rental for the account with the rental duration.
_registerRent(deal.account, contentId, content.rentalDuration);
return (true, "ok");

return (true, "Rental successfully executed");
}

/// @notice Retrieves the access terms for a specific account and content ID.
/// @param account The address of the account for which access terms are being retrieved.
/// @param contentId The ID of the content associated with the access terms.
/// @return The access terms as a `bytes` array, which can contain any necessary data
/// for validating on-chain or off-chain access. eg: PILTerms https://docs.story.foundation/docs/pil-terms
/// @return The access terms as a `bytes` array, which can contain the rental expiration timestamp.
function terms(
address account,
uint256 contentId
) external view override returns (bytes memory) {
// any data needed to validate by distributors can be returned here..
return abi.encode(rentals[account][contentId]);
}

/// @notice Verify whether the on-chain access terms for an account and content ID are satisfied.
/// @notice Verifies whether the on-chain access terms for an account and content ID are satisfied.
/// @param account The address of the account to check.
/// @param contentId The content ID to check against.
/// @param contentId The ID of the content to check against.
/// @return bool Returns `true` if the rental period is still valid, `false` otherwise.
function comply(
address account,
uint256 contentId
) external view override returns (bool) {
// the condition to validate access to content by the account..
// Check if the current time is before the rental expiration.
return block.timestamp <= rentals[account][contentId];
}
}
43 changes: 34 additions & 9 deletions examples/SubscriptionPolicy.sol
Original file line number Diff line number Diff line change
@@ -1,27 +1,56 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "contracts/base/BasePolicy.sol";
import "contracts/interfaces/IPolicy.sol";
import "contracts/libraries/Types.sol";

/// @title SubscriptionPolicy
/// @notice Implements a subscription-based content access policy, allowing users to subscribe to content catalogs for a set duration.
contract SubscriptionPolicy is BasePolicy, IPolicy {
/// @dev Structure to define a subscription package.
struct Package {
uint256 subscriptionDuration;
uint256 price;
uint256 subscriptionDuration; // Duration in seconds for which the subscription is valid.
uint256 price; // Price of the subscription package.
}

// Mapping from content holder (address) to their subscription package details.
mapping(address => Package) public packages;

// Mapping to track subscription expiration for each user (account) and content holder.
mapping(address => mapping(address => uint256)) private subscriptions;

/// @notice Constructor for the SubscriptionPolicy contract.
/// @param rmAddress Address of the Rights Manager (RM) contract.
/// @param ownershipAddress Address of the Ownership contract.
constructor(
address rmAddress,
address ownershipAddress
) BasePolicy(rmAddress, ownershipAddress) {}

/// @notice Returns the name of the policy.
/// @return The name of the policy, "SubscriptionPolicy".
function name() external pure override returns (string memory) {
return "SubscriptionPolicy";
}

/// @notice Returns the business/strategy model implemented by the policy.
/// @return A detailed description of the subscription policy as bytes.
function description() external pure override returns (bytes memory) {
return
abi.encodePacked(
"This policy implements a subscription-based model where users pay a fixed fee ",
"to access a content holder's catalog for a specified duration.\n\n",
"1) Flexible subscription duration, defined by the content holder.\n",
"2) Recurring revenue streams for content holders.\n",
"3) Immediate access to content catalog during the subscription period.\n",
"4) Automated payment processing."
);
}

/// @notice Registers a subscription package for the content holder.
/// @param subscriptionDuration The duration of the subscription in seconds.
/// @param price The price of the subscription package.
function registerPackage(
uint256 subscriptionDuration,
uint256 price
Expand All @@ -44,10 +73,12 @@ contract SubscriptionPolicy is BasePolicy, IPolicy {
return (false, "Insufficient funds for subscription");

// set rental expire
// Transfer the funds to the content holder.
deal.holder.transfer(deal.available, deal.currency);
uint256 subTime = block.timestamp + pck.subscriptionDuration;
// subscribe to content owner's catalog (content package)
subscriptions[deal.account][deal.holder] = subTime;
return (true, "success");
return (true, "ok");
}

function terms(
Expand All @@ -65,10 +96,4 @@ contract SubscriptionPolicy is BasePolicy, IPolicy {
address holder = getHolder(contentId);
return block.timestamp <= subscriptions[account][holder];
}

// Define cómo se manejarán los pagos de suscripción
function shares(
address account,
uint256 contentId
) external view override returns (T.Shares[] memory) {}
}

0 comments on commit 31bc502

Please sign in to comment.