AssetRegistry
AssetRegistry.sol is the on-chain ledger for Real-World Asset certificates. Registry partners submit verified certificates here; governance signers approve them; the oracle assigns quality scores. Once approved, a certificate can back a MultiSigGovernance mint proposal.
All contracts are UUPS upgradeable proxies. Source: contracts/core/AssetRegistry.sol.
Roles
| Role | keccak256 constant | Who holds it | What it allows |
|---|---|---|---|
DEFAULT_ADMIN_ROLE | (OZ default) | Deployer / admin multisig | Grant/revoke all roles |
ADMIN_ROLE | keccak256("ADMIN_ROLE") | Oracle service account | setQualityScore, allocateVolume, retireCertificate, setExternalId, upgrades |
REGISTRY_ROLE | keccak256("REGISTRY_ROLE") | Verified registry wallets | submitCertificate, approveCertificate |
ROLE_MANAGER | keccak256("ROLE_MANAGER") | Admin | Grant/revoke REGISTRY_ROLE |
REGISTRY_ROLEis administered byROLE_MANAGER, not byDEFAULT_ADMIN_ROLE. This lets the admin grant registry access without touching the root role.
Certificate Struct
struct Certificate {
string serialNumber; // Unique registry identifier (e.g. "VCS-2024-001-001")
string projectId; // Project identifier from the registry
uint8 assetType; // Asset type constant (0–9, 255)
string assetMetadata; // IPFS CID of the metadata JSON
uint256 vintage; // Year the environmental benefit was generated
uint256 volume; // Total units (e.g. tCO2e for carbon)
uint256 volumeAvailable; // Units not yet tokenized
string unitOfMeasurement; // "tCO2e", "kg", "acres", "kWh", etc.
address registry; // Submitting wallet address
uint256 submittedAt; // Unix timestamp of submission
uint256 approvedAt; // Unix timestamp of final approval (0 until approved)
bool isApproved; // True once 3-of-5 approvals received
bool isRetired; // True once permanently retired
string externalId; // External DB reference for duplicate prevention
uint8 qualityScore; // 0–100 score assigned by oracle after approval
string ipfsHash; // IPFS CID of supporting documents
}
Asset Type IDs
| ID | Constant | Description |
|---|---|---|
| 0 | CARBON_CREDIT | Carbon offsets (tCO₂e) |
| 1 | PLASTIC_CREDIT | Plastic waste credits |
| 2 | NITROGEN_CREDIT | Nitrogen reduction credits |
| 3 | PHOSPHORUS_CREDIT | Phosphorus reduction credits |
| 4 | AGRICULTURAL_LAND | Agricultural land rights |
| 5 | MINING_RIGHTS | Mining rights |
| 6 | WATER_RIGHTS | Water rights |
| 7 | RENEWABLE_ENERGY | RECs / renewable energy certificates |
| 8 | FORESTRY | Forestry credits |
| 9 | BIODIVERSITY_CREDIT | Biodiversity net gain units |
| 255 | OTHER | Other/custom asset type |
Key Functions
submitCertificate
function submitCertificate(
string memory _serialNumber,
string memory _projectId,
uint8 _assetType,
string memory _assetMetadata, // IPFS CID
uint256 _vintage,
uint256 _volume,
string memory _unitOfMeasurement,
string memory _ipfsHash
) external onlyRole(REGISTRY_ROLE) onlyActiveAccount nonReentrant
Submits a new certificate. Requirements:
- Caller must hold
REGISTRY_ROLEand be active inAccountManager. _serialNumbermust not already exist._volume > 0, vintage between 2000 and current year + 1.- Valid asset type (0–9 or 255).
On success, the certificate is stored with isApproved = false and approvalCount = 0. Emits CertificateSubmitted.
approveCertificate
function approveCertificate(string memory _serialNumber)
external onlyRole(REGISTRY_ROLE) onlyActiveAccount nonReentrant
Casts one approval vote. Any wallet with REGISTRY_ROLE and active status may approve. Each wallet can only vote once per certificate. When approvalCount >= REQUIRED_APPROVALS (3), the certificate is marked isApproved = true and approvedAt is set. Emits CertificateApproved and (if finalized) CertificateFinalized.
Required approvals: 3-of-5 (
REQUIRED_APPROVALS = 3)
setQualityScore
function setQualityScore(string memory _serialNumber, uint8 _qualityScore)
external onlyRole(ADMIN_ROLE)
Called by the oracle after running quality assessment. Certificate must be approved. Score must be 0–100. Emits QualityScoreUpdated.
allocateVolume
function allocateVolume(string memory _serialNumber, uint256 _volume)
external onlyRole(ADMIN_ROLE) nonReentrant returns (uint256)
Deducts _volume from volumeAvailable. Called by the oracle when a mint proposal is executed, to track how many units have been tokenized. Reverts if insufficient volume remains. Emits VolumeAllocated.
retireCertificate
function retireCertificate(string memory _serialNumber)
external onlyRole(ADMIN_ROLE)
Permanently marks a certificate as retired (isRetired = true). Retired certificates are excluded from the registry explorer and cannot have further volume allocated. Emits CertificateRetired.
getCertificate
function getCertificate(string memory _serialNumber)
external view returns (Certificate memory)
Returns the full Certificate struct for a serial number. Reverts if not found.
getCertificatesByRegistry
function getCertificatesByRegistry(address _registry)
external view returns (string[] memory)
Returns all serial numbers submitted by a registry address. Note: includes retired and unapproved certificates — filter on isRetired / isApproved when consuming.
getCertificatesByAssetType
function getCertificatesByAssetType(uint8 _assetType)
external view returns (string[] memory)
Returns all serial numbers for a given asset type ID.
updateAssetMetadata / updateIPFSHash
function updateAssetMetadata(string memory _serialNumber, string memory _assetMetadata) external nonReentrant
function updateIPFSHash(string memory _serialNumber, string memory _ipfsHash) external nonReentrant
Can be called by the submitting registry wallet or ADMIN_ROLE. Used to correct metadata or supporting documents after submission.
Events
| Event | When emitted |
|---|---|
CertificateSubmitted(serialNumber, projectId, assetType, registry, volume, vintage) | submitCertificate succeeds |
CertificateApproved(serialNumber, approver, currentApprovals) | Each approval vote |
CertificateFinalized(serialNumber, approvedAt) | 3rd approval received |
CertificateRetired(serialNumber, volume) | retireCertificate |
VolumeAllocated(serialNumber, volume, volumeRemaining) | allocateVolume |
QualityScoreUpdated(serialNumber, qualityScore) | setQualityScore |
AssetMetadataUpdated(serialNumber, assetMetadata) | updateAssetMetadata |
IPFSHashSet(serialNumber, ipfsHash) | updateIPFSHash |
AccountManagerSet(accountManager) | setAccountManager |
AccountManager integration
When an AccountManager address is set, every call to submitCertificate and approveCertificate checks that the caller is ACTIVE and not SUSPENDED. When accountManager == address(0) (open mode), the check is skipped — used during testnet setup before accounts are registered.
Certificate Approval Flow
Registry wallet AssetRegistry Oracle service
│ │ │
│── submitCertificate() ──────>│ │
│ │ CertificateSubmitted │
│ │ │
│── approveCertificate() ─────>│ (repeat ×3) │
│ │ CertificateFinalized │
│ │ │
│ │<── assessQuality() ─────│ (off-chain → QualityAssessment)
│ │<── setQualityScore() ───│
│ │ │
│ │<── createProposal() ────│ (MultiSigGovernance)
│ │<── allocateVolume() ────│ (after proposal execution)
UUPS Upgrade
_authorizeUpgrade requires ADMIN_ROLE. All upgrades must be performed through the UUPS proxy pattern via upgradeToAndCall.