Raised while reviewing #462, which introduces crates/composable-cow. Confirmed unchanged at the end-of-train tip, so no subsequent car fixes it. Sibling of #546, which does the same job for cow-venue; the two should land together.
ComposableBody (crates/composable-cow/src/body.rs) spells the ConditionalOrderParams tuple with raw primitives instead of the alloy types that describe them:
| Field |
Today |
Should be |
handler |
[u8; 20] |
alloy_primitives::Address |
salt |
[u8; 32] |
alloy_primitives::B256 (FixedBytes<32>) |
static_input |
Vec<u8> |
alloy_primitives::Bytes |
handler is an EVM contract address and should say so in the type: it is the IConditionalOrder contract that mints the tradeable order, and [u8; 20] loses the checksum formatting, the Display/FromStr behaviour and the address semantics that every other EVM-touching type in the workspace already has.
Unlike #546 there is no dependency argument to weigh here: composable-cow already depends on alloy-primitives non-optionally, so the alloy types are already in the crate's graph and the raw arrays are pure loss.
The one real constraint
ComposableBody derives BorshSerialize/BorshDeserialize, and alloy-primitives ships no borsh feature. This differs from #546's U256 case: there the underlying type is ruint::Uint, and ruint does have a borsh feature, so that swap is a feature flag. Address, B256 and Bytes are not ruint types and are not covered by it, so they need custom borsh handling, either borsh(with)-style adapters or thin newtypes. Confirm which borsh 1.x actually supports at implementation time rather than assuming.
The encoding itself need not change: FixedBytes<N> is [u8; N] underneath and Bytes is a length-prefixed blob exactly as Vec<u8> is, so the adapters can be byte-for-byte identical to today's derive output. Nothing is released either, so the wire format is free to change if that turns out simpler.
Acceptance criteria
ComposableBody carries Address, B256 and Bytes, the borsh round-trip is covered by a test, and no raw [u8; 20] or [u8; 32] remains in the crate's public body type.
ComposableBody(crates/composable-cow/src/body.rs) spells theConditionalOrderParamstuple with raw primitives instead of the alloy types that describe them:handler[u8; 20]alloy_primitives::Addresssalt[u8; 32]alloy_primitives::B256(FixedBytes<32>)static_inputVec<u8>alloy_primitives::Byteshandleris an EVM contract address and should say so in the type: it is theIConditionalOrdercontract that mints the tradeable order, and[u8; 20]loses the checksum formatting, theDisplay/FromStrbehaviour and the address semantics that every other EVM-touching type in the workspace already has.Unlike #546 there is no dependency argument to weigh here:
composable-cowalready depends onalloy-primitivesnon-optionally, so the alloy types are already in the crate's graph and the raw arrays are pure loss.The one real constraint
ComposableBodyderivesBorshSerialize/BorshDeserialize, andalloy-primitivesships no borsh feature. This differs from #546'sU256case: there the underlying type isruint::Uint, and ruint does have aborshfeature, so that swap is a feature flag.Address,B256andBytesare not ruint types and are not covered by it, so they need custom borsh handling, eitherborsh(with)-style adapters or thin newtypes. Confirm which borsh 1.x actually supports at implementation time rather than assuming.The encoding itself need not change:
FixedBytes<N>is[u8; N]underneath andBytesis a length-prefixed blob exactly asVec<u8>is, so the adapters can be byte-for-byte identical to today's derive output. Nothing is released either, so the wire format is free to change if that turns out simpler.Acceptance criteria
ComposableBodycarriesAddress,B256andBytes, the borsh round-trip is covered by a test, and no raw[u8; 20]or[u8; 32]remains in the crate's public body type.