Chapter 7 of 9

The program that brings the price every morning

Goal. Understand how a program outside the blockchain reads the prices and writes them to the oracle every day.

The contract does not wake up by itself: someone must bring it the number every morning. That someone is a small program (the backend) running on an always-on computer.

Analogy. It is the newspaper courier: every morning at 8:30 it collects the prices from the Ministry, checks them and delivers them to the notary (the contract). If it does not collect them, the sheet on the desk stays yesterday's.

The program takes five steps: 1) query the Ministry, 2) compute the median, 3) read the POL price, 4) save everything in a small local archive, 5) call setPrices on the contract, signing with the updater key, the only address with the role from chapter 4. Each day is recorded once, so running it again creates no duplicates.

import { Contract, JsonRpcProvider, Wallet } from "ethers";

function median(values) {
  const s = [...values].sort((a, b) => a - b);
  const m = s.length >> 1;
  return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
}

const abi = ["function setPrices(uint256 fuelEurPerLitre, uint256 polEur, uint64 sourceDate)"];
const provider = new JsonRpcProvider(process.env.RPC_URL);
const wallet = new Wallet(process.env.UPDATER_PRIVATE_KEY, provider);
const oracle = new Contract(process.env.ORACLE_ADDRESS, abi, wallet);

const fuelEur = median(pricesOfStations);   // e.g. 1.989
const polEur = 0.10;                        // from a price service
const scale = (x) => BigInt(Math.round(x * 1e6));
const today = Math.floor(Date.parse(new Date().toISOString().slice(0, 10)) / 1000);

const tx = await oracle.setPrices(scale(fuelEur), scale(polEur), today);
await tx.wait();

In the project the program starts by itself at 8:30 (Rome time) with a timer. If the request fails, it retries three times, ten minutes apart.

If the source is down

If the Ministry is unreachable or changes format, the program writes nothing. The blockchain keeps yesterday's price, with its date. After 3 days isFresh (chapter 4) answers "no" and buy() and sell() stop with an "old price" error. This is deliberate: better to stop than to operate on a number that no longer reflects reality.

Try it

Run only the median function with Node.js on a list of 6 numbers of your choice (an even count) and check by hand that it is the average of the two middle ones.

What you learned