Digital Art Authenticity Contract

What it does:
Provides immutable, on-chain proof of authenticity and provenance for digital artworks, allowing anyone to verify the original creator and ownership history.

Why it matters:
Prevents forgery and plagiarism in digital art, strengthens collector confidence, preserves artist attribution, and creates a verifiable source of truth for digital art markets.

How it works:

  • Artists register an artwork by submitting a cryptographic hash and metadata reference

  • Smart contract permanently links the artwork to the artist’s wallet address

  • Blockchain timestamps establish the moment of creation or registration

  • Ownership transfers are recorded on-chain to build full provenance history

  • No artwork files are stored on-chain, preserving privacy and scalability

  • Anyone can verify authenticity by comparing the artwork hash with on-chain records

  • Integrates seamlessly with NFTs, marketplaces, galleries, and metaverse platforms

      // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title DigitalArtAuthenticity
 * @author Nam
 * @notice On-chain authenticity and provenance tracking for digital artworks
 */
contract DigitalArtAuthenticity {

    uint256 public artworkCount;

    // -------------------- STRUCTS --------------------

    struct Artwork {
        bytes32 artHash;          // Hash of the artwork file
        string metadataURI;       // IPFS / Arweave metadata
        address creator;
        address currentOwner;
        uint256 createdAt;
        uint256 lastTransferAt;
    }

    struct TransferRecord {
        address from;
        address to;
        uint256 timestamp;
    }

    // -------------------- STORAGE --------------------

    mapping(uint256 => Artwork) public artworks;
    mapping(bytes32 => bool) public registeredHashes;
    mapping(uint256 => TransferRecord[]) public provenance;

    // -------------------- EVENTS --------------------

    event ArtworkRegistered(
        uint256 indexed artworkId,
        bytes32 indexed artHash,
        address indexed creator
    );

    event OwnershipTransferred(
        uint256 indexed artworkId,
        address indexed from,
        address indexed to
    );

    // -------------------- ART REGISTRATION --------------------

    function registerArtwork(bytes32 _artHash, string calldata _metadataURI)
        external
        returns (uint256)
    {
        require(_artHash != bytes32(0), "Invalid hash");
        require(!registeredHashes[_artHash], "Artwork already registered");

        artworkCount += 1;

        artworks[artworkCount] = Artwork({
            artHash: _artHash,
            metadataURI: _metadataURI,
            creator: msg.sender,
            currentOwner: msg.sender,
            createdAt: block.timestamp,
            lastTransferAt: block.timestamp
        });

        registeredHashes[_artHash] = true;

        provenance[artworkCount].push(
            TransferRecord({
                from: address(0),
                to: msg.sender,
                timestamp: block.timestamp
            })
        );

        emit ArtworkRegistered(artworkCount, _artHash, msg.sender);
        return artworkCount;
    }

    // -------------------- OWNERSHIP TRANSFER --------------------

    function transferOwnership(uint256 _artworkId, address _to) external {
        Artwork storage art = artworks[_artworkId];
        require(msg.sender == art.currentOwner, "Not owner");
        require(_to != address(0), "Invalid recipient");

        address previousOwner = art.currentOwner;
        art.currentOwner = _to;
        art.lastTransferAt = block.timestamp;

        provenance[_artworkId].push(
            TransferRecord({
                from: previousOwner,
                to: _to,
                timestamp: block.timestamp
            })
        );

        emit OwnershipTransferred(_artworkId, previousOwner, _to);
    }

    // -------------------- VERIFICATION --------------------

    function verifyArtwork(bytes32 _artHash)
        external
        view
        returns (
            uint256 artworkId,
            address creator,
            address currentOwner,
            uint256 createdAt
        )
    {
        for (uint256 i = 1; i <= artworkCount; i++) {
            if (artworks[i].artHash == _artHash) {
                Artwork memory art = artworks[i];
                return (
                    i,
                    art.creator,
                    art.currentOwner,
                    art.createdAt
                );
            }
        }
        revert("Artwork not found");
    }

    function getProvenance(uint256 _artworkId)
        external
        view
        returns (TransferRecord[] memory)
    {
        return provenance[_artworkId];
    }
}