The OrderParams now carries decimals
Every order the user signs commits to two new fields:
| Field | Type | Meaning |
|---|---|---|
orderDecimals | uint8 | Decimals of the token on the order chain (where the bridger deposits). |
adDecimals | uint8 | Decimals of the token on the ad chain (where the ad creator provides liquidity). |
params.amount is always in order-chain units
By convention, params.amount is the raw unit amount on the order chain.
For an 18-decimal ERC-20, that’s wei. For a 7-decimal Stellar asset, it’s
the base unit shown in Horizon.
When the order lands on the ad chain, AdManager computes the ad-side
amount internally:
DecimalScaling primitive (EVM library +
proofbridge-core::decimal_scaling on Stellar) before any pool accounting
or token transfer. The order chain only ever sees the raw
params.amount; the ad chain only ever sees the scaled adAmount.
Worked example: 18-decimal ↔ 7-decimal
Say a bridger is moving 1 ETH from an 18-decimal chain and matching it against an ad denominated in a 7-decimal token on Stellar.params.amount is what the bridger signs, what lands in
the OrderPortal, and what rides through the Merkle tree and the ZK proof.
On-chain decimals must match the signed decimals
Both portals defend against a signer lying about decimals. AtvalidateOrder time:
OrderPortalassertsparams.orderDecimals == token.decimals()for the deposited token (withwNativeTokenused for ETH / XLM wrappers).AdManagerassertsparams.adDecimals == token.decimals()for the ad-side token.
Guardrails
MAX_DECIMALS = 30— rejects absurd values at the boundary and keeps the10^difffactor well insideuint256headroom.- Scale-up is overflow-checked — Solidity 0.8 reverts on overflow by default, and the Stellar implementation uses explicit checked arithmetic.
- Scale-down reverts on non-exact division — if the raw amount doesn’t divide evenly when mapping from higher to lower decimals, the settlement reverts. Silent truncation would change the economic value of the order, so it’s treated as a signed-amount error and surfaced to the user before any money moves.
Error surface
New errors emitted when a decimal invariant is violated: EVMDecimalScaling__DecimalsOutOfRange(uint8)—orderDecimalsoradDecimalsexceedsMAX_DECIMALS.DecimalScaling__NonExactDownscale(uint256 amount, uint8 fromDec, uint8 toDec)— scale-down would truncate.DecimalScaling__DecimalsMismatch(uint8 expected, uint8 provided)— raised byassertMatchesOnChainwhen the signed decimals don’t equal the token contract’sdecimals().
proofbridge-core::errors)
order_decimals_mismatchad_decimals_mismatch- Equivalent range / non-exact-downscale errors inside the
decimal_scalingmodule.
From 1-to-1 bridging to cross-asset trades (exploratory)
Today’s scaling is a pure decimal re-basing between two representations of the same underlying asset —1 ETH ↔ 1 wETH, 1 XLM ↔ 1 wXLM. The
signed-order surface stays small and the settlement math stays
auditable.
Once the current roadmap is delivered, extending routes to quote
cross-asset rates is a natural follow-up: a single order could bridge
and swap in one step (for example ETH → USDC on Stellar). The
signed OrderParams would gain a rate field, DecimalScaling.scale
would gain a rate-aware overload, and ad creators would either set their
own rates or opt into a shared rate-balancing layer. Nothing in the
current design forecloses this direction — but it’s not part of the
current plan.