Contract test coverage
Contract behavior is checked at two levels:
- native Sway tests cover deterministic arithmetic and boundary helpers;
test/integration/contains isolated, named per-contract suites plus a cross-contract end-to-end suite. Together they deploy all six production contracts and a test-only legacy-bid storage fixture to local Fuel nodes and check calls, storage, balances, ownership, time, proxy forwarding, oracle failures, and exact revert reasons.
test/contract-coverage.test.ts is the drift guard. It fails when an implemented contract method is not invoked by the local-node suites or when a new named guard lacks an exact negative assertion. The current suite has 32 native Sway tests and 4 coverage-drift checks, while the integration assertion count is reported directly by Bun.
Inventory
| Contract | Implemented methods | require statements | Covered scenarios |
|---|---|---|---|
| Vault | 24 | 34 | Lifecycle, oracle, accounting and admin paths |
| Bond Market V2 | 25 | 55 | Asks, buy orders, filters, fees and proxy paths |
| Legacy Bond Market | 12 | 18 | Listings, purchases, fees and admin paths |
| Owned Proxy | 6 | 8 | Ownership, target validation and upgrades |
| Test Assets/SRC-20 | 9 | 4 | Metadata, supply, faucet and owner minting |
| Mock Simplified Oracle | 3 | 0 | Empty, set, read and clear states |
| Total | 79 | 119 |
Every reachable named rejection is asserted by its exact reason. Failed calls are followed by state or balance checks where state could otherwise have changed, including position counts, NFT ownership, buy-order budget, strategy liabilities, fees, and lifecycle status.
Adversarial access-control coverage
test/integration/access-control.integration.ts uses separate authority, pending-successor, and attacker wallets to verify:
- unauthorized callers cannot pause the Vault, change strategies, move strategy assets, or nominate a manager;
- manager handoffs cannot be front-run, pending managers have no early powers, and former managers lose every privileged operation immediately;
- allowlisting, revocation, invalid returns, and failed strategy transfers leave liabilities and token balances unchanged;
- Market V2 and proxy ownership handoffs cannot be seized or raced, failed upgrades preserve the target, and former owners lose all controls; and
- manager, market-owner, and proxy-owner roles remain isolated after assignment to three different wallets.
The tests assert exact rejection reasons and post-failure storage and balances, including refund atomicity for payable attacks. The unrestricted mock oracle is a test fixture and is not represented as a production access-control boundary.
Arithmetic attack coverage
Native Sway tests and test/integration/math-safety.integration.ts cover:
- checked
u64additions and subtractions at zero and maximum boundaries, with namedMATH_OVERFLOWandMATH_UNDERFLOWfailures; - fixed-point floor/ceiling behavior, maximum-value fee calculations, conserved
gross = net + fee, and monotonic bounded early-exit penalties; - a valid oracle value whose downstream collateral conversion would overflow;
- ten maximum-valued positions whose combined USDC obligation is exactly
u64::max() - 5, followed by an overflowing eleventh opening; - test-asset supply at
u64::max()followed by a malicious extra mint; - one-unit V2 asks, legacy asks, recovered V2 bids, and repeated buy-order fills where rounded-up fees consume the full unit and seller proceeds are zero; and
- one-unit Vault positions with zero rewards or fully rounded penalties.
Every failed overflow transaction is followed by position, NFT, liability, supply, wallet-balance, fee, or order assertions to prove atomic rollback. These attacks exposed that raw Sway u64 arithmetic wraps and that zero-value transfers revert. Production accounting now uses checked helpers, and zero-value payouts are skipped while their accounting and events remain exact.
Invariant-only and compatibility guards
The following guards cannot be reached from a fresh deployment without breaking an asset-supply invariant or introducing a purpose-built reentrant contract. They remain checked by source drift and are not represented as a normal user transaction:
- Vault
REENTRANCY: requires a malicious configured oracle callback; the production oracle ID is immutable. - Legacy and V2
ALREADY_LISTED: the only position NFT is already escrowed, so a caller cannot supply the second required NFT. - V2
create_bidchecks afterPOSITION_BIDS_DISABLED, including its paused and USDC checks, are unreachable because the unconditional disable guard runs first. Legacy bid cancellation, acceptance, expiration, authorization, timing, NFT validation, self-trade rejection, refunds, and settlement are tested by seeding pre-upgrade proxy storage with the test-only implementation. - V2 bid
CANCEL_ASK_FIRST: an ask already escrows the sole NFT needed to callaccept_bid. - V2 buy-order
CANCEL_ASK_FIRSTandPOSITION_NOT_ACTIVE: an ask or inactive Vault position no longer leaves the seller with the sole active position NFT required byfill_buy_order. - V2
REENTRANCY: requires a purpose-built malicious Vault callback; the production Vault ID is immutable.
These are intentionally listed rather than mislabeled as exercised. The method-level manifest in test/contract-guard-coverage.json distinguishes each exercised guard from each invariant-only guard and fails CI on source drift.
Commands
Use the Fuel toolchain compatible with fuels@0.101.1 (forc 0.68.1 and fuel-core 0.43.1):
bun run contracts:test
bun run test:integration
bun test
bun run typecheck