EVM Precompiles
QoreChain extends the QoreChain EVM Engine with six custom precompiled contracts that expose protocol-level features directly to Solidity. These precompiles provide on-chain access to post-quantum cryptography, AI risk scoring, cross-VM messaging, and PRISM consensus parameters.
The six QoreChain precompiles below are compiled into the node, but the EVM only dispatches addresses listed in the active_static_precompiles parameter. On the qorechain-vladi mainnet (EVM chain ID 9801) that list is empty at the time of writing, so none is active there. On the qorechain-diana testnet (EVM chain ID 9800) PQC Verify, PQC Key Status and PRISM Consensus Params are active, activated by testnet governance proposal 21 (passed 27 October 2026), and the CrossVM Bridge (0x…0901) is in the list too (read 7 November 2026; it runs with an allowlist of reviewed contracts, see Network status). The two AI precompiles are not active on either network. A call to an address that is not in the list succeeds and returns empty data (0x); it does not revert and it does not return an error. See Network status before relying on any of them.
Network status
| Precompile | Address | qorechain-vladi (mainnet) | qorechain-diana (testnet) |
|---|---|---|---|
| CrossVM Bridge | 0x…0901 | Not active | Active with an allowlist (list read 7 November 2026; an eth_call after the v3.2.7 upgrade reached an allowlisted contract and was refused for one outside the list; see Multi-VM) |
| PQC Verify | 0x…0A01 | Not active | Active (since 27 October 2026) |
| PQC Key Status | 0x…0A02 | Not active | Active (since 27 October 2026) |
| AI Risk Score | 0x…0B01 | Not active | Not active |
| AI Anomaly Check | 0x…0B02 | Not active | Not active |
| PRISM Consensus Params | 0x…0C01 | Not active | Active (since 27 October 2026) |
Status as read from the live active_static_precompiles parameter on 27 October 2026 (mainnet re-read on 4 November 2026, after v3.2.6: still empty; both networks re-read on 7 November 2026: mainnet empty, testnet listing 0x…0901, 0x…0A01, 0x…0A02 and 0x…0C01). It is a chain parameter changed by governance, so each network's list changes only when a governance proposal passes; the testnet list was set by proposal 21, and no mainnet proposal changes it at the time of writing. The v3.2.3 release (mainnet, 20 October 2026) put the hardened versions of the CrossVM bridge and the AI anomaly precompile into the binary behind a protocol feature, but did not activate any precompile, and neither v3.2.4 (mainnet since 23 October 2026, testnet since 19 October 2026) nor v3.2.6 (mainnet since 4 November 2026) nor v3.2.7 (testnet since 6 November 2026) changes that. The read-only PQC and consensus precompiles come first, the AI pair once their read-only, fixed-point path is in place, and the CrossVM bridge last. This table lists only what is applied: a proposal that is still in its voting period is not a status.
Check the live list yourself (an empty list means none is active):
curl -s https://api.qore.host/cosmos/evm/vm/v1/params | jq .params.active_static_precompiles
curl -s https://api-testnet.qore.host/cosmos/evm/vm/v1/params | jq .params.active_static_precompiles
Guard against empty return data
Because an inactive (or non-QoreChain) address returns success with no data, never read the raw return bytes of a low-level call without checking their length. abi.decode and typed interface calls revert on empty data, which is the safe outcome; a hand-rolled low-level call must check it explicitly:
(bool ok, bytes memory ret) = address(0x0000000000000000000000000000000000000A02)
.staticcall(abi.encodeWithSignature("pqcKeyStatus(address)", account));
require(ok && ret.length >= 96, "precompile not active");
(bool registered, uint8 algorithmId, bytes memory pubkey) = abi.decode(ret, (bool, uint8, bytes));
Do not send value to these addresses. They are not payable, and value sent to an inactive precompile address stays there with no way to recover it.
On the testnet, a quick check that the three active precompiles answer (with Foundry's cast; on mainnet these addresses return empty data instead):
RPC=https://evm-testnet.qore.host
cast call 0x0000000000000000000000000000000000000C01 "rlConsensusParams()(uint256,uint256,uint256,uint256)" --rpc-url $RPC
cast call 0x0000000000000000000000000000000000000A02 "pqcKeyStatus(address)(bool,uint8,bytes,bool)" 0x0000000000000000000000000000000000000001 --rpc-url $RPC
Standard precompiles
Besides the six QoreChain precompiles, the node carries the standard cosmos/evm precompiles. They follow the same rule: they run only where governance has listed their address in active_static_precompiles, and the community build of the node does not include them.
| Address | Precompile |
|---|---|
0x…0100 | P-256 (stateless) |
0x…0400 | Bech32 (stateless) |
0x…0800 | Staking |
0x…0801 | Distribution |
0x…0802 | IBC transfer (ICS-20) |
0x…0804 | Bank (read-only) |
0x…0805 | Governance |
0x…0806 | Slashing |
0x…0803 (vesting) is reserved upstream and not implemented: there is nothing at that address. Staking, delegation and validator operations on QoreChain are native-lane only (see Staking and Validators), so do not build staking flows on the staking precompile. Source: docs/EVM_PRECOMPILES.md in the public core repository at tag v3.2.6.
Precompile Address Map
Gas figures are what the node charges once a precompile is active (per byte means per byte of call input).
| Precompile | Address | Base Gas | Description |
|---|---|---|---|
| CrossVM Bridge | 0x0000000000000000000000000000000000000901 | 50,000 + 4/byte | Synchronous cross-VM calls (EVM to QoreChain Native) |
| PQC Verify | 0x0000000000000000000000000000000000000A01 | 25,000 + 8/byte | Verify ML-DSA-87 post-quantum signatures |
| PQC Key Status | 0x0000000000000000000000000000000000000A02 | 2,500 | Check if an account has a registered PQC key |
| AI Risk Score | 0x0000000000000000000000000000000000000B01 | 50,000 | Get AI-generated risk score for transaction data |
| AI Anomaly Check | 0x0000000000000000000000000000000000000B02 | 40,000 | Check if a transfer is flagged as anomalous |
| PRISM Consensus Params | 0x0000000000000000000000000000000000000C01 | 1,500 | Read current PRISM-tuned consensus parameters |
Solidity Interfaces
IQorePQC.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IQorePQC {
/// @notice Verify an ML-DSA-87 (Dilithium-5) signature
/// @param pubkey The 2592-byte ML-DSA-87 public key
/// @param signature The 4627-byte ML-DSA-87 signature
/// @param message The original message bytes
/// @return valid True if the signature is valid
function pqcVerify(
bytes calldata pubkey,
bytes calldata signature,
bytes calldata message
) external view returns (bool valid);
/// @notice Check if an account has a registered PQC public key
/// @param account The Ethereum-style address to check
/// @return registered True if a PQC key is registered
/// @return algorithmId The PQC algorithm identifier (1 = ML-DSA-87)
/// @return pubkey The registered public key bytes (empty if not registered)
function pqcKeyStatus(address account)
external
view
returns (bool registered, uint8 algorithmId, bytes memory pubkey);
}
From v3.2.4 (mainnet since 23 October 2026, testnet since 19 October 2026), the output of pqcKeyStatus carries a fourth value, bool possessionProven, after pubkey: whether the account's current key was proven by a verified proof of possession (see Post-Quantum Signing). It is returned wherever the precompile is active: on the testnet it is live (a call for an account without a key returns false, 0, 0x, false), on mainnet once an upgrade activates the precompile. The interface above lists the first three outputs; a contract that must trust the key should read the fourth as well.
IQoreAI.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IQoreAI {
/// @notice Get the AI-generated risk score for transaction data
/// @param txData The transaction data to evaluate
/// @return score Risk score in basis points (0-10000)
/// @return level Risk level: 0=SAFE, 1=LOW, 2=MEDIUM, 3=HIGH, 4=CRITICAL
function aiRiskScore(bytes calldata txData)
external
view
returns (uint256 score, uint8 level);
/// @notice Check if a transfer is flagged as anomalous
/// @param sender The sender address
/// @param amount The transfer amount in wei
/// @return anomalyScore The anomaly score (higher = more anomalous)
/// @return flagged True if the transfer exceeds the anomaly threshold
function aiAnomalyCheck(address sender, uint256 amount)
external
view
returns (uint256 anomalyScore, bool flagged);
}
IQoreConsensus.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IQoreConsensus {
/// @notice Get current PRISM-tuned consensus parameters
/// @return blockTime Current target block time in milliseconds
/// @return baseGasPrice Current base gas price in wei
/// @return validatorSetSize Current active validator set size
/// @return epoch Current PRISM epoch number
function rlConsensusParams()
external
view
returns (
uint256 blockTime,
uint256 baseGasPrice,
uint256 validatorSetSize,
uint256 epoch
);
}
Usage Examples
PQC Verify — Verify a Post-Quantum Signature
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "./interfaces/IQorePQC.sol";
contract PQCVerifier {
IQorePQC constant PQC = IQorePQC(0x0000000000000000000000000000000000000A01);
/// @notice Verify a PQC signature and revert if invalid
function verifyOrRevert(
bytes calldata pubkey,
bytes calldata signature,
bytes calldata message
) external view {
bool valid = PQC.pqcVerify(pubkey, signature, message);
require(valid, "PQC signature verification failed");
}
}
Gas cost: 25,000 base + 8 gas per byte of input data. The input is the ABI-encoded call (public key 2592 bytes, signature 4627 bytes, the message, plus ABI offsets and padding), so a typical ML-DSA-87 verification costs roughly 85,000-90,000 gas.
PQC Key Status — Check Account Registration
contract PQCChecker {
IQorePQC constant PQC_STATUS = IQorePQC(0x0000000000000000000000000000000000000A02);
function requirePQCKey(address account) external view {
(bool registered, , ) = PQC_STATUS.pqcKeyStatus(account);
require(registered, "Account must have a registered PQC key");
}
}
Gas cost: 2,500 flat.
AI Risk Score — Evaluate Transaction Risk
import "./interfaces/IQoreAI.sol";
contract RiskGate {
IQoreAI constant AI = IQoreAI(0x0000000000000000000000000000000000000B01);
uint8 constant MAX_ALLOWED_RISK = 2; // Allow up to MEDIUM
function executeIfSafe(bytes calldata txData) external {
(uint256 score, uint8 level) = AI.aiRiskScore(txData);
require(level <= MAX_ALLOWED_RISK, "Risk level too high");
// Proceed with execution...
}
}
Risk levels:
| Level | Value |
|---|---|
| SAFE | 0 |
| LOW | 1 |
| MEDIUM | 2 |
| HIGH | 3 |
| CRITICAL | 4 |
The level comes from the AI engine's severity label for the transaction; the score is returned in basis points (0 - 10000). There is no fixed mapping from score ranges to levels, so gate on level rather than on score thresholds.
Gas cost: 50,000 flat.
AI Anomaly Check — Flag Suspicious Transfers
contract AnomalyGuard {
IQoreAI constant AI = IQoreAI(0x0000000000000000000000000000000000000B02);
function safeTransfer(address sender, uint256 amount) external view {
(uint256 anomalyScore, bool flagged) = AI.aiAnomalyCheck(sender, amount);
if (flagged) {
revert("Transfer flagged as anomalous");
}
}
}
Gas cost: 40,000 flat.
PRISM Consensus Params — Read Consensus State
contract ConsensusReader {
IQoreConsensus constant RL = IQoreConsensus(
0x0000000000000000000000000000000000000C01
);
function getBlockTime() external view returns (uint256) {
(uint256 blockTime, , , ) = RL.rlConsensusParams();
return blockTime;
}
function getCurrentEpoch() external view returns (uint256) {
(, , , uint256 epoch) = RL.rlConsensusParams();
return epoch;
}
}
Gas cost: 1,500 flat, plus the state reads the call performs at the standard cost.
From v3.2.6 (both networks run it or a later release; the precompile itself is active on the testnet only) rlConsensusParams returns the values held by the rlconsensus module instead of fixed placeholders: the applied block-time target in milliseconds, the applied gas-price floor, the module's default_validator_set_size and the agent's current epoch. On the testnet on 27 October 2026 it returned 5000, 100, 100, 1 (the same applied values as GET /qorechain/rlconsensus/v1/applied_params, see REST / gRPC Endpoints). These are the module's own settings, not the measured block time (about 3.1 s on mainnet and about 1.3 s on the testnet), and the agent runs in shadow mode on the testnet, so it applies nothing. On mainnet the address is not in the active list: an eth_call to it returned empty data (0x) on 4 November 2026, even though the v3.2.6 code is in the binary (rl-params-precompile-live is enabled). On a node older than v3.2.6 the same call returns fixed placeholder defaults.
CrossVM Bridge — Call QoreChain Native from EVM
The CrossVM bridge precompile is not active on mainnet and is active on the testnet (read 7 November 2026), where a read-only eth_call through it was run on 7 November 2026 after the v3.2.7 upgrade: a target on the allowlist reaches the contract, a target outside it is refused with is not allowlisted (details in Cross-VM Interoperability). On mainnet it stays off, with cross-VM calls, even after a v3.2.7 upgrade, until a separate governance decision. The binary since v3.2.3 carries a hardened version of it that: refuses a STATICCALL; runs the nested call on a gas meter bounded by what the calling frame has left and journals it, so a revert in the calling contract also undoes the QoreChain Native side; reaches only allowlisted QoreChain Native targets (EVM targets are refused); and honours the cross-VM circuit breaker. Until a governance proposal activates it on mainnet there is no synchronous EVM-to-QoreChain Native path there; see Cross-VM Interoperability for the MsgCrossVMCall message path, which is itself switched off on mainnet today.
The node decodes calldata as executeCrossVMCall(uint8 targetVM, string targetContract, bytes payload) (4-byte selector, then the ABI-encoded arguments). The only supported use is EVM to QoreChain Native (targetVM = 1); the ABI may still change before activation. The @qorechain/rdk helper encodeCrossVmCalldata encodes this layout from 0.6.0 (earlier releases encoded a different function), and its isCrossVmPrecompileActive reads the live active_static_precompiles list; see Multi-VM.
Gas cost: 50,000 base + 4 gas per byte of input, plus the target contract execution cost. See Cross-VM Interoperability for details.
Interface File Locations
The Solidity interface files are available in the repository for direct import:
contracts/
interfaces/
IQorePQC.sol
IQoreAI.sol
IQoreConsensus.sol
Install them in your Hardhat or Foundry project:
# Copy interfaces to your project
cp -r contracts/interfaces/ my-project/contracts/interfaces/
Or reference them via import path in your Solidity files:
import "./interfaces/IQorePQC.sol";
import "./interfaces/IQoreAI.sol";
import "./interfaces/IQoreConsensus.sol";
Next Steps
- Cross-VM Interoperability — Full cross-VM messaging documentation
- EVM Development — Deploying Solidity contracts
- Account Abstraction — Linked-wallet authenticators and spending rules (enabled on the testnet, disabled on mainnet)