PD-53 — Rétrospective¶
1. Contexte¶
| Champ | Valeur |
|---|---|
| Story ID | PD-53 |
| Titre | Smart contract ancrage Merkle roots |
| Domaine | blockchain |
| Projet | infra |
| Date complétion | 2026-02-16 |
| Verdict | ACCEPTÉ (GO 9.19/10) |
2. Métriques¶
| Métrique | Valeur |
|---|---|
| Durée totale | ~6h |
| Lignes de code | ~104 (contrat) + ~229 (tests) |
| Tests | 18/18 PASS |
| Gas anchor() | ~27,000 (< 50k spec) |
| Gate iterations | 3× GO (8.6, 8.9, 9.2) |
3. Learnings clés¶
-
Storage packing Solidity : Le packing des structs est crucial pour le gas. 1 slot = 32 bytes. Toujours vérifier avec
forge inspect <contract> storage-layout. -
Gas testing Foundry : La mesure via
gasleft()inclut l'overhead du test harness. Pour un gas précis, utiliserforge test -vvvet lire les traces. -
CA de déploiement vs CA de code : Distinguer les critères d'acceptation testables (comportement du code) des critères de déploiement (vérification post-deploy).
-
Validation croisée efficace : La règle "Claude produit, ChatGPT review" a détecté le storage packing incorrect et réduit le gas de 54%.
4. Patterns applicables¶
Nouveau pattern : Struct Solidity optimisé (1 slot)¶
// OPTIMISÉ: 32 bytes = 1 slot
struct AnchorRecord {
address submitter; // 20 bytes
uint64 timestamp; // 8 bytes
uint32 blockNumber; // 4 bytes
} // Total: 32 bytes
// NON OPTIMISÉ: 36 bytes = 2 slots
struct AnchorRecordBad {
address submitter; // 20 bytes
uint64 timestamp; // 8 bytes
uint64 blockNumber; // 8 bytes ← trop grand
} // Total: 36 bytes
Pattern confirmé : Contrat append-only minimal¶
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
contract MerkleAnchor is Ownable {
event RootAnchored(bytes32 indexed root, address submitter, uint64 timestamp);
mapping(bytes32 => AnchorRecord) public anchors;
// Append-only: pas de suppression possible
function anchor(bytes32 root) external onlyOwner {
require(anchors[root].timestamp == 0, "Already anchored");
anchors[root] = AnchorRecord({
submitter: msg.sender,
timestamp: uint64(block.timestamp),
blockNumber: uint32(block.number)
});
emit RootAnchored(root, msg.sender, uint64(block.timestamp));
}
function isAnchored(bytes32 root) external view returns (bool) {
return anchors[root].timestamp != 0;
}
}
5. Signal CLAUDE.md¶
Priorité haute : Vérifier storage packing Solidity.
### Solidity — Storage packing (2026-02-XX)
**RÈGLE** : Toujours vérifier le packing des structs avec :
```bash
forge inspect <Contract> storage-layout
Optimisation : 1 slot = 32 bytes. Combiner les types pour atteindre exactement 32 bytes.
| Type | Taille |
|---|---|
| address | 20 bytes |
| uint256 | 32 bytes |
| uint128 | 16 bytes |
| uint64 | 8 bytes |
| uint32 | 4 bytes |
| bool | 1 byte |
Exemple PD-53 : uint64 blockNumber (8 bytes) → uint32 blockNumber (4 bytes) = gas réduit de 54%. ```
6. Conclusion¶
PD-53 a livré le smart contract Merkle anchor avec architecture minimaliste (append-only, non-upgradable), héritage OpenZeppelin Ownable v5, et gas optimisé (27k < 50k spec). Le storage packing corrigé grâce à la review ChatGPT a réduit le gas de 54%. Le contrat est prêt pour déploiement sur Polygon Amoy et Arbitrum Sepolia.
Rétrospective générée 2026-02-19 (Étape 10 batch blockchain)