Skip to main content

EVM Validator Contract

DOBValidator.sol is the on-chain component of the DobValidator platform. It stores certificate hashes and TRUFA scores on EVM chains, providing a permanent, tamper-proof record of asset verification results that anyone can query.

Contract Overview

PropertyValue
ContractDOBValidator.sol
LanguageSolidity
Access ControlOpenZeppelin AccessControl
Key RoleVALIDATOR_ROLE
NetworksSepolia, Polygon Amoy, Base Sepolia, Base Mainnet

Access Control

DOBValidator uses OpenZeppelin's AccessControl for permissioned operations:

bytes32 public constant VALIDATOR_ROLE = keccak256("VALIDATOR_ROLE");
RolePermissions
DEFAULT_ADMIN_ROLEGrant/revoke roles, administrative functions
VALIDATOR_ROLEAdd projects, approve projects with scores

Only addresses with VALIDATOR_ROLE can write certificate data on-chain. This ensures that only authorized DobValidator instances can attest to asset verification.

TrufaScores Struct

The TRUFA scoring data is stored on-chain using a packed struct:

struct TrufaScores {
uint32 technical; // Technical feasibility (0-100)
uint32 regulatory; // Regulatory compliance (0-100)
uint32 financial; // Financial viability (0-100)
uint32 environmental; // Environmental impact (0-100)
uint32 overall; // Composite score (0-100)
}

Each field is a uint32 storing a value from 0 to 100. The struct is tightly packed (5 * 4 bytes = 20 bytes), fitting in a single storage slot for gas efficiency.

Core Functions

addProject

function addProject(bytes32 _hash) external onlyRole(VALIDATOR_ROLE)

Registers a certificate hash on-chain. This is the first step in the two-step attestation process.

Parameters:

  • _hash -- The SHA-256 hash of the canonical submission data (0x-prefixed, 32 bytes)

Effects:

  • Creates an on-chain record for the hash
  • Sets the project state to "registered" (not yet approved)
  • Emits a ProjectAdded event

Reverts if:

  • Caller does not have VALIDATOR_ROLE
  • Hash has already been registered

setProjectApproved

function setProjectApproved(
bytes32 _hash,
TrufaScores calldata _scores
) external onlyRole(VALIDATOR_ROLE)

Approves a previously registered project and writes TRUFA scores on-chain. This is the second step in the attestation process.

Parameters:

  • _hash -- The certificate hash (must have been previously registered with addProject)
  • _scores -- The TrufaScores struct containing all five dimension scores

Effects:

  • Stores the TRUFA scores for the project
  • Marks the project as approved
  • Emits a ProjectApproved event

Reverts if:

  • Caller does not have VALIDATOR_ROLE
  • Hash has not been previously registered
  • Project is already approved

getProject

function getProject(bytes32 _hash) external view returns (
bool exists,
bool approved,
TrufaScores memory scores,
uint256 registeredAt,
uint256 approvedAt
)

Reads the on-chain record for a certificate hash.

Parameters:

  • _hash -- The certificate hash to look up

Returns:

  • exists -- Whether the hash has been registered
  • approved -- Whether the project has been approved with scores
  • scores -- The TRUFA scores (all zeros if not approved)
  • registeredAt -- Block timestamp when addProject was called
  • approvedAt -- Block timestamp when setProjectApproved was called (0 if not approved)

Two-Step Attestation

The attestation process is deliberately split into two steps for operational flexibility:

Step 1: Registration

When a submission enters the UNDER_REVIEW state, the validator registers the hash on-chain:

Validator calls addProject(0xa6352db1...)
--> On-chain record created
--> Project state: registered, not approved

This creates a timestamp proving that the hash was known at a specific block height, even before scoring is complete.

Step 2: Approval

When the submission is approved and TRUFA scores are finalized, the validator writes the scores:

Validator calls setProjectApproved(0xa6352db1..., {
technical: 95,
regulatory: 92,
financial: 85,
environmental: 88,
overall: 90
})
--> Scores stored on-chain
--> Project state: approved

Why Two Steps?

  • Separation of concerns -- Registration proves the hash existed at a point in time. Approval proves the scores.
  • Auditability -- The time gap between registration and approval is visible on-chain, showing how long the review took.
  • Flexibility -- A hash can be registered early in the review process, and scoring can happen asynchronously.

Events

event ProjectAdded(bytes32 indexed hash, uint256 timestamp);
event ProjectApproved(bytes32 indexed hash, TrufaScores scores, uint256 timestamp);

Both events are indexed by hash, making it efficient to query the history of a specific certificate.

Deployment Addresses

NetworkChain IDStatus
Ethereum Sepolia11155111Deployed
Polygon Amoy80002Deployed
Base Sepolia84532Deployed
Base Mainnet8453Deployed

Verification Flow

To verify a certificate on-chain:

  1. Take the certificate hash from the DobValidator platform
  2. Call getProject(hash) on the appropriate network
  3. Check that exists is true and approved is true
  4. Read the scores struct to see the TRUFA breakdown
  5. Compare the on-chain scores with the off-chain certificate data
// Example: Verify a certificate using ethers.js v6
const contract = new ethers.Contract(validatorAddress, abi, provider);

const { exists, approved, scores } = await contract.getProject(certificateHash);

if (exists && approved) {
console.log(`Technical: ${scores.technical}`);
console.log(`Regulatory: ${scores.regulatory}`);
console.log(`Financial: ${scores.financial}`);
console.log(`Environmental: ${scores.environmental}`);
console.log(`Overall: ${scores.overall}`);
} else {
console.log("Certificate not verified on-chain");
}

Integration with Token Studio

When a certificate is linked to a distribution pool on Token Studio, the frontend can cross-reference the on-chain record to display a "verified on-chain" indicator. The pool dashboard shows:

  • The certificate hash as a clickable link to the block explorer
  • The TRUFA scores pulled from the on-chain contract
  • The timestamp of on-chain attestation

This provides an independent verification path that does not rely on the DobValidator backend being available.

Gas Costs

FunctionApproximate Gas
addProject~50,000 gas (new storage slot)
setProjectApproved~60,000 gas (write struct to storage)
getProject0 gas (view function)

The struct packing (5 x uint32 = 20 bytes in one slot) minimizes storage costs. Both write operations fit comfortably within standard gas limits on all supported networks.