Minimal Proxy Contract for 0x057509d22e54dbfeb991a00ad6136eaf0373f488.
EIP-1167 - minimal bytecode implementation that delegates all calls to a known address
EIP-1167 - minimal bytecode implementation that delegates all calls to a known address
- Contract name:
- Publication
- Optimization enabled
- false
- Compiler version
- v0.8.12+commit.f00d7308
- Verified at
- 2025-02-21T18:28:21.599709Z
Publication.sol
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "./Editor.sol"; import "./Identity.sol"; import "./HitchensUnorderedAddressSet.sol"; import "./OwnableUpgradeable.sol"; // Decentral Publication Contracts v1.0 (contracts/Publication.sol) /** @title Contract for one specific publication which is owned by the publication's author */ contract Publication is OwnableUpgradeable { using HitchensUnorderedAddressSetLib for HitchensUnorderedAddressSetLib.Set; // Different roles an endorser can have enum EndorserRole{ NONE, READER, SUPERVISOR, EXAMINER } // Different states a publication can have enum PublicationState{ NONE, SUBMITTED, PUBLISHED, RETRACTED } struct Endorsement { bool exists; string document; EndorserRole role; } // Version of the publication contract uint8 public constant version = 1; // List of endorsers of the publication mapping(address => Endorsement) public endorsers; HitchensUnorderedAddressSetLib.Set internal endorsersSet; // Identifier of the publishing journal uint8 public journal; // File hashes for the published document and it's meta data string public document; string public metadata; // State of the publication process PublicationState public state; // Address of the editor proxy contract Editor public editorProxy; // Address of the identity proxy contract Identity public identityProxy; event LogNewPublication(address sender, string document, string metadata, PublicationState state); event LogNewEndorsement(address sender, string document, string comment); event LogUpdateEndorserRole(address sender, address endorser, string document, EndorserRole oldRole, EndorserRole newRole); event LogUpdateEndorsementDocument(address sender, address endorser, string oldDocument, string newDocument); event LogUpdateEndorsementComment(address sender, address endorser, string document, string comment); event LogEndorsementRevocation(address sender, address endorser, string document, string comment); event LogUpdatePublicationDocument(address sender, string oldDocument, string newDocument, string comment); event LogUpdatePublicationMetadata(address sender, string oldMetadata, string newMetadata, string comment); event LogPublicationPublished(address sender, string document, string metadata); event LogPublicationRetraction(address sender, string comment); /** * Called by the author to initialize the contract variables * @param _owner Address of the author of the publication * @param _document Off-chain reference to the main document of the publication (e.g., IPFS hash) * @param _metadata Off-chain reference to the publication metadata document (e.g., IPFS hash) * @param _identity Off-chain reference to the identity document (e.g., IPFS hash) * @param _editorProxy Address to the Editor (proxy) contract with which the clones are initialized * @param _identityProxy Address to the Identity (proxy) contract which maps addresses to real world identities * @param _journal Identifier of the publishing journal */ function initialize(address _owner, string memory _document, string memory _metadata, string memory _identity, Editor _editorProxy, Identity _identityProxy, uint8 _journal) external initializer { require( bytes(_document).length > 0 && bytes(_metadata).length > 0 && bytes(_identity).length > 0, "Input for document, identity, or metadata cannot be empty" ); identityProxy = _identityProxy; try identityProxy.hasIdentity(tx.origin) returns (bool result) { if(!result) { try identityProxy.addIdentity(_identity) { } catch Error(string memory reason){ require(false, reason); } } } catch Error(string memory reason){ require(false, reason); } editorProxy = _editorProxy; journal = _journal; document = _document; metadata = _metadata; state = PublicationState.SUBMITTED; emit LogNewPublication(tx.origin, _document, _metadata, state); __Ownable_init(); transferOwnership(_owner); } /** * Endorse the current publication document hash. Function automatically creates a new mapping * in the Identity contract for the endorser if no mapping exists. * @param _identity Off-chain reference to the identity document (e.g., IPFS hash) * @param _comment Comment of the endorser (e.g., reason for his endorsement) */ function endorse(string memory _identity, string calldata _comment) notRetracted external { require(msg.sender != owner(), "Function cannot be called by the contract owner"); require(!endorsersSet.exists(msg.sender), "Function cannot be called by an endorser"); try identityProxy.hasIdentity(msg.sender) returns (bool result) { if(!result) { try identityProxy.addIdentity(_identity) { } catch { require(false, "Identity could not be stored in Identity contract"); } } } catch { require(false, "Identity could not be stored in Identity contract"); } endorsersSet.insert(msg.sender); endorsers[msg.sender] = Endorsement(true,document,EndorserRole.READER); emit LogNewEndorsement(msg.sender, document, _comment); } /** * Updates the current role assigned to the specified endorser * @param _endorser Address of the endorser whose role is to be updated * @param _role New role to be assigned */ function updateEndorserRole(address _endorser, EndorserRole _role) onlyOwnerOrEditor(keccak256(abi.encodePacked(_endorser,_role))) endorsementExists(_endorser) external { require(_role != EndorserRole.NONE, "Invalid endorser role"); Endorsement memory e = endorsers[_endorser]; EndorserRole oldRole = e.role; e.role = _role; endorsers[_endorser] = e; emit LogUpdateEndorserRole(msg.sender, _endorser, e.document, oldRole, _role); } /** * Updates the endorsement of the caller to the current document hash version */ function updateEndorsementDocument() endorsementExists(msg.sender) external { string memory oldDocument = endorsers[msg.sender].document; endorsers[msg.sender].document = document; emit LogUpdateEndorsementDocument(msg.sender, msg.sender, oldDocument, document); } /** * Updates the endorsement comment of the caller to the provided text * @param _comment New comment of the endorser (e.g., reason for his endorsement) */ function updateEndorsementComment(string calldata _comment) endorsementExists(msg.sender) external { emit LogUpdateEndorsementComment(msg.sender, msg.sender, endorsers[msg.sender].document, _comment); } /** * Updates the endorsement comment of the specified endorser to the provided text * @param _endorser Address of the endorser whose comment is to be updated * @param _comment New comment of the endorser (e.g., reason for his endorsement) */ function updateEndorsementCommentExt(address _endorser, string calldata _comment) onlyEditor(keccak256(abi.encodePacked(_endorser,_comment))) endorsementExists(_endorser) external { emit LogUpdateEndorsementComment(msg.sender, _endorser, endorsers[_endorser].document, _comment); } /** * Removes the endorsement of the caller from the publication * @param _comment Reason for retracting the endorsement */ function revokeEndorsement(string calldata _comment) endorsementExists(msg.sender) external { __deleteEndorsement(msg.sender, _comment); } /** * Removes the endorsement of the specified endorser from the publication * @param _endorser Address of the endorser whose endorsement is to be revoked * @param _comment Reason for revoking the endorsement */ function revokeEndorsementExt(address _endorser, string calldata _comment) onlyOwnerOrEditor(keccak256(abi.encodePacked(_endorser,_comment))) endorsementExists(_endorser) external { __deleteEndorsement(_endorser, _comment); } /** * Publishes the publication if the contract state includes the required document hashes * and at least one privileged edorsement (i.e., from an endorser with supervisor or examiner role) */ function publish() onlyEditor(keccak256("")) external { require(state == PublicationState.SUBMITTED, "Function requires contract state: submitted"); require( bytes(document).length > 0 && bytes(metadata).length > 0, "Input for document or metadata cannot be empty" ); uint counter = 0; for(uint i = 0; i < endorsersSet.count(); i++) { Endorsement memory e = endorsers[endorsersSet.keyAtIndex(i)]; if((e.role == EndorserRole.SUPERVISOR || e.role == EndorserRole.EXAMINER) && keccak256(bytes(e.document)) == keccak256(bytes(document))) { counter++; } } require(counter >= 1,"Insufficient number of privileged endorsements"); state = PublicationState.PUBLISHED; emit LogPublicationPublished(msg.sender, document, metadata); } /** * Update the document hash * @param _document Off-chain reference to the new main document of the publication (e.g., IPFS hash) * @param _comment Comment describing the document changes */ function updateDocument(string memory _document, string calldata _comment) onlyOwnerOrEditor(keccak256(abi.encodePacked(_document,_comment))) notRetracted external { require( bytes(_document).length > 0, "Input for document cannot be empty" ); string memory oldDocument = document; document = _document; emit LogUpdatePublicationDocument(msg.sender, oldDocument, document, _comment); } /** * Update the metadata hash * @param _metadata Off-chain reference to the new publication metadata document (e.g., IPFS hash) * @param _comment Comment describing the document changes */ function updateMetadata(string memory _metadata, string calldata _comment) onlyOwnerOrEditor(keccak256(abi.encodePacked(_metadata,_comment))) notRetracted external { require( bytes(_metadata).length > 0, "Input for metadata cannot be empty" ); string memory oldMetadata = metadata; metadata = _metadata; emit LogUpdatePublicationMetadata(msg.sender, oldMetadata, metadata, _comment); } /** * Ireversibly retracts the publication * @param _comment Comment describing the reason for retracting the publication */ function retractPublication(string calldata _comment) onlyOwnerOrEditor(keccak256(abi.encodePacked(_comment))) notRetracted external { state = PublicationState.RETRACTED; delete document; delete metadata; while (endorsersSet.count() > 0) { address key = endorsersSet.keyAtIndex(0); delete endorsers[key]; endorsersSet.remove(key); } delete endorsersSet; emit LogPublicationRetraction(msg.sender, _comment); } /** * Removes the endorsement of the specified endorser from the publication * @param _endorser Address of the endorser whose endorsement is to be removed * @param _comment Reason for removing the endorsement */ function __deleteEndorsement(address _endorser, string calldata _comment) internal { endorsersSet.remove(_endorser); string memory doc = endorsers[_endorser].document; delete endorsers[_endorser]; emit LogEndorsementRevocation(msg.sender, _endorser, doc, _comment); } /** * Asserts whether the caller is allowed to perform a certain action * @param _data Encoded description of the action */ function assertEditorialPermission(bytes32 _data) internal view returns (bool) { bytes32 action = keccak256(abi.encodePacked(msg.sig,_data)); try editorProxy.actionAllowed(action) returns (bool allowed) { return allowed; } catch { return false; } } /** * Only editors may perfom the asserted action * @param _data Encoded description of the action */ modifier onlyEditor(bytes32 _data){ require(assertEditorialPermission(_data), "Function can only be called with editorial access rights"); _; } /** * Only the publication author or an editor may perform the asserted action * @param _data Encoded description of the action */ modifier onlyOwnerOrEditor(bytes32 _data){ require(msg.sender == owner() || assertEditorialPermission(_data), "Function is restricted to the contract owner and editors"); _; } /** * Allows to only call a function if the given endorsement exists * @param _endorser Address of the endorser to be admitted */ modifier endorsementExists(address _endorser) { require(endorsersSet.exists(_endorser), "No matching endorsement found"); _; } /** * Allows to only call a function only if the publication has not been retracted earlier */ modifier notRetracted(){ require(state != PublicationState.RETRACTED, "Function cannot be called on a retracted publication"); _; } }
Contract ABI
[{"type":"event","name":"LogEndorsementRevocation","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"address","name":"endorser","internalType":"address","indexed":false},{"type":"string","name":"document","internalType":"string","indexed":false},{"type":"string","name":"comment","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"LogNewEndorsement","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"string","name":"document","internalType":"string","indexed":false},{"type":"string","name":"comment","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"LogNewPublication","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"string","name":"document","internalType":"string","indexed":false},{"type":"string","name":"metadata","internalType":"string","indexed":false},{"type":"uint8","name":"state","internalType":"enum Publication.PublicationState","indexed":false}],"anonymous":false},{"type":"event","name":"LogPublicationPublished","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"string","name":"document","internalType":"string","indexed":false},{"type":"string","name":"metadata","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"LogPublicationRetraction","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"string","name":"comment","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"LogUpdateEndorsementComment","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"address","name":"endorser","internalType":"address","indexed":false},{"type":"string","name":"document","internalType":"string","indexed":false},{"type":"string","name":"comment","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"LogUpdateEndorsementDocument","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"address","name":"endorser","internalType":"address","indexed":false},{"type":"string","name":"oldDocument","internalType":"string","indexed":false},{"type":"string","name":"newDocument","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"LogUpdateEndorserRole","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"address","name":"endorser","internalType":"address","indexed":false},{"type":"string","name":"document","internalType":"string","indexed":false},{"type":"uint8","name":"oldRole","internalType":"enum Publication.EndorserRole","indexed":false},{"type":"uint8","name":"newRole","internalType":"enum Publication.EndorserRole","indexed":false}],"anonymous":false},{"type":"event","name":"LogUpdatePublicationDocument","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"string","name":"oldDocument","internalType":"string","indexed":false},{"type":"string","name":"newDocument","internalType":"string","indexed":false},{"type":"string","name":"comment","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"LogUpdatePublicationMetadata","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"string","name":"oldMetadata","internalType":"string","indexed":false},{"type":"string","name":"newMetadata","internalType":"string","indexed":false},{"type":"string","name":"comment","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"document","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract Editor"}],"name":"editorProxy","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"endorse","inputs":[{"type":"string","name":"_identity","internalType":"string"},{"type":"string","name":"_comment","internalType":"string"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"exists","internalType":"bool"},{"type":"string","name":"document","internalType":"string"},{"type":"uint8","name":"role","internalType":"enum Publication.EndorserRole"}],"name":"endorsers","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract Identity"}],"name":"identityProxy","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_owner","internalType":"address"},{"type":"string","name":"_document","internalType":"string"},{"type":"string","name":"_metadata","internalType":"string"},{"type":"string","name":"_identity","internalType":"string"},{"type":"address","name":"_editorProxy","internalType":"contract Editor"},{"type":"address","name":"_identityProxy","internalType":"contract Identity"},{"type":"uint8","name":"_journal","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"journal","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"metadata","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"publish","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"retractPublication","inputs":[{"type":"string","name":"_comment","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeEndorsement","inputs":[{"type":"string","name":"_comment","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeEndorsementExt","inputs":[{"type":"address","name":"_endorser","internalType":"address"},{"type":"string","name":"_comment","internalType":"string"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"enum Publication.PublicationState"}],"name":"state","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateDocument","inputs":[{"type":"string","name":"_document","internalType":"string"},{"type":"string","name":"_comment","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateEndorsementComment","inputs":[{"type":"string","name":"_comment","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateEndorsementCommentExt","inputs":[{"type":"address","name":"_endorser","internalType":"address"},{"type":"string","name":"_comment","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateEndorsementDocument","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateEndorserRole","inputs":[{"type":"address","name":"_endorser","internalType":"address"},{"type":"uint8","name":"_role","internalType":"enum Publication.EndorserRole"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateMetadata","inputs":[{"type":"string","name":"_metadata","internalType":"string"},{"type":"string","name":"_comment","internalType":"string"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"version","inputs":[]}]
Contract Creation Code
0x3d602d80600a3d3981f3363d3d373d3d3d363d73057509d22e54dbfeb991a00ad6136eaf0373f4885af43d82803e903d91602b57fd5bf3
Deployed ByteCode
0x363d3d373d3d3d363d73057509d22e54dbfeb991a00ad6136eaf0373f4885af43d82803e903d91602b57fd5bf3