Decentralized Publishing Platform
What it does:
Allows writers and creators to publish articles or digital works in a decentralized manner, with on-chain ownership, monetization, and audience access control.
Why it matters:
Eliminates platform lock-in, censorship risk, and opaque revenue models while giving creators direct ownership of content, readers, and income.
How it works:
-
Creators publish content by registering metadata and ownership on-chain
-
Content itself is stored off-chain (IPFS / Arweave) with on-chain references
-
Access can be free, pay-per-article, subscription-based, or token-gated
-
Readers pay directly to the smart contract for access rights
-
Revenue is distributed automatically to authors and collaborators
-
Ownership and publishing history are immutable and auditable
-
Integrates with wallets, DAOs, NFTs, and external frontends
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title DecentralizedPublishingPlatform
* @author Nam
* @notice On-chain publishing with paywalls and revenue distribution
*/
contract DecentralizedPublishingPlatform is Ownable {
struct Contributor {
address payable wallet;
uint256 share; // out of 10000 (100%)
}
struct Article {
string metadataURI;
uint256 price; // ETH price for access
bool active;
Contributor[] contributors;
mapping(address => bool) hasAccess;
mapping(address => uint256) pendingETH;
mapping(address => mapping(address => uint256)) pendingToken;
}
uint256 public articleCount;
mapping(uint256 => Article) private articles;
// -------------------- EVENTS --------------------
event ArticlePublished(uint256 indexed articleId, string metadataURI);
event AccessPurchased(uint256 indexed articleId, address indexed reader);
event Withdrawal(uint256 indexed articleId, address indexed contributor, address token, uint256 amount);
// -------------------- PUBLISHING --------------------
function publishArticle(
string calldata _metadataURI,
uint256 _price,
address[] calldata _wallets,
uint256[] calldata _shares
) external {
require(_wallets.length == _shares.length, "Length mismatch");
require(_wallets.length > 0, "No contributors");
uint256 totalShare;
articleCount++;
Article storage a = articles[articleCount];
a.metadataURI = _metadataURI;
a.price = _price;
a.active = true;
for (uint256 i = 0; i < _wallets.length; i++) {
totalShare += _shares[i];
a.contributors.push(
Contributor(payable(_wallets[i]), _shares[i])
);
}
require(totalShare == 10000, "Shares must total 100%");
emit ArticlePublished(articleCount, _metadataURI);
}
// -------------------- ACCESS --------------------
function purchaseAccess(uint256 _articleId) external payable {
Article storage a = articles[_articleId];
require(a.active, "Article inactive");
require(!a.hasAccess[msg.sender], "Already accessed");
require(msg.value == a.price, "Incorrect payment");
a.hasAccess[msg.sender] = true;
for (uint256 i = 0; i <a> 0, "Nothing to withdraw");
a.pendingETH[msg.sender] = 0;
payable(msg.sender).transfer(amount);
emit Withdrawal(_articleId, msg.sender, address(0), amount);
}
}