Skip to content

Commit b2c4dba

Browse files
devin-ai-integration[bot]Jayant
authored andcommitted
docs(entropy): add how-to guide for testing with MockEntropy
Co-Authored-By: Jayant <jayant@dourolabs.xyz>
1 parent 5ba010c commit b2c4dba

File tree

2 files changed

+328
-0
lines changed

2 files changed

+328
-0
lines changed

pages/entropy/_meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"generate-random-numbers": "Generate Random Numbers",
2828
"set-custom-gas-limits": "Set Custom Gas Limits",
2929
"debug-callback-failures": "Debug Callback Failures",
30+
"test-with-mock-entropy": "Test with MockEntropy",
3031

3132
"-- Reference Material": {
3233
"title": "Reference Material",
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
import { Callout } from "nextra/components";
2+
3+
# Test with MockEntropy
4+
5+
This guide shows how to use the MockEntropy contract for local testing of your Entropy integration. MockEntropy is a lightweight testing implementation that allows you to write fast, deterministic tests without needing network access or paying fees.
6+
7+
## Why Use MockEntropy?
8+
9+
MockEntropy is ideal for local testing because it provides:
10+
11+
- **No Network Dependency**: Test entirely offline without connecting to a blockchain
12+
- **Zero Fees**: All fee methods return 0, no native tokens required
13+
- **Deterministic Random Numbers**: You control exactly what random values are returned
14+
- **Fast Execution**: Callbacks are synchronous, no waiting for keeper transactions
15+
- **Full Control**: Manually trigger reveals in any order to test edge cases
16+
17+
MockEntropy is perfect for unit tests and CI pipelines, but always test with the real Entropy contract on a testnet before deploying to production.
18+
19+
## Prerequisites
20+
21+
Before following this guide, you should:
22+
23+
- Complete the basic setup from the [Generate Random Numbers in EVM Contracts](/entropy/generate-random-numbers/evm) guide
24+
- Have a Foundry-based testing environment set up
25+
- Be familiar with implementing `IEntropyConsumer` and `entropyCallback`
26+
27+
## Installation
28+
29+
MockEntropy is included in the Pyth Entropy Solidity SDK v2.1.0+. Install it using npm:
30+
31+
```bash copy
32+
npm install @pythnetwork/entropy-sdk-solidity
33+
```
34+
35+
For Foundry projects, add this line to your `remappings.txt`:
36+
37+
```text copy
38+
@pythnetwork/entropy-sdk-solidity/=node_modules/@pythnetwork/entropy-sdk-solidity
39+
```
40+
41+
## Basic Usage
42+
43+
### 1. Import MockEntropy in Your Test
44+
45+
```solidity copy
46+
import "forge-std/Test.sol";
47+
import "@pythnetwork/entropy-sdk-solidity/MockEntropy.sol";
48+
import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
49+
```
50+
51+
### 2. Set Up MockEntropy in Your Test
52+
53+
```solidity copy
54+
contract MyEntropyTest is Test {
55+
MockEntropy public entropy;
56+
MyEntropyConsumer public consumer;
57+
address public provider;
58+
59+
function setUp() public {
60+
// Use any address as the provider
61+
provider = address(0x1234);
62+
63+
// Deploy MockEntropy with the provider address
64+
entropy = new MockEntropy(provider);
65+
66+
// Deploy your consumer contract
67+
consumer = new MyEntropyConsumer(address(entropy));
68+
}
69+
}
70+
```
71+
72+
### 3. Request and Reveal Pattern
73+
74+
The basic testing pattern involves requesting a random number, then manually revealing it with `mockReveal()`:
75+
76+
```solidity copy
77+
function testBasicRandomNumber() public {
78+
// Request a random number
79+
uint64 sequenceNumber = consumer.requestRandomNumber();
80+
81+
// Manually reveal with your chosen random value
82+
bytes32 randomNumber = bytes32(uint256(42));
83+
entropy.mockReveal(provider, sequenceNumber, randomNumber);
84+
85+
// Verify the callback was triggered with the correct value
86+
assertEq(consumer.lastRandomNumber(), randomNumber);
87+
assertEq(consumer.lastSequenceNumber(), sequenceNumber);
88+
}
89+
```
90+
91+
The `mockReveal()` method signature is:
92+
93+
```solidity
94+
function mockReveal(
95+
address provider,
96+
uint64 sequenceNumber,
97+
bytes32 randomNumber
98+
) external
99+
```
100+
101+
When called, it immediately triggers the `entropyCallback` on your consumer contract with the provided random number.
102+
103+
## Advanced Testing Patterns
104+
105+
### Pattern 1: Multiple Requests and Out-of-Order Reveals
106+
107+
Test that your contract handles asynchronous callbacks correctly by revealing requests in a different order than they were made:
108+
109+
```solidity copy
110+
function testOutOfOrderReveals() public {
111+
// Request multiple random numbers
112+
uint64 seq1 = consumer.requestRandomNumber();
113+
uint64 seq2 = consumer.requestRandomNumber();
114+
uint64 seq3 = consumer.requestRandomNumber();
115+
116+
// Reveal in different order: 2, 3, then 1
117+
bytes32 random2 = bytes32(uint256(200));
118+
bytes32 random3 = bytes32(uint256(300));
119+
bytes32 random1 = bytes32(uint256(100));
120+
121+
entropy.mockReveal(provider, seq2, random2);
122+
assertEq(consumer.lastRandomNumber(), random2);
123+
124+
entropy.mockReveal(provider, seq3, random3);
125+
assertEq(consumer.lastRandomNumber(), random3);
126+
127+
entropy.mockReveal(provider, seq1, random1);
128+
assertEq(consumer.lastRandomNumber(), random1);
129+
}
130+
```
131+
132+
This pattern ensures your contract correctly handles callbacks arriving in any order.
133+
134+
### Pattern 2: Testing with Multiple Providers
135+
136+
Test your contract with different providers to ensure provider-specific logic works correctly:
137+
138+
```solidity copy
139+
function testMultipleProviders() public {
140+
address provider1 = address(0x1111);
141+
address provider2 = address(0x2222);
142+
143+
// Request from different providers
144+
uint64 seq1 = consumer.requestFromProvider(provider1, 100000);
145+
uint64 seq2 = consumer.requestFromProvider(provider2, 100000);
146+
147+
// Each provider has independent sequence numbers
148+
assertEq(seq1, 1);
149+
assertEq(seq2, 1);
150+
151+
// Reveal from each provider
152+
bytes32 random1 = bytes32(uint256(111));
153+
bytes32 random2 = bytes32(uint256(222));
154+
155+
entropy.mockReveal(provider1, seq1, random1);
156+
assertEq(consumer.lastRandomNumber(), random1);
157+
assertEq(consumer.lastProvider(), provider1);
158+
159+
entropy.mockReveal(provider2, seq2, random2);
160+
assertEq(consumer.lastRandomNumber(), random2);
161+
assertEq(consumer.lastProvider(), provider2);
162+
}
163+
```
164+
165+
### Pattern 3: Testing Gas Limit Configuration
166+
167+
While MockEntropy doesn't enforce gas limits, you can verify that your contract correctly stores and passes gas limit parameters:
168+
169+
```solidity copy
170+
function testGasLimitStorage() public {
171+
uint32 customGasLimit = 200000;
172+
173+
// Request with custom gas limit
174+
uint64 seq = consumer.requestWithGasLimit(customGasLimit);
175+
176+
// Verify gas limit was stored correctly
177+
EntropyStructsV2.Request memory req = entropy.getRequestV2(provider, seq);
178+
assertEq(req.gasLimit10k, 20); // 200000 / 10000 = 20
179+
180+
// Reveal works normally
181+
bytes32 randomNumber = bytes32(uint256(999));
182+
entropy.mockReveal(provider, seq, randomNumber);
183+
assertEq(consumer.lastRandomNumber(), randomNumber);
184+
}
185+
```
186+
187+
<Callout type="warning" emoji="⚠️">
188+
MockEntropy does not enforce gas limits on callbacks. Make sure to test with the real Entropy contract on a testnet before deploying to production to verify your callback stays within gas limits.
189+
</Callout>
190+
191+
### Pattern 4: Deterministic Testing with Specific Values
192+
193+
Use deterministic random values to create reproducible tests and cover edge cases:
194+
195+
```solidity copy
196+
function testEdgeCaseRandomValues() public {
197+
// Test with zero
198+
uint64 seq1 = consumer.requestRandomNumber();
199+
entropy.mockReveal(provider, seq1, bytes32(0));
200+
assertEq(consumer.lastRandomNumber(), bytes32(0));
201+
202+
// Test with maximum value
203+
uint64 seq2 = consumer.requestRandomNumber();
204+
entropy.mockReveal(provider, seq2, bytes32(type(uint256).max));
205+
assertEq(consumer.lastRandomNumber(), bytes32(type(uint256).max));
206+
207+
// Test with hash-based values
208+
uint64 seq3 = consumer.requestRandomNumber();
209+
bytes32 hashValue = keccak256("deterministic test value");
210+
entropy.mockReveal(provider, seq3, hashValue);
211+
assertEq(consumer.lastRandomNumber(), hashValue);
212+
}
213+
```
214+
215+
This approach ensures your contract handles all possible random values correctly.
216+
217+
## Key Differences from Real Entropy
218+
219+
MockEntropy simplifies testing but has important differences from the production Entropy contract:
220+
221+
### 1. No Fees Required
222+
223+
All `getFeeV2()` methods return 0:
224+
225+
```solidity
226+
// MockEntropy always returns 0
227+
uint128 fee = entropy.getFeeV2(); // Returns 0
228+
uint128 feeWithGas = entropy.getFeeV2(100000); // Returns 0
229+
```
230+
231+
You don't need to send native tokens with your requests during testing.
232+
233+
### 2. No Gas Limit Enforcement
234+
235+
MockEntropy stores gas limits but does **not** enforce them on callbacks. Your callback can use any amount of gas during testing.
236+
237+
<Callout type="warning" emoji="⚠️">
238+
Always test on a testnet with the real Entropy contract to verify your callback respects gas limits. A callback that works in MockEntropy tests may fail in production if it exceeds the gas limit.
239+
</Callout>
240+
241+
### 3. Synchronous Callbacks
242+
243+
`mockReveal()` immediately calls `entropyCallback` in the same transaction. The real Entropy contract uses keeper transactions that arrive asynchronously.
244+
245+
### 4. Manual Random Number Control
246+
247+
You decide exactly what random number is revealed and when. The real Entropy contract generates cryptographically secure random numbers.
248+
249+
### 5. Simplified Provider Setup
250+
251+
Providers in MockEntropy are just addresses with default configuration:
252+
- Default fee: 1 wei (not enforced)
253+
- Default gas limit: 100,000
254+
- Sequence numbers start at 1
255+
256+
No keeper infrastructure is required.
257+
258+
## Best Practices
259+
260+
### 1. Test Edge Cases
261+
262+
Use extreme random values to ensure your contract handles all cases:
263+
264+
```solidity copy
265+
// Test boundary values
266+
bytes32[] memory testValues = new bytes32[](3);
267+
testValues[0] = bytes32(uint256(0));
268+
testValues[1] = bytes32(type(uint256).max);
269+
testValues[2] = bytes32(uint256(1));
270+
271+
for (uint i = 0; i < testValues.length; i++) {
272+
uint64 seq = consumer.requestRandomNumber();
273+
entropy.mockReveal(provider, seq, testValues[i]);
274+
// Verify your contract handles the value correctly
275+
}
276+
```
277+
278+
### 2. Measure Callback Gas Usage
279+
280+
Even though MockEntropy doesn't enforce limits, measure your callback's gas usage:
281+
282+
```solidity copy
283+
function testCallbackGasUsage() public {
284+
uint64 seq = consumer.requestRandomNumber();
285+
286+
uint256 gasBefore = gasleft();
287+
entropy.mockReveal(provider, seq, bytes32(uint256(42)));
288+
uint256 gasUsed = gasBefore - gasleft();
289+
290+
console.log("Callback gas used:", gasUsed);
291+
292+
// Ensure it's within your target gas limit
293+
assertTrue(gasUsed < 100000, "Callback uses too much gas");
294+
}
295+
```
296+
297+
### 3. Always Test on Testnet
298+
299+
MockEntropy is perfect for development, but always run integration tests on a testnet before deploying to production. This verifies:
300+
301+
- Fee calculations work correctly
302+
- Callback gas usage is within limits
303+
- Keeper transactions are processed successfully
304+
- Your contract integrates properly with the real Entropy infrastructure
305+
306+
See the [Contract Addresses](/entropy/contract-addresses) page for testnet deployment addresses.
307+
308+
### 4. Use Deterministic Values for CI
309+
310+
For reproducible CI tests, use deterministic random values instead of actual randomness:
311+
312+
```solidity copy
313+
// Good for CI: deterministic and reproducible
314+
bytes32 testRandom = keccak256(abi.encodePacked("test-seed-", testName));
315+
316+
// Avoid in CI: non-deterministic
317+
bytes32 actualRandom = keccak256(abi.encodePacked(block.timestamp, msg.sender));
318+
```
319+
320+
## Additional Resources
321+
322+
- [Generate Random Numbers in EVM Contracts](/entropy/generate-random-numbers/evm) - Full integration guide
323+
- [Set Custom Gas Limits](/entropy/set-custom-gas-limits) - Gas limit configuration for production
324+
- [Debug Callback Failures](/entropy/debug-callback-failures) - Troubleshooting callback issues
325+
- [Contract Addresses](/entropy/contract-addresses) - Testnet and mainnet Entropy contracts
326+
- [MockEntropy.sol](https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/ethereum/entropy_sdk/solidity/MockEntropy.sol) - Source code
327+
- [MockEntropy.t.sol](https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/ethereum/contracts/test/MockEntropy.t.sol) - Complete test examples

0 commit comments

Comments
 (0)