audit-system 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +351 -0
- package/agents/AGENT_REGISTRY.md +150 -0
- package/agents/assumption-analyzer.json +7 -0
- package/agents/assumption-analyzer.md +37 -0
- package/agents/composition-attacker.json +7 -0
- package/agents/composition-attacker.md +46 -0
- package/agents/economic-attacker.json +7 -0
- package/agents/economic-attacker.md +43 -0
- package/agents/exploit-writer.json +7 -0
- package/agents/exploit-writer.md +48 -0
- package/agents/orchestrator.json +16 -0
- package/agents/orchestrator.md +46 -0
- package/agents/report-writer.json +7 -0
- package/agents/report-writer.md +52 -0
- package/agents/state-machine-hacker.json +7 -0
- package/agents/state-machine-hacker.md +43 -0
- package/agents/test-generator.json +7 -0
- package/agents/test-generator.md +49 -0
- package/cli.js +93 -0
- package/config.json +74 -0
- package/lib/detect-lang.js +109 -0
- package/lib/install.js +229 -0
- package/lib/utils.js +41 -0
- package/obsidian-vault/README.md +103 -0
- package/obsidian-vault/attack-patterns/state-inconsistency.md +90 -0
- package/obsidian-vault/exploits/_index.md +109 -0
- package/obsidian-vault/exploits/beanstalk-2022.md +334 -0
- package/obsidian-vault/exploits/nomad-2022.md +295 -0
- package/obsidian-vault/exploits/ronin-2022.md +251 -0
- package/obsidian-vault/exploits/wormhole-2022.md +284 -0
- package/obsidian-vault/failed-hypotheses/_template.md +77 -0
- package/obsidian-vault/hypotheses/_template.md +43 -0
- package/obsidian-vault/hypotheses/bridge-protocol-template.md +254 -0
- package/obsidian-vault/hypotheses/dex-protocol-template.md +185 -0
- package/obsidian-vault/hypotheses/governance-protocol-template.md +263 -0
- package/obsidian-vault/hypotheses/lending-protocol-template.md +218 -0
- package/obsidian-vault/hypotheses/staking-protocol-template.md +223 -0
- package/obsidian-vault/invariant-catalog/defi-invariants.md +307 -0
- package/obsidian-vault/invariant-catalog/solana-invariants.md +213 -0
- package/obsidian-vault/novel-patterns/pattern-mutation-framework.md +316 -0
- package/obsidian-vault/reports/_template.md +92 -0
- package/obsidian-vault/research/cross-protocol-analysis/.gitkeep +0 -0
- package/obsidian-vault/research/emerging-threats/.gitkeep +0 -0
- package/obsidian-vault/research/protocol-specific/.gitkeep +0 -0
- package/obsidian-vault/test-strategies/fuzzing.md +75 -0
- package/obsidian-vault/vulnerabilities/access-control.md +122 -0
- package/obsidian-vault/vulnerabilities/flash-loan-attack.md +66 -0
- package/obsidian-vault/vulnerabilities/oracle-manipulation.md +135 -0
- package/obsidian-vault/vulnerabilities/reentrancy.md +141 -0
- package/obsidian-vault/vulnerabilities/rust-unsafe-deserialization.md +128 -0
- package/obsidian-vault/vulnerabilities/solana-account-confusion.md +125 -0
- package/obsidian-vault/vulnerabilities/solana-close-account.md +141 -0
- package/obsidian-vault/vulnerabilities/solana-cpi-attacks.md +131 -0
- package/obsidian-vault/vulnerabilities/solana-signer-authorization.md +119 -0
- package/package.json +56 -0
- package/skills/audit-connect.md +385 -0
- package/skills/auditor.md +280 -0
- package/skills/exploit-generator.md +394 -0
- package/skills/novel-discovery.md +551 -0
- package/skills/test-generator.md +511 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
# Test Generator Skill
|
|
2
|
+
|
|
3
|
+
## Role
|
|
4
|
+
Smart Contract Test Architect — generates comprehensive test suites covering edge cases, invariants, and fuzzing scenarios that surface hidden vulnerabilities.
|
|
5
|
+
- **Solidity:** Foundry (`forge test`)
|
|
6
|
+
- **Rust/Solana:** Anchor (`anchor test`) + Rust integration tests
|
|
7
|
+
- **Rust/ink!:** `cargo contract test`
|
|
8
|
+
|
|
9
|
+
## Objective
|
|
10
|
+
Transform a contract into a full test suite that automatically discovers vulnerabilities through systematic coverage, fuzzing, and invariant checking.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Test Categories
|
|
15
|
+
|
|
16
|
+
### 1. Unit Tests — Function by Function
|
|
17
|
+
Test each function in isolation with boundary values.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
- Zero values
|
|
21
|
+
- Max uint256
|
|
22
|
+
- Zero address
|
|
23
|
+
- Empty arrays
|
|
24
|
+
- Single element arrays
|
|
25
|
+
- Caller = contract itself
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2. Integration Tests — Function Sequences
|
|
29
|
+
Test realistic interaction sequences.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
- deposit → withdraw
|
|
33
|
+
- approve → transferFrom
|
|
34
|
+
- stake → unstake → claim
|
|
35
|
+
- mint → transfer → burn
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3. Fuzz Tests — Random Input Discovery
|
|
39
|
+
Let Foundry's fuzzer find edge cases you can't imagine.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
- Fuzz all numerical inputs
|
|
43
|
+
- Fuzz addresses
|
|
44
|
+
- Fuzz sequences
|
|
45
|
+
- Fuzz amounts
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 4. Invariant Tests — Properties That Must Always Hold
|
|
49
|
+
Define mathematical truths the contract must never violate.
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
- totalSupply = sum of all balances
|
|
53
|
+
- contract ETH balance >= total deposits
|
|
54
|
+
- no user balance exceeds totalSupply
|
|
55
|
+
- owner cannot be zero address
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 5. Stateful Tests — Sequence of Operations
|
|
59
|
+
Simulate realistic user journeys and look for inconsistencies.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Foundry Test Templates
|
|
64
|
+
|
|
65
|
+
### Fuzz Test Template
|
|
66
|
+
```solidity
|
|
67
|
+
// SPDX-License-Identifier: MIT
|
|
68
|
+
pragma solidity ^0.8.0;
|
|
69
|
+
|
|
70
|
+
import "forge-std/Test.sol";
|
|
71
|
+
import "../src/TargetContract.sol";
|
|
72
|
+
|
|
73
|
+
contract FuzzTest is Test {
|
|
74
|
+
TargetContract target;
|
|
75
|
+
|
|
76
|
+
function setUp() public {
|
|
77
|
+
target = new TargetContract();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Foundry automatically fuzzes the inputs
|
|
81
|
+
function testFuzz_deposit(uint256 amount) public {
|
|
82
|
+
// Bound to realistic values
|
|
83
|
+
amount = bound(amount, 1, 1_000_000 ether);
|
|
84
|
+
|
|
85
|
+
deal(address(this), amount);
|
|
86
|
+
target.deposit{value: amount}();
|
|
87
|
+
|
|
88
|
+
assertEq(target.balanceOf(address(this)), amount);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function testFuzz_withdrawDoesNotExceedDeposit(
|
|
92
|
+
uint256 depositAmount,
|
|
93
|
+
uint256 withdrawAmount
|
|
94
|
+
) public {
|
|
95
|
+
depositAmount = bound(depositAmount, 1, 1000 ether);
|
|
96
|
+
withdrawAmount = bound(withdrawAmount, 1, depositAmount);
|
|
97
|
+
|
|
98
|
+
deal(address(this), depositAmount);
|
|
99
|
+
target.deposit{value: depositAmount}();
|
|
100
|
+
target.withdraw(withdrawAmount);
|
|
101
|
+
|
|
102
|
+
assertGe(target.balanceOf(address(this)), 0);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Invariant Test Template
|
|
108
|
+
```solidity
|
|
109
|
+
contract InvariantTest is Test {
|
|
110
|
+
TargetContract target;
|
|
111
|
+
Handler handler;
|
|
112
|
+
|
|
113
|
+
function setUp() public {
|
|
114
|
+
target = new TargetContract();
|
|
115
|
+
handler = new Handler(target);
|
|
116
|
+
|
|
117
|
+
// Tell Foundry to use the handler for calls
|
|
118
|
+
targetContract(address(handler));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// This invariant is checked after EVERY sequence of handler calls
|
|
122
|
+
function invariant_balanceSolvency() public view {
|
|
123
|
+
assertGe(
|
|
124
|
+
address(target).balance,
|
|
125
|
+
target.totalDeposits(),
|
|
126
|
+
"Contract is insolvent!"
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function invariant_totalSupplyMatchesSum() public view {
|
|
131
|
+
assertEq(
|
|
132
|
+
target.totalSupply(),
|
|
133
|
+
handler.sumOfAllBalances(),
|
|
134
|
+
"Total supply mismatch!"
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
contract Handler is Test {
|
|
140
|
+
TargetContract target;
|
|
141
|
+
address[] public actors;
|
|
142
|
+
uint256 public sumDeposited;
|
|
143
|
+
|
|
144
|
+
constructor(TargetContract _target) {
|
|
145
|
+
target = _target;
|
|
146
|
+
// Create test actors
|
|
147
|
+
for (uint i = 0; i < 3; i++) {
|
|
148
|
+
actors.push(makeAddr(string(abi.encodePacked("actor", i))));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function deposit(uint256 actorSeed, uint256 amount) external {
|
|
153
|
+
address actor = actors[actorSeed % actors.length];
|
|
154
|
+
amount = bound(amount, 1, 100 ether);
|
|
155
|
+
|
|
156
|
+
deal(actor, amount);
|
|
157
|
+
vm.prank(actor);
|
|
158
|
+
target.deposit{value: amount}();
|
|
159
|
+
sumDeposited += amount;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function withdraw(uint256 actorSeed, uint256 amount) external {
|
|
163
|
+
address actor = actors[actorSeed % actors.length];
|
|
164
|
+
uint256 balance = target.balanceOf(actor);
|
|
165
|
+
if (balance == 0) return;
|
|
166
|
+
|
|
167
|
+
amount = bound(amount, 1, balance);
|
|
168
|
+
vm.prank(actor);
|
|
169
|
+
target.withdraw(amount);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function sumOfAllBalances() external view returns (uint256 sum) {
|
|
173
|
+
for (uint i = 0; i < actors.length; i++) {
|
|
174
|
+
sum += target.balanceOf(actors[i]);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### State Transition Test Template
|
|
181
|
+
```solidity
|
|
182
|
+
contract StateTransitionTest is Test {
|
|
183
|
+
TargetContract target;
|
|
184
|
+
|
|
185
|
+
enum State { Idle, Active, Paused, Finalized }
|
|
186
|
+
|
|
187
|
+
function test_invalidStateTransition() public {
|
|
188
|
+
// Try to move to invalid state
|
|
189
|
+
vm.expectRevert();
|
|
190
|
+
target.finalize(); // Should fail if not active
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function test_stateSequence() public {
|
|
194
|
+
assertEq(uint(target.state()), uint(State.Idle));
|
|
195
|
+
|
|
196
|
+
target.activate();
|
|
197
|
+
assertEq(uint(target.state()), uint(State.Active));
|
|
198
|
+
|
|
199
|
+
target.pause();
|
|
200
|
+
assertEq(uint(target.state()), uint(State.Paused));
|
|
201
|
+
|
|
202
|
+
target.resume();
|
|
203
|
+
assertEq(uint(target.state()), uint(State.Active));
|
|
204
|
+
|
|
205
|
+
target.finalize();
|
|
206
|
+
assertEq(uint(target.state()), uint(State.Finalized));
|
|
207
|
+
|
|
208
|
+
// Cannot go back
|
|
209
|
+
vm.expectRevert();
|
|
210
|
+
target.activate();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Boundary Value Test Template
|
|
216
|
+
```solidity
|
|
217
|
+
contract BoundaryTest is Test {
|
|
218
|
+
TargetContract target;
|
|
219
|
+
|
|
220
|
+
function test_zeroAmount() public {
|
|
221
|
+
vm.expectRevert("Amount must be > 0");
|
|
222
|
+
target.deposit{value: 0}();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function test_maxUint() public {
|
|
226
|
+
// Test with maximum possible value
|
|
227
|
+
uint256 maxVal = type(uint256).max;
|
|
228
|
+
// Should either work correctly or revert cleanly (no overflow)
|
|
229
|
+
try target.someFunction(maxVal) {
|
|
230
|
+
// Check result is correct
|
|
231
|
+
} catch {
|
|
232
|
+
// Revert is acceptable, silent overflow is NOT
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function test_zeroAddress() public {
|
|
237
|
+
vm.expectRevert();
|
|
238
|
+
target.transfer(address(0), 100);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Rust/Solana Test Templates
|
|
246
|
+
|
|
247
|
+
### Anchor TypeScript Test Template
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import * as anchor from "@coral-xyz/anchor";
|
|
251
|
+
import { Program } from "@coral-xyz/anchor";
|
|
252
|
+
import { expect } from "chai";
|
|
253
|
+
import { MyProgram } from "../target/types/my_program";
|
|
254
|
+
|
|
255
|
+
describe("my-program", () => {
|
|
256
|
+
anchor.setProvider(anchor.AnchorProvider.env());
|
|
257
|
+
const program = anchor.workspace.MyProgram as Program<MyProgram>;
|
|
258
|
+
|
|
259
|
+
it("Is initialized!", async () => {
|
|
260
|
+
const tx = await program.methods.initialize().rpc();
|
|
261
|
+
console.log("Transaction signature:", tx);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("Deposit and withdraw", async () => {
|
|
265
|
+
const user = anchor.web3.Keypair.generate();
|
|
266
|
+
const amount = new anchor.BN(1_000_000);
|
|
267
|
+
|
|
268
|
+
// Airdrop
|
|
269
|
+
await anchor.getProvider().connection.confirmTransaction(
|
|
270
|
+
await anchor.getProvider().connection.requestAirdrop(
|
|
271
|
+
user.publicKey,
|
|
272
|
+
10 * anchor.web3.LAMPORTS_PER_SOL
|
|
273
|
+
)
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// Deposit
|
|
277
|
+
await program.methods
|
|
278
|
+
.deposit(amount)
|
|
279
|
+
.accounts({ user: user.publicKey })
|
|
280
|
+
.signers([user])
|
|
281
|
+
.rpc();
|
|
282
|
+
|
|
283
|
+
// Withdraw
|
|
284
|
+
await program.methods
|
|
285
|
+
.withdraw(amount)
|
|
286
|
+
.accounts({ user: user.publicKey })
|
|
287
|
+
.signers([user])
|
|
288
|
+
.rpc();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("Cannot withdraw more than deposited", async () => {
|
|
292
|
+
const user = anchor.web3.Keypair.generate();
|
|
293
|
+
const depositAmount = new anchor.BN(500_000);
|
|
294
|
+
const withdrawAmount = new anchor.BN(1_000_000);
|
|
295
|
+
|
|
296
|
+
// Airdrop and deposit
|
|
297
|
+
await anchor.getProvider().connection.confirmTransaction(
|
|
298
|
+
await anchor.getProvider().connection.requestAirdrop(
|
|
299
|
+
user.publicKey,
|
|
300
|
+
10 * anchor.web3.LAMPORTS_PER_SOL
|
|
301
|
+
)
|
|
302
|
+
);
|
|
303
|
+
await program.methods.deposit(depositAmount).accounts({ user: user.publicKey }).signers([user]).rpc();
|
|
304
|
+
|
|
305
|
+
// Attempt to withdraw more — should fail
|
|
306
|
+
try {
|
|
307
|
+
await program.methods.withdraw(withdrawAmount).accounts({ user: user.publicKey }).signers([user]).rpc();
|
|
308
|
+
expect.fail("Should have thrown");
|
|
309
|
+
} catch (e) {
|
|
310
|
+
expect(e).to.be.an("error");
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Rust Integration Test Template (Solana)
|
|
317
|
+
|
|
318
|
+
```rust
|
|
319
|
+
use solana_program_test::*;
|
|
320
|
+
use solana_sdk::{
|
|
321
|
+
signature::{Keypair, Signer},
|
|
322
|
+
transaction::Transaction,
|
|
323
|
+
pubkey::Pubkey,
|
|
324
|
+
};
|
|
325
|
+
use anchor_lang::AccountDeserialize;
|
|
326
|
+
|
|
327
|
+
#[tokio::test]
|
|
328
|
+
async fn test_exploit_prevention() {
|
|
329
|
+
let program_id = Pubkey::new_unique();
|
|
330
|
+
let mut program_test = ProgramTest::new(
|
|
331
|
+
"my_program",
|
|
332
|
+
program_id,
|
|
333
|
+
processor!(my_program::entry),
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
let (mut banks_client, payer, recent_blockhash) =
|
|
337
|
+
program_test.start().await;
|
|
338
|
+
|
|
339
|
+
let user = Keypair::new();
|
|
340
|
+
let amount = 1_000_000u64;
|
|
341
|
+
|
|
342
|
+
// Create transaction
|
|
343
|
+
let mut tx = Transaction::new_signed_with_payer(
|
|
344
|
+
&[/* instructions */],
|
|
345
|
+
Some(&payer.pubkey()),
|
|
346
|
+
&[&payer, &user],
|
|
347
|
+
recent_blockhash,
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// Execute
|
|
351
|
+
let result = banks_client.process_transaction(tx).await;
|
|
352
|
+
assert!(result.is_ok());
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Fuzz Testing Template (Rust/Solana — Trident)
|
|
357
|
+
|
|
358
|
+
```rust
|
|
359
|
+
use trident_client::fuzzing::*;
|
|
360
|
+
use my_program::*;
|
|
361
|
+
|
|
362
|
+
#[derive(Arbitrary, Debug, Clone)]
|
|
363
|
+
struct MyFuzzInstruction {
|
|
364
|
+
amount: u64,
|
|
365
|
+
user_index: u8,
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
impl FuzzTestExecutor for MyFuzzInstruction {
|
|
369
|
+
type AccountStorage = MyAccounts;
|
|
370
|
+
|
|
371
|
+
fn execute(
|
|
372
|
+
&self,
|
|
373
|
+
accounts: &mut Self::AccountStorage,
|
|
374
|
+
_runtime: &mut FuzzingRuntime,
|
|
375
|
+
) -> Result<(), FuzzError> {
|
|
376
|
+
let user = accounts.users[self.user_index as usize % accounts.users.len()];
|
|
377
|
+
|
|
378
|
+
let ix = my_program::instruction::deposit(
|
|
379
|
+
user.pubkey(),
|
|
380
|
+
self.amount,
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
let tx = Transaction::new_signed_with_payer(
|
|
384
|
+
&[ix],
|
|
385
|
+
Some(&user.pubkey()),
|
|
386
|
+
&[user],
|
|
387
|
+
accounts.recent_blockhash,
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
let result = accounts.banks_client.process_transaction(tx);
|
|
391
|
+
|
|
392
|
+
// Invariant: total deposits should never overflow
|
|
393
|
+
assert!(result.is_ok() || result.unwrap_err().to_string().contains("overflow"));
|
|
394
|
+
|
|
395
|
+
Ok(())
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### ink!/Substrate Test Template
|
|
401
|
+
|
|
402
|
+
```rust
|
|
403
|
+
#[ink::test]
|
|
404
|
+
fn test_transfer_works() {
|
|
405
|
+
let mut contract = MyContract::new(1000);
|
|
406
|
+
assert_eq!(contract.total_supply(), 1000);
|
|
407
|
+
|
|
408
|
+
contract.transfer(AccountId::from([0x02; 32]), 500);
|
|
409
|
+
assert_eq!(contract.balance_of(AccountId::from([0x01; 32])), 500);
|
|
410
|
+
assert_eq!(contract.balance_of(AccountId::from([0x02; 32])), 500);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
#[ink::test]
|
|
414
|
+
#[should_panic(expected = "insufficient balance")]
|
|
415
|
+
fn test_transfer_fails_insufficient_balance() {
|
|
416
|
+
let mut contract = MyContract::new(100);
|
|
417
|
+
contract.transfer(AccountId::from([0x02; 32]), 200);
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Prompt Templates
|
|
424
|
+
|
|
425
|
+
### Generate Full Test Suite (Solidity/Foundry)
|
|
426
|
+
```
|
|
427
|
+
You are a Foundry test architect.
|
|
428
|
+
|
|
429
|
+
Contract: [PASTE CONTRACT]
|
|
430
|
+
|
|
431
|
+
Generate a comprehensive test suite including:
|
|
432
|
+
1. Unit tests for every public/external function
|
|
433
|
+
2. Fuzz tests for all numerical inputs
|
|
434
|
+
3. Invariant tests defining 3-5 critical properties
|
|
435
|
+
4. State transition tests
|
|
436
|
+
5. Boundary tests (0, max, zero address)
|
|
437
|
+
6. Integration tests for realistic user flows
|
|
438
|
+
|
|
439
|
+
Format as a complete Foundry test file ready to run.
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Generate Full Test Suite (Rust/Solana/Anchor)
|
|
443
|
+
```
|
|
444
|
+
You are an Anchor/Solana test architect using mocha+chai TypeScript tests or Rust integration tests.
|
|
445
|
+
|
|
446
|
+
Program: [PASTE RUST PROGRAM or IDL]
|
|
447
|
+
|
|
448
|
+
Generate a comprehensive test suite including:
|
|
449
|
+
1. Unit tests for every instruction handler
|
|
450
|
+
2. Account validation tests (wrong accounts, missing signers)
|
|
451
|
+
3. Fuzz-like parameter bound testing
|
|
452
|
+
4. State invariant checks (balance sums, authority checks)
|
|
453
|
+
5. CPI integration tests
|
|
454
|
+
6. Edge case tests (zero amounts, max values, close+reinit)
|
|
455
|
+
|
|
456
|
+
Format as complete `describe/it` test blocks ready to run with `anchor test`.
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Generate Invariants Only
|
|
460
|
+
```
|
|
461
|
+
Analyze this contract and identify:
|
|
462
|
+
1. All state invariants that must ALWAYS hold
|
|
463
|
+
2. Economic invariants (solvency, supply)
|
|
464
|
+
3. Access invariants (ownership, roles)
|
|
465
|
+
4. Logic invariants (sequence, state machine)
|
|
466
|
+
|
|
467
|
+
For each invariant write:
|
|
468
|
+
- Natural language description
|
|
469
|
+
- Foundry invariant_ test function
|
|
470
|
+
|
|
471
|
+
Contract: [PASTE CONTRACT]
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Generate Fuzz Targets
|
|
475
|
+
```
|
|
476
|
+
Identify the 5 most valuable functions to fuzz in this contract.
|
|
477
|
+
For each, write a Foundry fuzz test that:
|
|
478
|
+
1. Bounds inputs to realistic ranges
|
|
479
|
+
2. Tests for correct behavior
|
|
480
|
+
3. Checks no funds are lost
|
|
481
|
+
4. Verifies no invariants broken
|
|
482
|
+
|
|
483
|
+
Contract: [PASTE CONTRACT]
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## Foundry Commands Reference
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
# Run all tests
|
|
492
|
+
forge test
|
|
493
|
+
|
|
494
|
+
# Run with verbosity (see logs)
|
|
495
|
+
forge test -vvvv
|
|
496
|
+
|
|
497
|
+
# Run specific test
|
|
498
|
+
forge test --match-test test_exploit
|
|
499
|
+
|
|
500
|
+
# Run fuzz tests (more runs = better coverage)
|
|
501
|
+
forge test --fuzz-runs 10000
|
|
502
|
+
|
|
503
|
+
# Run invariant tests
|
|
504
|
+
forge test --match-contract InvariantTest
|
|
505
|
+
|
|
506
|
+
# Coverage report
|
|
507
|
+
forge coverage
|
|
508
|
+
|
|
509
|
+
# Gas snapshot
|
|
510
|
+
forge snapshot
|
|
511
|
+
```
|