AI-Generated Content Ownership
What it does:
Defines, records, and enforces ownership and attribution rules for AI-generated content, including splits between prompt creators, model providers, data licensors, and publishers.
Why it matters:
Clarifies ambiguous IP ownership in AI content creation, prevents disputes, enables fair revenue sharing, and creates legally auditable provenance for AI-assisted works.
How it works:
-
Content is registered with a hash representing the final AI-generated output
-
Contributors (prompt author, model owner, dataset licensor, editor) are listed with fixed ownership shares
-
Smart contract stores immutable attribution and percentage splits
-
Monetization revenue is sent directly to the contract
-
Earnings are automatically allocated to contributors based on ownership shares
-
Contributors withdraw funds independently without trust assumptions
-
Ownership records can be referenced by marketplaces, publishers, and licensing systems
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title AIGeneratedContentOwnership
* @author Nam
* @notice Manages attribution and revenue distribution for AI-generated content
*/
contract AIGeneratedContentOwnership is Ownable {
struct Contributor {
address payable wallet;
uint256 share; // out of 10000 (100%)
string role; // prompt, model, dataset, editor, publisher
}
struct Content {
bytes32 contentHash;
string metadataURI;
Contributor[] contributors;
mapping(address => uint256) pendingETH;
mapping(address => mapping(address => uint256)) pendingToken;
bool exists;
}
uint256 public contentCount;
mapping(uint256 => Content) private contents;
// -------------------- EVENTS --------------------
event ContentRegistered(uint256 indexed contentId, bytes32 contentHash);
event RevenueReceived(uint256 indexed contentId, address token, uint256 amount);
event Withdrawal(uint256 indexed contentId, address indexed contributor, address token, uint256 amount);
// -------------------- CONTENT REGISTRATION --------------------
function registerContent(
bytes32 _contentHash,
string calldata _metadataURI,
address[] calldata _wallets,
uint256[] calldata _shares,
string[] calldata _roles
) external onlyOwner {
require(_wallets.length == _shares.length, "Length mismatch");
require(_wallets.length == _roles.length, "Role mismatch");
require(_wallets.length > 0, "No contributors");
uint256 totalShare;
contentCount++;
Content storage c = contents[contentCount];
c.contentHash = _contentHash;
c.metadataURI = _metadataURI;
c.exists = true;
for (uint256 i = 0; i 0, "No ETH sent");
for (uint256 i = 0; i 0, "Invalid amount");
IERC20(_token).transferFrom(msg.sender, address(this), _amount);
for (uint256 i = 0; i 0, "Nothing to withdraw");
c.pendingETH[msg.sender] = 0;
payable(msg.sender).transfer(amount);
emit Withdrawal(_contentId, msg.sender, address(0), amount);
}
function withdrawToken(uint256 _contentId, address _token) external {
Content storage c = contents[_contentId];
uint256 amount = c.pendingToken[_token][msg.sender];
require(amount > 0, "Nothing to withdraw");
c.pendingToken[_token][msg.sender] = 0;
IERC20(_token).transfer(msg.sender, amount);
emit Withdrawal(_contentId, msg.sender, _token, amount);
}
}