Files
smom-dbis-138/contracts/registry/ChainRegistry.sol
2026-03-02 12:14:09 -08:00

321 lines
11 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/**
* @title ChainRegistry
* @notice Central registry for all supported blockchains (EVM and non-EVM)
* @dev Maps chain identifiers to adapter contracts and metadata
*/
contract ChainRegistry is
Initializable,
AccessControlUpgradeable,
UUPSUpgradeable
{
bytes32 public constant REGISTRY_ADMIN_ROLE = keccak256("REGISTRY_ADMIN_ROLE");
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
enum ChainType {
EVM, // Ethereum, Polygon, Arbitrum, etc.
XRPL, // XRP Ledger
XDC, // XDC Network (EVM-compatible but different address format)
Stellar, // Stellar Network
Algorand, // Algorand
Hedera, // Hedera Hashgraph
Tron, // Tron
TON, // The Open Network
Cosmos, // Cosmos Hub
Solana, // Solana
Fabric, // Hyperledger Fabric
Corda, // R3 Corda
Indy, // Hyperledger Indy
Firefly, // Hyperledger Firefly
Cacti, // Hyperledger Cacti
Other // Custom/unknown
}
struct ChainMetadata {
uint256 chainId; // For EVM chains (0 for non-EVM)
string chainIdentifier; // For non-EVM (e.g., "XRPL", "Fabric-Channel1")
ChainType chainType;
address adapter; // Bridge adapter contract address
bool isActive;
uint256 minConfirmations; // Required confirmations
uint256 avgBlockTime; // Average block time in seconds
bool requiresOracle; // Non-EVM chains need oracle
string rpcEndpoint; // RPC endpoint (encrypted or public)
string explorerUrl; // Block explorer URL
bytes additionalData; // Chain-specific config
uint256 addedAt;
uint256 lastUpdated;
}
// EVM chains: chainId => metadata
mapping(uint256 => ChainMetadata) public evmChains;
// Non-EVM chains: identifier => metadata
mapping(string => ChainMetadata) public nonEvmChains;
// All registered chain identifiers
uint256[] public registeredEVMChainIds;
string[] public registeredNonEVMIdentifiers;
// Adapter validation
mapping(address => bool) public isValidAdapter;
mapping(address => ChainType) public adapterToChainType;
event ChainRegistered(
uint256 indexed chainId,
string indexed chainIdentifier,
ChainType chainType,
address adapter
);
event ChainUpdated(
uint256 indexed chainId,
string indexed chainIdentifier,
bool isActive
);
event ChainDeactivated(
uint256 indexed chainId,
string indexed chainIdentifier
);
event AdapterUpdated(
uint256 indexed chainId,
string indexed chainIdentifier,
address oldAdapter,
address newAdapter
);
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(address admin) external initializer {
__AccessControl_init();
__UUPSUpgradeable_init();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(REGISTRY_ADMIN_ROLE, admin);
_grantRole(UPGRADER_ROLE, admin);
}
function _authorizeUpgrade(address newImplementation)
internal override onlyRole(UPGRADER_ROLE) {}
/**
* @notice Register an EVM chain
*/
function registerEVMChain(
uint256 chainId,
address adapter,
string calldata explorerUrl,
uint256 minConfirmations,
uint256 avgBlockTime,
bytes calldata additionalData
) external onlyRole(REGISTRY_ADMIN_ROLE) {
require(chainId != 0, "Invalid chain ID");
require(adapter != address(0), "Zero adapter");
require(adapter.code.length > 0, "Not a contract");
ChainMetadata storage chain = evmChains[chainId];
// If new chain, add to list
if (chain.addedAt == 0) {
registeredEVMChainIds.push(chainId);
}
chain.chainId = chainId;
chain.chainIdentifier = string(abi.encodePacked("EVM-", chainId));
chain.chainType = ChainType.EVM;
chain.adapter = adapter;
chain.isActive = true;
chain.minConfirmations = minConfirmations;
chain.avgBlockTime = avgBlockTime;
chain.requiresOracle = false;
chain.explorerUrl = explorerUrl;
chain.additionalData = additionalData;
chain.lastUpdated = block.timestamp;
if (chain.addedAt == 0) {
chain.addedAt = block.timestamp;
}
isValidAdapter[adapter] = true;
adapterToChainType[adapter] = ChainType.EVM;
emit ChainRegistered(chainId, chain.chainIdentifier, ChainType.EVM, adapter);
}
/**
* @notice Register a non-EVM chain
*/
function registerNonEVMChain(
string calldata chainIdentifier,
ChainType chainType,
address adapter,
string calldata explorerUrl,
uint256 minConfirmations,
uint256 avgBlockTime,
bool requiresOracle,
bytes calldata additionalData
) external onlyRole(REGISTRY_ADMIN_ROLE) {
require(bytes(chainIdentifier).length > 0, "Empty identifier");
require(adapter != address(0), "Zero adapter");
require(adapter.code.length > 0, "Not a contract");
require(chainType != ChainType.EVM, "Use registerEVMChain");
ChainMetadata storage chain = nonEvmChains[chainIdentifier];
// If new chain, add to list
if (chain.addedAt == 0) {
registeredNonEVMIdentifiers.push(chainIdentifier);
}
chain.chainId = 0;
chain.chainIdentifier = chainIdentifier;
chain.chainType = chainType;
chain.adapter = adapter;
chain.isActive = true;
chain.minConfirmations = minConfirmations;
chain.avgBlockTime = avgBlockTime;
chain.requiresOracle = requiresOracle;
chain.explorerUrl = explorerUrl;
chain.additionalData = additionalData;
chain.lastUpdated = block.timestamp;
if (chain.addedAt == 0) {
chain.addedAt = block.timestamp;
}
isValidAdapter[adapter] = true;
adapterToChainType[adapter] = chainType;
emit ChainRegistered(0, chainIdentifier, chainType, adapter);
}
/**
* @notice Update chain adapter
*/
function updateAdapter(
uint256 chainId,
string calldata chainIdentifier,
address newAdapter
) external onlyRole(REGISTRY_ADMIN_ROLE) {
require(newAdapter != address(0), "Zero adapter");
require(newAdapter.code.length > 0, "Not a contract");
address oldAdapter;
if (chainId != 0) {
ChainMetadata storage chain = evmChains[chainId];
require(chain.addedAt != 0, "Chain not registered");
oldAdapter = chain.adapter;
chain.adapter = newAdapter;
chain.lastUpdated = block.timestamp;
} else {
ChainMetadata storage chain = nonEvmChains[chainIdentifier];
require(chain.addedAt != 0, "Chain not registered");
oldAdapter = chain.adapter;
chain.adapter = newAdapter;
chain.lastUpdated = block.timestamp;
}
isValidAdapter[oldAdapter] = false;
isValidAdapter[newAdapter] = true;
emit AdapterUpdated(chainId, chainIdentifier, oldAdapter, newAdapter);
}
/**
* @notice Enable/disable chain
*/
function setChainActive(
uint256 chainId,
string calldata chainIdentifier,
bool active
) external onlyRole(REGISTRY_ADMIN_ROLE) {
if (chainId != 0) {
ChainMetadata storage chain = evmChains[chainId];
require(chain.addedAt != 0, "Chain not registered");
chain.isActive = active;
chain.lastUpdated = block.timestamp;
} else {
ChainMetadata storage chain = nonEvmChains[chainIdentifier];
require(chain.addedAt != 0, "Chain not registered");
chain.isActive = active;
chain.lastUpdated = block.timestamp;
}
emit ChainUpdated(chainId, chainIdentifier, active);
if (!active) {
emit ChainDeactivated(chainId, chainIdentifier);
}
}
// View functions
function getEVMChain(uint256 chainId)
external view returns (ChainMetadata memory) {
return evmChains[chainId];
}
function getNonEVMChain(string calldata chainIdentifier)
external view returns (ChainMetadata memory) {
return nonEvmChains[chainIdentifier];
}
function getAdapter(uint256 chainId, string calldata chainIdentifier)
external view returns (address) {
if (chainId != 0) {
return evmChains[chainId].adapter;
} else {
return nonEvmChains[chainIdentifier].adapter;
}
}
function isChainActive(uint256 chainId, string calldata chainIdentifier)
external view returns (bool) {
if (chainId != 0) {
return evmChains[chainId].isActive;
} else {
return nonEvmChains[chainIdentifier].isActive;
}
}
function getAllEVMChains()
external view returns (uint256[] memory, ChainMetadata[] memory) {
uint256 length = registeredEVMChainIds.length;
ChainMetadata[] memory chains = new ChainMetadata[](length);
for (uint256 i = 0; i < length; i++) {
chains[i] = evmChains[registeredEVMChainIds[i]];
}
return (registeredEVMChainIds, chains);
}
function getAllNonEVMChains()
external view returns (string[] memory, ChainMetadata[] memory) {
uint256 length = registeredNonEVMIdentifiers.length;
ChainMetadata[] memory chains = new ChainMetadata[](length);
for (uint256 i = 0; i < length; i++) {
chains[i] = nonEvmChains[registeredNonEVMIdentifiers[i]];
}
return (registeredNonEVMIdentifiers, chains);
}
function getTotalChains() external view returns (uint256, uint256) {
return (registeredEVMChainIds.length, registeredNonEVMIdentifiers.length);
}
}