false
false

Contract Address Details

0x53787C2b2E69feaB1712512f70Ae50D47fa66630

Creator
0x827fa7–80c0d9 at 0x2729b6–1f3f83
Balance
0 Berg
Tokens
Fetching tokens...
Transactions
3 Transactions
Transfers
0 Transfers
Gas Used
317,581
Last Balance Update
28360688
Minimal Proxy Contract for 0x057509d22e54dbfeb991a00ad6136eaf0373f488.
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

Verify & Publish
0x3d602d80600a3d3981f3363d3d373d3d3d363d73057509d22e54dbfeb991a00ad6136eaf0373f4885af43d82803e903d91602b57fd5bf3

Deployed ByteCode

0x363d3d373d3d3d363d73057509d22e54dbfeb991a00ad6136eaf0373f4885af43d82803e903d91602b57fd5bf3