Spun out of #186 to keep that thread focused on the model. This issue is about how the org.accordproject.money@1.0.0 reference data (currency/token registries) is distributed and consumed, and what — if anything — we ship for arithmetic.
Background
money@1.0.0 moves the code → scale mapping out of the schema (no more hard-coded CurrencyCode enum / @DecimalPlaces decorator) and into reference data: validated instances of org.accordproject.money.reference@1.0.0.CurrencyRegistry, one per scheme (iso4217, erc20, slip44, …). See the draft data under data/ in #188.
PreciseAmount.unscaledValue is now an exact integer string (BigInteger), so amounts are exact at any scale (incl. 18-decimal tokens and aggregate balances). Consumers therefore need (a) the registry data and (b) an arithmetic story.
Arithmetic: reuse, don't build
The amount shape is integer mantissa + scale, which is exactly what mature arbitrary-precision money libraries already model — so we should not build or maintain a money math engine. A thin adapter is enough:
- Dinero.js v2 (recommended) — near 1:1 mapping with the bigint calculator:
Unit → currency { code, base: 10, exponent: scale }, unscaledValue → BigInt(amount). Gives exact add/subtract/allocate/compare and locale formatting for free. (Dinero v1 stores amount as a JS number → 2^53 limit, so v2 is required; note v2 is still alpha.)
- big.js / bignumber.js / decimal.js — construct the exact decimal from
mantissa + scale (via string, not lossy division); stable, tiny adapter.
So the Accord Project deliverable is registry data + a thin adapter + assertKnownUnit, not a bespoke library. Aligning Unit's field names (code, scale) with Dinero's (code, exponent) keeps the adapter trivial.
Illustrative adapter surface:
const toCurrency = u => ({ code: u.code, base: 10, exponent: u.scale }); // Unit -> Dinero currency
const toDinero = a => dinero({ amount: BigInt(a.unscaledValue), currency: toCurrency(a.unit) });
const fromDinero = (d, unit) => ({ $class: '…PreciseAmount', unscaledValue: String(toSnapshot(d).amount), unit });
assertKnownUnit(amount); // validate unit.scale against the registry for its scheme
Distribution options for the reference data
- HTTP alongside the models. The models repo already publishes
src/*.cto to models.accordproject.org with CORS. Teach build.js to also copy data/*.json so registries are fetchable at e.g. https://models.accordproject.org/data/iso4217.json. Language-neutral, versioned with the model; but a runtime fetch and no types.
- npm helper package (e.g.
@accordproject/money-units). Embeds the JSON + the Dinero adapter + assertKnownUnit. Best JS DX; separate publish lifecycle. Generated from the same data/ JSON so it can't drift.
- Bundle into an existing Cicero package (
cicero-core / template-engine). Fewer moving parts for template authors; couples to the Cicero release cadence.
(Not mutually exclusive — e.g. HTTP as the canonical source + a generated npm/Cicero adapter for DX.)
Open questions
- Which channel(s) — HTTP canonical + a generated adapter, or fold into Cicero?
- If a package: standalone
@accordproject/money-units, or inside cicero-core / template-engine?
- Do we bless Dinero.js v2 as the recommended adapter and ship it, given v2 is still alpha — or stay library-agnostic and only document
big.js/decimal.js adapters too?
- Codegen targets that can't represent an integer-string cleanly — anything to do for consumers there? (
BigInteger is a String scalar, so it maps to string uniformly, which is the easy case.)
- How is the reference data kept current (ISO 4217 revisions, new tokens)? Manual PRs, or generated from an upstream source?
Resolved since this was opened
Spun out of #186 to keep that thread focused on the model. This issue is about how the
org.accordproject.money@1.0.0reference data (currency/token registries) is distributed and consumed, and what — if anything — we ship for arithmetic.Background
money@1.0.0moves thecode → scalemapping out of the schema (no more hard-codedCurrencyCodeenum /@DecimalPlacesdecorator) and into reference data: validated instances oforg.accordproject.money.reference@1.0.0.CurrencyRegistry, one per scheme (iso4217,erc20,slip44, …). See the draft data underdata/in #188.PreciseAmount.unscaledValueis now an exact integer string (BigInteger), so amounts are exact at any scale (incl. 18-decimal tokens and aggregate balances). Consumers therefore need (a) the registry data and (b) an arithmetic story.Arithmetic: reuse, don't build
The amount shape is integer mantissa + scale, which is exactly what mature arbitrary-precision money libraries already model — so we should not build or maintain a money math engine. A thin adapter is enough:
Unit→currency { code, base: 10, exponent: scale },unscaledValue→BigInt(amount). Gives exact add/subtract/allocate/compare and locale formatting for free. (Dinero v1 storesamountas a JSnumber→ 2^53 limit, so v2 is required; note v2 is still alpha.)mantissa + scale(via string, not lossy division); stable, tiny adapter.So the Accord Project deliverable is registry data + a thin adapter +
assertKnownUnit, not a bespoke library. AligningUnit's field names (code,scale) with Dinero's (code,exponent) keeps the adapter trivial.Illustrative adapter surface:
Distribution options for the reference data
src/*.ctotomodels.accordproject.orgwith CORS. Teachbuild.jsto also copydata/*.jsonso registries are fetchable at e.g.https://models.accordproject.org/data/iso4217.json. Language-neutral, versioned with the model; but a runtimefetchand no types.@accordproject/money-units). Embeds the JSON + the Dinero adapter +assertKnownUnit. Best JS DX; separate publish lifecycle. Generated from the samedata/JSON so it can't drift.cicero-core/template-engine). Fewer moving parts for template authors; couples to the Cicero release cadence.(Not mutually exclusive — e.g. HTTP as the canonical source + a generated npm/Cicero adapter for DX.)
Open questions
@accordproject/money-units, or insidecicero-core/template-engine?big.js/decimal.jsadapters too?BigIntegeris aStringscalar, so it maps tostringuniformly, which is the easy case.)Resolved since this was opened
String-valued high-precision variant). Resolved in Proposal: org.accordproject.money@1.0.0 — remove DigitalCurrencyCode, support non-decimal denominations, fixed-point representation #186/feat(money): propose org.accordproject.money@1.0.0 + reference registries #188:unscaledValueis now an exact integer string, so fiat and 18-decimal tokens are both exact. No native-number/2^53 caveat remains at the model level.code → Unitregistries needed a concerto-core fix (cross-namespace concept map values) — fix(concerto-core): serialize maps with cross-namespace concept values concerto#1279.