Parasol IP Protection

Dripsy Drown Productions Film & Entertainment™

Network:Base Sepolia(testnet)
Chain ID:84532
Contracts deployed:0 / 5
Solidity Version0.8.28
Optimizer Runs200
Contracts5
PSOL Max Supply100,000,000

Smart Contracts

Parasol Royalty Token

PSOL
ERC-20

On-chain royalty entitlements for verified IP holders. 100M PSOL hard cap with time-locked release schedules.

Not yet deployed — run parasol-ip-protection deploy
pending

Parasol Access Token

PSAT
ERC-721

Soulbound access credentials for Parasol IP Protection services (API, PARTNER, BLOCKCHAIN, ADMIN_DELEGATE).

Not yet deployed — run parasol-ip-protection deploy
pending

Parasol IP Licensing

PIPL
Custom

Append-only registry of SYNC, MASTER, THEATRICAL, STREAMING, MERCHANDISE, and FULL_BUYOUT IP license agreements.

Not yet deployed — run parasol-ip-protection deploy
pending

Parasol IP Token

PIPT
ERC-721

ERC-721 on-chain IP provenance token. Embeds IPFS hash, Clock Shaman timestamp, beacon hash, FTCA/RICO claim reference, royalty BPS, and litigation-protected status.

Not yet deployed — run parasol-ip-protection deploy
pending

Royalty Distributor

DIST
Custom

ETH revenue splitter for ParasolIPToken holders. Auto-earmarks 10% Impact Pledge (veterans, creators & justice). Token owners call claim() to withdraw their share.

Not yet deployed — run parasol-ip-protection deploy
pending
Contract BalanceNot deployed
Token #1 Claimable
Impact Accumulated
Impact Released
NetworkBase Sepolia
Chain ID84532

Smart Contracts — Solidity Source

Solidity 0.8.28 · cancun
// SPDX-License-Identifier: PROPRIETARY
// Copyright © William Joshua Drown, Dripsy Drown Productions Film & Entertainment™
// Parasol IP Protection — Royalty Token Contract

pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/**
 * @title  ParasolRoyaltyToken (PSOL)
 * @notice ERC-20 royalty entitlement token for Parasol IP Protection.
 *         Only verified holders may receive or hold tokens.
 *         Royalties are scheduled on-chain with a time-lock and released
 *         exclusively by the contract owner (William Joshua Drown).
 */
contract ParasolRoyaltyToken is ERC20, ERC20Burnable, ERC20Permit, Ownable, ReentrancyGuard {

    uint256 public constant MAX_SUPPLY = 100_000_000 * 1e18;

    mapping(address => uint256) public royaltiesReleased;
    mapping(address => bool)    public verifiedHolders;

    struct RoyaltySchedule {
        address recipient;
        uint256 amount;
        uint256 releaseTimestamp;
        bool    released;
        string  projectId;
    }

    RoyaltySchedule[] public schedules;

    event HolderVerified(address indexed holder, bool status);
    event RoyaltyScheduled(uint256 indexed scheduleId, address indexed recipient, uint256 amount, string projectId);
    event RoyaltyReleased(uint256 indexed scheduleId, address indexed recipient, uint256 amount);

    constructor(address initialOwner)
        ERC20("Parasol Royalty Token", "PSOL")
        ERC20Permit("Parasol Royalty Token")
        Ownable(initialOwner)
    {}

    function mint(address to, uint256 amount) external onlyOwner {
        require(verifiedHolders[to], "ParasolRoyalty: recipient not verified");
        require(totalSupply() + amount <= MAX_SUPPLY, "ParasolRoyalty: exceeds max supply");
        _mint(to, amount);
    }

    function setVerifiedHolder(address holder, bool status) external onlyOwner {
        verifiedHolders[holder] = status;
        emit HolderVerified(holder, status);
    }

    function scheduleRoyalty(
        address recipient, uint256 amount, uint256 releaseTimestamp, string calldata projectId
    ) external onlyOwner returns (uint256 scheduleId) {
        require(verifiedHolders[recipient], "ParasolRoyalty: recipient not verified");
        require(releaseTimestamp >= block.timestamp, "ParasolRoyalty: release time in the past");
        require(totalSupply() + amount <= MAX_SUPPLY, "ParasolRoyalty: would exceed max supply");
        scheduleId = schedules.length;
        schedules.push(RoyaltySchedule(recipient, amount, releaseTimestamp, false, projectId));
        emit RoyaltyScheduled(scheduleId, recipient, amount, projectId);
    }

    function releaseRoyalty(uint256 scheduleId) external onlyOwner nonReentrant {
        RoyaltySchedule storage s = schedules[scheduleId];
        require(!s.released, "ParasolRoyalty: already released");
        require(block.timestamp >= s.releaseTimestamp, "ParasolRoyalty: not yet due");
        require(totalSupply() + s.amount <= MAX_SUPPLY, "ParasolRoyalty: exceeds max supply");
        s.released = true;
        royaltiesReleased[s.recipient] += s.amount;
        _mint(s.recipient, s.amount);
        emit RoyaltyReleased(scheduleId, s.recipient, s.amount);
    }

    function scheduleCount() external view returns (uint256) {
        return schedules.length;
    }

    // Soulbound transfer guard
    function _update(address from, address to, uint256 value) internal override {
        if (from != address(0) && to != address(0)) {
            require(verifiedHolders[to] || to == owner(), "ParasolRoyalty: recipient not verified");
        }
        super._update(from, to, value);
    }
}

hardhat.config.ts

hardhat.config.ts — parasol-ip-protection
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

// Fail-fast: throws at startup if any required env var is missing
const requireEnv = (name: string): string => {
  const value = process.env[name];
  if (!value || value.trim() === "") throw new Error(`Missing env var: ${name}`);
  return value;
};

const PRIVATE_KEY          = requireEnv("PRIVATE_KEY");
const BASE_SEPOLIA_RPC_URL = requireEnv("BASE_SEPOLIA_RPC_URL");
const BASE_MAINNET_RPC_URL = requireEnv("BASE_MAINNET_RPC_URL");
const AMOY_RPC_URL         = requireEnv("AMOY_RPC_URL");
const ETHERSCAN_API_KEY    = requireEnv("ETHERSCAN_API_KEY");
const BASESCAN_API_KEY     = requireEnv("BASESCAN_API_KEY");
const POLYGONSCAN_API_KEY  = requireEnv("POLYGONSCAN_API_KEY");

const config: HardhatUserConfig = {
  solidity: {
    version: "0.8.28",
    settings: { optimizer: { enabled: true, runs: 200 }, evmVersion: "cancun" },
  },
  paths: { sources: "./contracts", tests: "./test",
           cache: "./cache", artifacts: "./artifacts" },
  defaultNetwork: "baseSepolia",
  networks: {
    hardhat:    {},
    baseSepolia: { url: BASE_SEPOLIA_RPC_URL, chainId: 84532,  accounts: [PRIVATE_KEY] },
    base:        { url: BASE_MAINNET_RPC_URL, chainId: 8453,   accounts: [PRIVATE_KEY] },
    polygonAmoy: { url: AMOY_RPC_URL,         chainId: 80002,  accounts: [PRIVATE_KEY] },
  },
  etherscan: {
    apiKey: {
      mainnet: ETHERSCAN_API_KEY, sepolia: ETHERSCAN_API_KEY,
      base: BASESCAN_API_KEY, baseSepolia: BASESCAN_API_KEY,
      polygon: POLYGONSCAN_API_KEY, polygonAmoy: POLYGONSCAN_API_KEY,
    },
    customChains: [
      { network: "base",        chainId: 8453,
        urls: { apiURL: "https://api.basescan.org/api",
                browserURL: "https://basescan.org" } },
      { network: "baseSepolia", chainId: 84532,
        urls: { apiURL: "https://api-sepolia.basescan.org/api",
                browserURL: "https://sepolia.basescan.org" } },
      { network: "polygonAmoy", chainId: 80002,
        urls: { apiURL: "https://api-amoy.polygonscan.com/api",
                browserURL: "https://amoy.polygonscan.com" } },
    ],
  },
};

export default config;