Référence de BlockLucky
Cette page liste chaque fonction, événement et erreur de BlockLucky. Les tableaux sont générés à la construction de la documentation à partir de l'ABI embarquée par le backend, backend/src/blockchain/abi/BlockLucky.json, et du source Solidity pour les modificateurs. Ils ne peuvent donc pas diverger du contrat compilé : la CI vérifie que l'ABI est à jour avec node scripts/export-abi.mjs --check.
Pour le fonctionnement d'ensemble, lire d'abord Contrat BlockLucky et Cycle de vie d'un tour.
Lire les tableaux
- Fonctions du contrat : déclarées dans
BlockLucky.sol, y compris les variables publiques, pour lesquelles Solidity génère un accesseur. La colonne « Modificateurs » reprend la signature Solidity (payable,whenNotPaused,nonReentrant,onlyRole(...),view). - Fonctions héritées : apportées par OpenZeppelin (
AccessControl,Pausable) ou Chainlink (VRFConsumerBaseV2Plus,ConfirmedOwner). - Erreurs : le contrat n'utilise que des erreurs personnalisées, moins coûteuses en gaz qu'une chaîne de caractères et décodables avec leurs paramètres.
- Les structures
RoundConfig,VrfConfigetRoundapparaissent dans l'ABI sous le typetuple. Leurs champs sont détaillés dans Contrat BlockLucky.
Appeler le contrat avec ethers
Les exemples utilisent ethers v6, la version du backend et des scripts Hardhat. Ils s'exécutent par exemple dans un script Node en module ES, à la racine du dépôt.
Connexion
En local, les adresses sont déterministes (voir Blockchain locale). Sur Sepolia, elles se lisent dans smart-contract/deployments/sepolia.json après le déploiement.
import { readFileSync } from "node:fs";
import { Contract, JsonRpcProvider, Wallet, formatEther, parseEther } from "ethers";
const abi = JSON.parse(readFileSync("backend/src/blockchain/abi/BlockLucky.json", "utf8"));
const provider = new JsonRpcProvider("http://127.0.0.1:8545");
const lottery = new Contract("0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9", abi, provider);Pour envoyer des transactions, il faut un signataire. Sur le nœud local, les comptes Hardhat de développement sont approvisionnés ; la clé se lit dans une variable d'environnement, jamais dans le code.
const signer = new Wallet(process.env.PLAYER_PRIVATE_KEY!, provider);
const asPlayer = lottery.connect(signer) as Contract;Lire l'état d'un tour
const roundId: bigint = await lottery.currentRoundId();
const round = await lottery.getRound(roundId);
console.log({
status: Number(round.status),
ticketPrice: formatEther(round.ticketPrice),
sold: Number(round.ticketCount),
max: Number(round.maxTickets),
endsAt: new Date(Number(round.endTime) * 1000),
drawReady: await lottery.isDrawReady(),
});round.status vaut 0 (Open), 1 (Drawing), 2 (Completed) ou 3 (Cancelled). Les entiers sont renvoyés en bigint.
Acheter et offrir des tickets
Le montant doit être exactement ticketPrice * quantity. Le plus sûr est de le calculer à partir du prix lu sur la chaîne, juste avant l'envoi.
const { ticketPrice } = await lottery.getRound(await lottery.currentRoundId());
const quantity = 3n;
const tx = await asPlayer.buyTickets(quantity, { value: ticketPrice * quantity });
const receipt = await tx.wait();
const gift = await asPlayer.buyTicketsFor("0x70997970C51812dc3A010C7d01b50e0d17dc79C8", 2n, {
value: ticketPrice * 2n,
});
await gift.wait();C'est ce que fait la passerelle du contrat du backend, avec le portefeuille intégré du citoyen comme signataire.
Lire les événements d'une transaction
for (const log of receipt!.logs) {
const parsed = lottery.interface.parseLog(log);
if (parsed?.name === "TicketsPurchased") {
const { roundId, player, payer, quantity, firstTicketIndex, totalTickets } = parsed.args;
console.log(roundId, player, payer, quantity, firstTicketIndex, totalTickets);
}
}Décoder une erreur
Un revert renvoie le sélecteur de l'erreur suivi de ses paramètres. interface.parseError le décode.
try {
await asPlayer.buyTickets(1n, { value: parseEther("0.5") });
} catch (error) {
const data = (error as { data?: string }).data;
const decoded = data ? lottery.interface.parseError(data) : null;
console.log(decoded?.name, decoded?.args);
}Ici, decoded.name vaut IncorrectPayment et decoded.args contient le montant attendu puis le montant reçu. Le backend applique la même logique dans decodeContractError de backend/src/blockchain/chain-actions.ts, puis traduit chaque erreur en message HTTP dans backend/src/blockchain/lottery.gateway.ts.
Suivre les événements en continu
await lottery.on("WinnerPicked", (roundId, winner, winningTicket, prize, fee) => {
console.log(`Tour ${roundId} : ticket ${winningTicket} gagné par ${winner}, ${formatEther(prize)} ETH`);
});Pour une projection fiable en base, préférer une lecture par blocs avec curseur persistant, comme l'indexeur : un abonnement on ne rattrape pas les événements manqués pendant une coupure.
Retirer une cagnotte ou un remboursement
const winnerTx = await asPlayer.claimPrize(1n);
await winnerTx.wait();
const refundTx = await asPlayer.claimRefund(2n);
await refundTx.wait();Simuler avant d'envoyer
staticCall exécute la fonction sans transaction et renvoie l'erreur éventuelle, sans frais. Il est utile avant un requestDraw ou un retryDraw pour savoir si la condition est remplie.
const requestId: bigint = await lottery.requestDraw.staticCall();Depuis la console Hardhat
Les opérations ponctuelles d'exploitation se font aussi dans la console Hardhat, qui fournit ethers et les comptes configurés du réseau :
npx hardhat console --network localhostconst { ethers } = await network.create();
const lottery = await ethers.getContractAt("BlockLucky", "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9");
await lottery.currentRoundId();Voir Exploitation du contrat pour les opérations réservées aux rôles.
Table générée
Source : smart-contract/contracts/BlockLucky.sol. ABI : backend/src/blockchain/abi/BlockLucky.json, vérifiée en CI par node scripts/export-abi.mjs --check.
Fonctions du contrat
| Fonction | Retour | Mutabilité | Modificateurs | Rôle |
|---|---|---|---|---|
accruedFees() |
uint256 |
variable publique | - | Commissions cumulées des tours tirés et non encore versées à la trésorerie, en wei. Remis à zéro par withdrawFees. |
BPS_DENOMINATOR() |
uint16 |
variable publique | - | Constante 10 000 : base des points de base, 10 000 valent 100 %. Sert au calcul de la commission. |
buyTickets(uint32 quantity) |
- | payable | payable whenNotPaused nonReentrant |
Achète quantity tickets du tour en cours pour l'appelant, qui en est à la fois bénéficiaire et payeur. msg.value doit valoir exactement ticketPrice * quantity. Émet TicketsPurchased. |
buyTicketsFor(address player, uint32 quantity) |
- | payable | payable whenNotPaused nonReentrant |
Offre quantity tickets à player. Le bénéficiaire détient les tickets et peut gagner ; l'appelant paie et sera remboursé en cas d'annulation. Mêmes contrôles que buyTickets, plus le refus de l'adresse nulle. |
cancelRound() |
- | nonpayable | onlyRole(OPERATOR_ROLE) nonReentrant |
Annule le tour en cours, qu'il soit Open ou Drawing, efface la requête VRF éventuelle et ouvre le tour suivant. Les mises deviennent remboursables par claimRefund. Réservé à OPERATOR_ROLE. |
checkUpkeep(bytes) |
bool upkeepNeeded, bytes performData |
view | view |
Interface Chainlink Automation. Renvoie true et abi.encode(uint8(1)) si un tirage est à lancer, true et abi.encode(uint8(2)) si un tour expiré est à annuler, false sinon ou en pause. |
claimPrize(uint256 roundId) |
- | nonpayable | nonReentrant |
Verse au gagnant la cagnotte nette du tour roundId. Exige le statut Completed, l'appel par le gagnant et une cagnotte non encore retirée. Émet PrizeClaimed. Utilisable en pause. |
claimRefund(uint256 roundId) |
- | nonpayable | nonReentrant |
Rembourse à l'appelant l'intégralité des ethers qu'il a versés pour le tour annulé roundId, puis remet sa contribution à zéro. Émet RefundClaimed. Utilisable en pause. |
closeExpiredRound() |
- | nonpayable | nonReentrant |
Annule le tour en cours quand sa durée est écoulée sans atteindre minTicketsToDraw, puis ouvre le suivant. Appelable par tous, y compris en pause. |
contributionOf(uint256 roundId, address player) |
uint256 |
view | view |
Ethers versés par le payeur player pour le tour roundId et non encore remboursés, en wei. |
currentRoundId() |
uint256 |
variable publique | - | Numéro du tour en cours, qui vaut 1 au déploiement. Le tour qu'il désigne est toujours Open ou Drawing. |
getRound(uint256 roundId) |
tuple |
view | view |
Renvoie la structure Round complète du tour roundId. Un tour inexistant renvoie une structure vide, sans revert. |
isDrawReady() |
bool |
view | view |
Vrai si le tour en cours est Open, a au moins minTicketsToDraw tickets, et est complet ou a dépassé endTime. |
isExpiredWithoutDraw() |
bool |
view | view |
Vrai si le tour en cours est Open, a dépassé endTime et compte moins de minTicketsToDraw tickets. |
MAX_FEE_BPS() |
uint16 |
variable publique | - | Constante 2 000 : plafond de la commission de la Mairie, soit 20 %. Toute configuration au-delà est refusée par InvalidConfiguration. |
OPERATOR_ROLE() |
bytes32 |
variable publique | - | Identifiant du rôle opérateur, keccak256("OPERATOR_ROLE"). Autorise retryDraw, cancelRound et withdrawFees. |
pause() |
- | nonpayable | onlyRole(PAUSER_ROLE) |
Active le disjoncteur : bloque buyTickets, buyTicketsFor et requestDraw. Les retraits, l'annulation et le callback VRF restent possibles. Réservé à PAUSER_ROLE. |
PAUSER_ROLE() |
bytes32 |
variable publique | - | Identifiant du rôle disjoncteur, keccak256("PAUSER_ROLE"). Autorise pause et unpause. |
performUpkeep(bytes) |
- | nonpayable | - | Interface Chainlink Automation, appelable par tous. Lance requestDraw si le contrat n'est pas en pause et que le tirage est prêt, sinon closeExpiredRound si le tour est expiré, sinon revert DrawNotReady. Le paramètre est ignoré. |
requestDraw() |
uint256 requestId |
nonpayable | whenNotPaused nonReentrant |
Demande un mot aléatoire au coordinateur VRF pour le tour en cours et le passe à Drawing. Appelable par tous dès que isDrawReady() est vrai. Renvoie l'identifiant de la requête et émet DrawRequested. |
retryDraw() |
uint256 requestId |
nonpayable | onlyRole(OPERATOR_ROLE) nonReentrant |
Relance une requête VRF restée sans réponse : exige le statut Drawing et drawRequestedAt + drawTimeout dépassé. L'ancienne réponse sera ignorée. Réservé à OPERATOR_ROLE. |
roundByRequestId(uint256) |
uint256 |
variable publique | - | Tour associé à une requête VRF en attente. Vaut 0 pour une requête inconnue, déjà servie, relancée ou annulée. |
roundConfig() |
uint256 ticketPrice, uint32 maxTickets, uint32 minTicketsToDraw, uint32 maxTicketsPerPlayer, uint64 duration, uint16 feeBps |
variable publique | - | Paramètres appliqués au prochain tour ouvert : prix, plafonds, minimum, durée et commission. |
setRoundConfig(tuple newConfig) |
- | nonpayable | onlyRole(DEFAULT_ADMIN_ROLE) |
Remplace roundConfig après validation. La nouvelle configuration s'applique au tour suivant, jamais au tour en cours. Émet RoundConfigUpdated. Réservé à DEFAULT_ADMIN_ROLE. |
setTreasury(address newTreasury) |
- | nonpayable | onlyRole(DEFAULT_ADMIN_ROLE) |
Change l'adresse qui reçoit les commissions. Refuse l'adresse nulle. Émet TreasuryUpdated. Réservé à DEFAULT_ADMIN_ROLE. |
setVrfConfig(tuple newConfig) |
- | nonpayable | onlyRole(DEFAULT_ADMIN_ROLE) |
Remplace vrfConfig après validation. S'applique dès la prochaine requête, et drawTimeout s'applique aussi au tirage en attente. Émet VrfConfigUpdated. Réservé à DEFAULT_ADMIN_ROLE. |
supportsInterface(bytes4 interfaceId) |
bool |
view | view override(AccessControl) |
ERC-165 : indique les interfaces prises en charge, dont IAccessControl (0x7965db0b) et ERC-165 lui-même. |
ticketOwner(uint256 roundId, uint256 ticketIndex) |
address |
view | view |
Bénéficiaire du ticket d'index ticketIndex, compté à partir de 0, dans le tour roundId. Revert si l'index dépasse le nombre de tickets vendus. |
ticketsOf(uint256 roundId, address player) |
uint32 |
view | view |
Nombre de tickets détenus par le bénéficiaire player dans le tour roundId. |
treasury() |
address |
variable publique | - | Adresse de trésorerie de la Mairie, destinataire de withdrawFees. |
unpause() |
- | nonpayable | onlyRole(PAUSER_ROLE) |
Désactive le disjoncteur. Réservé à PAUSER_ROLE. |
vrfConfig() |
bytes32 keyHash, uint256 subscriptionId, uint32 callbackGasLimit, uint16 requestConfirmations, bool nativePayment, uint64 drawTimeout |
variable publique | - | Paramètres des requêtes Chainlink VRF : key hash, abonnement, gaz du callback, confirmations, mode de paiement et délai de relance. |
withdrawFees() |
- | nonpayable | onlyRole(OPERATOR_ROLE) nonReentrant |
Verse la totalité de accruedFees à l'adresse treasury et remet le compteur à zéro. Émet FeesWithdrawn. Réservé à OPERATOR_ROLE, utilisable en pause. |
Fonctions héritées
| Fonction | Retour | Mutabilité | Origine | Rôle |
|---|---|---|---|---|
acceptOwnership() |
- | nonpayable | ConfirmedOwner (Chainlink) | Finalise un transfert de propriété ConfirmedOwner demandé par transferOwnership. |
DEFAULT_ADMIN_ROLE() |
bytes32 |
view | AccessControl (OpenZeppelin) | Rôle administrateur (valeur nulle), administrateur de tous les autres rôles. |
getRoleAdmin(bytes32 role) |
bytes32 |
view | AccessControl (OpenZeppelin) | Rôle qui administre le rôle donné ; DEFAULT_ADMIN_ROLE pour les trois rôles du contrat. |
grantRole(bytes32 role, address account) |
- | nonpayable | AccessControl (OpenZeppelin) | Accorde un rôle à un compte. Réservé à l'administrateur du rôle. |
hasRole(bytes32 role, address account) |
bool |
view | AccessControl (OpenZeppelin) | Indique si un compte détient un rôle. |
owner() |
address |
view | ConfirmedOwner (Chainlink) | Propriétaire ConfirmedOwner : le compte qui a déployé le contrat. Il peut changer le coordinateur VRF. |
paused() |
bool |
view | Pausable (OpenZeppelin) | Indique si le disjoncteur est actif. |
rawFulfillRandomWords(uint256 requestId, uint256[] randomWords) |
- | nonpayable | VRFConsumerBaseV2Plus (Chainlink) | Point d'entrée du coordinateur VRF, qui livre l'aléa. Refuse tout autre appelant, puis appelle fulfillRandomWords. |
renounceRole(bytes32 role, address callerConfirmation) |
- | nonpayable | AccessControl (OpenZeppelin) | Permet à un compte de renoncer à son propre rôle. |
revokeRole(bytes32 role, address account) |
- | nonpayable | AccessControl (OpenZeppelin) | Retire un rôle à un compte. Réservé à l'administrateur du rôle. |
s_vrfCoordinator() |
address |
view | VRFConsumerBaseV2Plus (Chainlink) | Adresse du coordinateur Chainlink VRF utilisé pour les requêtes et seul autorisé à livrer l'aléa. |
setCoordinator(address _vrfCoordinator) |
- | nonpayable | VRFConsumerBaseV2Plus (Chainlink) | Remplace le coordinateur VRF. Réservé au propriétaire ou au coordinateur actuel. |
transferOwnership(address to) |
- | nonpayable | ConfirmedOwner (Chainlink) | Propose un nouveau propriétaire ConfirmedOwner, qui doit accepter par acceptOwnership. |
Événements
| Événement | Paramètres | Rôle |
|---|---|---|
CoordinatorSet |
address vrfCoordinator |
hérité (VRFConsumerBaseV2Plus, changement de coordinateur) |
DrawRequested |
uint256 indexed roundId, uint256 indexed requestId, uint32 ticketCount |
Émis par requestDraw et retryDraw quand une requête VRF est envoyée, avec son identifiant et le nombre de tickets du tour. |
FeesWithdrawn |
address indexed treasury, uint256 amount |
Émis par withdrawFees avec l'adresse de trésorerie et le montant versé. |
OwnershipTransferred |
address indexed from, address indexed to |
hérité (ConfirmedOwner, transfert de propriété effectif) |
OwnershipTransferRequested |
address indexed from, address indexed to |
hérité (ConfirmedOwner, transfert de propriété proposé) |
Paused |
address account |
hérité (Pausable, disjoncteur activé) |
PrizeClaimed |
uint256 indexed roundId, address indexed winner, uint256 amount |
Émis par claimPrize quand le gagnant retire sa cagnotte. |
RefundClaimed |
uint256 indexed roundId, address indexed player, uint256 amount |
Émis par claimRefund quand un payeur récupère sa mise d'un tour annulé. |
RoleAdminChanged |
bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole |
hérité (AccessControl, administrateur d'un rôle modifié) |
RoleGranted |
bytes32 indexed role, address indexed account, address indexed sender |
hérité (AccessControl, rôle accordé) |
RoleRevoked |
bytes32 indexed role, address indexed account, address indexed sender |
hérité (AccessControl, rôle retiré) |
RoundCancelled |
uint256 indexed roundId, uint32 ticketCount, uint256 totalCollected |
Émis par closeExpiredRound ou cancelRound, avec le nombre de tickets et le total à rembourser. |
RoundConfigUpdated |
uint256 ticketPrice, uint32 maxTickets, uint64 duration, uint16 feeBps |
Émis par setRoundConfig avec le prix, le plafond de tickets, la durée et la commission du prochain tour. |
RoundOpened |
uint256 indexed roundId, uint256 ticketPrice, uint32 maxTickets, uint64 endTime |
Émis à chaque ouverture de tour : au déploiement, après un tirage et après une annulation. Porte le prix, le plafond et l'heure de fin. |
TicketsPurchased |
uint256 indexed roundId, address indexed player, address indexed payer, uint32 quantity, uint32 firstTicketIndex, uint32 totalTickets, uint256 amount |
Émis à chaque achat ou cadeau. Distingue le bénéficiaire (player) du payeur (payer) et donne l'index du premier ticket acheté, le total vendu et le montant. |
TreasuryUpdated |
address indexed previousTreasury, address indexed newTreasury |
Émis par setTreasury avec l'ancienne et la nouvelle adresse. |
Unpaused |
address account |
hérité (Pausable, disjoncteur désactivé) |
VrfConfigUpdated |
bytes32 keyHash, uint256 subscriptionId, uint32 callbackGasLimit |
Émis par setVrfConfig avec la key hash, l'abonnement et le gaz du callback. |
WinnerPicked |
uint256 indexed roundId, address indexed winner, uint32 winningTicket, uint256 prize, uint256 fee, uint256 indexed requestId |
Émis par le callback VRF : gagnant, index du ticket gagnant, cagnotte nette, commission et identifiant de la requête. |
Erreurs
| Erreur | Paramètres | Déclenchée quand |
|---|---|---|
AccessControlBadConfirmation |
- | héritée (AccessControl, renounceRole appelé pour un autre compte) |
AccessControlUnauthorizedAccount |
address account, bytes32 neededRole |
héritée (AccessControl, l'appelant n'a pas le rôle exigé) |
DrawNotReady |
uint256 roundId |
requestDraw est appelé alors que isDrawReady() est faux ; retryDraw est appelé alors qu'aucun tirage n'est en cours ; performUpkeep n'a rien à faire. |
DrawNotStale |
uint256 roundId |
retryDraw est appelé avant que drawRequestedAt + drawTimeout soit atteint. |
EnforcedPause |
- | héritée (Pausable, appel bloqué par le disjoncteur ou pause déjà active) |
ExpectedPause |
- | héritée (Pausable, unpause alors que le contrat n'est pas en pause) |
IncorrectPayment |
uint256 expected, uint256 received |
Le montant envoyé à buyTickets ou buyTicketsFor diffère de ticketPrice * quantity, en plus ou en moins. Porte le montant attendu et le montant reçu. |
InvalidConfiguration |
- | Une configuration est incohérente au constructeur, dans setRoundConfig ou setVrfConfig : prix, plafond, minimum, plafond par participant ou durée nuls, minimum ou plafond par participant supérieurs à maxTickets, commission au-delà de 20 %, key hash nulle, gaz du callback sous 100 000, confirmations ou délai de relance nuls. |
InvalidQuantity |
- | Un achat porte sur zéro ticket. |
NothingToRefund |
uint256 roundId, address player |
claimRefund est appelé par un compte sans contribution sur ce tour, ou déjà remboursé. |
NothingToWithdraw |
- | withdrawFees est appelé alors que accruedFees vaut zéro. |
NotWinner |
address caller |
claimPrize est appelé par un autre compte que le gagnant du tour. |
OnlyCoordinatorCanFulfill |
address have, address want |
héritée (VRFConsumerBaseV2Plus, rawFulfillRandomWords appelé par un autre compte que le coordinateur) |
OnlyOwnerOrCoordinator |
address have, address owner, address coordinator |
héritée (VRFConsumerBaseV2Plus, setCoordinator appelé par un autre compte que le propriétaire ou le coordinateur) |
PlayerLimitReached |
uint32 owned, uint32 limit |
L'achat ferait dépasser au bénéficiaire maxTicketsPerPlayer. Porte le nombre de tickets déjà détenus et le plafond. |
PrizeAlreadyClaimed |
uint256 roundId |
claimPrize est appelé une seconde fois pour le même tour. |
ReentrancyGuardReentrantCall |
- | héritée (ReentrancyGuard, tentative de réentrance dans une fonction nonReentrant) |
RoundExpired |
uint256 roundId |
Un achat arrive alors que endTime est atteint. |
RoundNotCancellable |
uint256 roundId |
closeExpiredRound est appelé alors que isExpiredWithoutDraw() est faux. |
RoundNotCancelled |
uint256 roundId |
claimRefund est appelé sur un tour qui n'est pas Cancelled. |
RoundNotCompleted |
uint256 roundId |
claimPrize est appelé sur un tour qui n'est pas Completed. |
RoundNotOpen |
uint256 roundId |
Un achat arrive alors que le tour en cours est en tirage (Drawing). |
RoundSoldOut |
uint256 roundId, uint32 remaining |
L'achat dépasse le nombre de tickets restants du tour. Porte le nombre restant. |
TransferFailed |
address recipient, uint256 amount |
Le destinataire d'une cagnotte, d'un remboursement ou des commissions refuse les ethers. L'état est restauré et le retrait peut être retenté. |
ZeroAddress |
- | héritée (VRFConsumerBaseV2Plus, coordinateur nul au constructeur ou dans setCoordinator) |
ZeroAddressNotAllowed |
- | L'adresse nulle est passée comme administrateur ou trésorerie au constructeur, comme bénéficiaire à buyTicketsFor, ou comme trésorerie à setTreasury. |