On this page

Contract Testing

Run Solidity tests, choose the suites for your change and reproduce contract artifacts.

Prepare the checkout#

Choose a protocol revision and record any local changes. Contract source, spend layout, verifier artifacts and deployment scripts need to match. Save the source revision, Solidity and Foundry versions and external fork block with the results. If you change contract bytes afterward, rerun the affected checks.

The contract wrapper requires Linux and FUYU_REMOTE_EXPERIMENTS=1. Run experiments, fuzzing and fork campaigns on the designated Linux development host. Use the macOS workspace for source inspection and documentation; the wrapper refuses contract experiments there.

The Foundry profile uses Solidity 0.8.30, Cancun EVM, one optimizer run, via_ir false, no bytecode hash and no CBOR metadata. Dynamic test linking is disabled so constructor revert tests run at the actual CREATE call. Use these settings when comparing artifacts or reproducing contract sizes.

bash
# On the designated Linux host, from the protocol repository root
export FUYU_REMOTE_EXPERIMENTS=1
npm ci
npm run test:contracts

# The same wrapper with a focused Foundry selector
bash contracts/script/test.sh --match-path 'test/payments/*.t.sol'
bash contracts/script/test.sh --match-contract FuyuTransactionRouter
bash contracts/script/test.sh --match-contract PoolInvariantTest

Choose a test suite#

Select tests for the authority, asset movement and retry behavior your change affects. Then run the composition suite that connects the module to the Pool or payment rail. Mock-venue tests cover controlled failures; external-fork tests check behavior against the venue state at their chosen block.

DirectoryChecks
test/poolFunding amounts, spend layout, permanent roots, reserves, reserved slots and finalization
test/authorization / test/controllerEOA/Safe/1271 digests, malformed payloads and claim/refund deadlines
test/directory / test/registryRegistration nonces and revisions, descriptors and venue identity
test/portalInvoice witnesses, sweep bounds, recovery and owner-only credit
test/routerOrdered batches, full rollback and immediate settlement
test/paymentsVoucher deltas, close windows, holds, recurring charges, funding and splits
test/adaptersMeasured token changes, identity checks, leftover balances/allowances and venue failures
test/earnFlex/Term accounting, ticket windows, fees and adapter operations
test/governanceProposal delays, DENY limits, protected exits and renewal
test/migrationSource receipts, private credit and timed recovery
test/hash / test/verifierPoseidon vectors, the fixed spend verifier and oracle comparisons

Accounting invariants#

The default Foundry fuzz profile runs 256 cases. PoolInvariantTest exercises randomized transitions through the Pool fuzz harness. Set RUN_POOL_INVARIANT_DEEP=true to enable deep and census variants; FUZZ_CENSUS_CALLS sets census length. Include seeds and run settings in the report so a failing sequence can be reproduced.

Check credited liabilities against actual custody, unique nullifier consumption, output conservation and release of reserved slots. Verify that wallet/controller checks cannot be bypassed. Payment-rail tests must account for active escrow separately from settled credit supply and reject a second debit for the same cumulative capture or claim.

For failures, check state rollback as well as the error selector. Compare balances, reserves, allowances, nullifiers, receipts and nonce/revision state before and after. Include invalid constructor identities, token callbacks and a failed final append; a successful token transfer alone cannot exercise those cases.

Run venue fork tests#

Fork suites run only when their flags are enabled. Historical blocks make venue identity and liquidity repeatable; the mainnet suites listed here generally use Ethereum block 26,038,058. Some also have a latest-state flag for a separate compatibility check. Use current venue queries to check live capacity, even when the historical test passes.

Set FUYU_MAINNET_RPC_URL for the mainnet suites and check that your RPC serves the chosen block. Run the suite for the venue you are changing. Report skipped cases separately from tests that actually executed.

bash
# Linux only; these use external chain state but no public transaction broadcast
export FUYU_REMOTE_EXPERIMENTS=1
export FUYU_MAINNET_RPC_URL='<archive RPC endpoint>'
RUN_AAVE_FORK=true bash contracts/script/test.sh --match-contract AaveMainnetFork
RUN_PLAN_FORK=true bash contracts/script/test.sh --match-contract FuyuPlanMainnetFork
RUN_V3_ROUTER_FORK=true bash contracts/script/test.sh --match-contract UniswapV3ActionAdapterFork
RUN_DOLOMITE_FORK=true bash contracts/script/test.sh --match-contract DolomiteMainnetFork
RUN_MORPHO_FORK=true bash contracts/script/test.sh --match-contract MorphoMainnetFork

Reproduce artifacts#

npm run reproduce:contracts rebuilds historical deployed/test-only artifacts from archived inputs, checks source and archive hashes, and compares creation/runtime bytecode. Use the Solidity version recorded in the archive. The script builds in an isolated generated workspace without editing current source or the stored artifacts.

Reproducing an archived artifact checks that archive, not repaired current contracts/src. Test current source separately with Foundry. For a new deployment, build artifacts from the intended revision and record that connection rather than deploying an old artifact because its archive reproduced.

npm run check runs layout, artifact, contract-size, circuit-source and generated hash/verifier checks. Keep its output and manifest identities with the result. The docs build and TypeScript checks cover the site; they do not run these protocol checks.

bash
# Linux toolchain with the pinned solc available
npm run check
npm run reproduce:contracts

# SDK helpers complement Solidity tests
npm run test:payment-sessions
npm run test:payment-integration

Test the full application flow#

After component tests, fund a disposable deployment using the artifacts and configuration intended for release. Make a proof, review signatures, submit and check the recovered result in a fresh client. Include lost responses and permissionless completion, and verify both spent inputs and recovered outputs.

For routes, test supply and redemption or both directions where applicable. Include minimum failures, changed identities, token accounting, public exit, route revocation and queued finalization. For payment rails, cover close/refund, holds that survive cancellation, billing-period boundaries, refund destinations and split rounding.

Record the revision, commands, enabled flags, block, checks run and remaining limits. Tool-assisted tests are not an independent audit. A proof-key ceremony, physical-device/browser acceptance and public deployment checks also need their own release work.

Save the results#

Write down the command and source version, which contracts and venues ran, and which cases failed or were skipped. Keep transactions or traces for custody discrepancies. Enabling a flag, acquiring source or adding a test file does not mean the test has run.

When a test loses a submission response, use the original ID and chain state to find the outcome. Apply the same behavior in your app: check whether the first payment settled before a retry can create another payment.