LicenseAnchor

Description:

Smart contract deployed on Ethereum with Factory features.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/LicenseAnchor.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/**
 * @title LicenseAnchor
 * @dev Anchors licenses on the blockchain for immutable verification
 * @notice Supports both regular and enterprise licenses from Shuka
 */
contract LicenseAnchor {
    
    // Admin address (Shuka's admin wallet)
    address public admin;
    
    // License types
    enum LicenseType { REGULAR, ENTERPRISE }
    
    // License status
    enum LicenseStatus { ACTIVE, EXPIRED, REVOKED, TRANSFERRED }
    
    // License structure matching Shuka's License model
    struct License {
        string licenseId;           // Unique license ID (e.g., LIC-20240814-123456-abc)
        LicenseType licenseType;   // Regular or Enterprise
        string brand;               // Brand name
        string rightsHolder;        // Rights holder name
        address issuerWallet;       // Wallet that issued the license
        address ownerWallet;        // Current owner's wallet
        uint256 issueDate;          // Issue timestamp
        uint256 validUntil;         // Expiration timestamp
        uint256 buyToOpenPrice;     // Price in wei
        bool approved;              // Approval status
        bool available;             // Availability for purchase
        LicenseStatus status;       // Current status
        string metadataHash;        // IPFS hash for additional metadata
        uint256 anchorTimestamp;    // When anchored on blockchain
    }
    
    // Enterprise-specific data
    struct EnterpriseMetadata {
        uint256 teamId;             // Team ID from database
        uint256 monthlyLimit;       // Monthly license limit
        bool allowCreation;         // Allow license creation
        bool allowManagement;       // Allow enterprise management
    }
    
    // Mappings
    mapping(string => License) public licenses;
    mapping(string => EnterpriseMetadata) public enterpriseMetadata;
    mapping(address => string[]) public userLicenses;
    mapping(address => string[]) public issuedLicenses;
    mapping(string => bool) public licenseExists;
    
    // Transfer history
    mapping(string => address[]) public transferHistory;
    
    // Events
    event LicenseAnchored(
        string indexed licenseId,
        LicenseType indexed licenseType,
        address indexed ownerWallet,
        uint256 timestamp
    );
    
    event LicenseTransferred(
        string indexed licenseId,
        address indexed from,
        address indexed to,
        uint256 timestamp
    );
    
    event LicenseStatusChanged(
        string indexed licenseId,
        LicenseStatus indexed oldStatus,
        LicenseStatus indexed newStatus,
        uint256 timestamp
    );
    
    event LicensePurchased(
        string indexed licenseId,
        address indexed buyer,
        uint256 price,
        uint256 timestamp
    );
    
    // Modifiers
    modifier onlyAdmin() {
        require(msg.sender == admin, "Only admin can perform this action");
        _;
    }
    
    modifier licenseValid(string memory _licenseId) {
        require(licenseExists[_licenseId], "License does not exist");
        _;
    }
    
    modifier onlyLicenseOwner(string memory _licenseId) {
        require(licenses[_licenseId].ownerWallet == msg.sender, "Not the license owner");
        _;
    }
    
    constructor() {
        admin = msg.sender;
    }
    
    /**
     * @dev Anchor a new license on the blockchain
     * @param _licenseId Unique license identifier
     * @param _licenseType Type of license (0 for Regular, 1 for Enterprise)
     * @param _brand Brand name
     * @param _rightsHolder Rights holder name
     * @param _ownerWallet Initial owner's wallet address
     * @param _validUntil Expiration timestamp
     * @param _buyToOpenPrice Price in wei
     * @param _metadataHash IPFS hash for additional metadata
     */
    function anchorLicense(
        string memory _licenseId,
        LicenseType _licenseType,
        string memory _brand,
        string memory _rightsHolder,
        address _ownerWallet,
        uint256 _validUntil,
        uint256 _buyToOpenPrice,
        string memory _metadataHash
    ) external onlyAdmin {
        require(!licenseExists[_licenseId], "License already anchored");
        require(_ownerWallet != address(0), "Invalid owner wallet");
        require(_validUntil > block.timestamp, "Expiration must be in future");
        
        License memory newLicense = License({
            licenseId: _licenseId,
            licenseType: _licenseType,
            brand: _brand,
            rightsHolder: _rightsHolder,
            issuerWallet: msg.sender,
            ownerWallet: _ownerWallet,
            issueDate: block.timestamp,
            validUntil: _validUntil,
            buyToOpenPrice: _buyToOpenPrice,
            approved: true,
            available: true,
            status: LicenseStatus.ACTIVE,
            metadataHash: _metadataHash,
            anchorTimestamp: block.timestamp
        });
        
        licenses[_licenseId] = newLicense;
        licenseExists[_licenseId] = true;
        userLicenses[_ownerWallet].push(_licenseId);
        issuedLicenses[msg.sender].push(_licenseId);
        transferHistory[_licenseId].push(_ownerWallet);
        
        emit LicenseAnchored(_licenseId, _licenseType, _ownerWallet, block.timestamp);
    }
    
    /**
     * @dev Add enterprise metadata for enterprise licenses
     * @param _licenseId License identifier
     * @param _teamId Team ID from database
     * @param _monthlyLimit Monthly license creation limit
     * @param _allowCreation Allow license creation
     * @param _allowManagement Allow enterprise management
     */
    function setEnterpriseMetadata(
        string memory _licenseId,
        uint256 _teamId,
        uint256 _monthlyLimit,
        bool _allowCreation,
        bool _allowManagement
    ) external onlyAdmin licenseValid(_licenseId) {
        require(licenses[_licenseId].licenseType == LicenseType.ENTERPRISE, "Not an enterprise license");
        
        enterpriseMetadata[_licenseId] = EnterpriseMetadata({
            teamId: _teamId,
            monthlyLimit: _monthlyLimit,
            allowCreation: _allowCreation,
            allowManagement: _allowManagement
        });
    }
    
    /**
     * @dev Transfer license ownership
     * @param _licenseId License to transfer
     * @param _newOwner New owner's wallet address
     */
    function transferLicense(
        string memory _licenseId,
        address _newOwner
    ) external licenseValid(_licenseId) onlyLicenseOwner(_licenseId) {
        require(_newOwner != address(0), "Invalid new owner");
        require(_newOwner != msg.sender, "Cannot transfer to self");
        require(licenses[_licenseId].status == LicenseStatus.ACTIVE, "License not active");
        
        address previousOwner = licenses[_licenseId].ownerWallet;
        licenses[_licenseId].ownerWallet = _newOwner;
        
        // Update user license mappings
        _removeLicenseFromUser(previousOwner, _licenseId);
        userLicenses[_newOwner].push(_licenseId);
        transferHistory[_licenseId].push(_newOwner);
        
        emit LicenseTransferred(_licenseId, previousOwner, _newOwner, block.timestamp);
    }
    
    /**
     * @dev Purchase an available license
     * @param _licenseId License to purchase
     */
    function purchaseLicense(string memory _licenseId) 
        external 
        payable 
        licenseValid(_licenseId) 
    {
        License storage license = licenses[_licenseId];
        require(license.available, "License not available for purchase");
        require(license.status == LicenseStatus.ACTIVE, "License not active");
        require(msg.value >= license.buyToOpenPrice, "Insufficient payment");
        require(msg.sender != license.ownerWallet, "Already own this license");
        
        address previousOwner = license.ownerWallet;
        
        // Transfer ownership
        license.ownerWallet = msg.sender;
        license.available = false;
        
        // Update mappings
        _removeLicenseFromUser(previousOwner, _licenseId);
        userLicenses[msg.sender].push(_licenseId);
        transferHistory[_licenseId].push(msg.sender);
        
        // Transfer payment to admin
        payable(admin).transfer(msg.value);
        
        emit LicensePurchased(_licenseId, msg.sender, msg.value, block.timestamp);
        emit LicenseTransferred(_licenseId, previousOwner, msg.sender, block.timestamp);
    }
    
    /**
     * @dev Update license status
     * @param _licenseId License identifier
     * @param _newStatus New status
     */
    function updateLicenseStatus(
        string memory _licenseId,
        LicenseStatus _newStatus
    ) external onlyAdmin licenseValid(_licenseId) {
        LicenseStatus oldStatus = licenses[_licenseId].status;
        licenses[_licenseId].status = _newStatus;
        
        emit LicenseStatusChanged(_licenseId, oldStatus, _newStatus, block.timestamp);
    }
    
    /**
     * @dev Check if license is expired
     * @param _licenseId License identifier
     * @return Boolean indicating if expired
     */
    function isLicenseExpired(string memory _licenseId) 
        external 
        view 
        licenseValid(_licenseId) 
        returns (bool) 
    {
        return block.timestamp > licenses[_licenseId].validUntil;
    }
    
    /**
     * @dev Get license details
     * @param _licenseId License identifier
     * @return License struct
     */
    function getLicense(string memory _licenseId) 
        external 
        view 
        licenseValid(_licenseId) 
        returns (License memory) 
    {
        return licenses[_licenseId];
    }
    
    /**
     * @dev Get all licenses for a user
     * @param _user User's wallet address
     * @return Array of license IDs
     */
    function getUserLicenses(address _user) 
        external 
        view 
        returns (string[] memory) 
    {
        return userLicenses[_user];
    }
    
    /**
     * @dev Get transfer history for a license
     * @param _licenseId License identifier
     * @return Array of addresses in transfer history
     */
    function getTransferHistory(string memory _licenseId) 
        external 
        view 
        licenseValid(_licenseId) 
        returns (address[] memory) 
    {
        return transferHistory[_licenseId];
    }
    
    /**
     * @dev Internal function to remove license from user's array
     * @param _user User's address
     * @param _licenseId License to remove
     */
    function _removeLicenseFromUser(address _user, string memory _licenseId) internal {
        string[] storage userLicenseArray = userLicenses[_user];
        for (uint i = 0; i < userLicenseArray.length; i++) {
            if (keccak256(bytes(userLicenseArray[i])) == keccak256(bytes(_licenseId))) {
                userLicenseArray[i] = userLicenseArray[userLicenseArray.length - 1];
                userLicenseArray.pop();
                break;
            }
        }
    }
    
    /**
     * @dev Batch anchor multiple licenses - saves ~60% gas for 10+ licenses
     * @param _licenseIds Array of unique license identifiers
     * @param _licenseTypes Array of license types
     * @param _brands Array of brand names
     * @param _rightsHolders Array of rights holder names
     * @param _ownerWallets Array of owner wallet addresses
     * @param _validUntils Array of expiration timestamps
     * @param _prices Array of prices in wei
     * @param _metadataHashes Array of IPFS hashes
     */
    function batchAnchorLicenses(
        string[] memory _licenseIds,
        LicenseType[] memory _licenseTypes,
        string[] memory _brands,
        string[] memory _rightsHolders,
        address[] memory _ownerWallets,
        uint256[] memory _validUntils,
        uint256[] memory _prices,
        string[] memory _metadataHashes
    ) external onlyAdmin {
        uint256 length = _licenseIds.length;
        require(
            _licenseTypes.length == length &&
            _brands.length == length &&
            _rightsHolders.length == length &&
            _ownerWallets.length == length &&
            _validUntils.length == length &&
            _prices.length == length &&
            _metadataHashes.length == length,
            "Array length mismatch"
        );
        
        uint256 currentTime = block.timestamp;
        
        for (uint256 i = 0; i < length; i++) {
            require(!licenseExists[_licenseIds[i]], "License already exists");
            require(_ownerWallets[i] != address(0), "Invalid owner");
            require(_validUntils[i] > currentTime, "Invalid expiry");
            
            licenses[_licenseIds[i]] = License({
                licenseId: _licenseIds[i],
                licenseType: _licenseTypes[i],
                brand: _brands[i],
                rightsHolder: _rightsHolders[i],
                issuerWallet: msg.sender,
                ownerWallet: _ownerWallets[i],
                issueDate: currentTime,
                validUntil: _validUntils[i],
                buyToOpenPrice: _prices[i],
                approved: true,
                available: true,
                status: LicenseStatus.ACTIVE,
                metadataHash: _metadataHashes[i],
                anchorTimestamp: currentTime
            });
            
            licenseExists[_licenseIds[i]] = true;
            userLicenses[_ownerWallets[i]].push(_licenseIds[i]);
            issuedLicenses[msg.sender].push(_licenseIds[i]);
            transferHistory[_licenseIds[i]].push(_ownerWallets[i]);
            
            emit LicenseAnchored(_licenseIds[i], _licenseTypes[i], _ownerWallets[i], currentTime);
        }
    }
    
    /**
     * @dev Batch update license status - saves ~50% gas for multiple updates
     * @param _licenseIds Array of license identifiers
     * @param _newStatuses Array of new statuses
     */
    function batchUpdateStatus(
        string[] memory _licenseIds,
        LicenseStatus[] memory _newStatuses
    ) external onlyAdmin {
        require(_licenseIds.length == _newStatuses.length, "Array length mismatch");
        
        for (uint256 i = 0; i < _licenseIds.length; i++) {
            require(licenseExists[_licenseIds[i]], "License does not exist");
            LicenseStatus oldStatus = licenses[_licenseIds[i]].status;
            licenses[_licenseIds[i]].status = _newStatuses[i];
            emit LicenseStatusChanged(_licenseIds[i], oldStatus, _newStatuses[i], block.timestamp);
        }
    }
    
    /**
     * @dev Batch set enterprise metadata - saves gas for multiple enterprise licenses
     * @param _licenseIds Array of license identifiers
     * @param _teamIds Array of team IDs
     * @param _monthlyLimits Array of monthly limits
     * @param _allowCreations Array of creation permissions
     * @param _allowManagements Array of management permissions
     */
    function batchSetEnterpriseMetadata(
        string[] memory _licenseIds,
        uint256[] memory _teamIds,
        uint256[] memory _monthlyLimits,
        bool[] memory _allowCreations,
        bool[] memory _allowManagements
    ) external onlyAdmin {
        uint256 length = _licenseIds.length;
        require(
            _teamIds.length == length &&
            _monthlyLimits.length == length &&
            _allowCreations.length == length &&
            _allowManagements.length == length,
            "Array length mismatch"
        );
        
        for (uint256 i = 0; i < length; i++) {
            require(licenseExists[_licenseIds[i]], "License does not exist");
            require(licenses[_licenseIds[i]].licenseType == LicenseType.ENTERPRISE, "Not enterprise");
            
            enterpriseMetadata[_licenseIds[i]] = EnterpriseMetadata({
                teamId: _teamIds[i],
                monthlyLimit: _monthlyLimits[i],
                allowCreation: _allowCreations[i],
                allowManagement: _allowManagements[i]
            });
        }
    }
    
    /**
     * @dev Batch check if licenses are expired - view function, no gas cost
     * @param _licenseIds Array of license identifiers
     * @return Array of booleans indicating expiration status
     */
    function batchCheckExpired(
        string[] memory _licenseIds
    ) external view returns (bool[] memory) {
        bool[] memory expired = new bool[](_licenseIds.length);
        uint256 currentTime = block.timestamp;
        
        for (uint256 i = 0; i < _licenseIds.length; i++) {
            if (licenseExists[_licenseIds[i]]) {
                expired[i] = currentTime > licenses[_licenseIds[i]].validUntil;
            }
        }
        
        return expired;
    }
    
    /**
     * @dev Update admin address
     * @param _newAdmin New admin address
     */
    function updateAdmin(address _newAdmin) external onlyAdmin {
        require(_newAdmin != address(0), "Invalid admin address");
        admin = _newAdmin;
    }
}"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "viaIR": true,
    "evmVersion": "paris",
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    }
  }
}}

Tags:
Factory|addr:0xe7b036b9248f7c2e6ee456465bd320cca1a6c819|verified:true|block:23461312|tx:0xa5d809f59e69210a8f7ad86da7234e15d2712010b8f2547f6866b00bf8dbe63b|first_check:1759063928

Submitted on: 2025-09-28 14:52:09

Comments

Log in to comment.

No comments yet.