Chapter 7 of 9
The program that brings the price every morning
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.
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();
medianis the function from chapter 2.abiis the list of functions the program wants to call, written as text.provideris the doorway to the network (the RPC address);walletis the updater, with its private key taken from an environment variable, never written in the code.oraclecombines the contract address, the function list and the signing wallet.scaleturns 1.989 into 1989000, the six-decimal format.todayis today's date at midnight UTC in seconds, the data'ssourceDate.setPricessends the transaction;tx.wait()waits for it to enter a block.
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
- A contract does not act by itself: an outside program brings it the data at set times.
- The updater key signs the update and is never written in the code.
- If the source is missing, the price becomes "old" and operations stop on their own.