This note freezes the exact Phase 0 contracts for the Quantum transaction seam spike in the Foundry fork.
- Foundry fork base:
f1abb2ca347187bb6dea8c3881ca44ce50aab1e7 - Quantum harness source-of-truth repo:
https://github.com/multivmlabs/quantum-eth.git - Quantum harness commit:
8f3612c60f9fa66ea3a09eab99a2e0802f373673 - Behavioral evidence repo:
https://github.com/multivmlabs/tx-spammer - Behavioral evidence commit:
2c25f14a44b8cc88fc41a65f521f1ba8350e7fa4
- State-changing Quantum submission uses raw
eth_sendRawTransaction. - The submitted payload is a native
0x7Aenvelope with exactly one call. senderis explicit and never auto-derived from signer identity.key_idis explicit and defaults to0only for the Phase 0 seam spike.nonce_keyis fixed to0in v1.- Sender-pays only. Fee-payer and sponsorship fields remain out of scope for Phase 0.
- Deploy and future script flows must treat
eth_getTransactionReceiptas the source ofcontractAddress. - Ordinary
eth_callremains the read-path contract. - KeyVault lifecycle selectors are not simulated through ordinary send/call flows; the stable rejection text is:
KeyVault lifecycle operations (bootstrap/addKey/removeKey/updateKeyAuth) cannot be simulated via eth_call; use explicit lifecycle transaction submission
- Precompile address:
0x0000000000000000000000000000000000001000 bootstrapKey():0x5e8e7a13addKey(uint32,bytes,uint8,bytes,uint8,uint8,bytes):0x32bc2919removeKey(uint32):0xc98f21f4updateKeyAuth(uint32,bytes,uint8,bytes,uint8):0x8908154b
Every state-changing Quantum write surface must normalize into the same internal contract before signing or broadcast:
- explicit Quantum selection
- explicit
sender - auth-lane
key_id nonce_key = 0- one file-backed ML-DSA signer source
- optional bootstrap-only fields
- optional detached artifact for required P256 and ECDSA cosigner flows
- lifecycle target-key / scoped-permission inputs when applicable
- one normalized single-call payload that becomes the native
0x7Abody
The seam spike accepts exactly one primary signer source:
--quantum.primary-seed-file <PATH>
The file must contain one 32-byte ML-DSA seed encoded as hex, with or without a 0x prefix. The seed is normalized into bytes before signing.
The Phase 0 implementation uses the public ml-dsa crate directly and mirrors the quantum-eth2 wrapper behavior for deterministic key expansion, signing, and address derivation, so the seam spike does not depend on a private git dependency.
Detached P256 and detached ECDSA flows are frozen as a single versioned artifact schema, even though the Phase 0 code path only proves the primary-only send:
{
"version": 1,
"scheme": "p256|ecdsa",
"signing_hash": "0x...",
"public_key": "0x...",
"signature": "0x..."
}Rules:
- the fork owns construction of the request body and canonical signing hash
- detached signers sign exactly that hash
- the artifact
signing_hashmust match the fork-computed hash byte-for-byte - the schema is shared across
cast send,forge create, andforge script --broadcast
Phase 0 intentionally does not add the full QuantumNetwork adapter yet.
Instead, it proves the signer/broadcast seam in cast send by:
- using normal Foundry parsing/fill logic to resolve destination, calldata, gas, nonce, and fees
- normalizing those values into
QuantumWriteContractV1 - signing a native
0x7Aenvelope locally with the ML-DSA seed file - submitting the encoded raw bytes with
eth_sendRawTransaction - reusing Foundry’s existing raw-send receipt polling path
This is a spike, not the release architecture. The Phase 1 rule remains:
cast send,forge create, andforge script --broadcastmust converge on one shared Quantum signing and broadcast pipeline
Tracked fixture directory:
testdata/fixtures/quantum/phase0/
Phase 0 fixture set:
- canonical raw
0x7Asubmission example with parity metadata - deploy receipt example showing
status,transactionHash, andcontractAddress - lifecycle simulation rejection example with the stable surfaced message
The pinned local harness for manual and CI validation remains the Quantum source-of-truth e2e cluster at commit 8f3612c60f9fa66ea3a09eab99a2e0802f373673.
For Phase 0 closure, the smallest reproducible local proof uses the upstream single-node dev harness. It preserves the same 0x7A transaction and receipt behavior without requiring the full multi-node cluster:
# in /Users/ea/repos/quantum-eth2
cargo run --bin quantum-reth -- node --dev --http --http.port 18545 --http.api allPhase 0 validation still relies on the same upstream behavior exercised by:
crates/e2e/tests/transactions.rscrates/e2e/tests/keyvault_lifecycle.rs
The Foundry fork does not redefine the harness contract locally in Phase 0; it freezes the upstream harness commit and fixture expectations before broader adapter work begins.
Request the following proof from the operator / reviewer when closing Phase 0.
The upstream dev harness prefunds dev account 0, whose ML-DSA seed is sha256("quantum-ml-dsa-dev-0").
printf '0x4f600dd3c20fb7d9e12a3d51ee15ecc74b92e9c020ad1f795a774e96eb5634f4\n' >/tmp/quantum-dev0.seedFrozen addresses used in the proof:
- sender / dev-0:
0x47872C3e8676384B80648D95bEaC2c0C348eF272 - recipient example / dev-1:
0x9a9eA6B0e3d2984ddB7e8070f1F8B46Af36BF92C
cast send --quantum intentionally does not perform KeyVault lifecycle bootstrap in Phase 0, so the sender must be registered first.
# in /Users/ea/repos/quantum-eth2
cargo run --bin quantum-send-tx -- bootstrap \
--rpc-url http://127.0.0.1:18545 \
--fill \
--dev-index 0Expected result:
- stdout prints a bootstrap tx hash
- stderr includes
status: 0x1
Observed locally on 2026-04-15:
- bootstrap tx hash:
0xc7734c6501fb4074900015ba3e1e5c7e0990da78d25073e7029ce7399fbe73d8
Use --async for the manual proof. The Phase 0 seam successfully broadcasts without it, but non-async cast send currently trips over Foundry receipt deserialization because the upstream harness returns receipt type: "Pq".
# in /Users/ea/repos/quantum-foundry
TX_HASH=$(cargo +nightly run --bin cast -- send \
0x9a9eA6B0e3d2984ddB7e8070f1F8B46Af36BF92C \
--value 2 \
--async \
--rpc-url http://127.0.0.1:18545 \
--quantum \
--quantum.sender 0x47872C3e8676384B80648D95bEaC2c0C348eF272 \
--quantum.primary-seed-file /tmp/quantum-dev0.seed)
printf 'TX_HASH=%s\n' "$TX_HASH"Expected result:
- command exits
0 - stdout is only the transaction hash
Observed locally on 2026-04-15:
- async tx hash:
0xa300521dff7d22ed26eca888e19c09711cb524d88877f99b42f4258e81b197fe
Query raw RPC directly so no Ethereum-type deserializer gets in the way.
curl -s -H 'content-type: application/json' \
--data "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_getTransactionReceipt\",\"params\":[\"$TX_HASH\"]}" \
http://127.0.0.1:18545
curl -s -H 'content-type: application/json' \
--data "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_getTransactionByHash\",\"params\":[\"$TX_HASH\"]}" \
http://127.0.0.1:18545Expected receipt assertions:
result.type == "Pq"result.status == "0x1"result.from == "0x47872c3e8676384b80648d95beac2c0c348ef272"result.to == "0x9a9ea6b0e3d2984ddb7e8070f1f8b46af36bf92c"
Expected transaction assertions:
result.type == "0x7a"result.sender == "0x47872c3e8676384b80648d95beac2c0c348ef272"result.keyId == "0x0"result.nonceKey == "0x0"
Provide explicit nonce and gas so the command reaches QuantumWriteContractV1 validation instead of failing early during RPC gas estimation.
cargo +nightly run --bin cast -- send \
0x0000000000000000000000000000000000001000 \
--data 0x5e8e7a13 \
--async \
--rpc-url http://127.0.0.1:18545 \
--nonce 3 \
--gas-limit 2100000 \
--gas-price 15 \
--priority-gas-price 1 \
--quantum \
--quantum.sender 0x47872C3e8676384B80648D95bEaC2c0C348eF272 \
--quantum.primary-seed-file /tmp/quantum-dev0.seedExpected result:
KeyVault lifecycle operations (bootstrap/addKey/removeKey/updateKeyAuth) cannot be simulated via eth_call; use explicit lifecycle transaction submission
Note: if nonce / gas are omitted, Foundry may fail earlier during gas estimation with a generic RPC revert instead of surfacing the frozen seam-level rejection string.
Confirmed locally on 2026-04-15:
- upstream dev harness boot recipe is now concrete and reproducible
- native
cast send --quantumsubmission does reacheth_sendRawTransactionand lands successfully on the harness - raw node RPC shows transaction
type: "0x7a"and receipttype: "Pq" - the Phase 0 local lifecycle rejection path is reproducible with explicit nonce / gas
Still open after verification:
- broader golden fixtures are still missing for bootstrap raw shape and lifecycle calldata shape
- non-async
cast send --quantumis not yet operator-clean because receipt parsing does not recognize upstream receipttype: "Pq" - raw
eth_callagainst lifecycle selectors on the live harness currently returnsexecution revertedwith data0x202ce609, not the friendly frozen rejection string; treat this as an upstream RPC-surfacing mismatch that still needs confirmation before Phase 0 is considered fully evidenced end-to-end