@sivan-56/solpit
v0.1.11
Published
Foundry-style Solana mainnet fork for in-process testing
Downloads
93
Readme
@sivan-56/solpit
Foundry-style Solana mainnet fork for in-process testing.
Lazily loads real mainnet accounts into LiteSVM before each transaction — no need to pre-declare accounts at startup.
npm install @sivan-56/solpitRequires Node.js 18+. Supports macOS (ARM64/x86), Linux (x64/ARM64), Windows (x64).
Quick start
import { SolanaFork, assertReverted, cpiTree } from '@sivan-56/solpit'
const fork = await SolanaFork.fromMainnet()
// Airdrop SOL to any address
await fork.airdrop(myWallet, 2_000_000_000n) // 2 SOL
// Set SPL token balance without mint authority
await fork.setTokenBalance(myUsdcAta, 1_000_000_000n) // 1000 USDC
// Execute a transaction — missing accounts fetched from mainnet automatically
const result = await fork.sendTransaction(Buffer.from(tx.serialize()))
if (typeof result.error === 'function') {
console.error('failed:', result.error())
} else {
console.log('success, CU:', result.computeUnitsConsumed())
console.log(cpiTree(result.logs())) // print CPI call tree
}CLI
Run your Jest tests with solpit test (similar to forge test):
solpit test # run all tests
solpit test fork # filter by file name
solpit test -t "should transfer" # filter by test nameVerbosity flags
Like Foundry's -v through -vvvvv, use -l through -lllll to see what's happening inside transactions:
solpit test -l # show logs on failed transactions only
solpit test -ll # show logs on all transactions
solpit test -lll # logs + fee + compute units + return data
solpit test -llll # CPI call tree (structure only)
solpit test -lllll # CPI call tree with per-node log linesExample output at -llll:
[solpit] ✓ sendTransaction fee=5000 lamports cu=1234
├─ ✓ Token
│ └─ ✓ System
└─ ✓ ATA
[solpit] ✗ sendTransaction
error: custom program error: 0x1
└─ ✗ MyProgram [custom program error: 0x1]Key features
Impersonation (vm.prank)
Send transactions as any mainnet account without holding its private key:
await fork.setSigverify(false)
// build and send a transaction with any pubkey as signer
const result = await fork.sendTransaction(Buffer.from(privilegedTx.serialize()))
await fork.setSigverify(true)Snapshots
const snap = await fork.snapshot()
await fork.airdrop(addr, 999n)
// ... do things ...
await fork.revert(snap) // roll back to pre-airdrop stateAssert reverts
import { assertReverted } from '@sivan-56/solpit'
const result = await fork.sendTransaction(Buffer.from(tx.serialize()))
assertReverted(result, 'custom program error: 0x1')CPI call tree
import { cpiTree, parseCpiTree } from '@sivan-56/solpit'
console.log(cpiTree(result.logs()))
// ├─ ✓ Token
// │ └─ ✓ System
// └─ ✓ ATA
// Or as a structured tree for programmatic use
const nodes = parseCpiTree(result.logs())Time travel
await fork.advanceSlot(100n) // advance clock by 100 slots
await fork.warpToSlot(300_000_000n) // jump to a specific slot
await fork.warpToTimestamp(1_800_000_000n) // set Unix timestampToken balance manipulation
await fork.setTokenBalance(ataAddress, 1_000_000_000_000n)
const info = await fork.getTokenAccountInfo(ataAddress)
console.log(info.amount, info.mint, info.owner)Load a custom program
Auto-compiles from Rust source when files change:
// Native program
const programId = await fork.addProgramFromSource(
null, // auto-detect program ID
'./contracts/exploit/Cargo.toml'
)
// Anchor program
const programId = await fork.addAnchorProgram(null, 'my_program', './workspace')Requires
cargo build-sbffrom the Solana toolchain.
Parse Anchor events
import { parseAnchorEvents, assertAnchorEvent, anchorEventDiscriminator } from '@sivan-56/solpit'
const events = parseAnchorEvents(result.logs())
const disc = anchorEventDiscriminator('SwapEvent')
const ev = assertAnchorEvent(result, disc) // throws if not foundAPI reference
Fork creation
| Method | Description |
|--------|-------------|
| SolanaFork.fromMainnet() | Fork Solana mainnet |
| SolanaFork.fromDevnet() | Fork Solana devnet |
| SolanaFork.fromRpc(url) | Fork from a custom RPC endpoint |
Transactions
| Method | Description |
|--------|-------------|
| sendTransaction(tx) | Execute transaction, return metadata |
| simulateTransaction(tx) | Simulate without changing state |
| sendTransactionWithDiff(tx) | Execute and return account diffs |
| latestBlockhash() | Current blockhash (base58) |
Cheatcodes
| Method | Description |
|--------|-------------|
| airdrop(pubkey, lamports) | Credit SOL |
| setSigverify(enabled) | Toggle signature verification |
| setAccount(pubkey, account) | Overwrite account data |
| getAccount(pubkey) | Read cached account |
| fetchAccount(pubkey) | Force-fetch from RPC |
| setBalance(pubkey, lamports) | Set SOL balance |
| getBalance(pubkey) | Read SOL balance |
| setTokenBalance(ata, amount) | Set SPL token balance |
| getTokenAccountInfo(ata) | Read token account fields |
| minimumBalanceForRentExemption(dataLen) | Rent-exempt minimum |
| warpToSlot(slot) | Jump to exact slot |
| advanceSlot(n) | Advance slot by N |
| warpToTimestamp(ts) | Set Unix timestamp |
| getClock() / setClock(clock) | Read/write Sysvar clock |
| setComputeUnitLimit(units) | Override compute budget |
| snapshot() | Save state, returns snapshot ID |
| revert(id) | Restore to snapshot |
| currentSlot() | Current slot number |
Programs
| Method | Description |
|--------|-------------|
| addProgram(programId, elf) | Load from .so bytes |
| addProgramFromSource(programId, manifestPath) | Auto-compile Rust program |
| addAnchorProgram(programId, name, workspacePath?) | Auto-compile Anchor program |
| addProgramFromChain(programId) | Load a program from mainnet |
Helper functions
| Function | Description |
|----------|-------------|
| assertReverted(result, error?) | Assert tx failed; check error message |
| cpiTree(logs) | Parse and render CPI tree as string |
| parseCpiTree(logs) | Parse logs into CpiNode[] |
| formatCpiTree(nodes) | Render CpiNode[] as string |
| parseAnchorEvents(logs) | Extract Anchor events |
| assertAnchorEvent(result, disc, opts?) | Assert an Anchor event was emitted |
| anchorEventDiscriminator(name) | Compute 8-byte event discriminator |
License
Apache-2.0
