Skip to content

feat: add denomination_symbol and display_decimals metadata to Offering#514

Open
Keengfk wants to merge 1 commit into
RevoraOrg:masterfrom
Keengfk:feat/offering-denomination-metadata
Open

feat: add denomination_symbol and display_decimals metadata to Offering#514
Keengfk wants to merge 1 commit into
RevoraOrg:masterfrom
Keengfk:feat/offering-denomination-metadata

Conversation

@Keengfk

@Keengfk Keengfk commented Jul 1, 2026

Copy link
Copy Markdown

feat: add denomination_symbol and display_decimals to Offering metadata

Problem

Indexers, wallets, and dashboards had no on-chain source of truth for how to display payout
amounts. They were forced to guess display precision and currency labels from the payment token
address, which is unreliable and fragile across different asset types.

Solution

Add two new fields to the Offering struct at registration time:

  • denomination_symbol: Symbol — human-readable ticker (e.g. USDC, XLM) for the payout
    denomination
  • display_decimals: u32 — decimal precision wallets should use when rendering amounts

Both fields are written to persistent storage and included in the ofr_reg2 event payload, so
indexers get everything they need from a single event with no extra round-trips.

Changes

Core (src/lib.rs)

  • RevoraError: new variant DisplayDecimalsOutOfRange = 51 — emitted when display_decimals > 18
  • Offering struct: added denomination_symbol and display_decimals fields
  • DataKey2: new DenominationMetadata(OfferingId) storage key for O(1) targeted reads
  • register_offering: two new trailing parameters with eager validation (display_decimals ≤
    MAX_TOKEN_DECIMALS) checked before the duplicate-prevention guard
  • Storage: DenominationMetadata written alongside OfferItem and OfferingRecord
  • ofr_reg2 event: both emit calls updated to include the new fields in payload
  • get_denomination_metadata: new read-only helper returning Option<(Symbol, u32)>

Call sites

  • All 346 existing register_offering / try_register_offering calls across 28 test files updated
    with &symbol_short!(""), &0 defaults — no behaviour change for existing tests

Tests (src/test.rs) — 9 new

┌──────┬──────────────────┐
│ Test │ What it verifies │
├───────────────────────────────────────────┼──────────────────┤
│ denomination_metadata_stored_an │ Happy path — symbol and decimals round-trip │
│ d_readable │ through storage │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_zero_deci │
│ mals_accepted │ │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_max_decim │ Boundary: display_decimals = 18 is valid │
│ als_accepted │ │
├───────────────────────────────────────────┼──────────────────────────────────────────────┤
│ denomination_metadata_rejects_displa │
│ lay_decimals_over_18 │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_rejects_disp │
│ lay_decimals_u32_max │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_returns_none │
before_register │ unknown offering │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_in_ofr_reg2

│ event_payload │ │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_reflected_in │
│ _get_offering │ fields │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_validation_b │
│ ion_before_duplicate_guard │ no-ops on an existing offering │
└────────────────────────────────────┴─────────────────────────────────────────────────────┘

Security notes

  • denomination_symbol is informational only — it has no effect on payout math, token transfers,
    or authorization
  • display_decimals ≤ payment_token_decimals is a caller responsibility documented in the
    function doc-comment; enforcing it on-chain would require a cross-contract call that adds gas
    and complexity for no security benefit
  • Validation order: supply_cap → display_decimals → revenue_share_bps → duplicate guard — all
    parameter errors surface eagerly before any state is written
  • Error code 51 is new and wire-stable; all existing error discriminants are unchanged

Backward compatibility

Existing on-chain state is unaffected. The new fields default to symbol_short!("") and 0 for
any offering registered before this version. The ofr_reg2 event payload is additive — existing
indexers that only read the first three fields continue to work.

▸ Credits: 0.35 • Time: 15s
feat: add denomination_symbol and display_decimals to Offering metadata

Problem

Indexers, wallets, and dashboards had no on-chain source of truth for how to display payout
amounts. They were forced to guess display precision and currency labels from the payment token
address, which is unreliable and fragile across different asset types.

Solution

Add two new fields to the Offering struct at registration time:

  • denomination_symbol: Symbol — human-readable ticker (e.g. USDC, XLM) for the payout
    denomination
  • display_decimals: u32 — decimal precision wallets should use when rendering amounts

Both fields are written to persistent storage and included in the ofr_reg2 event payload, so
indexers get everything they need from a single event with no extra round-trips.

Changes

Core (src/lib.rs)

  • RevoraError: new variant DisplayDecimalsOutOfRange = 51 — emitted when display_decimals > 18
  • Offering struct: added denomination_symbol and display_decimals fields
  • DataKey2: new DenominationMetadata(OfferingId) storage key for O(1) targeted reads
  • register_offering: two new trailing parameters with eager validation (display_decimals ≤
    MAX_TOKEN_DECIMALS) checked before the duplicate-prevention guard
  • Storage: DenominationMetadata written alongside OfferItem and OfferingRecord
  • ofr_reg2 event: both emit calls updated to include the new fields in payload
  • get_denomination_metadata: new read-only helper returning Option<(Symbol, u32)>

Call sites

  • All 346 existing register_offering / try_register_offering calls across 28 test files updated
    with &symbol_short!(""), &0 defaults — no behaviour change for existing tests

Tests (src/test.rs) — 9 new

┌──────┬──────────────────┐
│ Test │ What it verifies │
├───────────────────────────────────────────┼──────────────────┤
│ denomination_metadata_stored_an │ Happy path — symbol and decimals round-trip │
│ d_readable │ through storage │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_zero_deci │
│ mals_accepted │ │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_max_decim │ Boundary: display_decimals = 18 is valid │
│ als_accepted │ │
├───────────────────────────────────────────┼──────────────────────────────────────────────┤
│ denomination_metadata_rejects_displa │
│ lay_decimals_over_18 │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_rejects_disp │
│ lay_decimals_u32_max │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_returns_none │
before_register │ unknown offering │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_in_ofr_reg2

│ event_payload │ │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_reflected_in │
│ _get_offering │ fields │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_validation_b │
│ ion_before_duplicate_guard │ no-ops on an existing offering │
└────────────────────────────────────┴─────────────────────────────────────────────────────┘

Security notes

  • denomination_symbol is informational only — it has no effect on payout math, token transfers,
    or authorization
  • display_decimals ≤ payment_token_decimals is a caller responsibility documented in the
    function doc-comment; enforcing it on-chain would require a cross-contract call that adds gas
    and complexity for no security benefit
  • Validation order: supply_cap → display_decimals → revenue_share_bps → duplicate guard — all
    parameter errors surface eagerly before any state is written
  • Error code 51 is new and wire-stable; all existing error discriminants are unchanged

Backward compatibility

Existing on-chain state is unaffected. The new fields default to symbol_short!("") and 0 for
any offering registered before this version. The ofr_reg2 event payload is additive — existing
indexers that only read the first three fields continue to work.

▸ Credits: 0.35 • Time: 15s
feat: add denomination_symbol and display_decimals to Offering metadata

Problem

Indexers, wallets, and dashboards had no on-chain source of truth for how to display payout
amounts. They were forced to guess display precision and currency labels from the payment token
address, which is unreliable and fragile across different asset types.

Solution

Add two new fields to the Offering struct at registration time:

  • denomination_symbol: Symbol — human-readable ticker (e.g. USDC, XLM) for the payout
    denomination
  • display_decimals: u32 — decimal precision wallets should use when rendering amounts

Both fields are written to persistent storage and included in the ofr_reg2 event payload, so
indexers get everything they need from a single event with no extra round-trips.

Changes

Core (src/lib.rs)

  • RevoraError: new variant DisplayDecimalsOutOfRange = 51 — emitted when display_decimals > 18
  • Offering struct: added denomination_symbol and display_decimals fields
  • DataKey2: new DenominationMetadata(OfferingId) storage key for O(1) targeted reads
  • register_offering: two new trailing parameters with eager validation (display_decimals ≤
    MAX_TOKEN_DECIMALS) checked before the duplicate-prevention guard
  • Storage: DenominationMetadata written alongside OfferItem and OfferingRecord
  • ofr_reg2 event: both emit calls updated to include the new fields in payload
  • get_denomination_metadata: new read-only helper returning Option<(Symbol, u32)>

Call sites

  • All 346 existing register_offering / try_register_offering calls across 28 test files updated
    with &symbol_short!(""), &0 defaults — no behaviour change for existing tests

Tests (src/test.rs) — 9 new

┌──────┬──────────────────┐
│ Test │ What it verifies │
├───────────────────────────────────────────┼──────────────────┤
│ denomination_metadata_stored_an │ Happy path — symbol and decimals round-trip │
│ d_readable │ through storage │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_zero_deci │
│ mals_accepted │ │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_max_decim │ Boundary: display_decimals = 18 is valid │
│ als_accepted │ │
├───────────────────────────────────────────┼──────────────────────────────────────────────┤
│ denomination_metadata_rejects_displa │
│ lay_decimals_over_18 │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_rejects_disp │
│ lay_decimals_u32_max │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_returns_none │
before_register │ unknown offering │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_in_ofr_reg2

│ event_payload │ │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_reflected_in │
│ _get_offering │ fields │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_validation_b │
│ ion_before_duplicate_guard │ no-ops on an existing offering │
└────────────────────────────────────┴─────────────────────────────────────────────────────┘

Security notes

  • denomination_symbol is informational only — it has no effect on payout math, token transfers,
    or authorization
  • display_decimals ≤ payment_token_decimals is a caller responsibility documented in the
    function doc-comment; enforcing it on-chain would require a cross-contract call that adds gas
    and complexity for no security benefit
  • Validation order: supply_cap → display_decimals → revenue_share_bps → duplicate guard — all
    parameter errors surface eagerly before any state is written
  • Error code 51 is new and wire-stable; all existing error discriminants are unchanged

Backward compatibility

Existing on-chain state is unaffected. The new fields default to symbol_short!("") and 0 for
any offering registered before this version. The ofr_reg2 event payload is additive — existing
indexers that only read the first three fields continue to work.

▸ Credits: 0.35 • Time: 15s
feat: add denomination_symbol and display_decimals to Offering metadata

Problem

Indexers, wallets, and dashboards had no on-chain source of truth for how to display payout
amounts. They were forced to guess display precision and currency labels from the payment token
address, which is unreliable and fragile across different asset types.

Solution

Add two new fields to the Offering struct at registration time:

  • denomination_symbol: Symbol — human-readable ticker (e.g. USDC, XLM) for the payout
    denomination
  • display_decimals: u32 — decimal precision wallets should use when rendering amounts

Both fields are written to persistent storage and included in the ofr_reg2 event payload, so
indexers get everything they need from a single event with no extra round-trips.

Changes

Core (src/lib.rs)

  • RevoraError: new variant DisplayDecimalsOutOfRange = 51 — emitted when display_decimals > 18
  • Offering struct: added denomination_symbol and display_decimals fields
  • DataKey2: new DenominationMetadata(OfferingId) storage key for O(1) targeted reads
  • register_offering: two new trailing parameters with eager validation (display_decimals ≤
    MAX_TOKEN_DECIMALS) checked before the duplicate-prevention guard
  • Storage: DenominationMetadata written alongside OfferItem and OfferingRecord
  • ofr_reg2 event: both emit calls updated to include the new fields in payload
  • get_denomination_metadata: new read-only helper returning Option<(Symbol, u32)>

Call sites

  • All 346 existing register_offering / try_register_offering calls across 28 test files updated
    with &symbol_short!(""), &0 defaults — no behaviour change for existing tests

Tests (src/test.rs) — 9 new

┌──────┬──────────────────┐
│ Test │ What it verifies │
├───────────────────────────────────────────┼──────────────────┤
│ denomination_metadata_stored_an │ Happy path — symbol and decimals round-trip │
│ d_readable │ through storage │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_zero_deci │
│ mals_accepted │ │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_max_decim │ Boundary: display_decimals = 18 is valid │
│ als_accepted │ │
├───────────────────────────────────────────┼──────────────────────────────────────────────┤
│ denomination_metadata_rejects_displa │
│ lay_decimals_over_18 │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_rejects_disp │
│ lay_decimals_u32_max │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_returns_none │
before_register │ unknown offering │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_in_ofr_reg2

│ event_payload │ │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_reflected_in │
│ _get_offering │ fields │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_validation_b │
│ ion_before_duplicate_guard │ no-ops on an existing offering │
└────────────────────────────────────┴─────────────────────────────────────────────────────┘

Security notes

  • denomination_symbol is informational only — it has no effect on payout math, token transfers,
    or authorization
  • display_decimals ≤ payment_token_decimals is a caller responsibility documented in the
    function doc-comment; enforcing it on-chain would require a cross-contract call that adds gas
    and complexity for no security benefit
  • Validation order: supply_cap → display_decimals → revenue_share_bps → duplicate guard — all
    parameter errors surface eagerly before any state is written
  • Error code 51 is new and wire-stable; all existing error discriminants are unchanged

Backward compatibility

Existing on-chain state is unaffected. The new fields default to symbol_short!("") and 0 for
any offering registered before this version. The ofr_reg2 event payload is additive — existing
indexers that only read the first three fields continue to work.

▸ Credits: 0.35 • Time: 15s
feat: add denomination_symbol and display_decimals to Offering metadata

Problem

Indexers, wallets, and dashboards had no on-chain source of truth for how to display payout
amounts. They were forced to guess display precision and currency labels from the payment token
address, which is unreliable and fragile across different asset types.

Solution

Add two new fields to the Offering struct at registration time:

  • denomination_symbol: Symbol — human-readable ticker (e.g. USDC, XLM) for the payout
    denomination
  • display_decimals: u32 — decimal precision wallets should use when rendering amounts

Both fields are written to persistent storage and included in the ofr_reg2 event payload, so
indexers get everything they need from a single event with no extra round-trips.

Changes

Core (src/lib.rs)

  • RevoraError: new variant DisplayDecimalsOutOfRange = 51 — emitted when display_decimals > 18
  • Offering struct: added denomination_symbol and display_decimals fields
  • DataKey2: new DenominationMetadata(OfferingId) storage key for O(1) targeted reads
  • register_offering: two new trailing parameters with eager validation (display_decimals ≤
    MAX_TOKEN_DECIMALS) checked before the duplicate-prevention guard
  • Storage: DenominationMetadata written alongside OfferItem and OfferingRecord
  • ofr_reg2 event: both emit calls updated to include the new fields in payload
  • get_denomination_metadata: new read-only helper returning Option<(Symbol, u32)>

Call sites

  • All 346 existing register_offering / try_register_offering calls across 28 test files updated
    with &symbol_short!(""), &0 defaults — no behaviour change for existing tests

Tests (src/test.rs) — 9 new

┌──────┬──────────────────┐
│ Test │ What it verifies │
├───────────────────────────────────────────┼──────────────────┤
│ denomination_metadata_stored_an │ Happy path — symbol and decimals round-trip │
│ d_readable │ through storage │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_zero_deci │
│ mals_accepted │ │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_max_decim │ Boundary: display_decimals = 18 is valid │
│ als_accepted │ │
├───────────────────────────────────────────┼──────────────────────────────────────────────┤
│ denomination_metadata_rejects_displa │
│ lay_decimals_over_18 │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_rejects_disp │
│ lay_decimals_u32_max │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_returns_none │
before_register │ unknown offering │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_in_ofr_reg2

│ event_payload │ │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_reflected_in │
│ _get_offering │ fields │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_validation_b │
│ ion_before_duplicate_guard │ no-ops on an existing offering │
└────────────────────────────────────┴─────────────────────────────────────────────────────┘

Security notes

  • denomination_symbol is informational only — it has no effect on payout math, token transfers,
    or authorization
  • display_decimals ≤ payment_token_decimals is a caller responsibility documented in the
    function doc-comment; enforcing it on-chain would require a cross-contract call that adds gas
    and complexity for no security benefit
  • Validation order: supply_cap → display_decimals → revenue_share_bps → duplicate guard — all
    parameter errors surface eagerly before any state is written
  • Error code 51 is new and wire-stable; all existing error discriminants are unchanged

Backward compatibility

Existing on-chain state is unaffected. The new fields default to symbol_short!("") and 0 for
any offering registered before this version. The ofr_reg2 event payload is additive — existing
indexers that only read the first three fields continue to work.

▸ Credits: 0.35 • Time: 15s
feat: add denomination_symbol and display_decimals to Offering metadata

Problem

Indexers, wallets, and dashboards had no on-chain source of truth for how to display payout
amounts. They were forced to guess display precision and currency labels from the payment token
address, which is unreliable and fragile across different asset types.

Solution

Add two new fields to the Offering struct at registration time:

  • denomination_symbol: Symbol — human-readable ticker (e.g. USDC, XLM) for the payout
    denomination
  • display_decimals: u32 — decimal precision wallets should use when rendering amounts

Both fields are written to persistent storage and included in the ofr_reg2 event payload, so
indexers get everything they need from a single event with no extra round-trips.

Changes

Core (src/lib.rs)

  • RevoraError: new variant DisplayDecimalsOutOfRange = 51 — emitted when display_decimals > 18
  • Offering struct: added denomination_symbol and display_decimals fields
  • DataKey2: new DenominationMetadata(OfferingId) storage key for O(1) targeted reads
  • register_offering: two new trailing parameters with eager validation (display_decimals ≤
    MAX_TOKEN_DECIMALS) checked before the duplicate-prevention guard
  • Storage: DenominationMetadata written alongside OfferItem and OfferingRecord
  • ofr_reg2 event: both emit calls updated to include the new fields in payload
  • get_denomination_metadata: new read-only helper returning Option<(Symbol, u32)>

Call sites

  • All 346 existing register_offering / try_register_offering calls across 28 test files updated
    with &symbol_short!(""), &0 defaults — no behaviour change for existing tests

Tests (src/test.rs) — 9 new

┌──────┬──────────────────┐
│ Test │ What it verifies │
├───────────────────────────────────────────┼──────────────────┤
│ denomination_metadata_stored_an │ Happy path — symbol and decimals round-trip │
│ d_readable │ through storage │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_zero_deci │
│ mals_accepted │ │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_max_decim │ Boundary: display_decimals = 18 is valid │
│ als_accepted │ │
├───────────────────────────────────────────┼──────────────────────────────────────────────┤
│ denomination_metadata_rejects_displa │
│ lay_decimals_over_18 │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_rejects_disp │
│ lay_decimals_u32_max │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_returns_none │
before_register │ unknown offering │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_in_ofr_reg2

│ event_payload │ │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_reflected_in │
│ _get_offering │ fields │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_validation_b │
│ ion_before_duplicate_guard │ no-ops on an existing offering │
└────────────────────────────────────┴─────────────────────────────────────────────────────┘

Security notes

  • denomination_symbol is informational only — it has no effect on payout math, token transfers,
    or authorization
  • display_decimals ≤ payment_token_decimals is a caller responsibility documented in the
    function doc-comment; enforcing it on-chain would require a cross-contract call that adds gas
    and complexity for no security benefit
  • Validation order: supply_cap → display_decimals → revenue_share_bps → duplicate guard — all
    parameter errors surface eagerly before any state is written
  • Error code 51 is new and wire-stable; all existing error discriminants are unchanged

Backward compatibility

Existing on-chain state is unaffected. The new fields default to symbol_short!("") and 0 for
any offering registered before this version. The ofr_reg2 event payload is additive — existing
indexers that only read the first three fields continue to work.

▸ Credits: 0.35 • Time: 15s
feat: add denomination_symbol and display_decimals to Offering metadata

Problem

Indexers, wallets, and dashboards had no on-chain source of truth for how to display payout
amounts. They were forced to guess display precision and currency labels from the payment token
address, which is unreliable and fragile across different asset types.

Solution

Add two new fields to the Offering struct at registration time:

  • denomination_symbol: Symbol — human-readable ticker (e.g. USDC, XLM) for the payout
    denomination
  • display_decimals: u32 — decimal precision wallets should use when rendering amounts

Both fields are written to persistent storage and included in the ofr_reg2 event payload, so
indexers get everything they need from a single event with no extra round-trips.

Changes

Core (src/lib.rs)

  • RevoraError: new variant DisplayDecimalsOutOfRange = 51 — emitted when display_decimals > 18
  • Offering struct: added denomination_symbol and display_decimals fields
  • DataKey2: new DenominationMetadata(OfferingId) storage key for O(1) targeted reads
  • register_offering: two new trailing parameters with eager validation (display_decimals ≤
    MAX_TOKEN_DECIMALS) checked before the duplicate-prevention guard
  • Storage: DenominationMetadata written alongside OfferItem and OfferingRecord
  • ofr_reg2 event: both emit calls updated to include the new fields in payload
  • get_denomination_metadata: new read-only helper returning Option<(Symbol, u32)>

Call sites

  • All 346 existing register_offering / try_register_offering calls across 28 test files updated
    with &symbol_short!(""), &0 defaults — no behaviour change for existing tests

Tests (src/test.rs) — 9 new

┌──────┬──────────────────┐
│ Test │ What it verifies │
├───────────────────────────────────────────┼──────────────────┤
│ denomination_metadata_stored_an │ Happy path — symbol and decimals round-trip │
│ d_readable │ through storage │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_zero_deci │
│ mals_accepted │ │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_max_decim │ Boundary: display_decimals = 18 is valid │
│ als_accepted │ │
├───────────────────────────────────────────┼──────────────────────────────────────────────┤
│ denomination_metadata_rejects_displa │
│ lay_decimals_over_18 │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_rejects_disp │
│ lay_decimals_u32_max │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_returns_none │
before_register │ unknown offering │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_in_ofr_reg2

│ event_payload │ │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_reflected_in │
│ _get_offering │ fields │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_validation_b │
│ ion_before_duplicate_guard │ no-ops on an existing offering │
└────────────────────────────────────┴─────────────────────────────────────────────────────┘

Security notes

  • denomination_symbol is informational only — it has no effect on payout math, token transfers,
    or authorization
  • display_decimals ≤ payment_token_decimals is a caller responsibility documented in the
    function doc-comment; enforcing it on-chain would require a cross-contract call that adds gas
    and complexity for no security benefit
  • Validation order: supply_cap → display_decimals → revenue_share_bps → duplicate guard — all
    parameter errors surface eagerly before any state is written
  • Error code 51 is new and wire-stable; all existing error discriminants are unchanged

Backward compatibility

Existing on-chain state is unaffected. The new fields default to symbol_short!("") and 0 for
any offering registered before this version. The ofr_reg2 event payload is additive — existing
indexers that only read the first three fields continue to work.

▸ Credits: 0.35 • Time: 15s
feat: add denomination_symbol and display_decimals to Offering metadata

Problem

Indexers, wallets, and dashboards had no on-chain source of truth for how to display payout
amounts. They were forced to guess display precision and currency labels from the payment token
address, which is unreliable and fragile across different asset types.

Solution

Add two new fields to the Offering struct at registration time:

  • denomination_symbol: Symbol — human-readable ticker (e.g. USDC, XLM) for the payout
    denomination
  • display_decimals: u32 — decimal precision wallets should use when rendering amounts

Both fields are written to persistent storage and included in the ofr_reg2 event payload, so
indexers get everything they need from a single event with no extra round-trips.

Changes

Core (src/lib.rs)

  • RevoraError: new variant DisplayDecimalsOutOfRange = 51 — emitted when display_decimals > 18
  • Offering struct: added denomination_symbol and display_decimals fields
  • DataKey2: new DenominationMetadata(OfferingId) storage key for O(1) targeted reads
  • register_offering: two new trailing parameters with eager validation (display_decimals ≤
    MAX_TOKEN_DECIMALS) checked before the duplicate-prevention guard
  • Storage: DenominationMetadata written alongside OfferItem and OfferingRecord
  • ofr_reg2 event: both emit calls updated to include the new fields in payload
  • get_denomination_metadata: new read-only helper returning Option<(Symbol, u32)>

Call sites

  • All 346 existing register_offering / try_register_offering calls across 28 test files updated
    with &symbol_short!(""), &0 defaults — no behaviour change for existing tests

Tests (src/test.rs) — 9 new

┌──────┬──────────────────┐
│ Test │ What it verifies │
├───────────────────────────────────────────┼──────────────────┤
│ denomination_metadata_stored_an │ Happy path — symbol and decimals round-trip │
│ d_readable │ through storage │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_zero_deci │
│ mals_accepted │ │
├──────────────────────────────────────┼───────────────────────────────────────────────────┤
│ denomination_metadata_max_decim │ Boundary: display_decimals = 18 is valid │
│ als_accepted │ │
├───────────────────────────────────────────┼──────────────────────────────────────────────┤
│ denomination_metadata_rejects_displa │
│ lay_decimals_over_18 │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_rejects_disp │
│ lay_decimals_u32_max │ DisplayDecimalsOutOfRange │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_returns_none │
before_register │ unknown offering │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_in_ofr_reg2

│ event_payload │ │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_reflected_in │
│ _get_offering │ fields │
├─────────────────────────────────────────┼────────────────────────────────────────────────┤
│ denomination_metadata_validation_b │
│ ion_before_duplicate_guard │ no-ops on an existing offering │
└────────────────────────────────────┴─────────────────────────────────────────────────────┘

Security notes

  • denomination_symbol is informational only — it has no effect on payout math, token transfers,
    or authorization
  • display_decimals ≤ payment_token_decimals is a caller responsibility documented in the
    function doc-comment; enforcing it on-chain would require a cross-contract call that adds gas
    and complexity for no security benefit
  • Validation order: supply_cap → display_decimals → revenue_share_bps → duplicate guard — all
    parameter errors surface eagerly before any state is written
  • Error code 51 is new and wire-stable; all existing error discriminants are unchanged

Backward compatibility

Existing on-chain state is unaffected. The new fields default to symbol_short!("") and 0 for
any offering registered before this version. The ofr_reg2 event payload is additive — existing
indexers that only read the first three fields continue to work.
closes #475

Adds two new fields to the Offering struct so wallets and dashboards can
render amounts correctly without guessing payment-token display semantics.

Changes
-------
* RevoraError: new variant DisplayDecimalsOutOfRange = 51 (wire-stable).
* Offering struct: +denomination_symbol: Symbol, +display_decimals: u32.
* DataKey2: new DenominationMetadata(OfferingId) auxiliary storage key.
* register_offering: two new trailing parameters (denomination_symbol,
  display_decimals). Validates display_decimals <= MAX_TOKEN_DECIMALS (18)
  before the duplicate-prevention guard, so bad values always return an
  error rather than silently no-oping on an existing offering.
* register_offering: writes DenominationMetadata to persistent storage
  alongside OfferItem and OfferingRecord.
* ofr_reg2 event payload extended to include denomination_symbol and
  display_decimals (both emit_v2_event calls updated).
* get_denomination_metadata: new read-only O(1) helper returning
  Option<(Symbol, u32)> keyed on (issuer, namespace, token).
* All 346 existing register_offering / try_register_offering call sites
  updated to pass the two new default arguments (&symbol_short!(""), &0).

Tests (src/test.rs)
-------------------
denomination_metadata_stored_and_readable
denomination_metadata_zero_decimals_accepted
denomination_metadata_max_decimals_accepted
denomination_metadata_rejects_display_decimals_over_18   (boundary: 19)
denomination_metadata_rejects_display_decimals_u32_max   (boundary: max)
denomination_metadata_returns_none_before_register
denomination_metadata_in_ofr_reg2_event_payload
denomination_metadata_reflected_in_get_offering
denomination_metadata_validation_before_duplicate_guard

Security notes
--------------
* denomination_symbol is informational only; it does not affect payout
  math, token transfers, or authorization.
* display_decimals is capped at 18 (MAX_TOKEN_DECIMALS) on-chain. The
  relationship display_decimals <= payment_token_decimals is a caller
  responsibility documented in the function doc-comment and can be
  verified off-chain via get_payment_token_decimals.
* Validation order: supply_cap -> display_decimals -> bps -> duplicate
  guard, so all parameter errors are surfaced eagerly.
* Wire value 51 is new and stable; all existing error codes unchanged.
@drips-wave

drips-wave Bot commented Jul 1, 2026

Copy link
Copy Markdown

@Keengfk Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add per-offering currency-denomination metadata and decimals helper for accurate display

1 participant