mollusk-svm-programs-token 0.4.2

SVM program SPL Token.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# Mollusk

Mollusk is a lightweight test harness for Solana programs. It provides a
simple interface for testing Solana program executions in a minified
Solana Virtual Machine (SVM) environment.

It does not create any semblance of a validator runtime, but instead
provisions a program execution pipeline directly from lower-level SVM
components.

In summary, the main processor - `process_instruction` - creates minified
instances of Agave's program cache, transaction context, and invoke
context. It uses these components to directly execute the provided
program's ELF using the BPF Loader.

Because it does not use AccountsDB, Bank, or any other large Agave
components, the harness is exceptionally fast. However, it does require
the user to provide an explicit list of accounts to use, since it has
nowhere to load them from.

The test environment can be further configured by adjusting the compute
budget, feature set, or sysvars. These configurations are stored directly
on the test harness (the `Mollusk` struct), but can be manipulated through
a handful of helpers.

Four main API methods are offered:

* `process_instruction`: Process an instruction and return the result.
* `process_and_validate_instruction`: Process an instruction and perform a
  series of checks on the result, panicking if any checks fail.
* `process_instruction_chain`: Process a chain of instructions and return
  the result.
* `process_and_validate_instruction_chain`: Process a chain of instructions
  and perform a series of checks on each result, panicking if any checks
  fail.

## Single Instructions

Both `process_instruction` and `process_and_validate_instruction` deal with
single instructions. The former simply processes the instruction and
returns the result, while the latter processes the instruction and then
performs a series of checks on the result. In both cases, the result is
also returned.

```rust
use {
    mollusk_svm::Mollusk,
    solana_sdk::{account::Account, instruction::{AccountMeta, Instruction}, pubkey::Pubkey},
};

let program_id = Pubkey::new_unique();
let key1 = Pubkey::new_unique();
let key2 = Pubkey::new_unique();

let instruction = Instruction::new_with_bytes(
    program_id,
    &[],
    vec![
        AccountMeta::new(key1, false),
        AccountMeta::new_readonly(key2, false),
    ],
);

let accounts = vec![
    (key1, Account::default()),
    (key2, Account::default()),
];

let mollusk = Mollusk::new(&program_id, "my_program");

// Execute the instruction and get the result.
let result = mollusk.process_instruction(&instruction, &accounts);
```

To apply checks via `process_and_validate_instruction`, developers can use
the `Check` enum, which provides a set of common checks.

```rust
use {
    mollusk_svm::{Mollusk, result::Check},
    solana_sdk::{
        account::Account,
        instruction::{AccountMeta, Instruction},
        pubkey::Pubkey
        system_instruction,
        system_program,
    },
};

let sender = Pubkey::new_unique();
let recipient = Pubkey::new_unique();

let base_lamports = 100_000_000u64;
let transfer_amount = 42_000u64;

let instruction = system_instruction::transfer(&sender, &recipient, transfer_amount);
let accounts = [
    (
        sender,
        Account::new(base_lamports, 0, &system_program::id()),
    ),
    (
        recipient,
        Account::new(base_lamports, 0, &system_program::id()),
    ),
];
let checks = vec![
    Check::success(),
    Check::compute_units(system_processor::DEFAULT_COMPUTE_UNITS),
    Check::account(&sender)
        .lamports(base_lamports - transfer_amount)
        .build(),
    Check::account(&recipient)
        .lamports(base_lamports + transfer_amount)
        .build(),
];

Mollusk::default().process_and_validate_instruction(
    &instruction,
    &accounts,
    &checks,
);
```

Note: `Mollusk::default()` will create a new `Mollusk` instance without
adding any provided BPF programs. It will still contain a subset of the
default builtin programs. For more builtin programs, you can add them
yourself or use the `all-builtins` feature.

## Instruction Chains

Both `process_instruction_chain` and
`process_and_validate_instruction_chain` deal with chains of instructions.
The former processes each instruction in the chain and returns the final
result, while the latter processes each instruction in the chain and then
performs a series of checks on each result. In both cases, the final result
is also returned.

```rust
use {
    mollusk_svm::Mollusk,
    solana_sdk::{account::Account, pubkey::Pubkey, system_instruction},
};

let mollusk = Mollusk::default();

let alice = Pubkey::new_unique();
let bob = Pubkey::new_unique();
let carol = Pubkey::new_unique();
let dave = Pubkey::new_unique();

let starting_lamports = 500_000_000;

let alice_to_bob = 100_000_000;
let bob_to_carol = 50_000_000;
let bob_to_dave = 50_000_000;

mollusk.process_instruction_chain(
    &[
        system_instruction::transfer(&alice, &bob, alice_to_bob),
        system_instruction::transfer(&bob, &carol, bob_to_carol),
        system_instruction::transfer(&bob, &dave, bob_to_dave),
    ],
    &[
        (alice, system_account_with_lamports(starting_lamports)),
        (bob, system_account_with_lamports(starting_lamports)),
        (carol, system_account_with_lamports(starting_lamports)),
        (dave, system_account_with_lamports(starting_lamports)),
    ],
);
```

Just like with `process_and_validate_instruction`, developers can use the
`Check` enum to apply checks via `process_and_validate_instruction_chain`.
Notice that `process_and_validate_instruction_chain` takes a slice of
tuples, where each tuple contains an instruction and a slice of checks.
This allows the developer to apply specific checks to each instruction in
the chain. The result returned by the method is the final result of the
last instruction in the chain.

```rust
use {
    mollusk_svm::{Mollusk, result::Check},
    solana_sdk::{account::Account, pubkey::Pubkey, system_instruction},
};

let mollusk = Mollusk::default();

let alice = Pubkey::new_unique();
let bob = Pubkey::new_unique();
let carol = Pubkey::new_unique();
let dave = Pubkey::new_unique();

let starting_lamports = 500_000_000;

let alice_to_bob = 100_000_000;
let bob_to_carol = 50_000_000;
let bob_to_dave = 50_000_000;

mollusk.process_and_validate_instruction_chain(
    &[
        (
            // 0: Alice to Bob
            &system_instruction::transfer(&alice, &bob, alice_to_bob),
            &[
                Check::success(),
                Check::account(&alice)
                    .lamports(starting_lamports - alice_to_bob) // Alice pays
                    .build(),
                Check::account(&bob)
                    .lamports(starting_lamports + alice_to_bob) // Bob receives
                    .build(),
                Check::account(&carol)
                    .lamports(starting_lamports) // Unchanged
                    .build(),
                Check::account(&dave)
                    .lamports(starting_lamports) // Unchanged
                    .build(),
            ],
        ),
        (
            // 1: Bob to Carol
            &system_instruction::transfer(&bob, &carol, bob_to_carol),
            &[
                Check::success(),
                Check::account(&alice)
                    .lamports(starting_lamports - alice_to_bob) // Unchanged
                    .build(),
                Check::account(&bob)
                    .lamports(starting_lamports + alice_to_bob - bob_to_carol) // Bob pays
                    .build(),
                Check::account(&carol)
                    .lamports(starting_lamports + bob_to_carol) // Carol receives
                    .build(),
                Check::account(&dave)
                    .lamports(starting_lamports) // Unchanged
                    .build(),
            ],
        ),
        (
            // 2: Bob to Dave
            &system_instruction::transfer(&bob, &dave, bob_to_dave),
            &[
                Check::success(),
                Check::account(&alice)
                    .lamports(starting_lamports - alice_to_bob) // Unchanged
                    .build(),
                Check::account(&bob)
                    .lamports(starting_lamports + alice_to_bob - bob_to_carol - bob_to_dave) // Bob pays
                    .build(),
                Check::account(&carol)
                    .lamports(starting_lamports + bob_to_carol) // Unchanged
                    .build(),
                Check::account(&dave)
                    .lamports(starting_lamports + bob_to_dave) // Dave receives
                    .build(),
            ],
        ),
    ],
    &[
        (alice, system_account_with_lamports(starting_lamports)),
        (bob, system_account_with_lamports(starting_lamports)),
        (carol, system_account_with_lamports(starting_lamports)),
        (dave, system_account_with_lamports(starting_lamports)),
    ],
);
```

It's important to understand that instruction chains _should not_ be
considered equivalent to Solana transactions. Mollusk does not impose
constraints on instruction chains, such as loaded account keys or size.
Developers should recognize that instruction chains are primarily used for
testing program execution.

## Stateful Testing with MolluskContext

For complex testing scenarios involving multiple instructions or persistent
state between calls, `MolluskContext` provides a stateful wrapper around
`Mollusk`. It automatically manages an account store and provides the same
API methods without requiring explicit account management.

`MolluskContext` is ideal for:
* Testing instruction chains where account state persists between calls
* Complex program interactions that require maintaining account state
* Scenarios where manually managing accounts becomes cumbersome

To use `MolluskContext`, you need to provide an implementation of the
`AccountStore` trait:

```rust
use {
    mollusk_svm::{Mollusk, account_store::AccountStore},
    solana_account::Account,
    solana_instruction::Instruction,
    solana_pubkey::Pubkey,
    solana_system_interface::instruction as system_instruction,
    std::collections::HashMap,
};

// Simple in-memory account store implementation
#[derive(Default)]
struct InMemoryAccountStore {
    accounts: HashMap<Pubkey, Account>,
}

impl AccountStore for InMemoryAccountStore {
    fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
        self.accounts.get(pubkey).cloned()
    }

    fn store_account(&mut self, pubkey: Pubkey, account: Account) {
        self.accounts.insert(pubkey, account);
    }
}

let mollusk = Mollusk::default();
let context = mollusk.with_context(InMemoryAccountStore::default());

let alice = Pubkey::new_unique();
let bob = Pubkey::new_unique();

// Execute instructions without managing accounts manually
let instruction1 = system_instruction::transfer(&alice, &bob, 1_000_000);
let result1 = context.process_instruction(&instruction1);

let instruction2 = system_instruction::transfer(&bob, &alice, 500_000);
let result2 = context.process_instruction(&instruction2);

// Account state is automatically preserved between calls
```

The `MolluskContext` API provides the same core methods as `Mollusk`:

* `process_instruction`: Process an instruction with automatic account management
* `process_instruction_chain`: Process a chain of instructions  
* `process_and_validate_instruction`: Process and validate an instruction
* `process_and_validate_instruction_chain`: Process and validate an instruction chain

All methods return `ContextResult` instead of `InstructionResult`, which omits
the `resulting_accounts` field since accounts are managed by the context's
account store.

Note that `HashMap<Pubkey, Account>` implements `AccountStore` directly,
so you can use it as a simple in-memory account store without needing
to implement your own.

## Benchmarking Compute Units
The Mollusk Compute Unit Bencher can be used to benchmark the compute unit
usage of Solana programs. It provides a simple API for developers to write
benchmarks for their programs, which can be checked while making changes to
the program.

A markdown file is generated, which captures all of the compute unit
benchmarks. If a benchmark has a previous value, the delta is also
recorded. This can be useful for developers to check the implications of
changes to the program on compute unit usage.

```rust
use {
    mollusk_svm_bencher::MolluskComputeUnitBencher,
    mollusk_svm::Mollusk,
    /* ... */
};

// Optionally disable logging.
solana_logger::setup_with("");

/* Instruction & accounts setup ... */

let mollusk = Mollusk::new(&program_id, "my_program");

MolluskComputeUnitBencher::new(mollusk)
    .bench(("bench0", &instruction0, &accounts0))
    .bench(("bench1", &instruction1, &accounts1))
    .bench(("bench2", &instruction2, &accounts2))
    .bench(("bench3", &instruction3, &accounts3))
    .must_pass(true)
    .out_dir("../target/benches")
    .execute();

```

The `must_pass` argument can be provided to trigger a panic if any defined
benchmark tests do not pass. `out_dir` specifies the directory where the
markdown file will be written.

Developers can invoke this benchmark test with `cargo bench`. They may need
to add a bench to the project's `Cargo.toml`.

```toml
[[bench]]
name = "compute_units"
harness = false
```

The markdown file will contain entries according to the defined benchmarks.

```markdown
| Name   | CUs   | Delta  |
|--------|-------|--------|
| bench0 | 450   | --     |
| bench1 | 579   | -129   |
| bench2 | 1,204 | +754   |
| bench3 | 2,811 | +2,361 |
```

## Fixtures

Mollusk also supports working with multiple kinds of fixtures, which can
help expand testing capabilities. Note this is all gated behind either the
`fuzz` or `fuzz-fd` feature flags.

A fixture is a structured representation of a test case, containing the
input data, the expected output data, and any additional context required
to run the test. One fixture maps to one instruction.

A classic use case for such fixtures is the act of testing two versions of
a program against each other, to ensure the new version behaves as
expected. The original version's test suite can be used to generate a set
of fixtures, which can then be used as inputs to test the new version.
Although you could also simply replace the program ELF file in the test
suite to achieve a similar result, fixtures provide exhaustive coverage.

### Generating Fixtures from Mollusk Tests

Mollusk is capable of generating fixtures from any defined test case. If
the `EJECT_FUZZ_FIXTURES` environment variable is set during a test run,
Mollusk will serialize every invocation of `process_instruction` into a
fixture, using the provided inputs, current Mollusk configurations, and
result returned. `EJECT_FUZZ_FIXTURES_JSON` can also be set to write the
fixtures in JSON format.

```
EJECT_FUZZ_FIXTURES="./fuzz-fixtures" cargo test-sbf ...
```

Note that Mollusk currently supports two types of fixtures: Mollusk's own
fixture layout and the fixture layout used by the Firedancer team. Both of
these layouts stem from Protobuf definitions.

These layouts live in separate crates, but a snippet of the Mollusk input
data for a fixture can be found below:

```rust
/// Instruction context fixture.
pub struct Context {
    /// The compute budget to use for the simulation.
    pub compute_budget: ComputeBudget,
    /// The feature set to use for the simulation.
    pub feature_set: FeatureSet,
    /// The runtime sysvars to use for the simulation.
    pub sysvars: Sysvars,
    /// The program ID of the program being invoked.
    pub program_id: Pubkey,
    /// Accounts to pass to the instruction.
    pub instruction_accounts: Vec<AccountMeta>,
    /// The instruction data.
    pub instruction_data: Vec<u8>,
    /// Input accounts with state.
    pub accounts: Vec<(Pubkey, Account)>,
}
```

### Loading and Executing Fixtures

Mollusk can also execute fixtures, just like it can with instructions. The
`process_fixture` method will process a fixture and return the result, while
`process_and_validate_fixture` will process a fixture and compare the result
against the fixture's effects.

An additional method, `process_and_partially_validate_fixture`, allows
developers to compare the result against the fixture's effects using a
specific subset of checks, rather than the entire set of effects. This
may be useful if you wish to ignore certain effects, such as compute units
consumed.

```rust
use {
    mollusk_svm::{Mollusk, fuzz::check::FixtureCheck},
    solana_sdk::{account::Account, pubkey::Pubkey, system_instruction},
    std::{fs, path::Path},
};

let mollusk = Mollusk::default();

for file in fs::read_dir(Path::new("fixtures-dir"))? {
    let fixture = Fixture::load_from_blob_file(&entry?.file_name());

    // Execute the fixture and apply partial checks.
    mollusk.process_and_partially_validate_fixture(
       &fixture,
       &[
           FixtureCheck::ProgramResult,
           FixtureCheck::ReturnData,
           FixtureCheck::all_resulting_accounts(),
        ],
    );
}
```

Fixtures can be loaded from files or decoded from raw blobs. These
capabilities are provided by the respective fixture crates.