Files
smom-dbis-138/contracts/registry/UniversalAssetRegistry.sol

977 lines
33 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../vendor/openzeppelin/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../vendor/openzeppelin/ReentrancyGuardUpgradeable.sol";
import "../interfaces/IRegulatedAssetMetadata.sol";
/**
* @title UniversalAssetRegistry
* @notice Central registry for all asset types with governance and compliance
* @dev Supports 10+ asset types with hybrid governance based on risk levels
*/
contract UniversalAssetRegistry is
Initializable,
AccessControlUpgradeable,
ReentrancyGuardUpgradeable,
UUPSUpgradeable
{
bytes32 public constant REGISTRAR_ROLE = keccak256("REGISTRAR_ROLE");
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
bytes32 public constant VALIDATOR_ROLE = keccak256("VALIDATOR_ROLE");
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
bytes32 public constant JURISDICTION_MANAGER_ROLE = keccak256("JURISDICTION_MANAGER_ROLE");
bytes32 public constant REGULATOR_ROLE = keccak256("REGULATOR_ROLE");
bytes32 public constant SUPERVISOR_ROLE = keccak256("SUPERVISOR_ROLE");
bytes32 public constant EMERGENCY_ADMIN_ROLE = keccak256("EMERGENCY_ADMIN_ROLE");
// Asset classification types
enum AssetType {
ERC20Standard, // Standard tokens
ISO4217W, // eMoney/CBDCs
GRU, // Global Reserve Units (M00/M0/M1)
Commodity, // Gold, oil, etc.
Security, // Tokenized securities
RealWorldAsset, // Real estate, art, etc.
Synthetic, // Derivatives, futures
Stablecoin, // USDT, USDC, etc.
GovernanceToken, // DAO tokens
NFTBacked // NFT-collateralized tokens
}
// Compliance levels
enum ComplianceLevel {
Public, // No restrictions
KYC, // KYC required
Accredited, // Accredited investors only
Institutional, // Institutions only
Sovereign // Central banks/governments only
}
// Proposal types
enum ProposalType {
AddAsset,
RemoveAsset,
UpdateRiskParams,
UpdateCompliance,
EmergencyPause
}
struct UniversalAsset {
address tokenAddress;
AssetType assetType;
ComplianceLevel complianceLevel;
// Metadata
string name;
string symbol;
uint8 decimals;
string jurisdiction;
// Risk parameters
uint8 volatilityScore; // 0-100
uint256 minBridgeAmount;
uint256 maxBridgeAmount;
uint256 dailyVolumeLimit;
// PMM liquidity
address pmmPool;
bool hasLiquidity;
uint256 liquidityReserveUSD;
// Governance
bool requiresGovernance;
address[] validators;
uint256 validationThreshold;
// Status
bool isActive;
uint256 registeredAt;
uint256 lastUpdated;
// Governance, storage, and supervision
bytes32 assetId;
bytes32 assetVersionId;
bytes32 governanceProfileId;
bytes32 supervisionProfileId;
bytes32 storageNamespace;
address canonicalUnderlyingAsset;
bool isWrappedTransport;
bool supervisionRequired;
bool governmentApprovalRequired;
uint256 minimumUpgradeNoticePeriod;
string regulatoryDisclosureURI;
string reportingURI;
}
struct PendingAssetProposal {
bytes32 proposalId;
ProposalType proposalType;
address proposer;
uint256 proposedAt;
uint256 executeAfter;
uint256 votesFor;
uint256 votesAgainst;
bool executed;
bool cancelled;
// Proposal data
address tokenAddress;
AssetType assetType;
ComplianceLevel complianceLevel;
string name;
string symbol;
uint8 decimals;
string jurisdiction;
uint8 volatilityScore;
uint256 minBridgeAmount;
uint256 maxBridgeAmount;
}
struct JurisdictionProfile {
bool active;
bool requiresGovernmentApproval;
bool requiresPeriodicReports;
uint256 minimumUpgradeNoticePeriod;
string supervisionURI;
bytes32 policyHash;
}
struct JurisdictionAuthority {
bool active;
bool canApproveGovernance;
bool canApproveUpgrades;
bool canPause;
bool canReceiveReports;
}
// Storage
mapping(address => UniversalAsset) public assets;
mapping(AssetType => address[]) public assetsByType;
mapping(bytes32 => PendingAssetProposal) public proposals;
mapping(bytes32 => mapping(address => bool)) public hasVoted;
// Governance parameters
uint256 public constant TIMELOCK_STANDARD = 1 days;
uint256 public constant TIMELOCK_MODERATE = 3 days;
uint256 public constant TIMELOCK_HIGH = 7 days;
uint256 public quorumPercentage;
// Validator set
address[] public validators;
mapping(address => bool) public isValidator;
address public governanceController;
mapping(bytes32 => JurisdictionProfile) private _jurisdictionProfiles;
mapping(bytes32 => mapping(address => JurisdictionAuthority)) private _jurisdictionAuthorities;
mapping(address => bytes32) private _assetJurisdictionIds;
// Events
event AssetProposed(
bytes32 indexed proposalId,
address indexed token,
AssetType assetType,
address proposer
);
event AssetApproved(
address indexed token,
AssetType assetType,
ComplianceLevel complianceLevel
);
event AssetRemoved(address indexed token, AssetType assetType);
event ValidatorAdded(address indexed validator);
event ValidatorRemoved(address indexed validator);
event ProposalVoted(
bytes32 indexed proposalId,
address indexed voter,
bool support
);
event ProposalExecuted(bytes32 indexed proposalId);
event ProposalCancelled(bytes32 indexed proposalId);
event JurisdictionProfileUpdated(
bytes32 indexed jurisdictionId,
string jurisdiction,
bool active,
bool requiresGovernmentApproval,
bool requiresPeriodicReports,
uint256 minimumUpgradeNoticePeriod,
string supervisionURI,
bytes32 policyHash
);
event JurisdictionAuthorityUpdated(
bytes32 indexed jurisdictionId,
string jurisdiction,
address indexed authority,
bool active,
bool canApproveGovernance,
bool canApproveUpgrades,
bool canPause,
bool canReceiveReports
);
event AssetGovernanceProfileUpdated(
address indexed token,
bytes32 governanceProfileId,
bytes32 supervisionProfileId,
bytes32 storageNamespace
);
event AssetSupervisionProfileUpdated(
address indexed token,
address canonicalUnderlyingAsset,
bool isWrappedTransport,
bool supervisionRequired,
bool governmentApprovalRequired,
uint256 minimumUpgradeNoticePeriod
);
error GovernanceControllerOnly(address caller);
error GovernanceControllerNotConfigured();
error ZeroGovernanceController();
modifier onlyGovernanceExecution() {
if (governanceController == address(0)) revert GovernanceControllerNotConfigured();
if (_msgSender() != governanceController) revert GovernanceControllerOnly(_msgSender());
_;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(address admin) external initializer {
__AccessControl_init();
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(REGISTRAR_ROLE, admin);
_grantRole(PROPOSER_ROLE, admin);
_grantRole(VALIDATOR_ROLE, admin);
_grantRole(UPGRADER_ROLE, admin);
_grantRole(JURISDICTION_MANAGER_ROLE, admin);
_grantRole(REGULATOR_ROLE, admin);
_grantRole(SUPERVISOR_ROLE, admin);
_grantRole(EMERGENCY_ADMIN_ROLE, admin);
quorumPercentage = 51; // 51% quorum
}
function _authorizeUpgrade(address newImplementation)
internal override onlyRole(UPGRADER_ROLE) {}
/**
* @notice Propose new asset with timelock governance
*/
function proposeAsset(
address tokenAddress,
AssetType assetType,
ComplianceLevel complianceLevel,
string calldata name,
string calldata symbol,
uint8 decimals,
string calldata jurisdiction,
uint8 volatilityScore,
uint256 minBridge,
uint256 maxBridge
) external onlyRole(PROPOSER_ROLE) returns (bytes32 proposalId) {
require(tokenAddress != address(0), "Zero address");
require(!assets[tokenAddress].isActive, "Already registered");
require(volatilityScore <= 100, "Invalid volatility");
require(maxBridge >= minBridge, "Invalid limits");
proposalId = keccak256(abi.encode(
tokenAddress,
assetType,
block.timestamp,
msg.sender
));
uint256 timelockPeriod = _getTimelockPeriod(assetType, complianceLevel);
PendingAssetProposal storage proposal = proposals[proposalId];
proposal.proposalId = proposalId;
proposal.proposalType = ProposalType.AddAsset;
proposal.proposer = msg.sender;
proposal.proposedAt = block.timestamp;
proposal.executeAfter = block.timestamp + timelockPeriod;
proposal.tokenAddress = tokenAddress;
proposal.assetType = assetType;
proposal.complianceLevel = complianceLevel;
proposal.name = name;
proposal.symbol = symbol;
proposal.decimals = decimals;
proposal.jurisdiction = jurisdiction;
proposal.volatilityScore = volatilityScore;
proposal.minBridgeAmount = minBridge;
proposal.maxBridgeAmount = maxBridge;
emit AssetProposed(proposalId, tokenAddress, assetType, msg.sender);
return proposalId;
}
/**
* @notice Vote on asset proposal
*/
function voteOnProposal(
bytes32 proposalId,
bool support
) external onlyRole(VALIDATOR_ROLE) {
PendingAssetProposal storage proposal = proposals[proposalId];
require(!proposal.executed, "Already executed");
require(!proposal.cancelled, "Cancelled");
require(!hasVoted[proposalId][msg.sender], "Already voted");
hasVoted[proposalId][msg.sender] = true;
if (support) {
proposal.votesFor++;
} else {
proposal.votesAgainst++;
}
emit ProposalVoted(proposalId, msg.sender, support);
}
/**
* @notice Execute proposal after timelock
*/
function executeProposal(bytes32 proposalId) external nonReentrant {
PendingAssetProposal storage proposal = proposals[proposalId];
require(!proposal.executed, "Already executed");
require(!proposal.cancelled, "Cancelled");
require(block.timestamp >= proposal.executeAfter, "Timelock active");
// Check quorum
uint256 totalVotes = proposal.votesFor + proposal.votesAgainst;
uint256 requiredVotes = (validators.length * quorumPercentage) / 100;
require(totalVotes >= requiredVotes, "Quorum not met");
require(proposal.votesFor > proposal.votesAgainst, "Rejected");
// Execute based on proposal type
if (proposal.proposalType == ProposalType.AddAsset) {
_registerAsset(proposal);
}
proposal.executed = true;
emit ProposalExecuted(proposalId);
}
/**
* @notice Register a GRU compliant token (c*) directly — no timelock. For use by deploy scripts to integrate all c* with GRU ERC-2535 / bridge.
* @dev Only REGISTRAR_ROLE. Registers as AssetType.GRU so GRUCCIPBridge and PoolManager accept the token.
*/
function registerGRUCompliantAsset(
address tokenAddress,
string calldata name,
string calldata symbol,
uint8 decimals,
string calldata jurisdiction
) external onlyRole(REGISTRAR_ROLE) {
require(tokenAddress != address(0), "Zero address");
require(!assets[tokenAddress].isActive, "Already registered");
_registerAssetDirect(
tokenAddress,
AssetType.GRU,
ComplianceLevel.Institutional,
name,
symbol,
decimals,
jurisdiction,
10, // volatilityScore (low for stable)
1e6, // minBridge (1 unit with 6 decimals)
100_000_000 * 10**6 // maxBridge (100M with 6 decimals)
);
}
/**
* @notice Internal asset registration from proposal
*/
function _registerAsset(PendingAssetProposal storage proposal) internal {
_registerAssetDirect(
proposal.tokenAddress,
proposal.assetType,
proposal.complianceLevel,
proposal.name,
proposal.symbol,
proposal.decimals,
proposal.jurisdiction,
proposal.volatilityScore,
proposal.minBridgeAmount,
proposal.maxBridgeAmount
);
}
/**
* @notice Internal asset registration (shared by proposal execution and registerGRUCompliantAsset)
*/
function _registerAssetDirect(
address tokenAddress,
AssetType assetType,
ComplianceLevel complianceLevel,
string memory name,
string memory symbol,
uint8 decimals,
string memory jurisdiction,
uint8 volatilityScore,
uint256 minBridgeAmount,
uint256 maxBridgeAmount
) internal {
UniversalAsset storage asset = assets[tokenAddress];
asset.tokenAddress = tokenAddress;
asset.assetType = assetType;
asset.complianceLevel = complianceLevel;
asset.name = name;
asset.symbol = symbol;
asset.decimals = decimals;
asset.jurisdiction = jurisdiction;
asset.volatilityScore = volatilityScore;
asset.minBridgeAmount = minBridgeAmount;
asset.maxBridgeAmount = maxBridgeAmount;
asset.dailyVolumeLimit = maxBridgeAmount * 10;
asset.requiresGovernance = _requiresGovernance(assetType, complianceLevel);
asset.isActive = true;
asset.registeredAt = block.timestamp;
asset.lastUpdated = block.timestamp;
_assetJurisdictionIds[tokenAddress] = jurisdictionIdFor(jurisdiction);
_syncAssetMetadata(tokenAddress, asset);
_applyJurisdictionDefaults(asset, _assetJurisdictionIds[tokenAddress]);
assetsByType[assetType].push(tokenAddress);
emit AssetApproved(tokenAddress, assetType, complianceLevel);
}
/**
* @notice Add validator
*/
function addValidator(address validator) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(validator != address(0), "Zero address");
require(!isValidator[validator], "Already validator");
validators.push(validator);
isValidator[validator] = true;
_grantRole(VALIDATOR_ROLE, validator);
emit ValidatorAdded(validator);
}
/**
* @notice Remove validator
*/
function removeValidator(address validator) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(isValidator[validator], "Not validator");
isValidator[validator] = false;
_revokeRole(VALIDATOR_ROLE, validator);
// Remove from array
for (uint256 i = 0; i < validators.length; i++) {
if (validators[i] == validator) {
validators[i] = validators[validators.length - 1];
validators.pop();
break;
}
}
emit ValidatorRemoved(validator);
}
/**
* @notice Update PMM pool for asset
*/
function updatePMMPool(
address token,
address pmmPool
) external onlyRole(REGISTRAR_ROLE) {
require(assets[token].isActive, "Not registered");
assets[token].pmmPool = pmmPool;
assets[token].hasLiquidity = pmmPool != address(0);
assets[token].lastUpdated = block.timestamp;
}
function setGovernanceController(address governanceController_) external onlyRole(EMERGENCY_ADMIN_ROLE) {
if (governanceController_ == address(0)) revert ZeroGovernanceController();
governanceController = governanceController_;
}
function setJurisdictionProfile(
string calldata jurisdiction,
bool active,
bool requiresGovernmentApproval,
bool requiresPeriodicReports,
uint256 minimumUpgradeNoticePeriod,
string calldata supervisionURI,
bytes32 policyHash
) external onlyGovernanceExecution {
_setJurisdictionProfileValue(
jurisdiction,
active,
requiresGovernmentApproval,
requiresPeriodicReports,
minimumUpgradeNoticePeriod,
supervisionURI,
policyHash
);
}
function setDerivedJurisdictionProfile(
address token,
bool active,
bool requiresGovernmentApproval,
bool requiresPeriodicReports,
uint256 minimumUpgradeNoticePeriod,
string calldata supervisionURI,
bytes32 policyHash
) external onlyGovernanceExecution {
require(assets[token].isActive, "Not registered");
_setJurisdictionProfileValue(
assets[token].jurisdiction,
active,
requiresGovernmentApproval,
requiresPeriodicReports,
minimumUpgradeNoticePeriod,
supervisionURI,
policyHash
);
}
function emergencySetJurisdictionProfile(
string calldata jurisdiction,
bool active,
bool requiresGovernmentApproval,
bool requiresPeriodicReports,
uint256 minimumUpgradeNoticePeriod,
string calldata supervisionURI,
bytes32 policyHash
) external onlyRole(EMERGENCY_ADMIN_ROLE) {
_setJurisdictionProfileValue(
jurisdiction,
active,
requiresGovernmentApproval,
requiresPeriodicReports,
minimumUpgradeNoticePeriod,
supervisionURI,
policyHash
);
}
function _setJurisdictionProfileValue(
string memory jurisdiction,
bool active,
bool requiresGovernmentApproval,
bool requiresPeriodicReports,
uint256 minimumUpgradeNoticePeriod,
string memory supervisionURI,
bytes32 policyHash
) internal {
bytes32 jurisdictionId = jurisdictionIdFor(jurisdiction);
JurisdictionProfile storage profile = _jurisdictionProfiles[jurisdictionId];
profile.active = active;
profile.requiresGovernmentApproval = requiresGovernmentApproval;
profile.requiresPeriodicReports = requiresPeriodicReports;
profile.minimumUpgradeNoticePeriod = minimumUpgradeNoticePeriod;
profile.supervisionURI = supervisionURI;
profile.policyHash = policyHash;
emit JurisdictionProfileUpdated(
jurisdictionId,
jurisdiction,
active,
requiresGovernmentApproval,
requiresPeriodicReports,
minimumUpgradeNoticePeriod,
supervisionURI,
policyHash
);
}
function setJurisdictionAuthority(
string calldata jurisdiction,
address authority,
bool active,
bool canApproveGovernance,
bool canApproveUpgrades,
bool canPause,
bool canReceiveReports
) external onlyGovernanceExecution {
_setJurisdictionAuthorityValue(
jurisdiction,
authority,
active,
canApproveGovernance,
canApproveUpgrades,
canPause,
canReceiveReports
);
}
function setDerivedJurisdictionAuthority(
address token,
address authority,
bool active,
bool canApproveGovernance,
bool canApproveUpgrades,
bool canPause,
bool canReceiveReports
) external onlyGovernanceExecution {
require(assets[token].isActive, "Not registered");
_setJurisdictionAuthorityValue(
assets[token].jurisdiction,
authority,
active,
canApproveGovernance,
canApproveUpgrades,
canPause,
canReceiveReports
);
}
function emergencySetJurisdictionAuthority(
string calldata jurisdiction,
address authority,
bool active,
bool canApproveGovernance,
bool canApproveUpgrades,
bool canPause,
bool canReceiveReports
) external onlyRole(EMERGENCY_ADMIN_ROLE) {
_setJurisdictionAuthorityValue(
jurisdiction,
authority,
active,
canApproveGovernance,
canApproveUpgrades,
canPause,
canReceiveReports
);
}
function _setJurisdictionAuthorityValue(
string memory jurisdiction,
address authority,
bool active,
bool canApproveGovernance,
bool canApproveUpgrades,
bool canPause,
bool canReceiveReports
) internal {
require(authority != address(0), "Zero address");
bytes32 jurisdictionId = jurisdictionIdFor(jurisdiction);
_jurisdictionAuthorities[jurisdictionId][authority] = JurisdictionAuthority({
active: active,
canApproveGovernance: canApproveGovernance,
canApproveUpgrades: canApproveUpgrades,
canPause: canPause,
canReceiveReports: canReceiveReports
});
emit JurisdictionAuthorityUpdated(
jurisdictionId,
jurisdiction,
authority,
active,
canApproveGovernance,
canApproveUpgrades,
canPause,
canReceiveReports
);
}
function setAssetGovernanceProfile(
address token,
bytes32 governanceProfileId,
bytes32 supervisionProfileId,
bytes32 storageNamespace,
address canonicalUnderlyingAsset,
bool isWrappedTransport,
bool supervisionRequired,
bool governmentApprovalRequired,
uint256 minimumUpgradeNoticePeriod,
string calldata regulatoryDisclosureURI,
string calldata reportingURI
) external onlyGovernanceExecution {
_setAssetGovernanceProfileValue(
token,
governanceProfileId,
supervisionProfileId,
storageNamespace,
canonicalUnderlyingAsset,
isWrappedTransport,
supervisionRequired,
governmentApprovalRequired,
minimumUpgradeNoticePeriod,
regulatoryDisclosureURI,
reportingURI
);
}
function emergencySetAssetGovernanceProfile(
address token,
bytes32 governanceProfileId,
bytes32 supervisionProfileId,
bytes32 storageNamespace,
address canonicalUnderlyingAsset,
bool isWrappedTransport,
bool supervisionRequired,
bool governmentApprovalRequired,
uint256 minimumUpgradeNoticePeriod,
string calldata regulatoryDisclosureURI,
string calldata reportingURI
) external onlyRole(EMERGENCY_ADMIN_ROLE) {
_setAssetGovernanceProfileValue(
token,
governanceProfileId,
supervisionProfileId,
storageNamespace,
canonicalUnderlyingAsset,
isWrappedTransport,
supervisionRequired,
governmentApprovalRequired,
minimumUpgradeNoticePeriod,
regulatoryDisclosureURI,
reportingURI
);
}
function _setAssetGovernanceProfileValue(
address token,
bytes32 governanceProfileId,
bytes32 supervisionProfileId,
bytes32 storageNamespace,
address canonicalUnderlyingAsset,
bool isWrappedTransport,
bool supervisionRequired,
bool governmentApprovalRequired,
uint256 minimumUpgradeNoticePeriod,
string memory regulatoryDisclosureURI,
string memory reportingURI
) internal {
require(assets[token].isActive, "Not registered");
UniversalAsset storage asset = assets[token];
asset.governanceProfileId = governanceProfileId;
asset.supervisionProfileId = supervisionProfileId;
asset.storageNamespace = storageNamespace;
asset.canonicalUnderlyingAsset = canonicalUnderlyingAsset;
asset.isWrappedTransport = isWrappedTransport;
asset.supervisionRequired = supervisionRequired;
asset.governmentApprovalRequired = governmentApprovalRequired;
asset.minimumUpgradeNoticePeriod = minimumUpgradeNoticePeriod;
asset.regulatoryDisclosureURI = regulatoryDisclosureURI;
asset.reportingURI = reportingURI;
asset.lastUpdated = block.timestamp;
emit AssetGovernanceProfileUpdated(token, governanceProfileId, supervisionProfileId, storageNamespace);
emit AssetSupervisionProfileUpdated(
token,
canonicalUnderlyingAsset,
isWrappedTransport,
supervisionRequired,
governmentApprovalRequired,
minimumUpgradeNoticePeriod
);
}
function syncAssetMetadataFromToken(address token) external onlyGovernanceExecution {
require(assets[token].isActive, "Not registered");
_syncAssetMetadata(token, assets[token]);
assets[token].lastUpdated = block.timestamp;
}
function emergencySyncAssetMetadataFromToken(address token) external onlyRole(EMERGENCY_ADMIN_ROLE) {
require(assets[token].isActive, "Not registered");
_syncAssetMetadata(token, assets[token]);
assets[token].lastUpdated = block.timestamp;
}
/**
* @notice Get timelock period based on asset type and compliance
*/
function _getTimelockPeriod(
AssetType assetType,
ComplianceLevel complianceLevel
) internal pure returns (uint256) {
if (assetType == AssetType.Security ||
assetType == AssetType.ISO4217W ||
complianceLevel >= ComplianceLevel.Institutional) {
return TIMELOCK_HIGH;
} else if (assetType == AssetType.Commodity ||
assetType == AssetType.RealWorldAsset ||
complianceLevel >= ComplianceLevel.Accredited) {
return TIMELOCK_MODERATE;
} else {
return TIMELOCK_STANDARD;
}
}
/**
* @notice Check if asset type requires governance
*/
function _requiresGovernance(
AssetType assetType,
ComplianceLevel complianceLevel
) internal pure returns (bool) {
return assetType == AssetType.Security ||
assetType == AssetType.ISO4217W ||
assetType == AssetType.Commodity ||
complianceLevel >= ComplianceLevel.Accredited;
}
// View functions
function getAsset(address token) external view returns (UniversalAsset memory) {
return assets[token];
}
function getAssetType(address token) external view returns (AssetType) {
return assets[token].assetType;
}
function getAssetsByType(AssetType assetType) external view returns (address[] memory) {
return assetsByType[assetType];
}
function getValidators() external view returns (address[] memory) {
return validators;
}
function getJurisdictionProfile(string calldata jurisdiction) external view returns (JurisdictionProfile memory) {
return _jurisdictionProfiles[jurisdictionIdFor(jurisdiction)];
}
function getJurisdictionAuthority(
string calldata jurisdiction,
address authority
) external view returns (JurisdictionAuthority memory) {
return _jurisdictionAuthorities[jurisdictionIdFor(jurisdiction)][authority];
}
function getAssetJurisdictionId(address token) external view returns (bytes32) {
return _assetJurisdictionIds[token];
}
function isGovernanceAuthority(bytes32 jurisdictionId, address authority) external view returns (bool) {
JurisdictionAuthority storage permissions = _jurisdictionAuthorities[jurisdictionId][authority];
return permissions.active && permissions.canApproveGovernance;
}
function getJurisdictionMinimumUpgradeNotice(bytes32 jurisdictionId) external view returns (uint256) {
return _jurisdictionProfiles[jurisdictionId].minimumUpgradeNoticePeriod;
}
function getProposal(bytes32 proposalId) external view returns (PendingAssetProposal memory) {
return proposals[proposalId];
}
function isAssetActive(address token) external view returns (bool) {
return assets[token].isActive;
}
function jurisdictionIdFor(string memory jurisdiction) public pure returns (bytes32) {
return keccak256(bytes(jurisdiction));
}
function _applyJurisdictionDefaults(UniversalAsset storage asset, bytes32 jurisdictionId) internal {
JurisdictionProfile storage profile = _jurisdictionProfiles[jurisdictionId];
if (!profile.active) {
return;
}
asset.supervisionRequired = true;
asset.governmentApprovalRequired = profile.requiresGovernmentApproval;
if (profile.minimumUpgradeNoticePeriod > asset.minimumUpgradeNoticePeriod) {
asset.minimumUpgradeNoticePeriod = profile.minimumUpgradeNoticePeriod;
}
}
function _syncAssetMetadata(address tokenAddress, UniversalAsset storage asset) internal {
asset.assetId = _defaultAssetId(asset.symbol);
asset.assetVersionId = _defaultAssetVersionId(asset.symbol);
asset.governanceProfileId = _defaultGovernanceProfileId(asset.symbol);
asset.supervisionProfileId = _defaultSupervisionProfileId(asset.symbol);
asset.storageNamespace = _defaultStorageNamespace(asset.symbol);
try IRegulatedAssetMetadata(tokenAddress).assetId() returns (bytes32 value) {
asset.assetId = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).assetVersionId() returns (bytes32 value) {
asset.assetVersionId = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).governanceProfileId() returns (bytes32 value) {
asset.governanceProfileId = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).supervisionProfileId() returns (bytes32 value) {
asset.supervisionProfileId = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).storageNamespace() returns (bytes32 value) {
asset.storageNamespace = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).primaryJurisdiction() returns (string memory jurisdictionValue) {
if (bytes(jurisdictionValue).length > 0) {
asset.jurisdiction = jurisdictionValue;
_assetJurisdictionIds[tokenAddress] = jurisdictionIdFor(jurisdictionValue);
}
} catch {}
try IRegulatedAssetMetadata(tokenAddress).regulatoryDisclosureURI() returns (string memory value) {
asset.regulatoryDisclosureURI = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).reportingURI() returns (string memory value) {
asset.reportingURI = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).canonicalUnderlyingAsset() returns (address value) {
asset.canonicalUnderlyingAsset = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).supervisionRequired() returns (bool value) {
asset.supervisionRequired = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).governmentApprovalRequired() returns (bool value) {
asset.governmentApprovalRequired = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).minimumUpgradeNoticePeriod() returns (uint256 value) {
asset.minimumUpgradeNoticePeriod = value;
} catch {}
try IRegulatedAssetMetadata(tokenAddress).wrappedTransport() returns (bool value) {
asset.isWrappedTransport = value;
} catch {}
}
function _defaultAssetId(string memory symbol) internal pure returns (bytes32) {
return keccak256(bytes(string.concat("GRU:", symbol)));
}
function _defaultAssetVersionId(string memory symbol) internal pure returns (bytes32) {
return keccak256(bytes(string.concat("GRU:", symbol, ":registry-v1")));
}
function _defaultGovernanceProfileId(string memory symbol) internal pure returns (bytes32) {
return keccak256(bytes(string.concat("GRU:GOV:", symbol)));
}
function _defaultSupervisionProfileId(string memory symbol) internal pure returns (bytes32) {
return keccak256(bytes(string.concat("GRU:SUP:", symbol)));
}
function _defaultStorageNamespace(string memory symbol) internal pure returns (bytes32) {
return keccak256(bytes(string.concat("gru.storage.registry.", symbol)));
}
}