Olive oil traceability, step by step

8. The QR code and the public page

Goal: Link the bottle's QR code to a read-only function that answers: does it exist? is it recalled?

The QR is like a booking reference: alone it says nothing, but at the right counter it shows your file.

The address

Each bottle has a web address shaped like /b/<lot>/<n>, for example /b/25B001/12. The QR is just that address drawn as small squares. Any QR library makes it, one line per bottle, starting from the site's public address.

The verification function

A view function reads data and costs the caller nothing: no wallet, no signature.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract VerificaMinima {
    mapping(bytes32 => uint256) private _idPerCodice; // keccak256(codice) => id, 0 = sconosciuto
    mapping(uint256 => uint32) private _nBottiglie;
    mapping(uint256 => bool) private _richiamato;

    function verificaBottiglia(string calldata codice, uint32 n)
        external
        view
        returns (bool esiste, bool richiamato, uint256 id)
    {
        id = _idPerCodice[keccak256(bytes(codice))];
        if (id == 0) return (false, false, 0);
        esiste = n >= 1 && n <= _nBottiglie[id];
        richiamato = _richiamato[id];
    }
}

Line by line

  1. Three helper lists: from the code (turned into a number with keccak256) to the lot number, then bottles and recall for each lot. In the real contract creaLotto, imbottiglia and richiama fill them.
  2. view means "read-only"; it returns three named values.
  3. If the code is unknown the id is 0 and we answer "does not exist".
  4. esiste is true if n is between 1 and the lot's bottle count: this is the third check of chapter 4.
  5. richiamato drives the red badge.

Calling it from the page

import { Contract, JsonRpcProvider } from "ethers";

const abi = ["function verificaBottiglia(string codice, uint32 n) view returns (bool esiste, bool richiamato, uint256 id)"];
const contratto = new Contract(INDIRIZZO, abi, new JsonRpcProvider(RPC_URL));

// dall'indirizzo della pagina: /b/25B001/12
const [, , codice, n] = window.location.pathname.split("/");
const [esiste, richiamato] = await contratto.verificaBottiglia(codice, Number(n));

Line by line

  1. We import ethers, a library that talks to the chain.
  2. The abi is the list of functions we want to call, in readable form.
  3. Contract joins the contract address, the abi and a connection to the network (INDIRIZZO and RPC_URL are your own values).
  4. From the browser address we split the lot code and the bottle number.
  5. We call verificaBottiglia and receive the answers to display.

In the demo this step is not even needed in the browser: the backend answers /api/pubblico/bottiglia/<lot>/<n> with the verification already done, and the page shows the badge.

Try it yourself

With the full contract in your Hardhat project, write a test that creates a 5-bottle lot and checks that verificaBottiglia("25B001", 5) gives esiste = true and verificaBottiglia("25B001", 6) gives false.

What you learned

To go deeper