neo-devpack-solidity 0.22.0

Production-focused Solidity-to-NeoVM compilation system
Documentation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/**
 * @title Neo N3 Runtime Library
 * @dev Runtime services and utilities for Neo N3 blockchain
 * @author Jimmy <jimmy@r3e.network>
 *
 * `Runtime` is a compiler intrinsic: the `neo-devpack-solidity` compiler
 * lowers the members below directly to Neo N3 syscalls. The Solidity bodies
 * in this file exist for editor tooling (signatures/docs) and are never
 * compiled. Only the members listed here are supported; calling anything
 * else fails compilation with a diagnostic.
 *
 * Supported members:
 * - Notifications: notify, notifyIndexed
 * - Witnesses: checkWitness (address or pubkey), requireWitness,
 *   checkAnyWitness, checkAllWitnesses, checkMultiSigWitness
 * - Gas: gasLeft, burnGas
 * - Context: getTime, getTrigger, getInvocationCounter, getCurrentSigners,
 *   getCallFlags, getScriptContainer, loadScript, getNetwork, getPlatform,
 *   getAddressVersion, getRandom, getExecutingScriptHash,
 *   getCallingScriptHash, getEntryScriptHash
 * - Misc: log, initializeServices
 */

import "../contracts/Syscalls.sol";

library Runtime {
    using Syscalls for *;

    // ========== Event and Notification System ==========

    /**
     * @dev Emit notification (maps to System.Runtime.Notify)
     */
    function notify(string memory eventName, bytes memory data) internal {
        bytes memory notificationData = abi.encode(eventName, data);
        Syscalls.notify(notificationData);
    }

    /**
     * @dev Emit indexed notification with topics
     *      (lowered to notify(eventName, abi.encode(topics, data)))
     */
    function notifyIndexed(
        string memory eventName,
        bytes32[] memory topics,
        bytes memory data
    ) internal {
        bytes memory indexedData = abi.encode(topics, data);
        notify(eventName, indexedData);
    }

    // ========== Witness and Authorization ==========

    /**
     * @dev Check witness for account
     */
    function checkWitness(address account) internal view returns (bool) {
        return Syscalls.checkWitness(account);
    }

    /**
     * @dev Check witness by public key
     */
    function checkWitness(bytes memory publicKey) internal view returns (bool) {
        return Syscalls.checkWitness(publicKey);
    }

    /**
     * @dev Require witness or revert
     */
    function requireWitness(address account) internal view {
        require(checkWitness(account), "Runtime: invalid witness");
    }

    /**
     * @dev Check multiple witnesses (OR logic)
     */
    function checkAnyWitness(address[] memory accounts) internal view returns (bool) {
        for (uint256 i = 0; i < accounts.length; i++) {
            if (checkWitness(accounts[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * @dev Check multiple witnesses (AND logic)
     */
    function checkAllWitnesses(address[] memory accounts) internal view returns (bool) {
        for (uint256 i = 0; i < accounts.length; i++) {
            if (!checkWitness(accounts[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Multi-signature witness verification (threshold of distinct signers)
     */
    function checkMultiSigWitness(
        address[] memory signers,
        uint256 threshold
    ) internal view returns (bool) {
        require(threshold > 0, "Runtime: threshold must be positive");
        require(threshold <= signers.length, "Runtime: threshold exceeds signers");

        uint256 validWitnesses = 0;

        for (uint256 i = 0; i < signers.length; i++) {
            address signer = signers[i];

            // Prevent duplicate signers from satisfying quorum multiple times.
            for (uint256 j = 0; j < i; j++) {
                require(signers[j] != signer, "Runtime: duplicate signer");
            }

            if (checkWitness(signer)) {
                validWitnesses++;
                if (validWitnesses >= threshold) {
                    return true;
                }
            }
        }

        return false;
    }

    // ========== Gas Management ==========

    /**
     * @dev Get remaining gas
     */
    function gasLeft() internal view returns (uint256) {
        return Syscalls.gasLeft();
    }

    /**
     * @dev Burn gas (anti-spam)
     */
    function burnGas(uint256 amount) internal {
        Syscalls.burnGas(amount);
    }

    // ========== Execution Context ==========

    /**
     * @dev Get current block timestamp (System.Runtime.GetTime)
     */
    function getTime() internal view returns (uint256) {
        return Syscalls.getTime();
    }

    /**
     * @dev Get invocation trigger type (System.Runtime.GetTrigger)
     */
    function getTrigger() internal view returns (uint8) {
        return Syscalls.getTrigger();
    }

    /**
     * @dev Get invocation counter for the executing contract
     */
    function getInvocationCounter() internal view returns (uint256) {
        return Syscalls.getInvocationCounter();
    }

    // NOTE: there is intentionally no `hasRole` helper. Neo N3's native
    // RoleManagement contract only exposes getDesignatedByRole(role, index)
    // (the ECPoint node list designated at a block height) and
    // designateAsRole; neither real Neo N3 nor the official C# devpack
    // provides a generic role-membership check. A previous `hasRole`
    // helper silently ignored its `role` argument and fell back to
    // checkWitness(account) — a misleading surface for an access-control
    // primitive (and uncallable anyway: Runtime is a compiler-intrinsic
    // library and `hasRole` had no intrinsic lowering). For node-role
    // checks use NativeCalls.getDesignatedByRole() and scan the returned
    // list; for account authorization use checkWitness/requireWitness.

    /**
     * @dev Get current transaction signers
     */
    function getCurrentSigners() internal view returns (Syscalls.Signer[] memory) {
        return Syscalls.getCurrentSigners();
    }

    /**
     * @dev Get current call flags
     */
    function getCallFlags() internal view returns (uint8) {
        return Syscalls.getCallFlags();
    }

    /**
     * @dev Get current script container (transaction)
     */
    function getScriptContainer() internal view returns (Syscalls.Transaction memory) {
        return Syscalls.getScriptContainer();
    }

    /**
     * @dev Load script with arguments
     */
    function loadScript(bytes memory script, uint8 callFlags, bytes[] memory args) internal {
        Syscalls.loadScript(script, callFlags, args);
    }

    /**
     * @dev Get network magic number
     */
    function getNetwork() internal view returns (uint32) {
        return Syscalls.getNetwork();
    }

    /**
     * @dev Get platform name ("NEO")
     */
    function getPlatform() internal view returns (string memory) {
        return Syscalls.getPlatform();
    }

    /**
     * @dev Get address version byte
     */
    function getAddressVersion() internal view returns (uint8) {
        return Syscalls.getAddressVersion();
    }

    /**
     * @dev Get next pseudo-random number (System.Runtime.GetRandom)
     */
    function getRandom() internal view returns (uint256) {
        return Syscalls.getCurrentRandom();
    }

    /**
     * @dev Get executing contract script hash
     */
    function getExecutingScriptHash() internal view returns (address) {
        return Syscalls.getExecutingScriptHash();
    }

    /**
     * @dev Get calling contract script hash
     */
    function getCallingScriptHash() internal view returns (address) {
        return Syscalls.getCallingScriptHash();
    }

    /**
     * @dev Get entry script hash
     */
    function getEntryScriptHash() internal view returns (address) {
        return Syscalls.getEntryScriptHash();
    }

    // ========== Logging and Debugging ==========

    /**
     * @dev Log message to Neo logs (System.Runtime.Log)
     */
    function log(string memory message) internal {
        Syscalls.log(message);
    }

    /**
     * @dev Devpack compatibility initializer. The compiler lowers this to a
     *      no-op (Neo N3 runtime services need no explicit initialization).
     */
    function initializeServices() internal pure returns (bool) {
        return true;
    }
}