Test Suite

Solidity Tests (Hardhat 3, node:test)

14 tests in test/BINSTPilot.ts covering the full contract lifecycle:

#TestWhat it verifies
1Deploy BINSTDeployerFactory deploys, owner set correctly
2Create InstitutioncreateInstitution() emits event, stores name/admin
3Set Inscription IDAdmin can bind Bitcoin inscription
4Set Rune IDAdmin can bind Rune for membership
5Add MemberAdmin adds member, isMember returns true
6Remove MemberAdmin removes member, isMember returns false
7Create Process TemplateTemplate created with correct step names
8Create Process InstanceInstance linked to template and institution
9Execute StepFirst step executed, state updated
10Execute All StepsSequential execution through all steps
11Complete InstanceFinal step marks instance as completed
12Access ControlNon-admin calls revert with correct error
13Set btcPubkeyAdmin can set Bitcoin x-only pubkey
14btcPubkey validationRejects zero pubkey and non-admin calls
npx hardhat test          # runs all 14

Rust Tests (cargo test)

79 tests across 4 crates in taproot-reader/:

binst-inscription (10 tests)

TestWhat it verifies
extract_binst_inscriptionParses binst metaprotocol envelope from witness
extract_with_parent_tagHandles parent inscription tag
no_envelope_in_random_dataRejects non-envelope witness data
ignore_non_binst_inscriptionSkips non-binst metaprotocol
handle_pushdata1Handles OP_PUSHDATA1 encoding
parse_institutionParses institution JSON body
parse_process_templateParses template JSON body
parse_step_executionParses step execution JSON body
parse_state_digestParses state digest body
reject_unknown_typeRejects unknown entity type

binst-decoder (27 unit + 14 value + 11 vault + 5 e2e = 57 tests)

TestWhat it verifies
registry_build_lookup_populates_tableForward-hash lookup table is populated
registry_lookupRegistry resolves address to contract type
registry_resolves_known_slotKnown slot hash maps to BINST field
map_state_diff_finds_binst_entriesState diff entries matched to BINST
map_state_diff_array_elementArray slot elements decoded correctly
decode_deployer_elementsDeployer storage slots decoded
decode_institution_simple_slotsInstitution simple fields decoded
decode_institution_members_array_elementInstitution members array decoded
decode_instance_completedInstance completion flag decoded
decode_instance_step_stateInstance step state decoded
slot_to_u64_simple/largeU256 → u64 conversion
sub_words_basic/underflowWord subtraction arithmetic
evm_storage_hash_*JMT key computation
parse_evm_storage/header/account/indexJMT key prefix parsing
summarize_mixed_diffJMT diff categorization
keccak256_known_vectorKeccak256 matches known output
array_base/element_slotArray storage layout
mapping_slot_addressMapping storage layout
add_word_offsetU256 word offset addition
full_pipeline_* (5 e2e)End-to-end: proof → registry → BINST changes

Vault module (vault — 11 tests)

TestWhat it verifies
compile_produces_descriptorPolicy compiles to tr(NUMS, {…}) descriptor
testnet_address_starts_with_tb1pTestnet address is valid Taproot bech32m
mainnet_address_starts_with_bc1pMainnet address is valid Taproot bech32m
csv_delay_one_compilesMinimum valid CSV delay compiles
csv_delay_zero_rejectedCSV delay = 0 rejected (miniscript minimum is 1)
analyze_returns_two_pathsTwo spending paths: admin + committee
admin_path_has_timelockAdmin path has CSV = 144, requires 1 key
committee_path_is_immediateCommittee path has no timelock, requires 3 keys
witness_sizes_are_reasonableAll paths < 200 vbytes
descriptor_round_tripsparse(format(descriptor)) == descriptor
address_accessor_worksNetwork-specific address accessor returns correct prefix

Value decoding (value module — 14 tests)

TestWhat it verifies
decode_address_from_wordAddress extracted from last 20 bytes of BE word
decode_uint256_smallSmall integer decoded from big-endian bytes
decode_uint256_zeroZero value decoded correctly
decode_uint256_timestampTimestamp-sized integer decoded
decode_bool_trueNon-zero byte → true
decode_bool_falseZero bytes → false
decode_bytes32_pubkeyFull 32-byte hex preserved
decode_short_stringInline Solidity string (≤31 chars) decoded
decode_short_string_emptyEmpty string slot decoded
decode_long_stringLong string length marker detected
decode_step_state_completedPacked StepState: status + actor extracted
decode_value_deletedNone raw value → DELETED
decode_value_from_hexFull pipeline: Citrea LE hex → BE → decoded address
field_type_coverageEvery FieldChange variant has a type mapping

citrea-decoder (7 tests)

TestWhat it verifies
parse_real_sequencer_commitmentParses real commitment from witness data
reject_too_shortRejects truncated input
batch_proof_output_roundtripProof output serialize/deserialize
heuristic_finds_embedded_journalHeuristic journal extraction
decompress_empty_failsBrotli rejects empty input
decompress_garbage_failsBrotli rejects random data
brotli_roundtripBrotli compress → decompress roundtrip

cli (5 tests)

TestWhat it verifies
state_diff_from_rpc_basicRPC state diff hex map conversion
state_diff_from_rpc_no_prefixHandles keys without 0x prefix
decode_address_array_emptyABI decodes empty address[]
decode_address_array_two_addrsABI decodes two-element address[]
decode_address_array_too_shortRejects truncated ABI data
cd taproot-reader && cargo test    # runs all 79

Running Everything

# From project root
npx hardhat test && cd taproot-reader && cargo test
# 14 Solidity + 79 Rust = 93 total tests

No external services required — all tests run against local state.