0
# Panic Codes
1
2
Constants and utilities for testing Solidity panic conditions and arithmetic errors that occur during smart contract execution.
3
4
## Import
5
6
```typescript
7
import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic";
8
```
9
10
## Capabilities
11
12
### Panic Code Constants
13
14
A comprehensive object containing all standard Solidity panic codes for testing different failure scenarios.
15
16
```typescript { .api }
17
const PANIC_CODES: {
18
ASSERTION_ERROR: 0x1;
19
ARITHMETIC_OVERFLOW: 0x11;
20
DIVISION_BY_ZERO: 0x12;
21
ENUM_CONVERSION_OUT_OF_BOUNDS: 0x21;
22
INCORRECTLY_ENCODED_STORAGE_BYTE_ARRAY: 0x22;
23
POP_ON_EMPTY_ARRAY: 0x31;
24
ARRAY_ACCESS_OUT_OF_BOUNDS: 0x32;
25
TOO_MUCH_MEMORY_ALLOCATED: 0x41;
26
ZERO_INITIALIZED_VARIABLE: 0x51;
27
};
28
```
29
30
## Panic Code Reference
31
32
### Assertion Error (0x1)
33
34
Triggered by failed `assert()` statements in Solidity code.
35
36
```typescript
37
import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic";
38
39
// Test assertion failures
40
await expect(contract.assertPositive(-5))
41
.to.be.revertedWithPanic(PANIC_CODES.ASSERTION_ERROR);
42
43
// Example Solidity code that triggers this:
44
// function assertPositive(int x) public pure {
45
// assert(x >= 0); // Fails for negative x
46
// }
47
```
48
49
### Arithmetic Overflow (0x11)
50
51
Triggered by arithmetic operations that overflow outside of `unchecked` blocks.
52
53
```typescript
54
// Test arithmetic overflow
55
await expect(contract.addUint256(type(uint256).max, 1))
56
.to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_OVERFLOW);
57
58
// Test underflow
59
await expect(contract.subtractUint256(0, 1))
60
.to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_OVERFLOW);
61
62
// Example Solidity code:
63
// function addUint256(uint256 a, uint256 b) public pure returns (uint256) {
64
// return a + b; // Overflows if a + b > type(uint256).max
65
// }
66
```
67
68
### Division by Zero (0x12)
69
70
Triggered by division or modulo operations with zero divisor.
71
72
```typescript
73
// Test division by zero
74
await expect(contract.divide(100, 0))
75
.to.be.revertedWithPanic(PANIC_CODES.DIVISION_BY_ZERO);
76
77
// Test modulo by zero
78
await expect(contract.modulo(100, 0))
79
.to.be.revertedWithPanic(PANIC_CODES.DIVISION_BY_ZERO);
80
81
// Example Solidity code:
82
// function divide(uint256 a, uint256 b) public pure returns (uint256) {
83
// return a / b; // Panics if b == 0
84
// }
85
```
86
87
### Enum Conversion Out of Bounds (0x21)
88
89
Triggered when converting a value to an enum type that doesn't have a corresponding member.
90
91
```typescript
92
// Test invalid enum conversion
93
await expect(contract.setStatus(99)) // Status enum only has values 0-2
94
.to.be.revertedWithPanic(PANIC_CODES.ENUM_CONVERSION_OUT_OF_BOUNDS);
95
96
// Example Solidity code:
97
// enum Status { Pending, Active, Inactive }
98
// function setStatus(uint8 value) public {
99
// status = Status(value); // Panics for value > 2
100
// }
101
```
102
103
### Incorrectly Encoded Storage Byte Array (0x22)
104
105
Triggered when accessing a storage byte array that has been corrupted or incorrectly encoded.
106
107
```typescript
108
// Test corrupted storage access (rare in normal testing)
109
await expect(contract.accessCorruptedStorage())
110
.to.be.revertedWithPanic(PANIC_CODES.INCORRECTLY_ENCODED_STORAGE_BYTE_ARRAY);
111
112
// This is typically caused by low-level storage manipulation
113
```
114
115
### Pop on Empty Array (0x31)
116
117
Triggered when calling `.pop()` on an empty dynamic array.
118
119
```typescript
120
// Test popping from empty array
121
await expect(contract.popFromEmptyArray())
122
.to.be.revertedWithPanic(PANIC_CODES.POP_ON_EMPTY_ARRAY);
123
124
// Example Solidity code:
125
// uint256[] public items;
126
// function popFromEmptyArray() public {
127
// items.pop(); // Panics if items.length == 0
128
// }
129
```
130
131
### Array Access Out of Bounds (0x32)
132
133
Triggered when accessing an array element at an invalid index.
134
135
```typescript
136
// Test out of bounds array access
137
await expect(contract.getItem(99)) // Array only has 5 elements
138
.to.be.revertedWithPanic(PANIC_CODES.ARRAY_ACCESS_OUT_OF_BOUNDS);
139
140
// Test negative index (converted to very large uint)
141
await expect(contract.getItemSigned(-1))
142
.to.be.revertedWithPanic(PANIC_CODES.ARRAY_ACCESS_OUT_OF_BOUNDS);
143
144
// Example Solidity code:
145
// uint256[] public items = [1, 2, 3, 4, 5];
146
// function getItem(uint256 index) public view returns (uint256) {
147
// return items[index]; // Panics if index >= items.length
148
// }
149
```
150
151
### Too Much Memory Allocated (0x41)
152
153
Triggered when trying to allocate too much memory or create arrays that are too large.
154
155
```typescript
156
// Test excessive memory allocation
157
await expect(contract.createHugeArray())
158
.to.be.revertedWithPanic(PANIC_CODES.TOO_MUCH_MEMORY_ALLOCATED);
159
160
// Example Solidity code:
161
// function createHugeArray() public pure {
162
// uint256[] memory huge = new uint256[](2**200); // Exceeds memory limits
163
// }
164
```
165
166
### Zero Initialized Variable (0x51)
167
168
Triggered when calling a zero-initialized variable of internal function type.
169
170
```typescript
171
// Test calling uninitialized internal function
172
await expect(contract.callUninitializedFunction())
173
.to.be.revertedWithPanic(PANIC_CODES.ZERO_INITIALIZED_VARIABLE);
174
175
// Example Solidity code:
176
// function() internal callback;
177
// function callUninitializedFunction() public {
178
// callback(); // Panics because callback is not initialized
179
// }
180
```
181
182
## Usage Patterns
183
184
### Generic Panic Testing
185
186
```typescript
187
// Test any panic condition without specifying which one
188
await expect(contract.riskyOperation())
189
.to.be.revertedWithPanic(); // Any panic code accepted
190
191
// Test specific panic code
192
await expect(contract.divideNumbers(a, 0))
193
.to.be.revertedWithPanic(PANIC_CODES.DIVISION_BY_ZERO);
194
```
195
196
### Complex Arithmetic Testing
197
198
```typescript
199
import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic";
200
201
describe("SafeMath operations", function() {
202
it("should handle overflow correctly", async function() {
203
const maxUint256 = ethers.MaxUint256;
204
205
// Test addition overflow
206
await expect(contract.safeAdd(maxUint256, 1))
207
.to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_OVERFLOW);
208
209
// Test multiplication overflow
210
await expect(contract.safeMultiply(maxUint256, 2))
211
.to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_OVERFLOW);
212
213
// Test subtraction underflow
214
await expect(contract.safeSubtract(0, 1))
215
.to.be.revertedWithPanic(PANIC_CODES.ARITHMETIC_OVERFLOW);
216
});
217
});
218
```
219
220
### Array Bounds Testing
221
222
```typescript
223
describe("Array operations", function() {
224
it("should handle array bounds correctly", async function() {
225
// Setup array with known length
226
await contract.initializeArray([1, 2, 3, 4, 5]);
227
228
// Test valid access
229
expect(await contract.getItem(0)).to.equal(1);
230
expect(await contract.getItem(4)).to.equal(5);
231
232
// Test out of bounds access
233
await expect(contract.getItem(5))
234
.to.be.revertedWithPanic(PANIC_CODES.ARRAY_ACCESS_OUT_OF_BOUNDS);
235
236
await expect(contract.getItem(100))
237
.to.be.revertedWithPanic(PANIC_CODES.ARRAY_ACCESS_OUT_OF_BOUNDS);
238
239
// Test pop on empty after clearing
240
await contract.clearArray();
241
await expect(contract.popItem())
242
.to.be.revertedWithPanic(PANIC_CODES.POP_ON_EMPTY_ARRAY);
243
});
244
});
245
```
246
247
### Enum Validation Testing
248
249
```typescript
250
describe("Enum handling", function() {
251
it("should validate enum conversions", async function() {
252
// Test valid enum values
253
await contract.setStatus(0); // Status.Pending
254
await contract.setStatus(1); // Status.Active
255
await contract.setStatus(2); // Status.Inactive
256
257
// Test invalid enum values
258
await expect(contract.setStatus(3))
259
.to.be.revertedWithPanic(PANIC_CODES.ENUM_CONVERSION_OUT_OF_BOUNDS);
260
261
await expect(contract.setStatus(255))
262
.to.be.revertedWithPanic(PANIC_CODES.ENUM_CONVERSION_OUT_OF_BOUNDS);
263
});
264
});
265
```
266
267
## Advanced Testing Patterns
268
269
### Combining with Other Matchers
270
271
```typescript
272
// Test that operation either succeeds or fails with specific panic
273
const result = contract.riskyDivision(a, b);
274
if (b === 0) {
275
await expect(result).to.be.revertedWithPanic(PANIC_CODES.DIVISION_BY_ZERO);
276
} else {
277
await expect(result).to.not.be.reverted;
278
expect(await result).to.equal(a / b);
279
}
280
```
281
282
### Error Recovery Testing
283
284
```typescript
285
describe("Error recovery", function() {
286
it("should handle and recover from panics", async function() {
287
// Verify panic occurs
288
await expect(contract.dangerousOperation())
289
.to.be.revertedWithPanic(PANIC_CODES.ARRAY_ACCESS_OUT_OF_BOUNDS);
290
291
// Verify contract state remains consistent after panic
292
expect(await contract.isHealthy()).to.be.true;
293
294
// Verify normal operations still work
295
await expect(contract.safeOperation()).to.not.be.reverted;
296
});
297
});
298
```
299
300
### Fuzz Testing with Panics
301
302
```typescript
303
describe("Fuzz testing", function() {
304
it("should handle edge cases gracefully", async function() {
305
const testCases = [
306
{ a: 0, b: 0, expectPanic: PANIC_CODES.DIVISION_BY_ZERO },
307
{ a: ethers.MaxUint256, b: 2, expectPanic: PANIC_CODES.ARITHMETIC_OVERFLOW },
308
{ a: 100, b: 5, expectPanic: null }, // Should succeed
309
];
310
311
for (const { a, b, expectPanic } of testCases) {
312
if (expectPanic) {
313
await expect(contract.calculate(a, b))
314
.to.be.revertedWithPanic(expectPanic);
315
} else {
316
await expect(contract.calculate(a, b)).to.not.be.reverted;
317
}
318
}
319
});
320
});
321
```
322
323
## Integration with Revert Testing
324
325
Panic codes work seamlessly with the revert testing matchers:
326
327
```typescript
328
// These are equivalent ways to test the same condition
329
await expect(contract.divide(10, 0))
330
.to.be.revertedWithPanic(PANIC_CODES.DIVISION_BY_ZERO);
331
332
await expect(contract.divide(10, 0))
333
.to.be.revertedWithPanic(0x12);
334
335
// Can also test generic revert
336
await expect(contract.divide(10, 0)).to.be.reverted;
337
```