Capitolo 4 di 9

Il contratto del prezzo

Obiettivo. Scrivere un oracolo minimo: un numero, una data e una sola persona autorizzata ad aggiornarlo.

Il nostro oracolo è un contratto che conserva tre cose: il prezzo della benzina, il prezzo del POL e il momento dell'ultimo aggiornamento. Chiunque può leggere; può scrivere solo chi ha un ruolo preciso.

Analogia. È una lavagna in piazza: tutti la vedono, ma il gesso ce l'ha una persona sola. Accanto al prezzo c'è sempre scritta la data.

Due regole sui numeri. Primo: la blockchain non conosce i decimali, quindi i prezzi sono interi moltiplicati per un milione: 1,989 € diventa 1989000. Secondo: il momento viene dalla blockchain stessa (block.timestamp), non lo sceglie chi scrive.

contract FuelPriceOracle is AccessControl {
    bytes32 public constant UPDATER_ROLE = keccak256("UPDATER_ROLE");

    uint256 private _fuelEurPerLitre;
    uint256 private _polEur;
    uint64 private _updatedAt;
    uint64 private _sourceDate;

    constructor(address admin) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
    }

    function setPrices(uint256 fuelEurPerLitre, uint256 polEur, uint64 sourceDate)
        external onlyRole(UPDATER_ROLE)
    {
        require(fuelEurPerLitre > 0 && polEur > 0, "zero price");
        _fuelEurPerLitre = fuelEurPerLitre;
        _polEur = polEur;
        _sourceDate = sourceDate;
        _updatedAt = uint64(block.timestamp);
    }

    function fuelPrice() external view returns (uint256, uint64) {
        return (_fuelEurPerLitre, _updatedAt);
    }
}

Il progetto completo aggiunge la funzione per il prezzo del POL e il controllo di freschezza, che il token userà nei prossimi capitoli, più un evento (PricesUpdated) che registra ogni aggiornamento:

function polPrice() external view returns (uint256, uint64) {
    return (_polEur, _updatedAt);
}

function isFresh(uint64 maxAgeSeconds) external view returns (bool) {
    return _updatedAt != 0
        && block.timestamp <= uint256(_updatedAt) + maxAgeSeconds;
}

Prova tu

Apri il sito Remix (remix.ethereum.org), incolla il contratto con le righe import di OpenZeppelin e la riga pragma solidity ^0.8.24; in testa, compilalo e pubblicalo nell'ambiente di prova di Remix. Poi prova a chiamare setPrices senza avere il ruolo e osserva il rifiuto.

Cosa hai imparato

Per approfondire