Skip to content

Commit 10ee987

Browse files
committed
Integrate Chainlink VRF and update Lottery tests
Added chainlink-brownie-contracts as a submodule and updated remappings. Removed Counter contract and its tests. Refactored Lottery contract imports and events, and replaced old tests with comprehensive Lottery tests using a VRFCoordinatorV2Mock for Chainlink VRF integration.
1 parent e0545af commit 10ee987

File tree

10 files changed

+166
-48
lines changed

10 files changed

+166
-48
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
[submodule "lib/openzeppelin-contracts"]
55
path = lib/openzeppelin-contracts
66
url = https://github.com/OpenZeppelin/openzeppelin-contracts
7+
[submodule "lib/chainlink-brownie-contracts"]
8+
path = lib/chainlink-brownie-contracts
9+
url = https://github.com/smartcontractkit/chainlink-brownie-contracts

foundry.lock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
{
2+
"lib/chainlink-brownie-contracts": {
3+
"tag": {
4+
"name": "0.6.1",
5+
"rev": "c6d0ca512f1b09d93bc81415d246dbee7e5c6894"
6+
}
7+
},
28
"lib/forge-std": {
39
"tag": {
410
"name": "v1.11.0",

foundry.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ src = "src"
33
out = "out"
44
libs = ["lib"]
55
optimizer = true
6-
remappings = [
7-
"@openzeppelin/=lib/openzeppelin-contracts/",
8-
"@chainlink/=lib/chainlink-brownie-contracts/"
9-
]
6+
via_ir = false
107

118
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options

lib/chainlink-brownie-contracts

remappings.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@openzeppelin/=lib/openzeppelin-contracts/contracts/
2+
@chainlink/=lib/chainlink-brownie-contracts/contracts/
3+
forge-std/=lib/forge-std/src/

src/Counter.sol

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/Lottery.sol

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.24;
33

4-
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
54
import "@openzeppelin/contracts/access/Ownable.sol";
65
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
7-
import "@openzeppelin/contracts/utils/Strings.sol";
86

97
import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
108
import "@chainlink/contracts/src/v0.8/vrf/interfaces/VRFCoordinatorV2Interface.sol";
@@ -44,9 +42,9 @@ contract Lottery is Ownable, ReentrancyGuard, VRFConsumerBaseV2{
4442
event RoundStarted(uint256 indexed roundId);
4543
event TicketsBought(uint256 indexed roundId, address indexed player, uint256 amount, uint256 value);
4644
event RoundClosed(uint256 indexed roundId, uint256 requestId);
47-
event WinnerSelected(uint256 indexed roundId, address indexed winner, uint256 prize, uint256 houseFee, uint256 randomWord);
45+
event WinnerSelected(uint256 indexed roundId, address indexed winner, uint256 prize, uint256 creatorFee, uint256 randomWord);
4846
event TicketPriceUpdated(uint256 newPrice);
49-
event HouseFeeUpdated(uint256 newFeeBps);
47+
event HouseFeeUpdated(uint256 newFeeBase);
5048

5149
error RoundNotOpen();
5250
error NoTicketsInRound();
@@ -70,9 +68,7 @@ contract Lottery is Ownable, ReentrancyGuard, VRFConsumerBaseV2{
7068
coordinator = VRFCoordinatorV2Interface(_vrfCoordinator);
7169
keyHash = _keyHash;
7270
subId = _subscriptionId;
73-
}
7471

75-
function startRound() external onlyOwner{
7672
_startNewRound();
7773
}
7874

test/Counter.t.sol

Lines changed: 0 additions & 24 deletions
This file was deleted.

test/Lottery.t.sol

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.24;
3+
4+
import "forge-std/Test.sol";
5+
import "../src/Lottery.sol";
6+
import "./mocks/VRFCoordinatorV2Mock.sol";
7+
8+
contract LotteryTest is Test {
9+
Lottery lottery;
10+
VRFCoordinatorV2Mock vrf;
11+
12+
// test
13+
address owner = address(1);
14+
address playerA = address(2);
15+
address playerB = address(3);
16+
17+
uint64 subId;
18+
19+
function setUp() public {
20+
vm.startPrank(owner);
21+
22+
// 1. create VRF mock
23+
vrf = new VRFCoordinatorV2Mock(
24+
0.1 ether, // base fee
25+
1e9 // gas price link
26+
);
27+
28+
subId = vrf.createSubscription();
29+
30+
// 3. Добавляем деньги на subscription
31+
vrf.fundSubscription(subId, 10 ether);
32+
33+
// 4. Деплоим Lottery
34+
lottery = new Lottery(
35+
0.01 ether, // ticketPrice
36+
500, // 5% fee
37+
address(vrf),
38+
bytes32(0), // mock keyHash, не важно
39+
subId
40+
);
41+
42+
// 5. Указываем контракт как consumer
43+
vrf.addConsumer(subId, address(lottery));
44+
45+
vm.stopPrank();
46+
}
47+
48+
function testBuyTickets() public {
49+
vm.deal(playerA, 10 ether);
50+
51+
vm.prank(playerA);
52+
lottery.buyTickets{value: 0.03 ether}(3);
53+
54+
// Проверим состояние раунда
55+
(uint256 pot, uint256 ticketsSold, , , , , ) = lottery.rounds(
56+
lottery.currentRoundId()
57+
);
58+
assertEq(pot, 0.03 ether);
59+
assertEq(ticketsSold, 3);
60+
61+
// Проверим кто купил билеты
62+
address[] memory tickets = lottery.getRoundTickets(
63+
lottery.currentRoundId()
64+
);
65+
assertEq(tickets.length, 3);
66+
assertEq(tickets[0], playerA);
67+
assertEq(tickets[1], playerA);
68+
}
69+
70+
function testPickWinner() public {
71+
// Player A покупает билет
72+
vm.deal(playerA, 1 ether);
73+
vm.prank(playerA);
74+
lottery.buyTickets{value: 0.01 ether}(1);
75+
76+
// Player B покупает билет
77+
vm.deal(playerB, 1 ether);
78+
vm.prank(playerB);
79+
lottery.buyTickets{value: 0.01 ether}(1);
80+
81+
uint256 potBefore = address(lottery).balance;
82+
83+
// Owner закрывает раунд → запрос VRF
84+
vm.prank(owner);
85+
uint256 requestId = lottery.closeRound();
86+
87+
// ТЕПЕРЬ ДЕЛАЕМ fulfillment
88+
// VRF mock вызывает fulfillRandomWords вручную:
89+
vrf.fulfillRandomWords(requestId, address(lottery));
90+
91+
// Проверим, что раунд завершён
92+
uint256 finishedRound = lottery.currentRoundId() - 1;
93+
(, , address winner, , , ) = lottery.rounds(finishedRound);
94+
95+
assertTrue(
96+
winner == playerA || winner == playerB,
97+
"winner must be A or B"
98+
);
99+
100+
// Проверим что pot = 0
101+
(uint256 pot, , , ) = lottery.rounds(finishedRound);
102+
assertEq(pot, 0);
103+
}
104+
105+
function testPayouts() public {
106+
vm.deal(playerA, 1 ether);
107+
vm.prank(playerA);
108+
lottery.buyTickets{value: 0.03 ether}(3);
109+
110+
vm.prank(owner);
111+
uint256 req = lottery.closeRound();
112+
113+
// balances before
114+
uint256 ownerBalBefore = owner.balance;
115+
uint256 playerABalBefore = playerA.balance;
116+
117+
// fulfill
118+
vrf.fulfillRandomWords(req, address(lottery));
119+
120+
// houseFee = 5% от 0.03 ETH = 0.0015 ETH
121+
uint256 expectedFee = (0.03 ether * 500) / 10000;
122+
123+
assertEq(owner.balance, ownerBalBefore + expectedFee);
124+
125+
// приз = pot - fee
126+
uint256 expectedPrize = 0.03 ether - expectedFee;
127+
128+
// поскольку A единственный игрок — он выиграет
129+
assertEq(playerA.balance, playerABalBefore + expectedPrize);
130+
}
131+
132+
function testNewRoundStartsOpen() public {
133+
vm.deal(playerA, 1 ether);
134+
vm.prank(playerA);
135+
lottery.buyTickets{value: 0.01 ether}(1);
136+
137+
vm.prank(owner);
138+
uint256 req = lottery.closeRound();
139+
vrf.fulfillRandomWords(req, address(lottery));
140+
141+
uint256 newRound = lottery.currentRoundId();
142+
143+
(, , uint256 ticketsSold, , , , ) = lottery.rounds(newRound);
144+
assertEq(ticketsSold, 0);
145+
}
146+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.24;
3+
4+
import "@chainlink/contracts/src/v0.8/VRFCoordinatorV2Mock.sol";

0 commit comments

Comments
 (0)