Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cov-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,22 @@ By replacing discretionary decisions with deterministic smart contract execution

## System Overview

<<<<<<< HEAD
The protocol is composed of three hierarchical layers, each with a clear domain of responsibility, from foundational governance to operational logic and enforcement:
=======
The architecture is composed of three modular layers, each with distinct responsibilities:
>>>>>>> origin/main

![image](https://github.com/user-attachments/assets/a1b2ead5-c1ff-48df-b48b-ff5d46762ac1)

### Level 3 — Rights Management
Executes deterministic policy enforcement in real time, ensuring strict compliance with approved access and usage conditions.

<<<<<<< HEAD
- **Rights**: A module that manages policies authorization, defines usage conditions, and validates content custody. It ensures that only authorized entities can access or manage content, while enforcing predefined rules for usage and custody validation.
=======
- **Rights:** Enforces access and custody conditions at runtime by resolving permissions and validating compliance against approved policies.
>>>>>>> origin/main

### Level 2 — Operational Layer: Assets, Policies, Custody & Finance
Coordinates asset lifecycle, programmable policies, decentralized custody, and financial execution under governance-defined logic.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ pragma solidity 0.8.26;
interface IRightsAssetCustodianVerifiable {
/// @notice Checks whether a custodian is currently assigned to a holder.
/// @dev Returns true only if the custodian is active and listed for the specified holder.
/// @param holder The address of the asset rights holder.
/// @param custodian The address of the custodian to verify.
/// @param holder The address of the asset rights holder.
/// @return True if `custodian` is valid and assigned to `holder`, false otherwise.
function isCustodian(address holder, address custodian) external view returns (bool);
function isCustodian(address custodian, address holder) external view returns (bool);

/// @notice Returns a custodian selected by a probabilistic balancing algorithm.
/// @dev The selection is based on priority, demand and economic weight (balance).
Expand All @@ -28,9 +28,9 @@ interface IRightsAssetCustodianVerifiable {

/// @notice Calculates the weighted score of a custodian for a specific holder and currency.
/// @dev Used to externally query the score that influences custodian selection.
/// @param holder The address of the rights holder.
/// @param custodian The address of the custodian.
/// @param holder The address of the rights holder.
/// @param currency The token used to evaluate economic backing.
/// @return The computed weight used in the balancing algorithm.
function calcWeight(address holder, address custodian, address currency) external view returns (uint256);
function calcWeight(address custodian, address holder, address currency) external view returns (uint256);
}
5 changes: 4 additions & 1 deletion contracts/economics/MMC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ERC20Votes } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20

// https://eips.ethereum.org/EIPS/eip-2612 - permit
// https://eips.ethereum.org/EIPS/eip-1363 - payable
// TODO upgradeable feature + DAO

/// @title Multimedia Coin (MMC)
/// @notice ERC20 token with governance and permit functionality.
Expand All @@ -22,7 +23,9 @@ contract MMC is ERC20, ERC20Permit, ERC20Votes {
_mint(initialHolder, totalSupply * (10 ** 18));
}

// TODO allowed restricted burn by treasury
function burn(uint256 amount) public virtual {
_burn(msg.sender, amount);
}

/// @inheritdoc IERC20Permit
function nonces(address owner) public view override(ERC20Permit, Nonces) returns (uint256) {
Expand Down
15 changes: 10 additions & 5 deletions contracts/economics/Tollgate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ contract Tollgate is Initializable, UUPSUpgradeable, AccessControlledUpgradeable
/// @param currency The address of the unsupported currency.
error UnsupportedCurrency(address target, address currency);

/// @notice Error for invalid target contexts.
/// @notice Error for invalid target scheme.
/// @param target The address of the invalid target context.
error InvalidTargetContext(address target);
error InvalidTargetScheme(address target);

/// @notice Error for invalid basis point fees.
/// @param bps The provided basis point value.
Expand All @@ -64,19 +64,24 @@ contract Tollgate is Initializable, UUPSUpgradeable, AccessControlledUpgradeable

/// @notice Allows execution if the scheme is accepted or if support cannot be determined.
/// @dev This modifier checks whether the `target` contract explicitly supports the scheme.
/// If `isFeeSchemeSupported` exists and returns `false`, the call is reverted with InvalidTargetContext.
/// If `isFeeSchemeSupported` exists and returns `false`, the call is reverted with InvalidTargetScheme.
/// If the call to `v` fails (e.g., target does not implement the function or reverts) allowed by default.
/// This enables compatibility with contracts that do not implement scheme validation.
/// @param scheme The scheme to validate.
/// @param target The address of the contract expected to support the scheme.
modifier onlySupportedScheme(T.Scheme scheme, address target) {
// if target is zero address, revert with InvalidTargetScheme error
if (target == address(0) || target.code.length == 0) {
revert InvalidTargetScheme(target);
}

bytes memory callData = abi.encodeCall(IFeeSchemeValidator.isFeeSchemeSupported, (scheme));
(bool success, bytes memory result) = target.call(callData);
// if the call was successful, the target implements the method and returned a result.
// decode the result and validate the scheme acceptance.
if (success) {
bool ok = abi.decode(result, (bool));
if (!ok) revert InvalidTargetContext(target);
if (!ok) revert InvalidTargetScheme(target);
}

// if call fails → by default all schemes are allowed
Expand Down Expand Up @@ -144,7 +149,7 @@ contract Tollgate is Initializable, UUPSUpgradeable, AccessControlledUpgradeable
// Example: If the target is the policy manager contract, the currency is MMC (ERC20 token),
// and the scheme is NOMINAL, setting a fee of 10% means:
// "In the policy manager contract, for MMC, using a nominal scheme, the fee is 10%."
if (target == address(0)) revert InvalidTargetContext(target);
if (target == address(0)) revert InvalidTargetScheme(target);
bytes32 composedKey = _computeComposedKey(target, currency, scheme);

_targetScheme[target] = scheme; // eg: rights manager => FLAT
Expand Down
4 changes: 2 additions & 2 deletions contracts/lifecycle/HookRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ contract HookRegistry is Initializable, UUPSUpgradeable, AccessControlledUpgrade
/// @notice Approves a registered hook contract.
/// @param hook The address of the hook to be approved.
/// Emits a HookApproved event upon success.
function approve(address hook) external onlyValidHook(hook) restricted {
function approve(address hook) external restricted {
_approve(uint160(hook));
emit HookApproved(hook, msg.sender);
}

/// @notice Revokes a previously approved hook contract.
/// @param hook The address of the hook to revoke.
/// Emits a HookRevoked event upon success.
function reject(address hook) external onlyValidHook(hook) restricted {
function reject(address hook) external restricted {
_revoke(uint160(hook));
emit HookRevoked(hook, msg.sender);
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/policies/PolicyAudit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ contract PolicyAudit is Initializable, UUPSUpgradeable, AccessControlledUpgradea
/// @notice Approves the audit of a given policy by a specified auditor.
/// @param policy The address of the policy to be audited.
/// @dev This function emits the PolicyApproved event upon successful audit approval.
function approve(address policy) external onlyValidPolicy(policy) restricted {
function approve(address policy) external restricted {
_approve(uint160(policy));
emit PolicyApproved(policy, msg.sender);
}

/// @notice Revokes the audit of a given policy by a specified auditor.
/// @param policy The address of the policy whose audit is to be revoked.
/// @dev This function emits the PolicyRevoked event upon successful audit revocation.
function reject(address policy) external onlyValidPolicy(policy) restricted {
function reject(address policy) external restricted {
_revoke(uint160(policy));
emit PolicyRevoked(policy, msg.sender);
}
Expand Down
17 changes: 14 additions & 3 deletions contracts/rights/RightsAssetCustodian.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ contract RightsAssetCustodian is Initializable, UUPSUpgradeable, AccessControlle
/// @param demand The total number of holders currently assigned to the custodian (under custody).
event CustodialRevoked(address indexed revokedCustody, address indexed rightsHolder, uint256 demand);

/// @notice Emitted when a priority is set or updated for a custodian by a rights holder.
/// @param holder The address of the rights holder.
/// @param custodian The address of the custodian whose priority was updated.
/// @param priority The new priority value set by the holder.
event PrioritySet(address indexed holder, address indexed custodian, uint256 priority);

/// @dev Error that is thrown when a content hash is already registered.
error InvalidInactiveCustodian();

Expand All @@ -60,6 +66,10 @@ contract RightsAssetCustodian is Initializable, UUPSUpgradeable, AccessControlle
/// @dev Error when failing to revoke custody from a custodian.
error RevokeCustodyFailed(address custodian, address holder);

/// @dev Error thrown when an invalid priority value is provided.
/// @param priority The invalid priority value provided.
error InvalidPriority(uint256 priority);

/// @dev Modifier to check if the custodian is active and not blocked.
/// @param custodian The custodian address to check.
modifier onlyActiveCustodian(address custodian) {
Expand Down Expand Up @@ -158,7 +168,7 @@ contract RightsAssetCustodian is Initializable, UUPSUpgradeable, AccessControlle
/// @notice Checks if the given custodian is a custodian for the specified content holder
/// @param holder The address of the asset holder.
/// @param custodian The address of the custodian to check.
function isCustodian(address holder, address custodian) external view returns (bool) {
function isCustodian(address custodian, address holder) external view returns (bool) {
return _custodians[holder].contains(custodian) && _isValidActiveCustodian(custodian);
}

Expand Down Expand Up @@ -288,8 +298,9 @@ contract RightsAssetCustodian is Initializable, UUPSUpgradeable, AccessControlle
/// @param holder The address of the rights holder.
/// @param priority The priority value (minimum 1).
function _setPriority(address custodian, address holder, uint256 priority) private {
bytes32 relation = _computeComposedKey(holder, custodian);
_priority[relation] = priority > 0 ? priority : 1; // default 1
if (priority == 0) revert InvalidPriority(priority);
_priority[_computeComposedKey(holder, custodian)] = priority;
emit PrioritySet(msg.sender, custodian, priority);
}

/// @dev Computes the weight of a custodian using the formula:
Expand Down
Loading